Fix PT_140711495: triple dash disrupts content assist
This commit is contained in:
@@ -10,8 +10,6 @@
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.commons.yaml.completion;
|
||||
|
||||
import static org.springframework.ide.vscode.commons.util.ExceptionUtil.getSimpleError;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
@@ -29,7 +27,6 @@ import org.springframework.ide.vscode.commons.util.ExceptionUtil;
|
||||
import org.springframework.ide.vscode.commons.util.FuzzyMatcher;
|
||||
import org.springframework.ide.vscode.commons.util.Log;
|
||||
import org.springframework.ide.vscode.commons.util.Renderable;
|
||||
import org.springframework.ide.vscode.commons.util.StringUtil;
|
||||
import org.springframework.ide.vscode.commons.util.ValueParseException;
|
||||
import org.springframework.ide.vscode.commons.yaml.hover.YPropertyInfoTemplates;
|
||||
import org.springframework.ide.vscode.commons.yaml.path.YamlPath;
|
||||
|
||||
@@ -69,5 +69,9 @@ public class SNodeDynamicSchemaContext extends CachingSchemaContext {
|
||||
return contextPath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SNodeDynamicSchemaContext("+contextPath+")";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,9 +16,7 @@ import java.io.Writer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@@ -32,7 +30,6 @@ import org.springframework.ide.vscode.commons.yaml.path.KeyAliases;
|
||||
import org.springframework.ide.vscode.commons.yaml.path.YamlNavigable;
|
||||
import org.springframework.ide.vscode.commons.yaml.path.YamlPath;
|
||||
import org.springframework.ide.vscode.commons.yaml.path.YamlPathSegment;
|
||||
import org.springframework.ide.vscode.commons.yaml.structure.YamlStructureParser.SKeyNode;
|
||||
import org.springframework.ide.vscode.commons.yaml.util.Streams;
|
||||
import org.springframework.ide.vscode.commons.yaml.util.YamlIndentUtil;
|
||||
|
||||
@@ -42,9 +39,6 @@ import com.google.common.collect.ListMultimap;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.google.common.collect.MultimapBuilder;
|
||||
|
||||
import reactor.util.function.Tuple2;
|
||||
import reactor.util.function.Tuples;
|
||||
|
||||
/**
|
||||
* A robust, coarse-grained parser that guesses the structure of a
|
||||
* yml document based on indentation levels.
|
||||
@@ -97,13 +91,20 @@ public class YamlStructureParser {
|
||||
// either "..." or "---" at the start of a line
|
||||
// followed by arbitrary amount of whitepsace
|
||||
// optionally followed by a "#" end of line comment.
|
||||
|
||||
//Note: "..." isn't a document separator but document terminator. Treating it as a separator is
|
||||
// technically not correct. As the structure parser is meant to be 'robust' and do something
|
||||
// sensible with incorrect input this makes sense here. The effect it will have is that user
|
||||
// can type after a document terminator and get content assist as if they are in a new document.
|
||||
// (They will also receive a syntax error message from the more formal and precise SnakeYaml parser)
|
||||
|
||||
|
||||
/**
|
||||
* Stuff to ignore at the beginning of a yaml file (before the start of the first document):
|
||||
*/
|
||||
private static final Pattern SKIP_AT_START_OF_DOC = Pattern.compile(
|
||||
"^((\\s*\\#)|(\\%)).*"
|
||||
);
|
||||
|
||||
// public static final Pattern SEQ_LINE = Pattern.compile(
|
||||
// "^( *)- .*");
|
||||
|
||||
@@ -153,8 +154,12 @@ public class YamlStructureParser {
|
||||
public int getStart() {
|
||||
return start;
|
||||
}
|
||||
public boolean matches(Pattern pat, boolean stripiIndentation) throws Exception {
|
||||
CharSequence text = stripiIndentation ? getTextWithoutIndent():getText();
|
||||
return pat.matcher(text).matches();
|
||||
}
|
||||
public boolean matches(Pattern pat) throws Exception {
|
||||
return pat.matcher(getTextWithoutIndent()).matches();
|
||||
return matches(pat, true);
|
||||
}
|
||||
public String getTextWithoutIndent() throws Exception {
|
||||
return doc.textBetween(getStart()+getIndent(), getEnd());
|
||||
@@ -192,6 +197,13 @@ public class YamlStructureParser {
|
||||
this.doc = doc;
|
||||
}
|
||||
|
||||
public YamlLine peek() throws Exception {
|
||||
if (nextLine < doc.getDocument().getNumberOfLines()) {
|
||||
return YamlLine.atLineNumber(doc, nextLine);
|
||||
}
|
||||
return null; //means EOF
|
||||
}
|
||||
|
||||
public YamlLine read() throws Exception {
|
||||
if (nextLine < doc.getDocument().getNumberOfLines()) {
|
||||
return YamlLine.atLineNumber(doc, nextLine++);
|
||||
@@ -272,7 +284,7 @@ public class YamlStructureParser {
|
||||
public IDocument getDocument() {
|
||||
return doc.getDocument();
|
||||
}
|
||||
|
||||
|
||||
public String getText() throws Exception {
|
||||
return doc.textBetween(start, end);
|
||||
}
|
||||
@@ -281,6 +293,7 @@ public class YamlStructureParser {
|
||||
* Default implementation, doesn't support any type of traversal operation.
|
||||
* Subclasses must override and implement where appropriate.
|
||||
*/
|
||||
@Override
|
||||
public Stream<SNode> traverseAmbiguously(YamlPathSegment s) {
|
||||
return Stream.empty();
|
||||
}
|
||||
@@ -290,6 +303,7 @@ public class YamlStructureParser {
|
||||
* the interface. This is only here to prevent subclasses from implementing
|
||||
* this. They should implement traverseAmbiguously instead.
|
||||
*/
|
||||
@Override
|
||||
public SNode traverse(YamlPathSegment s) {
|
||||
return traverseAmbiguously(s).findFirst().orElse(null);
|
||||
}
|
||||
@@ -308,7 +322,7 @@ public class YamlStructureParser {
|
||||
nodes.add(node);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public YamlPath getPath() throws Exception {
|
||||
List<YamlPathSegment> path = new ArrayList<>();
|
||||
for (SNode node : getPathNodes()) {
|
||||
@@ -321,7 +335,7 @@ public class YamlStructureParser {
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine a YamlPathSegment that corresponds to given node. This may be
|
||||
* Determine a YamlPathSegment that corresponds to given node. This may be
|
||||
* null because not all SNodes can be interpreted as 'step' in the yml
|
||||
* structure (e.g. raw nodes will return null, as will the 'root' node).
|
||||
*/
|
||||
@@ -528,7 +542,7 @@ public class YamlStructureParser {
|
||||
}
|
||||
return Stream.empty();
|
||||
}
|
||||
|
||||
|
||||
public SKeyNode getChildWithKey(String key) {
|
||||
return (SKeyNode)getChildrenWithKey(key).findFirst().orElse(null);
|
||||
}
|
||||
@@ -612,9 +626,16 @@ public class YamlStructureParser {
|
||||
|
||||
public SRootNode parse() throws Exception {
|
||||
SRootNode root = new SRootNode(input.getDocument());
|
||||
SDocNode doc = new SDocNode(root,0,0);
|
||||
SChildBearingNode parent = doc;
|
||||
YamlLine line;
|
||||
SChildBearingNode parent = root;
|
||||
YamlLine line = input.peek();
|
||||
while (line!=null && line.matches(SKIP_AT_START_OF_DOC, false)) {
|
||||
input.read();
|
||||
line = input.peek();
|
||||
}
|
||||
if (line!=null && !line.matches(DOCUMENT_SEPERATOR)) {
|
||||
//document separator missing, create document implicitly
|
||||
parent = new SDocNode(root,0,0);
|
||||
}
|
||||
while (null!=(line=input.read())) {
|
||||
int indent = line.getIndent();
|
||||
if (indent==-1) {
|
||||
|
||||
@@ -32,6 +32,51 @@ import org.springframework.ide.vscode.commons.yaml.structure.YamlStructureParser
|
||||
|
||||
public class YamlStructureParserTest {
|
||||
|
||||
@Test public void ignoreLeadingYamlCruftBeforeLeadingDocumentSeparator() throws Exception {
|
||||
String[] stuffToIgnore = {
|
||||
"#comment",
|
||||
" # comment with leading spaces",
|
||||
"%directive"
|
||||
};
|
||||
|
||||
for (String stuff : stuffToIgnore) {
|
||||
System.out.println(stuff);
|
||||
MockYamlEditor editor = new MockYamlEditor(
|
||||
stuff+"\n" +
|
||||
"---\n" +
|
||||
"hello:\n"+
|
||||
" world:\n" +
|
||||
" message\n"
|
||||
);
|
||||
assertParseOneDoc(editor,
|
||||
"DOC(0): ---",
|
||||
" KEY(0): hello:",
|
||||
" KEY(2): world:",
|
||||
" RAW(4): message",
|
||||
" RAW(-1): "
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test public void testLeadingDocumentSeparator() throws Exception {
|
||||
MockYamlEditor editor = new MockYamlEditor(
|
||||
"---\n" +
|
||||
"hello:\n"+
|
||||
" world:\n" +
|
||||
" message\n"
|
||||
);
|
||||
|
||||
assertParseOneDoc(editor,
|
||||
"DOC(0): ---",
|
||||
" KEY(0): hello:",
|
||||
" KEY(2): world:",
|
||||
" RAW(4): message",
|
||||
" RAW(-1): "
|
||||
);
|
||||
}
|
||||
|
||||
@Test public void testSimple() throws Exception {
|
||||
MockYamlEditor editor = new MockYamlEditor(
|
||||
"hello:\n"+
|
||||
@@ -76,7 +121,7 @@ public class YamlStructureParserTest {
|
||||
);
|
||||
assertParseOneDoc(editor,
|
||||
"DOC(0): ",
|
||||
" RAW(-1): #A comment",
|
||||
// " RAW(-1): #A comment",
|
||||
" KEY(0): hello:",
|
||||
" RAW(-1): #Another comment",
|
||||
" KEY(2): world:",
|
||||
|
||||
@@ -227,11 +227,7 @@ public class Editor {
|
||||
: "";
|
||||
}
|
||||
|
||||
private List<Diagnostic> reconcile() throws Exception {
|
||||
// We assume the language server works synchronously for now and it does an immediate reconcile
|
||||
// when the document changes. In the future this is probably not going to be the case though and then this
|
||||
// method will need to somehow ensure the linter is done working before retrieving the problems from the
|
||||
// test harness.
|
||||
public List<Diagnostic> reconcile() throws Exception {
|
||||
PublishDiagnosticsParams diagnostics = harness.getDiagnostics(document);
|
||||
if (diagnostics!=null) {
|
||||
return diagnostics.getDiagnostics();
|
||||
|
||||
@@ -2429,15 +2429,64 @@ public class ConcourseEditorTest {
|
||||
editor.assertHoverContains("path", 2, "The path to a directory where the output will be taken from");
|
||||
}
|
||||
|
||||
@Test public void PT_140711495_triple_dash_at_start_of_file_disrupts_content_assist() throws Exception {
|
||||
assertContextualCompletions(
|
||||
"#leading comment\n" +
|
||||
"---\n" +
|
||||
"resources:\n" +
|
||||
"- name: my-repo\n" +
|
||||
" type: git\n" +
|
||||
" source:\n" +
|
||||
" uri: https://github.com/spring-projects/sts4.git\n" +
|
||||
" <*>"
|
||||
, // ==================
|
||||
"bra<*>"
|
||||
, // ==>
|
||||
"branch: <*>"
|
||||
);
|
||||
|
||||
assertContextualCompletions(
|
||||
"---\n" +
|
||||
"resources:\n" +
|
||||
"- name: my-repo\n" +
|
||||
" type: git\n" +
|
||||
" source:\n" +
|
||||
" uri: https://github.com/spring-projects/sts4.git\n" +
|
||||
" <*>"
|
||||
, // ==================
|
||||
"bra<*>"
|
||||
, // ==>
|
||||
"branch: <*>"
|
||||
);
|
||||
|
||||
assertContextualCompletions(
|
||||
// "---\n" +
|
||||
"resources:\n" +
|
||||
"- name: my-repo\n" +
|
||||
" type: git\n" +
|
||||
" source:\n" +
|
||||
" uri: https://github.com/spring-projects/sts4.git\n" +
|
||||
" <*>"
|
||||
, // ==================
|
||||
"bra<*>"
|
||||
, // ==>
|
||||
"branch: <*>"
|
||||
);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void assertContextualCompletions(String conText, String textBefore, String... textAfter) throws Exception {
|
||||
Editor editor = harness.newEditor(conText);
|
||||
editor.reconcile(); //this ensures the conText is parsed and its AST is cached (will be used for
|
||||
//dynamic CA when the conText + textBefore is not parsable.
|
||||
assertContains(CURSOR, conText);
|
||||
textBefore = conText.replace(CURSOR, textBefore);
|
||||
textAfter = Arrays.stream(textAfter)
|
||||
.map((String t) -> conText.replace(CURSOR, t))
|
||||
.collect(Collectors.toList()).toArray(new String[0]);
|
||||
assertCompletions(textBefore, textAfter);
|
||||
editor.setText(textBefore);
|
||||
editor.assertCompletions(textAfter);
|
||||
}
|
||||
|
||||
private void assertCompletions(String textBefore, String... textAfter) throws Exception {
|
||||
|
||||
Reference in New Issue
Block a user