Skip to content

Commit 38284da

Browse files
committed
Eclipse and VSCode command to execute maven goals
1 parent d1ba9ed commit 38284da

File tree

14 files changed

+248
-58
lines changed

14 files changed

+248
-58
lines changed

eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/META-INF/MANIFEST.MF

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,9 @@ Require-Bundle: org.eclipse.jdt.launching;bundle-version="3.8.0",
2424
org.springsource.ide.eclipse.commons.core,
2525
org.eclipse.e4.ui.css.swt.theme,
2626
org.eclipse.swt,
27-
com.google.gson
27+
com.google.gson,
28+
org.eclipse.m2e.launching,
29+
org.eclipse.m2e.core
2830
Bundle-RequiredExecutionEnvironment: JavaSE-21
2931
Bundle-ActivationPolicy: lazy
3032
Export-Package: org.springframework.tooling.ls.eclipse.commons,

eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/plugin.xml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,10 @@
107107
class="org.springframework.tooling.ls.eclipse.commons.commands.OpenJarEntryInEditor"
108108
commandId="org.springframework.tooling.ls.eclipse.commons.commands.OpenJarEntryInEditor">
109109
</handler>
110+
<handler
111+
class="org.springframework.tooling.ls.eclipse.commons.commands.ExecuteMavenGoalHandler"
112+
commandId="maven.goal.custom">
113+
</handler>
110114
</extension>
111115
<extension
112116
point="org.eclipse.ui.commands">
@@ -195,6 +199,22 @@
195199
optional="false">
196200
</commandParameter>
197201
</command>
202+
<command
203+
id="maven.goal.custom"
204+
name="Explain with AI">
205+
<commandParameter
206+
id="org.eclipse.lsp4e.path.param"
207+
name="Resource Path (unnecessary, only to make lsp4e happy)"
208+
optional="true"
209+
typeId="org.eclipse.lsp4e.pathParameterType">
210+
</commandParameter>
211+
<commandParameter
212+
id="org.eclipse.lsp4e.command.param"
213+
name="Command id (unnecessary, only to make lsp4e happy)"
214+
optional="true"
215+
typeId="org.eclipse.lsp4e.commandParameterType">
216+
</commandParameter>
217+
</command>
198218
</extension>
199219
<extension
200220
point="org.eclipse.e4.ui.css.swt.theme">
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2025 Broadcom, Inc.
3+
* All rights reserved. This program and the accompanying materials
4+
* are made available under the terms of the Eclipse Public License v1.0
5+
* which accompanies this distribution, and is available at
6+
* https://www.eclipse.org/legal/epl-v10.html
7+
*
8+
* Contributors:
9+
* Broadcom, Inc. - initial API and implementation
10+
*******************************************************************************/
11+
package org.springframework.tooling.ls.eclipse.commons.commands;
12+
13+
import java.nio.file.Paths;
14+
15+
import org.eclipse.core.commands.AbstractHandler;
16+
import org.eclipse.core.commands.ExecutionEvent;
17+
import org.eclipse.core.commands.ExecutionException;
18+
import org.eclipse.core.resources.IContainer;
19+
import org.eclipse.core.resources.IFile;
20+
import org.eclipse.core.resources.IResource;
21+
import org.eclipse.core.runtime.CoreException;
22+
import org.eclipse.core.runtime.IPath;
23+
import org.eclipse.core.runtime.NullProgressMonitor;
24+
import org.eclipse.debug.core.DebugPlugin;
25+
import org.eclipse.debug.core.ILaunchConfiguration;
26+
import org.eclipse.debug.core.ILaunchConfigurationType;
27+
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
28+
import org.eclipse.debug.core.ILaunchManager;
29+
import org.eclipse.debug.ui.DebugUITools;
30+
import org.eclipse.debug.ui.IDebugUIConstants;
31+
import org.eclipse.debug.ui.RefreshTab;
32+
import org.eclipse.lsp4e.LSPEclipseUtils;
33+
import org.eclipse.lsp4e.command.LSPCommandHandler;
34+
import org.eclipse.lsp4j.Command;
35+
import org.eclipse.m2e.actions.MavenLaunchConstants;
36+
37+
import org.eclipse.m2e.core.MavenPlugin;
38+
import org.eclipse.m2e.core.internal.IMavenConstants;
39+
import org.eclipse.m2e.core.project.IMavenProjectFacade;
40+
import org.eclipse.m2e.core.project.IMavenProjectRegistry;
41+
import org.eclipse.m2e.core.project.IProjectConfiguration;
42+
43+
import com.google.gson.Gson;
44+
45+
@SuppressWarnings("restriction")
46+
public class ExecuteMavenGoalHandler extends AbstractHandler {
47+
48+
@Override
49+
public Object execute(ExecutionEvent event) throws ExecutionException {
50+
Command cmd = new Gson().fromJson(event.getParameter(LSPCommandHandler.LSP_COMMAND_PARAMETER_ID), Command.class);
51+
if (cmd != null) {
52+
try {
53+
String pomPath = (String) cmd.getArguments().get(0);
54+
String goal = (String) cmd.getArguments().get(1);
55+
System.out.println("Project: '%s', goal: '%s'".formatted(pomPath, goal));
56+
57+
IResource pomFile = LSPEclipseUtils.findResourceFor(Paths.get(pomPath).toUri());
58+
ILaunchConfiguration launchConfig = createLaunchConfiguration(pomFile.getParent(), goal);
59+
60+
DebugUITools.launch(launchConfig, ILaunchManager.RUN_MODE);
61+
} catch (Exception e) {
62+
throw new ExecutionException("Failed to execute Maven Goal command", e);
63+
}
64+
}
65+
throw new ExecutionException("Maven Goal Execution command is invalid");
66+
}
67+
68+
private ILaunchConfiguration createLaunchConfiguration(IContainer basedir, String goal) throws CoreException {
69+
ILaunchManager launchManager = DebugPlugin.getDefault().getLaunchManager();
70+
ILaunchConfigurationType launchConfigurationType = launchManager
71+
.getLaunchConfigurationType(MavenLaunchConstants.LAUNCH_CONFIGURATION_TYPE_ID);
72+
73+
String safeConfigName = launchManager.generateLaunchConfigurationName("Goal `%s`".formatted(goal));
74+
75+
ILaunchConfigurationWorkingCopy workingCopy = launchConfigurationType.newInstance(null, safeConfigName);
76+
workingCopy.setAttribute(MavenLaunchConstants.ATTR_POM_DIR, basedir.getLocation().toOSString());
77+
workingCopy.setAttribute(MavenLaunchConstants.ATTR_GOALS, goal);
78+
workingCopy.setAttribute(IDebugUIConstants.ATTR_PRIVATE, true);
79+
workingCopy.setAttribute(RefreshTab.ATTR_REFRESH_SCOPE, "${project}"); //$NON-NLS-1$
80+
workingCopy.setAttribute(RefreshTab.ATTR_REFRESH_RECURSIVE, true);
81+
82+
setProjectConfiguration(workingCopy, basedir);
83+
84+
return workingCopy;
85+
}
86+
87+
private void setProjectConfiguration(ILaunchConfigurationWorkingCopy workingCopy, IContainer basedir) {
88+
IMavenProjectRegistry projectManager = MavenPlugin.getMavenProjectRegistry();
89+
IFile pomFile = basedir.getFile(IPath.fromOSString(IMavenConstants.POM_FILE_NAME));
90+
IMavenProjectFacade projectFacade = projectManager.create(pomFile, false, new NullProgressMonitor());
91+
if (projectFacade != null) {
92+
IProjectConfiguration configuration = projectFacade.getConfiguration();
93+
94+
String selectedProfiles = configuration.getSelectedProfiles();
95+
if (selectedProfiles != null && selectedProfiles.length() > 0) {
96+
workingCopy.setAttribute(MavenLaunchConstants.ATTR_PROFILES, selectedProfiles);
97+
}
98+
}
99+
}
100+
101+
}

headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons/src/org/springframework/tooling/jdt/ls/commons/classpath/ClasspathUtil.java

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@
1515

1616
import java.io.File;
1717
import java.net.MalformedURLException;
18+
import java.net.URI;
1819
import java.nio.file.Path;
20+
import java.nio.file.Paths;
1921
import java.util.ArrayList;
2022
import java.util.Collections;
2123
import java.util.HashSet;
@@ -244,14 +246,14 @@ public static ProjectBuild createProjectBuild(IJavaProject jp, Logger logger) {
244246
// if (facade != null) {
245247
// return ProjectBuild.createMavenBuild(facade.getPom().getLocationURI().toASCIIString());
246248
// } else {
247-
return ProjectBuild.createMavenBuild(jp.getProject().getFile("pom.xml").getLocationURI().toASCIIString());
249+
return ProjectBuild.createMavenBuild(fromIFile(jp.getProject().getFile("pom.xml")).toASCIIString());
248250
// }
249251
} else if (GradleProjectNature.isPresentOn(jp.getProject())) {
250252
IFile g = jp.getProject().getFile("build.gradle");
251253
if (!g.exists()) {
252254
g = jp.getProject().getFile("build.gradle.kts");
253255
}
254-
return ProjectBuild.createGradleBuild(g.exists() ? g.getLocationURI().toASCIIString() : null);
256+
return ProjectBuild.createGradleBuild(g.exists() ? fromIFile(g).toASCIIString() : null);
255257
} else {
256258
try {
257259
for (IClasspathEntry e : jp.getRawClasspath()) {
@@ -271,20 +273,24 @@ public static ProjectBuild createProjectBuild(IJavaProject jp, Logger logger) {
271273
if (likelyMaven) {
272274
IFile f = jp.getProject().getFile("pom.xml");
273275
if (f.exists()) {
274-
return ProjectBuild.createMavenBuild(f.getLocationURI().toASCIIString());
276+
return ProjectBuild.createMavenBuild(fromIFile(f).toASCIIString());
275277
}
276278
} else if (likelyGradle) {
277279
IFile g = jp.getProject().getFile("build.gradle");
278280
if (!g.exists()) {
279281
g = jp.getProject().getFile("build.gradle.kts");
280282
}
281283
if (g.exists()) {
282-
return ProjectBuild.createGradleBuild(g.getLocationURI().toASCIIString());
284+
return ProjectBuild.createGradleBuild(fromIFile(g).toASCIIString());
283285
}
284286
}
285287
// } catch (JavaModelException e) {
286288
// // ignore
287289
// }
288290
return new ProjectBuild(null, null);
289291
}
292+
293+
private static URI fromIFile(IFile f) {
294+
return Paths.get(f.getLocationURI()).toUri();
295+
}
290296
}

headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootLanguageServerBootApp.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
import org.springframework.ide.vscode.boot.index.cache.IndexCache;
4747
import org.springframework.ide.vscode.boot.index.cache.IndexCacheOnDiscDeltaBased;
4848
import org.springframework.ide.vscode.boot.index.cache.IndexCacheVoid;
49+
import org.springframework.ide.vscode.boot.java.BuildCommandProvider;
4950
import org.springframework.ide.vscode.boot.java.JavaDefinitionHandler;
5051
import org.springframework.ide.vscode.boot.java.beans.DependsOnDefinitionProvider;
5152
import org.springframework.ide.vscode.boot.java.beans.NamedDefinitionProvider;
@@ -436,8 +437,8 @@ ModulithService modulithService(SimpleLanguageServer server, JavaProjectFinder p
436437
}
437438

438439
@Bean
439-
DataRepositoryAotMetadataService dataAotMetadataService(SimpleLanguageServer server, FileObserver fileObserver, JavaProjectFinder projectFinder) {
440-
return new DataRepositoryAotMetadataService(server, fileObserver, projectFinder);
440+
DataRepositoryAotMetadataService dataAotMetadataService(FileObserver fileObserver, JavaProjectFinder projectFinder, BuildCommandProvider buildCmds) {
441+
return new DataRepositoryAotMetadataService(fileObserver, projectFinder, buildCmds);
441442
}
442443

443444
@Bean

headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/JdtConfig.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import org.springframework.context.annotation.Conditional;
1919
import org.springframework.context.annotation.Configuration;
2020
import org.springframework.ide.vscode.boot.index.SpringMetamodelIndex;
21+
import org.springframework.ide.vscode.boot.java.VSCodeBuildCommandProvider;
2122
import org.springframework.ide.vscode.boot.java.codeaction.JdtAstCodeActionProvider;
2223
import org.springframework.ide.vscode.boot.java.codeaction.JdtCodeActionHandler;
2324
import org.springframework.ide.vscode.boot.java.cron.CronExpressionsInlayHintsProvider;
@@ -228,4 +229,9 @@ public class JdtConfig {
228229
@Bean JdtCodeActionHandler jdtCodeActionHandler(CompilationUnitCache cuCache, Collection<JdtAstCodeActionProvider> providers) {
229230
return new JdtCodeActionHandler(cuCache, providers);
230231
}
232+
233+
@Bean VSCodeBuildCommandProvider vsCodeBuildCommandProvider() {
234+
return new VSCodeBuildCommandProvider();
235+
}
236+
231237
}

headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaLanguageServerComponents.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -327,7 +327,7 @@ protected BootJavaCodeLensEngine createCodeLensEngine(SpringMetamodelIndex sprin
327327
codeLensProvider.add(new WebfluxHandlerCodeLensProvider(springIndex));
328328
codeLensProvider.add(new CopilotCodeLensProvider(projectFinder, server, spelSemanticTokens));
329329
codeLensProvider.add(new RouterFunctionCodeLensProvider());
330-
codeLensProvider.add(new DataRepositoryAotMetadataCodeLensProvider(projectFinder, repositoryAotMetadataService, refactorings, config));
330+
codeLensProvider.add(new DataRepositoryAotMetadataCodeLensProvider(server, projectFinder, repositoryAotMetadataService, refactorings, config));
331331
codeLensProvider.add(new WebConfigCodeLensProvider(projectFinder, springIndex, config));
332332

333333
return new BootJavaCodeLensEngine(this, codeLensProvider);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2025 Broadcom, Inc.
3+
* All rights reserved. This program and the accompanying materials
4+
* are made available under the terms of the Eclipse Public License v1.0
5+
* which accompanies this distribution, and is available at
6+
* https://www.eclipse.org/legal/epl-v10.html
7+
*
8+
* Contributors:
9+
* Broadcom, Inc. - initial API and implementation
10+
*******************************************************************************/
11+
package org.springframework.ide.vscode.boot.java;
12+
13+
import org.eclipse.lsp4j.Command;
14+
import org.springframework.ide.vscode.commons.java.IJavaProject;
15+
16+
public interface BuildCommandProvider {
17+
18+
Command executeMavenGoal(IJavaProject project, String goal);
19+
20+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2025 Broadcom, Inc.
3+
* All rights reserved. This program and the accompanying materials
4+
* are made available under the terms of the Eclipse Public License v1.0
5+
* which accompanies this distribution, and is available at
6+
* https://www.eclipse.org/legal/epl-v10.html
7+
*
8+
* Contributors:
9+
* Broadcom, Inc. - initial API and implementation
10+
*******************************************************************************/
11+
package org.springframework.ide.vscode.boot.java;
12+
13+
import java.nio.file.Paths;
14+
import java.util.List;
15+
16+
import org.eclipse.lsp4j.Command;
17+
import org.springframework.ide.vscode.commons.java.IJavaProject;
18+
19+
public class VSCodeBuildCommandProvider implements BuildCommandProvider {
20+
21+
@Override
22+
public Command executeMavenGoal(IJavaProject project, String goal) {
23+
Command cmd = new Command();
24+
cmd.setCommand("maven.goal.custom");
25+
cmd.setTitle("Execute Maven Goal");
26+
cmd.setArguments(List.of(Paths.get(project.getProjectBuild().getBuildFile()).toFile().toString(), goal));
27+
return cmd;
28+
}
29+
30+
}

headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/data/DataRepositoryAotMetadataCodeLensProvider.java

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
import org.springframework.ide.vscode.commons.java.IJavaProject;
4141
import org.springframework.ide.vscode.commons.java.SpringProjectUtil;
4242
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
43+
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
4344
import org.springframework.ide.vscode.commons.rewrite.config.RecipeScope;
4445
import org.springframework.ide.vscode.commons.rewrite.java.AddAnnotationOverMethod;
4546
import org.springframework.ide.vscode.commons.rewrite.java.FixDescriptor;
@@ -60,12 +61,22 @@ public class DataRepositoryAotMetadataCodeLensProvider implements CodeLensProvid
6061
private final RewriteRefactorings refactorings;
6162
private final BootJavaConfig config;
6263

63-
public DataRepositoryAotMetadataCodeLensProvider(JavaProjectFinder projectFinder, DataRepositoryAotMetadataService repositoryMetadataService,
64+
public DataRepositoryAotMetadataCodeLensProvider(SimpleLanguageServer server, JavaProjectFinder projectFinder, DataRepositoryAotMetadataService repositoryMetadataService,
6465
RewriteRefactorings refactorings, BootJavaConfig config) {
6566
this.projectFinder = projectFinder;
6667
this.repositoryMetadataService = repositoryMetadataService;
6768
this.refactorings = refactorings;
6869
this.config = config;
70+
71+
listenForAotMetadataChanges(server);
72+
}
73+
74+
private void listenForAotMetadataChanges(SimpleLanguageServer server) {
75+
repositoryMetadataService.addListener(files -> {
76+
if (config.isEnabledCodeLensOverDataQueryMethods()) {
77+
server.getClient().refreshCodeLenses();
78+
}
79+
});
6980
}
7081

7182
@Override
@@ -166,7 +177,7 @@ private List<CodeLens> createCodeLenses(IJavaProject project, MethodDeclaration
166177
)));
167178
codeLenses.add(new CodeLens(range, impl, null));
168179

169-
if (!isQueryAnnotated && methodMetadata != null) {
180+
if (!isQueryAnnotated) {
170181
String queryStatement = methodMetadata.getQueryStatement();
171182
if (queryStatement != null) {
172183
Command queryTitle = new Command();
@@ -176,7 +187,9 @@ private List<CodeLens> createCodeLenses(IJavaProject project, MethodDeclaration
176187
}
177188
}));
178189

179-
Command refreshCmd = new Command("Refresh", DataRepositoryAotMetadataService.CMD_REFESH_METADATA, List.of(project.getElementName()));
190+
String refreshCmdTitle = codeLenses.isEmpty() ? "Show AOT-generated Implementation, Query, etc..." : "Refresh";
191+
Command refreshCmd = repositoryMetadataService.regenerateMetadataCommand(project);
192+
refreshCmd.setTitle(refreshCmdTitle);
180193
codeLenses.add(new CodeLens(range, refreshCmd, null));
181194

182195
}

0 commit comments

Comments
 (0)