Quickfix for health-check-type: none
This commit is contained in:
@@ -10,6 +10,8 @@
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.commons.languageserver.reconcile;
|
||||
|
||||
import org.springframework.ide.vscode.commons.util.Assert;
|
||||
import org.springframework.ide.vscode.commons.util.StringUtil;
|
||||
import org.springframework.ide.vscode.commons.util.ValueParseException;
|
||||
|
||||
/**
|
||||
@@ -23,6 +25,8 @@ public class ReconcileException extends ValueParseException implements ProblemTy
|
||||
private static final long serialVersionUID = 1L;
|
||||
private final ProblemType problemType;
|
||||
|
||||
private ReplacementQuickfix replacement = null; //Optional info to create a 'replacement quickfix' for the value
|
||||
|
||||
public ReconcileException(String message, ProblemType problemType) {
|
||||
super(message);
|
||||
this.problemType = problemType;
|
||||
@@ -37,4 +41,17 @@ public class ReconcileException extends ValueParseException implements ProblemTy
|
||||
public ProblemType getProblemType() {
|
||||
return problemType;
|
||||
}
|
||||
|
||||
public ReconcileException fixWith(ReplacementQuickfix replacement) {
|
||||
//Silently ignore if the fix is either null or doesn't provide a proper replacement text.
|
||||
if (replacement!=null && StringUtil.hasText(replacement.replacement)) {
|
||||
Assert.isLegal(this.replacement==null, "Multiple fixes not yet supported");
|
||||
this.replacement = replacement;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public ReplacementQuickfix getReplacement() {
|
||||
return replacement;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017 Pivotal, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.commons.languageserver.reconcile;
|
||||
|
||||
/**
|
||||
* An info object that can be attached to a {@link ReconcileException} to indicate that
|
||||
* the problem can be fixed by a simple value substitution at the exact location of
|
||||
* the problem marker.
|
||||
*
|
||||
* @author Kris De Volder
|
||||
*/
|
||||
public class ReplacementQuickfix {
|
||||
public final String msg;
|
||||
public final String replacement;
|
||||
public ReplacementQuickfix(String msg, String replacement) {
|
||||
super();
|
||||
this.msg = msg;
|
||||
this.replacement = replacement;
|
||||
}
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ReplacementQuickfix [msg=" + msg + ", replacement=" + replacement + "]";
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@ import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.eclipse.lsp4j.Range;
|
||||
import org.springframework.ide.vscode.commons.util.Assert;
|
||||
import org.springframework.ide.vscode.commons.util.BadLocationException;
|
||||
import org.springframework.ide.vscode.commons.util.text.IDocument;
|
||||
@@ -290,4 +291,8 @@ public class DocumentRegion implements CharSequence {
|
||||
numChars = Math.min(getLength(), numChars);
|
||||
return new DocumentRegion(doc, end-numChars, end);
|
||||
}
|
||||
|
||||
public Range asRange() throws BadLocationException {
|
||||
return doc.toRange(asRegion());
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
package org.springframework.ide.vscode.commons.util.text;
|
||||
|
||||
import org.eclipse.lsp4j.Range;
|
||||
import org.springframework.ide.vscode.commons.util.BadLocationException;
|
||||
|
||||
public interface IDocument {
|
||||
@@ -30,5 +31,6 @@ public interface IDocument {
|
||||
String textBetween(int start, int end) throws BadLocationException;
|
||||
LanguageId getLanguageId();
|
||||
int getVersion();
|
||||
Range toRange(IRegion asRegion) throws BadLocationException;
|
||||
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
|
||||
package org.springframework.ide.vscode.commons.util.text;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@@ -278,6 +277,7 @@ public class TextDocument implements IDocument {
|
||||
return languageId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Range toRange(IRegion region) throws BadLocationException {
|
||||
return toRange(region.getOffset(), region.getLength());
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.commons.yaml.reconcile;
|
||||
package org.springframework.ide.vscode.commons.yaml.quickfix;
|
||||
|
||||
import org.eclipse.lsp4j.TextEdit;
|
||||
import org.eclipse.lsp4j.WorkspaceEdit;
|
||||
@@ -21,6 +21,8 @@ import org.springframework.ide.vscode.commons.util.text.TextDocument;
|
||||
import org.springframework.ide.vscode.commons.yaml.completion.YamlPathEdits;
|
||||
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.reconcile.MissingPropertiesData;
|
||||
import org.springframework.ide.vscode.commons.yaml.reconcile.ReplaceStringData;
|
||||
import org.springframework.ide.vscode.commons.yaml.structure.YamlDocument;
|
||||
import org.springframework.ide.vscode.commons.yaml.structure.YamlStructureParser.SChildBearingNode;
|
||||
import org.springframework.ide.vscode.commons.yaml.structure.YamlStructureParser.SNode;
|
||||
@@ -33,6 +35,7 @@ import com.google.common.collect.ImmutableMap;
|
||||
public class YamlQuickfixes {
|
||||
|
||||
public final QuickfixType MISSING_PROP_FIX;
|
||||
public final QuickfixType SIMPLE_TEXT_EDIT;
|
||||
|
||||
public YamlQuickfixes(QuickfixRegistry r, SimpleTextDocumentService textDocumentService, YamlStructureProvider structureProvider) {
|
||||
MISSING_PROP_FIX = r.register("MISSING_PROP_FIX", (Object _params) -> {
|
||||
@@ -69,6 +72,24 @@ public class YamlQuickfixes {
|
||||
//Something went wrong. Return empty edit object.
|
||||
return new WorkspaceEdit(ImmutableMap.of(), null);
|
||||
});
|
||||
|
||||
SIMPLE_TEXT_EDIT = r.register("SIMPLE_TEXT_EDIT", (_params) -> {
|
||||
try {
|
||||
ReplaceStringData params = new ObjectMapper().convertValue(_params, ReplaceStringData.class);
|
||||
TextDocument _doc = textDocumentService.getDocument(params.getUri());
|
||||
if (_doc!=null) {
|
||||
return new WorkspaceEdit(
|
||||
ImmutableMap.of(params.getUri(), ImmutableList.of(params.getEdit())),
|
||||
null
|
||||
);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.log(e);
|
||||
}
|
||||
//Something went wrong. Return empty edit object.
|
||||
//Something went wrong. Return empty edit object.
|
||||
return new WorkspaceEdit(ImmutableMap.of(), null);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017 Pivotal, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.commons.yaml.reconcile;
|
||||
|
||||
import org.eclipse.lsp4j.TextEdit;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.DocumentRegion;
|
||||
import org.springframework.ide.vscode.commons.util.BadLocationException;
|
||||
|
||||
public class ReplaceStringData {
|
||||
|
||||
private String uri;
|
||||
private TextEdit edit;
|
||||
|
||||
public ReplaceStringData() {}
|
||||
|
||||
public ReplaceStringData(DocumentRegion target, String newText) throws BadLocationException {
|
||||
this.uri = target.getDocument().getUri();
|
||||
this.edit = new TextEdit(target.asRange(), newText);
|
||||
}
|
||||
|
||||
public TextEdit getEdit() {
|
||||
return edit;
|
||||
}
|
||||
public void setEdit(TextEdit edit) {
|
||||
this.edit = edit;
|
||||
}
|
||||
|
||||
public String getUri() {
|
||||
return uri;
|
||||
}
|
||||
public void setUri(String uri) {
|
||||
this.uri = uri;
|
||||
}
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ReplaceStringData [uri=" + uri + ", edit=" + edit + "]";
|
||||
}
|
||||
}
|
||||
@@ -23,9 +23,13 @@ import java.util.TreeSet;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.ide.vscode.commons.languageserver.quickfix.Quickfix.QuickfixData;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemType;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemTypeProvider;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.ReconcileException;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.ReconcileProblemImpl;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.ReplacementQuickfix;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.DocumentRegion;
|
||||
import org.springframework.ide.vscode.commons.util.ExceptionUtil;
|
||||
import org.springframework.ide.vscode.commons.util.IntegerRange;
|
||||
@@ -38,9 +42,9 @@ 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.YamlPath;
|
||||
import org.springframework.ide.vscode.commons.yaml.path.YamlPathSegment;
|
||||
import org.springframework.ide.vscode.commons.yaml.quickfix.YamlQuickfixes;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.ASTDynamicSchemaContext;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.DynamicSchemaContext;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.SchemaContextAware;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.YType;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.YTypeUtil;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.YTypedProperty;
|
||||
@@ -181,21 +185,9 @@ public class SchemaBasedYamlASTReconciler implements YamlASTReconciler {
|
||||
}
|
||||
} catch (Exception e) {
|
||||
ProblemType problemType = getProblemType(e);
|
||||
DocumentRegion region = new DocumentRegion(doc, node.getStartMark().getIndex(), node.getEndMark().getIndex());
|
||||
if (e instanceof ValueParseException) {
|
||||
ValueParseException parseException = (ValueParseException) e;
|
||||
int start = parseException.getStartIndex() >= 0
|
||||
? Math.min(node.getStartMark().getIndex() + parseException.getStartIndex(),
|
||||
node.getEndMark().getIndex())
|
||||
: node.getStartMark().getIndex();
|
||||
int end = parseException.getEndIndex() >= 0
|
||||
? Math.min(node.getStartMark().getIndex() + parseException.getEndIndex(),
|
||||
node.getEndMark().getIndex())
|
||||
: node.getEndMark().getIndex();
|
||||
region = new DocumentRegion(doc, start, end);
|
||||
}
|
||||
DocumentRegion region = getRegion(e, doc, node);
|
||||
String msg = getMessage(e);
|
||||
valueParseError(type, region, msg, problemType);
|
||||
valueParseError(type, region, msg, problemType, getValueReplacement(e));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -208,6 +200,31 @@ public class SchemaBasedYamlASTReconciler implements YamlASTReconciler {
|
||||
}
|
||||
}
|
||||
|
||||
protected ReplacementQuickfix getValueReplacement(Exception _e) {
|
||||
if (_e instanceof ReconcileException) {
|
||||
ReconcileException e = (ReconcileException) _e;
|
||||
return e.getReplacement();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected DocumentRegion getRegion(Exception e, IDocument doc, Node node) {
|
||||
DocumentRegion region = new DocumentRegion(doc, node.getStartMark().getIndex(), node.getEndMark().getIndex());
|
||||
if (e instanceof ValueParseException) {
|
||||
ValueParseException parseException = (ValueParseException) e;
|
||||
int start = parseException.getStartIndex() >= 0
|
||||
? Math.min(node.getStartMark().getIndex() + parseException.getStartIndex(),
|
||||
node.getEndMark().getIndex())
|
||||
: node.getStartMark().getIndex();
|
||||
int end = parseException.getEndIndex() >= 0
|
||||
? Math.min(node.getStartMark().getIndex() + parseException.getEndIndex(),
|
||||
node.getEndMark().getIndex())
|
||||
: node.getEndMark().getIndex();
|
||||
region = new DocumentRegion(doc, start, end);
|
||||
}
|
||||
return region;
|
||||
}
|
||||
|
||||
private String getMessage(Exception _e) {
|
||||
Throwable e = ExceptionUtil.getDeepestCause(_e);
|
||||
|
||||
@@ -337,18 +354,24 @@ public class SchemaBasedYamlASTReconciler implements YamlASTReconciler {
|
||||
}
|
||||
}
|
||||
|
||||
private void valueParseError(YType type, Node node, String parseErrorMsg, ProblemType problemType) {
|
||||
private void valueParseError(YType type, DocumentRegion region, String parseErrorMsg, ProblemType problemType, ReplacementQuickfix fix) {
|
||||
if (!StringUtil.hasText(parseErrorMsg)) {
|
||||
parseErrorMsg= "Couldn't parse as '"+describe(type)+"'";
|
||||
}
|
||||
problem(node, parseErrorMsg, problemType);
|
||||
}
|
||||
|
||||
private void valueParseError(YType type, DocumentRegion region, String parseErrorMsg, ProblemType problemType) {
|
||||
if (!StringUtil.hasText(parseErrorMsg)) {
|
||||
parseErrorMsg= "Couldn't parse as '"+describe(type)+"'";
|
||||
ReconcileProblemImpl problem = YamlSchemaProblems.problem(problemType, parseErrorMsg, region);
|
||||
if (fix!=null && StringUtil.hasText(fix.replacement)) {
|
||||
try {
|
||||
problem.addQuickfix(
|
||||
new QuickfixData<>(quickfixes.SIMPLE_TEXT_EDIT,
|
||||
new ReplaceStringData(region, fix.replacement),
|
||||
fix.msg
|
||||
)
|
||||
);
|
||||
} catch (Exception e) {
|
||||
Log.log(e);
|
||||
}
|
||||
}
|
||||
problem(region, parseErrorMsg, problemType);
|
||||
problems.accept(problem);
|
||||
}
|
||||
|
||||
private void unknownBeanProperty(Node keyNode, YType type, String name) {
|
||||
@@ -404,14 +427,6 @@ public class SchemaBasedYamlASTReconciler implements YamlASTReconciler {
|
||||
problems.accept(YamlSchemaProblems.schemaProblem(msg, node));
|
||||
}
|
||||
|
||||
private void problem(Node node, String msg, ProblemType problemType) {
|
||||
problems.accept(YamlSchemaProblems.problem(problemType, msg, node));
|
||||
}
|
||||
|
||||
private void problem(DocumentRegion region, String msg, ProblemType problemType) {
|
||||
problems.accept(YamlSchemaProblems.problem(problemType, msg, region));
|
||||
}
|
||||
|
||||
private void problem(DocumentRegion region, String msg) {
|
||||
problems.accept(YamlSchemaProblems.schemaProblem(msg, region));
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemC
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.ReconcileProblem;
|
||||
import org.springframework.ide.vscode.commons.util.text.IDocument;
|
||||
import org.springframework.ide.vscode.commons.yaml.ast.YamlASTProvider;
|
||||
import org.springframework.ide.vscode.commons.yaml.quickfix.YamlQuickfixes;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.YamlSchema;
|
||||
|
||||
/**
|
||||
|
||||
@@ -49,6 +49,7 @@ public class YamlSchemaProblems {
|
||||
public static final ProblemType SYNTAX_PROBLEM = problemType("YamlSyntaxProblem");
|
||||
public static final ProblemType SCHEMA_PROBLEM = problemType("YamlSchemaProblem");
|
||||
public static final ProblemType DEPRECATED_PROPERTY = problemType("DeprecatedProperty", ProblemSeverity.WARNING);
|
||||
public static final ProblemType DEPRECATED_VALUE = problemType("DeprecatedValue", ProblemSeverity.WARNING);
|
||||
public static final ProblemType MISSING_PROPERTY = problemType("MissingProperty", ProblemSeverity.ERROR);
|
||||
public static final ProblemType EXTRA_PROPERTY = problemType("ExtraProperty", ProblemSeverity.ERROR);
|
||||
|
||||
|
||||
@@ -26,14 +26,13 @@ import java.util.function.BiFunction;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.ReconcileException;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.ReplacementQuickfix;
|
||||
import org.springframework.ide.vscode.commons.util.Assert;
|
||||
import org.springframework.ide.vscode.commons.util.EnumValueParser;
|
||||
import org.springframework.ide.vscode.commons.util.Renderable;
|
||||
import org.springframework.ide.vscode.commons.util.Renderables;
|
||||
import org.springframework.ide.vscode.commons.util.StringUtil;
|
||||
import org.springframework.ide.vscode.commons.util.ValueParser;
|
||||
import org.springframework.ide.vscode.commons.yaml.reconcile.YamlSchemaProblems;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.YTypeFactory.AbstractType;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.constraints.Constraint;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.constraints.Constraints;
|
||||
|
||||
@@ -53,12 +52,25 @@ import reactor.core.publisher.Flux;
|
||||
*/
|
||||
public class YTypeFactory {
|
||||
|
||||
private static class Deprecation {
|
||||
final String errorMsg;
|
||||
final String replacement;
|
||||
final String quickfixMsg;
|
||||
public Deprecation(String errorMsg, String replacement, String quickfixMsg) {
|
||||
super();
|
||||
this.errorMsg = errorMsg;
|
||||
this.replacement = replacement;
|
||||
this.quickfixMsg = quickfixMsg;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class EnumTypeBuilder {
|
||||
|
||||
private String name;
|
||||
private String[] values;
|
||||
|
||||
private Map<String, String> deprecationMsgs = new HashMap<>();
|
||||
private Map<String, Deprecation> deprecations = new HashMap<>();
|
||||
|
||||
public EnumTypeBuilder(String name, String[] values) {
|
||||
this.name = name;
|
||||
@@ -69,17 +81,15 @@ public class YTypeFactory {
|
||||
EnumValueParser basicParser = new EnumValueParser(name, values);
|
||||
YAtomicType t = yatomic(name);
|
||||
t.addHints(getNonDeprecatedValues());
|
||||
if (deprecationMsgs.isEmpty()) {
|
||||
if (deprecations.isEmpty()) {
|
||||
t.parseWith(basicParser);
|
||||
} else {
|
||||
t.parseWith(ValueParser.of((String value) -> {
|
||||
basicParser.parse(value);
|
||||
if (deprecationMsgs.containsKey(value)) {
|
||||
String msg = deprecationMsgs.get(value);
|
||||
if (!StringUtil.hasText(msg)) {
|
||||
msg = "'"+value+"' is deprecated";
|
||||
}
|
||||
throw new ReconcileException(msg, YamlSchemaProblems.DEPRECATED_PROPERTY);
|
||||
Deprecation d = deprecations.get(value);
|
||||
if (d!=null) {
|
||||
throw new ReconcileException(d.errorMsg, YamlSchemaProblems.DEPRECATED_VALUE)
|
||||
.fixWith(new ReplacementQuickfix(d.quickfixMsg, d.replacement));
|
||||
}
|
||||
return value;
|
||||
}));
|
||||
@@ -89,13 +99,23 @@ public class YTypeFactory {
|
||||
|
||||
public EnumTypeBuilder deprecate(String value, String msg) {
|
||||
Assert.isLegal(ImmutableSet.copyOf(values).contains(value));
|
||||
deprecationMsgs.put(value, msg);
|
||||
deprecations.put(value, new Deprecation(msg, null, null));
|
||||
return this;
|
||||
}
|
||||
|
||||
public EnumTypeBuilder deprecateWithReplacement(String value, String replacement) {
|
||||
Assert.isLegal(ImmutableSet.copyOf(values).contains(value));
|
||||
deprecations.put(value, new Deprecation(
|
||||
"The value '"+value+"' is deprecated in favor of '"+replacement+"'",
|
||||
replacement,
|
||||
"Replace deprecated value '"+value+"' by '"+replacement+"'"
|
||||
));
|
||||
return this;
|
||||
}
|
||||
|
||||
private String[] getNonDeprecatedValues() {
|
||||
return Flux.fromArray(values)
|
||||
.filter((value) -> !deprecationMsgs.containsKey(value))
|
||||
.filter((value) -> !deprecations.containsKey(value))
|
||||
.collectList()
|
||||
.map(l -> l.toArray(new String[l.size()]))
|
||||
.block();
|
||||
|
||||
Reference in New Issue
Block a user