Skip to content

Commit 3bee40e

Browse files
committed
Merge branch 'main' of github.com:fwcd/kotlin-language-server
2 parents e393dec + c7e0842 commit 3bee40e

27 files changed

+317
-192
lines changed

server/src/main/kotlin/org/javacs/kt/CompiledFile.kt

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,14 @@ package org.javacs.kt
22

33
import com.intellij.openapi.util.TextRange
44
import com.intellij.psi.PsiElement
5-
import com.intellij.psi.PsiIdentifier
65
import org.javacs.kt.compiler.CompilationKind
76
import org.javacs.kt.position.changedRegion
87
import org.javacs.kt.position.position
98
import org.javacs.kt.util.findParent
109
import org.javacs.kt.util.nullResult
1110
import org.javacs.kt.util.toPath
12-
import org.jetbrains.kotlin.container.ComponentProvider
1311
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
12+
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
1413
import org.jetbrains.kotlin.lexer.KtTokens
1514
import org.jetbrains.kotlin.psi.*
1615
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
@@ -23,7 +22,7 @@ class CompiledFile(
2322
val content: String,
2423
val parse: KtFile,
2524
val compile: BindingContext,
26-
val container: ComponentProvider,
25+
val module: ModuleDescriptor,
2726
val sourcePath: Collection<KtFile>,
2827
val classPath: CompilerClassPath,
2928
val isScript: Boolean = false,
@@ -33,7 +32,7 @@ class CompiledFile(
3332
* Find the type of the expression at `cursor`
3433
*/
3534
fun typeAtPoint(cursor: Int): KotlinType? {
36-
var cursorExpr = parseAtPoint(cursor, asReference = true)?.findParent<KtExpression>() ?: return nullResult("Couldn't find expression at ${describePosition(cursor)}")
35+
val cursorExpr = parseAtPoint(cursor, asReference = true)?.findParent<KtExpression>() ?: return nullResult("Couldn't find expression at ${describePosition(cursor)}")
3736
val surroundingExpr = expandForType(cursor, cursorExpr)
3837
val scope = scopeAtPoint(cursor) ?: return nullResult("Couldn't find scope at ${describePosition(cursor)}")
3938
return typeOfExpression(surroundingExpr, scope)
@@ -53,16 +52,33 @@ class CompiledFile(
5352
else return surroundingExpr
5453
}
5554

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

73+
/**
74+
* Looks for a reference expression at the given cursor.
75+
* This method is similar to [referenceAtPoint], but the latter fails to find declarations for JDK symbols.
76+
* This method should not be used for anything other than finding definitions (at least for now).
77+
*/
78+
fun referenceExpressionAtPoint(cursor: Int): Pair<KtExpression, DeclarationDescriptor>? {
79+
return referenceFromContext(cursor, compile)
80+
}
81+
6682
private fun referenceFromContext(cursor: Int, context: BindingContext): Pair<KtExpression, DeclarationDescriptor>? {
6783
val targets = context.getSliceContents(BindingContext.REFERENCE_TARGET)
6884
return targets.asSequence()
@@ -203,9 +219,3 @@ class CompiledFile(
203219
return "$file ${start.line}:${start.character + 1}-${end.line + 1}:${end.character + 1}"
204220
}
205221
}
206-
207-
private fun fileName(file: KtFile): String {
208-
val parts = file.name.split('/')
209-
210-
return parts.last()
211-
}

server/src/main/kotlin/org/javacs/kt/CompilerClassPath.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ class CompilerClassPath(private val config: CompilerConfiguration) : Closeable {
1717
private val javaSourcePath = mutableSetOf<Path>()
1818
private val buildScriptClassPath = mutableSetOf<Path>()
1919
val classPath = mutableSetOf<ClassPathEntry>()
20+
val javaHome: String? = System.getProperty("java.home", null)
2021

2122
var compiler = Compiler(javaSourcePath, classPath.map { it.compiledJar }.toSet(), buildScriptClassPath)
2223
private set

server/src/main/kotlin/org/javacs/kt/Configuration.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ public data class IndexingConfiguration(
3131
public data class ExternalSourcesConfiguration(
3232
/** Whether kls-URIs should be sent to the client to describe classes in JARs. */
3333
var useKlsScheme: Boolean = false,
34-
/** Whether external classes classes should be automatically converted to Kotlin. */
35-
var autoConvertToKotlin: Boolean = true
34+
/** Whether external classes should be automatically converted to Kotlin. */
35+
var autoConvertToKotlin: Boolean = false
3636
)
3737

3838
public data class Configuration(

server/src/main/kotlin/org/javacs/kt/KotlinLanguageServer.kt

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,13 @@ import org.eclipse.lsp4j.services.LanguageClient
77
import org.eclipse.lsp4j.services.LanguageClientAware
88
import org.eclipse.lsp4j.services.LanguageServer
99
import org.javacs.kt.command.ALL_COMMANDS
10-
import org.javacs.kt.externalsources.JarClassContentProvider
11-
import org.javacs.kt.externalsources.ClassPathSourceJarProvider
10+
import org.javacs.kt.externalsources.*
1211
import org.javacs.kt.util.AsyncExecutor
1312
import org.javacs.kt.util.TemporaryDirectory
1413
import org.javacs.kt.util.parseURI
1514
import org.javacs.kt.progress.Progress
1615
import org.javacs.kt.progress.LanguageClientProgress
1716
import org.javacs.kt.semantictokens.semanticTokensLegend
18-
import java.net.URI
1917
import java.io.Closeable
2018
import java.nio.file.Paths
2119
import java.util.concurrent.CompletableFuture
@@ -26,11 +24,11 @@ class KotlinLanguageServer : LanguageServer, LanguageClientAware, Closeable {
2624
val classPath = CompilerClassPath(config.compiler)
2725

2826
private val tempDirectory = TemporaryDirectory()
29-
private val uriContentProvider = URIContentProvider(JarClassContentProvider(config.externalSources, classPath, tempDirectory, ClassPathSourceJarProvider(classPath)))
27+
private val uriContentProvider = URIContentProvider(ClassContentProvider(config.externalSources, classPath, tempDirectory, CompositeSourceArchiveProvider(JdkSourceArchiveProvider(classPath), ClassPathSourceArchiveProvider(classPath))))
3028
val sourcePath = SourcePath(classPath, uriContentProvider, config.indexing)
3129
val sourceFiles = SourceFiles(sourcePath, uriContentProvider)
3230

33-
private val textDocuments = KotlinTextDocumentService(sourceFiles, sourcePath, config, tempDirectory, uriContentProvider)
31+
private val textDocuments = KotlinTextDocumentService(sourceFiles, sourcePath, config, tempDirectory, uriContentProvider, classPath)
3432
private val workspaces = KotlinWorkspaceService(sourceFiles, sourcePath, classPath, textDocuments, config)
3533
private val protocolExtensions = KotlinProtocolExtensionService(uriContentProvider)
3634

server/src/main/kotlin/org/javacs/kt/KotlinProtocolExtensionService.kt

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,8 @@
11
package org.javacs.kt
22

33
import org.eclipse.lsp4j.*
4-
import org.javacs.kt.externalsources.JarClassContentProvider
5-
import org.javacs.kt.externalsources.toKlsURI
64
import org.javacs.kt.util.AsyncExecutor
7-
import org.javacs.kt.util.noResult
85
import org.javacs.kt.util.parseURI
9-
import java.net.URI
10-
import java.net.URISyntaxException
116
import java.util.concurrent.CompletableFuture
127

138
class KotlinProtocolExtensionService(

server/src/main/kotlin/org/javacs/kt/KotlinTextDocumentService.kt

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import org.javacs.kt.codeaction.codeActions
88
import org.javacs.kt.completion.*
99
import org.javacs.kt.definition.goToDefinition
1010
import org.javacs.kt.diagnostic.convertDiagnostic
11-
import org.javacs.kt.externalsources.JarClassContentProvider
1211
import org.javacs.kt.formatting.formatKotlinCode
1312
import org.javacs.kt.hover.hoverAt
1413
import org.javacs.kt.position.offset
@@ -26,13 +25,11 @@ import org.javacs.kt.util.TemporaryDirectory
2625
import org.javacs.kt.util.parseURI
2726
import org.javacs.kt.util.describeURI
2827
import org.javacs.kt.util.describeURIs
29-
import org.javacs.kt.command.JAVA_TO_KOTLIN_COMMAND
3028
import org.javacs.kt.rename.renameSymbol
3129
import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics
3230
import java.net.URI
3331
import java.io.Closeable
3432
import java.nio.file.Path
35-
import java.nio.file.Paths
3633
import java.time.Duration
3734
import java.util.concurrent.CompletableFuture
3835

@@ -41,7 +38,8 @@ class KotlinTextDocumentService(
4138
private val sp: SourcePath,
4239
private val config: Configuration,
4340
private val tempDirectory: TemporaryDirectory,
44-
private val uriContentProvider: URIContentProvider
41+
private val uriContentProvider: URIContentProvider,
42+
private val cp: CompilerClassPath
4543
) : TextDocumentService, Closeable {
4644
private lateinit var client: LanguageClient
4745
private val async = AsyncExecutor()
@@ -93,7 +91,7 @@ class KotlinTextDocumentService(
9391

9492
override fun codeAction(params: CodeActionParams): CompletableFuture<List<Either<Command, CodeAction>>> = async.compute {
9593
val (file, _) = recover(params.textDocument.uri, params.range.start, Recompile.NEVER)
96-
codeActions(file, params.range, params.context)
94+
codeActions(file, sp.index, params.range, params.context)
9795
}
9896

9997
override fun hover(position: HoverParams): CompletableFuture<Hover?> = async.compute {
@@ -118,7 +116,7 @@ class KotlinTextDocumentService(
118116
LOG.info("Go-to-definition at {}", describePosition(position))
119117

120118
val (file, cursor) = recover(position, Recompile.NEVER)
121-
goToDefinition(file, cursor, uriContentProvider.jarClassContentProvider, tempDirectory, config.externalSources)
119+
goToDefinition(file, cursor, uriContentProvider.classContentProvider, tempDirectory, config.externalSources, cp)
122120
?.let(::listOf)
123121
?.let { Either.forLeft<List<Location>, List<LocationLink>>(it) }
124122
?: noResult("Couldn't find definition at ${describePosition(position)}", Either.forLeft(emptyList()))

server/src/main/kotlin/org/javacs/kt/SourcePath.kt

Lines changed: 14 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@ import org.javacs.kt.util.describeURI
88
import org.javacs.kt.index.SymbolIndex
99
import org.javacs.kt.progress.Progress
1010
import com.intellij.lang.Language
11-
import org.jetbrains.kotlin.container.ComponentProvider
12-
import org.jetbrains.kotlin.container.getService
1311
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
1412
import org.jetbrains.kotlin.psi.KtFile
1513
import org.jetbrains.kotlin.resolve.BindingContext
@@ -48,7 +46,7 @@ class SourcePath(
4846
var parsed: KtFile? = null,
4947
var compiledFile: KtFile? = null,
5048
var compiledContext: BindingContext? = null,
51-
var compiledContainer: ComponentProvider? = null,
49+
var module: ModuleDescriptor? = null,
5250
val language: Language? = null,
5351
val isTemporary: Boolean = false // A temporary source file will not be returned by .all()
5452
) {
@@ -66,7 +64,7 @@ class SourcePath(
6664
parsed = null
6765
compiledFile = null
6866
compiledContext = null
69-
compiledContainer = null
67+
module = null
7068
}
7169

7270
fun parse() {
@@ -97,10 +95,10 @@ class SourcePath(
9795

9896
val oldFile = clone()
9997

100-
val (context, container) = cp.compiler.compileKtFile(parsed!!, allIncludingThis(), kind)
98+
val (context, module) = cp.compiler.compileKtFile(parsed!!, allIncludingThis(), kind)
10199
parseDataWriteLock.withLock {
102100
compiledContext = context
103-
compiledContainer = container
101+
this.module = module
104102
compiledFile = parsed
105103
}
106104

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

119117
private fun doPrepareCompiledFile(): CompiledFile =
120-
CompiledFile(content, compiledFile!!, compiledContext!!, compiledContainer!!, allIncludingThis(), cp, isScript, kind)
118+
CompiledFile(content, compiledFile!!, compiledContext!!, module!!, allIncludingThis(), cp, isScript, kind)
121119

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

127125
// Creates a shallow copy
128-
fun clone(): SourceFile = SourceFile(uri, content, path, parsed, compiledFile, compiledContext, compiledContainer, language, isTemporary)
126+
fun clone(): SourceFile = SourceFile(uri, content, path, parsed, compiledFile, compiledContext, module, language, isTemporary)
129127
}
130128

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

219217
// Update cache
220218
for ((f, parsed) in parse) {
@@ -223,7 +221,7 @@ class SourcePath(
223221
//only updated if the parsed file didn't change:
224222
f.compiledFile = parsed
225223
f.compiledContext = context
226-
f.compiledContainer = container
224+
f.module = module
227225
}
228226
}
229227
}
@@ -257,9 +255,9 @@ class SourcePath(
257255
fun refreshDependencyIndexes() {
258256
compileAllFiles()
259257

260-
val container = files.values.first { it.compiledContainer != null }.compiledContainer
261-
if (container != null) {
262-
refreshDependencyIndexes(container)
258+
val module = files.values.first { it.module != null }.module
259+
if (module != null) {
260+
refreshDependencyIndexes(module)
263261
}
264262
}
265263

@@ -279,9 +277,8 @@ class SourcePath(
279277
/**
280278
* Refreshes the indexes. If already done, refreshes only the declarations in the files that were changed.
281279
*/
282-
private fun refreshDependencyIndexes(container: ComponentProvider) = indexAsync.execute {
280+
private fun refreshDependencyIndexes(module: ModuleDescriptor) = indexAsync.execute {
283281
if (indexEnabled) {
284-
val module = container.getService(ModuleDescriptor::class.java)
285282
val declarations = getDeclarationDescriptors(files.values)
286283
index.refresh(module, declarations)
287284
}
@@ -291,9 +288,8 @@ class SourcePath(
291288
private fun getDeclarationDescriptors(files: Collection<SourceFile>) =
292289
files.flatMap { file ->
293290
val compiledFile = file.compiledFile ?: file.parsed
294-
val compiledContainer = file.compiledContainer
295-
if (compiledFile != null && compiledContainer != null) {
296-
val module = compiledContainer.getService(ModuleDescriptor::class.java)
291+
val module = file.module
292+
if (compiledFile != null && module != null) {
297293
module.getPackage(compiledFile.packageFqName).memberScope.getContributedDescriptors(
298294
DescriptorKindFilter.ALL
299295
) { name -> compiledFile.declarations.map { it.name }.contains(name.toString()) }
Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,21 @@
11
package org.javacs.kt
22

33
import java.net.URI
4-
import java.net.URLDecoder
5-
import java.nio.charset.StandardCharsets
64
import java.nio.file.Paths
7-
import org.javacs.kt.externalsources.JarClassContentProvider
5+
import org.javacs.kt.externalsources.ClassContentProvider
86
import org.javacs.kt.externalsources.toKlsURI
97
import org.javacs.kt.util.KotlinLSException
10-
import org.javacs.kt.util.partitionAroundLast
118

129
/**
1310
* Fetches the content of Kotlin files identified by a URI.
1411
*/
1512
class URIContentProvider(
16-
val jarClassContentProvider: JarClassContentProvider
13+
val classContentProvider: ClassContentProvider
1714
) {
1815
fun contentOf(uri: URI): String = when (uri.scheme) {
1916
"file" -> Paths.get(uri).toFile().readText()
20-
"kls" -> uri.toKlsURI()?.let { jarClassContentProvider.contentOf(it).second }
21-
?: throw KotlinLSException("Could not find ${uri}")
17+
"kls" -> uri.toKlsURI()?.let { classContentProvider.contentOf(it).second }
18+
?: throw KotlinLSException("Could not find $uri")
2219
else -> throw KotlinLSException("Unrecognized scheme ${uri.scheme}")
2320
}
2421
}

server/src/main/kotlin/org/javacs/kt/codeaction/CodeAction.kt

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,23 @@ import org.eclipse.lsp4j.*
44
import org.eclipse.lsp4j.jsonrpc.messages.Either
55
import org.javacs.kt.CompiledFile
66
import org.javacs.kt.codeaction.quickfix.ImplementAbstractFunctionsQuickFix
7+
import org.javacs.kt.codeaction.quickfix.AddMissingImportsQuickFix
78
import org.javacs.kt.command.JAVA_TO_KOTLIN_COMMAND
89
import org.javacs.kt.util.toPath
10+
import org.javacs.kt.index.SymbolIndex
911

1012
val QUICK_FIXES = listOf(
11-
ImplementAbstractFunctionsQuickFix()
13+
ImplementAbstractFunctionsQuickFix(),
14+
AddMissingImportsQuickFix()
1215
)
1316

14-
fun codeActions(file: CompiledFile, range: Range, context: CodeActionContext): List<Either<Command, CodeAction>> {
15-
val requestedKinds = context.only ?: listOf(CodeActionKind.Refactor)
17+
fun codeActions(file: CompiledFile, index: SymbolIndex, range: Range, context: CodeActionContext): List<Either<Command, CodeAction>> {
18+
// context.only does not work when client is emacs...
19+
val requestedKinds = context.only ?: listOf(CodeActionKind.Refactor, CodeActionKind.QuickFix)
1620
return requestedKinds.map {
1721
when (it) {
1822
CodeActionKind.Refactor -> getRefactors(file, range)
19-
CodeActionKind.QuickFix -> getQuickFixes(file, range, context.diagnostics)
23+
CodeActionKind.QuickFix -> getQuickFixes(file, index, range, context.diagnostics)
2024
else -> listOf()
2125
}
2226
}.flatten()
@@ -38,8 +42,8 @@ fun getRefactors(file: CompiledFile, range: Range): List<Either<Command, CodeAct
3842
}
3943
}
4044

41-
fun getQuickFixes(file: CompiledFile, range: Range, diagnostics: List<Diagnostic>): List<Either<Command, CodeAction>> {
42-
return QUICK_FIXES.mapNotNull {
43-
it.compute(file, range, diagnostics)
45+
fun getQuickFixes(file: CompiledFile, index: SymbolIndex, range: Range, diagnostics: List<Diagnostic>): List<Either<Command, CodeAction>> {
46+
return QUICK_FIXES.flatMap {
47+
it.compute(file, index, range, diagnostics)
4448
}
4549
}

0 commit comments

Comments
 (0)