From 286d435d539ac42d2e4fed6067eedde6b126b211 Mon Sep 17 00:00:00 2001 From: BoykoAlex Date: Fri, 19 Jan 2018 19:26:29 -0500 Subject: [PATCH] PT #151978919 VSCode navigation to resource from hovers (POC) --- .../boot/java/livehover/LiveHoverUtils.java | 15 +- .../vscode/boot/java/utils/SourceLinks.java | 152 ++++++++++++++++++ .../utils/test/VSCodeSourceLinksTest.java | 91 +++++++++++ .../gradle/GradleProjectClasspath.java | 9 +- .../commons/jandex/JandexClasspath.java | 22 +++ .../vscode/commons/jandex/JandexIndex.java | 23 ++- .../java/DelegatingCachedClasspath.java | 15 +- .../ide/vscode/commons/java/IClasspath.java | 8 +- .../languageserver/util/LspClient.java | 46 ++++++ .../ide/vscode/commons/maven/MavenCore.java | 4 +- .../maven/java/MavenProjectClasspath.java | 9 +- .../java/classpathfile/FileClasspath.java | 14 +- .../vscode/commons/maven/JavaIndexTest.java | 21 ++- .../commons-vscode/src/launch-util.ts | 1 + 14 files changed, 417 insertions(+), 13 deletions(-) create mode 100644 headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SourceLinks.java create mode 100644 headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/utils/test/VSCodeSourceLinksTest.java create mode 100644 headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/LspClient.java diff --git a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/LiveHoverUtils.java b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/LiveHoverUtils.java index 72b27adc2..388395061 100644 --- a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/LiveHoverUtils.java +++ b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/LiveHoverUtils.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2017 Pivotal, Inc. + * Copyright (c) 2017, 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 @@ -10,13 +10,16 @@ *******************************************************************************/ package org.springframework.ide.vscode.boot.java.livehover; +import java.util.Optional; import java.util.stream.Stream; +import org.springframework.ide.vscode.boot.java.utils.SourceLinks; import org.springframework.ide.vscode.boot.java.utils.SpringResource; import org.springframework.ide.vscode.commons.boot.app.cli.SpringBootApp; import org.springframework.ide.vscode.commons.boot.app.cli.livebean.LiveBean; import org.springframework.ide.vscode.commons.boot.app.cli.livebean.LiveBeansModel; import org.springframework.ide.vscode.commons.java.IJavaProject; +import org.springframework.ide.vscode.commons.util.Renderables; import org.springframework.ide.vscode.commons.util.StringUtil; public class LiveHoverUtils { @@ -33,9 +36,17 @@ public class LiveHoverUtils { public static String showBeanWithResource(LiveBean bean, String indentStr, IJavaProject project) { String newline = " \n"+indentStr; //Note: the double space before newline makes markdown see it as a real line break + + String type = bean.getType(true); + + // Try creating a URL link to open source for the type + Optional url = SourceLinks.sourceLinkUrl(project, type); + if (url.isPresent()) { + return Renderables.link(type, url.get()).toMarkdown(); + } + StringBuilder buf = new StringBuilder("Bean: "); buf.append(bean.getId()); - String type = bean.getType(true); if (type != null) { buf.append(newline); buf.append("Type: `" + type + "`"); diff --git a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SourceLinks.java b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SourceLinks.java new file mode 100644 index 000000000..fedafc831 --- /dev/null +++ b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SourceLinks.java @@ -0,0 +1,152 @@ +/******************************************************************************* + * 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.java.utils; + +import java.io.File; +import java.net.URLEncoder; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Optional; + +import org.springframework.ide.vscode.commons.java.IClasspath; +import org.springframework.ide.vscode.commons.java.IJavaProject; +import org.springframework.ide.vscode.commons.languageserver.util.LspClient; +import org.springframework.ide.vscode.commons.util.Log; + +/** + * Utility for creating code navigate links for Markdown format documentation. + * The utility looks at the environment variable "sts.lsp.client" to create a URL supported on a specific client + * + * @author Alex Boyko + * + */ +public class SourceLinks { + + private static final String JAR = ".jar"; + private static final String JAVA = ".java"; + private static final String CLASS = ".class"; + + public static Optional sourceLinkUrl(IJavaProject project, String fqName) { + switch (LspClient.currentClient()) { + case VSCODE: + return createVSCodeSourceLink(project, fqName); + default: + return Optional.empty(); + } + } + + private static Optional createVSCodeSourceLink(IJavaProject project, String fqName) { + Optional classpathResource = project.getClasspath().findClasspathResourceContainer(fqName); + if (classpathResource.isPresent()) { + File file = classpathResource.get(); + if (file.isDirectory()) { + return javaSourceLinkUrl(project, fqName, file); + } else if (file.getName().endsWith(JAR)) { + return jarSourceLinkUrl(project, fqName, file); + } + } + return Optional.empty(); + } + + private static Optional javaSourceLinkUrl(IJavaProject project, String javaRelativePath, String fqName) { + Path path = Paths.get(javaRelativePath); + Optional sourceResource = project.getClasspath().getSourceFolders().stream() + .map(r -> Paths.get(r).resolve(path)).filter(r -> Files.exists(r)).findFirst(); + if (sourceResource.isPresent()) { + StringBuilder sb = new StringBuilder(); + sb.append("file://"); + sb.append(sourceResource.get().toString()); + return Optional.of(sb.toString()); + } + return Optional.empty(); + } + + private static Optional javaSourceLinkUrl(IJavaProject project, String fqName, File containerFolder) { + IClasspath classpath = project.getClasspath(); + if (containerFolder.toPath().startsWith(classpath.getOutputFolder())) { + int innerTypeIdx = fqName.indexOf('$'); + String topLevelType = innerTypeIdx > 0 ? fqName.substring(0, innerTypeIdx) : fqName; + return javaSourceLinkUrl(project, topLevelType.replaceAll("\\.", "/") + JAVA, fqName); + } + return Optional.empty(); + } + + private static Optional jarSourceLinkUrl(IJavaProject project, String fqName, File jarFile) { + try { + int lastDotIndex = fqName.lastIndexOf('.'); + String packageName = fqName.substring(0, lastDotIndex); + String typeName = fqName.substring(lastDotIndex + 1); + String jarFileName = jarFile.getName(); + StringBuilder sb = new StringBuilder(); + sb.append("jdt://contents/"); + sb.append(jarFileName); + sb.append("/"); + sb.append(packageName); + sb.append("/"); + sb.append(typeName); + sb.append(CLASS); + sb.append("?"); + + StringBuilder query = new StringBuilder(); + query.append("="); + query.append(project.getElementName()); + query.append("/"); + String convertedPath = String.join("\\/", jarFile.toString().split(File.separator)); + query.append(convertedPath); + query.append("<"); + query.append(packageName); + query.append("("); + query.append(typeName); + query.append(CLASS); + + sb.append(URLEncoder.encode(query.toString(), "UTF8")); + + return Optional.of(sb.toString()); + } catch (Throwable t) { + Log.log(t); + } + return Optional.empty(); + } + +// private static Optional findTypeRange(CompilationUnit cu, String fqName) { +// int[] values = new int[] {0, -1}; +// int lastDotIndex = fqName.lastIndexOf('.'); +// String packageName = fqName.substring(0, lastDotIndex); +// String typeName = fqName.substring(lastDotIndex + 1); +// if (packageName.equals(cu.getPackage().getName().getFullyQualifiedName())) { +// Stack visitedType = new Stack<>(); +// cu.accept(new ASTVisitor() { +// +// @Override +// public boolean visit(TypeDeclaration node) { +// if (values[1] < 0) { +// visitedType.push(node.getName().getIdentifier()); +// if (String.join("$", visitedType.toArray(new String[visitedType.size()])).equals(typeName)) { +// values[0] = node.getName().getStartPosition(); +// values[1] = node.getName().getLength(); +// } +// } +// return values[1] < 0; +// } +// +// @Override +// public void endVisit(TypeDeclaration node) { +// visitedType.pop(); +// super.endVisit(node); +// } +// +// }); +// } +// return Optional.ofNullable(values[1] < 0 ? null : new Region(values[0], values[1])); +// } + +} diff --git a/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/utils/test/VSCodeSourceLinksTest.java b/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/utils/test/VSCodeSourceLinksTest.java new file mode 100644 index 000000000..8ff19be92 --- /dev/null +++ b/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/utils/test/VSCodeSourceLinksTest.java @@ -0,0 +1,91 @@ +/******************************************************************************* + * 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.java.utils.test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.net.URL; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Optional; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.springframework.ide.vscode.boot.java.utils.SourceLinks; +import org.springframework.ide.vscode.commons.languageserver.util.LspClient; +import org.springframework.ide.vscode.commons.maven.MavenBuilder; +import org.springframework.ide.vscode.commons.maven.MavenCore; +import org.springframework.ide.vscode.commons.maven.java.MavenJavaProject; + +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; + +/** + * Tests for creation of VSCode links in hover documentation + * + * @author Alex Boyko + * + */ +public class VSCodeSourceLinksTest { + + private static LoadingCache mavenProjectsCache = CacheBuilder.newBuilder().build(new CacheLoader() { + + @Override + public MavenJavaProject load(String projectName) throws Exception { + Path testProjectPath = Paths.get(VSCodeSourceLinksTest.class.getResource("/test-projects/" + projectName).toURI()); + MavenBuilder.newBuilder(testProjectPath).clean().pack().javadoc().skipTests().execute(); + return new MavenJavaProject(MavenCore.getDefault(), testProjectPath.resolve(MavenCore.POM_XML).toFile()); + } + + }); + + @Before + public void setupAll() throws Exception { + System.setProperty("sts.lsp.client", LspClient.Client.VSCODE.toString()); + } + + @After + public void tearDownAll() throws Exception { + System.setProperty("sts.lsp.client", ""); + } + + @Test + public void testJavaSourceUrl() throws Exception { + MavenJavaProject project = mavenProjectsCache.get("empty-boot-15-web-app"); + Optional url = SourceLinks.sourceLinkUrl(project, "com.example.EmptyBoot15WebAppApplication"); + assertTrue(url.isPresent()); + Path projectPath = Paths.get(project.pom().getParent()); + Path relativePath = projectPath.relativize(Paths.get(new URL(url.get()).toURI())); + assertEquals(Paths.get("src/main/java/com/example/EmptyBoot15WebAppApplication.java"), relativePath); + } + + @Test + public void testJarUrl() throws Exception { + MavenJavaProject project = mavenProjectsCache.get("empty-boot-15-web-app"); + Optional url = SourceLinks.sourceLinkUrl(project, "org.springframework.boot.autoconfigure.SpringBootApplication"); + assertTrue(url.isPresent()); + String headerPart = url.get().substring(0, url.get().indexOf('?')); + assertEquals("jdt://contents/spring-boot-autoconfigure-1.5.8.RELEASE.jar/org.springframework.boot.autoconfigure/SpringBootApplication.class", headerPart); + } + + @Test + public void testJarUrlInnerType() throws Exception { + MavenJavaProject project = mavenProjectsCache.get("empty-boot-15-web-app"); + Optional url = SourceLinks.sourceLinkUrl(project, "org.springframework.web.client.RestTemplate$AcceptHeaderRequestCallback"); + assertTrue(url.isPresent()); + String headerPart = url.get().substring(0, url.get().indexOf('?')); + assertEquals("jdt://contents/spring-web-4.3.12.RELEASE.jar/org.springframework.web.client/RestTemplate$AcceptHeaderRequestCallback.class", headerPart); + } + +} diff --git a/headless-services/commons/commons-gradle/src/main/java/org/springframework/ide/vscode/commons/gradle/GradleProjectClasspath.java b/headless-services/commons/commons-gradle/src/main/java/org/springframework/ide/vscode/commons/gradle/GradleProjectClasspath.java index 22584b82b..cbffe7b47 100644 --- a/headless-services/commons/commons-gradle/src/main/java/org/springframework/ide/vscode/commons/gradle/GradleProjectClasspath.java +++ b/headless-services/commons/commons-gradle/src/main/java/org/springframework/ide/vscode/commons/gradle/GradleProjectClasspath.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2017 Pivotal, Inc. + * Copyright (c) 2017, 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 @@ -236,5 +236,10 @@ public class GradleProjectClasspath extends JandexClasspath { return false; } - + @Override + public ImmutableList getSourceFolders() { + return ImmutableList.copyOf(project.getSourceDirectories().stream() + .map(dir -> dir.getDirectory().toPath().toString()).collect(Collectors.toList())); + } + } diff --git a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/jandex/JandexClasspath.java b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/jandex/JandexClasspath.java index 63d200b0e..78b5878c9 100644 --- a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/jandex/JandexClasspath.java +++ b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/jandex/JandexClasspath.java @@ -1,7 +1,18 @@ +/******************************************************************************* + * Copyright (c) 2017, 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.jandex; import java.io.File; import java.nio.file.Path; +import java.util.Optional; import java.util.function.Predicate; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -17,6 +28,12 @@ import com.google.common.base.Suppliers; import reactor.core.publisher.Flux; import reactor.util.function.Tuple2; +/** + * Classpath with Jandex Java index for searching types + * + * @author Alex Boyko + * + */ public abstract class JandexClasspath implements IClasspath { public static JavadocProviderTypes providerType = JavadocProviderTypes.HTML; @@ -81,6 +98,11 @@ public abstract class JandexClasspath implements IClasspath { return JandexIndex.getIndexFolder(); } + @Override + public Optional findClasspathResourceContainer(String fqName) { + return javaIndex.get().findClasspathResourceForType(fqName); + } + abstract protected IJavadocProvider createParserJavadocProvider(File classpathResource); abstract protected IJavadocProvider createHtmlJavdocProvider(File classpathResource); diff --git a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/jandex/JandexIndex.java b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/jandex/JandexIndex.java index 791416d2e..9c992d6a5 100644 --- a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/jandex/JandexIndex.java +++ b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/jandex/JandexIndex.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2016-2017 Pivotal, Inc. + * Copyright (c) 2016, 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 @@ -227,6 +227,27 @@ public class JandexIndex { .orElse(null)); } + + private Optional> findMatch(DotName fqName) { + return (baseIndex == null ? Stream.>>empty() + : Arrays.stream( + baseIndex) + .filter( + jandexIndex -> jandexIndex != null) + .map(jandexIndex -> jandexIndex.findMatch(fqName))).filter(o -> o.isPresent()) + .findFirst() + // If not found look at indices owned by this + // JandexIndex instance + .orElseGet(() -> streamOfIndices() + .map(e -> Tuples.of(e.getT1(), Optional.ofNullable(e.getT2().getClassByName(fqName)))) + .filter(t -> t.getT2().isPresent()) + .map(e -> Tuples.of(e.getT1(), e.getT2().get())).findFirst()); + } + + public Optional findClasspathResourceForType(String fqName) { + Optional> match = findMatch(DotName.createSimple(fqName)); + return Optional.ofNullable(match.isPresent() ? match.get().getT1() : null); + } private IType createType(Tuple2 match) { File classpathResource = match.getT1(); diff --git a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/DelegatingCachedClasspath.java b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/DelegatingCachedClasspath.java index 9afefce9c..788d5d3a7 100644 --- a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/DelegatingCachedClasspath.java +++ b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/DelegatingCachedClasspath.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2017 Pivotal, Inc. + * Copyright (c) 2017, 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 @@ -18,6 +18,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.Collections; import java.util.LinkedHashSet; +import java.util.Optional; import java.util.Set; import java.util.concurrent.Callable; import java.util.concurrent.atomic.AtomicReference; @@ -255,5 +256,17 @@ public class DelegatingCachedClasspath implements IClasspa new LinkedHashSet<>(newDelegate.getClasspathResources()), newDelegate.getOutputFolder()); } } + + @Override + public ImmutableList getSourceFolders() { + T t = cachedDelegate.get(); + return t == null ? ImmutableList.of() : t.getSourceFolders(); + } + + @Override + public Optional findClasspathResourceContainer(String fqName) { + T t = cachedDelegate.get(); + return t == null ? Optional.empty() : t.findClasspathResourceContainer(fqName); + } } diff --git a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/IClasspath.java b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/IClasspath.java index 79722f8c3..b750729c3 100644 --- a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/IClasspath.java +++ b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/IClasspath.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2016 Pivotal, Inc. + * Copyright (c) 2016, 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 @@ -10,7 +10,9 @@ *******************************************************************************/ package org.springframework.ide.vscode.commons.java; +import java.io.File; import java.nio.file.Path; +import java.util.Optional; import java.util.function.Predicate; import com.google.common.collect.ImmutableList; @@ -54,5 +56,9 @@ public interface IClasspath { * @return classpath resource relative paths */ ImmutableList getClasspathResources(); + + ImmutableList getSourceFolders(); + + Optional findClasspathResourceContainer(String fqName); } diff --git a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/LspClient.java b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/LspClient.java new file mode 100644 index 000000000..77cb8e56e --- /dev/null +++ b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/LspClient.java @@ -0,0 +1,46 @@ +/******************************************************************************* + * 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.util; + +/** + * LSP Client information utilities + * + * @author Alex Boyko + * + */ +public class LspClient { + + public enum Client { + UNKNOWN, + VSCODE, + ECLIPSE, + ATOM, + INTELLIJ + } + + /** + * Extracts the LSP client from the current environment + * @return current LSP client + */ + public static Client currentClient() { + Client client = Client.UNKNOWN; + String clientStr = System.getProperty("sts.lsp.client"); + if (clientStr != null) { + try { + client = Client.valueOf(clientStr.toUpperCase()); + } catch (IllegalArgumentException e) { + // ignore + } + } + return client; + } + +} diff --git a/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/MavenCore.java b/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/MavenCore.java index 631b033b1..99bf15cbe 100644 --- a/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/MavenCore.java +++ b/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/MavenCore.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2016 Pivotal, Inc. + * Copyright (c) 2016, 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 @@ -293,7 +293,7 @@ public class MavenCore { public Stream getJreLibs() throws MavenException { String s = (String) maven.createExecutionRequest().getSystemProperties().get(JAVA_BOOT_CLASS_PATH); - return Arrays.stream(s.split(File.pathSeparator)).map(File::new).filter(f -> f.canRead()).map(f -> Paths.get(f.toURI())); + return s == null ? Stream.empty() : Arrays.stream(s.split(File.pathSeparator)).map(File::new).filter(f -> f.canRead()).map(f -> Paths.get(f.toURI())); } public String getJavaRuntimeVersion() throws MavenException { diff --git a/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/MavenProjectClasspath.java b/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/MavenProjectClasspath.java index 1cb961c15..a76868e46 100644 --- a/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/MavenProjectClasspath.java +++ b/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/MavenProjectClasspath.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2016 Pivotal, Inc. + * Copyright (c) 2016, 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 @@ -85,7 +85,7 @@ public class MavenProjectClasspath extends JandexClasspath { } public String getName() { - return project == null ? null : project.getName(); + return project == null ? null : project.getArtifact().getArtifactId(); } @Override @@ -262,4 +262,9 @@ public class MavenProjectClasspath extends JandexClasspath { return false; } + @Override + public ImmutableList getSourceFolders() { + return ImmutableList.of(project.getBuild().getSourceDirectory()); + } + } diff --git a/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/classpathfile/FileClasspath.java b/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/classpathfile/FileClasspath.java index ea9f8da65..1888f5cbb 100644 --- a/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/classpathfile/FileClasspath.java +++ b/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/classpathfile/FileClasspath.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2016 Pivotal, Inc. + * Copyright (c) 2016, 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 @@ -10,8 +10,10 @@ *******************************************************************************/ package org.springframework.ide.vscode.commons.maven.java.classpathfile; +import java.io.File; import java.nio.file.Files; import java.nio.file.Path; +import java.util.Optional; import java.util.function.Predicate; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -87,4 +89,14 @@ public class FileClasspath implements IClasspath { return null; } + @Override + public ImmutableList getSourceFolders() { + return ImmutableList.of(); + } + + @Override + public Optional findClasspathResourceContainer(String fqName) { + return Optional.empty(); + } + } diff --git a/headless-services/commons/commons-maven/src/test/java/org/springframework/ide/vscode/commons/maven/JavaIndexTest.java b/headless-services/commons/commons-maven/src/test/java/org/springframework/ide/vscode/commons/maven/JavaIndexTest.java index 0fe710e24..e556eff8b 100644 --- a/headless-services/commons/commons-maven/src/test/java/org/springframework/ide/vscode/commons/maven/JavaIndexTest.java +++ b/headless-services/commons/commons-maven/src/test/java/org/springframework/ide/vscode/commons/maven/JavaIndexTest.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2016-2017 Pivotal, Inc. + * Copyright (c) 2016, 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 @@ -16,10 +16,12 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import java.io.File; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Collections; import java.util.List; +import java.util.Optional; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -133,4 +135,21 @@ public class JavaIndexTest { assertEquals(IVoidType.DEFAULT, m.getReturnType()); assertEquals(Collections.singletonList(IPrimitiveType.INT), m.parameters().collect(Collectors.toList())); } + + @Test + public void testFindJarResource() throws Exception { + MavenJavaProject project = mavenProjectsCache.get("gs-rest-service-cors-boot-1.4.1-with-classpath-file"); + Optional jar = project.getClasspath().findClasspathResourceContainer("org.springframework.boot.autoconfigure.SpringBootApplication"); + assertTrue(jar.isPresent()); + assertEquals("spring-boot-autoconfigure-1.4.1.RELEASE.jar", jar.get().getName()); + } + + @Test + public void testFindJavaResource() throws Exception { + MavenJavaProject project = mavenProjectsCache.get("gs-rest-service-cors-boot-1.4.1-with-classpath-file"); + Optional file = project.getClasspath().findClasspathResourceContainer("hello.GreetingController"); + assertTrue(file.isPresent()); + assertTrue(file.get().exists()); + assertEquals(project.getClasspath().getOutputFolder().toString(), file.get().toString()); + } } diff --git a/vscode-extensions/commons-vscode/src/launch-util.ts b/vscode-extensions/commons-vscode/src/launch-util.ts index 39a31b720..ef7bde2bc 100644 --- a/vscode-extensions/commons-vscode/src/launch-util.ts +++ b/vscode-extensions/commons-vscode/src/launch-util.ts @@ -108,6 +108,7 @@ export function activate(options: ActivatorOptions, context: VSCode.ExtensionCon log('Redirecting server logs to ' + logfile); const args = [ '-Dserver.port=' + port, + '-Dsts.lsp.client=vscode', '-Dorg.slf4j.simpleLogger.logFile=' + logfile ]; if (options.classpath) {