Skip to content

Commit 2cb0437

Browse files
committed
basic work inline values
1 parent 91d6cf3 commit 2cb0437

File tree

8 files changed

+452
-6
lines changed

8 files changed

+452
-6
lines changed

build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ dependencies {
206206
intellijPlatform {
207207
intellijIdeaCommunity(Versions.ideaSDK)
208208
bundledPlugins("com.intellij.java", "org.jetbrains.kotlin")
209-
plugins("com.redhat.devtools.lsp4ij:0.14.0")
209+
plugins("com.redhat.devtools.lsp4ij:0.18.0")
210210
}
211211
}
212212

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
/*
2+
* Copyright (c) 2017. tangzx([email protected])
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.tang.intellij.lua.debugger
18+
19+
import com.intellij.openapi.editor.Editor
20+
import com.intellij.openapi.util.TextRange
21+
import com.intellij.openapi.vfs.VirtualFile
22+
import com.intellij.xdebugger.XDebuggerUtil
23+
import com.intellij.xdebugger.XSourcePosition
24+
import com.intellij.xdebugger.frame.XStackFrame
25+
import com.tang.intellij.lua.debugger.emmy.EmmyDebugStackFrame
26+
import com.tang.intellij.lua.debugger.emmy.value.LuaXValue
27+
import com.tang.intellij.lua.editor.LuaEditorUtil
28+
import com.tang.intellij.lua.lang.LSPIJUtils
29+
30+
/**
31+
* Context for tracking variable positions during debugging
32+
* Used to map variable names to their source positions for inline value display
33+
*/
34+
class LuaDebugVariableContext(
35+
private val stackFrame: XStackFrame
36+
) {
37+
private val variableRanges = mutableMapOf<String, TextRange>()
38+
private val variablePositions = mutableMapOf<String, XSourcePosition>()
39+
private val providers = mutableListOf<LuaDebugVariablePositionProvider>()
40+
private var editor: Editor? = null
41+
private var endLineOffset: Int = -1
42+
43+
init {
44+
// Register default provider
45+
providers.add(LuaHighlighterDebugVariablePositionProvider())
46+
47+
// Initialize editor if available
48+
val sourcePosition = stackFrame.sourcePosition
49+
if (sourcePosition != null && stackFrame is EmmyDebugStackFrame) {
50+
val project = stackFrame.process.session.project
51+
val editors = LuaEditorUtil.findEditors(project, sourcePosition.file)
52+
editor = editors.firstOrNull()
53+
if (editor != null) {
54+
endLineOffset = editor!!.document.getLineEndOffset(sourcePosition.line)
55+
}
56+
}
57+
}
58+
59+
/**
60+
* Configure context by scanning for variables
61+
*/
62+
fun configureContext() {
63+
providers.forEach { it.configureContext(this) }
64+
}
65+
66+
/**
67+
* Get the file for this context
68+
*/
69+
fun getFile(): VirtualFile? = stackFrame.sourcePosition?.file
70+
71+
/**
72+
* Get the editor for this context
73+
*/
74+
fun getEditor(): Editor? = editor
75+
76+
/**
77+
* Get the end line offset
78+
*/
79+
fun getEndLineOffset(): Int = endLineOffset
80+
81+
/**
82+
* Add a variable range
83+
*/
84+
fun addVariableRange(variableName: String, textRange: TextRange) {
85+
variableRanges[variableName] = textRange
86+
}
87+
88+
/**
89+
* Add a variable position
90+
*/
91+
fun addVariablePosition(variableName: String, position: XSourcePosition) {
92+
variablePositions[variableName] = position
93+
}
94+
95+
/**
96+
* Get source position for a variable name
97+
*/
98+
fun getSourcePosition(name: String): XSourcePosition? {
99+
// Return cached position if available
100+
variablePositions[name]?.let { return it }
101+
102+
// Try to create position from range
103+
val textRange = variableRanges[name] ?: return null
104+
val file = getFile() ?: return null
105+
val ed = editor ?: return null
106+
107+
val range = com.tang.intellij.lua.lang.LSPIJUtils.toRange(textRange, ed.document)
108+
val position = XDebuggerUtil.getInstance()
109+
.createPosition(file, range.start.line, range.end.character)
110+
111+
if (position != null) {
112+
addVariablePosition(name, position)
113+
}
114+
115+
return position
116+
}
117+
118+
/**
119+
* Get source position for a variable value
120+
*/
121+
fun getSourcePositionFor(value: LuaXValue): XSourcePosition? {
122+
for (provider in providers) {
123+
val position = provider.getSourcePosition(value, this)
124+
if (position != null) {
125+
return position
126+
}
127+
}
128+
return null
129+
}
130+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* Copyright (c) 2017. tangzx([email protected])
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.tang.intellij.lua.debugger
18+
19+
import com.intellij.xdebugger.XSourcePosition
20+
import com.tang.intellij.lua.debugger.emmy.value.LuaXValue
21+
22+
/**
23+
* Interface for resolving source positions of debug variables
24+
*/
25+
interface LuaDebugVariablePositionProvider {
26+
/**
27+
* Configure the context by scanning for variables
28+
*/
29+
fun configureContext(context: LuaDebugVariableContext)
30+
31+
/**
32+
* Get source position for a variable value
33+
*/
34+
fun getSourcePosition(value: LuaXValue, context: LuaDebugVariableContext): XSourcePosition?
35+
}
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/*
2+
* Copyright (c) 2017. tangzx([email protected])
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.tang.intellij.lua.debugger
18+
19+
import com.intellij.openapi.editor.ex.EditorEx
20+
import com.intellij.openapi.editor.highlighter.HighlighterIterator
21+
import com.intellij.openapi.util.TextRange
22+
import com.intellij.psi.tree.IElementType
23+
import com.intellij.xdebugger.XSourcePosition
24+
import com.tang.intellij.lua.debugger.emmy.value.LuaXValue
25+
import com.tang.intellij.lua.psi.LuaTypes
26+
27+
/**
28+
* Provider that uses syntax highlighting to find variable positions
29+
* This is the main strategy used by lsp4ij
30+
*/
31+
class LuaHighlighterDebugVariablePositionProvider : LuaDebugVariablePositionProvider {
32+
33+
override fun configureContext(context: LuaDebugVariableContext) {
34+
val editor = context.getEditor()
35+
if (editor !is EditorEx) {
36+
return
37+
}
38+
39+
val endLineOffset = context.getEndLineOffset()
40+
if (endLineOffset < 0) {
41+
return
42+
}
43+
44+
try {
45+
val highlighter = editor.highlighter
46+
val iterator: HighlighterIterator = highlighter.createIterator(0)
47+
48+
while (!iterator.atEnd()) {
49+
if (iterator.end > endLineOffset) {
50+
break
51+
}
52+
53+
val tokenType = iterator.tokenType
54+
if (isVariableToken(tokenType)) {
55+
val start = iterator.start
56+
val end = iterator.end
57+
val textRange = TextRange(start, end)
58+
val variableName = editor.document.getText(textRange).trim()
59+
60+
if (variableName.isNotBlank() && !isKeyword(variableName)) {
61+
context.addVariableRange(variableName, textRange)
62+
}
63+
}
64+
65+
iterator.advance()
66+
}
67+
} catch (e: Exception) {
68+
// Ignore errors during scanning
69+
e.printStackTrace()
70+
}
71+
}
72+
73+
override fun getSourcePosition(value: LuaXValue, context: LuaDebugVariableContext): XSourcePosition? {
74+
return context.getSourcePosition(value.name)
75+
}
76+
77+
/**
78+
* Check if a token type represents a variable
79+
*/
80+
private fun isVariableToken(tokenType: IElementType): Boolean {
81+
// Check for identifier tokens
82+
val tokenString = tokenType.toString()
83+
return tokenString.contains("ID", ignoreCase = true) ||
84+
tokenString.contains("IDENTIFIER", ignoreCase = true) ||
85+
tokenString.contains("NAME", ignoreCase = true) ||
86+
tokenType == LuaTypes.ID // Lua specific identifier
87+
}
88+
89+
/**
90+
* Check if a string is a Lua keyword (should not be treated as variable)
91+
*/
92+
private fun isKeyword(text: String): Boolean {
93+
return text in setOf(
94+
"and", "break", "do", "else", "elseif", "end", "false",
95+
"for", "function", "if", "in", "local", "nil", "not",
96+
"or", "repeat", "return", "then", "true", "until", "while",
97+
"goto", "self"
98+
)
99+
}
100+
}

src/main/java/com/tang/intellij/lua/debugger/emmy/EmmyDebugStackFrame.kt

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,23 +16,27 @@
1616

1717
package com.tang.intellij.lua.debugger.emmy
1818

19+
import com.intellij.openapi.application.ReadAction
1920
import com.intellij.ui.ColoredTextContainer
2021
import com.intellij.ui.SimpleTextAttributes
2122
import com.intellij.xdebugger.XSourcePosition
2223
import com.intellij.xdebugger.frame.XCompositeNode
2324
import com.intellij.xdebugger.frame.XStackFrame
2425
import com.intellij.xdebugger.frame.XValueChildrenList
2526
import com.intellij.xdebugger.impl.XSourcePositionImpl
27+
import com.tang.intellij.lua.debugger.LuaDebugVariableContext
2628
import com.tang.intellij.lua.debugger.emmy.value.LuaXValue
2729
import com.tang.intellij.lua.psi.LuaFileUtil
30+
import java.util.concurrent.atomic.AtomicBoolean
2831

2932
class EmmyDebugStackFrame(val data: Stack, val process: EmmyDebugProcessBase) : XStackFrame() {
3033
private val values = XValueChildrenList()
3134
private var evaluator: EmmyEvaluator? = null
32-
private val sourcePosition by lazy {
33-
val file = LuaFileUtil.findFile(process.session.project, data.file)
34-
if (file == null) null else XSourcePositionImpl.create(file, data.line - 1)
35-
}
35+
private var _sourcePosition: XSourcePosition? = null
36+
private val sourcePositionInitialized = java.util.concurrent.atomic.AtomicBoolean(false)
37+
38+
// Variable context for inline values
39+
private var variableContext: LuaDebugVariableContext? = null
3640

3741
init {
3842
data.localVariables.forEach {
@@ -62,6 +66,38 @@ class EmmyDebugStackFrame(val data: Stack, val process: EmmyDebugProcessBase) :
6266
}
6367

6468
override fun getSourcePosition(): XSourcePosition? {
65-
return sourcePosition
69+
// Initialize source position with read access
70+
if (!sourcePositionInitialized.get()) {
71+
com.intellij.openapi.application.ReadAction.run<RuntimeException> {
72+
if (!sourcePositionInitialized.get()) {
73+
_sourcePosition = try {
74+
val file = LuaFileUtil.findFile(process.session.project, data.file)
75+
if (file == null) null else XSourcePositionImpl.create(file, data.line - 1)
76+
} catch (e: Exception) {
77+
null
78+
}
79+
sourcePositionInitialized.set(true)
80+
}
81+
}
82+
}
83+
return _sourcePosition
84+
}
85+
86+
/**
87+
* Get or create variable context for inline value support
88+
*/
89+
fun getVariableContext(): LuaDebugVariableContext {
90+
if (variableContext == null) {
91+
variableContext = LuaDebugVariableContext(this)
92+
variableContext!!.configureContext()
93+
}
94+
return variableContext!!
95+
}
96+
97+
/**
98+
* Get source position for a variable value
99+
*/
100+
fun getSourcePositionFor(value: LuaXValue): XSourcePosition? {
101+
return getVariableContext().getSourcePositionFor(value)
66102
}
67103
}

0 commit comments

Comments
 (0)