Fix PT-136196057: correct indentation after 'do' step completion

This commit is contained in:
Kris De Volder
2017-01-13 10:53:35 -08:00
parent d4c3d84714
commit 15eec8b080
5 changed files with 97 additions and 7 deletions

View File

@@ -129,19 +129,25 @@ public class VscodeCompletionEngineAdapter implements VscodeCompletionEngine {
edits.apply(newDoc);
TextEdit vscodeEdit = new TextEdit();
vscodeEdit.setRange(doc.toRange(replaceEdit.start, replaceEdit.end-replaceEdit.start));
vscodeEdit.setNewText(vscodeIndentFix(vscodeEdit.getRange().getStart(), replaceEdit.newText));
vscodeEdit.setNewText(vscodeIndentFix(doc, vscodeEdit.getRange().getStart(), replaceEdit.newText));
//TODO: cursor offset within newText? for now we assume its always at the end.
item.setTextEdit(vscodeEdit);
}
}
private String vscodeIndentFix(Position start, String newText) {
private String vscodeIndentFix(TextDocument doc, Position start, String newText) {
//Vscode applies some magic indent to a multi-line edit text. We do everything ourself so we have adjust for the magic
// and do some kind of 'inverse magic' here.
int vscodeMagicIndent = start.getCharacter();
return StringUtil.stripIndentation(vscodeMagicIndent, newText);
//See here: https://github.com/Microsoft/language-server-protocol/issues/83
int referenceLine = start.getLine();
int referenceLineIndent = doc.getLineIndentation(referenceLine);
int vscodeMagicIndent = Math.min(start.getCharacter(), referenceLineIndent);
return vscodeMagicIndent>0
? StringUtil.stripIndentation(vscodeMagicIndent, newText)
: newText;
}
@Override
public CompletableFuture<CompletionItem> resolveCompletion(CompletionItem unresolved) {
//TODO: item is pre-resoved so we don't do anything, but we really should somehow defer some work, such as

View File

@@ -208,4 +208,47 @@ public class TextDocument implements IDocument {
return "TextDocument(uri="+uri+",\n"+this.text+"\n)";
}
/**
* Returns the number of leading spaces in front of a line. If the line only contains spaces then
* this returns the number of spaces the line contains.
* <p>
* This may return -1 if, for some reason, a line's indentation cannot be determined (e.g. the line does
* not exist in the document)
*/
public int getLineIndentation(int line) {
//TODO: this works fine only if we assume all indentation is done with spaces only.
// To generalize this it should probably return a String containing exactly the spaces
// and tabs at the front of the line.
IRegion r = getLineInformation(line);
if (r==null) {
//not a line in the document so it has no indentation
return -1;
}
int len = r.getLength();
int startOfLine = r.getOffset();
int leadingSpaces = 0;
while (leadingSpaces<len) {
char c = getSafeChar(startOfLine+leadingSpaces);
if (c==' ') {
leadingSpaces++;
} else if (c!=' ') {
return leadingSpaces;
}
leadingSpaces++;
}
return leadingSpaces;
}
/**
* Like getChar but never throws {@link BadLocationException}. Instead it
* return (char)0 for offsets outside the document.
*/
public char getSafeChar(int offset) {
try {
return getChar(offset);
} catch (BadLocationException e) {
return 0;
}
}
}

View File

@@ -271,9 +271,14 @@ public class Editor {
if (edit!=null) {
String replaceWith = edit.getNewText();
//Apply indentfix, this is magic vscode seems to apply to edits returned by language server. So our harness has to
// mimick that behavior. I'm not sure this fix is really emulating it faithfully as its undocumented :-(
int indentFix = edit.getRange().getStart().getCharacter();
replaceWith = replaceWith.replaceAll("\\n", "\n"+Strings.repeat(" ", indentFix));
// mimick that behavior. See https://github.com/Microsoft/language-server-protocol/issues/83
int referenceLine = edit.getRange().getStart().getLine();
int cursorOffset = edit.getRange().getStart().getCharacter();
String referenceIndent = document.getLineIndentString(referenceLine);
if (cursorOffset<referenceIndent.length()) {
referenceIndent = referenceIndent.substring(0, cursorOffset);
}
replaceWith = replaceWith.replaceAll("\\n", "\n"+referenceIndent);
int cursorReplaceOffset = replaceWith.indexOf(VS_CODE_CURSOR_MARKER);
if (cursorReplaceOffset>=0) {

View File

@@ -142,4 +142,24 @@ public class TextDocumentInfo {
return id;
}
public String getLineIndentString(int line) {
int start = startOfLine(line);
int scan = start;
char c = getSafeChar(scan);
StringBuilder indentStr = new StringBuilder();
while (c==' '|| c=='\t') {
indentStr.append(c);
c = getSafeChar(++scan);
}
return indentStr.toString();
}
private char getSafeChar(int pos) {
String text = getText();
if (pos>0 && pos<text.length()) {
return text.charAt(pos);
}
return 0;
}
}

View File

@@ -158,6 +158,22 @@ public class PipelineYamlEditorTest {
" <*>"
);
}
@Test
public void PT_136196057_do_step_completion_indentation() throws Exception {
assertCompletions(
"jobs:\n" +
"- name:\n"+
" plan:\n" +
" - do<*>"
, // =>
"jobs:\n" +
"- name:\n"+
" plan:\n" +
" - do:\n" +
" - <*>"
);
}
@Test
public void primaryStepHovers() throws Exception {