Skip to content

Support definitions on JDK symbols #339

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Apr 5, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 21 additions & 11 deletions server/src/main/kotlin/org/javacs/kt/CompiledFile.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,14 @@ package org.javacs.kt

import com.intellij.openapi.util.TextRange
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiIdentifier
import org.javacs.kt.compiler.CompilationKind
import org.javacs.kt.position.changedRegion
import org.javacs.kt.position.position
import org.javacs.kt.util.findParent
import org.javacs.kt.util.nullResult
import org.javacs.kt.util.toPath
import org.jetbrains.kotlin.container.ComponentProvider
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
Expand All @@ -23,7 +22,7 @@ class CompiledFile(
val content: String,
val parse: KtFile,
val compile: BindingContext,
val container: ComponentProvider,
val module: ModuleDescriptor,
val sourcePath: Collection<KtFile>,
val classPath: CompilerClassPath,
val isScript: Boolean = false,
Expand All @@ -33,7 +32,7 @@ class CompiledFile(
* Find the type of the expression at `cursor`
*/
fun typeAtPoint(cursor: Int): KotlinType? {
var cursorExpr = parseAtPoint(cursor, asReference = true)?.findParent<KtExpression>() ?: return nullResult("Couldn't find expression at ${describePosition(cursor)}")
val cursorExpr = parseAtPoint(cursor, asReference = true)?.findParent<KtExpression>() ?: return nullResult("Couldn't find expression at ${describePosition(cursor)}")
val surroundingExpr = expandForType(cursor, cursorExpr)
val scope = scopeAtPoint(cursor) ?: return nullResult("Couldn't find scope at ${describePosition(cursor)}")
return typeOfExpression(surroundingExpr, scope)
Expand All @@ -53,16 +52,33 @@ class CompiledFile(
else return surroundingExpr
}

/**
* Looks for a reference expression at the given cursor.
* This is currently used by many features in the language server.
* Unfortunately, it fails to find declarations for JDK symbols.
* [referenceExpressionAtPoint] provides an alternative implementation that can find JDK symbols.
* It cannot, however, replace this method at the moment.
* TODO: Investigate why this method doesn't find JDK symbols.
*/
fun referenceAtPoint(cursor: Int): Pair<KtExpression, DeclarationDescriptor>? {
val element = parseAtPoint(cursor, asReference = true)
var cursorExpr = element?.findParent<KtExpression>() ?: return nullResult("Couldn't find expression at ${describePosition(cursor)} (only found $element)")
val cursorExpr = element?.findParent<KtExpression>() ?: return nullResult("Couldn't find expression at ${describePosition(cursor)} (only found $element)")
val surroundingExpr = expandForReference(cursor, cursorExpr)
val scope = scopeAtPoint(cursor) ?: return nullResult("Couldn't find scope at ${describePosition(cursor)}")
val context = bindingContextOf(surroundingExpr, scope)
LOG.info("Hovering {}", surroundingExpr)
return referenceFromContext(cursor, context)
}

/**
* Looks for a reference expression at the given cursor.
* This method is similar to [referenceAtPoint], but the latter fails to find declarations for JDK symbols.
* This method should not be used for anything other than finding definitions (at least for now).
*/
fun referenceExpressionAtPoint(cursor: Int): Pair<KtExpression, DeclarationDescriptor>? {
return referenceFromContext(cursor, compile)
}

private fun referenceFromContext(cursor: Int, context: BindingContext): Pair<KtExpression, DeclarationDescriptor>? {
val targets = context.getSliceContents(BindingContext.REFERENCE_TARGET)
return targets.asSequence()
Expand Down Expand Up @@ -203,9 +219,3 @@ class CompiledFile(
return "$file ${start.line}:${start.character + 1}-${end.line + 1}:${end.character + 1}"
}
}

private fun fileName(file: KtFile): String {
val parts = file.name.split('/')

return parts.last()
}
1 change: 1 addition & 0 deletions server/src/main/kotlin/org/javacs/kt/CompilerClassPath.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ class CompilerClassPath(private val config: CompilerConfiguration) : Closeable {
private val javaSourcePath = mutableSetOf<Path>()
private val buildScriptClassPath = mutableSetOf<Path>()
val classPath = mutableSetOf<ClassPathEntry>()
val javaHome: String? = System.getProperty("java.home", null)

var compiler = Compiler(javaSourcePath, classPath.map { it.compiledJar }.toSet(), buildScriptClassPath)
private set
Expand Down
8 changes: 3 additions & 5 deletions server/src/main/kotlin/org/javacs/kt/KotlinLanguageServer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,13 @@ import org.eclipse.lsp4j.services.LanguageClient
import org.eclipse.lsp4j.services.LanguageClientAware
import org.eclipse.lsp4j.services.LanguageServer
import org.javacs.kt.command.ALL_COMMANDS
import org.javacs.kt.externalsources.JarClassContentProvider
import org.javacs.kt.externalsources.ClassPathSourceJarProvider
import org.javacs.kt.externalsources.*
import org.javacs.kt.util.AsyncExecutor
import org.javacs.kt.util.TemporaryDirectory
import org.javacs.kt.util.parseURI
import org.javacs.kt.progress.Progress
import org.javacs.kt.progress.LanguageClientProgress
import org.javacs.kt.semantictokens.semanticTokensLegend
import java.net.URI
import java.io.Closeable
import java.nio.file.Paths
import java.util.concurrent.CompletableFuture
Expand All @@ -26,11 +24,11 @@ class KotlinLanguageServer : LanguageServer, LanguageClientAware, Closeable {
val classPath = CompilerClassPath(config.compiler)

private val tempDirectory = TemporaryDirectory()
private val uriContentProvider = URIContentProvider(JarClassContentProvider(config.externalSources, classPath, tempDirectory, ClassPathSourceJarProvider(classPath)))
private val uriContentProvider = URIContentProvider(ClassContentProvider(config.externalSources, classPath, tempDirectory, CompositeSourceArchiveProvider(JdkSourceArchiveProvider(classPath), ClassPathSourceArchiveProvider(classPath))))
val sourcePath = SourcePath(classPath, uriContentProvider, config.indexing)
val sourceFiles = SourceFiles(sourcePath, uriContentProvider)

private val textDocuments = KotlinTextDocumentService(sourceFiles, sourcePath, config, tempDirectory, uriContentProvider)
private val textDocuments = KotlinTextDocumentService(sourceFiles, sourcePath, config, tempDirectory, uriContentProvider, classPath)
private val workspaces = KotlinWorkspaceService(sourceFiles, sourcePath, classPath, textDocuments, config)
private val protocolExtensions = KotlinProtocolExtensionService(uriContentProvider)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,8 @@
package org.javacs.kt

import org.eclipse.lsp4j.*
import org.javacs.kt.externalsources.JarClassContentProvider
import org.javacs.kt.externalsources.toKlsURI
import org.javacs.kt.util.AsyncExecutor
import org.javacs.kt.util.noResult
import org.javacs.kt.util.parseURI
import java.net.URI
import java.net.URISyntaxException
import java.util.concurrent.CompletableFuture

class KotlinProtocolExtensionService(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import org.javacs.kt.codeaction.codeActions
import org.javacs.kt.completion.*
import org.javacs.kt.definition.goToDefinition
import org.javacs.kt.diagnostic.convertDiagnostic
import org.javacs.kt.externalsources.JarClassContentProvider
import org.javacs.kt.formatting.formatKotlinCode
import org.javacs.kt.hover.hoverAt
import org.javacs.kt.position.offset
Expand All @@ -26,13 +25,11 @@ import org.javacs.kt.util.TemporaryDirectory
import org.javacs.kt.util.parseURI
import org.javacs.kt.util.describeURI
import org.javacs.kt.util.describeURIs
import org.javacs.kt.command.JAVA_TO_KOTLIN_COMMAND
import org.javacs.kt.rename.renameSymbol
import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics
import java.net.URI
import java.io.Closeable
import java.nio.file.Path
import java.nio.file.Paths
import java.time.Duration
import java.util.concurrent.CompletableFuture

Expand All @@ -41,7 +38,8 @@ class KotlinTextDocumentService(
private val sp: SourcePath,
private val config: Configuration,
private val tempDirectory: TemporaryDirectory,
private val uriContentProvider: URIContentProvider
private val uriContentProvider: URIContentProvider,
private val cp: CompilerClassPath
) : TextDocumentService, Closeable {
private lateinit var client: LanguageClient
private val async = AsyncExecutor()
Expand Down Expand Up @@ -118,7 +116,7 @@ class KotlinTextDocumentService(
LOG.info("Go-to-definition at {}", describePosition(position))

val (file, cursor) = recover(position, Recompile.NEVER)
goToDefinition(file, cursor, uriContentProvider.jarClassContentProvider, tempDirectory, config.externalSources)
goToDefinition(file, cursor, uriContentProvider.classContentProvider, tempDirectory, config.externalSources, cp)
?.let(::listOf)
?.let { Either.forLeft<List<Location>, List<LocationLink>>(it) }
?: noResult("Couldn't find definition at ${describePosition(position)}", Either.forLeft(emptyList()))
Expand Down
32 changes: 14 additions & 18 deletions server/src/main/kotlin/org/javacs/kt/SourcePath.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@ import org.javacs.kt.util.describeURI
import org.javacs.kt.index.SymbolIndex
import org.javacs.kt.progress.Progress
import com.intellij.lang.Language
import org.jetbrains.kotlin.container.ComponentProvider
import org.jetbrains.kotlin.container.getService
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.BindingContext
Expand Down Expand Up @@ -48,7 +46,7 @@ class SourcePath(
var parsed: KtFile? = null,
var compiledFile: KtFile? = null,
var compiledContext: BindingContext? = null,
var compiledContainer: ComponentProvider? = null,
var module: ModuleDescriptor? = null,
val language: Language? = null,
val isTemporary: Boolean = false // A temporary source file will not be returned by .all()
) {
Expand All @@ -66,7 +64,7 @@ class SourcePath(
parsed = null
compiledFile = null
compiledContext = null
compiledContainer = null
module = null
}

fun parse() {
Expand Down Expand Up @@ -97,10 +95,10 @@ class SourcePath(

val oldFile = clone()

val (context, container) = cp.compiler.compileKtFile(parsed!!, allIncludingThis(), kind)
val (context, module) = cp.compiler.compileKtFile(parsed!!, allIncludingThis(), kind)
parseDataWriteLock.withLock {
compiledContext = context
compiledContainer = container
this.module = module
compiledFile = parsed
}

Expand All @@ -117,15 +115,15 @@ class SourcePath(
parseIfChanged().apply { compileIfNull() }.let { doPrepareCompiledFile() }

private fun doPrepareCompiledFile(): CompiledFile =
CompiledFile(content, compiledFile!!, compiledContext!!, compiledContainer!!, allIncludingThis(), cp, isScript, kind)
CompiledFile(content, compiledFile!!, compiledContext!!, module!!, allIncludingThis(), cp, isScript, kind)

private fun allIncludingThis(): Collection<KtFile> = parseIfChanged().let {
if (isTemporary) (all().asSequence() + sequenceOf(parsed!!)).toList()
else all()
}

// Creates a shallow copy
fun clone(): SourceFile = SourceFile(uri, content, path, parsed, compiledFile, compiledContext, compiledContainer, language, isTemporary)
fun clone(): SourceFile = SourceFile(uri, content, path, parsed, compiledFile, compiledContext, module, language, isTemporary)
}

private fun sourceFile(uri: URI): SourceFile {
Expand Down Expand Up @@ -214,7 +212,7 @@ class SourcePath(
// Get all the files. This will parse them if they changed
val allFiles = all()
beforeCompileCallback.invoke()
val (context, container) = cp.compiler.compileKtFiles(parse.values, allFiles, kind)
val (context, module) = cp.compiler.compileKtFiles(parse.values, allFiles, kind)

// Update cache
for ((f, parsed) in parse) {
Expand All @@ -223,7 +221,7 @@ class SourcePath(
//only updated if the parsed file didn't change:
f.compiledFile = parsed
f.compiledContext = context
f.compiledContainer = container
f.module = module
}
}
}
Expand Down Expand Up @@ -257,9 +255,9 @@ class SourcePath(
fun refreshDependencyIndexes() {
compileAllFiles()

val container = files.values.first { it.compiledContainer != null }.compiledContainer
if (container != null) {
refreshDependencyIndexes(container)
val module = files.values.first { it.module != null }.module
if (module != null) {
refreshDependencyIndexes(module)
}
}

Expand All @@ -279,9 +277,8 @@ class SourcePath(
/**
* Refreshes the indexes. If already done, refreshes only the declarations in the files that were changed.
*/
private fun refreshDependencyIndexes(container: ComponentProvider) = indexAsync.execute {
private fun refreshDependencyIndexes(module: ModuleDescriptor) = indexAsync.execute {
if (indexEnabled) {
val module = container.getService(ModuleDescriptor::class.java)
val declarations = getDeclarationDescriptors(files.values)
index.refresh(module, declarations)
}
Expand All @@ -291,9 +288,8 @@ class SourcePath(
private fun getDeclarationDescriptors(files: Collection<SourceFile>) =
files.flatMap { file ->
val compiledFile = file.compiledFile ?: file.parsed
val compiledContainer = file.compiledContainer
if (compiledFile != null && compiledContainer != null) {
val module = compiledContainer.getService(ModuleDescriptor::class.java)
val module = file.module
if (compiledFile != null && module != null) {
module.getPackage(compiledFile.packageFqName).memberScope.getContributedDescriptors(
DescriptorKindFilter.ALL
) { name -> compiledFile.declarations.map { it.name }.contains(name.toString()) }
Expand Down
11 changes: 4 additions & 7 deletions server/src/main/kotlin/org/javacs/kt/URIContentProvider.kt
Original file line number Diff line number Diff line change
@@ -1,24 +1,21 @@
package org.javacs.kt

import java.net.URI
import java.net.URLDecoder
import java.nio.charset.StandardCharsets
import java.nio.file.Paths
import org.javacs.kt.externalsources.JarClassContentProvider
import org.javacs.kt.externalsources.ClassContentProvider
import org.javacs.kt.externalsources.toKlsURI
import org.javacs.kt.util.KotlinLSException
import org.javacs.kt.util.partitionAroundLast

/**
* Fetches the content of Kotlin files identified by a URI.
*/
class URIContentProvider(
val jarClassContentProvider: JarClassContentProvider
val classContentProvider: ClassContentProvider
) {
fun contentOf(uri: URI): String = when (uri.scheme) {
"file" -> Paths.get(uri).toFile().readText()
"kls" -> uri.toKlsURI()?.let { jarClassContentProvider.contentOf(it).second }
?: throw KotlinLSException("Could not find ${uri}")
"kls" -> uri.toKlsURI()?.let { classContentProvider.contentOf(it).second }
?: throw KotlinLSException("Could not find $uri")
else -> throw KotlinLSException("Unrecognized scheme ${uri.scheme}")
}
}
Loading