Skip to content

Commit 1fa315f

Browse files
committed
Command for Data Query method CodeLens and CodeAction
1 parent 8bebac5 commit 1fa315f

File tree

18 files changed

+799
-220
lines changed

18 files changed

+799
-220
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
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.commons.languageserver.reconcile;
12+
13+
import java.util.Collection;
14+
15+
public class BasicCollector<T> implements ICollector<T> {
16+
17+
private final Collection<T> collection;
18+
19+
public BasicCollector(Collection<T> collection) {
20+
this.collection = collection;
21+
}
22+
23+
@Override
24+
public void beginCollecting() {
25+
}
26+
27+
@Override
28+
public void endCollecting() {
29+
}
30+
31+
@Override
32+
public void accept(T t) {
33+
collection.add(t);
34+
}
35+
36+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
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.commons.languageserver.reconcile;
12+
13+
public interface ICollector<T> {
14+
15+
void beginCollecting();
16+
void endCollecting();
17+
void accept(T t);
18+
19+
/**
20+
* Optional for both implementors and callers.
21+
* <p/>
22+
* This method optionally allows callers to do partial collection between the
23+
* start and end collecting, and can be called numerous times. The caller is
24+
* responsible to decide when and how often these checkpoints are invoked during
25+
* a collecting session.
26+
* <p/>
27+
* For implementors, this optional support handles cases where problems need to be processed in
28+
* intermediate phases between the start and end collecting stages, and if
29+
* implemented, should support multiple checkpoint invocations.
30+
*/
31+
default void checkPointCollecting() {
32+
33+
}
34+
35+
}

headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/reconcile/IProblemCollector.java

Lines changed: 2 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2014-2017 Pivotal, Inc.
2+
* Copyright (c) 2014, 2025 Pivotal, Inc.
33
* All rights reserved. This program and the accompanying materials
44
* are made available under the terms of the Eclipse Public License v1.0
55
* which accompanies this distribution, and is available at
@@ -10,27 +10,7 @@
1010
*******************************************************************************/
1111
package org.springframework.ide.vscode.commons.languageserver.reconcile;
1212

13-
public interface IProblemCollector {
14-
15-
void beginCollecting();
16-
void endCollecting();
17-
void accept(ReconcileProblem problem);
18-
19-
/**
20-
* Optional for both implementors and callers.
21-
* <p/>
22-
* This method optionally allows callers to do partial collection between the
23-
* start and end collecting, and can be called numerous times. The caller is
24-
* responsible to decide when and how often these checkpoints are invoked during
25-
* a collecting session.
26-
* <p/>
27-
* For implementors, this optional support handles cases where problems need to be processed in
28-
* intermediate phases between the start and end collecting stages, and if
29-
* implemented, should support multiple checkpoint invocations.
30-
*/
31-
default void checkPointCollecting() {
32-
33-
}
13+
public interface IProblemCollector extends ICollector<ReconcileProblem> {
3414

3515
/**
3616
* Problem collector that simply ignores/discards anything passed to it.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
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.commons.rewrite.java;
12+
13+
import java.util.ArrayList;
14+
import java.util.List;
15+
import java.util.Optional;
16+
17+
import org.jspecify.annotations.Nullable;
18+
import org.openrewrite.ExecutionContext;
19+
import org.openrewrite.NlsRewrite.Description;
20+
import org.openrewrite.NlsRewrite.DisplayName;
21+
import org.openrewrite.Option;
22+
import org.openrewrite.Preconditions;
23+
import org.openrewrite.Recipe;
24+
import org.openrewrite.Tree;
25+
import org.openrewrite.TreeVisitor;
26+
import org.openrewrite.java.AddOrUpdateAnnotationAttribute;
27+
import org.openrewrite.java.JavaIsoVisitor;
28+
import org.openrewrite.java.MethodMatcher;
29+
import org.openrewrite.java.search.DeclaresMethod;
30+
import org.openrewrite.java.tree.J;
31+
import org.openrewrite.java.tree.J.Annotation;
32+
import org.openrewrite.java.tree.J.MethodDeclaration;
33+
import org.openrewrite.java.tree.JavaType;
34+
import org.openrewrite.java.tree.Space;
35+
import org.openrewrite.java.tree.TypeUtils;
36+
import org.openrewrite.marker.Markers;
37+
38+
import com.fasterxml.jackson.annotation.JsonCreator;
39+
import com.fasterxml.jackson.annotation.JsonProperty;
40+
41+
public class AddAnnotationOverMethod extends Recipe {
42+
43+
public record Attribute(String name, String value) {}
44+
45+
@Override
46+
public @DisplayName String getDisplayName() {
47+
return "Add annotation over method";
48+
}
49+
50+
@Override
51+
public @Description String getDescription() {
52+
return "Add annotation over method.";
53+
}
54+
55+
@Option(description = "Method pattern", example = "com.example.Person setAge(int)")
56+
private String method;
57+
58+
@Option(description = "Annotation type")
59+
private String annotationType;
60+
61+
@Nullable
62+
@Option(description = "Annotation attributes", required = false)
63+
private List<Attribute> attributes;
64+
65+
@JsonCreator
66+
public AddAnnotationOverMethod(
67+
@JsonProperty("method") String method,
68+
@JsonProperty("annotationType") String annotationType,
69+
@JsonProperty("attributes") @Nullable List<Attribute> attributes) {
70+
this.method = method;
71+
this.annotationType = annotationType;
72+
this.attributes = attributes;
73+
}
74+
75+
@Override
76+
public TreeVisitor<?, ExecutionContext> getVisitor() {
77+
final MethodMatcher matcher = new MethodMatcher(method);
78+
return Preconditions.check(new DeclaresMethod<>(matcher), new JavaIsoVisitor<>() {
79+
@Override
80+
public MethodDeclaration visitMethodDeclaration(MethodDeclaration method, ExecutionContext ctx) {
81+
MethodDeclaration m = super.visitMethodDeclaration(method, ctx);
82+
if (matcher.matches(m.getMethodType())) {
83+
Optional<Annotation> optAnnotation = m.getLeadingAnnotations().stream().filter(a -> TypeUtils.isOfClassType(a.getType(), annotationType)).findFirst();
84+
if (optAnnotation.isEmpty()) {
85+
List<J.Annotation> annotations = new ArrayList<>(m.getLeadingAnnotations());
86+
JavaType.ShallowClass at = JavaType.ShallowClass.build(annotationType);
87+
J.Annotation annotation = new J.Annotation(
88+
Tree.randomId(),
89+
Space.EMPTY,
90+
Markers.EMPTY,
91+
new J.Identifier(Tree.randomId(), Space.EMPTY, Markers.EMPTY, List.of(), at.getClassName(), at, null),
92+
null);
93+
annotations.add(autoFormat(annotation, ctx));
94+
m = m.withLeadingAnnotations(annotations);
95+
m = autoFormat(m, m.getName(), ctx, getCursor().getParent());
96+
maybeAddImport(annotationType);
97+
optAnnotation = Optional.of(annotation);
98+
}
99+
if (attributes != null) {
100+
for (Attribute attr : attributes) {
101+
m = (MethodDeclaration) new AddOrUpdateAnnotationAttribute(annotationType, attr.name(),
102+
attr.value(), true, false).getVisitor().visit(m, ctx, getCursor().getParent());
103+
}
104+
}
105+
106+
}
107+
return m;
108+
}
109+
});
110+
}
111+
112+
113+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
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.commons.rewrite.java;
12+
13+
import java.util.List;
14+
15+
import org.junit.jupiter.api.Test;
16+
import org.openrewrite.java.Assertions;
17+
import org.openrewrite.test.RewriteTest;
18+
19+
public class AddAnnotationOverMethodTest implements RewriteTest {
20+
21+
@Test
22+
void addAnnotationToMethod() {
23+
rewriteRun(
24+
spec -> spec.recipe(new AddAnnotationOverMethod("demo.A foo()", "java.lang.Deprecated", List.of(new AddAnnotationOverMethod.Attribute("value", "\"Expected Text\"")))),
25+
Assertions.java(
26+
"""
27+
package demo;
28+
interface A {
29+
void foo();
30+
}
31+
""",
32+
"""
33+
package demo;
34+
interface A {
35+
@Deprecated("Expected Text")
36+
void foo();
37+
}
38+
"""
39+
)
40+
);
41+
}
42+
43+
@Test
44+
void addAnnotationToMethodWithAnnotation() {
45+
rewriteRun(
46+
spec -> spec.recipe(new AddAnnotationOverMethod("demo.A foo()", "java.lang.Deprecated", List.of(new AddAnnotationOverMethod.Attribute("value", "\"Expected Text\"")))),
47+
Assertions.java(
48+
"""
49+
package demo;
50+
class A {
51+
@SuppressWarnings("null")
52+
void foo() {
53+
System.out.println("foo");
54+
}
55+
}
56+
""",
57+
"""
58+
package demo;
59+
class A {
60+
@SuppressWarnings("null")
61+
@Deprecated("Expected Text")
62+
void foo() {
63+
System.out.println("foo");
64+
}
65+
}
66+
"""
67+
)
68+
);
69+
}
70+
71+
}

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,16 @@
1010
*******************************************************************************/
1111
package org.springframework.ide.vscode.boot.app;
1212

13+
import java.util.Collection;
1314
import java.util.Optional;
1415

1516
import org.springframework.beans.factory.annotation.Qualifier;
1617
import org.springframework.context.annotation.Bean;
1718
import org.springframework.context.annotation.Conditional;
1819
import org.springframework.context.annotation.Configuration;
1920
import org.springframework.ide.vscode.boot.index.SpringMetamodelIndex;
21+
import org.springframework.ide.vscode.boot.java.codeaction.JdtAstCodeActionProvider;
22+
import org.springframework.ide.vscode.boot.java.codeaction.JdtCodeActionHandler;
2023
import org.springframework.ide.vscode.boot.java.cron.CronExpressionsInlayHintsProvider;
2124
import org.springframework.ide.vscode.boot.java.cron.CronReconciler;
2225
import org.springframework.ide.vscode.boot.java.cron.CronSemanticTokens;
@@ -56,6 +59,7 @@
5659
import org.springframework.ide.vscode.boot.java.spel.JdtSpelSemanticTokensProvider;
5760
import org.springframework.ide.vscode.boot.java.spel.SpelReconciler;
5861
import org.springframework.ide.vscode.boot.java.spel.SpelSemanticTokens;
62+
import org.springframework.ide.vscode.boot.java.utils.CompilationUnitCache;
5963
import org.springframework.ide.vscode.commons.languageserver.util.LspClient;
6064
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
6165

@@ -191,4 +195,7 @@ public class JdtConfig {
191195
return new JdtCronReconciler(cronReconciler);
192196
}
193197

198+
@Bean JdtCodeActionHandler jdtCodeActionHandler(CompilationUnitCache cuCache, Collection<JdtAstCodeActionProvider> providers) {
199+
return new JdtCodeActionHandler(cuCache, providers);
200+
}
194201
}

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

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,13 @@
1313
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
1414
import org.springframework.context.annotation.Bean;
1515
import org.springframework.context.annotation.Configuration;
16+
import org.springframework.ide.vscode.boot.java.data.DataRepositoryAotMetadataService;
17+
import org.springframework.ide.vscode.boot.java.data.QueryMethodCodeActionProvider;
1618
import org.springframework.ide.vscode.boot.java.reconcilers.JdtReconciler;
17-
import org.springframework.ide.vscode.boot.java.rewrite.RewriteCodeActionHandler;
19+
import org.springframework.ide.vscode.boot.java.reconcilers.ReconcileProblemCodeActionProvider;
1820
import org.springframework.ide.vscode.boot.java.rewrite.RewriteRecipeRepository;
1921
import org.springframework.ide.vscode.boot.java.rewrite.RewriteRefactorings;
2022
import org.springframework.ide.vscode.boot.java.rewrite.SpringBootUpgrade;
21-
import org.springframework.ide.vscode.boot.java.utils.CompilationUnitCache;
2223
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
2324
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
2425

@@ -35,13 +36,18 @@ public class RewriteConfig {
3536
}
3637

3738
@ConditionalOnBean(RewriteRecipeRepository.class)
38-
@Bean RewriteCodeActionHandler rewriteCodeActionHandler(CompilationUnitCache cuCache, BootJavaConfig config, JdtReconciler jdtReconciler, SimpleLanguageServer server) {
39-
return new RewriteCodeActionHandler(cuCache, config, jdtReconciler, server.getQuickfixRegistry(), server.getDiagnosticSeverityProvider());
39+
@Bean ReconcileProblemCodeActionProvider reconcileProblemCodeActionProvider(JdtReconciler reconciler, SimpleLanguageServer server) {
40+
return new ReconcileProblemCodeActionProvider(reconciler, server.getDiagnosticSeverityProvider());
4041
}
4142

4243
@ConditionalOnBean(RewriteRecipeRepository.class)
4344
@Bean SpringBootUpgrade springBootUpgrade(SimpleLanguageServer server, RewriteRecipeRepository recipeRepo, JavaProjectFinder projectFinder) {
4445
return new SpringBootUpgrade(server, recipeRepo, projectFinder);
4546
}
47+
48+
@ConditionalOnBean(RewriteRefactorings.class)
49+
@Bean QueryMethodCodeActionProvider queryMethodCodeActionProvider(DataRepositoryAotMetadataService dataRepoAotService, RewriteRefactorings refactorings) {
50+
return new QueryMethodCodeActionProvider(dataRepoAotService, refactorings);
51+
}
4652

4753
}

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@
6262
import org.springframework.ide.vscode.boot.java.requestmapping.RequestMappingHoverProvider;
6363
import org.springframework.ide.vscode.boot.java.requestmapping.WebfluxHandlerCodeLensProvider;
6464
import org.springframework.ide.vscode.boot.java.requestmapping.WebfluxRouteHighlightProdivder;
65+
import org.springframework.ide.vscode.boot.java.rewrite.RewriteRefactorings;
6566
import org.springframework.ide.vscode.boot.java.spel.SpelSemanticTokens;
6667
import org.springframework.ide.vscode.boot.java.utils.CompilationUnitCache;
6768
import org.springframework.ide.vscode.boot.java.value.ValueHoverProvider;
@@ -181,7 +182,7 @@ public BootJavaLanguageServerComponents(ApplicationContext appContext) {
181182

182183
spelSemanticTokens = appContext.getBean(SpelSemanticTokens.class);
183184
dataRepositoryAotMetadataService = appContext.getBean(DataRepositoryAotMetadataService.class);
184-
codeLensHandler = createCodeLensEngine(springIndex, projectFinder, server, spelSemanticTokens, dataRepositoryAotMetadataService);
185+
codeLensHandler = createCodeLensEngine(springIndex, projectFinder, server, spelSemanticTokens, dataRepositoryAotMetadataService, appContext.getBean(RewriteRefactorings.class));
185186

186187
highlightsEngine = createDocumentHighlightEngine(appContext);
187188
documents.onDocumentHighlight(highlightsEngine);
@@ -316,12 +317,12 @@ protected ReferencesHandler createReferenceHandler(SimpleLanguageServer server,
316317
}
317318

318319
protected BootJavaCodeLensEngine createCodeLensEngine(SpringMetamodelIndex springIndex, JavaProjectFinder projectFinder, SimpleLanguageServer server,
319-
SpelSemanticTokens spelSemanticTokens, DataRepositoryAotMetadataService repositoryAotMetadataService) {
320+
SpelSemanticTokens spelSemanticTokens, DataRepositoryAotMetadataService repositoryAotMetadataService, RewriteRefactorings refactorings) {
320321

321322
Collection<CodeLensProvider> codeLensProvider = new ArrayList<>();
322323
codeLensProvider.add(new WebfluxHandlerCodeLensProvider(springIndex));
323324
codeLensProvider.add(new CopilotCodeLensProvider(projectFinder, server, spelSemanticTokens));
324-
codeLensProvider.add(new DataRepositoryAotMetadataCodeLensProvider(projectFinder, repositoryAotMetadataService));
325+
codeLensProvider.add(new DataRepositoryAotMetadataCodeLensProvider(projectFinder, repositoryAotMetadataService, refactorings));
325326

326327
return new BootJavaCodeLensEngine(this, codeLensProvider);
327328
}

0 commit comments

Comments
 (0)