diff --git a/quarkus.jdt/com.redhat.quarkus.jdt.core/META-INF/MANIFEST.MF b/quarkus.jdt/com.redhat.quarkus.jdt.core/META-INF/MANIFEST.MF index be91fcba1..9b3385b06 100644 --- a/quarkus.jdt/com.redhat.quarkus.jdt.core/META-INF/MANIFEST.MF +++ b/quarkus.jdt/com.redhat.quarkus.jdt.core/META-INF/MANIFEST.MF @@ -10,7 +10,8 @@ Require-Bundle: org.eclipse.core.runtime, org.eclipse.core.resources, org.eclipse.m2e.core, org.eclipse.m2e.maven.runtime, - org.eclipse.jdt.ls.core;resolution:=optional + org.eclipse.jdt.ls.core;resolution:=optional, + org.eclipse.lsp4j Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Bundle-ActivationPolicy: lazy Export-Package: com.redhat.quarkus.commons, diff --git a/quarkus.jdt/com.redhat.quarkus.jdt.core/plugin.xml b/quarkus.jdt/com.redhat.quarkus.jdt.core/plugin.xml index e0b6b02ca..3bf022a90 100644 --- a/quarkus.jdt/com.redhat.quarkus.jdt.core/plugin.xml +++ b/quarkus.jdt/com.redhat.quarkus.jdt.core/plugin.xml @@ -5,6 +5,7 @@ + diff --git a/quarkus.jdt/com.redhat.quarkus.jdt.core/src/main/java/com/redhat/quarkus/commons/QuarkusPropertyDefinitionParams.java b/quarkus.jdt/com.redhat.quarkus.jdt.core/src/main/java/com/redhat/quarkus/commons/QuarkusPropertyDefinitionParams.java new file mode 100644 index 000000000..7033f11d9 --- /dev/null +++ b/quarkus.jdt/com.redhat.quarkus.jdt.core/src/main/java/com/redhat/quarkus/commons/QuarkusPropertyDefinitionParams.java @@ -0,0 +1,65 @@ +/******************************************************************************* +* Copyright (c) 2019 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 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package com.redhat.quarkus.commons; + +/** + * Quarkus property definition parameters to retrieve the definition of the + * Quarkus property in Java class field. + * + * @author Angelo ZERR + * + */ +public class QuarkusPropertyDefinitionParams { + + private String uri; + + private String propertySource; + + /** + * Returns the application.properties URI. + * + * @return the application.properties URI + */ + public String getUri() { + return uri; + } + + /** + * Set the application.properties URI + * + * @param uri the application.properties URI + */ + public void setUri(String uri) { + this.uri = uri; + } + + /** + * Returns the Quarkus property source. This source contains the class name and + * field name where Quarkus property is defined like + * io.quarkus.deployment.ApplicationConfig#name. + * + * @return the Quarkus property source. + */ + public String getPropertySource() { + return propertySource; + } + + /** + * Set the Quarkus property source. This source contains the class name and + * field name where Quarkus property is defined like + * io.quarkus.deployment.ApplicationConfig#name. + * + * @param propertySource the Quarkus property source. + */ + public void setPropertySource(String propertySource) { + this.propertySource = propertySource; + } + +} diff --git a/quarkus.jdt/com.redhat.quarkus.jdt.core/src/main/java/com/redhat/quarkus/jdt/core/JDTQuarkusManager.java b/quarkus.jdt/com.redhat.quarkus.jdt.core/src/main/java/com/redhat/quarkus/jdt/core/JDTQuarkusManager.java index 58e89f11d..1fab78443 100644 --- a/quarkus.jdt/com.redhat.quarkus.jdt.core/src/main/java/com/redhat/quarkus/jdt/core/JDTQuarkusManager.java +++ b/quarkus.jdt/com.redhat.quarkus.jdt.core/src/main/java/com/redhat/quarkus/jdt/core/JDTQuarkusManager.java @@ -61,6 +61,7 @@ import com.redhat.quarkus.commons.ExtendedConfigDescriptionBuildItem; import com.redhat.quarkus.commons.QuarkusProjectInfo; import com.redhat.quarkus.commons.QuarkusPropertiesScope; +import com.redhat.quarkus.jdt.internal.core.QuarkusDeploymentJavaProject; import com.redhat.quarkus.jdt.internal.core.utils.JDTQuarkusSearchUtils; import com.redhat.quarkus.jdt.internal.core.utils.JDTQuarkusUtils; @@ -90,6 +91,51 @@ private JDTQuarkusManager() { } + /** + * Returns the Java field from the given property source + * + * @param file the application.properties file + * @param propertySource the property source to find + * @param progress the progress monitor. + * @return the Java field from the given property source + * @throws JavaModelException + * @throws CoreException + */ + public IField findDeclaredQuarkusProperty(IFile file, String propertySource, IProgressMonitor progress) + throws JavaModelException, CoreException { + String projectName = file.getProject().getName(); + IJavaProject javaProject = JavaModelManager.getJavaModelManager().getJavaModel().getJavaProject(projectName); + return findDeclaredQuarkusProperty(javaProject, propertySource, progress); + } + + /** + * Returns the Java field from the given property source + * + * @param javaProject the Java project + * @param propertySource the property source to find + * @param progress the progress monitor. + * @return the Java field from the given property sources + * @throws JavaModelException + */ + public IField findDeclaredQuarkusProperty(IJavaProject javaProject, String propertySource, + IProgressMonitor progress) throws JavaModelException { + int index = propertySource.indexOf('#'); + if (index == -1) { + return null; + } + String className = propertySource.substring(0, index); + String fieldName = propertySource.substring(index + 1, propertySource.length()); + // Try to find type with standard classpath + IType type = javaProject.findType(className, progress); + if (type == null) { + // Not found, type could be included in deployment JAR which is not in classpath + // Try to find type from deployment JAR + type = new QuarkusDeploymentJavaProject(javaProject, QuarkusDeploymentJavaProject.MAVEN_ARTIFACT_RESOLVER, + false).findType(className, progress); + } + return type.getField(fieldName); + } + public QuarkusProjectInfo getQuarkusProjectInfo(IFile file, QuarkusPropertiesScope propertiesScope, DocumentationConverter converter, IProgressMonitor progress) throws JavaModelException, CoreException { String projectName = file.getProject().getName(); diff --git a/quarkus.jdt/com.redhat.quarkus.jdt.core/src/main/java/com/redhat/quarkus/jdt/internal/core/MavenArtifactResolver.java b/quarkus.jdt/com.redhat.quarkus.jdt.core/src/main/java/com/redhat/quarkus/jdt/internal/core/MavenArtifactResolver.java new file mode 100644 index 000000000..2dd709a18 --- /dev/null +++ b/quarkus.jdt/com.redhat.quarkus.jdt.core/src/main/java/com/redhat/quarkus/jdt/internal/core/MavenArtifactResolver.java @@ -0,0 +1,47 @@ +/******************************************************************************* +* Copyright (c) 2019 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 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package com.redhat.quarkus.jdt.internal.core; + +import java.io.File; + +import com.redhat.quarkus.jdt.internal.core.QuarkusDeploymentJavaProject.ArtifactResolver; +import com.redhat.quarkus.jdt.internal.core.utils.DependencyUtil; + +/** + * Maven artifact resolver used to download JAR and JAR sources with maven. + * + * @author Angelo ZERR + * + */ +public class MavenArtifactResolver implements ArtifactResolver { + + @Override + public String getArtifact(String groupId, String artifactId, String version) { + File jarFile = null; + try { + jarFile = DependencyUtil.getArtifact(groupId, artifactId, version, null); + } catch (Exception e) { + return null; + } + return jarFile != null ? jarFile.toString() : null; + } + + @Override + public String getSources(String groupId, String artifactId, String version) { + File jarFile = null; + try { + jarFile = DependencyUtil.getSources(groupId, artifactId, version); + } catch (Exception e) { + return null; + } + return jarFile != null ? jarFile.toString() : null; + } + +} diff --git a/quarkus.jdt/com.redhat.quarkus.jdt.core/src/main/java/com/redhat/quarkus/jdt/internal/core/QuarkusDeploymentJavaProject.java b/quarkus.jdt/com.redhat.quarkus.jdt.core/src/main/java/com/redhat/quarkus/jdt/internal/core/QuarkusDeploymentJavaProject.java index 98efdc2d7..d747d6af9 100644 --- a/quarkus.jdt/com.redhat.quarkus.jdt.core/src/main/java/com/redhat/quarkus/jdt/internal/core/QuarkusDeploymentJavaProject.java +++ b/quarkus.jdt/com.redhat.quarkus.jdt.core/src/main/java/com/redhat/quarkus/jdt/internal/core/QuarkusDeploymentJavaProject.java @@ -11,11 +11,11 @@ import static com.redhat.quarkus.jdt.internal.core.QuarkusConstants.QUARKUS_EXTENSION_PROPERTIES; -import java.io.File; import java.util.ArrayList; import java.util.List; import java.util.Properties; +import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.Path; import org.eclipse.jdt.core.IClasspathEntry; import org.eclipse.jdt.core.IJarEntryResource; @@ -27,7 +27,6 @@ import org.eclipse.jdt.internal.core.ExternalJavaProject; import com.redhat.quarkus.commons.QuarkusPropertiesScope; -import com.redhat.quarkus.jdt.internal.core.utils.DependencyUtil; import com.redhat.quarkus.jdt.internal.core.utils.JDTQuarkusSearchUtils; /** @@ -42,26 +41,19 @@ public class QuarkusDeploymentJavaProject extends ExternalJavaProject { * Artifact resolver API * */ - @FunctionalInterface public static interface ArtifactResolver { - String resolve(String groupId, String artifactId, String version); + String getArtifact(String groupId, String artifactId, String version); + + String getSources(String groupId, String artifactId, String version); } - public static final ArtifactResolver MAVEN_ARTIFACT_RESOLVER = (groupId, artifactId, version) -> { - File jarFile = null; - try { - jarFile = DependencyUtil.getArtifact(groupId, artifactId, version, null); - } catch (Exception e) { - return null; - } - return jarFile != null ? jarFile.toString() : null; - }; + public static final ArtifactResolver MAVEN_ARTIFACT_RESOLVER = new MavenArtifactResolver(); private final IJavaProject rootProject; - public QuarkusDeploymentJavaProject(IJavaProject rootProject, ArtifactResolver artifactResolver, boolean excludeTestCode) - throws JavaModelException { + public QuarkusDeploymentJavaProject(IJavaProject rootProject, ArtifactResolver artifactResolver, + boolean excludeTestCode) throws JavaModelException { super(createDeploymentClasspath(rootProject, artifactResolver, excludeTestCode)); this.rootProject = rootProject; } @@ -72,12 +64,13 @@ public QuarkusDeploymentJavaProject(IJavaProject rootProject, ArtifactResolver a * @param project the quarkus project * @param artifactResolver the artifact resolver to use to download deployment * JARs. - * @param excludeTestCode + * @param excludeTestCode + * @param downloadSources * @return the classpath of deployment JARs. * @throws JavaModelException */ - private static IClasspathEntry[] createDeploymentClasspath(IJavaProject project, ArtifactResolver artifactResolver, boolean excludeTestCode) - throws JavaModelException { + private static IClasspathEntry[] createDeploymentClasspath(IJavaProject project, ArtifactResolver artifactResolver, + boolean excludeTestCode) throws JavaModelException { List externalJarEntries = new ArrayList<>(); IClasspathEntry[] entries = project.getResolvedClasspath(true); @@ -104,9 +97,17 @@ private static IClasspathEntry[] createDeploymentClasspath(IJavaProject project, String groupId = result[0]; String artifactId = result[1]; String version = result[2]; - String jarFile = artifactResolver.resolve(groupId, artifactId, version); + // Get or download deployment JAR + String jarFile = artifactResolver.getArtifact(groupId, artifactId, version); if (jarFile != null) { - externalJarEntries.add(JavaCore.newLibraryEntry(new Path(jarFile), null, null)); + IPath sourceAttachmentPath = null; + // Get or download deployment sources JAR + String sourceJarFile = artifactResolver.getSources(groupId, artifactId, version); + if (sourceJarFile != null) { + sourceAttachmentPath = new Path(sourceJarFile); + } + externalJarEntries + .add(JavaCore.newLibraryEntry(new Path(jarFile), sourceAttachmentPath, null)); } } } @@ -153,5 +154,4 @@ public IJavaElement[] getElementsToSearch(QuarkusPropertiesScope propertiesScope } return elements; } - } diff --git a/quarkus.jdt/com.redhat.quarkus.jdt.core/src/main/java/com/redhat/quarkus/jdt/internal/core/ls/QuarkusDelegateCommandHandler.java b/quarkus.jdt/com.redhat.quarkus.jdt.core/src/main/java/com/redhat/quarkus/jdt/internal/core/ls/QuarkusDelegateCommandHandler.java index 946528fbf..9fa155419 100644 --- a/quarkus.jdt/com.redhat.quarkus.jdt.core/src/main/java/com/redhat/quarkus/jdt/internal/core/ls/QuarkusDelegateCommandHandler.java +++ b/quarkus.jdt/com.redhat.quarkus.jdt.core/src/main/java/com/redhat/quarkus/jdt/internal/core/ls/QuarkusDelegateCommandHandler.java @@ -13,14 +13,23 @@ import java.util.List; import java.util.Map; +import java.util.Optional; import org.eclipse.core.resources.IFile; import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jdt.core.IClassFile; +import org.eclipse.jdt.core.IField; +import org.eclipse.jdt.core.IJavaElement; +import org.eclipse.jdt.core.IPackageFragmentRoot; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.ls.core.internal.IDelegateCommandHandler; import org.eclipse.jdt.ls.core.internal.JDTUtils; import org.eclipse.jdt.ls.core.internal.JavaLanguageServerPlugin; +import org.eclipse.jdt.ls.core.internal.managers.IBuildSupport; +import org.eclipse.lsp4j.Location; +import org.eclipse.m2e.core.embedder.ArtifactKey; import com.redhat.quarkus.commons.QuarkusPropertiesScope; import com.redhat.quarkus.jdt.core.DocumentationConverter; @@ -37,6 +46,8 @@ public class QuarkusDelegateCommandHandler implements IDelegateCommandHandler { public static final String PROJECT_INFO_COMMAND_ID = "quarkus.java.projectInfo"; + public static final String PROPERTY_DEFINITION_COMMAND_ID = "quarkus.java.propertyDefinition"; + private static final IQuarkusPropertiesChangedListener LISTENER = (event) -> { JavaLanguageServerPlugin.getInstance().getClientConnection() .executeClientCommand(QUARKUS_PROPERTIES_CHANGED_COMMAND, event); @@ -57,8 +68,9 @@ public QuarkusDelegateCommandHandler() { public Object executeCommand(String commandId, List arguments, IProgressMonitor progress) throws Exception { if (PROJECT_INFO_COMMAND_ID.equals(commandId)) { return getQuarkusProjectInfo(arguments, commandId, progress); + } else if (PROPERTY_DEFINITION_COMMAND_ID.equals(commandId)) { + return findDeclaredQuarkusProperty(arguments, commandId, progress); } - throw new UnsupportedOperationException(String.format("Unsupported command '%s'!", commandId)); } @@ -118,4 +130,45 @@ private static DocumentationConverter getDocumentationConverter(String[] documen return DocumentationConverter.DEFAULT_CONVERTER; } + private static Location findDeclaredQuarkusProperty(List arguments, String commandId, + IProgressMonitor progress) throws CoreException { + Map obj = arguments.size() > 0 ? (Map) arguments.get(0) : null; + if (obj == null) { + throw new UnsupportedOperationException(String + .format("Command '%s' must be call with one QuarkusPropertyDefinitionParams argument!", commandId)); + } + // Get project name from the application.properties URI + String applicationPropertiesUri = (String) obj.get("uri"); + if (applicationPropertiesUri == null) { + throw new UnsupportedOperationException(String.format( + "Command '%s' must be call with required QuarkusPropertyDefinitionParams.uri (application.properties URI)!", + commandId)); + } + IFile file = JDTUtils.findFile(applicationPropertiesUri); + if (file == null) { + throw new UnsupportedOperationException( + String.format("Cannot find IFile for '%s'", applicationPropertiesUri)); + } + String propertySource = (String) obj.get("propertySource"); + if (propertySource == null) { + throw new UnsupportedOperationException(String.format( + "Command '%s' must be call with required QuarkusPropertyDefinitionParams.propertySource!", + commandId)); + } + IField field = JDTQuarkusManager.getInstance().findDeclaredQuarkusProperty(file, propertySource, progress); + if (field != null) { + IClassFile classFile = field.getClassFile(); + if (classFile != null) { + // Try to download source + Optional bs = JavaLanguageServerPlugin.getProjectsManager() + .getBuildSupport(file.getProject()); + if (bs.isPresent()) { + bs.get().discoverSource(classFile, progress); + } + } + return JDTUtils.toLocation(field); + } + return null; + } + } diff --git a/quarkus.ls/com.redhat.quarkus.ls/src/main/java/com/redhat/quarkus/commons/QuarkusPropertyDefinitionParams.java b/quarkus.ls/com.redhat.quarkus.ls/src/main/java/com/redhat/quarkus/commons/QuarkusPropertyDefinitionParams.java new file mode 100644 index 000000000..7033f11d9 --- /dev/null +++ b/quarkus.ls/com.redhat.quarkus.ls/src/main/java/com/redhat/quarkus/commons/QuarkusPropertyDefinitionParams.java @@ -0,0 +1,65 @@ +/******************************************************************************* +* Copyright (c) 2019 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 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package com.redhat.quarkus.commons; + +/** + * Quarkus property definition parameters to retrieve the definition of the + * Quarkus property in Java class field. + * + * @author Angelo ZERR + * + */ +public class QuarkusPropertyDefinitionParams { + + private String uri; + + private String propertySource; + + /** + * Returns the application.properties URI. + * + * @return the application.properties URI + */ + public String getUri() { + return uri; + } + + /** + * Set the application.properties URI + * + * @param uri the application.properties URI + */ + public void setUri(String uri) { + this.uri = uri; + } + + /** + * Returns the Quarkus property source. This source contains the class name and + * field name where Quarkus property is defined like + * io.quarkus.deployment.ApplicationConfig#name. + * + * @return the Quarkus property source. + */ + public String getPropertySource() { + return propertySource; + } + + /** + * Set the Quarkus property source. This source contains the class name and + * field name where Quarkus property is defined like + * io.quarkus.deployment.ApplicationConfig#name. + * + * @param propertySource the Quarkus property source. + */ + public void setPropertySource(String propertySource) { + this.propertySource = propertySource; + } + +} diff --git a/quarkus.ls/com.redhat.quarkus.ls/src/main/java/com/redhat/quarkus/ls/QuarkusProjectInfoCache.java b/quarkus.ls/com.redhat.quarkus.ls/src/main/java/com/redhat/quarkus/ls/QuarkusProjectInfoCache.java index 73331f50b..3aa86049e 100644 --- a/quarkus.ls/com.redhat.quarkus.ls/src/main/java/com/redhat/quarkus/ls/QuarkusProjectInfoCache.java +++ b/quarkus.ls/com.redhat.quarkus.ls/src/main/java/com/redhat/quarkus/ls/QuarkusProjectInfoCache.java @@ -23,6 +23,7 @@ import com.redhat.quarkus.commons.QuarkusProjectInfoParams; import com.redhat.quarkus.commons.QuarkusPropertiesChangeEvent; import com.redhat.quarkus.commons.QuarkusPropertiesScope; +import com.redhat.quarkus.ls.api.QuarkusProjectInfoProvider; /** * Quarkus project information cache. diff --git a/quarkus.ls/com.redhat.quarkus.ls/src/main/java/com/redhat/quarkus/ls/QuarkusTextDocumentService.java b/quarkus.ls/com.redhat.quarkus.ls/src/main/java/com/redhat/quarkus/ls/QuarkusTextDocumentService.java index e8c2c3ec7..4d017c025 100644 --- a/quarkus.ls/com.redhat.quarkus.ls/src/main/java/com/redhat/quarkus/ls/QuarkusTextDocumentService.java +++ b/quarkus.ls/com.redhat.quarkus.ls/src/main/java/com/redhat/quarkus/ls/QuarkusTextDocumentService.java @@ -29,6 +29,8 @@ import org.eclipse.lsp4j.DocumentSymbol; import org.eclipse.lsp4j.DocumentSymbolParams; import org.eclipse.lsp4j.Hover; +import org.eclipse.lsp4j.Location; +import org.eclipse.lsp4j.LocationLink; import org.eclipse.lsp4j.PublishDiagnosticsParams; import org.eclipse.lsp4j.SymbolInformation; import org.eclipse.lsp4j.TextDocumentClientCapabilities; @@ -64,6 +66,8 @@ public class QuarkusTextDocumentService implements TextDocumentService { private boolean hierarchicalDocumentSymbolSupport; + private boolean definitionLinkSupport; + public QuarkusTextDocumentService(QuarkusLanguageServer quarkusLanguageServer) { this.quarkusLanguageServer = quarkusLanguageServer; this.documents = new ModelTextDocuments((document, cancelChecker) -> { @@ -85,6 +89,9 @@ public void updateClientCapabilities(ClientCapabilities capabilities) { hierarchicalDocumentSymbolSupport = textDocumentClientCapabilities.getDocumentSymbol() != null && textDocumentClientCapabilities.getDocumentSymbol().getHierarchicalDocumentSymbolSupport() != null && textDocumentClientCapabilities.getDocumentSymbol().getHierarchicalDocumentSymbolSupport(); + definitionLinkSupport = textDocumentClientCapabilities.getDefinition() != null + && textDocumentClientCapabilities.getDefinition().getLinkSupport() != null + && textDocumentClientCapabilities.getDefinition().getLinkSupport(); } } @@ -176,6 +183,22 @@ public CompletableFuture>> docume }); } + @Override + public CompletableFuture, List>> definition( + TextDocumentPositionParams params) { + QuarkusProjectInfoParams projectInfoParams = createProjectInfoParams(params.getTextDocument(), null); + return getProjectInfoCache().getQuarkusProjectInfo(projectInfoParams).thenComposeAsync(projectInfo -> { + if (projectInfo.getProperties().isEmpty()) { + return null; + } + // then get the Properties model document + return getDocument(params.getTextDocument().getUri()).getModel().thenComposeAsync(document -> { + return getQuarkusLanguageService().findDefinition(document, params.getPosition(), projectInfo, + quarkusLanguageServer.getLanguageClient(), definitionLinkSupport); + }); + }); + } + private static QuarkusProjectInfoParams createProjectInfoParams(TextDocumentIdentifier id, List documentationFormat) { return createProjectInfoParams(id.getUri(), documentationFormat); diff --git a/quarkus.ls/com.redhat.quarkus.ls/src/main/java/com/redhat/quarkus/ls/api/QuarkusLanguageClientAPI.java b/quarkus.ls/com.redhat.quarkus.ls/src/main/java/com/redhat/quarkus/ls/api/QuarkusLanguageClientAPI.java index dbe2675f6..40ac6c585 100644 --- a/quarkus.ls/com.redhat.quarkus.ls/src/main/java/com/redhat/quarkus/ls/api/QuarkusLanguageClientAPI.java +++ b/quarkus.ls/com.redhat.quarkus.ls/src/main/java/com/redhat/quarkus/ls/api/QuarkusLanguageClientAPI.java @@ -11,13 +11,12 @@ import org.eclipse.lsp4j.services.LanguageClient; -import com.redhat.quarkus.ls.QuarkusProjectInfoProvider; - /** * Quarkus language client API. * * @author Angelo ZERR * */ -public interface QuarkusLanguageClientAPI extends LanguageClient, QuarkusProjectInfoProvider { +public interface QuarkusLanguageClientAPI + extends LanguageClient, QuarkusProjectInfoProvider, QuarkusPropertyDefinitionProvider { } diff --git a/quarkus.ls/com.redhat.quarkus.ls/src/main/java/com/redhat/quarkus/ls/QuarkusProjectInfoProvider.java b/quarkus.ls/com.redhat.quarkus.ls/src/main/java/com/redhat/quarkus/ls/api/QuarkusProjectInfoProvider.java similarity index 90% rename from quarkus.ls/com.redhat.quarkus.ls/src/main/java/com/redhat/quarkus/ls/QuarkusProjectInfoProvider.java rename to quarkus.ls/com.redhat.quarkus.ls/src/main/java/com/redhat/quarkus/ls/api/QuarkusProjectInfoProvider.java index 20cd4055c..eabb21b9c 100644 --- a/quarkus.ls/com.redhat.quarkus.ls/src/main/java/com/redhat/quarkus/ls/QuarkusProjectInfoProvider.java +++ b/quarkus.ls/com.redhat.quarkus.ls/src/main/java/com/redhat/quarkus/ls/api/QuarkusProjectInfoProvider.java @@ -7,7 +7,7 @@ * Contributors: * Red Hat Inc. - initial API and implementation *******************************************************************************/ -package com.redhat.quarkus.ls; +package com.redhat.quarkus.ls.api; import java.util.concurrent.CompletableFuture; @@ -17,7 +17,7 @@ import com.redhat.quarkus.commons.QuarkusProjectInfoParams; /** - * Quarkus project information provider.. + * Quarkus project information provider. * * @author Angelo ZERR * diff --git a/quarkus.ls/com.redhat.quarkus.ls/src/main/java/com/redhat/quarkus/ls/api/QuarkusPropertyDefinitionProvider.java b/quarkus.ls/com.redhat.quarkus.ls/src/main/java/com/redhat/quarkus/ls/api/QuarkusPropertyDefinitionProvider.java new file mode 100644 index 000000000..8ba8d20a5 --- /dev/null +++ b/quarkus.ls/com.redhat.quarkus.ls/src/main/java/com/redhat/quarkus/ls/api/QuarkusPropertyDefinitionProvider.java @@ -0,0 +1,30 @@ +/******************************************************************************* +* Copyright (c) 2019 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 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package com.redhat.quarkus.ls.api; + +import java.util.concurrent.CompletableFuture; + +import org.eclipse.lsp4j.Location; +import org.eclipse.lsp4j.jsonrpc.services.JsonRequest; + +import com.redhat.quarkus.commons.QuarkusPropertyDefinitionParams; + +/** + * Quarkus property definition provider. + * + * @author Angelo ZERR + * + */ +public interface QuarkusPropertyDefinitionProvider { + + @JsonRequest("quarkus/propertyDefinition") + CompletableFuture getPropertyDefinition(QuarkusPropertyDefinitionParams params); + +} diff --git a/quarkus.ls/com.redhat.quarkus.ls/src/main/java/com/redhat/quarkus/services/QuarkusDefinition.java b/quarkus.ls/com.redhat.quarkus.ls/src/main/java/com/redhat/quarkus/services/QuarkusDefinition.java new file mode 100644 index 000000000..cab925b56 --- /dev/null +++ b/quarkus.ls/com.redhat.quarkus.ls/src/main/java/com/redhat/quarkus/services/QuarkusDefinition.java @@ -0,0 +1,114 @@ +/******************************************************************************* +* Copyright (c) 2019 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 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package com.redhat.quarkus.services; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.eclipse.lsp4j.Location; +import org.eclipse.lsp4j.LocationLink; +import org.eclipse.lsp4j.Position; +import org.eclipse.lsp4j.jsonrpc.messages.Either; + +import com.redhat.quarkus.commons.ExtendedConfigDescriptionBuildItem; +import com.redhat.quarkus.commons.QuarkusProjectInfo; +import com.redhat.quarkus.commons.QuarkusPropertyDefinitionParams; +import com.redhat.quarkus.ls.api.QuarkusPropertyDefinitionProvider; +import com.redhat.quarkus.ls.commons.BadLocationException; +import com.redhat.quarkus.model.Node; +import com.redhat.quarkus.model.PropertiesModel; +import com.redhat.quarkus.model.PropertyKey; +import com.redhat.quarkus.utils.PositionUtils; +import com.redhat.quarkus.utils.QuarkusPropertiesUtils; + +/** + * The Quarkus definition. + * + * @author Angelo ZERR + * + */ +public class QuarkusDefinition { + + private static final Logger LOGGER = Logger.getLogger(QuarkusDefinition.class.getName()); + + /** + * Returns as promise the Java field definition location of the property at the + * given position of the given application.properties + * document. + * + * @param document the properties model. + * @param position the position where definition was triggered + * @param projectInfo the Quarkus properties + * @param provider the Quarkus property definition provider. + * @param definitionLinkSupport true if {@link LocationLink} must be returned + * and false otherwise. + * @return as promise the Java field definition location of the property at the + * given position of the given application.properties + * document. + */ + public CompletableFuture, List>> findDefinition( + PropertiesModel document, Position position, QuarkusProjectInfo projectInfo, + QuarkusPropertyDefinitionProvider provider, boolean definitionLinkSupport) { + Node node = null; + try { + node = document.findNodeAt(position); + } catch (BadLocationException e) { + LOGGER.log(Level.SEVERE, "In QuarkusDefinition, position error", e); + return CompletableFuture.completedFuture(null); + } + // Get the property at the given position + PropertyKey key = getPropertyKey(node); + if (key != null) { + String propertyName = key.getPropertyName(); + // Get metatada of the property + ExtendedConfigDescriptionBuildItem item = QuarkusPropertiesUtils.getProperty(propertyName, projectInfo); + if (item != null) { + // Get Java field definition from the given property source + String propertySource = item.getSource(); + QuarkusPropertyDefinitionParams definitionParams = new QuarkusPropertyDefinitionParams(); + definitionParams.setUri(document.getDocumentURI()); + definitionParams.setPropertySource(propertySource); + return provider.getPropertyDefinition(definitionParams).thenApply(target -> { + if (target == null) { + return null; + } + if (definitionLinkSupport) { + // Use document link + List locations = new ArrayList<>(); + LocationLink link = new LocationLink(target.getUri(), target.getRange(), target.getRange(), + PositionUtils.createRange(key)); + locations.add(link); + return Either.forRight(locations); + } + // Use simple location + List locations = new ArrayList<>(); + locations.add(target); + return Either.forLeft(locations); + }); + } + } + return CompletableFuture.completedFuture(null); + } + + private static PropertyKey getPropertyKey(Node node) { + if (node == null) { + return null; + } + switch (node.getNodeType()) { + case PROPERTY_KEY: + return (PropertyKey) node; + default: + return null; + } + } +} diff --git a/quarkus.ls/com.redhat.quarkus.ls/src/main/java/com/redhat/quarkus/services/QuarkusLanguageService.java b/quarkus.ls/com.redhat.quarkus.ls/src/main/java/com/redhat/quarkus/services/QuarkusLanguageService.java index c4960850c..2bfd55a0a 100644 --- a/quarkus.ls/com.redhat.quarkus.ls/src/main/java/com/redhat/quarkus/services/QuarkusLanguageService.java +++ b/quarkus.ls/com.redhat.quarkus.ls/src/main/java/com/redhat/quarkus/services/QuarkusLanguageService.java @@ -10,16 +10,21 @@ package com.redhat.quarkus.services; import java.util.List; +import java.util.concurrent.CompletableFuture; import org.eclipse.lsp4j.CompletionList; import org.eclipse.lsp4j.Diagnostic; import org.eclipse.lsp4j.DocumentSymbol; import org.eclipse.lsp4j.Hover; +import org.eclipse.lsp4j.Location; +import org.eclipse.lsp4j.LocationLink; import org.eclipse.lsp4j.Position; import org.eclipse.lsp4j.SymbolInformation; import org.eclipse.lsp4j.jsonrpc.CancelChecker; +import org.eclipse.lsp4j.jsonrpc.messages.Either; import com.redhat.quarkus.commons.QuarkusProjectInfo; +import com.redhat.quarkus.ls.api.QuarkusPropertyDefinitionProvider; import com.redhat.quarkus.model.PropertiesModel; import com.redhat.quarkus.settings.QuarkusCompletionSettings; import com.redhat.quarkus.settings.QuarkusHoverSettings; @@ -36,12 +41,14 @@ public class QuarkusLanguageService { private final QuarkusCompletions completions; private final QuarkusSymbolsProvider symbolsProvider; private final QuarkusHover hover; + private final QuarkusDefinition definition; private final QuarkusDiagnostics diagnostics; public QuarkusLanguageService() { this.completions = new QuarkusCompletions(); this.symbolsProvider = new QuarkusSymbolsProvider(); this.hover = new QuarkusHover(); + this.definition = new QuarkusDefinition(); this.diagnostics = new QuarkusDiagnostics(); } @@ -49,7 +56,7 @@ public QuarkusLanguageService() { * Returns completion list for the given position * * @param document the properties model document - * @param position the position where completion was triggereds + * @param position the position where completion was triggered * @param projectInfo the Quarkus project information * @param completionSettings the completion settings * @param cancelChecker the cancel checker @@ -96,6 +103,27 @@ public List findDocumentSymbols(PropertiesModel document, Cancel return symbolsProvider.findDocumentSymbols(document, cancelChecker); } + /** + * Returns as promise the Java field definition location of the property at the + * given position of the given application.properties + * document. + * + * @param document the properties model. + * @param position the position where definition was triggered + * @param projectInfo the Quarkus properties + * @param provider the Quarkus property definition provider. + * @param definitionLinkSupport true if {@link LocationLink} must be returned + * and false otherwise. + * @return as promise the Java field definition location of the property at the + * given position of the given application.properties + * document. + */ + public CompletableFuture, List>> findDefinition( + PropertiesModel document, Position position, QuarkusProjectInfo projectInfo, + QuarkusPropertyDefinitionProvider provider, boolean definitionLinkSupport) { + return definition.findDefinition(document, position, projectInfo, provider, definitionLinkSupport); + } + /** * Validate the given application.properties document by using the * given Quarkus properties metadata projectInfo. @@ -103,7 +131,7 @@ public List findDocumentSymbols(PropertiesModel document, Cancel * @param document the properties model. * @param projectInfo the Quarkus properties * @param validationSettings the validation settings. - * @param cancelChecker the cancel checker. + * @param cancelChecker the cancel checker. * @return the result of the validation. */ public List doDiagnostics(PropertiesModel document, QuarkusProjectInfo projectInfo, diff --git a/quarkus.ls/com.redhat.quarkus.ls/src/main/java/com/redhat/quarkus/services/QuarkusSymbolsProvider.java b/quarkus.ls/com.redhat.quarkus.ls/src/main/java/com/redhat/quarkus/services/QuarkusSymbolsProvider.java index 99572a69f..f9ed5d4ee 100644 --- a/quarkus.ls/com.redhat.quarkus.ls/src/main/java/com/redhat/quarkus/services/QuarkusSymbolsProvider.java +++ b/quarkus.ls/com.redhat.quarkus.ls/src/main/java/com/redhat/quarkus/services/QuarkusSymbolsProvider.java @@ -10,9 +10,7 @@ package com.redhat.quarkus.services; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; -import java.util.Map; import org.eclipse.lsp4j.DocumentSymbol; import org.eclipse.lsp4j.Location; diff --git a/quarkus.ls/com.redhat.quarkus.ls/src/main/java/com/redhat/quarkus/settings/capabilities/ClientCapabilitiesWrapper.java b/quarkus.ls/com.redhat.quarkus.ls/src/main/java/com/redhat/quarkus/settings/capabilities/ClientCapabilitiesWrapper.java index 200b19154..492cc73c0 100644 --- a/quarkus.ls/com.redhat.quarkus.ls/src/main/java/com/redhat/quarkus/settings/capabilities/ClientCapabilitiesWrapper.java +++ b/quarkus.ls/com.redhat.quarkus.ls/src/main/java/com/redhat/quarkus/settings/capabilities/ClientCapabilitiesWrapper.java @@ -48,6 +48,10 @@ public boolean isDocumentSymbolDynamicRegistrationSupported() { return v3Supported && isDynamicRegistrationSupported(getTextDocument().getDocumentSymbol()); } + public boolean isDefinitionDynamicRegistered() { + return v3Supported && isDynamicRegistrationSupported(getTextDocument().getDefinition()); + } + private boolean isDynamicRegistrationSupported(DynamicRegistrationCapabilities capability) { return capability != null && capability.getDynamicRegistration() != null && capability.getDynamicRegistration().booleanValue(); diff --git a/quarkus.ls/com.redhat.quarkus.ls/src/main/java/com/redhat/quarkus/settings/capabilities/QuarkusCapabilityManager.java b/quarkus.ls/com.redhat.quarkus.ls/src/main/java/com/redhat/quarkus/settings/capabilities/QuarkusCapabilityManager.java index 2d78e27d2..4a5e6efb0 100644 --- a/quarkus.ls/com.redhat.quarkus.ls/src/main/java/com/redhat/quarkus/settings/capabilities/QuarkusCapabilityManager.java +++ b/quarkus.ls/com.redhat.quarkus.ls/src/main/java/com/redhat/quarkus/settings/capabilities/QuarkusCapabilityManager.java @@ -9,9 +9,11 @@ import static com.redhat.quarkus.settings.capabilities.ServerCapabilitiesConstants.COMPLETION_ID; import static com.redhat.quarkus.settings.capabilities.ServerCapabilitiesConstants.DEFAULT_COMPLETION_OPTIONS; +import static com.redhat.quarkus.settings.capabilities.ServerCapabilitiesConstants.DEFINITION_ID; import static com.redhat.quarkus.settings.capabilities.ServerCapabilitiesConstants.DOCUMENT_SYMBOL_ID; import static com.redhat.quarkus.settings.capabilities.ServerCapabilitiesConstants.HOVER_ID; import static com.redhat.quarkus.settings.capabilities.ServerCapabilitiesConstants.TEXT_DOCUMENT_COMPLETION; +import static com.redhat.quarkus.settings.capabilities.ServerCapabilitiesConstants.TEXT_DOCUMENT_DEFINITION; import static com.redhat.quarkus.settings.capabilities.ServerCapabilitiesConstants.TEXT_DOCUMENT_DOCUMENT_SYMBOL; import static com.redhat.quarkus.settings.capabilities.ServerCapabilitiesConstants.TEXT_DOCUMENT_HOVER; @@ -52,6 +54,9 @@ public void initializeCapabilities() { if (this.getClientCapabilities().isDocumentSymbolDynamicRegistrationSupported()) { registerCapability(DOCUMENT_SYMBOL_ID, TEXT_DOCUMENT_DOCUMENT_SYMBOL); } + if (this.getClientCapabilities().isDefinitionDynamicRegistered()) { + registerCapability(DEFINITION_ID, TEXT_DOCUMENT_DEFINITION); + } } public void setClientCapabilities(ClientCapabilities clientCapabilities) { diff --git a/quarkus.ls/com.redhat.quarkus.ls/src/main/java/com/redhat/quarkus/settings/capabilities/ServerCapabilitiesConstants.java b/quarkus.ls/com.redhat.quarkus.ls/src/main/java/com/redhat/quarkus/settings/capabilities/ServerCapabilitiesConstants.java index 27d456673..fcd3b1db8 100644 --- a/quarkus.ls/com.redhat.quarkus.ls/src/main/java/com/redhat/quarkus/settings/capabilities/ServerCapabilitiesConstants.java +++ b/quarkus.ls/com.redhat.quarkus.ls/src/main/java/com/redhat/quarkus/settings/capabilities/ServerCapabilitiesConstants.java @@ -26,10 +26,12 @@ private ServerCapabilitiesConstants() { public static final String TEXT_DOCUMENT_COMPLETION = "textDocument/completion"; public static final String TEXT_DOCUMENT_HOVER = "textDocument/hover"; public static final String TEXT_DOCUMENT_DOCUMENT_SYMBOL = "textDocument/documentSymbol"; + public static final String TEXT_DOCUMENT_DEFINITION = "textDocument/definition"; public static final String COMPLETION_ID = UUID.randomUUID().toString(); public static final String HOVER_ID = UUID.randomUUID().toString(); public static final String DOCUMENT_SYMBOL_ID = UUID.randomUUID().toString(); + public static final String DEFINITION_ID = UUID.randomUUID().toString(); public static final CompletionOptions DEFAULT_COMPLETION_OPTIONS = new CompletionOptions(false, Arrays.asList(".", "%", "=")); diff --git a/quarkus.ls/com.redhat.quarkus.ls/src/main/java/com/redhat/quarkus/settings/capabilities/ServerCapabilitiesInitializer.java b/quarkus.ls/com.redhat.quarkus.ls/src/main/java/com/redhat/quarkus/settings/capabilities/ServerCapabilitiesInitializer.java index 640ca0d89..937128892 100644 --- a/quarkus.ls/com.redhat.quarkus.ls/src/main/java/com/redhat/quarkus/settings/capabilities/ServerCapabilitiesInitializer.java +++ b/quarkus.ls/com.redhat.quarkus.ls/src/main/java/com/redhat/quarkus/settings/capabilities/ServerCapabilitiesInitializer.java @@ -42,6 +42,7 @@ public static ServerCapabilities getNonDynamicServerCapabilities(ClientCapabilit } serverCapabilities .setDocumentSymbolProvider(!clientCapabilities.isDocumentSymbolDynamicRegistrationSupported()); + serverCapabilities.setDefinitionProvider(!clientCapabilities.isDefinitionDynamicRegistered()); return serverCapabilities; } } \ No newline at end of file diff --git a/quarkus.ls/com.redhat.quarkus.ls/src/test/java/com/redhat/quarkus/ls/MockQuarkusLanguageClient.java b/quarkus.ls/com.redhat.quarkus.ls/src/test/java/com/redhat/quarkus/ls/MockQuarkusLanguageClient.java index da549f167..1ec39e44d 100644 --- a/quarkus.ls/com.redhat.quarkus.ls/src/test/java/com/redhat/quarkus/ls/MockQuarkusLanguageClient.java +++ b/quarkus.ls/com.redhat.quarkus.ls/src/test/java/com/redhat/quarkus/ls/MockQuarkusLanguageClient.java @@ -8,6 +8,7 @@ import java.util.Map; import java.util.concurrent.CompletableFuture; +import org.eclipse.lsp4j.Location; import org.eclipse.lsp4j.MessageActionItem; import org.eclipse.lsp4j.MessageParams; import org.eclipse.lsp4j.PublishDiagnosticsParams; @@ -18,6 +19,7 @@ import com.redhat.quarkus.commons.QuarkusProjectInfoParams; import com.redhat.quarkus.commons.QuarkusPropertiesChangeEvent; import com.redhat.quarkus.commons.QuarkusPropertiesScope; +import com.redhat.quarkus.commons.QuarkusPropertyDefinitionParams; import com.redhat.quarkus.ls.api.QuarkusLanguageClientAPI; public class MockQuarkusLanguageClient implements QuarkusLanguageClientAPI { @@ -131,4 +133,9 @@ public void changedJavaSources(String projectURI, ExtendedConfigDescriptionBuild languageServer.quarkusPropertiesChanged(event); } + @Override + public CompletableFuture getPropertyDefinition(QuarkusPropertyDefinitionParams params) { + return null; + } + }