diff --git a/headless-services/commons/commons-util/src/main/java/org/springframework/ide/vscode/commons/util/text/TextDocument.java b/headless-services/commons/commons-util/src/main/java/org/springframework/ide/vscode/commons/util/text/TextDocument.java index 11b723dbf..4a820e449 100644 --- a/headless-services/commons/commons-util/src/main/java/org/springframework/ide/vscode/commons/util/text/TextDocument.java +++ b/headless-services/commons/commons-util/src/main/java/org/springframework/ide/vscode/commons/util/text/TextDocument.java @@ -28,7 +28,7 @@ import javolution.text.Text; public class TextDocument implements IDocument { ILineTracker lineTracker = new DefaultLineTracker(); - private static final Pattern NEWLINE = Pattern.compile("\\r|\\n|\\r\\n|\\n\\r"); + private static final Pattern NEWLINE = Pattern.compile("\\n|\\r\\n"); private final LanguageId languageId; private final String uri; diff --git a/headless-services/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/completion/AppendTextBuilder.java b/headless-services/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/completion/AppendTextBuilder.java index 7a1ffa3da..3f23301ab 100644 --- a/headless-services/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/completion/AppendTextBuilder.java +++ b/headless-services/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/completion/AppendTextBuilder.java @@ -14,6 +14,7 @@ import org.springframework.ide.vscode.commons.util.Streams; import org.springframework.ide.vscode.commons.yaml.schema.YType; import org.springframework.ide.vscode.commons.yaml.schema.YTypeUtil; import org.springframework.ide.vscode.commons.yaml.schema.YTypedProperty; +import org.springframework.ide.vscode.commons.yaml.structure.YamlDocument; import org.springframework.ide.vscode.commons.yaml.util.YamlIndentUtil; /** @@ -29,7 +30,7 @@ public class AppendTextBuilder { this.typeUtil = typeUtil; } - public String buildFor(YType type) { + public String buildFor(YType type, YamlDocument doc) { //Note that caller is responsible for proper indentation //to align with the parent. The strings created here only need to contain //indentation spaces to indent *more* than the parent node. @@ -39,22 +40,22 @@ public class AppendTextBuilder { //(or potentially do a lot of string copying) StringBuilder text = new StringBuilder(); - build(type, 0, text); + build(type, 0, text, doc); return text.toString(); } - private void build(YType type, int indent, StringBuilder text) { + private void build(YType type, int indent, StringBuilder text, YamlDocument doc) { if (type==null) { //Assume its some kind of pojo bean - newline(text, indent+YamlIndentUtil.INDENT_BY); + newline(text, indent+YamlIndentUtil.INDENT_BY, doc); } else if (typeUtil.isMap(type)) { //ready to enter nested map key on next line - newline(text, indent+YamlIndentUtil.INDENT_BY); + newline(text, indent+YamlIndentUtil.INDENT_BY, doc); } else if (typeUtil.isSequencable(type)) { //ready to enter sequence element on next line - newline(text, indent); + newline(text, indent, doc); text.append("- "); - singleMostImportantProperty(typeUtil.getDomainType(type), indent+2, text); + singleMostImportantProperty(typeUtil.getDomainType(type), indent+2, text, doc); //Yes using 2 here instead of YamlIndentUtil.INDENT_BY is deliberate. It's the same value (now), // but the 2 used here is the width of the "- " which should determine nested indent level for things to // line up properly. @@ -62,11 +63,11 @@ public class AppendTextBuilder { //ready to enter whatever on the same line text.append(" "); } else { - newline(text, indent+YamlIndentUtil.INDENT_BY); + newline(text, indent+YamlIndentUtil.INDENT_BY, doc); } } - private void singleMostImportantProperty(YType type, int indent, StringBuilder text) { + private void singleMostImportantProperty(YType type, int indent, StringBuilder text, YamlDocument doc) { if (type!=null) { YTypedProperty singleProp = Streams.getSingle(typeUtil.getProperties(type).stream() .filter(p -> p.isPrimary())); @@ -77,13 +78,13 @@ public class AppendTextBuilder { if (singleProp!=null) { text.append(singleProp.getName()); text.append(':'); - build(singleProp.getType(), indent+YamlIndentUtil.INDENT_BY, text); + build(singleProp.getType(), indent+YamlIndentUtil.INDENT_BY, text, doc); } } } - private void newline(StringBuilder text, int indent) { - text.append("\n"); + private void newline(StringBuilder text, int indent, YamlDocument doc) { + text.append(doc == null || doc.getDocument() == null ? System.lineSeparator() : doc.getDocument().getDefaultLineDelimiter()); for (int i = 0; i < indent; i++) { text.append(' '); } diff --git a/headless-services/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/completion/YTypeAssistContext.java b/headless-services/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/completion/YTypeAssistContext.java index f029e6dd1..d0148d317 100644 --- a/headless-services/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/completion/YTypeAssistContext.java +++ b/headless-services/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/completion/YTypeAssistContext.java @@ -201,7 +201,7 @@ public class YTypeAssistContext extends AbstractYamlAssistContext { } snippet.append(p.getName()); snippet.append(":"); - snippet.append(appendTextFor(YType)); + snippet.append(appendTextFor(YType, doc)); edits.insert(queryOffset, indenter.applyIndentation(snippet.toString(), referenceIndent)); } ICompletionProposal completion = completionFactory().beanProperty(doc.getDocument(), @@ -262,9 +262,10 @@ public class YTypeAssistContext extends AbstractYamlAssistContext { /** * Computes the text that should be appended at the end of a completion * proposal depending on what type of value is expected. + * @param doc */ - protected String appendTextFor(YType type) { - return new AppendTextBuilder(typeUtil).buildFor(type); + protected String appendTextFor(YType type, YamlDocument doc) { + return new AppendTextBuilder(typeUtil).buildFor(type, doc); } private List getValueCompletions(YamlDocument doc, SNode node, int offset, String query) { @@ -461,7 +462,7 @@ public class YTypeAssistContext extends AbstractYamlAssistContext { @Override protected DocumentEdits transformEdit(DocumentEdits textEdit) { textEdit.transformFirstNonWhitespaceEdit((Integer offset, String insertText) -> { - YamlIndentUtil indenter = new YamlIndentUtil("\n"); + YamlIndentUtil indenter = new YamlIndentUtil(doc); if (needNewline(textEdit)) { return insertText.substring(0, offset) + "\n" +Strings.repeat(" ", node.getIndent())+"- " 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 9e500241b..94f7673cd 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 @@ -114,7 +114,7 @@ public class YamlCompletionEngine implements ICompletionEngine { protected Collection getRelaxedCompletions(int offset, YamlDocument doc, SNode current, SNode contextNode, int baseIndent, double deempasizeBy) { try { return fixIndentations(getBaseCompletions(offset, doc, current, contextNode), - current, contextNode, baseIndent, deempasizeBy); + current, contextNode, baseIndent, deempasizeBy, doc); } catch (Exception e) { Log.log(e); } @@ -122,14 +122,14 @@ public class YamlCompletionEngine implements ICompletionEngine { } protected Collection fixIndentations(Collection completions, SNode currentNode, - SNode contextNode, int baseIndent, double deempasizeBy) { + SNode contextNode, int baseIndent, double deempasizeBy, YamlDocument doc) { if (!completions.isEmpty()) { int dashyIndent = getTargetIndent(contextNode, currentNode, true); int plainIndent = getTargetIndent(contextNode, currentNode, false); List transformed = new ArrayList<>(); for (ICompletionProposal p : completions) { int targetIndent = p.getLabel().startsWith("- ") ? dashyIndent : plainIndent; - ScoreableProposal p_fixed = indentFix((ScoreableProposal)p, targetIndent - baseIndent, currentNode, contextNode); + ScoreableProposal p_fixed = indentFix((ScoreableProposal)p, targetIndent - baseIndent, currentNode, contextNode, doc); if (p_fixed!=null) { p_fixed.deemphasize(deempasizeBy); transformed.add(p_fixed); @@ -140,12 +140,12 @@ public class YamlCompletionEngine implements ICompletionEngine { return Collections.emptyList(); } - protected ScoreableProposal indentFix(ScoreableProposal p, int fixIndentBy, SNode currentNode, SNode contextNode) { + protected ScoreableProposal indentFix(ScoreableProposal p, int fixIndentBy, SNode currentNode, SNode contextNode, YamlDocument doc) { if (fixIndentBy==0) { return p; } else if (fixIndentBy>0) { if (isExtraIndentRelaxable(contextNode, fixIndentBy)) { - return indented(p, Strings.repeat(" ", fixIndentBy)); + return indented(p, Strings.repeat(" ", fixIndentBy), doc); } } else { // fixIndentBy < 0 if (isLesserIndentRelaxable(currentNode, contextNode)) { @@ -228,7 +228,7 @@ public class YamlCompletionEngine implements ICompletionEngine { return null; } - public ScoreableProposal indented(ICompletionProposal proposal, String indentStr) { + public ScoreableProposal indented(ICompletionProposal proposal, String indentStr, YamlDocument doc) { int numArrows = (indentStr.length()+1)/2; ScoreableProposal transformed = new TransformedCompletion(proposal) { @Override public String tranformLabel(String originalLabel) { @@ -236,7 +236,7 @@ public class YamlCompletionEngine implements ICompletionEngine { } @Override public DocumentEdits transformEdit(DocumentEdits originalEdit) { // originalEdit.indentFirstEdit(indentStr); - YamlIndentUtil indenter = new YamlIndentUtil("\n"); + YamlIndentUtil indenter = new YamlIndentUtil(doc); if (originalEdit.hasRelativeIndents()) { originalEdit.transformFirstNonWhitespaceEdit((Integer offset, String insertText) -> { String prefix = insertText.substring(0, offset); 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 573dd4c0f..c7d00da50 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 @@ -40,7 +40,7 @@ public class YamlIndentUtil { */ public final String NEWLINE; - public YamlIndentUtil(String newline) { + private YamlIndentUtil(String newline) { this.NEWLINE = newline; Assert.isNotNull(NEWLINE); } diff --git a/headless-services/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/Editor.java b/headless-services/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/Editor.java index ca53dcc61..6137080bc 100644 --- a/headless-services/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/Editor.java +++ b/headless-services/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/Editor.java @@ -241,7 +241,7 @@ public class Editor { if (selectionStart\n"); + editor.assertContextualCompletions(ci -> ci.getLabel().contains("spring.activemq.close-timeout"), + "springactcloti<*>", "spring:\n" + " activemq:\n" + " close-timeout: <*>" @@ -1423,7 +1424,9 @@ public class ApplicationYamlEditorTest extends AbstractPropsEditorTest { @Test public void testContentAssistSimple() throws Exception { defaultTestData(); - assertCompletion("port<*>", + Editor editor = newEditor("<*>\n"); + editor.assertContextualCompletions(ci -> ci.getLabel().contains("server.port"), + "port<*>", "server:\n"+ " port: <*>"); assertCompletion( @@ -1450,6 +1453,21 @@ public class ApplicationYamlEditorTest extends AbstractPropsEditorTest { // => nothing ); } + + /** + * https://github.com/spring-projects/sts4/issues/709 + */ + @Test public void testGH709() throws Exception { + data("server.port", "java.lang.Integer", null, "Server http port"); + + Editor editor = newEditor("logging:\r\n" + + " enabled: true\r\n" + + "ser<*>"); + editor.assertCompletions("logging:\r\n" + + " enabled: true\r\n" + + "server:\r\n" + + " port: <*>"); + } @Test public void testContentAssistNested() throws Exception { data("server.port", "java.lang.Integer", null, "Server http port"); @@ -1881,7 +1899,8 @@ public class ApplicationYamlEditorTest extends AbstractPropsEditorTest { defaultTestData(); //Ensure this test is not trivially passing because of missing test data - assertCompletion( + Editor editor = newEditor("<*>\n"); + editor.assertContextualCompletions(ci -> ci.getLabel().contains("server.port"), "po<*>" , "server:\n"+ @@ -2237,7 +2256,9 @@ public class ApplicationYamlEditorTest extends AbstractPropsEditorTest { useProject(createPredefinedMavenProject("enums-boot-1.3.2-app")); data("foo.color", "demo.Color", null, "A foonky colour"); - assertCompletion("foo.c<*>", + Editor editor = newEditor("<*>\n"); + editor.assertContextualCompletions(ci -> ci.getLabel().equals("foo.color"), + "foo.c<*>", "foo:\n" + " color: <*>" //Should complete on same line because enums are 'simple' values. ); @@ -2381,11 +2402,12 @@ public class ApplicationYamlEditorTest extends AbstractPropsEditorTest { ); //Map Enum -> Pojo: - assertCompletions("foo.coldat<*>", + Editor editor = newEditor("<*>\n"); + editor.assertContextualCompletions("foo.coldat<*>", "foo:\n" + " color-data:\n" + " <*>"); - assertCompletions( + editor.assertContextualCompletions( "foo:\n" + " color-data:\n" + " <*>", @@ -2403,7 +2425,7 @@ public class ApplicationYamlEditorTest extends AbstractPropsEditorTest { " red:\n" + " <*>" ); - assertCompletions( + editor.assertContextualCompletions( "foo:\n" + " color-data:\n" + " B<*>", @@ -2414,7 +2436,7 @@ public class ApplicationYamlEditorTest extends AbstractPropsEditorTest { " <*>" ); - assertCompletions( + editor.assertContextualCompletions( "foo:\n" + " color-data:\n" + " b<*>", @@ -2425,7 +2447,7 @@ public class ApplicationYamlEditorTest extends AbstractPropsEditorTest { " <*>" ); - assertCompletions( + editor.assertContextualCompletions( "foo:\n" + " color-data: b<*>", //=>