diff --git a/vscode-extensions/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/DocumentRegion.java b/vscode-extensions/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/DocumentRegion.java index 093587a0f..b4a6b9bd0 100644 --- a/vscode-extensions/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/DocumentRegion.java +++ b/vscode-extensions/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/DocumentRegion.java @@ -266,7 +266,21 @@ public class DocumentRegion implements CharSequence { } public int getLength() { - return getEnd() - getStart(); + return length(); + } + + public boolean endsWith(CharSequence string) { + int myLen = length(); + int strLen = string.length(); + if (myLen>=strLen) { + for (int i = 0; i < strLen; i++) { + if (charAt(myLen-strLen+i)!=string.charAt(i)) { + return false; + } + } + return true; + } + return false; } } \ No newline at end of file diff --git a/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/reconcile/SchemaBasedYamlASTReconciler.java b/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/reconcile/SchemaBasedYamlASTReconciler.java index e0c372256..99d8dbff1 100644 --- a/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/reconcile/SchemaBasedYamlASTReconciler.java +++ b/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/reconcile/SchemaBasedYamlASTReconciler.java @@ -234,14 +234,14 @@ public class SchemaBasedYamlASTReconciler implements YamlASTReconciler { } else { message = "Properties "+missingProps+" are required for '"+type+"'"; } - problems.accept(YamlSchemaProblems.missingProperty(message, parent, map)); + problems.accept(YamlSchemaProblems.missingProperty(message, dc.getDocument(), parent, map)); } //Check for other constraints attached to the type for (SchemaContextAware _constraint : typeUtil.getConstraints(type)) { Constraint constraint = _constraint.withContext(dc); if (constraint!=null) { - constraint.verify(parent, map, type, foundProps, problems); + constraint.verify(dc.getDocument(), parent, map, type, foundProps, problems); } } } diff --git a/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/reconcile/YamlSchemaProblems.java b/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/reconcile/YamlSchemaProblems.java index afeed88fc..ad1fcd0f2 100644 --- a/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/reconcile/YamlSchemaProblems.java +++ b/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/reconcile/YamlSchemaProblems.java @@ -17,15 +17,18 @@ import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemTy import org.springframework.ide.vscode.commons.languageserver.reconcile.ReconcileProblem; import org.springframework.ide.vscode.commons.languageserver.reconcile.ReconcileProblemImpl; import org.springframework.ide.vscode.commons.languageserver.util.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.path.NodeCursor; 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.schema.YType; import org.springframework.ide.vscode.commons.yaml.schema.YTypedProperty; +import org.yaml.snakeyaml.error.Mark; import org.yaml.snakeyaml.nodes.MappingNode; import org.yaml.snakeyaml.nodes.Node; import org.yaml.snakeyaml.nodes.NodeTuple; +import org.yaml.snakeyaml.nodes.SequenceNode; import com.google.common.collect.ImmutableSet; @@ -90,19 +93,36 @@ public class YamlSchemaProblems { return deprecatedProperty("Property '"+property.getName()+"' of '"+bean+"' is Deprecated", node); } + public static ReconcileProblem problem(ProblemType problemType, String msg, DocumentRegion node) { + int start = node.getStart(); + int end = node.getEnd(); + return new ReconcileProblemImpl(problemType, msg, start, end-start); + } + public static ReconcileProblem problem(ProblemType problemType, String msg, Node node) { int start = node.getStartMark().getIndex(); int end = node.getEndMark().getIndex(); return new ReconcileProblemImpl(problemType, msg, start, end-start); } - public static ReconcileProblem missingProperty(String msg, Node parent, MappingNode map) { + public static ReconcileProblem missingProperty(String msg, IDocument doc, Node parent, MappingNode map) { if (parent instanceof MappingNode) { for (NodeTuple prop : ((MappingNode) parent).getValue()) { if (prop.getValueNode()==map) { return problem(MISSING_PROPERTY, msg, prop.getKeyNode()); } } + } else if (parent instanceof SequenceNode) { + Boolean flowStyle = ((SequenceNode) parent).getFlowStyle(); + if (flowStyle!=null && !flowStyle) { + Mark nodeStart = map.getStartMark(); + DocumentRegion underline = new DocumentRegion(doc, 0, nodeStart.getIndex()); + underline = underline.trimEnd(); + if (underline.endsWith("-")) { + underline = underline.subSequence(underline.length()-1, underline.length()); + return problem(MISSING_PROPERTY, msg, underline); + } + } } return problem(MISSING_PROPERTY, msg, map); } diff --git a/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/schema/constraints/Constraint.java b/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/schema/constraints/Constraint.java index f04b6369c..74ab5a8a6 100644 --- a/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/schema/constraints/Constraint.java +++ b/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/schema/constraints/Constraint.java @@ -13,6 +13,7 @@ package org.springframework.ide.vscode.commons.yaml.schema.constraints; import java.util.Set; import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector; +import org.springframework.ide.vscode.commons.util.text.IDocument; import org.springframework.ide.vscode.commons.yaml.schema.YType; import org.yaml.snakeyaml.nodes.MappingNode; import org.yaml.snakeyaml.nodes.Node; @@ -36,6 +37,7 @@ public interface Constraint { * @param foundProps The properties this node defines. * @param problems Problem collector where to which the constraint should add the validation problems it finds. */ - void verify(Node parent, MappingNode map, YType type, Set foundProps, IProblemCollector problems); + void verify(IDocument doc, Node parent, MappingNode map, YType type, Set foundProps, + IProblemCollector problems); } diff --git a/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/schema/constraints/Constraints.java b/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/schema/constraints/Constraints.java index 8d978b897..fb9195d3f 100644 --- a/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/schema/constraints/Constraints.java +++ b/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/schema/constraints/Constraints.java @@ -21,6 +21,7 @@ import java.util.function.Function; import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector; import org.springframework.ide.vscode.commons.util.Assert; +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.reconcile.YamlSchemaProblems; import org.springframework.ide.vscode.commons.yaml.schema.YType; @@ -61,7 +62,7 @@ public class Constraints { } @Override - public void verify(Node parent, MappingNode map, YType type, Set foundProps, IProblemCollector problems) { + public void verify(IDocument doc, Node parent, MappingNode map, YType type, Set foundProps, IProblemCollector problems) { List requiredProps = Arrays.asList(_requiredProps); long foundPropsCount = requiredProps.stream() .filter(foundProps::contains) @@ -69,7 +70,7 @@ public class Constraints { if (foundPropsCount==0) { if (!allowFewer) { problems.accept(missingProperty( - "One of "+requiredProps+" is required for '"+type+"'", parent, map)); + "One of "+requiredProps+" is required for '"+type+"'", doc, parent, map)); } } else if (foundPropsCount>1) { //Mark each of the found keys as a violation: @@ -86,7 +87,7 @@ public class Constraints { public static Constraint deprecated(Function messageFormatter, String... _deprecatedNames) { Set deprecatedNames = ImmutableSet.copyOf(_deprecatedNames); - return (Node parent, MappingNode map, YType type, Set foundProps, IProblemCollector problems) -> { + return (IDocument doc, Node parent, MappingNode map, YType type, Set foundProps, IProblemCollector problems) -> { for (NodeTuple prop : map.getValue()) { Node keyNode = prop.getKeyNode(); String name = NodeUtil.asScalar(keyNode); diff --git a/vscode-extensions/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/Editor.java b/vscode-extensions/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/Editor.java index d2f547796..469cea058 100644 --- a/vscode-extensions/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/Editor.java +++ b/vscode-extensions/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/Editor.java @@ -112,10 +112,14 @@ public class Editor { /** * Check that a 'expectedProblems' are found by the reconciler. Expected problems are - * specified by string of the form "${badSnippet}|${messageSnippet}". The badSnippet - * is the text expected to be covered by the marker's region and the message snippet must + * specified by string of the form "${badSnippet}|${messageSnippet}" or + * "${badSnippet}^${followSnippet}|${messageSnippet}" + *

+ * The badSnippet is the text expected to be covered by the marker's region and the message snippet must * be found in the error marker's message. *

+ * In addition, if followSnippet is specified, the text that comes right after the error marker must match it. + *

* The expected problems are matched one-to-one in the order given (so markers in the * editor must appear in the expected order for the assert to pass). * @@ -206,6 +210,12 @@ public class Editor { String[] parts = expect.split("\\|"); assertEquals(2, parts.length); String badSnippet = parts[0]; + String snippetFollow = null; + int carretOffset = badSnippet.indexOf('^'); + if (carretOffset>=0) { + snippetFollow = badSnippet.substring(carretOffset+1); + badSnippet = badSnippet.substring(0, carretOffset); + } String messageSnippet = parts[1]; boolean spaceSensitive = badSnippet.trim().length()