Skip to content

Commit 61a104e

Browse files
committed
Implement expression completions
1 parent c26053d commit 61a104e

File tree

6 files changed

+101
-4
lines changed

6 files changed

+101
-4
lines changed

adapter/src/main/kotlin/org/javacs/ktda/adapter/DAPConverter.kt

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,15 @@ private typealias DAPScope = org.eclipse.lsp4j.debug.Scope
1515
private typealias DAPVariable = org.eclipse.lsp4j.debug.Variable
1616
private typealias DAPThread = org.eclipse.lsp4j.debug.Thread
1717
private typealias DAPExceptionBreakpointsFilter = org.eclipse.lsp4j.debug.ExceptionBreakpointsFilter
18+
private typealias DAPCompletionItem = org.eclipse.lsp4j.debug.CompletionItem
19+
private typealias DAPCompletionItemType = org.eclipse.lsp4j.debug.CompletionItemType
1820
private typealias InternalSource = org.javacs.ktda.core.Source
1921
private typealias InternalSourceBreakpoint = org.javacs.ktda.core.breakpoint.SourceBreakpoint
2022
private typealias InternalExceptionBreakpoint = org.javacs.ktda.core.breakpoint.ExceptionBreakpoint
2123
private typealias InternalBreakpoint = org.javacs.ktda.core.breakpoint.Breakpoint
2224
private typealias InternalStackFrame = org.javacs.ktda.core.stack.StackFrame
25+
private typealias InternalCompletionItem = org.javacs.ktda.core.completion.CompletionItem
26+
private typealias InternalCompletionItemType = org.javacs.ktda.core.completion.CompletionItemType
2327

