diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 8208dc76..167e4e5e 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -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" } diff --git a/server/src/main/kotlin/org/javacs/kt/KotlinLanguageServer.kt b/server/src/main/kotlin/org/javacs/kt/KotlinLanguageServer.kt index e8da0ff9..09d2b261 100644 --- a/server/src/main/kotlin/org/javacs/kt/KotlinLanguageServer.kt +++ b/server/src/main/kotlin/org/javacs/kt/KotlinLanguageServer.kt @@ -93,6 +93,7 @@ class KotlinLanguageServer( serverCapabilities.documentRangeFormattingProvider = Either.forLeft(true) serverCapabilities.executeCommandProvider = ExecuteCommandOptions(ALL_COMMANDS) serverCapabilities.documentHighlightProvider = Either.forLeft(true) + serverCapabilities.inlineValueProvider = Either.forLeft(true) val storagePath = getStoragePath(params) databaseService.setup(storagePath) diff --git a/server/src/main/kotlin/org/javacs/kt/KotlinTextDocumentService.kt b/server/src/main/kotlin/org/javacs/kt/KotlinTextDocumentService.kt index 2ec1e522..fe4ca4b5 100644 --- a/server/src/main/kotlin/org/javacs/kt/KotlinTextDocumentService.kt +++ b/server/src/main/kotlin/org/javacs/kt/KotlinTextDocumentService.kt @@ -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.inlinevalue.findInlineValues class KotlinTextDocumentService( private val sf: SourceFiles, @@ -267,6 +268,11 @@ class KotlinTextDocumentService( TODO("not implemented") } + override fun inlineValue(params: InlineValueParams): CompletableFuture> = async.compute { + val (file, _) = recover(params.textDocument.uri, params.range.start, Recompile.ALWAYS) ?: return@compute emptyList() + findInlineValues(file, params.range) + } + private fun describePosition(position: TextDocumentPositionParams): String { return "${describeURI(position.textDocument.uri)} ${position.position.line + 1}:${position.position.character + 1}" } diff --git a/server/src/main/kotlin/org/javacs/kt/inlinevalue/InlineValue.kt b/server/src/main/kotlin/org/javacs/kt/inlinevalue/InlineValue.kt new file mode 100644 index 00000000..f2c1059f --- /dev/null +++ b/server/src/main/kotlin/org/javacs/kt/inlinevalue/InlineValue.kt @@ -0,0 +1,83 @@ +package org.javacs.kt.inlinevalue + +import org.eclipse.lsp4j.InlineValue +import org.eclipse.lsp4j.InlineValueText +import org.eclipse.lsp4j.Range +import org.javacs.kt.CompiledFile +import org.javacs.kt.position.position +import org.jetbrains.kotlin.psi.psiUtil.endOffset +import org.jetbrains.kotlin.psi.psiUtil.startOffset +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.calls.util.getResolvedCall +import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi +import org.jetbrains.kotlin.lexer.KtTokens.INTEGER_LITERAL +import org.jetbrains.kotlin.lexer.KtTokens.FLOAT_LITERAL +import org.jetbrains.kotlin.lexer.KtTokens.CHARACTER_LITERAL +import org.jetbrains.kotlin.lexer.KtTokens.TRUE_KEYWORD +import org.jetbrains.kotlin.lexer.KtTokens.FALSE_KEYWORD +import org.jetbrains.kotlin.lexer.KtTokens.NULL_KEYWORD +import org.jetbrains.kotlin.psi.KtElement +import org.jetbrains.kotlin.psi.KtExpression +import org.jetbrains.kotlin.psi.KtVisitorVoid +import org.jetbrains.kotlin.psi.KtProperty +import org.jetbrains.kotlin.psi.KtStringTemplateExpression +import org.jetbrains.kotlin.psi.KtConstantExpression + + + +fun findInlineValues(file: CompiledFile, range: Range): List { + val inlineValues = mutableListOf() + val content = file.content + val bindingContext = file.compile + + // Find all variable declarations in the range + file.parse.accept(object : KtVisitorVoid() { + override fun visitProperty(property: KtProperty) { + val initializer = property.initializer + if (initializer != null && isInRange(property, range, content)) { + val value = evaluateExpression(initializer, bindingContext) + if (value != null) { + inlineValues.add(InlineValue( + InlineValueText( + Range(position(content, property.startOffset), position(content, property.endOffset)), + value + ) + )) + } + } + } +}) + +return inlineValues +} + +private fun isInRange(element: KtElement, range: Range, content: String): Boolean { + val elementRange = Range(position(content, element.startOffset), position(content, element.endOffset)) + return elementRange.start.line >= range.start.line && + elementRange.end.line <= range.end.line && + !(elementRange.end.line == range.start.line && elementRange.end.character < range.start.character) && + !(elementRange.start.line == range.end.line && elementRange.start.character > range.end.character) +} + +private fun evaluateExpression(expression: KtExpression, bindingContext: BindingContext): String? { + return when (expression) { + is KtStringTemplateExpression -> { + val resolvedCall = expression.getResolvedCall(bindingContext) + resolvedCall?.resultingDescriptor?.valueParameters?.firstOrNull()?.let { param -> + bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, param.findPsi()]?.toString() + } + } + is KtConstantExpression -> { + when (expression.node.elementType) { + INTEGER_LITERAL -> expression.text + FLOAT_LITERAL -> expression.text + CHARACTER_LITERAL -> expression.text + TRUE_KEYWORD -> "true" + FALSE_KEYWORD -> "false" + NULL_KEYWORD -> "null" + else -> null + } + } + else -> null + } +}