Skip to content

Commit fbe96e5

Browse files
committed
Bytecode support for 'Go to definition' - fixes #23
1 parent 191c85b commit fbe96e5

File tree

1 file changed

+55
-2
lines changed

1 file changed

+55
-2
lines changed

src/main/kotlin/org/javacs/kt/definition/goToDefinition.kt

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,63 @@ import org.eclipse.lsp4j.Location
44
import org.javacs.kt.CompiledFile
55
import org.javacs.kt.position.location
66
import org.javacs.kt.LOG
7+
import org.javacs.kt.util.KotlinLSException
8+
import java.util.jar.JarFile
9+
import java.net.URI
10+
import java.nio.file.Paths
11+
import java.nio.file.Files
12+
import java.io.File
713

814
fun goToDefinition(file: CompiledFile, cursor: Int): Location? {
915
val (_, target) = file.referenceAtPoint(cursor) ?: return null
1016
// TODO go to declaration name rather than beginning of javadoc comment
1117
LOG.info("Found declaration descriptor $target")
12-
return location(target)
13-
}
18+
val destination = location(target)
19+
20+
if (destination != null) {
21+
val rawURI = destination.uri
22+
if (isInsideJar(rawURI)) {
23+
destination.uri = readCompiledClassToTemporaryFile(rawURI)
24+
}
25+
}
26+
27+
return destination
28+
}
29+
30+
private fun isInsideJar(uri: String) = uri.contains(".jar!")
31+
32+
private fun readCompiledClassToTemporaryFile(uriWithJar: String): String {
33+
val splittedUri = uriWithJar.split(".jar!")
34+
val jarUri = splittedUri[0] + ".jar"
35+
val jarPath = Paths.get(URI.create(jarUri))
36+
val classInJarPath = Paths.get(trimLeadingPathSeparator(splittedUri[1]))
37+
val className = classInJarPath.fileName.toString().trimSuffixIfPresent(".class")
38+
39+
JarFile(jarPath.toFile()).use { jarFile ->
40+
for (jarEntry in jarFile.entries()) {
41+
val jarEntryPath = Paths.get(jarEntry.name)
42+
43+
if (classInJarPath.equals(jarEntryPath)) {
44+
// Found the correct class inside of the JAR file
45+
val tmp = Files.createTempFile(className, ".class").toFile()
46+
tmp.deleteOnExit() // Make sure the file is deleted upon exit
47+
48+
tmp.outputStream().use {
49+
jarFile.getInputStream(jarEntry).copyTo(it)
50+
}
51+
52+
return tmp.toURI().toString()
53+
}
54+
}
55+
}
56+
57+
throw KotlinLSException("Could not find $classInJarPath in ${jarPath.fileName}")
58+
}
59+
60+
private fun trimLeadingPathSeparator(path: String): String {
61+
val firstChar = path[0]
62+
return if (firstChar == '/' || firstChar == '\\') path.substring(1) else path
63+
}
64+
65+
private fun String.trimSuffixIfPresent(suffix: String) =
66+
if (endsWith(suffix)) substring(0, length - suffix.length) else this

0 commit comments

Comments
 (0)