From 869ead4713157ebc6e15a453df959a3af8ba1cd6 Mon Sep 17 00:00:00 2001 From: azerr Date: Mon, 5 Aug 2024 10:44:36 +0200 Subject: [PATCH] feat: Workspace symbols for JAX-RS endpoints Fixes #1363 Signed-off-by: azerr --- .../org/acme/config/GreetingResource.java | 6 + .../psi/core/PropertiesManagerForJava.java | 50 +++++-- .../codelens/IJavaCodeLensParticipant.java | 3 +- .../IJavaCompletionParticipant.java | 3 +- .../IJavaDefinitionParticipant.java | 3 +- .../IJavaDiagnosticsParticipant.java | 2 +- .../java/hover/IJavaHoverParticipant.java | 2 +- .../IJavaWorkspaceSymbolsParticipant.java | 42 ++++++ .../psi/core/jaxrs/IJaxRsInfoProvider.java | 4 +- .../jaxrs/java/DefaultJaxRsInfoProvider.java | 62 +++++++-- .../jaxrs/java/JaxRsInfoProviderRegistry.java | 2 +- .../java/JaxRsWorkspaceSymbolParticipant.java | 128 +++++++++++++++++ .../quarkus/lsp/QuarkusLanguageClient.java | 4 +- .../java/RenardeJaxRsInfoProvider.java | 15 +- .../quarkus/renarde/java/RenardeUtils.java | 20 ++- .../java/ReactiveRouteJaxRsInfoProvider.java | 5 +- src/main/resources/META-INF/plugin.xml | 5 +- .../psi/core/MicroProfileForJavaAssert.java | 63 +++++++++ .../JaxRsWorkspaceSymbolParticipantTest.java | 53 +++++++ .../psi/quarkus/renarde/RenardeJaxRsTest.java | 129 +++++++++--------- 20 files changed, 506 insertions(+), 95 deletions(-) create mode 100644 src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/core/java/symbols/IJavaWorkspaceSymbolsParticipant.java create mode 100644 src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/internal/jaxrs/java/JaxRsWorkspaceSymbolParticipant.java create mode 100644 src/test/java/com/redhat/microprofile/psi/quarkus/jaxrs/JaxRsWorkspaceSymbolParticipantTest.java diff --git a/projects/lsp4mp/projects/maven/config-quickstart/src/main/java/org/acme/config/GreetingResource.java b/projects/lsp4mp/projects/maven/config-quickstart/src/main/java/org/acme/config/GreetingResource.java index 4b276e837..ca64bde85 100644 --- a/projects/lsp4mp/projects/maven/config-quickstart/src/main/java/org/acme/config/GreetingResource.java +++ b/projects/lsp4mp/projects/maven/config-quickstart/src/main/java/org/acme/config/GreetingResource.java @@ -3,6 +3,7 @@ import java.util.Optional; import javax.ws.rs.GET; +import javax.ws.rs.PATCH; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; @@ -40,4 +41,9 @@ public String hello2() { public String hello3() { return message + " 4 " + name.orElse("world") + suffix; } + + @PATCH + @Path("hello5") + public String hello5() { + } } diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/core/PropertiesManagerForJava.java b/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/core/PropertiesManagerForJava.java index 0a17397e5..3935c5a96 100644 --- a/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/core/PropertiesManagerForJava.java +++ b/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/core/PropertiesManagerForJava.java @@ -14,6 +14,7 @@ import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.editor.Document; import com.intellij.openapi.module.Module; +import com.intellij.openapi.module.ModuleManager; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.project.DumbService; import com.intellij.openapi.util.Computable; @@ -27,21 +28,15 @@ import com.redhat.devtools.intellij.lsp4mp4ij.psi.core.java.completion.JavaCompletionContext; import com.redhat.devtools.intellij.lsp4mp4ij.psi.core.java.definition.IJavaDefinitionParticipant; import com.redhat.devtools.intellij.lsp4mp4ij.psi.core.java.definition.JavaDefinitionContext; +import com.redhat.devtools.intellij.lsp4mp4ij.psi.core.java.symbols.IJavaWorkspaceSymbolsParticipant; import com.redhat.devtools.intellij.lsp4mp4ij.psi.core.utils.IPsiUtils; import com.redhat.devtools.intellij.lsp4mp4ij.psi.core.java.diagnostics.IJavaDiagnosticsParticipant; import com.redhat.devtools.intellij.lsp4mp4ij.psi.core.java.diagnostics.JavaDiagnosticsContext; import com.redhat.devtools.intellij.lsp4mp4ij.psi.core.java.hover.IJavaHoverParticipant; import com.redhat.devtools.intellij.lsp4mp4ij.psi.core.java.hover.JavaHoverContext; import com.redhat.devtools.intellij.lsp4mp4ij.psi.internal.core.java.codeaction.CodeActionHandler; -import org.eclipse.lsp4j.CodeAction; -import org.eclipse.lsp4j.CodeLens; -import org.eclipse.lsp4j.CompletionItem; -import org.eclipse.lsp4j.CompletionList; +import org.eclipse.lsp4j.*; import org.eclipse.lsp4mp.commons.*; -import org.eclipse.lsp4j.Diagnostic; -import org.eclipse.lsp4j.Hover; -import org.eclipse.lsp4j.Position; -import org.eclipse.lsp4j.PublishDiagnosticsParams; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; @@ -637,4 +632,43 @@ public CodeAction resolveCodeAction(CodeAction unresolved, IPsiUtils utils) { return codeActionHandler.resolveCodeAction(unresolved, utils); } + /** + * Returns the workspace symbols for the given java project. + * + * @param projectUri the uri of the java project + * @param utils the JDT utils + * @param monitor the progress monitor + * @return the workspace symbols for the given java project + */ + public List workspaceSymbols(String projectUri, IPsiUtils utils, ProgressIndicator monitor) { + List symbols = new ArrayList<>(); + Module module = getModule(projectUri, utils); + if (module != null) { + collectWorkspaceSymbols(module, utils, symbols, monitor); + } + return symbols; + } + + private static @Nullable Module getModule(String uri, IPsiUtils utils) { + Module[] modules = ModuleManager.getInstance(utils.getProject()).getModules(); + for (Module module : modules) { + if (uri.equals(module.getName())) { + return module; + } + } + return null; + } + + private void collectWorkspaceSymbols(Module project, IPsiUtils utils, List symbols, + ProgressIndicator monitor) { + if (monitor.isCanceled()) { + return; + } + + List definitions = IJavaWorkspaceSymbolsParticipant.EP_NAME.getExtensionList(); + if (definitions.isEmpty()) { + return; + } + definitions.forEach(definition -> definition.collectSymbols(project, utils, symbols, monitor)); + } } diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/core/java/codelens/IJavaCodeLensParticipant.java b/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/core/java/codelens/IJavaCodeLensParticipant.java index 3ace83b32..5f4aac3ac 100644 --- a/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/core/java/codelens/IJavaCodeLensParticipant.java +++ b/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/core/java/codelens/IJavaCodeLensParticipant.java @@ -26,7 +26,8 @@ * */ public interface IJavaCodeLensParticipant { - public static final ExtensionPointName EP_NAME = ExtensionPointName.create("com.redhat.devtools.intellij.quarkus.javaCodeLensParticipant"); + + ExtensionPointName EP_NAME = ExtensionPointName.create("com.redhat.devtools.intellij.quarkus.javaCodeLensParticipant"); /** diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/core/java/completion/IJavaCompletionParticipant.java b/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/core/java/completion/IJavaCompletionParticipant.java index ce41e50e0..43290c3ea 100644 --- a/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/core/java/completion/IJavaCompletionParticipant.java +++ b/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/core/java/completion/IJavaCompletionParticipant.java @@ -24,7 +24,8 @@ * @author datho7561 */ public interface IJavaCompletionParticipant { - public static final ExtensionPointName EP_NAME = ExtensionPointName.create("com.redhat.devtools.intellij.quarkus.javaCompletionParticipant"); + + ExtensionPointName EP_NAME = ExtensionPointName.create("com.redhat.devtools.intellij.quarkus.javaCompletionParticipant"); /** * Returns true if this completion feature should be active in this context, and false otherwise diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/core/java/definition/IJavaDefinitionParticipant.java b/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/core/java/definition/IJavaDefinitionParticipant.java index e640240c1..0f0d79240 100644 --- a/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/core/java/definition/IJavaDefinitionParticipant.java +++ b/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/core/java/definition/IJavaDefinitionParticipant.java @@ -25,7 +25,8 @@ * */ public interface IJavaDefinitionParticipant { - public static final ExtensionPointName EP_NAME = ExtensionPointName.create("com.redhat.devtools.intellij.quarkus.javaDefinitionParticipant"); + + ExtensionPointName EP_NAME = ExtensionPointName.create("com.redhat.devtools.intellij.quarkus.javaDefinitionParticipant"); /** * Returns true if definition must be collected for the given context and false diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/core/java/diagnostics/IJavaDiagnosticsParticipant.java b/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/core/java/diagnostics/IJavaDiagnosticsParticipant.java index 4698b19c4..919160706 100644 --- a/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/core/java/diagnostics/IJavaDiagnosticsParticipant.java +++ b/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/core/java/diagnostics/IJavaDiagnosticsParticipant.java @@ -25,7 +25,7 @@ */ public interface IJavaDiagnosticsParticipant { - public static final ExtensionPointName EP_NAME = ExtensionPointName.create("com.redhat.devtools.intellij.quarkus.javaDiagnosticsParticipant"); + ExtensionPointName EP_NAME = ExtensionPointName.create("com.redhat.devtools.intellij.quarkus.javaDiagnosticsParticipant"); /** * Returns true if diagnostics must be collected for the given context and false diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/core/java/hover/IJavaHoverParticipant.java b/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/core/java/hover/IJavaHoverParticipant.java index 30a792025..55c863574 100644 --- a/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/core/java/hover/IJavaHoverParticipant.java +++ b/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/core/java/hover/IJavaHoverParticipant.java @@ -23,7 +23,7 @@ */ public interface IJavaHoverParticipant { - public static final ExtensionPointName EP_NAME = ExtensionPointName.create("com.redhat.devtools.intellij.quarkus.javaHoverParticipant"); + ExtensionPointName EP_NAME = ExtensionPointName.create("com.redhat.devtools.intellij.quarkus.javaHoverParticipant"); /** * Returns true if hover must be collected for the given context and false diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/core/java/symbols/IJavaWorkspaceSymbolsParticipant.java b/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/core/java/symbols/IJavaWorkspaceSymbolsParticipant.java new file mode 100644 index 000000000..e368d9952 --- /dev/null +++ b/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/core/java/symbols/IJavaWorkspaceSymbolsParticipant.java @@ -0,0 +1,42 @@ +/******************************************************************************* + * Copyright (c) 2024 Red Hat Inc. and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 + * which is available at https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 + * + * Contributors: + * Red Hat Inc. - initial API and implementation + *******************************************************************************/ +package com.redhat.devtools.intellij.lsp4mp4ij.psi.core.java.symbols; + +import com.intellij.openapi.extensions.ExtensionPointName; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.progress.ProgressIndicator; +import com.redhat.devtools.intellij.lsp4mp4ij.psi.core.utils.IPsiUtils; +import org.eclipse.lsp4j.SymbolInformation; + +import java.util.List; + +/** + * Represents an object that can collect workspace symbols for java projects. + */ +public interface IJavaWorkspaceSymbolsParticipant { + + ExtensionPointName EP_NAME = ExtensionPointName.create("com.redhat.devtools.intellij.quarkus.javaWorkspaceSymbolsParticipant"); + + /** + * Fill in symbols with workspace symbols of the given project. + * + * @param project the project to collect workspace symbols from + * @param utils the JDT utils + * @param symbols the list of symbols to add to + * @param monitor the progress monitor + */ + void collectSymbols(Module project, IPsiUtils utils, List symbols, + ProgressIndicator monitor); + +} \ No newline at end of file diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/core/jaxrs/IJaxRsInfoProvider.java b/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/core/jaxrs/IJaxRsInfoProvider.java index c601d709e..df541d8a7 100644 --- a/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/core/jaxrs/IJaxRsInfoProvider.java +++ b/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/core/jaxrs/IJaxRsInfoProvider.java @@ -43,15 +43,17 @@ public interface IJaxRsInfoProvider { * Returns a non-null set of all the classes in the given project that this provider can provide JAX-RS method information for. * * @param javaProject the project to check for JAX-RS method information + * @param utils the Psi utilities. * @param monitor the progress monitor * @return a non-null set of all the classes in the given project that this provider can provide JAX-RS method information for */ - @NotNull Set getAllJaxRsClasses(@NotNull Module javaProject, @NotNull ProgressIndicator monitor); + @NotNull Set getAllJaxRsClasses(@NotNull Module javaProject, @NotNull IPsiUtils utils, @NotNull ProgressIndicator monitor); /** * Returns a list of all the JAX-RS methods in the given type. * * @param type the type to check for JAX-RS methods + * @param jaxrsContext the JAX-RS context. * @param monitor the progress monitor * @return a list of all the JAX-RS methods in the given type */ diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/internal/jaxrs/java/DefaultJaxRsInfoProvider.java b/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/internal/jaxrs/java/DefaultJaxRsInfoProvider.java index 4323cd123..968978d11 100644 --- a/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/internal/jaxrs/java/DefaultJaxRsInfoProvider.java +++ b/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/internal/jaxrs/java/DefaultJaxRsInfoProvider.java @@ -18,18 +18,20 @@ import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.project.IndexNotReadyException; import com.intellij.psi.*; +import com.intellij.psi.search.SearchScope; +import com.intellij.psi.search.searches.AnnotatedElementsSearch; import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.util.MergeQuery; +import com.intellij.util.Query; import com.redhat.devtools.lsp4ij.LSPIJUtils; import com.redhat.devtools.intellij.lsp4mp4ij.psi.core.jaxrs.*; import com.redhat.devtools.intellij.lsp4mp4ij.psi.core.utils.IPsiUtils; import com.redhat.devtools.intellij.lsp4mp4ij.psi.core.utils.PsiTypeUtils; +import org.jetbrains.annotations.NotNull; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Set; +import java.util.*; import java.util.concurrent.CancellationException; +import java.util.function.Consumer; import java.util.logging.Level; import java.util.logging.Logger; @@ -49,14 +51,58 @@ public class DefaultJaxRsInfoProvider implements IJaxRsInfoProvider { private static final Logger LOGGER = Logger.getLogger(DefaultJaxRsInfoProvider.class.getName()); @Override - public boolean canProvideJaxRsMethodInfoForClass(PsiFile typeRoot, Module javaProject, ProgressIndicator monitor) { + public boolean canProvideJaxRsMethodInfoForClass(@NotNull PsiFile typeRoot, Module javaProject, ProgressIndicator monitor) { return PsiTypeUtils.findType(javaProject, JAVAX_WS_RS_PATH_ANNOTATION) != null || PsiTypeUtils.findType(javaProject, JAKARTA_WS_RS_PATH_ANNOTATION) != null; } @Override - public Set getAllJaxRsClasses(Module javaProject, ProgressIndicator monitor) { - // TODO: implement when LSP4IJ will support workspace symbols + public Set getAllJaxRsClasses(Module javaProject, IPsiUtils utils, ProgressIndicator monitor) { + if (monitor.isCanceled()) { + return Collections.emptySet(); + } + + try { + SearchScope scope = javaProject.getModuleScope(false); + + Query query = null; + for (var httpAnnotation : JaxRsConstants.HTTP_METHOD_ANNOTATIONS) { + PsiClass annotationClass = utils.findClass(javaProject, httpAnnotation); + if (annotationClass != null) { + Query annotationQuery = AnnotatedElementsSearch.searchElements(annotationClass, scope, PsiModifierListOwner.class); + if (query == null) { + query = annotationQuery; + } else { + query = new MergeQuery<>(query, annotationQuery); + } + } + } + if (query == null) { + return Collections.emptySet(); + } + + Set jaxRsClasses = new HashSet<>(); + query.forEach((Consumer) item -> { + if (item instanceof PsiMember) { + PsiClass cl = ((PsiMember) item).getContainingClass(); + if (cl != null) { + jaxRsClasses.add(cl); + } + } + }); + if (monitor.isCanceled()) { + return Collections.emptySet(); + } + return jaxRsClasses; + } catch (ProcessCanceledException e) { + //Since 2024.2 ProcessCanceledException extends CancellationException so we can't use multicatch to keep backward compatibility + //TODO delete block when minimum required version is 2024.2 + throw e; + } catch (IndexNotReadyException | CancellationException e) { + throw e; + } catch (Exception e) { + LOGGER.log(Level.SEVERE, "While collecting JAX-RS method information for project " + javaProject.getName(), e); + } return Collections.emptySet(); } diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/internal/jaxrs/java/JaxRsInfoProviderRegistry.java b/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/internal/jaxrs/java/JaxRsInfoProviderRegistry.java index c4f8dfbef..7aa685464 100644 --- a/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/internal/jaxrs/java/JaxRsInfoProviderRegistry.java +++ b/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/internal/jaxrs/java/JaxRsInfoProviderRegistry.java @@ -62,7 +62,7 @@ public static JaxRsInfoProviderRegistry getInstance() { return null; } - private List getProviders() { + public List getProviders() { if (!initialized) { providers = loadProviders(); return providers; diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/internal/jaxrs/java/JaxRsWorkspaceSymbolParticipant.java b/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/internal/jaxrs/java/JaxRsWorkspaceSymbolParticipant.java new file mode 100644 index 000000000..83c3b1e2e --- /dev/null +++ b/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/internal/jaxrs/java/JaxRsWorkspaceSymbolParticipant.java @@ -0,0 +1,128 @@ +/******************************************************************************* +* Copyright (c) 2024 Red Hat Inc. and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +* which is available at https://www.apache.org/licenses/LICENSE-2.0. +* +* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package com.redhat.devtools.intellij.lsp4mp4ij.psi.internal.jaxrs.java; + +import com.intellij.openapi.module.Module; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.util.TextRange; +import com.intellij.psi.PsiClass; +import com.redhat.devtools.intellij.lsp4mp4ij.psi.core.java.symbols.IJavaWorkspaceSymbolsParticipant; +import com.redhat.devtools.intellij.lsp4mp4ij.psi.core.jaxrs.IJaxRsInfoProvider; +import com.redhat.devtools.intellij.lsp4mp4ij.psi.core.jaxrs.JaxRsContext; +import com.redhat.devtools.intellij.lsp4mp4ij.psi.core.jaxrs.JaxRsMethodInfo; +import com.redhat.devtools.intellij.lsp4mp4ij.psi.core.utils.IPsiUtils; +import org.eclipse.lsp4j.Location; +import org.eclipse.lsp4j.Range; +import org.eclipse.lsp4j.SymbolInformation; +import org.eclipse.lsp4j.SymbolKind; + +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Collects workspace symbols for JAX-RS REST endpoints. + */ +public class JaxRsWorkspaceSymbolParticipant implements IJavaWorkspaceSymbolsParticipant { + + private static final Logger LOGGER = Logger.getLogger(JaxRsWorkspaceSymbolParticipant.class.getName()); + + @Override + public void collectSymbols(Module project, IPsiUtils utils, List symbols, ProgressIndicator monitor) { + if (monitor.isCanceled()) { + return; + } + + JaxRsContext jaxrsContext = new JaxRsContext(project); + Set jaxrsTypes = getAllJaxRsTypes(project, utils, monitor); + if (jaxrsTypes == null || monitor.isCanceled()) { + return; + } + List methodsInfo = new ArrayList<>(); + for (PsiClass typeRoot : jaxrsTypes) { + IJaxRsInfoProvider provider = getProviderForType(typeRoot, project, monitor); + if (provider != null) { + methodsInfo.addAll(provider.getJaxRsMethodInfo(typeRoot.getContainingFile(), jaxrsContext, utils, monitor)); + } + if (monitor.isCanceled()) { + return; + } + } + + methodsInfo.forEach(methodInfo -> { + try { + symbols.add(createSymbol(methodInfo, utils)); + } catch (Exception e) { + LOGGER.log(Level.WARNING, "failed to create workspace symbol for jax-rs method", e); + } + }); + } + + /** + * Returns the provider that can provide JAX-RS method info for the given class, + * or null if no provider can provide info. + * + * @param typeRoot the class to collect JAX-RS method info for + * @param project the Module project + * @param monitor the progress monitor + * @return the provider that can provide JAX-RS method info for the given class, + * or null if no provider can provide info + */ + private static IJaxRsInfoProvider getProviderForType(PsiClass typeRoot, Module project, ProgressIndicator monitor) { + for (IJaxRsInfoProvider provider : JaxRsInfoProviderRegistry.getInstance().getProviders()) { + if (provider.canProvideJaxRsMethodInfoForClass(typeRoot.getContainingFile(), project, monitor)) { + return provider; + } + } + LOGGER.severe("Attempted to collect JAX-RS info for " + typeRoot.getQualifiedName() + + ", but no participant was suitable, despite the fact that an earlier check found a suitable participant"); + return null; + } + + private static Set getAllJaxRsTypes(Module javaProject, IPsiUtils utils, ProgressIndicator monitor) { + Set jaxrsTypes = new HashSet<>(); + for (IJaxRsInfoProvider provider : JaxRsInfoProviderRegistry.getInstance().getProviders()) { + jaxrsTypes.addAll(provider.getAllJaxRsClasses(javaProject, utils,monitor)); + if (monitor.isCanceled()) { + return null; + } + } + return jaxrsTypes; + } + + private static SymbolInformation createSymbol(JaxRsMethodInfo methodInfo, IPsiUtils utils) throws MalformedURLException { + TextRange sourceRange = methodInfo.getJavaMethod().getNameIdentifier().getTextRange(); + Range r = utils.toRange(methodInfo.getJavaMethod(), sourceRange.getStartOffset(), sourceRange.getLength()); + Location location = new Location(methodInfo.getDocumentUri(), r); + + StringBuilder nameBuilder = new StringBuilder("@"); + URL url = new URL(methodInfo.getUrl()); + String path = url.getPath(); + nameBuilder.append(path); + nameBuilder.append(": "); + nameBuilder.append(methodInfo.getHttpMethod()); + + SymbolInformation symbol = new SymbolInformation(); + symbol.setName(nameBuilder.toString()); + symbol.setKind(SymbolKind.Method); + symbol.setLocation(location); + return symbol; + } + +} \ No newline at end of file diff --git a/src/main/java/com/redhat/devtools/intellij/quarkus/lsp/QuarkusLanguageClient.java b/src/main/java/com/redhat/devtools/intellij/quarkus/lsp/QuarkusLanguageClient.java index 8a30793d1..760d42dd8 100644 --- a/src/main/java/com/redhat/devtools/intellij/quarkus/lsp/QuarkusLanguageClient.java +++ b/src/main/java/com/redhat/devtools/intellij/quarkus/lsp/QuarkusLanguageClient.java @@ -267,8 +267,8 @@ public CompletableFuture getJavaCursorContext(MicroProf @Override public CompletableFuture> getJavaWorkspaceSymbols(String projectUri) { - //Workspace symbols not supported yet https://github.com/redhat-developer/intellij-quarkus/issues/808 - return CompletableFuture.completedFuture(null); + var coalesceBy = new CoalesceByKey("microprofile/java/workspaceSymbols", projectUri); + return runAsBackground("Computing Java workspace symbols", monitor -> PropertiesManagerForJava.getInstance().workspaceSymbols(projectUri, PsiUtilsLSImpl.getInstance(getProject()), monitor), coalesceBy); } @Override diff --git a/src/main/java/com/redhat/microprofile/psi/internal/quarkus/renarde/java/RenardeJaxRsInfoProvider.java b/src/main/java/com/redhat/microprofile/psi/internal/quarkus/renarde/java/RenardeJaxRsInfoProvider.java index c03f75ee6..4265f6676 100644 --- a/src/main/java/com/redhat/microprofile/psi/internal/quarkus/renarde/java/RenardeJaxRsInfoProvider.java +++ b/src/main/java/com/redhat/microprofile/psi/internal/quarkus/renarde/java/RenardeJaxRsInfoProvider.java @@ -20,6 +20,7 @@ import com.redhat.devtools.lsp4ij.LSPIJUtils; import com.redhat.devtools.intellij.lsp4mp4ij.psi.core.jaxrs.*; import com.redhat.devtools.intellij.lsp4mp4ij.psi.core.utils.IPsiUtils; +import org.jetbrains.annotations.NotNull; import java.util.ArrayList; import java.util.Collections; @@ -40,18 +41,24 @@ public class RenardeJaxRsInfoProvider extends KeyedLazyInstanceEP getAllJaxRsClasses(Module javaProject, ProgressIndicator monitor) { + public @NotNull Set getAllJaxRsClasses(@NotNull Module javaProject, + @NotNull IPsiUtils utils, + @NotNull ProgressIndicator monitor) { return RenardeUtils.getAllControllerClasses(javaProject, monitor); } @Override - public List getJaxRsMethodInfo(PsiFile typeRoot, JaxRsContext jaxrsContext, IPsiUtils utils, - ProgressIndicator monitor) { + public @NotNull List getJaxRsMethodInfo(@NotNull PsiFile typeRoot, + @NotNull JaxRsContext jaxrsContext, + @NotNull IPsiUtils utils, + @NotNull ProgressIndicator monitor) { try { PsiClass type = findFirstClass(typeRoot); if (type == null) { diff --git a/src/main/java/com/redhat/microprofile/psi/internal/quarkus/renarde/java/RenardeUtils.java b/src/main/java/com/redhat/microprofile/psi/internal/quarkus/renarde/java/RenardeUtils.java index 6636280a3..b56cef745 100644 --- a/src/main/java/com/redhat/microprofile/psi/internal/quarkus/renarde/java/RenardeUtils.java +++ b/src/main/java/com/redhat/microprofile/psi/internal/quarkus/renarde/java/RenardeUtils.java @@ -12,6 +12,8 @@ package com.redhat.microprofile.psi.internal.quarkus.renarde.java; import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.Set; import com.intellij.openapi.module.Module; @@ -19,6 +21,9 @@ import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.psi.PsiClass; import com.intellij.psi.PsiFile; +import com.intellij.psi.impl.source.PsiClassReferenceType; +import com.intellij.psi.search.ProjectScope; +import com.intellij.psi.search.searches.DefinitionsScopedSearch; import com.intellij.psi.util.PsiTreeUtil; import com.redhat.devtools.intellij.lsp4mp4ij.psi.core.utils.PsiTypeUtils; import org.jetbrains.annotations.NotNull; @@ -87,8 +92,19 @@ public static boolean isControllerClass(@NotNull Module project, @Nullable PsiCl * Controller class */ public static Set getAllControllerClasses(Module project, ProgressIndicator monitor) { - // TODO: implement when LSP4IJ will support workspace symbols - return Collections.emptySet(); + PsiClass renardeControllerType = PsiTypeUtils.findType(project, RenardeConstants.CONTROLLER_FQN); + if (renardeControllerType == null) { + return Collections.emptySet(); + } + + Set types = new HashSet<>(); + DefinitionsScopedSearch.search(renardeControllerType, ProjectScope.getAllScope(project.getProject()), true) + .forEach(element -> { + if (element instanceof PsiClass renardeType) { + types.add(renardeType); + } + }); + return types; } } \ No newline at end of file diff --git a/src/main/java/com/redhat/microprofile/psi/internal/quarkus/route/java/ReactiveRouteJaxRsInfoProvider.java b/src/main/java/com/redhat/microprofile/psi/internal/quarkus/route/java/ReactiveRouteJaxRsInfoProvider.java index b902a8e29..03bd71c43 100644 --- a/src/main/java/com/redhat/microprofile/psi/internal/quarkus/route/java/ReactiveRouteJaxRsInfoProvider.java +++ b/src/main/java/com/redhat/microprofile/psi/internal/quarkus/route/java/ReactiveRouteJaxRsInfoProvider.java @@ -21,6 +21,7 @@ import com.redhat.devtools.intellij.lsp4mp4ij.psi.core.jaxrs.*; import com.redhat.devtools.intellij.lsp4mp4ij.psi.core.utils.IPsiUtils; import com.redhat.devtools.intellij.lsp4mp4ij.psi.core.utils.PsiTypeUtils; +import org.jetbrains.annotations.NotNull; import java.util.ArrayList; import java.util.Collections; @@ -44,12 +45,12 @@ public class ReactiveRouteJaxRsInfoProvider extends KeyedLazyInstanceEP getAllJaxRsClasses(Module javaProject, ProgressIndicator monitor) { + public Set getAllJaxRsClasses(@NotNull Module javaProject, @NotNull IPsiUtils utils, @NotNull ProgressIndicator monitor) { // TODO: implement when LSP4IJ will support workspace symbols return Collections.emptySet(); } diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index a3facf244..521a1461c 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -432,7 +432,8 @@ - + @@ -538,6 +539,8 @@ + + diff --git a/src/test/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/core/MicroProfileForJavaAssert.java b/src/test/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/core/MicroProfileForJavaAssert.java index 73f336857..d7e4cd713 100644 --- a/src/test/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/core/MicroProfileForJavaAssert.java +++ b/src/test/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/core/MicroProfileForJavaAssert.java @@ -16,6 +16,7 @@ import com.intellij.openapi.progress.EmptyProgressIndicator; import com.redhat.devtools.intellij.lsp4mp4ij.psi.core.java.diagnostics.IJavaErrorCode; import com.redhat.devtools.intellij.lsp4mp4ij.psi.core.utils.IPsiUtils; +import com.redhat.devtools.intellij.lsp4mp4ij.psi.core.utils.PsiMicroProfileUtils; import org.eclipse.lsp4j.*; import org.eclipse.lsp4j.jsonrpc.messages.Either; import org.eclipse.lsp4mp.commons.*; @@ -329,4 +330,66 @@ public static CodeLens cl(String title, String commandId, Range range) { codeLens.setCommand(new Command(title, commandId, Collections.singletonList(title))); return codeLens; } + + + // Assert for WorkspaceSymbol + + /** + * Returns a new symbol information. + * + * @param name the name of the symbol + * @param range the range of the symbol + * @return a new symbol information + */ + public static SymbolInformation si(String name, Range range) { + SymbolInformation symbolInformation = new SymbolInformation(); + symbolInformation.setName(name); + Location location = new Location("", range); + symbolInformation.setLocation(location); + return symbolInformation; + } + + /** + * Asserts that the actual workspace symbols for the given project are the same + * as the list of expected workspace symbols. + * + * @param javaProject the project to check the workspace symbols of + * @param utils the jdt utils + * @param expected the expected workspace symbols + */ + public static void assertWorkspaceSymbols(Module javaProject, IPsiUtils utils, SymbolInformation... expected) { + List actual = PropertiesManagerForJava.getInstance() + .workspaceSymbols(PsiMicroProfileUtils.getProjectURI(javaProject), utils, new EmptyProgressIndicator()); + MicroProfileForJavaAssert.assertWorkspaceSymbols(Arrays.asList(expected), actual); + } + + /** + * Asserts that the given lists of workspace symbols are the same. + * + * @param expected the expected symbols + * @param actual the actual symbols + */ + public static void assertWorkspaceSymbols(List expected, List actual) { + assertEquals(expected.size(), actual.size()); + Collections.sort(expected, (si1, si2) -> si1.getName().compareTo(si2.getName())); + Collections.sort(actual, (si1, si2) -> si1.getName().compareTo(si2.getName())); + for (int i = 0; i < expected.size(); i++) { + assertSymbolInformation(expected.get(i), actual.get(i)); + } + } + + /** + * Asserts that the expected and actual symbol informations' name and range are + * the same. + * + * Doesn't check any of the other properties. For instance, the URI is avoided + * since this will change between systems + * + * @param expected the expected symbol information + * @param actual the actual symbol information + */ + public static void assertSymbolInformation(SymbolInformation expected, SymbolInformation actual) { + assertEquals(expected.getName(), actual.getName()); + assertEquals(expected.getLocation().getRange(), actual.getLocation().getRange()); + } } diff --git a/src/test/java/com/redhat/microprofile/psi/quarkus/jaxrs/JaxRsWorkspaceSymbolParticipantTest.java b/src/test/java/com/redhat/microprofile/psi/quarkus/jaxrs/JaxRsWorkspaceSymbolParticipantTest.java new file mode 100644 index 000000000..bee97d161 --- /dev/null +++ b/src/test/java/com/redhat/microprofile/psi/quarkus/jaxrs/JaxRsWorkspaceSymbolParticipantTest.java @@ -0,0 +1,53 @@ +/******************************************************************************* +* Copyright (c) 2024 Red Hat Inc. and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +* which is available at https://www.apache.org/licenses/LICENSE-2.0. +* +* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package com.redhat.microprofile.psi.quarkus.jaxrs; + +import static com.redhat.devtools.intellij.lsp4mp4ij.psi.core.MicroProfileForJavaAssert.assertWorkspaceSymbols; +import static com.redhat.devtools.intellij.lsp4mp4ij.psi.core.MicroProfileForJavaAssert.si; +import static com.redhat.devtools.intellij.lsp4mp4ij.psi.core.MicroProfileForJavaAssert.r; + +import com.intellij.openapi.module.Module; +import com.redhat.devtools.intellij.lsp4mp4ij.psi.core.LSP4MPMavenModuleImportingTestCase; +import com.redhat.devtools.intellij.lsp4mp4ij.psi.core.MicroProfileMavenProjectName; +import com.redhat.devtools.intellij.lsp4mp4ij.psi.core.utils.IPsiUtils; +import com.redhat.devtools.intellij.lsp4mp4ij.psi.internal.core.ls.PsiUtilsLSImpl; +import org.junit.Test; + +/** + * Tests for JaxRsWorkspaceSymbolParticipantTest. + */ +public class JaxRsWorkspaceSymbolParticipantTest extends LSP4MPMavenModuleImportingTestCase { + + @Test + public void testConfigQuickstart() throws Exception { + Module javaProject = loadMavenProject(MicroProfileMavenProjectName.config_quickstart); + + assertWorkspaceSymbols(javaProject, PsiUtilsLSImpl.getInstance(myProject), // + si("@/greeting/hello4: GET", r(40, 18, 24)), // + si("@/greeting/constructor: GET", r(34, 18, 23)), // + si("@/greeting/hello: GET", r(33, 18, 24)), // + si("@/greeting: GET", r(26, 18, 23)), // + si("@/greeting/method: GET", r(38, 18, 23)), // + si("@/greeting/hello5: PATCH", r(46, 18, 24))); + } + + @Test + public void testOpenLiberty() throws Exception { + Module javaProject = loadMavenProject(MicroProfileMavenProjectName.open_liberty); + + assertWorkspaceSymbols(javaProject, PsiUtilsLSImpl.getInstance(myProject), // + si("@/api/api/resource: GET", r(13, 15, 20))); + } + +} \ No newline at end of file diff --git a/src/test/java/com/redhat/microprofile/psi/quarkus/renarde/RenardeJaxRsTest.java b/src/test/java/com/redhat/microprofile/psi/quarkus/renarde/RenardeJaxRsTest.java index 447b6eb24..94fd2a1e6 100644 --- a/src/test/java/com/redhat/microprofile/psi/quarkus/renarde/RenardeJaxRsTest.java +++ b/src/test/java/com/redhat/microprofile/psi/quarkus/renarde/RenardeJaxRsTest.java @@ -1,14 +1,14 @@ /******************************************************************************* -* Copyright (c) 2023 Red Hat Inc. and others. -* All rights reserved. This program and the accompanying materials -* which accompanies this distribution, and is available at -* http://www.eclipse.org/legal/epl-v20.html -* -* SPDX-License-Identifier: EPL-2.0 -* -* Contributors: -* Red Hat Inc. - initial API and implementation -*******************************************************************************/ + * Copyright (c) 2023 Red Hat Inc. and others. + * All rights reserved. This program and the accompanying materials + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v20.html + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Red Hat Inc. - initial API and implementation + *******************************************************************************/ package com.redhat.microprofile.psi.quarkus.renarde; import com.intellij.openapi.module.Module; @@ -26,65 +26,72 @@ */ public class RenardeJaxRsTest extends QuarkusMavenModuleImportingTestCase { - @Test - public void testCodeLens() throws Exception { - Module javaProject = loadMavenProject(QuarkusMavenProjectName.quarkus_renarde_todo); - assertNotNull(javaProject); + @Test + public void testCodeLens() throws Exception { + Module javaProject = loadMavenProject(QuarkusMavenProjectName.quarkus_renarde_todo); + assertNotNull(javaProject); - MicroProfileJavaCodeLensParams params = new MicroProfileJavaCodeLensParams(); - params.setCheckServerAvailable(false); - String javaFileUri = getFileUri("src/main/java/rest/Application.java", javaProject); - params.setUri(javaFileUri); - params.setUrlCodeLensEnabled(true); + MicroProfileJavaCodeLensParams params = new MicroProfileJavaCodeLensParams(); + params.setCheckServerAvailable(false); + String javaFileUri = getFileUri("src/main/java/rest/Application.java", javaProject); + params.setUri(javaFileUri); + params.setUrlCodeLensEnabled(true); - assertCodeLens(params, PsiUtilsLSImpl.getInstance(myProject), // - cl("http://localhost:8080/", "", r(20, 4, 4)), // - cl("http://localhost:8080/about", "", r(25, 4, 4)), // - cl("http://localhost:8080/Application/test", "", r(30, 4, 4)), // - cl("http://localhost:8080/Application/endpoint", "", r(34, 4, 4))); - } + assertCodeLens(params, PsiUtilsLSImpl.getInstance(myProject), // + cl("http://localhost:8080/", "", r(20, 4, 4)), // + cl("http://localhost:8080/about", "", r(25, 4, 4)), // + cl("http://localhost:8080/Application/test", "", r(30, 4, 4)), // + cl("http://localhost:8080/Application/endpoint", "", r(34, 4, 4))); + } - @Test - public void testAbsolutePathCodeLens() throws Exception { - Module javaProject = loadMavenProject(QuarkusMavenProjectName.quarkus_renarde_todo); + @Test + public void testAbsolutePathCodeLens() throws Exception { + Module javaProject = loadMavenProject(QuarkusMavenProjectName.quarkus_renarde_todo); - assertNotNull(javaProject); + assertNotNull(javaProject); - MicroProfileJavaCodeLensParams params = new MicroProfileJavaCodeLensParams(); - params.setCheckServerAvailable(false); - String javaFileUri = getFileUri("src/main/java/rest/Game.java", javaProject); - params.setUri(javaFileUri); - params.setUrlCodeLensEnabled(true); + MicroProfileJavaCodeLensParams params = new MicroProfileJavaCodeLensParams(); + params.setCheckServerAvailable(false); + String javaFileUri = getFileUri("src/main/java/rest/Game.java", javaProject); + params.setUri(javaFileUri); + params.setUrlCodeLensEnabled(true); - assertCodeLens(params, PsiUtilsLSImpl.getInstance(myProject), // - cl("http://localhost:8080/play/id", "", r(9, 4, 4)), - cl("http://localhost:8080/play/start", "", r(13, 4, 4))); - } + assertCodeLens(params, PsiUtilsLSImpl.getInstance(myProject), // + cl("http://localhost:8080/play/id", "", r(9, 4, 4)), + cl("http://localhost:8080/play/start", "", r(13, 4, 4))); + } - /*@Test - public void workspaceSymbols() throws Exception { - IJavaProject javaProject = loadMavenProject(quarkus_renarde_todo); + @Test + public void testWorkspaceSymbols() throws Exception { + Module javaProject = loadMavenProject(QuarkusMavenProjectName.quarkus_renarde_todo); - assertNotNull(javaProject); + assertNotNull(javaProject); - assertWorkspaceSymbols(javaProject, JDT_UTILS, // - si("@/: GET", r(20, 28, 33)), // - si("@/Application/endpoint: GET", r(34, 18, 26)), // - si("@/Application/test: POST", r(30, 16, 20)), // - si("@/Login/complete: POST", r(174, 20, 28)), // - si("@/Login/confirm: GET", r(138, 28, 35)), // - si("@/Login/login: GET", r(57, 28, 33)), // - si("@/Login/logoutFirst: GET", r(153, 28, 39)), // - si("@/Login/manualLogin: POST", r(73, 20, 31)), // - si("@/Login/register: POST", r(118, 28, 36)), // - si("@/Login/welcome: GET", r(65, 28, 35)), // - si("@/Todos/add: POST", r(59, 16, 19)), // - si("@/Todos/delete: POST", r(35, 16, 22)), // - si("@/Todos/done: POST", r(46, 16, 20)), // - si("@/Todos/index: GET", r(29, 28, 33)), // - si("@/about: GET", r(25, 28, 33)), // - si("@/play/id: GET", r(9, 18, 26)), // - si("@/play/start: GET", r(13, 18, 23))); - }*/ + assertWorkspaceSymbols(javaProject, PsiUtilsLSImpl.getInstance(myProject), // + si("@/: GET", r(20, 28, 33)), // + si("@/Application/endpoint: GET", r(34, 18, 26)), // + si("@/Application/test: POST", r(30, 16, 20)), // + si("@/Login/complete: POST", r(174, 20, 28)), // + si("@/Login/confirm: GET", r(138, 28, 35)), // + si("@/Login/login: GET", r(57, 28, 33)), // + si("@/Login/logoutFirst: GET", r(153, 28, 39)), // + si("@/Login/manualLogin: POST", r(73, 20, 31)), // + si("@/Login/register: POST", r(118, 28, 36)), // + si("@/Login/welcome: GET", r(65, 28, 35)), // + si("@/Todos/add: POST", r(59, 16, 19)), // + si("@/Todos/delete: POST", r(35, 16, 22)), // + si("@/Todos/done: POST", r(46, 16, 20)), // + si("@/Todos/index: GET", r(29, 28, 33)), // + si("@/about: GET", r(25, 28, 33)), // + si("@/play/id: GET", r(9, 18, 26)), // + si("@/play/start: GET", r(13, 18, 23)), // + // From Renarde JAR + si("@/_renarde/security/login-{provider}: GET", r(24, 16, 30)), + si("@/_renarde/security/logout: GET", r(27, 37, 43)), + si("@/_renarde/security/github-success: GET", r(31, 16, 29)), + si("@/_renarde/security/twitter-success: GET", r(35, 16, 30)), + si("@/_renarde/security/oidc-success: GET", r(39, 16, 30)), + si("@/_renarde/security/oidc-success: POST", r(44, 16, 31))); + } }