diff --git a/eclipse-extensions/org.springframework.ide.eclipse.boot.launch/plugin.xml b/eclipse-extensions/org.springframework.ide.eclipse.boot.launch/plugin.xml
index ed7ec551c..c09175773 100644
--- a/eclipse-extensions/org.springframework.ide.eclipse.boot.launch/plugin.xml
+++ b/eclipse-extensions/org.springframework.ide.eclipse.boot.launch/plugin.xml
@@ -221,6 +221,11 @@
id="org.springframework.ide.eclipse.boot.commands.editStartersCommand"
name="Edit Starters">
+
+
-
+
diff --git a/eclipse-extensions/org.springframework.ide.eclipse.boot.wizard/.settings/org.eclipse.jdt.core.prefs b/eclipse-extensions/org.springframework.ide.eclipse.boot.wizard/.settings/org.eclipse.jdt.core.prefs
index 9f6ece88b..8aabe8df8 100644
--- a/eclipse-extensions/org.springframework.ide.eclipse.boot.wizard/.settings/org.eclipse.jdt.core.prefs
+++ b/eclipse-extensions/org.springframework.ide.eclipse.boot.wizard/.settings/org.eclipse.jdt.core.prefs
@@ -1,8 +1,14 @@
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
-org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
-org.eclipse.jdt.core.compiler.compliance=1.8
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=16
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=16
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
-org.eclipse.jdt.core.compiler.release=disabled
-org.eclipse.jdt.core.compiler.source=1.8
+org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning
+org.eclipse.jdt.core.compiler.release=enabled
+org.eclipse.jdt.core.compiler.source=16
diff --git a/eclipse-extensions/org.springframework.ide.eclipse.boot.wizard/plugin.xml b/eclipse-extensions/org.springframework.ide.eclipse.boot.wizard/plugin.xml
index 55ef78cc6..83406514d 100644
--- a/eclipse-extensions/org.springframework.ide.eclipse.boot.wizard/plugin.xml
+++ b/eclipse-extensions/org.springframework.ide.eclipse.boot.wizard/plugin.xml
@@ -142,6 +142,10 @@
+
+
diff --git a/eclipse-extensions/org.springframework.ide.eclipse.boot.wizard/src/org/springframework/ide/eclipse/boot/wizard/AddBootStartersHandler2.java b/eclipse-extensions/org.springframework.ide.eclipse.boot.wizard/src/org/springframework/ide/eclipse/boot/wizard/AddBootStartersHandler2.java
new file mode 100644
index 000000000..3eb99e1cd
--- /dev/null
+++ b/eclipse-extensions/org.springframework.ide.eclipse.boot.wizard/src/org/springframework/ide/eclipse/boot/wizard/AddBootStartersHandler2.java
@@ -0,0 +1,38 @@
+/*******************************************************************************
+ * Copyright (c) 2024 Broadcom, 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:
+ * Broadcom, Inc. - initial API and implementation
+ *******************************************************************************/
+package org.springframework.ide.eclipse.boot.wizard;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IFileEditorInput;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.handlers.HandlerUtil;
+import org.springframework.ide.eclipse.boot.wizard.starters.AddStartersWizard;
+
+public class AddBootStartersHandler2 extends AbstractHandler {
+
+ @Override
+ public Object execute(ExecutionEvent event) throws ExecutionException {
+ try {
+ IEditorInput input = HandlerUtil.getActiveEditorInput(event);
+ if (input instanceof IFileEditorInput fileInput) {
+ AddStartersWizard.openFor(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(), fileInput.getFile().getProject());
+ return null;
+ }
+ } catch (Exception e) {
+ throw new ExecutionException("", e);
+ }
+ throw new ExecutionException("Current active editor is not for pom.xml");
+ }
+
+}
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 9942e4a07..de1ec1509 100644
--- a/eclipse-language-servers/org.springframework.tooling.boot.ls/plugin.xml
+++ b/eclipse-language-servers/org.springframework.tooling.boot.ls/plugin.xml
@@ -82,6 +82,10 @@
-->
+
+
@@ -173,6 +177,17 @@
file-names="spring.factories,aot.factories"
file-patterns="*.factories">
+
+
+
+
+
+
@@ -292,6 +311,24 @@
id="org.springframework.tooling.boot.ls.modulith.metadata.refresh"
name="Refresh Modulith Metadata">
+
+
+
+
+
+
componentsByLanguageId = new HashMap<>();
+ private Map> componentsByLanguageId = new HashMap<>();
public void add(LanguageServerComponents components) {
for (LanguageId language : components.getInterestingLanguages()) {
- //Multiple associations to a single language id not yet supported.
- Assert.isLegal(!componentsByLanguageId.containsKey(language));
- componentsByLanguageId.put(language, components);
+ List list = componentsByLanguageId.get(language);
+ if (list == null) {
+ list = new ArrayList<>(1);
+ componentsByLanguageId.put(language, list);
+ }
+ list.add(components);
}
}
@@ -60,30 +70,33 @@ public class CompositeLanguageServerComponents implements LanguageServerComponen
}
}
- private final Map componentsByLanguageId;
+ private final Map> componentsByLanguageId;
private final IReconcileEngine reconcileEngine;
private final HoverHandler hoverHandler;
private final CodeActionHandler codeActionHandler;
+ private final CodeLensHandler codeLensHandler;
private final DocumentSymbolHandler docSymbolHandler;
+ private final InlayHintHandler inlayHintHandler;
public CompositeLanguageServerComponents(SimpleLanguageServer server, Builder builder) {
this.componentsByLanguageId = ImmutableMap.copyOf(builder.componentsByLanguageId);
//Create composite Reconcile engine
- if (componentsByLanguageId.values().stream().map(LanguageServerComponents::getReconcileEngine).anyMatch(Optional::isPresent)) {
+ if (componentsByLanguageId.values().stream().flatMap(l -> l.stream()).map(LanguageServerComponents::getReconcileEngine).anyMatch(Optional::isPresent)) {
this.reconcileEngine = new IReconcileEngine() {
@Override
public void reconcile(IDocument document, IProblemCollector problemCollector) {
LanguageId language = document.getLanguageId();
- LanguageServerComponents subComponents = componentsByLanguageId.get(language);
+ List subComponents = componentsByLanguageId.get(language);
if (subComponents!=null) {
- Optional subEngine = subComponents.getReconcileEngine();
- if (subEngine.isPresent()) {
- subEngine.get().reconcile(document, problemCollector);
- return;
+ for (LanguageServerComponents subComponent : subComponents) {
+ Optional subEngine = subComponent.getReconcileEngine();
+ if (subEngine.isPresent()) {
+ subEngine.get().reconcile(document, problemCollector);
+ }
}
}
- //No applicable subEngine... but we still have to obey the IReconcileEngine contract!
- IReconcileEngine.NULL.reconcile(document, problemCollector);
+// //No applicable subEngine... but we still have to obey the IReconcileEngine contract!
+// IReconcileEngine.NULL.reconcile(document, problemCollector);
}
};
} else {
@@ -95,11 +108,13 @@ public class CompositeLanguageServerComponents implements LanguageServerComponen
public Hover handle(CancelChecker cancelToken, HoverParams params) {
TextDocument doc = server.getTextDocumentService().getLatestSnapshot(params.getTextDocument().getUri());
LanguageId language = doc.getLanguageId();
- LanguageServerComponents subComponents = componentsByLanguageId.get(language);
+ List subComponents = componentsByLanguageId.get(language);
if (subComponents != null) {
- HoverHandler subEngine = subComponents.getHoverProvider();
- if (subEngine != null) {
- return subEngine.handle(cancelToken, params);
+ for (LanguageServerComponents subComponent : subComponents) {
+ HoverHandler subEngine = subComponent.getHoverProvider();
+ if (subEngine != null) {
+ return subEngine.handle(cancelToken, params);
+ }
}
}
//No applicable subEngine...
@@ -113,11 +128,51 @@ public class CompositeLanguageServerComponents implements LanguageServerComponen
public List> handle(CancelChecker cancelToken,
CodeActionCapabilities capabilities, CodeActionContext context, TextDocument doc, IRegion region) {
LanguageId language = doc.getLanguageId();
- LanguageServerComponents subComponents = componentsByLanguageId.get(language);
+ List subComponents = componentsByLanguageId.get(language);
if (subComponents != null) {
- return subComponents.getCodeActionProvider()
- .map(subEngine -> subEngine.handle(cancelToken, capabilities, context, doc, region))
- .orElse(Collections.emptyList());
+ return subComponents.stream()
+ .map(sc -> sc.getCodeActionProvider())
+ .filter(provider -> provider.isPresent())
+ .flatMap(se -> se.get().handle(cancelToken, capabilities, context, doc, region).stream())
+ .collect(Collectors.toList());
+ }
+ // No applicable subEngine...
+ return Collections.emptyList();
+ }
+ };
+
+ this.codeLensHandler = new CodeLensHandler() {
+
+ @Override
+ public List extends CodeLens> handle(CancelChecker cancelToken, CodeLensParams params) {
+ TextDocument doc = server.getTextDocumentService().getLatestSnapshot(params.getTextDocument().getUri());
+ LanguageId language = doc.getLanguageId();
+ List subComponents = componentsByLanguageId.get(language);
+ if (subComponents != null) {
+ return subComponents.stream()
+ .map(sc -> sc.getCodeLensHandler())
+ .filter(h -> h.isPresent())
+ .flatMap(h -> h.get().handle(cancelToken, params).stream())
+ .collect(Collectors.toList());
+ }
+ // No applicable subEngine...
+ return Collections.emptyList();
+ }
+ };
+
+ this.inlayHintHandler = new InlayHintHandler() {
+
+ @Override
+ public List handle(CancelChecker token, InlayHintParams params) {
+ TextDocument doc = server.getTextDocumentService().getLatestSnapshot(params.getTextDocument().getUri());
+ LanguageId language = doc.getLanguageId();
+ List subComponents = componentsByLanguageId.get(language);
+ if (subComponents != null) {
+ return subComponents.stream()
+ .map(sc -> sc.getInlayHintHandler())
+ .filter(h -> h.isPresent())
+ .flatMap(h -> h.get().handle(token, params).stream())
+ .collect(Collectors.toList());
}
// No applicable subEngine...
return Collections.emptyList();
@@ -130,12 +185,13 @@ public class CompositeLanguageServerComponents implements LanguageServerComponen
public List extends WorkspaceSymbol> handle(DocumentSymbolParams params) {
TextDocument doc = server.getTextDocumentService().getLatestSnapshot(params.getTextDocument().getUri());
LanguageId language = doc.getLanguageId();
- LanguageServerComponents subComponents = componentsByLanguageId.get(language);
+ List subComponents = componentsByLanguageId.get(language);
if (subComponents != null) {
- DocumentSymbolHandler subHandler = subComponents.getDocumentSymbolProvider().orElse(null);
- if (subHandler != null) {
- return subHandler.handle(params);
- }
+ return subComponents.stream()
+ .map(sc -> sc.getDocumentSymbolProvider())
+ .filter(ds -> ds.isPresent())
+ .flatMap(ds -> ds.get().handle(params).stream())
+ .collect(Collectors.toList());
}
//No applicable subEngine...
return Collections.emptyList();
@@ -163,14 +219,13 @@ public class CompositeLanguageServerComponents implements LanguageServerComponen
return Optional.ofNullable(docSymbolHandler);
}
- @SuppressWarnings("unchecked")
public C get(Class subComponentsType) {
- for (LanguageServerComponents subcomp : this.componentsByLanguageId.values()) {
- if (subComponentsType.isInstance(subcomp)) {
- return (C) subcomp;
- }
- }
- return null;
+ return this.componentsByLanguageId.values().stream()
+ .flatMap(l -> l.stream())
+ .filter(subComponentsType::isInstance)
+ .map(subComponentsType::cast)
+ .findFirst()
+ .orElse(null);
}
@Override
@@ -178,4 +233,14 @@ public class CompositeLanguageServerComponents implements LanguageServerComponen
return Optional.of(codeActionHandler);
}
+ @Override
+ public Optional getCodeLensHandler() {
+ return Optional.of(codeLensHandler);
+ }
+
+ @Override
+ public Optional getInlayHintHandler() {
+ return Optional.of(inlayHintHandler);
+ }
+
}
diff --git a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/composable/LanguageServerComponents.java b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/composable/LanguageServerComponents.java
index 9925d3113..257426106 100644
--- a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/composable/LanguageServerComponents.java
+++ b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/composable/LanguageServerComponents.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2018, 2022 Pivotal, Inc.
+ * Copyright (c) 2018, 2024 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
@@ -15,15 +15,19 @@ import java.util.Set;
import org.springframework.ide.vscode.commons.languageserver.reconcile.IReconcileEngine;
import org.springframework.ide.vscode.commons.languageserver.util.CodeActionHandler;
+import org.springframework.ide.vscode.commons.languageserver.util.CodeLensHandler;
import org.springframework.ide.vscode.commons.languageserver.util.DocumentSymbolHandler;
import org.springframework.ide.vscode.commons.languageserver.util.HoverHandler;
+import org.springframework.ide.vscode.commons.languageserver.util.InlayHintHandler;
import org.springframework.ide.vscode.commons.util.text.LanguageId;
public interface LanguageServerComponents {
Set getInterestingLanguages();
default Optional getReconcileEngine() { return Optional.empty(); }
- HoverHandler getHoverProvider();
+ default HoverHandler getHoverProvider() { return null; }
default Optional getCodeActionProvider() { return Optional.empty(); }
default Optional getDocumentSymbolProvider() { return Optional.empty(); }
+ default Optional getCodeLensHandler() { return Optional.empty(); }
+ default Optional getInlayHintHandler() { return Optional.empty(); }
}
diff --git a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/InlayHintHandler.java b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/InlayHintHandler.java
new file mode 100644
index 000000000..6c9c09f06
--- /dev/null
+++ b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/InlayHintHandler.java
@@ -0,0 +1,23 @@
+/*******************************************************************************
+ * Copyright (c) 2024 Broadcom, 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:
+ * Broadcom, Inc. - initial API and implementation
+ *******************************************************************************/
+package org.springframework.ide.vscode.commons.languageserver.util;
+
+import java.util.List;
+
+import org.eclipse.lsp4j.InlayHint;
+import org.eclipse.lsp4j.InlayHintParams;
+import org.eclipse.lsp4j.jsonrpc.CancelChecker;
+
+public interface InlayHintHandler {
+
+ List handle(CancelChecker token, InlayHintParams params);
+
+}
diff --git a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleLanguageServer.java b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleLanguageServer.java
index 4745b7c5d..adec2806c 100644
--- a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleLanguageServer.java
+++ b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleLanguageServer.java
@@ -521,6 +521,9 @@ public final class SimpleLanguageServer implements Sts4LanguageServer, SpringInd
codeLensOptions.setResolveProvider(hasCodeLensResolveProvider());
c.setCodeLensProvider(codeLensOptions );
}
+ if (hasInlayHintHandler()) {
+ c.setInlayHintProvider(true);
+ }
if (hasExecuteCommandSupport && (
hasQuickFixes() ||
!commands.isEmpty()
@@ -566,6 +569,10 @@ public final class SimpleLanguageServer implements Sts4LanguageServer, SpringInd
private boolean hasCodeLensHandler() {
return getTextDocumentService().hasCodeLensHandler();
}
+
+ private boolean hasInlayHintHandler() {
+ return getTextDocumentService().hasInlayHintHandler();
+ }
private boolean hasCodeLensResolveProvider() {
return getTextDocumentService().hasCodeLensResolveProvider();
diff --git a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleTextDocumentService.java b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleTextDocumentService.java
index 06d0b4ee8..7c0f377f4 100644
--- a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleTextDocumentService.java
+++ b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleTextDocumentService.java
@@ -52,6 +52,8 @@ import org.eclipse.lsp4j.DocumentSymbol;
import org.eclipse.lsp4j.DocumentSymbolParams;
import org.eclipse.lsp4j.Hover;
import org.eclipse.lsp4j.HoverParams;
+import org.eclipse.lsp4j.InlayHint;
+import org.eclipse.lsp4j.InlayHintParams;
import org.eclipse.lsp4j.Location;
import org.eclipse.lsp4j.LocationLink;
import org.eclipse.lsp4j.PublishDiagnosticsParams;
@@ -119,6 +121,7 @@ public class SimpleTextDocumentService implements TextDocumentService, DocumentE
private CodeLensHandler codeLensHandler;
private CodeLensResolveHandler codeLensResolveHandler;
private CodeActionHandler codeActionHandler;
+ private InlayHintHandler inlayHintHandler;
final private ApplicationContext appContext;
@@ -531,6 +534,18 @@ public class SimpleTextDocumentService implements TextDocumentService, DocumentE
}
return CompletableFuture.completedFuture(Collections.emptyList());
}
+
+ @Override
+ public CompletableFuture> inlayHint(InlayHintParams params) {
+ InlayHintHandler handler = this.inlayHintHandler;
+
+ if (handler != null) {
+ return CompletableFutures.computeAsync(messageWorkerThreadPool, cancelToken -> {
+ return handler.handle(cancelToken, params);
+ });
+ }
+ return CompletableFuture.completedFuture(Collections.emptyList());
+ }
@Override
public CompletableFuture resolveCodeLens(CodeLens unresolved) {
@@ -665,10 +680,19 @@ public class SimpleTextDocumentService implements TextDocumentService, DocumentE
Assert.isNull("A code action handler is already set, multiple handlers not supported yet", codeActionHandler);
this.codeActionHandler = h;
}
+
+ public synchronized void onInlayHint(InlayHintHandler h) {
+ Assert.isNull("A inlay hint handler is already set, multiple handlers not supported yet", inlayHintHandler);
+ this.inlayHintHandler = h;
+ }
public boolean hasCodeLensHandler() {
return this.codeLensHandler != null;
}
+
+ public boolean hasInlayHintHandler() {
+ return this.inlayHintHandler != null;
+ }
public synchronized void onCodeLensResolve(CodeLensResolveHandler h) {
Assert.isNull("A code lens resolve handler is already set, multiple handlers not supported yet", codeLensResolveHandler);
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 431c20678..232e27819 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
@@ -29,8 +29,10 @@ import org.springframework.ide.vscode.boot.java.links.JavaElementLocationProvide
import org.springframework.ide.vscode.boot.java.links.SourceLinks;
import org.springframework.ide.vscode.boot.java.livehover.v2.SpringProcessLiveDataProvider;
import org.springframework.ide.vscode.boot.java.utils.CompilationUnitCache;
+import org.springframework.ide.vscode.boot.maven.PomLanguageServerComponents;
import org.springframework.ide.vscode.boot.metadata.ProjectBasedPropertyIndexProvider;
import org.springframework.ide.vscode.boot.properties.BootPropertiesLanguageServerComponents;
+import org.springframework.ide.vscode.boot.validation.generations.SpringProjectsProvider;
import org.springframework.ide.vscode.boot.xml.SpringXMLLanguageServerComponents;
import org.springframework.ide.vscode.commons.java.IJavaProject;
import org.springframework.ide.vscode.commons.languageserver.completion.CompositeCompletionEngine;
@@ -119,7 +121,8 @@ public class BootLanguageServerInitializer implements InitializingBean {
new BootPropertiesLanguageServerComponents(server, params, javaElementLocationProvider, parser, yamlStructureProvider, yamlAssistContextProvider, sourceLinks),
new BootJavaLanguageServerComponents(appContext),
new SpringXMLLanguageServerComponents(server, springIndexer, params, config),
- new SpringFactoriesLanguageServerComponents(projectFinder, springIndexer, config)
+ new SpringFactoriesLanguageServerComponents(projectFinder, springIndexer, config),
+ new PomLanguageServerComponents(server, projectFinder, params.projectObserver, appContext.getBean(SpringProjectsProvider.class))
);
for (LanguageServerComponents c : componentsList) {
@@ -144,7 +147,11 @@ public class BootLanguageServerInitializer implements InitializingBean {
components.getCodeActionProvider().ifPresent(documents::onCodeAction);
+ components.getCodeLensHandler().ifPresent(documents::onCodeLens);
+
components.getDocumentSymbolProvider().ifPresent(documents::onDocumentSymbol);
+
+ components.getInlayHintHandler().ifPresent(documents::onInlayHint);
startListeningToPerformReconcile();
@@ -164,8 +171,7 @@ public class BootLanguageServerInitializer implements InitializingBean {
documents.publishDiagnostics(d.getId(), Collections.emptyList());
}
});
-
-
+
}
private void startListeningToPerformReconcile() {
diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaLanguageServerComponents.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaLanguageServerComponents.java
index b13a24927..783b6932f 100644
--- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaLanguageServerComponents.java
+++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaLanguageServerComponents.java
@@ -174,7 +174,6 @@ public class BootJavaLanguageServerComponents implements LanguageServerComponent
sourceLinks);
codeLensHandler = createCodeLensEngine(indexer);
- documents.onCodeLens(codeLensHandler);
highlightsEngine = createDocumentHighlightEngine(indexer);
documents.onDocumentHighlight(highlightsEngine);
@@ -218,8 +217,8 @@ public class BootJavaLanguageServerComponents implements LanguageServerComponent
return hoverProvider;
}
- public CodeLensHandler getCodeLensHandler() {
- return codeLensHandler;
+ public Optional getCodeLensHandler() {
+ return Optional.of(codeLensHandler);
}
public DocumentHighlightHandler getDocumentHighlightHandler() {
diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/maven/PomInlayHintHandler.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/maven/PomInlayHintHandler.java
new file mode 100644
index 000000000..1fcef0135
--- /dev/null
+++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/maven/PomInlayHintHandler.java
@@ -0,0 +1,170 @@
+/*******************************************************************************
+ * Copyright (c) 2024 Broadcom, 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:
+ * Broadcom, Inc. - initial API and implementation
+ *******************************************************************************/
+package org.springframework.ide.vscode.boot.maven;
+
+import java.net.URI;
+import java.nio.file.Paths;
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+
+import org.eclipse.lemminx.dom.DOMDocument;
+import org.eclipse.lemminx.dom.DOMElement;
+import org.eclipse.lemminx.dom.DOMParser;
+import org.eclipse.lsp4j.Command;
+import org.eclipse.lsp4j.InlayHint;
+import org.eclipse.lsp4j.InlayHintKind;
+import org.eclipse.lsp4j.InlayHintLabelPart;
+import org.eclipse.lsp4j.InlayHintParams;
+import org.eclipse.lsp4j.jsonrpc.CancelChecker;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.ide.vscode.boot.validation.generations.GenerationsValidator;
+import org.springframework.ide.vscode.boot.validation.generations.SpringProjectsProvider;
+import org.springframework.ide.vscode.boot.validation.generations.VersionValidationUtils;
+import org.springframework.ide.vscode.boot.validation.generations.json.Generation;
+import org.springframework.ide.vscode.boot.validation.generations.json.ResolvedSpringProject;
+import org.springframework.ide.vscode.commons.Version;
+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.java.ProjectObserver;
+import org.springframework.ide.vscode.commons.languageserver.util.InlayHintHandler;
+import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
+import org.springframework.ide.vscode.commons.util.text.TextDocument;
+
+public class PomInlayHintHandler implements InlayHintHandler {
+
+ private static final String POM_XML = "pom.xml";
+
+ private static final Logger log = LoggerFactory.getLogger(PomInlayHintHandler.class);
+
+ final private SimpleLanguageServer server;
+ final private JavaProjectFinder projectFinder;
+ final private SpringProjectsProvider generationsProvider;
+
+ public PomInlayHintHandler(SimpleLanguageServer server, JavaProjectFinder projectFinder, ProjectObserver projectObserver, SpringProjectsProvider generationsProvider) {
+ this.server = server;
+ this.projectFinder = projectFinder;
+ this.generationsProvider = generationsProvider;
+
+ projectObserver.addListener(new ProjectObserver.Listener() {
+
+ private void refreshOpenDocs(IJavaProject project) {
+ if (project.getProjectBuild() != null && project.getProjectBuild().getBuildFile() != null ) {
+ URI projectBuildFileUri = project.getProjectBuild().getBuildFile();
+ if (projectBuildFileUri.toString().endsWith(POM_XML)) {
+ server.getTextDocumentService().getAll().stream()
+ .map(doc -> URI.create(doc.getId().getUri()))
+ .filter(projectBuildFileUri::equals)
+ .findFirst().ifPresent(docPath -> {
+ log.info("Refresh inlays for: " + docPath);
+ server.getClient().refreshInlayHints();
+ });
+ }
+ }
+ }
+
+ @Override
+ public void deleted(IJavaProject project) {
+ refreshOpenDocs(project);
+ }
+
+ @Override
+ public void created(IJavaProject project) {
+ refreshOpenDocs(project);
+ }
+
+ @Override
+ public void changed(IJavaProject project) {
+ refreshOpenDocs(project);
+ }
+ });
+
+ }
+
+ @Override
+ public List handle(CancelChecker token, InlayHintParams params) {
+ URI uri = URI.create(params.getTextDocument().getUri());
+ if ("file".equals(uri.getScheme()) && POM_XML.equals(Paths.get(uri).getFileName().toString())) {
+
+ log.info("INLAY for " + uri);
+
+ Optional projectOpt = projectFinder.find(params.getTextDocument());
+ if (projectOpt.isPresent()) {
+
+ URI buildFileUri = projectOpt.get().getProjectBuild().getBuildFile();
+
+ if (buildFileUri.equals(uri) && SpringProjectUtil.isBootProject(projectOpt.get())) {
+ Version currentVersion = SpringProjectUtil.getSpringBootVersion(projectOpt.get());
+ if (currentVersion != null) {
+ try {
+ ResolvedSpringProject genProject = generationsProvider.getProject(SpringProjectUtil.SPRING_BOOT);
+ if (genProject != null) {
+ Generation generation = GenerationsValidator.getGenerationForJavaProject(projectOpt.get(), genProject);
+ if (generation != null && VersionValidationUtils.isOssValid(generation)) {
+ TextDocument doc = server.getTextDocumentService().getLatestSnapshot(params.getTextDocument().getUri());
+
+ if (doc != null) {
+ String content = doc.get();
+
+ if (!content.isEmpty()) {
+ // if doc is not empty, dive into the details and provide more sophisticated content assist proposals
+ DOMParser parser = DOMParser.getInstance();
+ DOMDocument dom = parser.parse(content, "", null);
+
+ DOMElement project = dom.getDocumentElement();
+ if (project != null && "project".equals(project.getTagName())) {
+ for (int j = 0; j < project.getChildNodes().getLength(); j++) {
+ var child = project.getChildNodes().item(j);
+ if ("dependencies".equals(child.getNodeName()) && child instanceof DOMElement dependencies) {
+ try {
+ Command command = new Command();
+ command.setTitle("Add Spring Boot Starters");
+ command.setCommand("spring.initializr.addStarters");
+
+ InlayHintLabelPart label = new InlayHintLabelPart("Add Spring Boot Starters...");
+ label.setCommand(command);
+
+ InlayHint hint = new InlayHint();
+ hint.setPosition(doc.toPosition(dependencies.getStartTagCloseOffset() + 1));
+ hint.setKind(InlayHintKind.Parameter);
+ hint.setPaddingLeft(true);
+ hint.setLabel(List.of(label));
+
+ log.info("Sending inlay for " + uri);
+ return List.of(hint);
+ } catch (Exception e) {
+ log.error("", e);
+ break;
+ }
+ }
+ }
+ }
+
+
+ }
+ }
+ }
+ }
+ } catch (Exception e) {
+ log.error("", e);
+ }
+
+ }
+ }
+ }
+
+ }
+ return Collections.emptyList();
+ }
+
+}
diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/maven/PomLanguageServerComponents.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/maven/PomLanguageServerComponents.java
new file mode 100644
index 000000000..2c317a6fe
--- /dev/null
+++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/maven/PomLanguageServerComponents.java
@@ -0,0 +1,42 @@
+/*******************************************************************************
+ * Copyright (c) 2024 Broadcom, 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:
+ * Broadcom, Inc. - initial API and implementation
+ *******************************************************************************/
+package org.springframework.ide.vscode.boot.maven;
+
+import java.util.Optional;
+import java.util.Set;
+
+import org.springframework.ide.vscode.boot.validation.generations.SpringProjectsProvider;
+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.java.ProjectObserver;
+import org.springframework.ide.vscode.commons.languageserver.util.InlayHintHandler;
+import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
+import org.springframework.ide.vscode.commons.util.text.LanguageId;
+
+public class PomLanguageServerComponents implements LanguageServerComponents{
+
+ private PomInlayHintHandler inlayHintHandler;
+
+ public PomLanguageServerComponents(SimpleLanguageServer server, JavaProjectFinder projectFinder, ProjectObserver projectObserver, SpringProjectsProvider generationsProvider) {
+ this.inlayHintHandler = new PomInlayHintHandler(server, projectFinder, projectObserver, generationsProvider);
+ }
+
+ @Override
+ public Set getInterestingLanguages() {
+ return Set.of(LanguageId.XML);
+ }
+
+ @Override
+ public Optional getInlayHintHandler() {
+ return Optional.of(inlayHintHandler);
+ }
+
+}
diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/GenerationsValidator.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/GenerationsValidator.java
index e2b291940..dcafe77ab 100644
--- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/GenerationsValidator.java
+++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/GenerationsValidator.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2023 VMware, Inc.
+ * Copyright (c) 2023, 2024 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
@@ -33,7 +33,7 @@ public class GenerationsValidator extends AbstractDiagnosticValidator {
this.provider = provider;
}
- private static Generation getGenerationForJavaProject(IJavaProject javaProject, ResolvedSpringProject springProject)
+ public static Generation getGenerationForJavaProject(IJavaProject javaProject, ResolvedSpringProject springProject)
throws Exception {
List genList = springProject.getGenerations();
Version javaProjectVersion = SpringProjectUtil.getDependencyVersion(javaProject, springProject.getSlug());