Deal better with anchors and references in outline

See: https://github.com/spring-projects/sts4/issues/483

Signed-off-by: Kris De Volder <kdevolder@pivotal.io>
This commit is contained in:
Kris De Volder
2020-11-13 16:28:26 -08:00
parent 4e8e3e59a6
commit 2a02e28d85
5 changed files with 91 additions and 10 deletions

View File

@@ -359,4 +359,8 @@ public class DocumentRegion implements CharSequence, IRegion {
return charBefore.equals("\n"); //This should work on windows too because windows uses "\r\n" but that still ends with "\n".
}
public boolean contains(DocumentRegion inner) {
return start<=inner.start && inner.end <= end;
}
}

View File

@@ -12,7 +12,9 @@ package org.springframework.ide.vscode.commons.yaml.path;
import java.util.stream.Stream;
import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector;
import org.springframework.ide.vscode.commons.util.Assert;
import org.springframework.ide.vscode.commons.yaml.ast.NodeMergeSupport;
import org.springframework.ide.vscode.commons.yaml.ast.NodeUtil;
import org.yaml.snakeyaml.nodes.MappingNode;
import org.yaml.snakeyaml.nodes.Node;
@@ -29,6 +31,8 @@ public class NodeCursor extends ASTCursor {
private final Node currentNode;
private NodeMergeSupport nodeMerger = new NodeMergeSupport(IProblemCollector.NULL);
public NodeCursor(Node node) {
Assert.isNotNull(node);
this.currentNode = node;
@@ -41,7 +45,7 @@ public class NodeCursor extends ASTCursor {
switch (s.getType()) {
case KEY_AT_KEY: {
String key = s.toPropString();
MappingNode mappingNode = NodeUtil.asMapping(getNode());
MappingNode mappingNode = asMapping(getNode());
if (mappingNode!=null) {
return mappingNode.getValue().stream()
.filter((c) -> key.equals(NodeUtil.asScalar(c.getKeyNode())))
@@ -51,7 +55,7 @@ public class NodeCursor extends ASTCursor {
return Stream.empty();
}
case ANY_CHILD: {
MappingNode mappingNode = NodeUtil.asMapping(getNode());
MappingNode mappingNode = asMapping(getNode());
if (mappingNode!=null) {
return mappingNode.getValue().stream()
.map((c) -> new NodeCursor(c.getValueNode()));
@@ -72,7 +76,7 @@ public class NodeCursor extends ASTCursor {
return Stream.empty();
}
case VAL_AT_KEY: {
MappingNode mappingNode = NodeUtil.asMapping(getNode());
MappingNode mappingNode = asMapping(getNode());
if (mappingNode!=null) {
String key = s.toPropString();
return mappingNode.getValue().stream()
@@ -87,6 +91,16 @@ public class NodeCursor extends ASTCursor {
}
}
private MappingNode asMapping(Node node) {
MappingNode mapping = NodeUtil.asMapping(node);
if (mapping!=null) {
nodeMerger.flattenMapping(mapping);
}
return mapping;
}
@Override
public Node getNode() {
return currentNode;

View File

@@ -24,6 +24,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ide.vscode.commons.languageserver.util.HierarchicalDocumentSymbolHandler;
import org.springframework.ide.vscode.commons.util.Assert;
import org.springframework.ide.vscode.commons.util.text.DocumentRegion;
import org.springframework.ide.vscode.commons.util.text.IDocument;
import org.springframework.ide.vscode.commons.yaml.ast.NodeUtil;
import org.springframework.ide.vscode.commons.yaml.ast.YamlFileAST;
@@ -76,9 +77,16 @@ public class TypeBasedYamlHierarchicalSymbolHandler implements HierarchicalDocum
if (namePath!=null) {
Node nameNode = namePath.traverseNode(node);
if (nameNode!=null) {
DocumentRegion nodeRegion = NodeUtil.region(doc, node);
DocumentRegion nameRegion = NodeUtil.region(doc, nameNode);
if (!nodeRegion.contains(nameRegion)) {
//This violates the expectation that most clients have.
//vscode, for example drops the entire symbols hierarchy as 'invalid' if this happens
nodeRegion = nameRegion;
}
return new DocumentSymbol(NodeUtil.asScalar(nameNode), kind,
NodeUtil.region(doc, node).asRange(),
NodeUtil.region(doc, nameNode).asRange(),
nodeRegion.asRange(),
nameRegion.asRange(),
detail
);
}
@@ -151,7 +159,9 @@ public class TypeBasedYamlHierarchicalSymbolHandler implements HierarchicalDocum
@Override
public List<? extends DocumentSymbol> handleHierarchic(DocumentSymbolParams params) {
return outlineByUri.get(params.getTextDocument().getUri());
List<DocumentSymbol> symbols = outlineByUri.get(params.getTextDocument().getUri());
log.info("hierarchical symbols: {}", symbols);
return symbols;
}
@Override
@@ -168,10 +178,12 @@ public class TypeBasedYamlHierarchicalSymbolHandler implements HierarchicalDocum
if (def!=null) {
Item parent = findParent(path);
DocumentSymbol sym = def.createSymbol(currentAst, node, type, path);
if (parent!=null) {
parent.addChild(sym);
} else {
rootSymbols.add(sym);
if (sym!=null) {
if (parent!=null) {
parent.addChild(sym);
} else {
rootSymbols.add(sym);
}
}
stack.push(new Item(path, sym));
}

View File

@@ -55,6 +55,7 @@ import org.junit.Assert;
import org.springframework.ide.vscode.commons.protocol.HighlightParams;
import org.springframework.ide.vscode.commons.util.StringUtil;
import org.springframework.ide.vscode.commons.util.Unicodes;
import org.springframework.ide.vscode.commons.util.text.DocumentRegion;
import org.springframework.ide.vscode.commons.util.text.LanguageId;
import com.google.common.collect.ImmutableList;
@@ -991,12 +992,36 @@ public class Editor {
symbolDump.append(s.getDetail());
symbolDump.append("\n");
assertEquals(s.getName(), getText(s.getSelectionRange()));
assertNestedRange("Invalid selection range for "+s.getName(), s.getRange(), s.getSelectionRange());
List<DocumentSymbol> children = s.getChildren();
if (children!=null) {
dumpSymbols(children, indent+1, symbolDump);
}
}
private void assertNestedRange(String msg, Range outerRange, Range innerRange) {
boolean startOk = compare(outerRange.getStart(), innerRange.getStart()) <= 0;
boolean endOk = compare(outerRange.getEnd(), innerRange.getEnd()) >= 0;
if (startOk && endOk) {
//it's fine!
} else {
fail(msg + "\n" +
"outer: '"+this.getText(outerRange)+"'\n" +
"does not contain\n" +
"inner: '"+this.getText(innerRange)+"'"
);
}
}
private static int compare(Position p1, Position p2) {
if (p1.getLine()==p2.getLine()) {
return p1.getCharacter() - p2.getCharacter();
} else {
return p1.getLine() - p2.getLine();
}
}
private List<? extends DocumentSymbol> getHierarchicalDocumentSymbols() throws Exception {
return harness.getHierarchicalDocumentSymbols(this.doc);
}

View File

@@ -27,6 +27,7 @@ import java.util.stream.Collectors;
import org.eclipse.lsp4j.CompletionItem;
import org.eclipse.lsp4j.Diagnostic;
import org.eclipse.lsp4j.DiagnosticSeverity;
import org.eclipse.lsp4j.DocumentSymbol;
import org.eclipse.lsp4j.InsertTextFormat;
import org.junit.Before;
import org.junit.Ignore;
@@ -240,6 +241,31 @@ public class ConcourseEditorTest {
InputStream stream = ConcourseEditorTest.class.getClassLoader().getResourceAsStream(resourceName);
return IOUtil.toString(stream);
}
@Test
public void outlineWithAnchors() throws Exception {
harness.enableHierarchicalDocumentSymbols(true);
//See: https://github.com/spring-projects/sts4/issues/483
Editor editor = harness.newEditor(
"def: &stuff\n" +
" name: git\n" +
" type: git\n" +
" source:\n" +
" uri: \n" +
"resources:\n" +
"- <<: *stuff\n" +
"- name: git2\n" +
" type: git\n" +
" source:\n" +
" uri: git@github.com:kdvolder/7zip.git"
);
editor.assertHierarchicalDocumentSymbols(
"resources::Resources\n" +
" git::Resource\n" +
" git2::Resource\n"
);
}
@Test
public void reconcileStructuralProblems() throws Exception {