From 0fb7e7daca30135b6b671cc16a60927a2736ea19 Mon Sep 17 00:00:00 2001 From: aboyko Date: Thu, 20 Oct 2022 10:15:29 -0400 Subject: [PATCH] Minimal Spring Factories support in Boot LS --- .../.project | 17 +++++ .../plugin.xml | 51 +++++++++++++ .../languageserver/util/ListenerList.java | 2 +- .../app/BootLanguageServerInitializer.java | 3 +- ...ringFactoriesLanguageServerComponents.java | 54 +++++++++++++ .../SpringFactoriesReconcileEngine.java | 76 +++++++++++++++++++ .../boot/java/Boot3JavaProblemType.java | 4 +- .../src/main/resources/problem-types.json | 6 ++ .../vscode-spring-boot/lib/Main.ts | 5 ++ .../vscode-spring-boot/package.json | 28 +++++++ 10 files changed, 243 insertions(+), 3 deletions(-) create mode 100644 eclipse-distribution/org.springframework.boot.ide.product.e417/.project create mode 100644 headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/factories/SpringFactoriesLanguageServerComponents.java create mode 100644 headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/factories/SpringFactoriesReconcileEngine.java diff --git a/eclipse-distribution/org.springframework.boot.ide.product.e417/.project b/eclipse-distribution/org.springframework.boot.ide.product.e417/.project new file mode 100644 index 000000000..bee6e73c3 --- /dev/null +++ b/eclipse-distribution/org.springframework.boot.ide.product.e417/.project @@ -0,0 +1,17 @@ + + + org.springframework.boot.ide.product.e417 + + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.m2e.core.maven2Nature + + 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 c8396672a..90c842288 100644 --- a/eclipse-language-servers/org.springframework.tooling.boot.ls/plugin.xml +++ b/eclipse-language-servers/org.springframework.tooling.boot.ls/plugin.xml @@ -34,6 +34,12 @@ id="org.eclipse.languageserver.languages.springboot"> + + + + + + + + + + + + + + + + + + + + + + + { } } - public void add(Consumer l) { + public synchronized void add(Consumer l) { listeners.add(l); } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootLanguageServerInitializer.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootLanguageServerInitializer.java index 3bdc86c30..187ebba19 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootLanguageServerInitializer.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootLanguageServerInitializer.java @@ -11,7 +11,6 @@ package org.springframework.ide.vscode.boot.app; import java.net.URI; -import java.nio.file.Path; import java.time.Duration; import java.util.ArrayList; import java.util.HashMap; @@ -28,6 +27,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.ApplicationContext; import org.springframework.ide.vscode.boot.common.IJavaProjectReconcileEngine; +import org.springframework.ide.vscode.boot.factories.SpringFactoriesLanguageServerComponents; import org.springframework.ide.vscode.boot.java.BootJavaLanguageServerComponents; import org.springframework.ide.vscode.boot.java.links.JavaElementLocationProvider; import org.springframework.ide.vscode.boot.java.links.SourceLinks; @@ -143,6 +143,7 @@ public class BootLanguageServerInitializer implements InitializingBean { BootJavaLanguageServerComponents bootJavaLanguageServerComponent = new BootJavaLanguageServerComponents(appContext); builder.add(bootJavaLanguageServerComponent); builder.add(new SpringXMLLanguageServerComponents(server, springIndexer, params, config)); + builder.add(new SpringFactoriesLanguageServerComponents(projectFinder, springIndexer)); components = builder.build(server); projectReconciler = (IJavaProjectReconcileEngine) bootJavaLanguageServerComponent.getReconcileEngine().get(); diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/factories/SpringFactoriesLanguageServerComponents.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/factories/SpringFactoriesLanguageServerComponents.java new file mode 100644 index 000000000..21b7bee84 --- /dev/null +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/factories/SpringFactoriesLanguageServerComponents.java @@ -0,0 +1,54 @@ +/******************************************************************************* + * Copyright (c) 2022 VMware, 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: + * VMware, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.boot.factories; + +import java.util.Optional; +import java.util.Set; + +import org.springframework.ide.vscode.boot.app.SpringSymbolIndex; +import org.springframework.ide.vscode.commons.languageserver.composable.LanguageServerComponents; +import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; +import org.springframework.ide.vscode.commons.languageserver.reconcile.IReconcileEngine; +import org.springframework.ide.vscode.commons.languageserver.util.DocumentSymbolHandler; +import org.springframework.ide.vscode.commons.languageserver.util.HoverHandler; +import org.springframework.ide.vscode.commons.util.text.LanguageId; + +public class SpringFactoriesLanguageServerComponents implements LanguageServerComponents { + + private SpringFactoriesReconcileEngine reconciler; + private SpringSymbolIndex springIndex; + + public SpringFactoriesLanguageServerComponents(JavaProjectFinder projectFinder, SpringSymbolIndex springIndex) { + this.springIndex = springIndex; + reconciler = new SpringFactoriesReconcileEngine(projectFinder); + } + + @Override + public Set getInterestingLanguages() { + return Set.of(LanguageId.SPRING_FACTORIES); + } + + @Override + public HoverHandler getHoverProvider() { + return null; + } + + @Override + public Optional getReconcileEngine() { + return Optional.of(reconciler); + } + + @Override + public Optional getDocumentSymbolProvider() { + return Optional.of(params -> springIndex.getSymbols(params.getTextDocument().getUri())); + } + +} diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/factories/SpringFactoriesReconcileEngine.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/factories/SpringFactoriesReconcileEngine.java new file mode 100644 index 000000000..5c223330a --- /dev/null +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/factories/SpringFactoriesReconcileEngine.java @@ -0,0 +1,76 @@ +package org.springframework.ide.vscode.boot.factories; + +/******************************************************************************* + * Copyright (c) 2022 VMware, 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: + * VMware, Inc. - initial API and implementation + *******************************************************************************/ +import java.util.Map; + +import org.eclipse.lsp4j.TextDocumentIdentifier; +import org.springframework.ide.vscode.boot.java.Boot3JavaProblemType; +import org.springframework.ide.vscode.commons.java.IJavaProject; +import org.springframework.ide.vscode.commons.java.SpringProjectUtil; +import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; +import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector; +import org.springframework.ide.vscode.commons.languageserver.reconcile.IReconcileEngine; +import org.springframework.ide.vscode.commons.languageserver.reconcile.ReconcileProblemImpl; +import org.springframework.ide.vscode.commons.util.text.IDocument; +import org.springframework.ide.vscode.java.properties.antlr.parser.AntlrParser; +import org.springframework.ide.vscode.java.properties.parser.PropertiesAst; +import org.springframework.ide.vscode.java.properties.parser.PropertiesAst.KeyValuePair; + +public class SpringFactoriesReconcileEngine implements IReconcileEngine { + + @FunctionalInterface + interface KeyValuePairReconciler { + void reconcile(IJavaProject project, KeyValuePair pair, IProblemCollector problemCollector); + } + + private final JavaProjectFinder projectFinder; + private AntlrParser parser = new AntlrParser(); + + private final Map keyValuePairReconcilers = Map.of( + "org.springframework.boot.autoconfigure.EnableAutoConfiguration", (project, pair, problemCollector) -> { + if (SpringProjectUtil.springBootVersionGreaterOrEqual(3, 0, 0).test(project)) { + ReconcileProblemImpl problem = new ReconcileProblemImpl( + Boot3JavaProblemType.FACTORIES_KEY_NOT_SUPPORTED, + "Key is not supported as of Spring Boot 3. See Boot 3 documentation topic \"Locating Auto-configuration Candidates\"", + pair.getOffset(), + pair.getLength()); + problemCollector.accept(problem); + } + } + ); + + public SpringFactoriesReconcileEngine(JavaProjectFinder projectFinder) { + this.projectFinder = projectFinder; + } + + @Override + public void reconcile(IDocument doc, IProblemCollector problemCollector) { + projectFinder.find(new TextDocumentIdentifier(doc.getUri())).ifPresent(project -> { + problemCollector.beginCollecting(); + try { + PropertiesAst ast = parser.parse(doc.get()).ast; + if (ast != null) { + for (KeyValuePair pair : ast.getNodes(KeyValuePair.class)) { + String key = pair.getKey().decode().trim(); + KeyValuePairReconciler r = keyValuePairReconcilers.get(key); + if (r != null) { + r.reconcile(project, pair, problemCollector); + } + } + } + } finally { + problemCollector.endCollecting(); + } + }); + } + +} diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/Boot3JavaProblemType.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/Boot3JavaProblemType.java index df35790d6..0726e2233 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/Boot3JavaProblemType.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/Boot3JavaProblemType.java @@ -30,7 +30,9 @@ public enum Boot3JavaProblemType implements ProblemType { JAVA_BEAN_NOT_REGISTERED_IN_AOT(WARNING, "Not registered as Bean", "Not registered as a Bean"), - JAVA_TYPE_NOT_SUPPORTED(ERROR, "Type no supported as of Spring Boot 3", "Type not supported as of Spring Boot 3"); + JAVA_TYPE_NOT_SUPPORTED(ERROR, "Type no supported as of Spring Boot 3", "Type not supported as of Spring Boot 3"), + + FACTORIES_KEY_NOT_SUPPORTED(ERROR, "Spring factories key not supported", "Spring factories key not supported"); private final ProblemSeverity defaultSeverity; private String description; 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 index 00c2091a6..56269efca 100644 --- 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 @@ -72,6 +72,12 @@ "label": "Type not supported as of Spring Boot 3", "description": "Type no supported as of Spring Boot 3", "defaultSeverity": "ERROR" + }, + { + "code": "FACTORIES_KEY_NOT_SUPPORTED", + "label": "Spring factories key not supported", + "description": "Spring factories key not supported", + "defaultSeverity": "ERROR" } ] }, diff --git a/vscode-extensions/vscode-spring-boot/lib/Main.ts b/vscode-extensions/vscode-spring-boot/lib/Main.ts index 3c2f3ff8b..34a5cfb8f 100644 --- a/vscode-extensions/vscode-spring-boot/lib/Main.ts +++ b/vscode-extensions/vscode-spring-boot/lib/Main.ts @@ -16,6 +16,7 @@ const PROPERTIES_LANGUAGE_ID = "spring-boot-properties"; const YAML_LANGUAGE_ID = "spring-boot-properties-yaml"; const JAVA_LANGUAGE_ID = "java"; const XML_LANGUAGE_ID = "xml"; +const FACTORIES_LANGUAGE_ID = "spring-factories"; const NEVER_SHOW_AGAIN = "Do not show again"; @@ -93,6 +94,10 @@ export function activate(context: VSCode.ExtensionContext): Thenable