@@ -4,10 +4,63 @@ import org.eclipse.lsp4j.Location
4
4
import org.javacs.kt.CompiledFile
5
5
import org.javacs.kt.position.location
6
6
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
7
13
8
14
fun goToDefinition (file : CompiledFile , cursor : Int ): Location ? {
9
15
val (_, target) = file.referenceAtPoint(cursor) ? : return null
10
16
// TODO go to declaration name rather than beginning of javadoc comment
11
17
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