2428
/**
2529
* Handles conversions between debug adapter types
@@ -95,4 +99,31 @@ class DAPConverter(
9599
name = internalThread.name
96100
id = internalThread.id
97101
}
102+
103+
fun toDAPCompletionItem(internalItem: InternalCompletionItem) = DAPCompletionItem().apply {
104+
label = internalItem.label
105+
type = toDAPCompletionItemType(internalItem.type)
106+
}
107+
108+
fun toDAPCompletionItemType(internalType: InternalCompletionItemType) = when (internalType) {
109+
InternalCompletionItemType.METHOD -> DAPCompletionItemType.METHOD
110+
InternalCompletionItemType.FUNCTION -> DAPCompletionItemType.FUNCTION
111+
InternalCompletionItemType.CONSTRUCTOR -> DAPCompletionItemType.CONSTRUCTOR
112+
InternalCompletionItemType.FIELD -> DAPCompletionItemType.FIELD
113+
InternalCompletionItemType.VARIABLE -> DAPCompletionItemType.VARIABLE
114+
InternalCompletionItemType.CLASS -> DAPCompletionItemType.CLASS
115+
InternalCompletionItemType.INTERFACE -> DAPCompletionItemType.INTERFACE
116+
InternalCompletionItemType.MODULE -> DAPCompletionItemType.MODULE
117+
InternalCompletionItemType.PROPERTY -> DAPCompletionItemType.PROPERTY
118+
InternalCompletionItemType.UNIT -> DAPCompletionItemType.UNIT
119+
InternalCompletionItemType.VALUE -> DAPCompletionItemType.VALUE
120+
InternalCompletionItemType.ENUM -> DAPCompletionItemType.ENUM
121+
InternalCompletionItemType.KEYWORD -> DAPCompletionItemType.KEYWORD
122+
InternalCompletionItemType.SNIPPET -> DAPCompletionItemType.SNIPPET
123+
InternalCompletionItemType.TEXT -> DAPCompletionItemType.TEXT
124+
InternalCompletionItemType.COLOR -> DAPCompletionItemType.COLOR
125+
InternalCompletionItemType.FILE -> DAPCompletionItemType.FILE
126+
InternalCompletionItemType.REFERENCE -> DAPCompletionItemType.REFERENCE
127+
InternalCompletionItemType.CUSTOMCOLOR -> DAPCompletionItemType.CUSTOMCOLOR
128+
}
98129
}

adapter/src/main/kotlin/org/javacs/ktda/adapter/KotlinDebugAdapter.kt

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ class KotlinDebugAdapter(
5656

5757
val capabilities = Capabilities()
5858
capabilities.supportsConfigurationDoneRequest = true
59+
capabilities.supportsCompletionsRequest = true
5960
capabilities.exceptionBreakpointFilters = ExceptionBreakpoint.values()
6061
.map(converter::toDAPExceptionBreakpointsFilter)
6162
.toTypedArray()
@@ -371,7 +372,16 @@ class KotlinDebugAdapter(
371372

372373
override fun gotoTargets(args: GotoTargetsArguments): CompletableFuture<GotoTargetsResponse> = notImplementedDAPMethod()
373374

374-
override fun completions(args: CompletionsArguments): CompletableFuture<CompletionsResponse> = notImplementedDAPMethod()
375+
override fun completions(args: CompletionsArguments): CompletableFuture<CompletionsResponse> = async.compute {
376+
CompletionsResponse().apply {
377+
targets = (args.frameId
378+
.let(converter::toInternalStackFrame)
379+
?: throw KotlinDAException("Could not find stack frame with ID ${args.frameId}"))
380+
.completions(args.text)
381+
.map(converter::toDAPCompletionItem)
382+
.toTypedArray()
383+
}
384+
}
375385

376386
override fun exceptionInfo(args: ExceptionInfoArguments): CompletableFuture<ExceptionInfoResponse> = notImplementedDAPMethod()
377387

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package org.javacs.ktda.core.completion
2+
3+
data class CompletionItem(
4+
val label: String,
5+
val type: CompletionItemType
6+
)
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package org.javacs.ktda.core.completion
2+
3+
enum class CompletionItemType {
4+
METHOD,
5+
FUNCTION,
6+
CONSTRUCTOR,
7+
FIELD,
8+
VARIABLE,
9+
CLASS,
10+
INTERFACE,
11+
MODULE,
12+
PROPERTY,
13+
UNIT,
14+
VALUE,
15+
ENUM,
16+
KEYWORD,
17+
SNIPPET,
18+
TEXT,
19+
COLOR,
20+
FILE,
21+
REFERENCE,
22+
CUSTOMCOLOR
23+
}

adapter/src/main/kotlin/org/javacs/ktda/core/stack/StackFrame.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package org.javacs.ktda.core.stack
22

33
import org.javacs.ktda.core.Position
4+
import org.javacs.ktda.core.completion.CompletionItem
45
import org.javacs.ktda.core.scope.VariableTreeNode
56

67
interface StackFrame {
@@ -9,4 +10,6 @@ interface StackFrame {
910
val scopes: List<VariableTreeNode>
1011

1112
fun evaluate(expression: String): VariableTreeNode?
13+
14+
fun completions(expression: String): List<CompletionItem>
1215
}

adapter/src/main/kotlin/org/javacs/ktda/jdi/stack/JDIStackFrame.kt

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package org.javacs.ktda.jdi.stack
22

33
import org.javacs.ktda.core.Position
4+
import org.javacs.ktda.core.completion.CompletionItem
5+
import org.javacs.ktda.core.completion.CompletionItemType
46
import org.javacs.ktda.core.scope.VariableTreeNode
57
import org.javacs.ktda.core.stack.StackFrame
68
import org.javacs.ktda.jdi.JDISessionContext
@@ -17,18 +19,35 @@ class JDIStackFrame(
1719
JDILocalScope(frame)
1820
) }
1921

22+
private val variables by lazy { scopes.flatMap { it.childs ?: emptyList() } }
23+
2024
// TODO: Scope "Fields"
2125
// TODO: Argument values?
2226

23-
private fun evaluateQualified(qualName: List<String>, variables: List<VariableTreeNode> = scopes.flatMap { it.childs ?: emptyList() }): VariableTreeNode? =
27+
private fun evaluateQualified(qualName: List<String>, scopeVariables: List<VariableTreeNode> = variables): VariableTreeNode? =
2428
qualName.firstOrNull().let { qual ->
2529
val rest = qualName.drop(1)
26-
variables
30+
scopeVariables
2731
.filter { it.name == qual }
2832
.mapNotNull { if (rest.isEmpty()) it else evaluateQualified(rest, it.childs ?: emptyList()) }
2933
.firstOrNull()
3034
}
3135

36+
private fun completeQualified(qualName: List<String>, scopeVariables: List<VariableTreeNode> = variables): List<CompletionItem> =
37+
qualName.firstOrNull()?.let { qual ->
38+
val rest = qualName.drop(1)
39+
scopeVariables
40+
.filter { it.name == qual }
41+
.flatMap { completeQualified(rest, it.childs ?: emptyList()) }
42+
.takeIf { it.isNotEmpty() }
43+
?: scopeVariables
44+
.takeIf { rest.isEmpty() }
45+
?.filter { it.name.startsWith(qual) }
46+
?.map { CompletionItem(it.name, CompletionItemType.VARIABLE) }
47+
} ?: emptyList()
48+
49+
private fun parseQualified(expression: String): List<String> = expression.split(".")
50+
3251
override fun evaluate(expression: String): VariableTreeNode? {
3352
// TODO: Implement proper expression parsing
3453
//
@@ -43,8 +62,13 @@ class JDIStackFrame(
4362
// Creating JDI values from primitives and strings is possible though,
4463
// using VirtualMachine.mirrorOf.
4564

46-
val qualified = expression.split(".")
65+
val qualified = parseQualified(expression)
4766
return evaluateQualified(qualified)
4867
?: evaluateQualified(listOf("this") + qualified)
4968
}
69+
70+
override fun completions(expression: String): List<CompletionItem> {
71+
val qualified = parseQualified(expression)
72+
return completeQualified(qualified) + completeQualified(listOf("this") + qualified)
73+
}
5074
}

0 commit comments

Comments
 (0)