Skip to content

feat: Implemented CodeLens and resolveCodeLens #653

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

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
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
2 changes: 1 addition & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -49,5 +49,5 @@ org-jetbrains-kotlin-gradle-plugin = { module = "org.jetbrains.kotlin:kotlin-gra

[plugins]
com-github-jk1-tcdeps = { id = "com.github.jk1.tcdeps", version = "1.6.2" }
com-jaredsburrows-license = { id = "com.jaredsburrows.license", version = "0.8.42" }
com-jaredsburrows-license = { id = "com.jaredsburrows.license", version = "0.9.8" }
io-gitlab-arturbosch-detekt = { id = "io.gitlab.arturbosch.detekt", version = "1.22.0" }
Original file line number Diff line number Diff line change
@@ -93,6 +93,7 @@ class KotlinLanguageServer(
serverCapabilities.documentRangeFormattingProvider = Either.forLeft(true)
serverCapabilities.executeCommandProvider = ExecuteCommandOptions(ALL_COMMANDS)
serverCapabilities.documentHighlightProvider = Either.forLeft(true)
serverCapabilities.codeLensProvider = CodeLensOptions(true)

val storagePath = getStoragePath(params)
databaseService.setup(storagePath)
73 changes: 69 additions & 4 deletions server/src/main/kotlin/org/javacs/kt/KotlinTextDocumentService.kt
Original file line number Diff line number Diff line change
@@ -34,6 +34,7 @@ import java.io.Closeable
import java.nio.file.Path
import java.time.Duration
import java.util.concurrent.CompletableFuture
import org.javacs.kt.codelens.findCodeLenses

class KotlinTextDocumentService(
private val sf: SourceFiles,
@@ -144,8 +145,14 @@ class KotlinTextDocumentService(
))
}

override fun codeLens(params: CodeLensParams): CompletableFuture<List<CodeLens>> {
TODO("not implemented")
override fun codeLens(params: CodeLensParams): CompletableFuture<List<CodeLens>> = async.compute {
reportTime {
LOG.info("Finding code lenses in {}", describeURI(params.textDocument.uri))

val uri = parseURI(params.textDocument.uri)
val file = sp.currentVersion(uri)
return@compute findCodeLenses(file)
}
}

override fun rename(params: RenameParams) = async.compute {
@@ -263,8 +270,66 @@ class KotlinTextDocumentService(
}
}

override fun resolveCodeLens(unresolved: CodeLens): CompletableFuture<CodeLens> {
TODO("not implemented")
override fun resolveCodeLens(unresolved: CodeLens): CompletableFuture<CodeLens> = async.compute {
reportTime {
LOG.info("Resolving code lens {}", unresolved.command?.command)

val command = unresolved.command
if (command == null) {
return@compute unresolved
}

val args = command.arguments as List<*>
if (args.size != 3) {
return@compute unresolved
}

val uri = args[0] as String
val line = args[1] as Int
val character = args[2] as Int

val file = sp.currentVersion(parseURI(uri))
val content = sp.content(parseURI(uri))
val offset = offset(content, line, character)

when (command.command) {
"kotlin.showImplementations" -> {
val implementations = findImplementation(sp, sf, file, offset)
if (implementations.isNotEmpty()) {
unresolved.command = Command(
command.title,
command.command,
listOf(uri, line, character, implementations)
)
}
}
"kotlin.showSubclasses" -> {
val implementations = findImplementation(sp, sf, file, offset)
if (implementations.isNotEmpty()) {
unresolved.command = Command(
command.title,
command.command,
listOf(uri, line, character, implementations)
)
}
}
"kotlin.showReferences" -> {
val filePath = parseURI(uri).filePath
if (filePath != null) {
val references = findReferences(filePath, offset, sp)
if (references.isNotEmpty()) {
unresolved.command = Command(
command.title,
command.command,
listOf(uri, line, character, references)
)
}
}
}
}

return@compute unresolved
}
}

private fun describePosition(position: TextDocumentPositionParams): String {
64 changes: 64 additions & 0 deletions server/src/main/kotlin/org/javacs/kt/codelens/CodeLens.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package org.javacs.kt.codelens

import org.eclipse.lsp4j.CodeLens
import org.eclipse.lsp4j.Command
import org.eclipse.lsp4j.Range
import org.javacs.kt.CompiledFile
import org.javacs.kt.LOG
import org.javacs.kt.position.location
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtNamedFunction
import org.jetbrains.kotlin.resolve.BindingContext
import java.nio.file.Paths

fun findCodeLenses(file: CompiledFile): List<CodeLens> {
val codeLenses = mutableListOf<CodeLens>()
val parsedFile = file.parse
val filePath = Paths.get(parsedFile.containingFile.virtualFile.path)
val uri = filePath.toUri().toString()

// Add code lenses for classes and interfaces
parsedFile.declarations.filterIsInstance<KtClassOrObject>().forEach { ktClass ->
val classDesc = file.compile.get(BindingContext.CLASS, ktClass)
if (classDesc != null) {
when (classDesc.kind) {
ClassKind.INTERFACE -> {
// Add "Show Implementations" code lens for interfaces
location(ktClass)?.let { loc ->
codeLenses.add(CodeLens(
loc.range,
Command("Show Implementations", "kotlin.showImplementations", listOf(uri, loc.range.start.line, loc.range.start.character)),
null
))
}
}
ClassKind.CLASS -> {
// Add "Show Subclasses" code lens for classes
location(ktClass)?.let { loc ->
codeLenses.add(CodeLens(
loc.range,
Command("Show Subclasses", "kotlin.showSubclasses", listOf(uri, loc.range.start.line, loc.range.start.character)),
null
))
}
}
else -> {}
}
}
}

// Add code lenses for functions
parsedFile.declarations.filterIsInstance<KtNamedFunction>().forEach { ktFunction ->
location(ktFunction)?.let { loc ->
codeLenses.add(CodeLens(
loc.range,
Command("Show References", "kotlin.showReferences", listOf(uri, loc.range.start.line, loc.range.start.character)),
null
))
}
}

return codeLenses
}