diff --git a/vscode-extensions/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/completion/VscodeCompletionEngineAdapter.java b/vscode-extensions/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/completion/VscodeCompletionEngineAdapter.java index 67b8b5e60..4c7e8d213 100644 --- a/vscode-extensions/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/completion/VscodeCompletionEngineAdapter.java +++ b/vscode-extensions/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/completion/VscodeCompletionEngineAdapter.java @@ -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 resolveCompletion(CompletionItem unresolved) { //TODO: item is pre-resoved so we don't do anything, but we really should somehow defer some work, such as diff --git a/vscode-extensions/commons/commons-util/src/main/java/org/springframework/ide/vscode/commons/util/text/TextDocument.java b/vscode-extensions/commons/commons-util/src/main/java/org/springframework/ide/vscode/commons/util/text/TextDocument.java index 60f59d7dc..393009f8f 100644 --- a/vscode-extensions/commons/commons-util/src/main/java/org/springframework/ide/vscode/commons/util/text/TextDocument.java +++ b/vscode-extensions/commons/commons-util/src/main/java/org/springframework/ide/vscode/commons/util/text/TextDocument.java @@ -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. + *

+ * 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=0) { diff --git a/vscode-extensions/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/TextDocumentInfo.java b/vscode-extensions/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/TextDocumentInfo.java index 6aa225e86..52066fc43 100644 --- a/vscode-extensions/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/TextDocumentInfo.java +++ b/vscode-extensions/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/TextDocumentInfo.java @@ -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" ); } + + @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 {