From 591adec33deaf95433190098f89d7447227199f8 Mon Sep 17 00:00:00 2001 From: nsingh Date: Fri, 9 Mar 2018 12:05:31 -0800 Subject: [PATCH] Added JDT command for getting Java project Also added a JDT Project cache in the Spring Boot LS --- .../languageserver/ClasspathResponse.java | 82 ++++++++ .../languageserver/ProjectResponse.java | 44 +++++ .../languageserver/STS4LanguageClient.java | 4 +- .../testharness/LanguageServerHarness.java | 9 +- .../plugin.xml | 5 + .../ls/extension/ResolveClasspathHandler.java | 63 +----- .../ls/extension/ResolveProjectHandler.java | 79 ++++++++ .../jdt/ls/extension/ResourceUtils.java | 82 ++++++++ .../vscode/boot/BootLanguageServerParams.java | 6 +- .../vscode/boot/jdt/ls/JdtLsProjectCache.java | 186 ++++++++++++++++++ .../{classpath.ts => classpath-service.ts} | 0 .../commons-vscode/src/launch-util.ts | 4 +- .../commons-vscode/src/project-service.ts | 22 +++ 13 files changed, 527 insertions(+), 59 deletions(-) create mode 100644 headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/ClasspathResponse.java create mode 100644 headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/ProjectResponse.java create mode 100644 headless-services/org.springframework.tooling.jdt.ls.extension/src/org/springframework/tooling/jdt/ls/extension/ResolveProjectHandler.java create mode 100644 headless-services/org.springframework.tooling.jdt.ls.extension/src/org/springframework/tooling/jdt/ls/extension/ResourceUtils.java create mode 100644 headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/jdt/ls/JdtLsProjectCache.java rename vscode-extensions/commons-vscode/src/{classpath.ts => classpath-service.ts} (100%) create mode 100644 vscode-extensions/commons-vscode/src/project-service.ts diff --git a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/ClasspathResponse.java b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/ClasspathResponse.java new file mode 100644 index 000000000..b14447fb7 --- /dev/null +++ b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/ClasspathResponse.java @@ -0,0 +1,82 @@ +/******************************************************************************* + * Copyright (c) 2018 Pivotal, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Pivotal, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.commons.languageserver; + +import java.util.List; + +public class ClasspathResponse { + + public static final String ENTRY_KIND_SOURCE = "source"; + public static final String ENTRY_KIND_BINARY = "binary"; + + private List entries; + private String defaultOutputFolder; + + public ClasspathResponse(List entries, String defaultOutputFolder) { + super(); + this.entries = entries; + this.defaultOutputFolder = defaultOutputFolder; + } + + public List getEntries() { + return entries; + } + + public void setEntries(List entries) { + this.entries = entries; + } + + public String getDefaultOutputFolder() { + return defaultOutputFolder; + } + + public void setDefaultOutputFolder(String defaultOutputFolder) { + this.defaultOutputFolder = defaultOutputFolder; + } + + @Override + public String toString() { + return "Classpath [entries=" + entries + ", defaultOutputFolder=" + defaultOutputFolder + "]"; + } + + public static class Entry { + + private String kind; + private String path; + + public Entry(String kind, String path) { + super(); + this.kind = kind; + this.path = path; + } + + public String getKind() { + return kind; + } + + public void setKind(String kind) { + this.kind = kind; + } + + public String getPath() { + return path; + } + + public void setPath(String path) { + this.path = path; + } + + @Override + public String toString() { + return "CPE [kind=" + kind + ", path=" + path + "]\n"; + } + } +} \ No newline at end of file diff --git a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/ProjectResponse.java b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/ProjectResponse.java new file mode 100644 index 000000000..1dec28fa4 --- /dev/null +++ b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/ProjectResponse.java @@ -0,0 +1,44 @@ +/******************************************************************************* + * Copyright (c) 2018 Pivotal, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Pivotal, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.commons.languageserver; + +public class ProjectResponse { + + private String name; + private String uri; + + public ProjectResponse(String name, String uri) { + super(); + this.name = name; + this.uri = uri; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getUri() { + return uri; + } + + public void setUri(String uri) { + this.uri = uri; + } + + @Override + public String toString() { + return "ProjectResponse [name=" + name + ", uri=" + uri + "]"; + } +} diff --git a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/STS4LanguageClient.java b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/STS4LanguageClient.java index e1674447f..d5f16bdd9 100644 --- a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/STS4LanguageClient.java +++ b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/STS4LanguageClient.java @@ -35,6 +35,8 @@ public interface STS4LanguageClient extends LanguageClient { CompletableFuture moveCursor(CursorMovement cursorMovement); @JsonRequest("sts/classpath") - CompletableFuture classpath(ClasspathParams classpathEvent); + CompletableFuture classpath(ClasspathParams classpathParams); + @JsonRequest("sts/project") + CompletableFuture project(String uri); } diff --git a/headless-services/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/LanguageServerHarness.java b/headless-services/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/LanguageServerHarness.java index dd7d59615..47b8fcb2e 100644 --- a/headless-services/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/LanguageServerHarness.java +++ b/headless-services/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/LanguageServerHarness.java @@ -91,8 +91,10 @@ import org.eclipse.lsp4j.WorkspaceEdit; import org.eclipse.lsp4j.jsonrpc.messages.Either; import org.eclipse.lsp4j.services.LanguageClientAware; import org.springframework.ide.vscode.commons.languageserver.ClasspathParams; +import org.springframework.ide.vscode.commons.languageserver.ClasspathResponse; import org.springframework.ide.vscode.commons.languageserver.HighlightParams; import org.springframework.ide.vscode.commons.languageserver.ProgressParams; +import org.springframework.ide.vscode.commons.languageserver.ProjectResponse; import org.springframework.ide.vscode.commons.languageserver.STS4LanguageClient; import org.springframework.ide.vscode.commons.languageserver.completion.DocumentEdits; import org.springframework.ide.vscode.commons.languageserver.quickfix.QuickfixEdit.CursorMovement; @@ -294,7 +296,12 @@ public class LanguageServerHarness { } @Override - public CompletableFuture classpath(ClasspathParams classpathEvent) { + public CompletableFuture classpath(ClasspathParams classpathEvent) { + return CompletableFuture.completedFuture(null); + } + + @Override + public CompletableFuture project(String uri) { return CompletableFuture.completedFuture(null); } diff --git a/headless-services/org.springframework.tooling.jdt.ls.extension/plugin.xml b/headless-services/org.springframework.tooling.jdt.ls.extension/plugin.xml index 4e2611527..c2bf5eb23 100644 --- a/headless-services/org.springframework.tooling.jdt.ls.extension/plugin.xml +++ b/headless-services/org.springframework.tooling.jdt.ls.extension/plugin.xml @@ -6,5 +6,10 @@ + + + + + \ No newline at end of file diff --git a/headless-services/org.springframework.tooling.jdt.ls.extension/src/org/springframework/tooling/jdt/ls/extension/ResolveClasspathHandler.java b/headless-services/org.springframework.tooling.jdt.ls.extension/src/org/springframework/tooling/jdt/ls/extension/ResolveClasspathHandler.java index 2556aeb12..2a75a1251 100644 --- a/headless-services/org.springframework.tooling.jdt.ls.extension/src/org/springframework/tooling/jdt/ls/extension/ResolveClasspathHandler.java +++ b/headless-services/org.springframework.tooling.jdt.ls.extension/src/org/springframework/tooling/jdt/ls/extension/ResolveClasspathHandler.java @@ -12,37 +12,31 @@ package org.springframework.tooling.jdt.ls.extension; import static org.springframework.tooling.jdt.ls.extension.Logger.log; -import java.io.File; import java.net.URI; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; -import java.util.Optional; -import org.eclipse.core.resources.IContainer; -import org.eclipse.core.resources.IProject; -import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.jdt.core.IClasspathEntry; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.IPackageFragmentRoot; -import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.ls.core.internal.IDelegateCommandHandler; @SuppressWarnings("restriction") public class ResolveClasspathHandler implements IDelegateCommandHandler { - public static final String ENTRY_CONTENT_SOURCE = "source"; - public static final String ENTRY_CONTENT_BINARY = "binary"; + public static final String ENTRY_KIND_SOURCE = "source"; + public static final String ENTRY_KIND_BINARY = "binary"; public static final String OUTPUT_LOCATION = "output_location"; @Override public Object executeCommand(String commandId, List arguments, IProgressMonitor monitor) throws Exception { + log("ResolveClasspathHandler=" + commandId); try { - URI resourceUri = getResourceUri(arguments); + URI resourceUri = ResourceUtils.getResourceUri(arguments); - IJavaProject javaProject = getJavaProject(resourceUri); + IJavaProject javaProject = ResourceUtils.getJavaProject(resourceUri); return resolveClasspathHandler(javaProject); } catch (Exception e) { @@ -64,62 +58,21 @@ public class ResolveClasspathHandler implements IDelegateCommandHandler { } } Classpath classpath = new Classpath(cpEntries, javaProject.getOutputLocation().toString()); - log("classpath=" + classpath); +// log("classpath=" + classpath); return classpath; } private String toContentKind(IClasspathEntry entry) { switch (entry.getContentKind()) { case IPackageFragmentRoot.K_BINARY: - return ENTRY_CONTENT_BINARY; + return ENTRY_KIND_BINARY; case IPackageFragmentRoot.K_SOURCE: - return ENTRY_CONTENT_SOURCE; + return ENTRY_KIND_SOURCE; default: return "unknown: " + entry.getContentKind(); } } - private IJavaProject getJavaProject(URI resourceUri) throws Exception { - IContainer[] containers = ResourcesPlugin.getWorkspace().getRoot().findContainersForLocationURI(resourceUri); - log("containers=" + containers); - if (containers.length > 0) { - log("containers.length=" + containers.length); - Optional shortest = Arrays.stream(containers) - .min((f1, f2) -> f1.getFullPath().segmentCount() - f2.getFullPath().segmentCount()); - log("shortest=" + shortest.isPresent()); - - if (shortest.isPresent()) { - log("shortest.fullpath=" + shortest.get().getFullPath()); - - IProject project = shortest.get().getProject(); - log("project=" + project.getName()); - - if (project.isAccessible() && project.hasNature(JavaCore.NATURE_ID)) { - IJavaProject javaProject = JavaCore.create(project); - log("javaProject=" + javaProject.getElementName()); - - return javaProject; - } - } - } - - throw new Exception("Resolve classpath: unable to resolve a Java project from : " + resourceUri); - } - - private URI getResourceUri(List arguments) throws Exception { - if (arguments == null || arguments.size() == 0) { - throw new IllegalArgumentException( - "Resolve classpath: Invalid number of arguments. A resource URI is required."); - } - URI uri = URI.create((String) arguments.get(0)); - File resource = new File(uri); - if (!resource.exists()) { - throw new IllegalArgumentException("Resolve classpath: Resource does not exist: " + resource); - } - - return uri; - } - public static class CPE { private String kind; private String path; diff --git a/headless-services/org.springframework.tooling.jdt.ls.extension/src/org/springframework/tooling/jdt/ls/extension/ResolveProjectHandler.java b/headless-services/org.springframework.tooling.jdt.ls.extension/src/org/springframework/tooling/jdt/ls/extension/ResolveProjectHandler.java new file mode 100644 index 000000000..2ac29dd5c --- /dev/null +++ b/headless-services/org.springframework.tooling.jdt.ls.extension/src/org/springframework/tooling/jdt/ls/extension/ResolveProjectHandler.java @@ -0,0 +1,79 @@ +/******************************************************************************* + * Copyright (c) 2018 Pivotal, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Pivotal, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.tooling.jdt.ls.extension; + +import static org.springframework.tooling.jdt.ls.extension.Logger.log; + +import java.net.URI; +import java.util.List; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jdt.core.IJavaProject; +import org.eclipse.jdt.ls.core.internal.IDelegateCommandHandler; + +@SuppressWarnings("restriction") +public class ResolveProjectHandler implements IDelegateCommandHandler { + + @Override + public Object executeCommand(String commandId, List arguments, IProgressMonitor monitor) throws Exception { + + log("ResolveProjectHandler=" + commandId); + try { + URI resourceUri = ResourceUtils.getResourceUri(arguments); + + IJavaProject javaProject = ResourceUtils.getJavaProject(resourceUri); + + ProjectResponse projectResponse = new ProjectResponse(javaProject.getElementName(), javaProject.getProject().getLocationURI().toString()); + + log("projectResponse="+projectResponse); + + return projectResponse; + } catch (Exception e) { + log(e); + throw e; + } + } + + public class ProjectResponse { + + private String name; + private String uri; + + public ProjectResponse(String name, String uri) { + super(); + this.name = name; + this.uri = uri; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getUri() { + return uri; + } + + public void setUri(String uri) { + this.uri = uri; + } + + @Override + public String toString() { + return "ProjectResponse [name=" + name + ", uri=" + uri + "]"; + } + + } + +} diff --git a/headless-services/org.springframework.tooling.jdt.ls.extension/src/org/springframework/tooling/jdt/ls/extension/ResourceUtils.java b/headless-services/org.springframework.tooling.jdt.ls.extension/src/org/springframework/tooling/jdt/ls/extension/ResourceUtils.java new file mode 100644 index 000000000..587faaf11 --- /dev/null +++ b/headless-services/org.springframework.tooling.jdt.ls.extension/src/org/springframework/tooling/jdt/ls/extension/ResourceUtils.java @@ -0,0 +1,82 @@ +/******************************************************************************* + * Copyright (c) 2018 Pivotal, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Pivotal, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.tooling.jdt.ls.extension; + +import static org.springframework.tooling.jdt.ls.extension.Logger.log; + +import java.io.File; +import java.net.URI; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; + +import org.eclipse.core.resources.IContainer; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.jdt.core.IJavaProject; +import org.eclipse.jdt.core.JavaCore; + +public final class ResourceUtils { + + /** + * Resolves a Java project given the resource URI. The resource URI could be any resource contained in the project. + * @param resourceUri + * @return Java project + * @throws Exception if unable to resolve Java project from given resource URI + */ + public static IJavaProject getJavaProject(URI resourceUri) throws Exception { + IContainer[] containers = ResourcesPlugin.getWorkspace().getRoot().findContainersForLocationURI(resourceUri); + log("containers=" + containers); + if (containers.length > 0) { + log("containers.length=" + containers.length); + Optional shortest = Arrays.stream(containers) + .min((f1, f2) -> f1.getFullPath().segmentCount() - f2.getFullPath().segmentCount()); + log("shortest=" + shortest.isPresent()); + + if (shortest.isPresent()) { + log("shortest.fullpath=" + shortest.get().getFullPath()); + + IProject project = shortest.get().getProject(); + log("project=" + project.getName()); + + if (project.isAccessible() && project.hasNature(JavaCore.NATURE_ID)) { + IJavaProject javaProject = JavaCore.create(project); + log("javaProject=" + javaProject.getElementName()); + + return javaProject; + } + } + } + + throw new Exception("Resolve classpath: unable to resolve a Java project from : " + resourceUri); + } + + /** + * Returns the resource URI, IFF it exists. Throws exception if it doesn't exist. + * @param arguments + * @return + * @throws Exception + */ + public static URI getResourceUri(List arguments) throws Exception { + if (arguments == null || arguments.size() == 0) { + throw new IllegalArgumentException( + "Resolve classpath: Invalid number of arguments. A resource URI is required."); + } + URI uri = URI.create((String) arguments.get(0)); + File resource = new File(uri); + if (!resource.exists()) { + throw new IllegalArgumentException("Resolve classpath: Resource does not exist: " + resource); + } + + return uri; + } + +} diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/BootLanguageServerParams.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/BootLanguageServerParams.java index bb232166b..2b9f67f73 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/BootLanguageServerParams.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/BootLanguageServerParams.java @@ -17,6 +17,7 @@ import java.util.Arrays; import org.eclipse.lsp4j.TextDocumentIdentifier; import org.springframework.ide.vscode.boot.java.handlers.RunningAppProvider; import org.springframework.ide.vscode.boot.java.utils.SpringLiveHoverWatchdog; +import org.springframework.ide.vscode.boot.jdt.ls.JdtLsProjectCache; import org.springframework.ide.vscode.boot.metadata.DefaultSpringPropertyIndexProvider; import org.springframework.ide.vscode.boot.metadata.SpringPropertyIndexProvider; import org.springframework.ide.vscode.boot.metadata.types.TypeUtil; @@ -80,13 +81,16 @@ public class BootLanguageServerParams { return (SimpleLanguageServer server) -> { // Initialize project finders, project caches and project observers CompositeJavaProjectFinder javaProjectFinder = new CompositeJavaProjectFinder(); + JdtLsProjectCache jdtProjectCache = new JdtLsProjectCache(server); + javaProjectFinder.addJavaProjectFinder(jdtProjectCache); + MavenProjectCache mavenProjectCache = new MavenProjectCache(server, MavenCore.getDefault(), true, Paths.get(IJavaProject.PROJECT_CACHE_FOLDER)); javaProjectFinder.addJavaProjectFinder(new MavenProjectFinder(mavenProjectCache)); GradleProjectCache gradleProjectCache = new GradleProjectCache(server, GradleCore.getDefault(), true, Paths.get(IJavaProject.PROJECT_CACHE_FOLDER)); javaProjectFinder.addJavaProjectFinder(new GradleProjectFinder(gradleProjectCache)); - CompositeProjectOvserver projectObserver = new CompositeProjectOvserver(Arrays.asList(mavenProjectCache, gradleProjectCache)); + CompositeProjectOvserver projectObserver = new CompositeProjectOvserver(Arrays.asList(jdtProjectCache, mavenProjectCache, gradleProjectCache)); DefaultSpringPropertyIndexProvider indexProvider = new DefaultSpringPropertyIndexProvider(javaProjectFinder, projectObserver); indexProvider.setProgressService(server.getProgressService()); diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/jdt/ls/JdtLsProjectCache.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/jdt/ls/JdtLsProjectCache.java new file mode 100644 index 000000000..e6aa153d8 --- /dev/null +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/jdt/ls/JdtLsProjectCache.java @@ -0,0 +1,186 @@ +/******************************************************************************* + * Copyright (c) 2018 Pivotal, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Pivotal, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.boot.jdt.ls; + +import java.io.File; +import java.net.URI; +import java.net.URL; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; + +import org.eclipse.lsp4j.TextDocumentIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.ide.vscode.commons.jandex.JandexClasspath; +import org.springframework.ide.vscode.commons.java.ClasspathData; +import org.springframework.ide.vscode.commons.java.IClasspath; +import org.springframework.ide.vscode.commons.java.IJavaProject; +import org.springframework.ide.vscode.commons.java.IJavadocProvider; +import org.springframework.ide.vscode.commons.languageserver.ClasspathResponse; +import org.springframework.ide.vscode.commons.languageserver.ProjectResponse; +import org.springframework.ide.vscode.commons.languageserver.ClasspathParams; +import org.springframework.ide.vscode.commons.languageserver.STS4LanguageClient; +import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; +import org.springframework.ide.vscode.commons.languageserver.java.ProjectObserver; +import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer; +import org.springframework.ide.vscode.commons.util.CollectorUtil; + +import com.google.common.base.Supplier; +import com.google.common.base.Suppliers; +import com.google.common.collect.ImmutableList; + +public class JdtLsProjectCache implements JavaProjectFinder, ProjectObserver { + + private SimpleLanguageServer server; + private Map table = new HashMap(); + private Logger log = LoggerFactory.getLogger(JdtLsProjectCache.class); + + public JdtLsProjectCache(SimpleLanguageServer server) { + this.server = server; + } + + @Override + public void addListener(Listener listener) { +// throw new Error("not implemented yet"); + + } + + @Override + public void removeListener(Listener listener) { +// throw new Error("not implemented yet"); + + } + + private synchronized IJavaProject project(ProjectResponse project) { + if (project == null) { + return null; + } + return table.computeIfAbsent(project.getUri(), uri -> + new JdtLsProject(project) + ); + } + + @Override + public Optional find(TextDocumentIdentifier doc) { + try { + ProjectResponse projectUri = getClient().project(doc.getUri()).get(); + return Optional.of(project(projectUri)); + } catch (Exception e) { + log.error("Problems finding project for {}", doc.getUri(), e); + } + return Optional.empty(); + } + + private STS4LanguageClient getClient() { + return ((SimpleLanguageServer) server).getClient(); + } + + private class JdtLsProject implements IJavaProject { + + private Supplier classpath = Suppliers.memoize(this::computeClasspath); + private ProjectResponse projectResponse; + + public JdtLsProject(ProjectResponse projectResponse) { + this.projectResponse = projectResponse; + } + + @Override + public IClasspath getClasspath() { + return classpath.get(); + } + + private IClasspath computeClasspath() { + try { + ClasspathResponse response = getClient().classpath(new ClasspathParams(projectResponse.getUri())).get(); + return new JdtClasspath(response, projectResponse); + } catch (Exception e) { + log.error("", e); + } + return null; + } + + } + + private class JdtClasspath extends JandexClasspath { + + private ClasspathResponse response; + private String name; + private String projectUri; + + public JdtClasspath(ClasspathResponse response, ProjectResponse projectResponse) { + this.response = response; + this.name = projectResponse.getName(); + this.projectUri = projectResponse.getUri(); + } + + @Override + public String getName() { + return this.name; + } + + @Override + public boolean exists() { + return new File(projectUri).exists(); + } + + @Override + public Path getOutputFolder() { + return Paths.get(response.getDefaultOutputFolder()); + } + + @Override + public ImmutableList getClasspathEntries() throws Exception { + return response + .getEntries() + .stream() + .filter(cpe -> cpe.getKind().equals(ClasspathResponse.ENTRY_KIND_BINARY)) + .map(cpe -> Paths.get(cpe.getPath())) + .collect(CollectorUtil.toImmutableList()); + } + + @Override + public ImmutableList getClasspathResources() { + // TODO Auto-generated method stub + return null; + } + + @Override + public ImmutableList getSourceFolders() { + // TODO Auto-generated method stub + return null; + } + + @Override + public ClasspathData createClasspathData() throws Exception { + // We should not be needing this as we dont use DelegatingCachedClasspath + throw new UnsupportedOperationException("Not supported for JDT classpath: "); + } + + @Override + public Optional sourceContainer(File classpathResource) { + // TODO Auto-generated method stub + return null; + } + + @Override + protected IJavadocProvider createHtmlJavdocProvider(File classpathResource) { + // TODO Auto-generated method stub + return null; + } + + } + +} diff --git a/vscode-extensions/commons-vscode/src/classpath.ts b/vscode-extensions/commons-vscode/src/classpath-service.ts similarity index 100% rename from vscode-extensions/commons-vscode/src/classpath.ts rename to vscode-extensions/commons-vscode/src/classpath-service.ts diff --git a/vscode-extensions/commons-vscode/src/launch-util.ts b/vscode-extensions/commons-vscode/src/launch-util.ts index 50ca7c351..eac880d14 100644 --- a/vscode-extensions/commons-vscode/src/launch-util.ts +++ b/vscode-extensions/commons-vscode/src/launch-util.ts @@ -15,7 +15,8 @@ import {HighlightService, HighlightParams} from './highlight-service'; import { log } from 'util'; import { tmpdir } from 'os'; import { JVM, findJvm, findJdk } from '@pivotal-tools/jvm-launch-utils'; -import { registerClasspathService } from './classpath'; +import { registerClasspathService } from './classpath-service'; +import { registerProjectService } from './project-service'; let p2c = P2C.createConverter(); @@ -215,6 +216,7 @@ function setupLanguageClient(context: VSCode.ExtensionContext, createServer: Ser } return { applied: true}; }); + registerProjectService(client); registerClasspathService(client); return client; }); diff --git a/vscode-extensions/commons-vscode/src/project-service.ts b/vscode-extensions/commons-vscode/src/project-service.ts new file mode 100644 index 000000000..63614a0f8 --- /dev/null +++ b/vscode-extensions/commons-vscode/src/project-service.ts @@ -0,0 +1,22 @@ +'use strict'; + + +import * as VSCode from 'vscode'; +import { LanguageClient, RequestType } from 'vscode-languageclient'; + +export function registerProjectService(client : LanguageClient) : void { + let projectRequest = new RequestType("sts/project"); + + client.onRequest(projectRequest, async (uri: string) => { + return await executeProjectCommand(uri); + }); +} + +async function executeProjectCommand(resourceUri : string) : Promise { + return (await VSCode.commands.executeCommand("java.execute.workspaceCommand", "sts.java.resolveProject", resourceUri)); +} + +interface ProjectResponse { + name: string, + uri : string +} \ No newline at end of file