Fix issue in SNode.getPath() related to empty lines
See https://www.pivotaltracker.com/story/show/163752179
This commit is contained in:
@@ -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;
|
||||
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user