From dbe9520f3d8faa5e31c8fb9ca0bf22482851550e Mon Sep 17 00:00:00 2001 From: Kris De Volder Date: Fri, 2 Oct 2020 10:22:41 -0700 Subject: [PATCH] Preference pages for props and yaml ls editor Allows configuring problem severities. The ui works but is not yet wired to transmit these setting to the language server. --- .../framework/SpringBootProjectBuilder.java | 3 +- .../BootValidationPreferencesPage.java | 9 +- .../BootValidationProjectPropertyPage.java | 7 +- .../META-INF/MANIFEST.MF | 3 +- ...bstractProblemSeverityPreferencesPage.java | 16 +- .../PreferencesBasedSeverityProvider.java | 11 +- .../ProblemSeverityPreferencesUtil.java | 20 ++- ...blemSeverityPreferityPageFromMetadata.java | 99 +++++++++++ eclipse-language-servers/local-build.sh | 2 +- .../META-INF/MANIFEST.MF | 3 +- .../plugin.xml | 14 ++ ...pertiesEditorProblemSeverityPrefsPage.java | 32 ++++ ...ionYamlEditorProblemSeverityPrefsPage.java | 32 ++++ .../LanguageServerProblemTypesMetadata.java | 29 ++++ eclipse-language-servers/pom.xml | 9 + .../reconcile/BadWordReconcileEngine.java | 10 ++ .../languageserver/reconcile/ProblemType.java | 2 + .../reconcile/ProblemTypes.java | 20 ++- .../handlers/SpelExpressionReconciler.java | 1 - .../ApplicationPropertiesProblemType.java | 3 +- .../reconcile/ApplicationYamlProblems.java | 34 +--- .../ApplicationYamlReconcileEngine.java | 4 +- .../src/main/resources/problem-types.json | 150 +++++++++++++++++ .../boot/test/ProblemTypesMetadataTest.java | 29 ++++ .../vscode/boot/test/ProblemTypesToJson.java | 154 ++++++++++++++++++ 25 files changed, 632 insertions(+), 64 deletions(-) create mode 100644 eclipse-extensions/org.springframework.ide.eclipse.editor.support/src/org/springframework/ide/eclipse/editor/support/preferences/ProblemSeverityPreferityPageFromMetadata.java create mode 100644 eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/prefs/ApplicationPropertiesEditorProblemSeverityPrefsPage.java create mode 100644 eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/prefs/ApplicationYamlEditorProblemSeverityPrefsPage.java create mode 100644 eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/prefs/LanguageServerProblemTypesMetadata.java create mode 100644 headless-services/spring-boot-language-server/src/main/resources/problem-types.json create mode 100644 headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/test/ProblemTypesMetadataTest.java create mode 100644 headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/test/ProblemTypesToJson.java diff --git a/eclipse-extensions/org.springframework.ide.eclipse.boot.validation/src/org/springframework/ide/eclipse/boot/validation/framework/SpringBootProjectBuilder.java b/eclipse-extensions/org.springframework.ide.eclipse.boot.validation/src/org/springframework/ide/eclipse/boot/validation/framework/SpringBootProjectBuilder.java index 8e4ba66e3..650de3949 100644 --- a/eclipse-extensions/org.springframework.ide.eclipse.boot.validation/src/org/springframework/ide/eclipse/boot/validation/framework/SpringBootProjectBuilder.java +++ b/eclipse-extensions/org.springframework.ide.eclipse.boot.validation/src/org/springframework/ide/eclipse/boot/validation/framework/SpringBootProjectBuilder.java @@ -30,6 +30,7 @@ import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.SubProgressMonitor; import org.springframework.ide.eclipse.boot.core.BootPropertyTester; import org.springframework.ide.eclipse.boot.validation.BootValidationActivator; +import org.springframework.ide.eclipse.boot.validation.preferences.BootValidationPreferencesPage; import org.springframework.ide.eclipse.boot.validation.rules.ValidationRuleDefinitions; import org.springframework.ide.eclipse.editor.support.preferences.EditorType; import org.springframework.ide.eclipse.editor.support.preferences.PreferencesBasedSeverityProvider; @@ -111,7 +112,7 @@ public class SpringBootProjectBuilder extends IncrementalProjectBuilder { } private IValidationContext validationContext(IResource rsrc, IValidationRule rule) { - SeverityProvider severityProvider = new PreferencesBasedSeverityProvider(rsrc.getProject(), BootValidationActivator.PLUGIN_ID, EditorType.JAVA); + SeverityProvider severityProvider = new PreferencesBasedSeverityProvider(BootValidationPreferencesPage.util, rsrc.getProject(), BootValidationActivator.PLUGIN_ID, EditorType.JAVA); return (IResource cu, ProblemType problemId, String msg, int offset, int end) -> { ProblemSeverity severity = severityProvider.getSeverity(problemId); if (severity==ProblemSeverity.IGNORE) { diff --git a/eclipse-extensions/org.springframework.ide.eclipse.boot.validation/src/org/springframework/ide/eclipse/boot/validation/preferences/BootValidationPreferencesPage.java b/eclipse-extensions/org.springframework.ide.eclipse.boot.validation/src/org/springframework/ide/eclipse/boot/validation/preferences/BootValidationPreferencesPage.java index a360556d7..89516b521 100644 --- a/eclipse-extensions/org.springframework.ide.eclipse.boot.validation/src/org/springframework/ide/eclipse/boot/validation/preferences/BootValidationPreferencesPage.java +++ b/eclipse-extensions/org.springframework.ide.eclipse.boot.validation/src/org/springframework/ide/eclipse/boot/validation/preferences/BootValidationPreferencesPage.java @@ -24,13 +24,20 @@ import org.springframework.ide.eclipse.editor.support.reconcile.ProblemType; */ public class BootValidationPreferencesPage extends AbstractProblemSeverityPreferencesPage { + public static final ProblemSeverityPreferencesUtil util = new ProblemSeverityPreferencesUtil("boot.project.validation.builder."); + + public BootValidationPreferencesPage() { + super(util); + } + + protected List getProblemTypes() { return BootValidationProblemType.values(); } @Override protected String getEnableProjectPreferencesKey() { - return ProblemSeverityPreferencesUtil.ENABLE_PROJECT_PREFERENCES(EditorType.PROP); + return util.ENABLE_PROJECT_PREFERENCES(EditorType.PROP); } @Override diff --git a/eclipse-extensions/org.springframework.ide.eclipse.boot.validation/src/org/springframework/ide/eclipse/boot/validation/preferences/BootValidationProjectPropertyPage.java b/eclipse-extensions/org.springframework.ide.eclipse.boot.validation/src/org/springframework/ide/eclipse/boot/validation/preferences/BootValidationProjectPropertyPage.java index a72a22194..942efb326 100644 --- a/eclipse-extensions/org.springframework.ide.eclipse.boot.validation/src/org/springframework/ide/eclipse/boot/validation/preferences/BootValidationProjectPropertyPage.java +++ b/eclipse-extensions/org.springframework.ide.eclipse.boot.validation/src/org/springframework/ide/eclipse/boot/validation/preferences/BootValidationProjectPropertyPage.java @@ -16,7 +16,6 @@ import org.springframework.ide.eclipse.boot.validation.BootValidationActivator; import org.springframework.ide.eclipse.boot.validation.rules.ValidationRuleDefinitions; import org.springframework.ide.eclipse.editor.support.preferences.AbstractProblemSeverityPreferencesPage; import org.springframework.ide.eclipse.editor.support.preferences.EditorType; -import org.springframework.ide.eclipse.editor.support.preferences.ProblemSeverityPreferencesUtil; import org.springframework.ide.eclipse.editor.support.reconcile.ProblemType; /** @@ -24,13 +23,17 @@ import org.springframework.ide.eclipse.editor.support.reconcile.ProblemType; */ public class BootValidationProjectPropertyPage extends AbstractProblemSeverityPreferencesPage { + public BootValidationProjectPropertyPage() { + super(BootValidationPreferencesPage.util); + } + protected List getProblemTypes() { return ValidationRuleDefinitions.getProblemTypes(); } @Override protected String getEnableProjectPreferencesKey() { - return ProblemSeverityPreferencesUtil.ENABLE_PROJECT_PREFERENCES(EditorType.JAVA); + return BootValidationPreferencesPage.util.ENABLE_PROJECT_PREFERENCES(EditorType.JAVA); } @Override diff --git a/eclipse-extensions/org.springframework.ide.eclipse.editor.support/META-INF/MANIFEST.MF b/eclipse-extensions/org.springframework.ide.eclipse.editor.support/META-INF/MANIFEST.MF index 43c0ceaec..6a7549603 100644 --- a/eclipse-extensions/org.springframework.ide.eclipse.editor.support/META-INF/MANIFEST.MF +++ b/eclipse-extensions/org.springframework.ide.eclipse.editor.support/META-INF/MANIFEST.MF @@ -25,7 +25,8 @@ Require-Bundle: org.eclipse.ui, org.reactivestreams.reactive-streams;bundle-version="1.0.0", org.apache.commons.lang3, org.springsource.ide.eclipse.commons.ui, - javax.inject + javax.inject, + com.google.gson Bundle-ActivationPolicy: lazy Export-Package: org.springframework.ide.eclipse.editor.support, org.springframework.ide.eclipse.editor.support.completions, diff --git a/eclipse-extensions/org.springframework.ide.eclipse.editor.support/src/org/springframework/ide/eclipse/editor/support/preferences/AbstractProblemSeverityPreferencesPage.java b/eclipse-extensions/org.springframework.ide.eclipse.editor.support/src/org/springframework/ide/eclipse/editor/support/preferences/AbstractProblemSeverityPreferencesPage.java index 9e862bd8f..12c8723de 100644 --- a/eclipse-extensions/org.springframework.ide.eclipse.editor.support/src/org/springframework/ide/eclipse/editor/support/preferences/AbstractProblemSeverityPreferencesPage.java +++ b/eclipse-extensions/org.springframework.ide.eclipse.editor.support/src/org/springframework/ide/eclipse/editor/support/preferences/AbstractProblemSeverityPreferencesPage.java @@ -54,6 +54,8 @@ import org.springsource.ide.eclipse.commons.livexp.core.UIValueListener; */ public abstract class AbstractProblemSeverityPreferencesPage extends FieldEditorPreferencePage implements IWorkbenchPreferencePage, IWorkbenchPropertyPage { + private final ProblemSeverityPreferencesUtil util; + /** * Project for a project propertypage, or null for a workspace preference page. */ @@ -64,6 +66,8 @@ public abstract class AbstractProblemSeverityPreferencesPage extends FieldEditor */ private LiveVariable enablePreferences = new LiveVariable<>(true); + private boolean initialized; + private static final Comparator PROBLEM_TYPE_COMPARATOR = new Comparator() { public int compare(ProblemType o1, ProblemType o2) { @@ -76,9 +80,9 @@ public abstract class AbstractProblemSeverityPreferencesPage extends FieldEditor {"Ignore", ProblemSeverity.IGNORE.toString()} }; - protected AbstractProblemSeverityPreferencesPage() { + protected AbstractProblemSeverityPreferencesPage(ProblemSeverityPreferencesUtil util) { super(FieldEditorPreferencePage.GRID); - initializeDefaults(); + this.util = util; } /** @@ -90,7 +94,7 @@ public abstract class AbstractProblemSeverityPreferencesPage extends FieldEditor protected void initializeDefaults() { IEclipsePreferences defaults = DefaultScope.INSTANCE.getNode(getPluginId()); for (ProblemType problemType : getProblemTypes()) { - defaults.put(getPreferenceName(problemType), problemType.getDefaultSeverity().toString()); + defaults.put(util.getPreferenceName(problemType), problemType.getDefaultSeverity().toString()); } try { defaults.flush(); @@ -112,12 +116,16 @@ public abstract class AbstractProblemSeverityPreferencesPage extends FieldEditor @Override protected void createFieldEditors() { + if (!initialized) { + initialized = true; + initializeDefaults(); + } ProblemType[] problemTypes = getProblemTypes().toArray(new ProblemType[0]); Arrays.sort(problemTypes, PROBLEM_TYPE_COMPARATOR); for (ProblemType problemType : problemTypes) { ComboFieldEditor field = new ComboFieldEditor( - ProblemSeverityPreferencesUtil.getPreferenceName(problemType), + util.getPreferenceName(problemType), problemType.getLabel(), SEVERITY_NAMES_AND_VALUES, getFieldEditorParent() diff --git a/eclipse-extensions/org.springframework.ide.eclipse.editor.support/src/org/springframework/ide/eclipse/editor/support/preferences/PreferencesBasedSeverityProvider.java b/eclipse-extensions/org.springframework.ide.eclipse.editor.support/src/org/springframework/ide/eclipse/editor/support/preferences/PreferencesBasedSeverityProvider.java index 4e8d7896c..963072683 100644 --- a/eclipse-extensions/org.springframework.ide.eclipse.editor.support/src/org/springframework/ide/eclipse/editor/support/preferences/PreferencesBasedSeverityProvider.java +++ b/eclipse-extensions/org.springframework.ide.eclipse.editor.support/src/org/springframework/ide/eclipse/editor/support/preferences/PreferencesBasedSeverityProvider.java @@ -35,15 +35,18 @@ public class PreferencesBasedSeverityProvider implements SeverityProvider { private IPreferenceStore workspacePrefs; private Map cache = null; + private final ProblemSeverityPreferencesUtil util; - public PreferencesBasedSeverityProvider(IPreferenceStore projectPrefs, IPreferenceStore workspacePrefs, EditorType editorType) { + public PreferencesBasedSeverityProvider(ProblemSeverityPreferencesUtil util, IPreferenceStore projectPrefs, IPreferenceStore workspacePrefs, EditorType editorType) { + this.util = util; this.projectPrefs = projectPrefs; this.workspacePrefs = workspacePrefs; this.editorType = editorType; } - public PreferencesBasedSeverityProvider(IProject project, String pluginId, EditorType editorType) { + public PreferencesBasedSeverityProvider(ProblemSeverityPreferencesUtil util, IProject project, String pluginId, EditorType editorType) { this( + util, new ScopedPreferenceStore(new ProjectScope(project), pluginId), new ScopedPreferenceStore(InstanceScope.INSTANCE, pluginId), editorType @@ -56,7 +59,7 @@ public class PreferencesBasedSeverityProvider implements SeverityProvider { } ProblemSeverity existing = cache.get(problemType); if (existing==null) { - cache.put(problemType, existing = ProblemSeverityPreferencesUtil.getSeverity(getPrefs(), problemType)); + cache.put(problemType, existing = util.getSeverity(getPrefs(), problemType)); } return existing; } @@ -71,7 +74,7 @@ public class PreferencesBasedSeverityProvider implements SeverityProvider { private boolean useProjectPreferences() { if (projectPrefs!=null) { - return ProblemSeverityPreferencesUtil.projectPreferencesEnabled(projectPrefs, editorType); + return util.projectPreferencesEnabled(projectPrefs, editorType); } return false; } diff --git a/eclipse-extensions/org.springframework.ide.eclipse.editor.support/src/org/springframework/ide/eclipse/editor/support/preferences/ProblemSeverityPreferencesUtil.java b/eclipse-extensions/org.springframework.ide.eclipse.editor.support/src/org/springframework/ide/eclipse/editor/support/preferences/ProblemSeverityPreferencesUtil.java index f16dd8a7d..bad4f3b96 100644 --- a/eclipse-extensions/org.springframework.ide.eclipse.editor.support/src/org/springframework/ide/eclipse/editor/support/preferences/ProblemSeverityPreferencesUtil.java +++ b/eclipse-extensions/org.springframework.ide.eclipse.editor.support/src/org/springframework/ide/eclipse/editor/support/preferences/ProblemSeverityPreferencesUtil.java @@ -24,17 +24,21 @@ import org.springsource.ide.eclipse.commons.core.util.StringUtil; */ public class ProblemSeverityPreferencesUtil { - public static final String PREFERENCE_PREFIX = "spring.properties.editor.problem."; + public final String PREFERENCE_PREFIX;// = "spring.properties.editor.problem."; - public static final String ENABLE_PROJECT_PREFERENCES(EditorType et) { + public ProblemSeverityPreferencesUtil(String preferencePrefix) { + this.PREFERENCE_PREFIX = preferencePrefix; + } + + public final String ENABLE_PROJECT_PREFERENCES(EditorType et) { return PREFERENCE_PREFIX+et+".project.prefs.enabled"; } - public static String getPreferenceName(ProblemType problemType) { - return PREFERENCE_PREFIX+problemType.toString(); + public String getPreferenceName(ProblemType problemType) { + return PREFERENCE_PREFIX+problemType.getId(); } - public static ProblemSeverity getSeverity(IPreferenceStore prefs, ProblemType problemType) { + public ProblemSeverity getSeverity(IPreferenceStore prefs, ProblemType problemType) { String value = prefs.getString(getPreferenceName(problemType)); try { if (StringUtil.hasText(value)) { @@ -47,15 +51,15 @@ public class ProblemSeverityPreferencesUtil { return problemType.getDefaultSeverity(); } - public static void setSeverity(IPreferenceStore prefs, ProblemType problemType, ProblemSeverity severity) { + public void setSeverity(IPreferenceStore prefs, ProblemType problemType, ProblemSeverity severity) { prefs.setValue(getPreferenceName(problemType), severity.toString()); } - public static boolean projectPreferencesEnabled(IPreferenceStore projectPrefs, EditorType et) { + public boolean projectPreferencesEnabled(IPreferenceStore projectPrefs, EditorType et) { return projectPrefs.getBoolean(ENABLE_PROJECT_PREFERENCES(et)); } - public static void enableProjectPrefs(IPreferenceStore projectPrefs, EditorType et, boolean enable) { + public void enableProjectPrefs(IPreferenceStore projectPrefs, EditorType et, boolean enable) { projectPrefs.setValue(ENABLE_PROJECT_PREFERENCES(et), enable); } diff --git a/eclipse-extensions/org.springframework.ide.eclipse.editor.support/src/org/springframework/ide/eclipse/editor/support/preferences/ProblemSeverityPreferityPageFromMetadata.java b/eclipse-extensions/org.springframework.ide.eclipse.editor.support/src/org/springframework/ide/eclipse/editor/support/preferences/ProblemSeverityPreferityPageFromMetadata.java new file mode 100644 index 000000000..0d927f41c --- /dev/null +++ b/eclipse-extensions/org.springframework.ide.eclipse.editor.support/src/org/springframework/ide/eclipse/editor/support/preferences/ProblemSeverityPreferityPageFromMetadata.java @@ -0,0 +1,99 @@ +/******************************************************************************* + * Copyright (c) 2020 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 + * https://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Pivotal, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.eclipse.editor.support.preferences; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; +import java.io.Reader; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +import org.springframework.ide.eclipse.editor.support.reconcile.ProblemSeverity; +import org.springframework.ide.eclipse.editor.support.reconcile.ProblemType; + +import com.google.common.reflect.TypeToken; +import com.google.gson.Gson; + +public abstract class ProblemSeverityPreferityPageFromMetadata extends AbstractProblemSeverityPreferencesPage { + + public static Map readFromFile(File metadataFile) throws FileNotFoundException, IOException { + Gson gson = new Gson(); + TypeToken> tt = new TypeToken>() {}; + try (Reader json = new FileReader(metadataFile)) { + return gson.fromJson(json, tt.getType()); + } + } + + public static class ProblemTypeData implements ProblemType { + String code; + String label; + String description; + String defaultSeverity; + + public ProblemTypeData() {} + + public String getCode() { + return code; + } + public void setCode(String code) { + this.code = code; + } + public String getDescription() { + return description; + } + public void setDescription(String description) { + this.description = description; + } + + public ProblemSeverity getDefaultSeverity() { + return ProblemSeverity.valueOf(defaultSeverity); + } + + public void setDefaultSeverity(String defaultSeverity) { + this.defaultSeverity = defaultSeverity; + } + + public String getLabel() { + return label; + } + + public void setLabel(String label) { + this.label = label; + } + + @Override + public String getId() { + return code; + } + } + + private ProblemType[] problemTypes; + + public ProblemSeverityPreferityPageFromMetadata(ProblemSeverityPreferencesUtil util, ProblemTypeData[] problemTypeJsonData) { + super(util); + this.problemTypes = problemTypeJsonData; + } + + @Override + protected List getProblemTypes() { + return Arrays.asList(problemTypes); + } + + @Override + protected String getEnableProjectPreferencesKey() { + // TODO Auto-generated method stub + return null; + } + +} diff --git a/eclipse-language-servers/local-build.sh b/eclipse-language-servers/local-build.sh index a1ea85a17..ea30ee852 100755 --- a/eclipse-language-servers/local-build.sh +++ b/eclipse-language-servers/local-build.sh @@ -5,4 +5,4 @@ cd ../headless-services ./mvnw clean install -Dmaven.test.skip=true cd $workdir -./mvnw -Pe416 clean install -Dmaven.test.skip=true +./mvnw -Psnapshot -Pe416 clean install -Dmaven.test.skip=true diff --git a/eclipse-language-servers/org.springframework.tooling.boot.ls/META-INF/MANIFEST.MF b/eclipse-language-servers/org.springframework.tooling.boot.ls/META-INF/MANIFEST.MF index 78dfaae9f..8ca6046f1 100644 --- a/eclipse-language-servers/org.springframework.tooling.boot.ls/META-INF/MANIFEST.MF +++ b/eclipse-language-servers/org.springframework.tooling.boot.ls/META-INF/MANIFEST.MF @@ -31,7 +31,8 @@ Require-Bundle: org.eclipse.jdt.launching;bundle-version="3.9.0", org.eclipse.mylyn.wikitext, com.google.gson, org.eclipse.lsp4e.jdt;bundle-version="0.10.0", - org.springsource.ide.eclipse.commons.boot.ls + org.springsource.ide.eclipse.commons.boot.ls, + org.springframework.ide.eclipse.editor.support Import-Package: com.google.common.base, com.google.common.collect, com.google.gson;version="2.7.0", diff --git a/eclipse-language-servers/org.springframework.tooling.boot.ls/plugin.xml b/eclipse-language-servers/org.springframework.tooling.boot.ls/plugin.xml index f4fbaab6d..2f1e5a832 100644 --- a/eclipse-language-servers/org.springframework.tooling.boot.ls/plugin.xml +++ b/eclipse-language-servers/org.springframework.tooling.boot.ls/plugin.xml @@ -130,8 +130,22 @@ id="org.springframework.tooling.boot.ls.preferences" name="Spring Boot Language Server"> + + + + + + load() throws IOException { + File root = FileLocator.getBundleFile(BootLanguageServerPlugin.getDefault().getBundle()); + File metadataFile = root.toPath().resolve("servers/spring-boot-language-server/BOOT-INF/classes/problem-types.json").toFile(); + return ApplicationPropertiesEditorProblemSeverityPrefsPage.readFromFile(metadataFile); + } + +} diff --git a/eclipse-language-servers/pom.xml b/eclipse-language-servers/pom.xml index ada4fe16d..cc0b7ded5 100644 --- a/eclipse-language-servers/pom.xml +++ b/eclipse-language-servers/pom.xml @@ -38,10 +38,14 @@ + ../eclipse-extensions/org.springframework.ide.eclipse.boot + ../eclipse-extensions/org.springframework.ide.eclipse.editor.support ../headless-services/jdt-ls-extension + org.springframework.tooling.ls.eclipse.commons org.springframework.tooling.ls.eclipse.commons.test + org.springsource.ide.eclipse.commons.boot.ls org.springsource.ide.eclipse.commons.core org.springsource.ide.eclipse.commons.frameworks.core @@ -292,6 +296,11 @@ p2 https://dist.springsource.com/release/TOOLS/third-party/misc-p2-repo/${misc.p2.repo.version} + + yedit + p2 + https://dist.springsource.com/release/TOOLS/third-party/yedit + diff --git a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/reconcile/BadWordReconcileEngine.java b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/reconcile/BadWordReconcileEngine.java index 8d8c8c459..09bdbd066 100644 --- a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/reconcile/BadWordReconcileEngine.java +++ b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/reconcile/BadWordReconcileEngine.java @@ -36,6 +36,16 @@ public class BadWordReconcileEngine implements IReconcileEngine { public String getCode() { return this.name(); } + + @Override + public String getDescription() { + return "It is bad"; + } + + @Override + public String getLabel() { + return "Bad label"; + } } private final String[] BADWORDS = { diff --git a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/reconcile/ProblemType.java b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/reconcile/ProblemType.java index b1824524d..6b704f63c 100644 --- a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/reconcile/ProblemType.java +++ b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/reconcile/ProblemType.java @@ -26,4 +26,6 @@ public interface ProblemType { ProblemSeverity getDefaultSeverity(); String toString(); String getCode(); + String getLabel(); + String getDescription(); } diff --git a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/reconcile/ProblemTypes.java b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/reconcile/ProblemTypes.java index 852be4816..9b37db0c9 100644 --- a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/reconcile/ProblemTypes.java +++ b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/reconcile/ProblemTypes.java @@ -10,18 +10,24 @@ *******************************************************************************/ package org.springframework.ide.vscode.commons.languageserver.reconcile; -public class ProblemTypes { +import java.util.HashSet; +import org.springframework.ide.vscode.commons.util.Assert; + +public class ProblemTypes { + /** * Creates a new problem type. The newly created problem type is not 'equals' to any other * problem type. + * + * This method is deprecated. Look at ApplicationYamlProblemType for an example of how to define + * problem types to facilitate integration with preferences ui. * * @param defaultSeverity * @param typeName A unique name for this problem type. Note that it is the caller's responsibility that the typeName is unique. - * If this method is called more than once with identical typeName's it makes no attempts to veify that - * the name is uniquer, or to return the same object for the same typeName. * @return A newly create problem type. */ + @Deprecated public static ProblemType create(String typeName, ProblemSeverity defaultSeverity) { return new ProblemType() { @Override @@ -36,6 +42,14 @@ public class ProblemTypes { public String getCode() { return typeName; } + @Override + public String getDescription() { + return typeName; + } + @Override + public String getLabel() { + return typeName; + } }; } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/SpelExpressionReconciler.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/SpelExpressionReconciler.java index bd18116e8..5e4f6d638 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/SpelExpressionReconciler.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/SpelExpressionReconciler.java @@ -57,7 +57,6 @@ public class SpelExpressionReconciler implements Reconciler { private void createProblem(String spelExpression, String message, int startPosition, int position, IProblemCollector problemCollector) { int start = startPosition + position; int length = spelExpression.length() - position; - ReconcileProblem problem = new ReconcileProblemImpl(ProblemTypes.create("SpEL Expression Problem", ProblemSeverity.ERROR), message, start, length); problemCollector.accept(problem); } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/properties/reconcile/ApplicationPropertiesProblemType.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/properties/reconcile/ApplicationPropertiesProblemType.java index ed12258dd..127c19783 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/properties/reconcile/ApplicationPropertiesProblemType.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/properties/reconcile/ApplicationPropertiesProblemType.java @@ -13,6 +13,7 @@ package org.springframework.ide.vscode.boot.properties.reconcile; import static org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemSeverity.ERROR; import static org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemSeverity.WARNING; +import org.springframework.boot.SpringApplication; import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemSeverity; import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemType; @@ -20,7 +21,7 @@ import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemTy * @author Kris De Volder */ public enum ApplicationPropertiesProblemType implements ProblemType { - + PROP_INVALID_BEAN_NAVIGATION("Accessing a 'bean property' in a type that doesn't have properties (e.g. like String or Integer)"), PROP_INVALID_INDEXED_NAVIGATION("Accessing a property using [] in a type that doesn't support that"), PROP_EXPECTED_DOT_OR_LBRACK("Unexpected character found where a '.' or '[' was expected"), diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/yaml/reconcile/ApplicationYamlProblems.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/yaml/reconcile/ApplicationYamlProblems.java index 871eefb0e..6f4d1974e 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/yaml/reconcile/ApplicationYamlProblems.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/yaml/reconcile/ApplicationYamlProblems.java @@ -11,44 +11,12 @@ package org.springframework.ide.vscode.boot.yaml.reconcile; -import static org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemSeverity.ERROR; - -import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemSeverity; -import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemType; import org.springframework.ide.vscode.commons.languageserver.reconcile.ReconcileProblem; import org.springframework.ide.vscode.commons.languageserver.reconcile.ReconcileProblemImpl; public class ApplicationYamlProblems { - public static enum Type implements ProblemType { - - YAML_SYNTAX_ERROR; - - Type() { - this(ERROR); - } - - Type(ProblemSeverity defaultSeverity) { - this.severity = defaultSeverity; - } - - private final ProblemSeverity severity; - - @Override - public ProblemSeverity getDefaultSeverity() { - return severity; - } - - @Override - public String getCode() { - return name(); - } - - } - - public static final String YAML_SYNTAX_ERROR = "YAML_SYNTAX_ERROR"; - - public static ReconcileProblem problem(Type type, String msg, int offset, int len) { + public static ReconcileProblem problem(ApplicationYamlProblemType type, String msg, int offset, int len) { return new ReconcileProblemImpl(type, msg, offset, len); } } 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 0eff3dc9d..37fc03e45 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 @@ -10,8 +10,6 @@ *******************************************************************************/ package org.springframework.ide.vscode.boot.yaml.reconcile; -import static org.springframework.ide.vscode.boot.yaml.reconcile.ApplicationYamlProblems.Type.YAML_SYNTAX_ERROR; - import org.springframework.ide.vscode.boot.java.links.SourceLinks; import org.springframework.ide.vscode.boot.metadata.IndexNavigator; import org.springframework.ide.vscode.boot.metadata.PropertyInfo; @@ -53,6 +51,6 @@ public class ApplicationYamlReconcileEngine extends YamlReconcileEngine { @Override protected ReconcileProblem syntaxError(String msg, int offset, int len) { - return ApplicationYamlProblems.problem(YAML_SYNTAX_ERROR, msg, offset, len); + return ApplicationYamlProblems.problem(ApplicationYamlProblemType.YAML_SYNTAX_ERROR, msg, offset, len); } } diff --git a/headless-services/spring-boot-language-server/src/main/resources/problem-types.json b/headless-services/spring-boot-language-server/src/main/resources/problem-types.json new file mode 100644 index 000000000..61908a5e0 --- /dev/null +++ b/headless-services/spring-boot-language-server/src/main/resources/problem-types.json @@ -0,0 +1,150 @@ +{ + "application-yaml": [ + { + "code": "YAML_SYNTAX_ERROR", + "label": "Syntax error", + "description": "Error parsing the input using snakeyaml", + "defaultSeverity": "ERROR" + }, + { + "code": "YAML_UNKNOWN_PROPERTY", + "label": "Unknown property", + "description": "Property-key not found in the configuration metadata on the project\u0027s classpath", + "defaultSeverity": "WARNING" + }, + { + "code": "YAML_VALUE_TYPE_MISMATCH", + "label": "Value type mismatch", + "description": "Expecting a value of a certain type, but value doesn\u0027t parse as such", + "defaultSeverity": "ERROR" + }, + { + "code": "YAML_EXPECT_SCALAR", + "label": "Expect scalar", + "description": "Expecting a \u0027scalar\u0027 value but found something more complex.", + "defaultSeverity": "ERROR" + }, + { + "code": "YAML_EXPECT_TYPE_FOUND_SEQUENCE", + "label": "Expect type found sequence", + "description": "Found a \u0027sequence\u0027 node where a non \u0027list-like\u0027 type is expected", + "defaultSeverity": "ERROR" + }, + { + "code": "YAML_EXPECT_TYPE_FOUND_MAPPING", + "label": "Expect type found mapping", + "description": "Found a \u0027mapping\u0027 node where a type that can\u0027t be treated as a \u0027property map\u0027 is expected", + "defaultSeverity": "ERROR" + }, + { + "code": "YAML_EXPECT_MAPPING", + "label": "Expect mapping", + "description": "Expecting a \u0027mapping\u0027 node but found something else", + "defaultSeverity": "ERROR" + }, + { + "code": "YAML_EXPECT_BEAN_PROPERTY_NAME", + "label": "Expect bean property name", + "description": "Expecting a \u0027bean property\u0027 name but found something more complex", + "defaultSeverity": "ERROR" + }, + { + "code": "YAML_INVALID_BEAN_PROPERTY", + "label": "Invalid bean property", + "description": "Accessing a named property in a type that doesn\u0027t provide a property accessor with that name", + "defaultSeverity": "ERROR" + }, + { + "code": "YAML_DEPRECATED_ERROR", + "label": "Deprecated error", + "description": "Property is marked as Deprecated(Error)", + "defaultSeverity": "ERROR" + }, + { + "code": "YAML_DEPRECATED_WARNING", + "label": "Deprecated warning", + "description": "Property is marked as Deprecated(Warning)", + "defaultSeverity": "WARNING" + }, + { + "code": "YAML_DUPLICATE_KEY", + "label": "Duplicate key", + "description": "A mapping node contains multiple entries for the same key", + "defaultSeverity": "ERROR" + }, + { + "code": "YAML_SHOULD_ESCAPE", + "label": "Should escape", + "description": "This key contains special characters and should probably be escaped by surrounding it with \u0027[]\u0027", + "defaultSeverity": "WARNING" + } + ], + "application-properties": [ + { + "code": "PROP_INVALID_BEAN_NAVIGATION", + "label": "Invalid bean navigation", + "description": "Accessing a \u0027bean property\u0027 in a type that doesn\u0027t have properties (e.g. like String or Integer)", + "defaultSeverity": "ERROR" + }, + { + "code": "PROP_INVALID_INDEXED_NAVIGATION", + "label": "Invalid indexed navigation", + "description": "Accessing a property using [] in a type that doesn\u0027t support that", + "defaultSeverity": "ERROR" + }, + { + "code": "PROP_EXPECTED_DOT_OR_LBRACK", + "label": "Expected dot or lbrack", + "description": "Unexpected character found where a \u0027.\u0027 or \u0027[\u0027 was expected", + "defaultSeverity": "ERROR" + }, + { + "code": "PROP_NO_MATCHING_RBRACK", + "label": "No matching rbrack", + "description": "Found a \u0027[\u0027 but no matching \u0027]\u0027", + "defaultSeverity": "ERROR" + }, + { + "code": "PROP_NON_INTEGER_IN_BRACKETS", + "label": "Non integer in brackets", + "description": "Use of [..] navigation with non-integer value", + "defaultSeverity": "ERROR" + }, + { + "code": "PROP_VALUE_TYPE_MISMATCH", + "label": "Value type mismatch", + "description": "Expecting a value of a certain type, but value doesn\u0027t parse as such", + "defaultSeverity": "ERROR" + }, + { + "code": "PROP_INVALID_BEAN_PROPERTY", + "label": "Invalid bean property", + "description": "Accessing a named property in a type that doesn\u0027t provide a property accessor with that name", + "defaultSeverity": "ERROR" + }, + { + "code": "PROP_UNKNOWN_PROPERTY", + "label": "Unknown property", + "description": "Property-key not found in any configuration metadata on the project\u0027s classpath", + "defaultSeverity": "WARNING" + }, + { + "code": "PROP_DEPRECATED", + "label": "Deprecated", + "description": "Property is marked as Deprecated", + "defaultSeverity": "WARNING" + }, + { + "code": "PROP_DUPLICATE_KEY", + "label": "Duplicate key", + "description": "Multiple assignments to the same property value", + "defaultSeverity": "ERROR" + }, + { + "code": "PROP_SYNTAX_ERROR", + "label": "Syntax error", + "description": "Syntax Error", + "defaultSeverity": "ERROR" + } + ] +} \ No newline at end of file diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/test/ProblemTypesMetadataTest.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/test/ProblemTypesMetadataTest.java new file mode 100644 index 000000000..9dd02d008 --- /dev/null +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/test/ProblemTypesMetadataTest.java @@ -0,0 +1,29 @@ +/******************************************************************************* + * Copyright (c) 2020 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 + * https://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Pivotal, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.boot.test; + +import org.junit.jupiter.api.Test; +import org.springframework.ide.vscode.boot.properties.reconcile.ApplicationPropertiesProblemType; +import org.springframework.ide.vscode.boot.yaml.reconcile.ApplicationYamlProblemType; + +public class ProblemTypesMetadataTest { + + @Test + public void dataIsConsitent() throws Exception { + //If this test fails it probably just means you have to run the main + // method in org.springframework.ide.vscode.boot.test.ProblemTypesToJson + // to synchronize the metadata json file with the real problem type objects in the source code. + ProblemTypesToJson reader = new ProblemTypesToJson().read(); + reader.validate("application-properties", ApplicationPropertiesProblemType.values()); + reader.validate("application-yaml", ApplicationYamlProblemType.values()); + } + +} diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/test/ProblemTypesToJson.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/test/ProblemTypesToJson.java new file mode 100644 index 000000000..5113f19c5 --- /dev/null +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/test/ProblemTypesToJson.java @@ -0,0 +1,154 @@ +/******************************************************************************* + * Copyright (c) 2020 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 + * https://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Pivotal, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.boot.test; + +import static org.junit.Assert.assertEquals; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.apache.commons.io.FileUtils; +import org.springframework.ide.vscode.boot.properties.reconcile.ApplicationPropertiesProblemType; +import org.springframework.ide.vscode.boot.yaml.reconcile.ApplicationYamlProblemType; +import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemType; + +import com.google.common.reflect.TypeToken; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +/** + * Helper that dumps out ProblemTypes to a json file. This file can + * be used (how?) to drive problem severity configuration ui. + *

+ * Run this from Eclipse simply by selecting this file and do + * "Run As >> Java Application" + */ +public class ProblemTypesToJson { + + public static final String resourcesPath = "src/main/resources"; + public static final String metaDataFileName = "problem-types.json"; + + public static class ProblemTypeData { + String code; + String label; + String description; + String defaultSeverity; + + public ProblemTypeData() {} + + public ProblemTypeData(ProblemType type) { + this.code = type.getCode(); + this.description = type.getDescription(); + this.defaultSeverity =type.getDefaultSeverity().toString(); + this.label = type.getLabel(); + } + + public ProblemTypeData(String defaultSeverity) { + super(); + this.setDefaultSeverity(defaultSeverity); + } + + public String getCode() { + return code; + } + public void setCode(String code) { + this.code = code; + } + public String getDescription() { + return description; + } + public void setDescription(String description) { + this.description = description; + } + + public String getDefaultSeverity() { + return defaultSeverity; + } + + public void setDefaultSeverity(String defaultSeverity) { + this.defaultSeverity = defaultSeverity; + } + + public String getLabel() { + return label; + } + + public void setLabel(String label) { + this.label = label; + } + } + + Map problemTypes = new HashMap<>(); + + public static void main(String[] args) throws IOException { + ProblemTypesToJson writer = new ProblemTypesToJson(); + writer.problemsFor("application-yaml", ApplicationYamlProblemType.values()); + writer.problemsFor("application-properties", ApplicationPropertiesProblemType.values()); + writer.dump(); + } + + private void dump() throws IOException { + Gson gson = new GsonBuilder().setPrettyPrinting().create(); + String json = gson.toJson(problemTypes); + FileUtils.writeStringToFile(new File(resourcesPath+"/"+metaDataFileName), json); + } + + private void problemsFor(String string, ProblemType[] values) { + ProblemTypeData[] data = new ProblemTypeData[values.length]; + for (int i = 0; i < data.length; i++) { + data[i] = new ProblemTypeData(values[i]); + } + problemTypes.put(string, data); + } + + public ProblemTypesToJson read() throws IOException { + try (InputStream input = this.getClass().getResourceAsStream("/"+metaDataFileName)) { + Gson gson = new GsonBuilder().setPrettyPrinting().create(); + TypeToken> tt = new TypeToken>() {}; + problemTypes = gson.fromJson(new InputStreamReader(input), tt.getType()); + } + return this; + } + + public void validate(String name, ProblemType[] actualProblemTypes) { + ProblemTypeData[] metadataProblemTypes = problemTypes.get(name); + Set metadataHas = Stream.of(metadataProblemTypes).map(x -> x.getCode()).collect(Collectors.toSet()); + Set actualHas = Stream.of(actualProblemTypes).map(x-> x.getCode()).collect(Collectors.toSet()); + for (String string : actualHas) { + if (!metadataHas.contains(string)) { + throw new IllegalStateException("No metadata for "+string); + } + } + for (String string : metadataHas) { + if (!actualHas.contains(string)) { + throw new IllegalStateException("Metadata for non-existent problem type "+string); + } + } + + for (int i = 0; i < metadataProblemTypes.length; i++) { + ProblemTypeData metadata = metadataProblemTypes[i]; + ProblemType actual = actualProblemTypes[i]; + assertEquals(actual.getCode(), metadata.getCode()); + assertEquals(actual.getDescription(), metadata.getDescription()); + assertEquals(actual.getDefaultSeverity().toString(), metadata.getDefaultSeverity()); + assertEquals(actual.getLabel(), metadata.getLabel()); + } + } + + +}