Fix PT-140709005 better underlining for missing property warnings

Try to underline corresponding parent property key instead of the entire node that is missing the property.
This commit is contained in:
Kris De Volder
2017-04-03 12:11:34 -07:00
parent f64b90e19c
commit 88013ef803
5 changed files with 46 additions and 40 deletions

View File

@@ -90,7 +90,7 @@ public class SchemaBasedYamlASTReconciler implements YamlASTReconciler {
if (nodes!=null && !nodes.isEmpty()) {
for (int i = 0; i < nodes.size(); i++) {
Node node = nodes.get(i);
reconcile(ast.getDocument(), new YamlPath(YamlPathSegment.valueAt(i)), node, schema.getTopLevelType());
reconcile(ast.getDocument(), new YamlPath(YamlPathSegment.valueAt(i)), /*parent*/null, node, schema.getTopLevelType());
}
}
} finally {
@@ -116,7 +116,7 @@ public class SchemaBasedYamlASTReconciler implements YamlASTReconciler {
return allOf(ast, node);
}
private void reconcile(IDocument doc, YamlPath path, Node node, YType type) {
private void reconcile(IDocument doc, YamlPath path, Node parent, Node node, YType type) {
if (type!=null) {
DynamicSchemaContext schemaContext = new ASTDynamicSchemaContext(doc, path, node);
type = typeUtil.inferMoreSpecificType(type, schemaContext);
@@ -130,12 +130,12 @@ public class SchemaBasedYamlASTReconciler implements YamlASTReconciler {
if (typeUtil.isMap(type)) {
for (NodeTuple entry : map.getValue()) {
String key = NodeUtil.asScalar(entry.getKeyNode());
reconcile(doc, keyAt(path, key), entry.getKeyNode(), typeUtil.getKeyType(type));
reconcile(doc, valueAt(path, key), entry.getValueNode(), typeUtil.getDomainType(type));
reconcile(doc, keyAt(path, key), map, entry.getKeyNode(), typeUtil.getKeyType(type));
reconcile(doc, valueAt(path, key), map, entry.getValueNode(), typeUtil.getDomainType(type));
}
} else if (typeUtil.isBean(type)) {
Map<String, YTypedProperty> beanProperties = typeUtil.getPropertiesMap(type);
checkRequiredProperties(map, type, beanProperties, schemaContext);
checkRequiredProperties(parent, map, type, beanProperties, schemaContext);
for (NodeTuple entry : map.getValue()) {
Node keyNode = entry.getKeyNode();
String key = NodeUtil.asScalar(keyNode);
@@ -149,7 +149,7 @@ public class SchemaBasedYamlASTReconciler implements YamlASTReconciler {
if (prop.isDeprecated()) {
problems.accept(YamlSchemaProblems.deprecatedProperty(keyNode, type, prop));
}
reconcile(doc, valueAt(path, key), entry.getValueNode(), prop.getType());
reconcile(doc, valueAt(path, key), map, entry.getValueNode(), prop.getType());
}
}
}
@@ -162,7 +162,7 @@ public class SchemaBasedYamlASTReconciler implements YamlASTReconciler {
if (typeUtil.isSequencable(type)) {
for (int i = 0; i < seq.getValue().size(); i++) {
Node el = seq.getValue().get(i);
reconcile(doc, valueAt(path, i), el, typeUtil.getDomainType(type));
reconcile(doc, valueAt(path, i), seq, el, typeUtil.getDomainType(type));
}
} else {
expectTypeButFoundSequence(type, node);
@@ -214,7 +214,7 @@ public class SchemaBasedYamlASTReconciler implements YamlASTReconciler {
return e instanceof ProblemTypeProvider ? ((ProblemTypeProvider) e).getProblemType() : YamlSchemaProblems.SCHEMA_PROBLEM;
}
private void checkRequiredProperties(MappingNode map, YType type, Map<String, YTypedProperty> beanProperties, DynamicSchemaContext dc) {
private void checkRequiredProperties(Node parent, MappingNode map, YType type, Map<String, YTypedProperty> beanProperties, DynamicSchemaContext dc) {
Set<String> foundProps = NodeUtil.getScalarKeys(map);
boolean allPropertiesKnown = beanProperties.keySet().containsAll(foundProps);
//Don't check for missing properties if some properties look like they might be spelled incorrectly.
@@ -234,14 +234,14 @@ public class SchemaBasedYamlASTReconciler implements YamlASTReconciler {
} else {
message = "Properties "+missingProps+" are required for '"+type+"'";
}
problems.accept(YamlSchemaProblems.missingProperty(message, map));
problems.accept(YamlSchemaProblems.missingProperty(message, parent, map));
}
//Check for other constraints attached to the type
for (SchemaContextAware<Constraint> _constraint : typeUtil.getConstraints(type)) {
Constraint constraint = _constraint.withContext(dc);
if (constraint!=null) {
constraint.verify(map, type, foundProps, problems);
constraint.verify(parent, map, type, foundProps, problems);
}
}
}

View File

@@ -17,10 +17,15 @@ 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.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.nodes.MappingNode;
import org.yaml.snakeyaml.nodes.Node;
import org.yaml.snakeyaml.nodes.NodeTuple;
import com.google.common.collect.ImmutableSet;
@@ -91,7 +96,14 @@ public class YamlSchemaProblems {
return new ReconcileProblemImpl(problemType, msg, start, end-start);
}
public static ReconcileProblem missingProperty(String msg, MappingNode map) {
public static ReconcileProblem missingProperty(String msg, 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());
}
}
}
return problem(MISSING_PROPERTY, msg, map);
}

View File

@@ -15,6 +15,7 @@ import java.util.Set;
import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector;
import org.springframework.ide.vscode.commons.yaml.schema.YType;
import org.yaml.snakeyaml.nodes.MappingNode;
import org.yaml.snakeyaml.nodes.Node;
/**
* An implementations of this interface represents some 'programatic' constraint attached to a schema type.
@@ -35,6 +36,6 @@ 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(MappingNode map, YType type, Set<String> foundProps, IProblemCollector problems);
void verify(Node parent, MappingNode map, YType type, Set<String> foundProps, IProblemCollector problems);
}

View File

@@ -61,7 +61,7 @@ public class Constraints {
}
@Override
public void verify(MappingNode map, YType type, Set<String> foundProps, IProblemCollector problems) {
public void verify(Node parent, MappingNode map, YType type, Set<String> foundProps, IProblemCollector problems) {
List<String> requiredProps = Arrays.asList(_requiredProps);
long foundPropsCount = requiredProps.stream()
.filter(foundProps::contains)
@@ -69,7 +69,7 @@ public class Constraints {
if (foundPropsCount==0) {
if (!allowFewer) {
problems.accept(missingProperty(
"One of "+requiredProps+" is required for '"+type+"'", map));
"One of "+requiredProps+" is required for '"+type+"'", parent, map));
}
} else if (foundPropsCount>1) {
//Mark each of the found keys as a violation:
@@ -86,7 +86,7 @@ public class Constraints {
public static Constraint deprecated(Function<String, String> messageFormatter, String... _deprecatedNames) {
Set<String> deprecatedNames = ImmutableSet.copyOf(_deprecatedNames);
return (MappingNode map, YType type, Set<String> foundProps, IProblemCollector problems) -> {
return (Node parent, MappingNode map, YType type, Set<String> foundProps, IProblemCollector problems) -> {
for (NodeTuple prop : map.getValue()) {
Node keyNode = prop.getKeyNode();
String name = NodeUtil.asScalar(keyNode);