Skip to content

Commit 3551511

Browse files
committed
feat(intellij): add CodeLens
1 parent fcb0057 commit 3551511

File tree

6 files changed

+133
-2
lines changed

6 files changed

+133
-2
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package com.tabbyml.intellijtabby.inlineChat
2+
3+
import com.intellij.codeInsight.codeVision.*
4+
import com.intellij.openapi.editor.Editor
5+
import javax.swing.Icon
6+
7+
8+
class InlineChatCodeVisionProvider: CodeVisionProvider<Any> {
9+
override val defaultAnchor: CodeVisionAnchorKind = CodeVisionAnchorKind.Top
10+
override val id: String = "InlineChatCodeVisionProvider"
11+
override val name: String = "Inline Chat Code Vision Provider"
12+
override val relativeOrderings: List<CodeVisionRelativeOrdering> = listOf()
13+
14+
override fun precomputeOnUiThread(editor: Editor): Any {
15+
return Any()
16+
}
17+
18+
override fun computeCodeVision(editor: Editor, uiData: Any): CodeVisionState {
19+
TODO()
20+
}
21+
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
package com.tabbyml.intellijtabby.inlineChat
2+
3+
import com.intellij.codeInsight.hints.*
4+
import com.intellij.lang.Language
5+
import com.intellij.openapi.components.serviceOrNull
6+
import com.intellij.openapi.editor.Editor
7+
import com.intellij.psi.PsiElement
8+
import com.intellij.psi.PsiFile
9+
import com.tabbyml.intellijtabby.lsp.ConnectionService
10+
import kotlinx.coroutines.CoroutineScope
11+
import org.eclipse.lsp4j.CodeLens
12+
import org.eclipse.lsp4j.CodeLensParams
13+
import org.eclipse.lsp4j.TextDocumentIdentifier
14+
import java.util.concurrent.CompletableFuture
15+
import java.util.concurrent.ExecutionException
16+
import kotlinx.coroutines.Dispatchers
17+
import kotlinx.coroutines.launch
18+
import javax.swing.JComponent
19+
import javax.swing.JPanel
20+
21+
22+
class CodeLensInlayHintsProvider :
23+
InlayHintsProvider<Any> {
24+
private val scope = CoroutineScope(Dispatchers.IO)
25+
26+
override fun getCollectorFor(
27+
file: PsiFile,
28+
editor: Editor,
29+
settings: Any,
30+
sink: InlayHintsSink
31+
): InlayHintsCollector? {
32+
return object : FactoryInlayHintsCollector(editor) {
33+
override fun collect(element: PsiElement, editor: Editor, sink: InlayHintsSink): Boolean {
34+
try {
35+
val codeLenses = getCodeLenses(element.containingFile).get() ?: return false
36+
for (codeLens in codeLenses) {
37+
val range = codeLens.range
38+
if (range == null || codeLens.command == null) continue
39+
40+
val start = range.start
41+
val line = start.line
42+
val offset = editor.document.getLineStartOffset(line)
43+
44+
// Create the inlay presentation for the CodeLens
45+
val presentation = factory.smallText(codeLens.command.title)
46+
47+
// Add to the editor
48+
sink.addBlockElement(offset, true, true, 0, presentation)
49+
}
50+
} catch (e: InterruptedException) {
51+
// Handle exceptions
52+
} catch (e: ExecutionException) {
53+
// Handle exceptions
54+
}
55+
return true
56+
}
57+
}
58+
}
59+
60+
fun getCodeLenses(psiFile: PsiFile): CompletableFuture<List<CodeLens>?> {
61+
val virtualFile = psiFile.virtualFile
62+
?: return CompletableFuture.completedFuture(listOf())
63+
val uri = virtualFile.url.replace("file://", "file:/")
64+
val params = CodeLensParams(TextDocumentIdentifier(uri))
65+
return CompletableFuture<List<CodeLens>?>().also { future ->
66+
scope.launch {
67+
try {
68+
val server = psiFile.project.serviceOrNull<ConnectionService>()?.getServerAsync() ?: run {
69+
future.complete(null)
70+
return@launch
71+
}
72+
val result = server.textDocumentFeature.codeLens(params)
73+
future.complete(result.get())
74+
} catch (e: Exception) {
75+
future.completeExceptionally(e)
76+
}
77+
}
78+
}
79+
}
80+
81+
override fun createSettings(): NoSettings {
82+
return NoSettings()
83+
}
84+
85+
override fun isLanguageSupported(language: Language): Boolean {
86+
// Specify which languages should support CodeLens
87+
return true // or check specific languages
88+
}
89+
90+
override val key: SettingsKey<Any> = SettingsKey("Tabby")
91+
92+
override val name: String = "Tabby CodeLens"
93+
override val previewText: String = "Tabby CodeLens"
94+
95+
override fun createConfigurable(settings: Any): ImmediateConfigurable {
96+
return object : ImmediateConfigurable {
97+
override val cases: List<ImmediateConfigurable.Case> = emptyList()
98+
override fun createComponent(listener: ChangeListener): JComponent {
99+
return JPanel()
100+
}
101+
}
102+
}
103+
}

clients/intellij/src/main/kotlin/com/tabbyml/intellijtabby/lsp/LanguageClient.kt

+3-2
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,9 @@ class LanguageClient(private val project: Project) : com.tabbyml.intellijtabby.l
6262
inlineCompletion = InlineCompletionCapabilities(
6363
dynamicRegistration = true,
6464
),
65+
codeLensCapabilities = CodeLensCapabilities(
66+
true,
67+
),
6568
),
6669
workspace = WorkspaceClientCapabilities().apply {
6770
workspaceFolders = true
@@ -259,7 +262,6 @@ class LanguageClient(private val project: Project) : com.tabbyml.intellijtabby.l
259262
val future = CompletableFuture<ApplyWorkspaceEditResponse>()
260263
invokeLater {
261264
try {
262-
println(params.toString())
263265
val edit = params.edit
264266
runWriteCommandAction(project) {
265267
edit.changes?.forEach { (uri, edits) ->
@@ -268,7 +270,6 @@ class LanguageClient(private val project: Project) : com.tabbyml.intellijtabby.l
268270
edits.forEach { textEdit ->
269271
val startOffset = offsetInDocument(document, textEdit.range.start).coerceIn(0, document.textLength)
270272
val endOffset = offsetInDocument(document, textEdit.range.end).coerceIn(0, document.textLength)
271-
println("startOffset: $startOffset, endOffset: $endOffset, newText: ${textEdit.newText}")
272273
document.replaceString(startOffset, endOffset, textEdit.newText)
273274
}
274275
}

clients/intellij/src/main/kotlin/com/tabbyml/intellijtabby/lsp/protocol/ProtocolData.kt

+1
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ data class TextDocumentClientCapabilities(
4848
val synchronization: SynchronizationCapabilities? = null,
4949
val completion: CompletionCapabilities? = null,
5050
val inlineCompletion: InlineCompletionCapabilities? = null,
51+
var codeLensCapabilities: CodeLensCapabilities ? = null,
5152
)
5253

5354
data class InlineCompletionCapabilities(

clients/intellij/src/main/kotlin/com/tabbyml/intellijtabby/lsp/protocol/server/TextDocumentFeature.kt

+3
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,7 @@ interface TextDocumentFeature {
3131

3232
@JsonNotification
3333
fun willSave(params: WillSaveTextDocumentParams)
34+
35+
@JsonNotification
36+
fun codeLens(params: CodeLensParams): CompletableFuture<List<CodeLens>?>
3437
}

clients/intellij/src/main/resources/META-INF/plugin.xml

+1
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@
6868
</intentionAction>
6969

7070
<codeInsight.lineMarkerProvider implementationClass="com.tabbyml.intellijtabby.inlineChat.GutterIconProvider"/>
71+
<codeInsight.inlayProvider language=" " implementationClass="com.tabbyml.intellijtabby.inlineChat.CodeLensInlayHintsProvider" />
7172
</extensions>
7273

7374
<actions>

0 commit comments

Comments
 (0)