From e6c7ee2aaada99cf05e8b461c010a1a1ca55aec5 Mon Sep 17 00:00:00 2001 From: Kris De Volder Date: Fri, 16 Dec 2016 14:16:49 -0800 Subject: [PATCH] Some refactorings towards a fix. --- .../yaml/completion/YamlCompletionEngine.java | 69 ++++++++++++++++--- .../boot/test/ApplicationYamlEditorTest.java | 29 ++++++++ 2 files changed, 87 insertions(+), 11 deletions(-) diff --git a/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/completion/YamlCompletionEngine.java b/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/completion/YamlCompletionEngine.java index 435168570..a86cc1045 100644 --- a/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/completion/YamlCompletionEngine.java +++ b/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/completion/YamlCompletionEngine.java @@ -64,15 +64,17 @@ public class YamlCompletionEngine implements ICompletionEngine { if (!doc.isCommented(offset)) { SRootNode root = doc.getStructure(); SNode current = root.find(offset); - YamlPath contextPath = getContextPath(doc, current, offset); - YamlAssistContext context = getContext(doc, offset, current, contextPath); - if (context==null && isDubiousKey(current, offset)) { - current = current.getParent(); - contextPath = contextPath.dropLast(); - context = getContext(doc, offset, current, contextPath); - } - if (context!=null) { - return context.getCompletions(doc, current, offset); + SNode contextNode = getContextNode(doc, current, offset); + if (contextNode!=null) { + YamlAssistContext context = getContext(doc, contextNode); + if (context==null && isDubiousKey(contextNode, offset)) { + current = current.getParent(); + contextNode = contextNode.getParent(); + context = getContext(doc, contextNode); + } + if (context!=null) { + return context.getCompletions(doc, current, offset); + } } } return Collections.emptyList(); @@ -91,12 +93,57 @@ public class YamlCompletionEngine implements ICompletionEngine { return false; } - protected YamlAssistContext getContext(YamlDocument doc, int offset, SNode node, YamlPath contextPath) { + protected YamlAssistContext getContext(YamlDocument doc, SNode contextNode) { try { - return contextPath.traverse(getGlobalContext(doc)); + if (contextNode!=null) { + YamlPath contextPath = contextNode.getPath(); + return contextPath.traverse(getGlobalContext(doc)); + } } catch (Exception e) { logger.error("Error obtaining YamlAssistContext", e); + } + return null; + } + + protected SNode getContextNode(YamlDocument doc, SNode node, int offset) throws Exception { + if (node==null) { return null; + } else if (node.getNodeType()==SNodeType.KEY) { + //slight complication. The area in the key and value of a key node represent different + // contexts for content assistance + SKeyNode keyNode = (SKeyNode)node; + if (keyNode.isInValue(offset)) { + return keyNode; + } else { + return keyNode.getParent(); + } + } else if (node.getNodeType()==SNodeType.RAW) { + //Treat raw node as a 'key node'. This is basically assuming that is misclasified + // by structure parser because the ':' was not yet typed into the document. + + //Complication: if line with cursor is empty or the cursor is inside the indentation + // area then the structure may not reflect correctly the context. This is because + // the correct context depends on text the user has not typed yet.(which will change the + // indentation level of the current line. So we must use the cursorIndentation + // rather than the structure-tree to determine the 'context' node. + int cursorIndent = doc.getColumn(offset); + int nodeIndent = node.getIndent(); + int currentIndent = YamlIndentUtil.minIndent(cursorIndent, nodeIndent); + while (node.getIndent()==-1 || (node.getIndent()>=currentIndent && node.getNodeType()!=SNodeType.DOC)) { + node = node.getParent(); + } + return node; + } else if (node.getNodeType()==SNodeType.SEQ) { + SSeqNode seqNode = (SSeqNode)node; + if (seqNode.isInValue(offset)) { + return seqNode; + } else { + return seqNode.getParent(); + } + } else if (node.getNodeType()==SNodeType.DOC) { + return node; + } else { + throw new IllegalStateException("Missing case"); } } diff --git a/vscode-extensions/vscode-boot-properties/src/test/java/org/springframework/ide/vscode/boot/test/ApplicationYamlEditorTest.java b/vscode-extensions/vscode-boot-properties/src/test/java/org/springframework/ide/vscode/boot/test/ApplicationYamlEditorTest.java index dc209313d..07ec7e162 100644 --- a/vscode-extensions/vscode-boot-properties/src/test/java/org/springframework/ide/vscode/boot/test/ApplicationYamlEditorTest.java +++ b/vscode-extensions/vscode-boot-properties/src/test/java/org/springframework/ide/vscode/boot/test/ApplicationYamlEditorTest.java @@ -3155,6 +3155,35 @@ public class ApplicationYamlEditorTest extends AbstractPropsEditorTest { } + /** + * TODO: Delete this tempotary test. + * This test is just one piece copied from another test to do more focussed debugging. If you find this test + * in the git repo, then it was committed by accident. So feel free to delete it. + */ + @Test public void testClasspathResourceCompletionTemp() throws Exception { + CachingValueProvider.TIMEOUT = Duration.ofSeconds(20); + + useProject(createPredefinedMavenProject("empty-boot-1.3.0-app")); + + data("my.nice.resource", "org.springframework.core.io.Resource", null, "A very nice resource."); + data("my.nice.list", "java.util.List", null, "A nice list of resources."); + + assertCompletionWithLabel( + "my:\n" + + " nice:\n" + + " list:\n"+ + " - classpath:<*>\n" + ,// ========== + "classpath:application.yml" + , // => + "my:\n" + + " nice:\n" + + " list:\n"+ + " - classpath:application.yml<*>\n" + ); + + } + @Test public void testClasspathResourceCompletion() throws Exception { CachingValueProvider.TIMEOUT = Duration.ofSeconds(20);