diff --git a/headless-services/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/quickfix/YamlQuickfixes.java b/headless-services/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/quickfix/YamlQuickfixes.java index 68885e1cf..ea4b42733 100644 --- a/headless-services/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/quickfix/YamlQuickfixes.java +++ b/headless-services/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/quickfix/YamlQuickfixes.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2017 Pivotal, Inc. + * Copyright (c) 2017, 2019 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 @@ -21,6 +21,7 @@ import org.springframework.ide.vscode.commons.languageserver.quickfix.QuickfixEd import org.springframework.ide.vscode.commons.languageserver.quickfix.QuickfixRegistry; import org.springframework.ide.vscode.commons.languageserver.quickfix.QuickfixType; import org.springframework.ide.vscode.commons.languageserver.util.SimpleTextDocumentService; +import org.springframework.ide.vscode.commons.util.BadLocationException; import org.springframework.ide.vscode.commons.util.text.IRegion; import org.springframework.ide.vscode.commons.util.text.TextDocument; import org.springframework.ide.vscode.commons.yaml.completion.YamlPathEdits; @@ -80,16 +81,7 @@ public class YamlQuickfixes { } edits.insert(insertAt, indenter.applyIndentation(propSnippet.substring(cursorOffset), indentBy)); } - TextReplace replaceEdit = edits.asReplacement(_doc); - if (replaceEdit!=null) { - WorkspaceEdit wsEdits = new WorkspaceEdit(); - wsEdits.setChanges(ImmutableMap.of( - params.getUri(), - ImmutableList.of(new TextEdit(_doc.toRange(replaceEdit.getRegion()), replaceEdit.newText)) - )); - Position newCursor = getCursorPostionAfter(_doc, edits); - return new QuickfixEdit(wsEdits, newCursor==null ? null : new CursorMovement(params.getUri(), newCursor)); - } + return createReplacementQuickfic(_doc, edits); } } } @@ -120,7 +112,21 @@ public class YamlQuickfixes { }); } - private Position getCursorPostionAfter(TextDocument _doc, YamlPathEdits edits) { + public static QuickfixEdit createReplacementQuickfic(TextDocument doc, YamlPathEdits edits) throws BadLocationException { + TextReplace replaceEdit = edits.asReplacement(doc); + if (replaceEdit!=null) { + WorkspaceEdit wsEdits = new WorkspaceEdit(); + wsEdits.setChanges(ImmutableMap.of( + doc.getUri(), + ImmutableList.of(new TextEdit(doc.toRange(replaceEdit.getRegion()), replaceEdit.newText)) + )); + Position newCursor = getCursorPostionAfter(doc, edits); + return new QuickfixEdit(wsEdits, newCursor==null ? null : new CursorMovement(doc.getUri(), newCursor)); + } + return NULL_FIX; + } + + private static Position getCursorPostionAfter(TextDocument _doc, YamlPathEdits edits) { try { IRegion newSelection = edits.getSelection(); if (newSelection!=null) { diff --git a/headless-services/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/Editor.java b/headless-services/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/Editor.java index 62eca92c0..789aea57f 100644 --- a/headless-services/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/Editor.java +++ b/headless-services/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/Editor.java @@ -783,8 +783,10 @@ public class Editor { return null; //unreachable but compiler doesn't know } - public CompletionItem assertFirstQuickfix(Diagnostic problem, String expectLabel) { - throw new UnsupportedOperationException("Not implemented yet!"); + public CodeAction assertFirstQuickfix(Diagnostic problem, String expectLabel) throws Exception { + CodeAction ca = assertCodeAction(problem); + assertEquals(expectLabel, ca.getLabel()); + return ca; } public void assertText(String expected) { diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/properties/BootPropertiesLanguageServerComponents.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/properties/BootPropertiesLanguageServerComponents.java index d49535fa7..30bb6e850 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/properties/BootPropertiesLanguageServerComponents.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/properties/BootPropertiesLanguageServerComponents.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2016, 2018 Pivotal, Inc. + * Copyright (c) 2016, 2019 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 @@ -19,7 +19,9 @@ import org.springframework.ide.vscode.boot.metadata.SpringPropertyIndexProvider; import org.springframework.ide.vscode.boot.metadata.types.TypeUtilProvider; import org.springframework.ide.vscode.boot.properties.completions.SpringPropertiesCompletionEngine; import org.springframework.ide.vscode.boot.properties.hover.PropertiesHoverInfoProvider; +import org.springframework.ide.vscode.boot.properties.quickfix.AppPropertiesQuickFixes; import org.springframework.ide.vscode.boot.properties.reconcile.SpringPropertiesReconcileEngine; +import org.springframework.ide.vscode.boot.yaml.quickfix.AppYamlQuickfixes; import org.springframework.ide.vscode.boot.yaml.reconcile.ApplicationYamlReconcileEngine; import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionEngine; import org.springframework.ide.vscode.commons.languageserver.composable.LanguageServerComponents; @@ -75,6 +77,9 @@ public class BootPropertiesLanguageServerComponents implements LanguageServerCom private final SimpleLanguageServer server; private YamlASTProvider parser; + private SpringPropertiesReconcileEngine propertiesReconciler; + private ApplicationYamlReconcileEngine ymlReconciler; + public BootPropertiesLanguageServerComponents( SimpleLanguageServer server, @@ -91,7 +96,11 @@ public class BootPropertiesLanguageServerComponents implements LanguageServerCom this.projectObserver = serverParams.projectObserver; this.yamlStructureProvider = yamlStructureProvider; this.yamlAssistContextProvider = yamlAssistContextProvider; - + this.propertiesReconciler = new SpringPropertiesReconcileEngine(indexProvider, + typeUtilProvider, new AppPropertiesQuickFixes(server.getQuickfixRegistry())); + this.ymlReconciler = new ApplicationYamlReconcileEngine(parser, indexProvider, typeUtilProvider, + new AppYamlQuickfixes(server.getQuickfixRegistry(), server.getTextDocumentService(), + yamlStructureProvider)); } @Override @@ -137,9 +146,6 @@ public class BootPropertiesLanguageServerComponents implements LanguageServerCom @Override public Optional getReconcileEngine() { - IReconcileEngine propertiesReconciler = new SpringPropertiesReconcileEngine(indexProvider, typeUtilProvider); - IReconcileEngine ymlReconciler = new ApplicationYamlReconcileEngine(parser, indexProvider, typeUtilProvider); - return Optional.of((doc, problemCollector) -> { String uri = doc.getUri(); if (uri!=null) { diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/properties/quickfix/AppPropertiesQuickFixes.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/properties/quickfix/AppPropertiesQuickFixes.java new file mode 100644 index 000000000..9e9a6b5e1 --- /dev/null +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/properties/quickfix/AppPropertiesQuickFixes.java @@ -0,0 +1,68 @@ +/******************************************************************************* + * Copyright (c) 2019 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.boot.properties.quickfix; + +import org.eclipse.lsp4j.Position; +import org.eclipse.lsp4j.TextEdit; +import org.eclipse.lsp4j.WorkspaceEdit; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.ide.vscode.commons.languageserver.quickfix.QuickfixEdit; +import org.springframework.ide.vscode.commons.languageserver.quickfix.QuickfixEdit.CursorMovement; +import org.springframework.ide.vscode.commons.languageserver.quickfix.QuickfixRegistry; +import org.springframework.ide.vscode.commons.languageserver.quickfix.QuickfixType; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.gson.Gson; +import com.google.gson.JsonElement; + +/** + * Boot app Properties file quick fix handlers + * + * @author Alex Boyko + * + */ +public class AppPropertiesQuickFixes { + + private static final Logger log = LoggerFactory.getLogger(AppPropertiesQuickFixes.class); + + private static final QuickfixEdit NULL_FIX = new QuickfixEdit( + new WorkspaceEdit(ImmutableMap.of()), + null + ); + + public final QuickfixType DEPRECATED_PROPERTY; + + private final Gson gson = new Gson(); + + public AppPropertiesQuickFixes(QuickfixRegistry r) { + DEPRECATED_PROPERTY = r.register("DEPRECATED_PROPERTY", (Object _params) -> { + DeprecatedPropertyData params = gson.fromJson((JsonElement)_params, DeprecatedPropertyData.class); + try { + if (params.getRange() != null && params.getReplacement() != null) { + WorkspaceEdit wsEdits = new WorkspaceEdit(); + wsEdits.setChanges(ImmutableMap.of( + params.getUri(), + ImmutableList.of(new TextEdit(params.getRange(), params.getReplacement())) + )); + Position start = params.getRange().getStart(); + Position cursor = new Position(start.getLine(), start.getCharacter() + params.getReplacement().length()); + return new QuickfixEdit(wsEdits, new CursorMovement(params.getUri(), cursor)); + } + } catch (Exception e) { + log.error("", e); + } + return NULL_FIX; + }); + } + +} diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/properties/quickfix/DeprecatedPropertyData.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/properties/quickfix/DeprecatedPropertyData.java new file mode 100644 index 000000000..04dd6ccfb --- /dev/null +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/properties/quickfix/DeprecatedPropertyData.java @@ -0,0 +1,57 @@ +/******************************************************************************* + * Copyright (c) 2019 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.boot.properties.quickfix; + +import org.eclipse.lsp4j.Range; + +/** + * Deprecated property quick fix data + * + * @author Alex Boyko + * + */ +public class DeprecatedPropertyData { + + private Range range; + private String replacement; + private String uri; + + public DeprecatedPropertyData(String uri, Range range, String replacement) { + this.setUri(uri); + this.range = range; + this.replacement = replacement; + } + + public Range getRange() { + return range; + } + + public void setRange(Range range) { + this.range = range; + } + + public String getReplacement() { + return replacement; + } + + public void setReplacement(String replacement) { + this.replacement = replacement; + } + + public String getUri() { + return uri; + } + + public void setUri(String uri) { + this.uri = uri; + } + +} diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/properties/quickfix/ReplaceDeprecatedPropertyQuickfix.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/properties/quickfix/ReplaceDeprecatedPropertyQuickfix.java deleted file mode 100644 index b76edb6e7..000000000 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/properties/quickfix/ReplaceDeprecatedPropertyQuickfix.java +++ /dev/null @@ -1,163 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016 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.boot.properties.quickfix; - -import org.eclipse.lsp4j.CompletionItemKind; -import org.springframework.ide.vscode.commons.languageserver.completion.DocumentEdits; -import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionProposal; -import org.springframework.ide.vscode.commons.languageserver.completion.ScoreableProposal; -import org.springframework.ide.vscode.commons.util.Renderable; - -public class ReplaceDeprecatedPropertyQuickfix implements ICompletionProposal { - -// public static ProblemFixer FIXER = (context, problem, proposals) -> { -// throw new UnsupportedOperationException("Not yet implemented"); -// PropertyInfo metadata = problem.getMetadata(); -// if (metadata!=null) { -// String replacement = metadata.getDeprecationReplacement(); -// if (replacement!=null) { -// //No need to check problem type... we only attach this fixer to problems of applicable type. -// proposals.add(new ReplaceDeprecatedYamlQuickfix(context, problem)); -// } -// } -// }; - - @Override - public String getLabel() { - throw new UnsupportedOperationException("Not yet implemented"); - } - @Override - public CompletionItemKind getKind() { - throw new UnsupportedOperationException("Not yet implemented"); - } - @Override - public DocumentEdits getTextEdit() { - throw new UnsupportedOperationException("Not yet implemented"); - } - @Override - public String getDetail() { - throw new UnsupportedOperationException("Not yet implemented"); - } - @Override - public Renderable getDocumentation() { - throw new UnsupportedOperationException("Not yet implemented"); - } - @Override - public ScoreableProposal deemphasize(double howmuch) { - throw new UnsupportedOperationException("Not yet implemented"); - } - -// private final QuickfixContext context; -// private final SpringPropertyProblem problem; -// -// private LazyProposalApplier applier = new LazyProposalApplier() { -// protected ProposalApplier create() throws Exception { -// String newName = problem.getMetadata().getDeprecationReplacement(); -// String oldName = problem.getPropertyName(); -// YamlPath newPath = YamlPath.fromProperty(newName); -// YamlPath oldPath = YamlPath.fromProperty(oldName); -// YamlPath prefix = newPath.commonPrefix(oldPath); -// if (prefix.size()==newPath.size()-1 && newPath.size()==oldPath.size()) { -// //only the last segment has changed. We can do a simple 'in-place' replace -// // of just the change segment. -// DocumentEdits edits = new DocumentEdits(context.getDocument()); -// edits.replace(problem.getOffset(), problem.getEnd(), newPath.getLastSegment().toPropString()); -// return edits; -// } -// YamlDocument doc = new YamlDocument(context.getDocument(), YamlStructureProvider.DEFAULT); -// SNode problemNode = doc.getStructure().find(problem.getOffset()); -// if (problemNode.getNodeType()==SNodeType.KEY) { -// SKeyNode problemKey = (SKeyNode) problemNode; -// if (problemKey.isInKey(problem.getOffset())) { -// YamlPathEdits edits = new YamlPathEdits(doc); -//// print(doc, edits); -// String valueText = problemKey.getValueWithRelativeIndent(); -// edits.deleteNode(problemKey); -// int maxParentDeletions = oldPath.size() - prefix.size() - 1; // don't delete bits of the common prefix! -// SChildBearingNode parent = problemNode.getParent(); -// while (maxParentDeletions>0 && parent!=null && parent.getChildren().size()==1) { -// edits.deleteNode(parent); -// parent = parent.getParent(); -// maxParentDeletions--; -// } -//// print(doc, edits); -// SDocNode docRoot = problemNode.getDocNode(); //edits should stay within the same 'document' for yaml file that has multiple documents inside of it. -// edits.createPath(docRoot, YamlPath.fromProperty(newName), valueText); -//// print(doc, edits); -// return edits; -// } -// } -// //Not sure what to do... case not covered... so do nothing but tell the user. -// context.getUI().error("Yaml file too complex", -// "Sorry, but the yaml file is too complex for this quickfix. " + -// "Please make the change manually." -// ); -// return ProposalApplier.NULL; -// } -// -//// private void print(YamlDocument doc, YamlPathEdits edits) throws Exception { -//// Document workingCopy = new Document(doc.getDocument().get()); -//// edits.apply(workingCopy); -//// System.out.println("=============="); -//// System.out.println(workingCopy.get()); -//// System.out.println("=============="); -//// } -// }; -// -// public ReplaceDeprecatedYamlQuickfix(QuickfixContext context, SpringPropertyProblem problem) { -// this.context = context; -// this.problem = problem; -// } -// -// @Override -// public void apply(IDocument doc) { -// try { -// applier.apply(doc); -// } catch (Exception e) { -// Log.log(e); -// } -// } -// -// private String getReplacementProperty() { -// return problem.getMetadata().getDeprecationReplacement(); -// } -// -// @Override -// public Point getSelection(IDocument doc) { -// try { -// return applier.getSelection(doc); -// } catch (Exception e) { -// Log.log(e); -// return null; -// } -// } -// -// @Override -// public String getAdditionalProposalInfo() { -// return null; -// } -// -// @Override -// public String getDisplayString() { -// return "Change to '"+getReplacementProperty()+"'"; -// } -// -// @Override -// public Image getImage() { -// return JavaPluginImages.get(JavaPluginImages.IMG_CORRECTION_CHANGE); -// } -// -// @Override -// public IContextInformation getContextInformation() { -// return null; -// } - -} diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/properties/reconcile/SpringPropertiesReconcileEngine.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/properties/reconcile/SpringPropertiesReconcileEngine.java index 7eeff9ac6..6f6386147 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/properties/reconcile/SpringPropertiesReconcileEngine.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/properties/reconcile/SpringPropertiesReconcileEngine.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2014-2016 Pivotal, Inc. + * Copyright (c) 2014, 2019 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 @@ -18,6 +18,8 @@ import static org.springframework.ide.vscode.commons.util.StringUtil.commonPrefi import java.util.regex.Pattern; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.ide.vscode.boot.metadata.PropertyInfo; import org.springframework.ide.vscode.boot.metadata.SpringPropertyIndex; import org.springframework.ide.vscode.boot.metadata.SpringPropertyIndexProvider; @@ -25,12 +27,15 @@ import org.springframework.ide.vscode.boot.metadata.types.Type; import org.springframework.ide.vscode.boot.metadata.types.TypeParser; import org.springframework.ide.vscode.boot.metadata.types.TypeUtil; import org.springframework.ide.vscode.boot.metadata.types.TypeUtilProvider; +import org.springframework.ide.vscode.boot.properties.quickfix.DeprecatedPropertyData; +import org.springframework.ide.vscode.boot.properties.quickfix.AppPropertiesQuickFixes; +import org.springframework.ide.vscode.commons.languageserver.quickfix.Quickfix.QuickfixData; +import org.springframework.ide.vscode.commons.languageserver.quickfix.QuickfixType; import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector; import org.springframework.ide.vscode.commons.languageserver.reconcile.IReconcileEngine; import org.springframework.ide.vscode.commons.util.BadLocationException; import org.springframework.ide.vscode.commons.util.ExceptionUtil; import org.springframework.ide.vscode.commons.util.FuzzyMap; -import org.springframework.ide.vscode.commons.util.Log; import org.springframework.ide.vscode.commons.util.ValueParseException; import org.springframework.ide.vscode.commons.util.ValueParser; import org.springframework.ide.vscode.commons.util.text.DocumentRegion; @@ -53,6 +58,8 @@ import org.springframework.ide.vscode.java.properties.parser.PropertiesFileEscap */ public class SpringPropertiesReconcileEngine implements IReconcileEngine { + private static final Logger log = LoggerFactory.getLogger(SpringPropertiesReconcileEngine.class); + /** * Regexp that matches a ',' surrounded by whitespace, including escaped whitespace / newlines */ @@ -67,10 +74,12 @@ public class SpringPropertiesReconcileEngine implements IReconcileEngine { private SpringPropertyIndexProvider fIndexProvider; private TypeUtilProvider typeUtilProvider; private Parser parser = new AntlrParser(); + private AppPropertiesQuickFixes quickFixes; - public SpringPropertiesReconcileEngine(SpringPropertyIndexProvider provider, TypeUtilProvider typeUtilProvider) { + public SpringPropertiesReconcileEngine(SpringPropertyIndexProvider provider, TypeUtilProvider typeUtilProvider, AppPropertiesQuickFixes quickFixes) { this.fIndexProvider = provider; this.typeUtilProvider = typeUtilProvider; + this.quickFixes = quickFixes; } @Override @@ -104,7 +113,7 @@ public class SpringPropertiesReconcileEngine implements IReconcileEngine { // it all with just passing around 'fullName' DocumentRegion. This may require changes // in PropertyNavigator (probably these changes are also for the better making it simpler as well) if (validProperty.isDeprecated()) { - problemCollector.accept(problemDeprecated(propertyNameRegion, validProperty)); + problemCollector.accept(problemDeprecated(doc, propertyNameRegion, validProperty, quickFixes.DEPRECATED_PROPERTY)); } int offset = validProperty.getId().length() + propertyNameRegion.getStart(); PropertyNavigator navigator = new PropertyNavigator(doc, problemCollector, typeUtilProvider.getTypeUtil(doc), propertyNameRegion); @@ -119,17 +128,17 @@ public class SpringPropertiesReconcileEngine implements IReconcileEngine { problemCollector.accept(problemUnkownProperty(propertyNameRegion, similarEntry, validPrefix)); } //end: validProperty==null } catch (Exception e) { - Log.log(e); + log.error("", e); } }); } catch (Throwable e2) { - Log.log(e2); + log.error("", e2); } finally { problemCollector.endCollecting(); } } - protected SpringPropertyProblem problemDeprecated(DocumentRegion region, PropertyInfo property) { + protected SpringPropertyProblem problemDeprecated(IDocument doc, DocumentRegion region, PropertyInfo property, QuickfixType fixType) { SpringPropertyProblem p = problem(PROP_DEPRECATED, TypeUtil.deprecatedPropertyMessage( property.getId(), null, @@ -140,7 +149,15 @@ public class SpringPropertiesReconcileEngine implements IReconcileEngine { ); p.setPropertyName(property.getId()); p.setMetadata(property); -// p.setProblemFixer(ReplaceDeprecatedPropertyQuickfix.FIXER); + + try { + p.addQuickfix(new QuickfixData<>(fixType, + new DeprecatedPropertyData(doc.getUri(), doc.toRange(region), property.getDeprecationReplacement()), + "Replace with `" + property.getDeprecationReplacement() + "`")); + } catch (BadLocationException e) { + log.error("", e); + } + return p; } @@ -186,7 +203,7 @@ public class SpringPropertiesReconcileEngine implements IReconcileEngine { problems.accept(problem(ApplicationPropertiesProblemType.PROP_VALUE_TYPE_MISMATCH, ExceptionUtil.getMessage(e), e.getHighlightRegion(escapedValue))); - + } catch (Exception e) { problems.accept(problem(ApplicationPropertiesProblemType.PROP_VALUE_TYPE_MISMATCH, "Expecting '"+typeUtil.niceTypeName(expectType)+"'", diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/properties/reconcile/SpringPropertyProblem.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/properties/reconcile/SpringPropertyProblem.java index da1c21138..0ef517960 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/properties/reconcile/SpringPropertyProblem.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/properties/reconcile/SpringPropertyProblem.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2016-2017 Pivotal, Inc. + * Copyright (c) 2016, 2019 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 @@ -12,7 +12,6 @@ package org.springframework.ide.vscode.boot.properties.reconcile; import org.springframework.ide.vscode.boot.metadata.PropertyInfo; -//import org.springframework.ide.vscode.commons.languageserver.quickfix.ProblemFixer; import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemType; import org.springframework.ide.vscode.commons.languageserver.reconcile.ReconcileProblemImpl; import org.springframework.ide.vscode.commons.util.text.DocumentRegion; @@ -22,7 +21,6 @@ import org.springframework.ide.vscode.commons.util.text.DocumentRegion; public class SpringPropertyProblem extends ReconcileProblemImpl { private PropertyInfo property = null; -// private ProblemFixer fixer; private String propertyName; public SpringPropertyProblem(ProblemType type, String msg, int offset, int len) { @@ -44,10 +42,6 @@ public class SpringPropertyProblem extends ReconcileProblemImpl { this.property = property; } -// public void setProblemFixer(ProblemFixer fixer) { -// this.fixer = fixer; -// } - public void setPropertyName(String name) { propertyName = name; } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/yaml/quickfix/AppYamlQuickfixes.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/yaml/quickfix/AppYamlQuickfixes.java new file mode 100644 index 000000000..e36439965 --- /dev/null +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/yaml/quickfix/AppYamlQuickfixes.java @@ -0,0 +1,114 @@ +/******************************************************************************* + * Copyright (c) 2019 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.boot.yaml.quickfix; + +import org.eclipse.lsp4j.TextEdit; +import org.eclipse.lsp4j.WorkspaceEdit; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.ide.vscode.boot.properties.quickfix.DeprecatedPropertyData; +import org.springframework.ide.vscode.commons.languageserver.quickfix.QuickfixEdit; +import org.springframework.ide.vscode.commons.languageserver.quickfix.QuickfixEdit.CursorMovement; +import org.springframework.ide.vscode.commons.languageserver.quickfix.QuickfixRegistry; +import org.springframework.ide.vscode.commons.languageserver.quickfix.QuickfixType; +import org.springframework.ide.vscode.commons.languageserver.util.SimpleTextDocumentService; +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.quickfix.YamlQuickfixes; +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.SDocNode; +import org.springframework.ide.vscode.commons.yaml.structure.YamlStructureParser.SKeyNode; +import org.springframework.ide.vscode.commons.yaml.structure.YamlStructureParser.SNode; +import org.springframework.ide.vscode.commons.yaml.structure.YamlStructureParser.SNodeType; +import org.springframework.ide.vscode.commons.yaml.structure.YamlStructureProvider; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.gson.Gson; +import com.google.gson.JsonElement; + +/** + * Boot YAML file properties quick fix code action handlers + * + * @author Alex Boyko + * + */ +public class AppYamlQuickfixes { + + private static final Logger log = LoggerFactory.getLogger(AppYamlQuickfixes.class); + + public final QuickfixType DEPRECATED_PROPERTY; + + private static final QuickfixEdit NULL_FIX = new QuickfixEdit( + new WorkspaceEdit(ImmutableMap.of()), + null + ); + + private final Gson gson = new Gson(); + + public AppYamlQuickfixes(QuickfixRegistry r, SimpleTextDocumentService textDocumentService, YamlStructureProvider structureProvider) { + DEPRECATED_PROPERTY = r.register("DEPRECATED_YAML_PROPERTY", (Object _params) -> { + DeprecatedPropertyData params = gson.fromJson((JsonElement)_params, DeprecatedPropertyData.class); + try { + TextDocument _doc = textDocumentService.getDocument(params.getUri()); + if (_doc!=null) { + YamlDocument doc = new YamlDocument(_doc, structureProvider); + SNode root = doc.getStructure(); + int offset = _doc.toOffset(params.getRange().getStart()); + SNode node = root.find(offset); + if (node != null) { + // Drop the doc root + YamlPath oldPath = node.getPath().dropFirst(1); + YamlPath newPath = YamlPath.fromProperty(params.getReplacement()); + YamlPath prefix = newPath.commonPrefix(oldPath); + if (prefix.size()==newPath.size()-1 && newPath.size()==oldPath.size()) { + //only the last segment has changed. We can do a simple 'in-place' replace + // of just the change segment. + WorkspaceEdit wsEdits = new WorkspaceEdit(); + String replacement = newPath.getLastSegment().toPropString(); + wsEdits.setChanges(ImmutableMap.of( + params.getUri(), + ImmutableList.of(new TextEdit(params.getRange(), replacement)) + )); + return new QuickfixEdit(wsEdits, new CursorMovement(params.getUri(), _doc.toPosition(node.getNodeEnd()))); + } + if (node.getNodeType()==SNodeType.KEY) { + SKeyNode problemKey = (SKeyNode) node; + if (problemKey.isInKey(offset)) { + YamlPathEdits edits = new YamlPathEdits(doc); + String valueText = problemKey.getValueWithRelativeIndent(); + edits.deleteNode(problemKey); + int maxParentDeletions = oldPath.size() - prefix.size() - 1; // don't delete bits of the common prefix! + SChildBearingNode parent = node.getParent(); + while (maxParentDeletions>0 && parent!=null && parent.getChildren().size()==1) { + edits.deleteNode(parent); + parent = parent.getParent(); + maxParentDeletions--; + } + SDocNode docRoot = node.getDocNode(); //edits should stay within the same 'document' for yaml file that has multiple documents inside of it. + edits.createPath(docRoot, YamlPath.fromProperty(params.getReplacement()), valueText); + + return YamlQuickfixes.createReplacementQuickfic(_doc, edits); + } + } + + } + } + } catch (Exception e) { + log.error("", e); + } + return NULL_FIX; + }); + } + +} diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/yaml/quickfix/ReplaceDeprecatedYamlQuickfix.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/yaml/quickfix/ReplaceDeprecatedYamlQuickfix.java deleted file mode 100644 index ebb7b2192..000000000 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/yaml/quickfix/ReplaceDeprecatedYamlQuickfix.java +++ /dev/null @@ -1,163 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016 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.boot.yaml.quickfix; - -import org.eclipse.lsp4j.CompletionItemKind; -import org.springframework.ide.vscode.commons.languageserver.completion.DocumentEdits; -import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionProposal; -import org.springframework.ide.vscode.commons.languageserver.completion.ScoreableProposal; -import org.springframework.ide.vscode.commons.util.Renderable; - -public class ReplaceDeprecatedYamlQuickfix implements ICompletionProposal { - -// public static ProblemFixer FIXER = (context, problem, proposals) -> { -// throw new UnsupportedOperationException("Not yet implemented"); -// PropertyInfo metadata = problem.getMetadata(); -// if (metadata!=null) { -// String replacement = metadata.getDeprecationReplacement(); -// if (replacement!=null) { -// //No need to check problem type... we only attach this fixer to problems of applicable type. -// proposals.add(new ReplaceDeprecatedYamlQuickfix(context, problem)); -// } -// } -// }; - - @Override - public String getLabel() { - throw new UnsupportedOperationException("Not yet implemented"); - } - @Override - public CompletionItemKind getKind() { - throw new UnsupportedOperationException("Not yet implemented"); - } - @Override - public DocumentEdits getTextEdit() { - throw new UnsupportedOperationException("Not yet implemented"); - } - @Override - public String getDetail() { - throw new UnsupportedOperationException("Not yet implemented"); - } - @Override - public Renderable getDocumentation() { - throw new UnsupportedOperationException("Not yet implemented"); - } - @Override - public ScoreableProposal deemphasize(double howmuch) { - throw new UnsupportedOperationException("Not yet implemented"); - } - -// private final QuickfixContext context; -// private final SpringPropertyProblem problem; -// -// private LazyProposalApplier applier = new LazyProposalApplier() { -// protected ProposalApplier create() throws Exception { -// String newName = problem.getMetadata().getDeprecationReplacement(); -// String oldName = problem.getPropertyName(); -// YamlPath newPath = YamlPath.fromProperty(newName); -// YamlPath oldPath = YamlPath.fromProperty(oldName); -// YamlPath prefix = newPath.commonPrefix(oldPath); -// if (prefix.size()==newPath.size()-1 && newPath.size()==oldPath.size()) { -// //only the last segment has changed. We can do a simple 'in-place' replace -// // of just the change segment. -// DocumentEdits edits = new DocumentEdits(context.getDocument()); -// edits.replace(problem.getOffset(), problem.getEnd(), newPath.getLastSegment().toPropString()); -// return edits; -// } -// YamlDocument doc = new YamlDocument(context.getDocument(), YamlStructureProvider.DEFAULT); -// SNode problemNode = doc.getStructure().find(problem.getOffset()); -// if (problemNode.getNodeType()==SNodeType.KEY) { -// SKeyNode problemKey = (SKeyNode) problemNode; -// if (problemKey.isInKey(problem.getOffset())) { -// YamlPathEdits edits = new YamlPathEdits(doc); -//// print(doc, edits); -// String valueText = problemKey.getValueWithRelativeIndent(); -// edits.deleteNode(problemKey); -// int maxParentDeletions = oldPath.size() - prefix.size() - 1; // don't delete bits of the common prefix! -// SChildBearingNode parent = problemNode.getParent(); -// while (maxParentDeletions>0 && parent!=null && parent.getChildren().size()==1) { -// edits.deleteNode(parent); -// parent = parent.getParent(); -// maxParentDeletions--; -// } -//// print(doc, edits); -// SDocNode docRoot = problemNode.getDocNode(); //edits should stay within the same 'document' for yaml file that has multiple documents inside of it. -// edits.createPath(docRoot, YamlPath.fromProperty(newName), valueText); -//// print(doc, edits); -// return edits; -// } -// } -// //Not sure what to do... case not covered... so do nothing but tell the user. -// context.getUI().error("Yaml file too complex", -// "Sorry, but the yaml file is too complex for this quickfix. " + -// "Please make the change manually." -// ); -// return ProposalApplier.NULL; -// } -// -//// private void print(YamlDocument doc, YamlPathEdits edits) throws Exception { -//// Document workingCopy = new Document(doc.getDocument().get()); -//// edits.apply(workingCopy); -//// System.out.println("=============="); -//// System.out.println(workingCopy.get()); -//// System.out.println("=============="); -//// } -// }; -// -// public ReplaceDeprecatedYamlQuickfix(QuickfixContext context, SpringPropertyProblem problem) { -// this.context = context; -// this.problem = problem; -// } -// -// @Override -// public void apply(IDocument doc) { -// try { -// applier.apply(doc); -// } catch (Exception e) { -// Log.log(e); -// } -// } -// -// private String getReplacementProperty() { -// return problem.getMetadata().getDeprecationReplacement(); -// } -// -// @Override -// public Point getSelection(IDocument doc) { -// try { -// return applier.getSelection(doc); -// } catch (Exception e) { -// Log.log(e); -// return null; -// } -// } -// -// @Override -// public String getAdditionalProposalInfo() { -// return null; -// } -// -// @Override -// public String getDisplayString() { -// return "Change to '"+getReplacementProperty()+"'"; -// } -// -// @Override -// public Image getImage() { -// return JavaPluginImages.get(JavaPluginImages.IMG_CORRECTION_CHANGE); -// } -// -// @Override -// public IContextInformation getContextInformation() { -// return null; -// } - -} diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/yaml/reconcile/ApplicationYamlASTReconciler.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/yaml/reconcile/ApplicationYamlASTReconciler.java index 852a8f85d..dc84e4bfc 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/yaml/reconcile/ApplicationYamlASTReconciler.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/yaml/reconcile/ApplicationYamlASTReconciler.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2016-2017 Pivotal, Inc. + * Copyright (c) 2016, 2019 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 @@ -22,6 +22,8 @@ import java.util.Map; import java.util.Set; import java.util.regex.Pattern; +import org.eclipse.lsp4j.Position; +import org.eclipse.lsp4j.Range; import org.springframework.ide.vscode.boot.metadata.IndexNavigator; import org.springframework.ide.vscode.boot.metadata.PropertyInfo; import org.springframework.ide.vscode.boot.metadata.types.Type; @@ -30,6 +32,10 @@ import org.springframework.ide.vscode.boot.metadata.types.TypeUtil; import org.springframework.ide.vscode.boot.metadata.types.TypeUtil.BeanPropertyNameMode; import org.springframework.ide.vscode.boot.metadata.types.TypeUtil.EnumCaseMode; import org.springframework.ide.vscode.boot.metadata.types.TypedProperty; +import org.springframework.ide.vscode.boot.properties.quickfix.DeprecatedPropertyData; +import org.springframework.ide.vscode.boot.yaml.quickfix.AppYamlQuickfixes; +import org.springframework.ide.vscode.commons.languageserver.quickfix.Quickfix.QuickfixData; +import org.springframework.ide.vscode.commons.languageserver.quickfix.QuickfixType; import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector; import org.springframework.ide.vscode.commons.util.ExceptionUtil; import org.springframework.ide.vscode.commons.util.StringUtil; @@ -58,11 +64,13 @@ public class ApplicationYamlASTReconciler implements YamlASTReconciler { private final IProblemCollector problems; private final TypeUtil typeUtil; private final IndexNavigator nav; + private AppYamlQuickfixes quickFixes; - public ApplicationYamlASTReconciler(IProblemCollector problems, IndexNavigator nav, TypeUtil typeUtil) { + public ApplicationYamlASTReconciler(IProblemCollector problems, IndexNavigator nav, TypeUtil typeUtil, AppYamlQuickfixes quickFixes) { this.problems = problems; this.typeUtil = typeUtil; this.nav = nav; + this.quickFixes = quickFixes; } @Override @@ -155,7 +163,7 @@ public class ApplicationYamlASTReconciler implements YamlASTReconciler { } else if (match!=null) { Type type = TypeParser.parse(match.getType()); if (match.isDeprecated()) { - deprecatedProperty(match, keyNode); + deprecatedProperty(root.getDocument().getUri(), match, keyNode, quickFixes.DEPRECATED_PROPERTY); } reconcile(root, entry.getValueNode(), type); } else if (extension!=null) { @@ -241,7 +249,7 @@ public class ApplicationYamlASTReconciler implements YamlASTReconciler { TypedProperty typedProperty = props.get(key); if (typedProperty!=null) { if (typedProperty.isDeprecated()) { - deprecatedProperty(type, typedProperty, keyNode); + deprecatedProperty(root.getDocument().getUri(), type, typedProperty, keyNode, quickFixes.DEPRECATED_PROPERTY); } reconcile(root, valNode, typedProperty.getType()); } @@ -363,24 +371,28 @@ public class ApplicationYamlASTReconciler implements YamlASTReconciler { problems.accept(problem(problemType, node, "Expecting a '"+typeUtil.niceTypeName(type)+"' but got "+describe(node))); } - private void deprecatedProperty(PropertyInfo property, Node keyNode) { - SpringPropertyProblem problem = deprecatedPropertyProblem(property.getId(), null, keyNode, - property.getDeprecationReplacement(), property.getDeprecationReason()); + private void deprecatedProperty(String docUri, PropertyInfo property, Node keyNode, QuickfixType fixType) { + SpringPropertyProblem problem = deprecatedPropertyProblem(docUri, property.getId(), null, keyNode, + property.getDeprecationReplacement(), property.getDeprecationReason(), fixType); problem.setMetadata(property); //problem.setProblemFixer(ReplaceDeprecatedYamlQuickfix.FIXER); problems.accept(problem); } - private void deprecatedProperty(Type contextType, TypedProperty property, Node keyNode) { - SpringPropertyProblem problem = deprecatedPropertyProblem(property.getName(), typeUtil.niceTypeName(contextType), - keyNode, property.getDeprecationReplacement(), property.getDeprecationReason()); + private void deprecatedProperty(String docUri, Type contextType, TypedProperty property, Node keyNode, QuickfixType fixType) { + SpringPropertyProblem problem = deprecatedPropertyProblem(docUri, property.getName(), typeUtil.niceTypeName(contextType), + keyNode, property.getDeprecationReplacement(), property.getDeprecationReason(), fixType); problems.accept(problem); } - protected SpringPropertyProblem deprecatedPropertyProblem(String name, String contextType, Node keyNode, - String replace, String reason) { + protected SpringPropertyProblem deprecatedPropertyProblem(String docUri, String name, String contextType, Node keyNode, + String replace, String reason, QuickfixType fixType) { SpringPropertyProblem problem = problem(YAML_DEPRECATED, keyNode, TypeUtil.deprecatedPropertyMessage(name, contextType, replace, reason)); problem.setPropertyName(name); + Range range = new Range(new Position(keyNode.getStartMark().getLine(), keyNode.getStartMark().getColumn()), + new Position(keyNode.getEndMark().getLine(), keyNode.getEndMark().getColumn())); + + problem.addQuickfix(new QuickfixData<>(fixType, new DeprecatedPropertyData(docUri, range, replace), "Replace with `" + replace + "`")); return problem; } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/yaml/reconcile/ApplicationYamlReconcileEngine.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/yaml/reconcile/ApplicationYamlReconcileEngine.java index 4166cf048..ef5392b36 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/yaml/reconcile/ApplicationYamlReconcileEngine.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/yaml/reconcile/ApplicationYamlReconcileEngine.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2015, 2016 Pivotal, Inc. + * Copyright (c) 2015, 2019 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 @@ -16,6 +16,7 @@ import org.springframework.ide.vscode.boot.metadata.IndexNavigator; import org.springframework.ide.vscode.boot.metadata.PropertyInfo; import org.springframework.ide.vscode.boot.metadata.SpringPropertyIndexProvider; import org.springframework.ide.vscode.boot.metadata.types.TypeUtilProvider; +import org.springframework.ide.vscode.boot.yaml.quickfix.AppYamlQuickfixes; import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector; import org.springframework.ide.vscode.commons.languageserver.reconcile.ReconcileProblem; import org.springframework.ide.vscode.commons.util.FuzzyMap; @@ -28,18 +29,21 @@ public class ApplicationYamlReconcileEngine extends YamlReconcileEngine { private SpringPropertyIndexProvider indexProvider; private TypeUtilProvider typeUtilProvider; + private AppYamlQuickfixes quickFixes; - public ApplicationYamlReconcileEngine(YamlASTProvider astProvider, SpringPropertyIndexProvider indexProvider, TypeUtilProvider typeUtilProvider) { + public ApplicationYamlReconcileEngine(YamlASTProvider astProvider, SpringPropertyIndexProvider indexProvider, TypeUtilProvider typeUtilProvider, AppYamlQuickfixes quickFixes) { super(astProvider); this.indexProvider = indexProvider; this.typeUtilProvider = typeUtilProvider; + this.quickFixes = quickFixes; } + @Override protected YamlASTReconciler getASTReconciler(IDocument doc, IProblemCollector problemCollector) { FuzzyMap index = indexProvider.getIndex(doc); if (index!=null && !index.isEmpty()) { IndexNavigator nav = IndexNavigator.with(index); - return new ApplicationYamlASTReconciler(problemCollector, nav, typeUtilProvider.getTypeUtil(doc)); + return new ApplicationYamlASTReconciler(problemCollector, nav, typeUtilProvider.getTypeUtil(doc), quickFixes); } return null; } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/yaml/reconcile/SpringPropertyProblem.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/yaml/reconcile/SpringPropertyProblem.java index 128b67ef0..a58361b15 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/yaml/reconcile/SpringPropertyProblem.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/yaml/reconcile/SpringPropertyProblem.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2016-2017 Pivotal, Inc. + * Copyright (c) 2016, 2019 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 @@ -18,7 +18,6 @@ import org.springframework.ide.vscode.commons.languageserver.reconcile.Reconcile public class SpringPropertyProblem extends ReconcileProblemImpl { private PropertyInfo property = null; -// private ProblemFixer fixer; private String propertyName; public SpringPropertyProblem(ProblemType type, String msg, int offset, int len) { @@ -33,10 +32,6 @@ public class SpringPropertyProblem extends ReconcileProblemImpl { this.property = property; } -// public void setProblemFixer(ProblemFixer fixer) { -// this.fixer = fixer; -// } - public void setPropertyName(String name) { propertyName = name; } diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/test/ApplicationPropertiesEditorTest.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/test/ApplicationPropertiesEditorTest.java index 7e668de8f..ea580e296 100644 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/test/ApplicationPropertiesEditorTest.java +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/test/ApplicationPropertiesEditorTest.java @@ -25,7 +25,6 @@ import java.util.List; import org.eclipse.lsp4j.CompletionItem; import org.eclipse.lsp4j.Diagnostic; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; @@ -42,6 +41,7 @@ import org.springframework.ide.vscode.commons.java.IJavaProject; import org.springframework.ide.vscode.commons.java.IType; import org.springframework.ide.vscode.commons.maven.java.MavenJavaProject; import org.springframework.ide.vscode.commons.util.text.LanguageId; +import org.springframework.ide.vscode.languageserver.testharness.CodeAction; import org.springframework.ide.vscode.languageserver.testharness.Editor; import org.springframework.ide.vscode.project.harness.ProjectsHarness.ProjectCustomizer; import org.springframework.test.context.junit4.SpringRunner; @@ -997,7 +997,7 @@ public class ApplicationPropertiesEditorTest extends AbstractPropsEditorTest { editor.assertHoverText("path", "**Deprecated!**"); } - @Ignore @Test public void testDeprecatedPropertyQuickfix() throws Exception { + @Test public void testDeprecatedPropertyQuickfix() throws Exception { data("error.path", "java.lang.String", null, "Path of the error controller."); deprecate("error.path", "server.error.path", null); @@ -1007,8 +1007,8 @@ public class ApplicationPropertiesEditorTest extends AbstractPropsEditorTest { ); Diagnostic problem = editor.assertProblem("error.path"); - CompletionItem fix = editor.assertFirstQuickfix(problem, "Change to 'server.error.path'"); - editor.apply(fix); + CodeAction fix = editor.assertFirstQuickfix(problem, "Replace with `server.error.path`"); + fix.perform(); editor.assertText( "# a comment\n"+ "server.error.path<*>=foo\n" diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/test/ApplicationYamlEditorTest.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/test/ApplicationYamlEditorTest.java index 4def55f18..a93fc11be 100644 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/test/ApplicationYamlEditorTest.java +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/test/ApplicationYamlEditorTest.java @@ -20,7 +20,6 @@ import static org.springframework.ide.vscode.languageserver.testharness.Editor.I import java.time.Duration; import java.util.Optional; -import org.eclipse.lsp4j.CompletionItem; import org.eclipse.lsp4j.Diagnostic; import org.junit.Ignore; import org.junit.Test; @@ -39,6 +38,7 @@ import org.springframework.ide.vscode.commons.maven.java.MavenJavaProject; import org.springframework.ide.vscode.commons.util.RunnableWithException; import org.springframework.ide.vscode.commons.util.StringUtil; import org.springframework.ide.vscode.commons.util.text.LanguageId; +import org.springframework.ide.vscode.languageserver.testharness.CodeAction; import org.springframework.ide.vscode.languageserver.testharness.Editor; import org.springframework.test.context.junit4.SpringRunner; @@ -2767,7 +2767,7 @@ public class ApplicationYamlEditorTest extends AbstractPropsEditorTest { assertCompletionDetailsWithDeprecation("foo:\n nam<*>", "alt-name", "String", null, Boolean.TRUE); } - @Ignore @Test public void testDeprecatedPropertyQuickfixSimple() throws Exception { + @Test public void testDeprecatedPropertyQuickfixSimple() throws Exception { //A simple case for starters. The path edits aren't too complicated since there's //just the one property in the file and only the last part of the 'path' changes. //So this is a simple 'in-place' edit. @@ -2783,12 +2783,12 @@ public class ApplicationYamlEditorTest extends AbstractPropsEditorTest { ); Diagnostic problem = editor.assertProblem("old-name"); - CompletionItem fix = editor.assertFirstQuickfix(problem, "Change to 'my.new-name'"); - editor.apply(fix); + CodeAction fix = editor.assertFirstQuickfix(problem, "Replace with `my.new-name`"); + fix.perform(); editor.assertText( "# a comment\n"+ "my:\n"+ - " new-name: foo\n" + " new-name: foo<*>\n" ); } @@ -2801,12 +2801,12 @@ public class ApplicationYamlEditorTest extends AbstractPropsEditorTest { ); Diagnostic problem = editor.assertProblem("old-name"); - CompletionItem fix = editor.assertFirstQuickfix(problem, "Change to 'my.new-name'"); - editor.apply(fix); + CodeAction fix = editor.assertFirstQuickfix(problem, "Replace with `my.new-name`"); + fix.perform(); editor.assertText( "# a comment\n"+ "my:\n"+ - " new-name: foo\n"+ + " new-name: foo<*>\n"+ "your: stuff" ); } @@ -2820,18 +2820,18 @@ public class ApplicationYamlEditorTest extends AbstractPropsEditorTest { ); Diagnostic problem = editor.assertProblem("old-name"); - CompletionItem fix = editor.assertFirstQuickfix(problem, "Change to 'my.new-name'"); - editor.apply(fix); + CodeAction fix = editor.assertFirstQuickfix(problem, "Replace with `my.new-name`"); + fix.perform(); editor.assertText( "# a comment\n"+ "my:\n"+ - " new-name: foo\n" + + " new-name: foo<*>\n" + " other: bar\n" ); } } - @Ignore @Test public void testDeprecatedPropertyQuickfixMovingValue() throws Exception { + @Test public void testDeprecatedPropertyQuickfixMovingValue() throws Exception { data("my.old-name", "java.lang.String", null, "Old and deprecated name"); deprecate("my.old-name", "your.new-name", null); @@ -2853,15 +2853,15 @@ public class ApplicationYamlEditorTest extends AbstractPropsEditorTest { ); Diagnostic problem = editor.assertProblem("pieces"); - CompletionItem fix = editor.assertFirstQuickfix(problem, "Change to 'my.path.with.many.pieces'"); - editor.apply(fix); + CodeAction fix = editor.assertFirstQuickfix(problem, "Replace with `my.path.with.many.pieces`"); + fix.perform(); editor.assertText( "# a comment\n"+ "my:\n" + " path:\n"+ " with:\n"+ " many:\n"+ - " pieces: foo" + " pieces: foo<*>" ); } @@ -2878,8 +2878,8 @@ public class ApplicationYamlEditorTest extends AbstractPropsEditorTest { ); Diagnostic problem = editor.assertProblem("pieces"); - CompletionItem fix = editor.assertFirstQuickfix(problem, "Change to 'my.path.with.many.pieces'"); - editor.apply(fix); + CodeAction fix = editor.assertFirstQuickfix(problem, "Replace with `my.path.with.many.pieces`"); + fix.perform(); editor.assertText( "# a comment\n"+ "my:\n" + @@ -2891,7 +2891,7 @@ public class ApplicationYamlEditorTest extends AbstractPropsEditorTest { " path:\n"+ " with:\n"+ " many:\n"+ - " pieces: foo" + " pieces: foo<*>" ); } @@ -2903,13 +2903,13 @@ public class ApplicationYamlEditorTest extends AbstractPropsEditorTest { ); Diagnostic problem = editor.assertProblem("stuff"); - CompletionItem fix = editor.assertFirstQuickfix(problem, "Change to 'my.for-sale.stuff'"); - editor.apply(fix); + CodeAction fix = editor.assertFirstQuickfix(problem, "Replace with `my.for-sale.stuff`"); + fix.perform(); editor.assertText( "# a comment\n"+ "my:\n" + " for-sale:\n"+ - " stuff: foo" + " stuff: foo<*>" ); } @@ -2921,12 +2921,12 @@ public class ApplicationYamlEditorTest extends AbstractPropsEditorTest { ); Diagnostic problem = editor.assertProblem("old-name"); - CompletionItem fix = editor.assertFirstQuickfix(problem, "Change to 'your.new-name'"); - editor.apply(fix); + CodeAction fix = editor.assertFirstQuickfix(problem, "Replace with `your.new-name`"); + fix.perform(); editor.assertText( "# a comment\n"+ "your:\n"+ - " new-name: foo" + " new-name: foo<*>" ); } @@ -2940,13 +2940,13 @@ public class ApplicationYamlEditorTest extends AbstractPropsEditorTest { ); Diagnostic problem = editor.assertProblem("old-name"); - CompletionItem fix = editor.assertFirstQuickfix(problem, "Change to 'your.new-name'"); - editor.apply(fix); + CodeAction fix = editor.assertFirstQuickfix(problem, "Replace with `your.new-name`"); + fix.perform(); editor.assertText( "# a comment\n"+ "your:\n"+ " goodies: nice\n"+ - " new-name: foo" + " new-name: foo<*>" ); } @@ -2961,20 +2961,20 @@ public class ApplicationYamlEditorTest extends AbstractPropsEditorTest { ); Diagnostic problem = editor.assertProblem("old-name"); - CompletionItem fix = editor.assertFirstQuickfix(problem, "Change to 'your.new-name'"); - editor.apply(fix); + CodeAction fix = editor.assertFirstQuickfix(problem, "Replace with `your.new-name`"); + fix.perform(); editor.assertText( "# a comment\n"+ "your:\n"+ " goodies: nice\n"+ - " new-name: foo\n" + + " new-name: foo<*>\n" + "my:\n" + " other: stuff" ); } } - @Ignore @Test public void testDeprecatedPropertyQuickfixMovingIndentedValue() throws Exception { + @Test public void testDeprecatedPropertyQuickfixMovingIndentedValue() throws Exception { data("my.old-name", "java.lang.String", null, "Old and deprecated name"); deprecate("my.old-name", "your.new-name", null); //same indent level @@ -3000,8 +3000,8 @@ public class ApplicationYamlEditorTest extends AbstractPropsEditorTest { " stuff: goes here\n" ); Diagnostic problem = editor.assertProblem("pieces"); - CompletionItem fix = editor.assertFirstQuickfix(problem, "Change to 'short.path'"); - editor.apply(fix); + CodeAction fix = editor.assertFirstQuickfix(problem, "Replace with `short.path`"); + fix.perform(); editor.assertText( "# a comment\n"+ "short:\n"+ @@ -3009,7 +3009,7 @@ public class ApplicationYamlEditorTest extends AbstractPropsEditorTest { " path: >\n" + " foo spread over\n"+ " several lines\n" + - " of text\n" + " of text<*>\n" ); } @@ -3025,8 +3025,8 @@ public class ApplicationYamlEditorTest extends AbstractPropsEditorTest { " of text\n" ); Diagnostic problem = editor.assertProblem("stuff"); - CompletionItem fix = editor.assertFirstQuickfix(problem, "Change to 'my.long.path.with.many.pieces'"); - editor.apply(fix); + CodeAction fix = editor.assertFirstQuickfix(problem, "Replace with `my.long.path.with.many.pieces`"); + fix.perform(); editor.assertText( "# a comment\n"+ "my:\n" + @@ -3038,7 +3038,7 @@ public class ApplicationYamlEditorTest extends AbstractPropsEditorTest { " pieces: >\n" + " foo spread over\n"+ " several lines\n" + - " of text" + " of text<*>" ); } { @@ -3060,8 +3060,8 @@ public class ApplicationYamlEditorTest extends AbstractPropsEditorTest { " stuff: goes here\n" ); Diagnostic problem = editor.assertProblem("pieces"); - CompletionItem fix = editor.assertFirstQuickfix(problem, "Change to 'short.path'"); - editor.apply(fix); + CodeAction fix = editor.assertFirstQuickfix(problem, "Replace with `short.path`"); + fix.perform(); editor.assertText( "# a comment\n"+ "short:\n"+ @@ -3072,7 +3072,7 @@ public class ApplicationYamlEditorTest extends AbstractPropsEditorTest { " #confusing\n" + " - several lines\n" + " #comments\n" + - " - of text\n" + " - of text<*>\n" ); } }