Fix PT_140711495: triple dash disrupts content assist

This commit is contained in:
Kris De Volder
2017-03-03 13:47:50 -08:00
parent 3267d1de7c
commit ff531d3a9b
6 changed files with 137 additions and 25 deletions

View File

@@ -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;

View File

@@ -69,5 +69,9 @@ public class SNodeDynamicSchemaContext extends CachingSchemaContext {
return contextPath;
}
@Override
public String toString() {
return "SNodeDynamicSchemaContext("+contextPath+")";
}
}

View File

@@ -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) {

View File

@@ -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:",

View File

@@ -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();

View File

@@ -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 {