|
4 | 4 | import com.intellij.lang.Language; |
5 | 5 | import com.intellij.lang.java.JavaLanguage; |
6 | 6 | import com.intellij.openapi.application.ReadAction; |
| 7 | +import com.intellij.openapi.command.WriteCommandAction; |
7 | 8 | import com.intellij.openapi.diagnostic.Logger; |
8 | 9 | import com.intellij.openapi.editor.Editor; |
9 | 10 | import com.intellij.openapi.fileEditor.FileEditor; |
10 | 11 | import com.intellij.openapi.fileEditor.FileEditorManager; |
| 12 | +import com.intellij.openapi.module.ModuleUtilCore; |
11 | 13 | import com.intellij.openapi.project.DumbService; |
12 | 14 | import com.intellij.openapi.project.Project; |
13 | 15 | import com.intellij.openapi.roots.ProjectFileIndex; |
|
22 | 24 | import org.digma.intellij.plugin.log.Log; |
23 | 25 | import org.digma.intellij.plugin.model.discovery.DocumentInfo; |
24 | 26 | import org.digma.intellij.plugin.model.discovery.MethodUnderCaret; |
| 27 | +import org.digma.intellij.plugin.psi.CanInstrumentMethodResult; |
25 | 28 | import org.digma.intellij.plugin.psi.LanguageService; |
26 | 29 | import org.digma.intellij.plugin.psi.PsiUtils; |
27 | 30 | import org.digma.intellij.plugin.ui.CaretContextService; |
@@ -138,6 +141,68 @@ public MethodUnderCaret detectMethodUnderCaret(@NotNull Project project, @NotNul |
138 | 141 | return new MethodUnderCaret("", "", "", PsiUtils.psiFileToUri(psiFile), true); |
139 | 142 | } |
140 | 143 |
|
| 144 | + public CanInstrumentMethodResult canInstrumentMethod(@NotNull Project project, String methodId){ |
| 145 | + |
| 146 | + var psiMethod = findPsiMethodByMethodCodeObjectId(methodId); |
| 147 | + if (psiMethod == null) { |
| 148 | + Log.log(LOGGER::warn, "Failed to get PsiMethod from method id '{}'", methodId); |
| 149 | + return CanInstrumentMethodResult.Failure(); |
| 150 | + } |
| 151 | + |
| 152 | + var psiFile = psiMethod.getContainingFile(); |
| 153 | + if (!(psiFile instanceof PsiJavaFile psiJavaFile)) { |
| 154 | + Log.log(LOGGER::warn, "PsiMethod's file is not java file (methodId: {})", methodId); |
| 155 | + return CanInstrumentMethodResult.Failure(); |
| 156 | + } |
| 157 | + |
| 158 | + var module = ModuleUtilCore.findModuleForPsiElement(psiMethod); |
| 159 | + if (module == null) { |
| 160 | + Log.log(LOGGER::warn, "Failed to get module from PsiMethod '{}'", methodId); |
| 161 | + return CanInstrumentMethodResult.Failure(); |
| 162 | + } |
| 163 | + |
| 164 | + var withSpanClass = JavaPsiFacade.getInstance(project).findClass( |
| 165 | + "io.opentelemetry.instrumentation.annotations.WithSpan", |
| 166 | + GlobalSearchScope.allScope(project)); |
| 167 | + if (withSpanClass == null) { |
| 168 | + Log.log(LOGGER::warn, "Failed to get WithSpan PsiClass (methodId: {})", methodId); |
| 169 | + return new CanInstrumentMethodResult(new CanInstrumentMethodResult.MissingDependencyCause("io.opentelemetry.instrumentation:opentelemetry-instrumentation-annotations")); |
| 170 | + } |
| 171 | + |
| 172 | + return new JavaCanInstrumentMethodResult(methodId, psiMethod, withSpanClass, psiJavaFile); |
| 173 | + } |
| 174 | + |
| 175 | + public boolean instrumentMethod(@NotNull CanInstrumentMethodResult result){ |
| 176 | + |
| 177 | + if (!(result instanceof JavaCanInstrumentMethodResult goodResult)) { |
| 178 | + Log.log(LOGGER::warn, "instrumentMethod was called with failing result from canInstrumentMethod"); |
| 179 | + return false; |
| 180 | + } |
| 181 | + |
| 182 | + var psiJavaFile = goodResult.psiJavaFile; |
| 183 | + var psiMethod = goodResult.psiMethod; |
| 184 | + var methodId = goodResult.methodId; |
| 185 | + var withSpanClass = goodResult.withSpanClass; |
| 186 | + |
| 187 | + var importList = psiJavaFile.getImportList(); |
| 188 | + if (importList == null) { |
| 189 | + Log.log(LOGGER::warn, "Failed to get ImportList from PsiFile (methodId: {})", methodId); |
| 190 | + return false; |
| 191 | + } |
| 192 | + |
| 193 | + WriteCommandAction.runWriteCommandAction(project, () -> { |
| 194 | + var psiFactory = PsiElementFactory.getInstance(project); |
| 195 | + |
| 196 | + psiMethod.getModifierList().addAnnotation("WithSpan"); |
| 197 | + |
| 198 | + var existing = importList.findSingleClassImportStatement(withSpanClass.getQualifiedName()); |
| 199 | + if (existing == null) { |
| 200 | + var importStatement = psiFactory.createImportStatement(withSpanClass); |
| 201 | + importList.add(importStatement); |
| 202 | + } |
| 203 | + }); |
| 204 | + return true; |
| 205 | + } |
141 | 206 |
|
142 | 207 | /** |
143 | 208 | * Navigate to any method in the project even if the file is not opened |
@@ -253,37 +318,45 @@ public Map<String, Pair<String, Integer>> findWorkspaceUrisForMethodCodeObjectId |
253 | 318 |
|
254 | 319 | methodCodeObjectIds.forEach(methodId -> { |
255 | 320 |
|
256 | | - if (methodId.contains("$_$")) { |
257 | | - var className = methodId.substring(0, methodId.indexOf("$_$")); |
| 321 | + var psiMethod = findPsiMethodByMethodCodeObjectId(methodId); |
| 322 | + if (psiMethod != null) { |
| 323 | + String url = PsiUtils.psiFileToUri(psiMethod.getContainingFile()); |
| 324 | + workspaceUrls.put(methodId, new Pair<>(url, psiMethod.getTextOffset())); |
| 325 | + } |
| 326 | + }); |
258 | 327 |
|
259 | | - //the code object id for inner classes separates inner classes name with $, but intellij index them with a dot |
260 | | - className = className.replace('$', '.'); |
| 328 | + return workspaceUrls; |
| 329 | + } |
261 | 330 |
|
262 | | - //searching in project scope will find only project classes |
263 | | - Collection<PsiClass> psiClasses = |
264 | | - JavaFullClassNameIndex.getInstance().get(className, project, GlobalSearchScope.projectScope(project)); |
265 | | - if (!psiClasses.isEmpty()) { |
266 | | - //hopefully there is only one class by that name in the project |
267 | | - PsiClass psiClass = psiClasses.stream().findAny().get(); |
268 | | - PsiFile psiFile = PsiTreeUtil.getParentOfType(psiClass, PsiFile.class); |
269 | | - for (PsiMethod method : psiClass.getMethods()) { |
270 | | - String javaMethodCodeObjectId = createJavaMethodCodeObjectId(method); |
271 | | - if (javaMethodCodeObjectId.equals(methodId) && psiFile != null) { |
272 | | - String url = PsiUtils.psiFileToUri(psiFile); |
273 | | - workspaceUrls.put(methodId, new Pair<>(url, method.getTextOffset())); |
| 331 | + private @Nullable PsiMethod findPsiMethodByMethodCodeObjectId(String methodId){ |
| 332 | + if (methodId.contains("$_$")) { |
| 333 | + var className = methodId.substring(0, methodId.indexOf("$_$")); |
| 334 | + |
| 335 | + //the code object id for inner classes separates inner classes name with $, but intellij index them with a dot |
| 336 | + className = className.replace('$', '.'); |
274 | 337 |
|
275 | | - } |
| 338 | + //searching in project scope will find only project classes |
| 339 | + Collection<PsiClass> psiClasses = |
| 340 | + JavaFullClassNameIndex.getInstance().get(className, project, GlobalSearchScope.projectScope(project)); |
| 341 | + if (!psiClasses.isEmpty()) { |
| 342 | + //hopefully there is only one class by that name in the project |
| 343 | + PsiClass psiClass = psiClasses.stream().findAny().get(); |
| 344 | + PsiFile psiFile = PsiTreeUtil.getParentOfType(psiClass, PsiFile.class); |
| 345 | + for (PsiMethod method : psiClass.getMethods()) { |
| 346 | + String javaMethodCodeObjectId = createJavaMethodCodeObjectId(method); |
| 347 | + if (javaMethodCodeObjectId.equals(methodId) && psiFile != null) { |
| 348 | +// String url = PsiUtils.psiFileToUri(psiFile); |
| 349 | +// workspaceUrls.put(methodId, new Pair<>(url, method.getTextOffset())); |
| 350 | + return method; |
276 | 351 | } |
277 | 352 | } |
278 | | - }else{ |
279 | | - Log.log(LOGGER::debug, "method id in findWorkspaceUrisForMethodCodeObjectIds does not contain $_$ {}", methodId); |
280 | 353 | } |
281 | | - }); |
282 | | - |
283 | | - return workspaceUrls; |
| 354 | + }else{ |
| 355 | + Log.log(LOGGER::debug, "method id in findWorkspaceUrisForMethodCodeObjectIds does not contain $_$ {}", methodId); |
| 356 | + } |
| 357 | + return null; |
284 | 358 | } |
285 | 359 |
|
286 | | - |
287 | 360 | @NotNull |
288 | 361 | @Override |
289 | 362 | public Map<String, Pair<String, Integer>> findWorkspaceUrisForSpanIds(@NotNull List<String> spanIds) { |
@@ -375,4 +448,19 @@ public boolean isCodeVisionSupported() { |
375 | 448 | public @NotNull List<Pair<TextRange, CodeVisionEntry>> getCodeLens(@NotNull PsiFile psiFile) { |
376 | 449 | return JavaCodeLensService.getInstance(project).getCodeLens(psiFile); |
377 | 450 | } |
| 451 | + |
| 452 | + private static final class JavaCanInstrumentMethodResult extends CanInstrumentMethodResult { |
| 453 | + private final String methodId; |
| 454 | + private final PsiMethod psiMethod; |
| 455 | + private final PsiClass withSpanClass; |
| 456 | + private final PsiJavaFile psiJavaFile; |
| 457 | + |
| 458 | + private JavaCanInstrumentMethodResult(String methodId, PsiMethod psiMethod, PsiClass withSpanClass, |
| 459 | + PsiJavaFile psiJavaFile) { |
| 460 | + this.methodId = methodId; |
| 461 | + this.psiMethod = psiMethod; |
| 462 | + this.withSpanClass = withSpanClass; |
| 463 | + this.psiJavaFile = psiJavaFile; |
| 464 | + } |
| 465 | + } |
378 | 466 | } |
0 commit comments