diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/links/SourceLinks.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/links/SourceLinks.java index bbbe4426e..84fb7eadc 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/links/SourceLinks.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/links/SourceLinks.java @@ -45,6 +45,8 @@ import org.springframework.ide.vscode.commons.languageserver.java.ls.Classpath.C */ public interface SourceLinks { + static final String WEB_INF_CLASSES = "/WEB-INF/classes/"; + static final Logger log = LoggerFactory.getLogger(SourceLinks.class); static final String JAR = ".jar"; @@ -107,10 +109,26 @@ public interface SourceLinks { return Optional.empty(); } + static Optional sourceLinkUrlForClasspathResourceOnTomcat(SourceLinks sourceLinks, JavaProjectFinder projectFinder, String path) { + int indexOfWenInfClasses = path.indexOf(WEB_INF_CLASSES); + String projectName = Paths.get(path.substring(0, indexOfWenInfClasses)).getFileName().toString(); + String fqName = path.substring(0, path.lastIndexOf(CLASS)).substring(indexOfWenInfClasses + WEB_INF_CLASSES.length()).replace('/', '.'); + for (IJavaProject p : projectFinder.all()) { + if (projectName.equals(p.getElementName())) { + return sourceLinks.sourceLinkUrlForFQName(p, fqName); + } + } + return sourceLinks.sourceLinkUrlForFQName(null, fqName); + } + public static Optional sourceLinkUrlForClasspathResource(SourceLinks sourceLinks, JavaProjectFinder projectFinder, String path) { if (projectFinder != null) { int idx = path.lastIndexOf(CLASS); if (idx >= 0) { + int web_inf_index = path.indexOf(WEB_INF_CLASSES); + if (web_inf_index >= 0) { + return sourceLinkUrlForClasspathResourceOnTomcat(sourceLinks, projectFinder, path); + } Path filePath = Paths.get(path.substring(0, idx)); IJavaProject project = projectFinder.find(new TextDocumentIdentifier(filePath.toUri().toString())).orElse(null); if (project == null) { diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/utils/test/VSCodeSourceLinksTest.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/utils/test/VSCodeSourceLinksTest.java index 8fbab1a72..e59d46501 100644 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/utils/test/VSCodeSourceLinksTest.java +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/utils/test/VSCodeSourceLinksTest.java @@ -17,12 +17,17 @@ import java.io.File; import java.net.URI; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.Collection; import java.util.Optional; +import org.eclipse.lsp4j.TextDocumentIdentifier; +import org.gradle.internal.impldep.com.google.common.collect.ImmutableList; import org.junit.Test; import org.springframework.ide.vscode.boot.java.links.VSCodeSourceLinks; import org.springframework.ide.vscode.boot.java.utils.CompilationUnitCache; +import org.springframework.ide.vscode.commons.java.IJavaProject; import org.springframework.ide.vscode.commons.javadoc.JavaDocProviders; +import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; 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; @@ -70,6 +75,35 @@ public class VSCodeSourceLinksTest { assertEquals("#7,14", positionPart); } + @Test + public void testClasspathResourceOnTomcatUrl() throws Exception { + MavenJavaProject project = mavenProjectsCache.get("empty-boot-15-web-app"); + Optional url = new VSCodeSourceLinks(new CompilationUnitCache(null, null, null), new JavaProjectFinder() { + + @Override + public Optional find(TextDocumentIdentifier doc) { + return Optional.of(project); + } + + @Override + public Collection all() { + return ImmutableList.of(project); + } + }) + .sourceLinkUrlForClasspathResource("Users/aboyko/pivotal-tc-server/instances/base/wtpwebapps/empty-boot-15-web-app/WEB-INF/classes/com/example/EmptyBoot15WebAppApplication.class"); + assertTrue(url.isPresent()); + Path projectPath = Paths.get(project.pom().getParent()); + URI uri = URI.create(url.get()); + + // Use File to get rid of the fragment parts of the URL. The URL may have fragments that indicate line and column numbers + uri = new File(uri.getPath()).toURI(); + + Path relativePath = projectPath.relativize(Paths.get(uri)); + assertEquals(Paths.get("src/main/java/com/example/EmptyBoot15WebAppApplication.java"), relativePath); + String positionPart = url.get().substring(url.get().lastIndexOf('#')); + assertEquals("#7,14", positionPart); + } + @Test public void testJarUrl() throws Exception { MavenJavaProject project = mavenProjectsCache.get("empty-boot-15-web-app");