Fix issue in SNode.getPath() related to empty lines

See https://www.pivotaltracker.com/story/show/163752179
This commit is contained in:
Kris De Volder
2019-02-06 15:01:56 -08:00
parent 66453d617c
commit 2c1ba5808d
4 changed files with 125 additions and 25 deletions

View File

@@ -13,7 +13,6 @@ package org.springframework.ide.vscode.commons.yaml.path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.springframework.ide.vscode.commons.yaml.ast.NodeRef;
@@ -21,9 +20,7 @@ import org.springframework.ide.vscode.commons.yaml.ast.NodeRef.RootRef;
import org.springframework.ide.vscode.commons.yaml.ast.NodeRef.SeqRef;
import org.springframework.ide.vscode.commons.yaml.ast.NodeRef.TupleValueRef;
import org.springframework.ide.vscode.commons.yaml.ast.NodeUtil;
import org.springframework.ide.vscode.commons.yaml.ast.YamlFileAST;
import org.springframework.ide.vscode.commons.yaml.path.YamlPathSegment.YamlPathSegmentType;
import org.yaml.snakeyaml.nodes.Node;
import reactor.core.publisher.Flux;

View File

@@ -443,7 +443,8 @@ public class YamlStructureParser {
public abstract class SChildBearingNode extends SNode {
private List<SNode> children = null;
private Multimap<String, SNode> keyMap = null; //lazily constructed index of children.
private Multimap<Object, SNode> keyMap = null; //lazily constructed index of children.
private int seqChildren = 0; //Keeps a tally of number of children of type Seq
public SChildBearingNode(SChildBearingNode parent, YamlDocument doc, int indent, int start, int end) {
super(parent, doc, indent, start, end);
@@ -460,6 +461,9 @@ public class YamlStructureParser {
children = new ArrayList<SNode>();
}
children.add(c);
if (c instanceof SSeqNode) {
seqChildren++;
}
}
public SNode getLastChild() {
List<SNode> cs = getChildren();
@@ -519,9 +523,9 @@ public class YamlStructureParser {
private SSeqNode getSeqChildWithIndex(int index) {
if (index>=0) {
List<SNode> children = getChildren();
if (index<children.size()) {
SNode child = children.get(index);
Collection<SNode> children = keyMap().get(index);
if (!children.isEmpty()) {
SNode child = children.iterator().next();
if (child instanceof SSeqNode) {
return (SSeqNode) child;
}
@@ -555,15 +559,19 @@ public class YamlStructureParser {
return (SKeyNode)getChildrenWithKey(key).findFirst().orElse(null);
}
private Multimap<String, SNode> keyMap() {
private Multimap<Object, SNode> keyMap() {
if (keyMap==null) {
ListMultimap<String, SNode> index = MultimapBuilder.hashKeys().arrayListValues().build();
ListMultimap<Object, SNode> index = MultimapBuilder.hashKeys().arrayListValues().build();
for (SNode node: getChildren()) {
try {
if (node.getNodeType()==SNodeType.KEY) {
SKeyNode keyNode = (SKeyNode)node;
String key = ((SKeyNode)node).getKey();
String key = keyNode.getKey();
index.put(key, keyNode);
} else if (node.getNodeType()==SNodeType.SEQ) {
SSeqNode seqNode = (SSeqNode) node;
int key = seqNode.index;
index.put(key, seqNode);
}
} catch (Exception e) {
Log.log(e);
@@ -592,6 +600,10 @@ public class YamlStructureParser {
return null;
}
public int seqChildrenCount() {
return seqChildren;
}
}
public abstract class SLeafNode extends SNode {
@@ -729,7 +741,7 @@ public class YamlStructureParser {
public SSeqNode(SChildBearingNode parent, YamlDocument doc, int indent, int start, int end) throws Exception {
super(parent, doc, indent, start, end);
this.index = parent.getChildren().size()-1;
this.index = parent.seqChildrenCount() - 1;
}
public int getIndex() {

View File

@@ -17,6 +17,7 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.springframework.ide.vscode.commons.yaml.path.YamlPathSegment.*;
import java.util.ArrayList;
import java.util.regex.Pattern;
@@ -610,20 +611,20 @@ public class YamlStructureParserTest {
" moonstone\n"
);
assertParseOneDoc(editor, ////////////////
"DOC(0): ",
" KEY(0): world:",
" KEY(2): europe:",
" KEY(4): france:",
" RAW(6): cheese",
" KEY(4): belgium:",
" RAW(2): beer",
" KEY(2): canada:",
" KEY(4): montreal: poutine",
" KEY(4): vancouver:",
" RAW(6): salmon",
" KEY(0): moon:",
" KEY(2): moonbase-alfa:",
" RAW(4): moonstone",
"DOC(0): ",
" KEY(0): world:",
" KEY(2): europe:",
" KEY(4): france:",
" RAW(6): cheese",
" KEY(4): belgium:",
" RAW(2): beer",
" KEY(2): canada:",
" KEY(4): montreal: poutine",
" KEY(4): vancouver:",
" RAW(6): salmon",
" KEY(0): moon:",
" KEY(2): moonbase-alfa:",
" RAW(4): moonstone",
" RAW(-1):"
);
}
@@ -835,6 +836,61 @@ public class YamlStructureParserTest {
assertValueRange(editor, root, "foo:", null);
}
@Test public void testEmptyLines() throws Exception {
//Attempt to pin down issue in YamlStructureParser related to
//https://www.pivotaltracker.com/story/show/163752179
String[] insertion = {
"",
"\n"
};
YamlPath expect = new YamlPath(
valueAt(0), //document index
valueAt("jobs"),
valueAt(0),
valueAt("plan"),
valueAt(1),
valueAt("params")
);
for (String maybeEmptyLine : insertion) {
MockYamlEditor editor = new MockYamlEditor(
// "resources:\n" +
// "\n" +
// "- name: banana-img.git\n" +
// " type: docker-image\n" +
// " source:\n" +
// " repository: repo/banana-scratch-build\n" +
// "\n" +
// "- name: repo.git\n" +
// " type: git\n" +
// " source:\n" +
// " uri: https://example.com/repo.git\n" +
// "\n" +
"jobs:\n" +
"- name: work-img\n" +
" plan:\n" +
maybeEmptyLine +
" - get: repo.git\n" +
" - put: banana-img.git\n" +
" params:\n" +
" "
);
int cursor = editor.getRawText().length()-1;
SRootNode root = editor.parseStructure();
SNode node = root.find(cursor);
System.out.println("node = "+node);
YamlPath path = node.getPath();
System.out.println("path = "+path);
assertEquals(expect.toString(), path.toString());
SNode traversed = path.traverse(root);
//Note: We might expact path of a node should lead to that node, but actually it doesn't for historic reasons.
assertEquals(node.getParent(), traversed);
}
}
private void assertValueRange(MockYamlEditor editor, SRootNode root, String nodeText, String expectedValue) throws Exception {
int start = editor.getText().indexOf(nodeText);
SKeyNode node = (SKeyNode) root.find(start);

View File

@@ -5159,6 +5159,41 @@ public class ConcourseEditorTest {
editor.assertProblems(/*None*/);
}
@Test
public void PT_163752179_completions_confused_by_empty_lines() throws Exception {
//See: https://www.pivotaltracker.com/story/show/163752179
String[] insertion = {
"",
"\n"
};
for (String maybeEmptyLine : insertion) {
Editor editor = harness.newEditor(
"resources:\n" +
"\n" +
"- name: banana-img.git\n" +
" type: docker-image\n" +
" source:\n" +
" repository: repo/banana-scratch-build\n" +
"\n" +
"- name: repo.git\n" +
" type: git\n" +
" source:\n" +
" uri: https://example.com/repo.git\n" +
"\n" +
"jobs:\n" +
"- name: work-img\n" +
" plan:\n" +
maybeEmptyLine +
" - get: repo.git\n" +
" - put: banana-img.git\n" +
" params:\n" +
" <*>"
);
editor.assertCompletionLabels(c -> c.getLabel().startsWith("cache"), "cache", "cache_from", "cache_tag");
}
}
//////////////////////////////////////////////////////////////////////////////
private void assertContextualCompletions(String conText, String textBefore, String... textAfter) throws Exception {