13
13
import java .util .ArrayList ;
14
14
import java .util .Arrays ;
15
15
import java .util .Collections ;
16
+ import java .util .HashMap ;
16
17
import java .util .List ;
18
+ import java .util .Map ;
17
19
import java .util .Optional ;
18
20
import java .util .Set ;
19
21
import java .util .concurrent .CompletableFuture ;
25
27
import org .eclipse .jdt .core .dom .Expression ;
26
28
import org .eclipse .jdt .core .dom .MemberValuePair ;
27
29
import org .eclipse .jdt .core .dom .MethodDeclaration ;
30
+ import org .eclipse .jdt .core .dom .MethodInvocation ;
28
31
import org .eclipse .jdt .core .dom .NormalAnnotation ;
32
+ import org .eclipse .jdt .core .dom .SimpleName ;
29
33
import org .eclipse .jdt .core .dom .SingleMemberAnnotation ;
34
+ import org .eclipse .jdt .core .dom .StringLiteral ;
30
35
import org .eclipse .lsp4j .CodeLens ;
31
36
import org .eclipse .lsp4j .Command ;
32
37
import org .eclipse .lsp4j .jsonrpc .CancelChecker ;
33
38
import org .slf4j .Logger ;
34
39
import org .slf4j .LoggerFactory ;
40
+ import org .springframework .ide .vscode .boot .java .Annotations ;
35
41
import org .springframework .ide .vscode .boot .java .spel .AnnotationParamSpelExtractor ;
36
42
import org .springframework .ide .vscode .boot .java .spel .AnnotationParamSpelExtractor .Snippet ;
37
43
import org .springframework .ide .vscode .boot .java .spel .SpelSemanticTokens ;
49
55
/**
50
56
* @author Udayani V
51
57
*/
52
- public class QueryCodeLensProvider implements CodeLensProvider {
58
+ public class CopilotCodeLensProvider implements CodeLensProvider {
53
59
54
- protected static Logger logger = LoggerFactory .getLogger (QueryCodeLensProvider .class );
60
+ protected static Logger logger = LoggerFactory .getLogger (CopilotCodeLensProvider .class );
55
61
56
62
public static final String CMD_ENABLE_COPILOT_FEATURES = "sts/enable/copilot/features" ;
57
63
@@ -66,13 +72,13 @@ public class QueryCodeLensProvider implements CodeLensProvider {
66
72
private SpelSemanticTokens spelSemanticTokens ;
67
73
68
74
private static boolean showCodeLenses ;
69
-
70
- public QueryCodeLensProvider (JavaProjectFinder projectFinder , SimpleLanguageServer server , SpelSemanticTokens spelSemanticTokens ) {
75
+
76
+ public CopilotCodeLensProvider (JavaProjectFinder projectFinder , SimpleLanguageServer server , SpelSemanticTokens spelSemanticTokens ) {
71
77
this .projectFinder = projectFinder ;
72
78
this .spelSemanticTokens = spelSemanticTokens ;
73
79
server .onCommand (CMD_ENABLE_COPILOT_FEATURES , params -> {
74
80
if (params .getArguments ().get (0 ) instanceof JsonPrimitive ) {
75
- QueryCodeLensProvider .showCodeLenses = ((JsonPrimitive ) params .getArguments ().get (0 )).getAsBoolean ();
81
+ CopilotCodeLensProvider .showCodeLenses = ((JsonPrimitive ) params .getArguments ().get (0 )).getAsBoolean ();
76
82
}
77
83
return CompletableFuture .completedFuture (showCodeLenses );
78
84
});
@@ -84,53 +90,66 @@ public void provideCodeLenses(CancelChecker cancelToken, TextDocument document,
84
90
if (!showCodeLenses ) {
85
91
return ;
86
92
}
93
+
94
+ Map <String , String > pointcutMap = findPointcuts (cu );
95
+
87
96
cu .accept (new ASTVisitor () {
88
97
89
98
@ Override
90
99
public boolean visit (SingleMemberAnnotation node ) {
91
100
Arrays .stream (spelExtractors ).map (e -> e .getSpelRegion (node )).filter (o -> o .isPresent ())
92
101
.map (o -> o .get ()).forEach (snippet -> {
93
102
String additionalContext = parseSpelAndFetchContext (cu , snippet .text ());
94
- provideCodeLensForSpelExpression (cancelToken , node , document , snippet ,
95
- additionalContext , resultAccumulator );
103
+ provideCodeLensForSpelExpression (cancelToken , node , document , snippet , additionalContext , resultAccumulator );
96
104
});
97
105
98
106
if (isQueryAnnotation (node )) {
99
- String queryPrompt = determineQueryPrompt (document );
100
- provideCodeLensForQuery (cancelToken , node , document , node .getValue (), queryPrompt ,
101
- resultAccumulator );
107
+ QueryType queryType = determineQueryType (document );
108
+ provideCodeLensForExpression (cancelToken , node , document , queryType , "" , resultAccumulator );
109
+ } else if (isAopAnnotation (node )) {
110
+ String additionalPointcutContext = extractPointcutReference (node .getValue (), pointcutMap );
111
+ provideCodeLensForExpression (cancelToken , node , document , QueryType .AOP , additionalPointcutContext , resultAccumulator );
102
112
}
103
113
104
114
return super .visit (node );
105
115
}
106
116
107
117
@ Override
108
118
public boolean visit (NormalAnnotation node ) {
109
-
110
119
111
120
Arrays .stream (spelExtractors ).map (e -> e .getSpelRegion (node )).filter (o -> o .isPresent ())
112
121
.map (o -> o .get ()).forEach (snippet -> {
113
122
String additionalContext = parseSpelAndFetchContext (cu , snippet .text ());
114
- provideCodeLensForSpelExpression (cancelToken , node , document , snippet , additionalContext ,
115
- resultAccumulator );
123
+ provideCodeLensForSpelExpression (cancelToken , node , document , snippet , additionalContext , resultAccumulator );
116
124
});
117
125
118
126
if (isQueryAnnotation (node )) {
119
- String queryPrompt = determineQueryPrompt (document );
120
- for (Object value : node .values ()) {
121
- if (value instanceof MemberValuePair ) {
122
- MemberValuePair pair = (MemberValuePair ) value ;
123
- if ("value" .equals (pair .getName ().getIdentifier ())) {
124
- provideCodeLensForQuery (cancelToken , node , document , pair .getValue (), queryPrompt ,
125
- resultAccumulator );
126
- break ;
127
- }
128
- }
127
+ QueryType queryType = determineQueryType (document );
128
+ provideCodeLensForExpression (cancelToken , node , document , queryType , "" , resultAccumulator );
129
+ } else if (isAopAnnotation (node )) {
130
+ Expression value = getMemberValue (node );
131
+ String additionalPointcutContext = null ;
132
+ if (value != null ) {
133
+ additionalPointcutContext = extractPointcutReference (value , pointcutMap );
129
134
}
135
+ provideCodeLensForExpression (cancelToken , node , document , QueryType .AOP , additionalPointcutContext , resultAccumulator );
130
136
}
131
137
132
138
return super .visit (node );
133
139
}
140
+
141
+ private Expression getMemberValue (NormalAnnotation node ) {
142
+ for (Object value : node .values ()) {
143
+ if (value instanceof MemberValuePair ) {
144
+ MemberValuePair pair = (MemberValuePair ) value ;
145
+ if ("pointcut" .equals (pair .getName ().getIdentifier ())) {
146
+ return pair .getValue ();
147
+ }
148
+ }
149
+ }
150
+ return null ;
151
+ }
152
+
134
153
});
135
154
}
136
155
@@ -163,20 +182,26 @@ protected void provideCodeLensForSpelExpression(CancelChecker cancelToken, Annot
163
182
}
164
183
}
165
184
166
- protected void provideCodeLensForQuery (CancelChecker cancelToken , Annotation node , TextDocument document ,
167
- Expression valueExp , String query , List <CodeLens > resultAccumulator ) {
185
+ protected void provideCodeLensForExpression (CancelChecker cancelToken , Annotation node , TextDocument document ,
186
+ QueryType queryType , String additionalContext , List <CodeLens > resultAccumulator ) {
168
187
cancelToken .checkCanceled ();
169
188
170
- if (valueExp != null ) {
189
+ if (node != null ) {
171
190
try {
172
-
191
+
192
+ String context = additionalContext != null && !additionalContext .isEmpty () ? String .format (
193
+ """
194
+ This is the pointcut definition referenced in the above annotation. \n \n %s \n \n Provide a brief summary of the pointcut's role within the annotation.
195
+ Avoid detailed implementation steps and avoid repeating information covered earlier.
196
+ """ ,additionalContext ) : "" ;
197
+
173
198
CodeLens codeLens = new CodeLens ();
174
- codeLens .setRange (document .toRange (valueExp .getStartPosition (), valueExp .getLength ()));
199
+ codeLens .setRange (document .toRange (node .getStartPosition (), node .getLength ()));
175
200
176
201
Command cmd = new Command ();
177
- cmd .setTitle (QueryType . DEFAULT .getTitle ());
202
+ cmd .setTitle (queryType .getTitle ());
178
203
cmd .setCommand (CMD );
179
- cmd .setArguments (ImmutableList .of (query + valueExp .toString ()));
204
+ cmd .setArguments (ImmutableList .of (queryType . getPrompt () + node .toString () + " \n \n " + context ));
180
205
codeLens .setCommand (cmd );
181
206
182
207
resultAccumulator .add (codeLens );
@@ -190,15 +215,15 @@ private static boolean isQueryAnnotation(Annotation a) {
190
215
return FQN_QUERY .equals (a .getTypeName ().getFullyQualifiedName ())
191
216
|| QUERY .equals (a .getTypeName ().getFullyQualifiedName ());
192
217
}
193
-
194
- private String determineQueryPrompt (TextDocument document ) {
218
+
219
+ private QueryType determineQueryType (TextDocument document ) {
195
220
Optional <IJavaProject > optProject = projectFinder .find (document .getId ());
196
221
if (optProject .isPresent ()) {
197
222
IJavaProject jp = optProject .get ();
198
- return SpringProjectUtil .hasDependencyStartingWith (jp , "hibernate-core" , null ) ? QueryType .HQL . getPrompt ()
199
- : QueryType .JPQL . getPrompt () ;
223
+ return SpringProjectUtil .hasDependencyStartingWith (jp , "hibernate-core" , null ) ? QueryType .HQL
224
+ : QueryType .JPQL ;
200
225
}
201
- return QueryType .DEFAULT . getPrompt () ;
226
+ return QueryType .DEFAULT ;
202
227
}
203
228
204
229
private String parseSpelAndFetchContext (CompilationUnit cu , String spelExpression ) {
@@ -238,4 +263,49 @@ public boolean visit(MethodDeclaration node) {
238
263
return methodContext ;
239
264
}
240
265
266
+ private boolean isAopAnnotation (Annotation a ) {
267
+ String annotationFQN = a .getTypeName ().getFullyQualifiedName ();
268
+ return Annotations .AOP_ANNOTATIONS .containsKey (annotationFQN )
269
+ || Annotations .AOP_ANNOTATIONS .containsValue (annotationFQN );
270
+ }
271
+
272
+ private Map <String , String > findPointcuts (CompilationUnit cu ) {
273
+ Map <String , String > pointcutMap = new HashMap <>();
274
+ cu .accept (new ASTVisitor () {
275
+ @ Override
276
+ public boolean visit (MethodDeclaration node ) {
277
+ for (Object modifierObj : node .modifiers ()) {
278
+ if (modifierObj instanceof Annotation ) {
279
+ Annotation annotation = (Annotation ) modifierObj ;
280
+ if ("Pointcut" .equals (annotation .getTypeName ().getFullyQualifiedName ())) {
281
+ String methodName = node .getName ().getIdentifier ();
282
+ pointcutMap .put (methodName , node .toString ());
283
+ }
284
+ }
285
+ }
286
+ return super .visit (node );
287
+ }
288
+ });
289
+ return pointcutMap ;
290
+
291
+ }
292
+
293
+ private String extractPointcutReference (org .eclipse .jdt .core .dom .Expression expression , Map <String , String > pointcutMap ) {
294
+ if (expression instanceof MethodInvocation ) {
295
+ return ((MethodInvocation ) expression ).getName ().getIdentifier ();
296
+ } else if (expression instanceof SimpleName ) {
297
+ return ((SimpleName ) expression ).getIdentifier ();
298
+ } else if (expression instanceof StringLiteral ) {
299
+ String literalValue = ((StringLiteral ) expression ).getLiteralValue ();
300
+ StringBuilder pointcuts = new StringBuilder ();
301
+ for (Map .Entry <String , String > entry : pointcutMap .entrySet ()) {
302
+ if (literalValue .contains (entry .getKey ())) {
303
+ pointcuts .append (entry .getValue ());
304
+ }
305
+ }
306
+ return pointcuts .toString ();
307
+ }
308
+ return null ;
309
+ }
310
+
241
311
}
0 commit comments