From 775d715c35c7c1e14f8fd1d8c4106af4f119832f Mon Sep 17 00:00:00 2001 From: Kris De Volder Date: Wed, 2 Aug 2017 10:42:03 -0700 Subject: [PATCH] Fix but around dedented snippets and completions --- .../ide/vscode/bosh/BoshEditorTest.java | 81 ++++++++++++++++++- .../ide/vscode/commons/util/StringUtil.java | 18 ++++- .../yaml/completion/YamlCompletionEngine.java | 6 ++ .../commons/yaml/util/YamlIndentUtil.java | 29 ++++++- .../languageserver/testharness/Editor.java | 19 +---- 5 files changed, 131 insertions(+), 22 deletions(-) diff --git a/headless-services/bosh-language-server/src/test/java/org/springframework/ide/vscode/bosh/BoshEditorTest.java b/headless-services/bosh-language-server/src/test/java/org/springframework/ide/vscode/bosh/BoshEditorTest.java index ac8c1e902..c49ede0e2 100644 --- a/headless-services/bosh-language-server/src/test/java/org/springframework/ide/vscode/bosh/BoshEditorTest.java +++ b/headless-services/bosh-language-server/src/test/java/org/springframework/ide/vscode/bosh/BoshEditorTest.java @@ -1628,7 +1628,7 @@ public class BoshEditorTest { "instance_groups:\n" + "- name: $10\n" + " azs:\n" + - " - $11\n" + + " - $11\n" + " instances: $12\n" + " jobs:\n" + " - name: $13\n" + @@ -1641,7 +1641,7 @@ public class BoshEditorTest { "instance_groups:\n" + "- name: $1\n" + " azs:\n" + - " - $2\n" + + " - $2\n" + " instances: $3\n" + " jobs:\n" + " - name: $4\n" + @@ -1738,6 +1738,81 @@ public class BoshEditorTest { ); } + @Test public void snippet_dedented() throws Exception { + Editor editor; + editor = harness.newEditor( + "name: \n" + + "variables:\n" + + "- name: voo\n" + + " type: aaa\n" + + "<*>" + ); + editor.assertContextualCompletions(LanguageId.BOSH_DEPLOYMENT, DEDENTED_COMPLETION.and(SNIPPET_COMPLETION), + " <*>" + , // ==> + "instance_groups:\n" + + "- name: $1\n" + + " azs:\n" + + " - $2\n" + + " instances: $3\n" + + " jobs:\n" + + " - name: $4\n" + + " release: $5\n" + + " vm_type: $6\n" + + " stemcell: $7\n" + + " networks:\n" + + " - name: $8<*>" + , //========= + "releases:\n" + + "- name: $1\n" + + " version: $2<*>" + , //======== + "stemcells:\n" + + "- alias: $1\n" + + " version: $2<*>" + , //======== + "update:\n" + + " canaries: $1\n" + + " max_in_flight: $2\n" + + " canary_watch_time: $3\n" + + " update_watch_time: $4<*>" + , //======== + "- name: $1\n" + + " type: $2<*>" + ); + } + + @Test public void relaxedCALessSpaces() throws Exception { + Editor editor; + editor = harness.newEditor( + "name: \n" + + "variables:\n" + + "- name: voo\n" + + " type: aaa\n" + + "<*>" + ); + editor.assertContextualCompletions(LanguageId.BOSH_DEPLOYMENT, DEDENTED_COMPLETION.and(SNIPPET_COMPLETION.negate()), + " <*>" + , //==> + "instance_groups:\n" + + "- name: <*>" + , //---- + "releases:\n" + + "- name: <*>" + , //---- + "stemcells:\n" + + "- <*>" + , //--- + "tags:\n" + + " <*>" + , //--- + "update:\n" + + " <*>" + , //--- + "- name: <*>" + ); + } + @Test public void relaxedCAmoreSpaces() throws Exception { Editor editor = harness.newEditor( "name: foo\n" + @@ -1753,7 +1828,7 @@ public class BoshEditorTest { ); } - @Test @Ignore public void keyCompletionThatNeedANewline() throws Exception { + @Test @Ignore public void keyCompletionThatNeedsANewline() throws Exception { Editor editor = harness.newEditor( "name: foo\n" + "update: canwa<*>" diff --git a/headless-services/commons/commons-util/src/main/java/org/springframework/ide/vscode/commons/util/StringUtil.java b/headless-services/commons/commons-util/src/main/java/org/springframework/ide/vscode/commons/util/StringUtil.java index d4c3c20f6..a6e80cef8 100644 --- a/headless-services/commons/commons-util/src/main/java/org/springframework/ide/vscode/commons/util/StringUtil.java +++ b/headless-services/commons/commons-util/src/main/java/org/springframework/ide/vscode/commons/util/StringUtil.java @@ -12,9 +12,11 @@ package org.springframework.ide.vscode.commons.util; import java.text.SimpleDateFormat; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Date; +import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -185,5 +187,19 @@ public class StringUtil { return line.substring(start); } - + public static String[] split(String string, char c) { + //Why not use String.split? Because when the string being split ends with separator, it drops the final + // empty string. But... we need that empty string! I.e. we want the number of pieces to allways be equal + // to the number of separators + 1, even if it means some of the Strings are "" + List pieces = new ArrayList<>(); + int start = 0; + int next = string.indexOf(c); + while (next>=0) { + pieces.add(string.substring(start, next)); + start = next+1; + next = string.indexOf(c, start); + } + pieces.add(string.substring(start)); + return pieces.toArray(new String[pieces.size()]); + } } diff --git a/headless-services/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/completion/YamlCompletionEngine.java b/headless-services/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/completion/YamlCompletionEngine.java index 6cb30cc96..3f6004d01 100644 --- a/headless-services/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/completion/YamlCompletionEngine.java +++ b/headless-services/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/completion/YamlCompletionEngine.java @@ -193,6 +193,7 @@ public class YamlCompletionEngine implements ICompletionEngine { int spacesStart = spacesEnd-numSpacesToRemove; int numArrows = numSpacesToRemove / YamlIndentUtil.INDENT_BY; String spaces = new DocumentRegion(doc, spacesStart, spacesEnd).toString(); + YamlIndentUtil indenter = new YamlIndentUtil(doc); if (spaces.length()==numSpacesToRemove && SPACES.matcher(spaces).matches()) { ScoreableProposal transformed = new TransformedCompletion(proposal) { @Override public String tranformLabel(String originalLabel) { @@ -200,6 +201,11 @@ public class YamlCompletionEngine implements ICompletionEngine { } @Override public DocumentEdits transformEdit(DocumentEdits originalEdit) { originalEdit.firstDelete(spacesStart, spacesEnd); + originalEdit.transformFirstNonWhitespaceEdit((offset, insertText) -> { + String prefix = insertText.substring(0, offset); + String dedented = indenter.applyIndentation(insertText.substring(offset), -numSpacesToRemove); + return prefix + dedented; + }); return originalEdit; } @Override diff --git a/headless-services/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/util/YamlIndentUtil.java b/headless-services/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/util/YamlIndentUtil.java index 8fe013109..51f44e002 100644 --- a/headless-services/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/util/YamlIndentUtil.java +++ b/headless-services/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/util/YamlIndentUtil.java @@ -11,6 +11,7 @@ package org.springframework.ide.vscode.commons.yaml.util; import org.springframework.ide.vscode.commons.util.Assert; +import org.springframework.ide.vscode.commons.util.StringUtil; import org.springframework.ide.vscode.commons.util.text.IDocument; import org.springframework.ide.vscode.commons.yaml.structure.YamlDocument; @@ -89,9 +90,35 @@ public class YamlIndentUtil { * Notes: * - '\n' are replaced by the default line delimeter for the current document. * - indentation is not applied to the first line of text. + * - negative indentations are support and result in removing upto that number of spaces after each newline */ public String applyIndentation(String text, int indentBy) { - return text.replaceAll("\\n", newlineWithIndent(indentBy)); + if (indentBy>0) { + return text.replaceAll("\\n", newlineWithIndent(indentBy)); + } else if (indentBy<0) { + int dedentBy = - indentBy; + StringBuilder dedented = new StringBuilder(); + boolean first = true; + for (String line : StringUtil.split(text, '\n')) { + if (!first) { + dedented.append('\n'); + line = dedentLine(line, dedentBy); + } + dedented.append(line); + first = false; + } + return dedented.toString(); + } else { // indentBy==0 + return text; + } + } + + private String dedentLine(String line, int dedentBy) { + int i = 0; + while (i pieces = new ArrayList<>(); - int start = 0; - int next = string.indexOf(c); - while (next>=0) { - pieces.add(string.substring(start, next)); - start = next+1; - next = string.indexOf(c, start); - } - pieces.add(string.substring(start)); - return pieces.toArray(new String[pieces.size()]); - } - private String getText(Position start, int length) { int offset = doc.toOffset(start); String text = doc.getText();