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 8f5832448..9efacf4e8 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 @@ -858,6 +858,18 @@ public class Editor { return actions.get(0); } + public void assertNoCodeAction(Diagnostic problem) throws Exception { + List actions = getCodeActions(problem); + if (actions!=null && !actions.isEmpty()) { + StringBuilder found = new StringBuilder(); + for (CodeAction codeAction : actions) { + found.append("\n"+codeAction.getLabel()); + } + fail("Expected no code actions but found:"+found); + } + } + + public String getUri() { return doc.getUri(); } @@ -896,4 +908,5 @@ public class Editor { public void setCursor(Position position) { this.selectionStart = this.selectionEnd = doc.toOffset(position); } + } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/configurationmetadata/Deprecation.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/configurationmetadata/Deprecation.java index df3c306e3..6006c3ebb 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/configurationmetadata/Deprecation.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/configurationmetadata/Deprecation.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2015 the original author or authors. + * Copyright 2012-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,13 +28,30 @@ import java.io.Serializable; @SuppressWarnings("serial") public class Deprecation implements Serializable { + private Level level = Level.WARNING; + private String reason; + private String shortReason; + private String replacement; + /** + * Define the {@link Level} of deprecation. + * @return the deprecation level + */ + public Level getLevel() { + return this.level; + } + + public void setLevel(Level level) { + this.level = level; + } + /** * A reason why the related property is deprecated, if any. Can be multi-lines. * @return the deprecation reason + * @see #getShortReason() */ public String getReason() { return this.reason; @@ -44,6 +61,20 @@ public class Deprecation implements Serializable { this.reason = reason; } + /** + * A single-line, single-sentence reason why the related property is deprecated, if + * any. + * @return the short deprecation reason + * @see #getReason() + */ + public String getShortReason() { + return this.shortReason; + } + + public void setShortReason(String shortReason) { + this.shortReason = shortReason; + } + /** * The full name of the property that replaces the related deprecated property, if * any. @@ -59,8 +90,24 @@ public class Deprecation implements Serializable { @Override public String toString() { - return "Deprecation{" + "reason='" + this.reason + '\'' + ", replacement='" - + this.replacement + '\'' + '}'; + return "Deprecation{" + "level='" + this.level + '\'' + ", reason='" + this.reason + + '\'' + ", replacement='" + this.replacement + '\'' + '}'; } -} + /** + * Define the deprecation level. + */ + public enum Level { + + /** + * The property is still bound. + */ + WARNING, + + /** + * The property has been removed and is no longer bound. + */ + ERROR + + } +} \ No newline at end of file diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/metadata/PropertyInfo.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/metadata/PropertyInfo.java index 3c261181c..783e2ff49 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/metadata/PropertyInfo.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/metadata/PropertyInfo.java @@ -17,6 +17,7 @@ import java.util.List; import org.springframework.ide.vscode.boot.configurationmetadata.ConfigurationMetadataProperty; import org.springframework.ide.vscode.boot.configurationmetadata.ConfigurationMetadataSource; import org.springframework.ide.vscode.boot.configurationmetadata.Deprecation; +import org.springframework.ide.vscode.boot.configurationmetadata.Deprecation.Level; import org.springframework.ide.vscode.boot.configurationmetadata.ValueHint; import org.springframework.ide.vscode.boot.configurationmetadata.ValueProvider; import org.springframework.ide.vscode.boot.metadata.ValueProviderRegistry.ValueProviderStrategy; @@ -207,6 +208,11 @@ public class PropertyInfo { return deprecation == null ? null : deprecation.getReplacement(); } + public Level getDeprecationLevel() { + return deprecation == null ? null : deprecation.getLevel(); + } + + public void addValueHints(List hints) { Builder builder = ImmutableList.builder(); builder.addAll(valueHints); diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/metadata/types/TypedProperty.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/metadata/types/TypedProperty.java index 4c912b78d..d636fc6b7 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/metadata/types/TypedProperty.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/metadata/types/TypedProperty.java @@ -12,6 +12,7 @@ package org.springframework.ide.vscode.boot.metadata.types; import org.springframework.ide.vscode.boot.configurationmetadata.Deprecation; +import org.springframework.ide.vscode.boot.configurationmetadata.Deprecation.Level; import org.springframework.ide.vscode.commons.util.Renderable; import org.springframework.ide.vscode.commons.util.Renderables; import org.springframework.ide.vscode.commons.yaml.schema.YTypedProperty; @@ -51,10 +52,12 @@ public class TypedProperty implements YTypedProperty { this.deprecation = deprecation; } + @Override public String getName() { return name; } + @Override public Type getType() { return type; } @@ -82,6 +85,7 @@ public class TypedProperty implements YTypedProperty { return null; } + @Override public boolean isDeprecated() { return deprecation!=null; } @@ -100,8 +104,12 @@ public class TypedProperty implements YTypedProperty { return null; } + public Level getDeprecationLevel() { + return deprecation == null ? null : deprecation.getLevel(); + } + public Deprecation getDeprecation() { return deprecation; } - + } 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 df71736ec..a9ddfe714 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 @@ -11,7 +11,7 @@ package org.springframework.ide.vscode.boot.yaml.reconcile; -import static org.springframework.ide.vscode.boot.yaml.reconcile.ApplicationYamlProblemType.YAML_DEPRECATED; +import static org.springframework.ide.vscode.boot.yaml.reconcile.ApplicationYamlProblemType.*; import static org.springframework.ide.vscode.boot.yaml.reconcile.ApplicationYamlProblemType.YAML_DUPLICATE_KEY; import static org.springframework.ide.vscode.commons.yaml.ast.NodeUtil.asScalar; import static org.springframework.ide.vscode.commons.yaml.ast.YamlFileAST.getChildren; @@ -25,6 +25,7 @@ import java.util.regex.Pattern; import org.eclipse.lsp4j.Position; import org.eclipse.lsp4j.Range; import org.eclipse.lsp4j.TextDocumentIdentifier; +import org.springframework.ide.vscode.boot.configurationmetadata.Deprecation.Level; 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; @@ -389,7 +390,7 @@ public class ApplicationYamlASTReconciler implements YamlASTReconciler { private void deprecatedProperty(String docUri, PropertyInfo property, Node keyNode, QuickfixType fixType) { SpringPropertyProblem problem = deprecatedPropertyProblem(docUri, property.getId(), null, keyNode, - property.getDeprecationReplacement(), property.getDeprecationReason(), fixType); + property.getDeprecationReplacement(), property.getDeprecationReason(), property.getDeprecationLevel(), fixType); problem.setMetadata(property); //problem.setProblemFixer(ReplaceDeprecatedYamlQuickfix.FIXER); problems.accept(problem); @@ -397,18 +398,20 @@ public class ApplicationYamlASTReconciler implements YamlASTReconciler { 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); + keyNode, property.getDeprecationReplacement(), property.getDeprecationReason(), property.getDeprecationLevel(), fixType); problems.accept(problem); } 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)); + String replace, String reason, Level level, QuickfixType fixType) { + ApplicationYamlProblemType problemType = level==Level.ERROR ? YAML_DEPRECATED_ERROR : YAML_DEPRECATED_WARNING; + SpringPropertyProblem problem = problem(problemType, 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 + "`")); + if (StringUtil.hasText(replace)) { + 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/ApplicationYamlProblemType.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/yaml/reconcile/ApplicationYamlProblemType.java index d025c536d..b22d30f14 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/yaml/reconcile/ApplicationYamlProblemType.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/yaml/reconcile/ApplicationYamlProblemType.java @@ -30,7 +30,8 @@ public enum ApplicationYamlProblemType implements ProblemType { YAML_EXPECT_MAPPING("Expecting a 'mapping' node but found something else"), YAML_EXPECT_BEAN_PROPERTY_NAME("Expecting a 'bean property' name but found something more complex"), YAML_INVALID_BEAN_PROPERTY("Accessing a named property in a type that doesn't provide a property accessor with that name"), - YAML_DEPRECATED(WARNING, "Property is marked as Deprecated"), + YAML_DEPRECATED_ERROR(ERROR, "Property is marked as Deprecated(Error)"), + YAML_DEPRECATED_WARNING(WARNING, "Property is marked as Deprecated(Warning)"), YAML_DUPLICATE_KEY("A mapping node contains multiple entries for the same key"); private final ProblemSeverity defaultSeverity; @@ -51,6 +52,7 @@ public enum ApplicationYamlProblemType implements ProblemType { this(ERROR, description); } + @Override public ProblemSeverity getDefaultSeverity() { return defaultSeverity; } 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 a58361b15..f610c62da 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 @@ -36,4 +36,9 @@ public class SpringPropertyProblem extends ReconcileProblemImpl { propertyName = name; } + @Override + public String toString() { + return "SpringPropertyProblem("+propertyName+", "+this.getMessage()+")"; + } + } diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/editor/harness/AbstractPropsEditorTest.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/editor/harness/AbstractPropsEditorTest.java index ce4e0f6f9..9cf22d10a 100644 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/editor/harness/AbstractPropsEditorTest.java +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/editor/harness/AbstractPropsEditorTest.java @@ -28,6 +28,7 @@ import org.eclipse.lsp4j.TextDocumentIdentifier; import org.junit.Before; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.ide.vscode.boot.app.BootLanguageServerInitializer; +import org.springframework.ide.vscode.boot.configurationmetadata.Deprecation.Level; import org.springframework.ide.vscode.boot.editor.harness.PropertyIndexHarness.ItemConfigurer; import org.springframework.ide.vscode.boot.metadata.SpringPropertyIndexProvider; import org.springframework.ide.vscode.commons.java.IJavaProject; @@ -259,6 +260,10 @@ public abstract class AbstractPropsEditorTest { } } + public void deprecate(String key, String replacedBy, String reason, Level level) { + md.deprecate(key, replacedBy, reason, level); + } + public void deprecate(String key, String replacedBy, String reason) { md.deprecate(key, replacedBy, reason); } diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/editor/harness/PropertyIndexHarness.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/editor/harness/PropertyIndexHarness.java index 71a91fd36..388c1c507 100644 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/editor/harness/PropertyIndexHarness.java +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/editor/harness/PropertyIndexHarness.java @@ -21,6 +21,7 @@ import org.eclipse.lsp4j.TextDocumentIdentifier; import org.gradle.internal.impldep.com.google.common.collect.ImmutableList; import org.springframework.ide.vscode.boot.configurationmetadata.ConfigurationMetadataProperty; import org.springframework.ide.vscode.boot.configurationmetadata.Deprecation; +import org.springframework.ide.vscode.boot.configurationmetadata.Deprecation.Level; import org.springframework.ide.vscode.boot.configurationmetadata.ValueHint; import org.springframework.ide.vscode.boot.configurationmetadata.ValueProvider; import org.springframework.ide.vscode.boot.metadata.PropertyInfo; @@ -147,6 +148,16 @@ public class PropertyIndexHarness { } } + public synchronized void deprecate(String key, String replacedBy, String reason, Level level) { + index = null; + ConfigurationMetadataProperty info = datas.get(key); + Deprecation d = new Deprecation(); + d.setReplacement(replacedBy); + d.setReason(reason); + d.setLevel(level); + info.setDeprecation(d); + } + public synchronized void deprecate(String key, String replacedBy, String reason) { index = null; ConfigurationMetadataProperty info = datas.get(key); 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 a4131e3e5..282cf5acc 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 @@ -21,6 +21,7 @@ import java.time.Duration; import java.util.Optional; import org.eclipse.lsp4j.Diagnostic; +import org.eclipse.lsp4j.DiagnosticSeverity; import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; @@ -30,6 +31,7 @@ import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.ide.vscode.boot.bootiful.BootLanguageServerTest; import org.springframework.ide.vscode.boot.bootiful.PropertyEditorTestConf; +import org.springframework.ide.vscode.boot.configurationmetadata.Deprecation.Level; import org.springframework.ide.vscode.boot.editor.harness.AbstractPropsEditorTest; import org.springframework.ide.vscode.boot.metadata.CachingValueProvider; import org.springframework.ide.vscode.boot.metadata.PropertyInfo; @@ -4015,6 +4017,28 @@ public class ApplicationYamlEditorTest extends AbstractPropsEditorTest { editor.assertContextualCompletions("B<*>", "BLUE<*>"); } + @Test public void test_NoQuickfixForDeprecatedProperty() throws Exception { + //See: https://www.pivotaltracker.com/story/show/163720976 + //Summary: if a deprecated property metadata does *not* + // provide a 'replace with' hint then it should not create + // a 'Replace with' quickfix. + + data("spring.devtools.remote.debug.local-port", "java.lang.Integer", + 8000, "Local remote debug server port." + ); + deprecate("spring.devtools.remote.debug.local-port", null, "No longer supported", Level.ERROR); + + Editor editor = harness.newEditor( + "spring:\n" + + " devtools:\n" + + " remote:\n" + + " debug:\n" + + " local-port: 8888" + ); + Diagnostic problem = editor.assertProblems("local-port|Deprecated").get(0); + editor.assertNoCodeAction(problem); + assertEquals(DiagnosticSeverity.Error, problem.getSeverity()); + } ///////////////// cruft ////////////////////////////////////////////////////////