Skip to content

Commit 5dba571

Browse files
committed
feat(langserver): correct highlightning to better highlight python expresseions, escape sequences, environment variable default value and embedded argument regexes
1 parent 8eb9338 commit 5dba571

File tree

8 files changed

+360
-166
lines changed

8 files changed

+360
-166
lines changed

intellij-client/src/main/kotlin/dev/robotcode/robotcode4ij/highlighting/RobotCodeHighlighterProvider.kt

Lines changed: 0 additions & 81 deletions
This file was deleted.
Lines changed: 94 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package dev.robotcode.robotcode4ij.highlighting
22

3+
import com.intellij.lexer.LexerBase
34
import com.intellij.openapi.util.registry.Registry
5+
import com.intellij.psi.TokenType
46
import com.intellij.psi.tree.IElementType
57
import dev.robotcode.robotcode4ij.TextMateBundleHolder
68
import dev.robotcode.robotcode4ij.psi.ARGUMENT
@@ -10,6 +12,8 @@ import dev.robotcode.robotcode4ij.psi.CONTINUATION
1012
import dev.robotcode.robotcode4ij.psi.CONTROL_FLOW
1113
import dev.robotcode.robotcode4ij.psi.ENVIRONMENT_VARIABLE_BEGIN
1214
import dev.robotcode.robotcode4ij.psi.ENVIRONMENT_VARIABLE_END
15+
import dev.robotcode.robotcode4ij.psi.EXPRESSION_VARIABLE_BEGIN
16+
import dev.robotcode.robotcode4ij.psi.EXPRESSION_VARIABLE_END
1317
import dev.robotcode.robotcode4ij.psi.HEADER
1418
import dev.robotcode.robotcode4ij.psi.KEYWORD_CALL
1519
import dev.robotcode.robotcode4ij.psi.KEYWORD_NAME
@@ -20,22 +24,25 @@ import dev.robotcode.robotcode4ij.psi.TESTCASE_NAME
2024
import dev.robotcode.robotcode4ij.psi.VARIABLE
2125
import dev.robotcode.robotcode4ij.psi.VARIABLE_BEGIN
2226
import dev.robotcode.robotcode4ij.psi.VARIABLE_END
23-
import org.jetbrains.plugins.textmate.language.syntax.lexer.TextMateElementType
24-
import org.jetbrains.plugins.textmate.language.syntax.lexer.TextMateHighlightingLexer
27+
import org.jetbrains.plugins.textmate.language.syntax.lexer.TextMateLexer
28+
import org.jetbrains.plugins.textmate.language.syntax.lexer.TextMateScope
29+
import java.util.*
30+
import kotlin.math.min
2531

26-
class RobotTextMateHighlightingLexer : TextMateHighlightingLexer(
27-
TextMateBundleHolder.descriptor, Registry.get("textmate.line.highlighting.limit").asInteger()
28-
) {
32+
class RobotCodeTextMateHighlightingLexer : LexerBase() {
2933
companion object {
3034
val mapping by lazy {
3135
mapOf(
3236
"comment.line.robotframework" to COMMENT_LINE,
3337
"comment.line.rest.robotframework" to COMMENT_LINE,
3438
"comment.block.robotframework" to COMMENT_BLOCK,
39+
3540
"punctuation.definition.variable.begin.robotframework" to VARIABLE_BEGIN,
3641
"punctuation.definition.variable.end.robotframework" to VARIABLE_END,
3742
"punctuation.definition.envvar.begin.robotframework" to ENVIRONMENT_VARIABLE_BEGIN,
3843
"punctuation.definition.envvar.end.robotframework" to ENVIRONMENT_VARIABLE_END,
44+
"punctuation.definition.expression.begin.robotframework" to EXPRESSION_VARIABLE_BEGIN,
45+
"punctuation.definition.expression.end.robotframework" to EXPRESSION_VARIABLE_END,
3946

4047
"entity.name.function.testcase.name.robotframework" to TESTCASE_NAME,
4148
"entity.name.function.keyword.name.robotframework" to KEYWORD_NAME,
@@ -63,16 +70,94 @@ class RobotTextMateHighlightingLexer : TextMateHighlightingLexer(
6370
"string.unquoted.argument.robotframework" to ARGUMENT,
6471

6572
"keyword.operator.continue.robotframework" to CONTINUATION,
73+
74+
"punctuation.definition.variable.python.begin.robotframework" to VARIABLE_BEGIN,
6675
)
6776
}
6877
}
6978

79+
80+
private val myLexer =
81+
TextMateLexer(
82+
TextMateBundleHolder.descriptor, Registry.get("textmate.line.highlighting.limit").asInteger(),
83+
true
84+
)
85+
private var currentLineTokens = LinkedList<TextMateLexer.Token?>()
86+
private lateinit var buffer: CharSequence
87+
private var endOffset = 0
88+
private var currentOffset = 0
89+
private var tokenType: IElementType? = null
90+
private var tokenStart = 0
91+
private var tokenEnd = 0
92+
private var restartable = false
93+
94+
override fun start(buffer: CharSequence, startOffset: Int, endOffset: Int, initialState: Int) {
95+
this.buffer = buffer
96+
this.endOffset = endOffset
97+
this.currentOffset = startOffset
98+
this.endOffset = endOffset
99+
this.currentLineTokens.clear()
100+
this.restartable = initialState == 0
101+
myLexer.init(buffer, startOffset)
102+
this.advance()
103+
}
104+
105+
override fun getState(): Int {
106+
return if (restartable) 0 else 1
107+
}
108+
70109
override fun getTokenType(): IElementType? {
71-
val result = super.getTokenType() ?: return null
72-
if (result is TextMateElementType) {
73-
return mapping[result.scope.scopeName] ?: RobotTextMateElementType(result)
110+
return tokenType
111+
}
112+
113+
override fun getTokenStart(): Int {
114+
return tokenStart
115+
}
116+
117+
override fun getTokenEnd(): Int {
118+
return tokenEnd
119+
}
120+
121+
override fun advance() {
122+
if (this.currentOffset >= this.endOffset) {
123+
this.updateState(null as TextMateLexer.Token?, this.endOffset)
124+
} else {
125+
if (currentLineTokens.isEmpty()) {
126+
myLexer.advanceLine(this.currentLineTokens)
127+
}
128+
129+
this.updateState(
130+
currentLineTokens.poll(),
131+
myLexer.currentOffset
132+
)
74133
}
75-
return result
134+
}
135+
136+
private fun updateState(token: TextMateLexer.Token?, fallbackOffset: Int) {
137+
if (token != null) {
138+
this.tokenType =
139+
(if (token.scope === TextMateScope.WHITESPACE) TokenType.WHITE_SPACE else mapping[token.scope.scopeName]
140+
?: RobotTextMateElementType.create(token.scope))
141+
142+
tokenStart = token.startOffset
143+
tokenEnd = min(token.endOffset.toDouble(), endOffset.toDouble()).toInt()
144+
currentOffset = token.endOffset
145+
restartable = token.restartable
146+
} else {
147+
tokenType = null
148+
tokenStart = fallbackOffset
149+
tokenEnd = fallbackOffset
150+
currentOffset = fallbackOffset
151+
restartable = true
152+
}
153+
}
154+
155+
override fun getBufferSequence(): CharSequence {
156+
return buffer
157+
}
158+
159+
override fun getBufferEnd(): Int {
160+
return endOffset
76161
}
77162
}
78163

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
package dev.robotcode.robotcode4ij.highlighting
2+
3+
import com.intellij.lexer.Lexer
4+
import com.intellij.openapi.editor.colors.TextAttributesKey
5+
import com.intellij.openapi.fileTypes.PlainSyntaxHighlighter
6+
import com.intellij.openapi.fileTypes.SyntaxHighlighterBase
7+
import com.intellij.psi.tree.IElementType
8+
import com.intellij.util.containers.ContainerUtil
9+
import dev.robotcode.robotcode4ij.psi.ARGUMENT
10+
import dev.robotcode.robotcode4ij.psi.COMMENT_BLOCK
11+
import dev.robotcode.robotcode4ij.psi.COMMENT_LINE
12+
import dev.robotcode.robotcode4ij.psi.CONTINUATION
13+
import dev.robotcode.robotcode4ij.psi.CONTROL_FLOW
14+
import dev.robotcode.robotcode4ij.psi.ENVIRONMENT_VARIABLE_BEGIN
15+
import dev.robotcode.robotcode4ij.psi.ENVIRONMENT_VARIABLE_END
16+
import dev.robotcode.robotcode4ij.psi.HEADER
17+
import dev.robotcode.robotcode4ij.psi.KEYWORD_CALL
18+
import dev.robotcode.robotcode4ij.psi.KEYWORD_NAME
19+
import dev.robotcode.robotcode4ij.psi.OPERATOR
20+
import dev.robotcode.robotcode4ij.psi.RobotTextMateElementType
21+
import dev.robotcode.robotcode4ij.psi.SETTING
22+
import dev.robotcode.robotcode4ij.psi.TESTCASE_NAME
23+
import dev.robotcode.robotcode4ij.psi.VARIABLE
24+
import dev.robotcode.robotcode4ij.psi.VARIABLE_BEGIN
25+
import dev.robotcode.robotcode4ij.psi.VARIABLE_END
26+
import org.jetbrains.plugins.textmate.TextMateService
27+
import org.jetbrains.plugins.textmate.language.TextMateScopeComparator
28+
import org.jetbrains.plugins.textmate.language.syntax.highlighting.TextMateTheme
29+
import org.jetbrains.plugins.textmate.language.syntax.lexer.TextMateScope
30+
import java.util.function.Function
31+
32+
33+
class RobotCodeHighlighter : SyntaxHighlighterBase() {
34+
companion object {
35+
val elementTypeMap = mapOf(
36+
COMMENT_LINE to arrayOf(RobotColors.LINE_COMMENT),
37+
COMMENT_BLOCK to arrayOf(RobotColors.BLOCK_COMMENT),
38+
VARIABLE_BEGIN to arrayOf(RobotColors.VARIABLE_BEGIN),
39+
VARIABLE_END to arrayOf(RobotColors.VARIABLE_END),
40+
ENVIRONMENT_VARIABLE_BEGIN to arrayOf(RobotColors.VARIABLE_BEGIN),
41+
ENVIRONMENT_VARIABLE_END to arrayOf(RobotColors.VARIABLE_END),
42+
TESTCASE_NAME to arrayOf(RobotColors.TESTCASE_NAME),
43+
KEYWORD_NAME to arrayOf(RobotColors.KEYWORD_NAME),
44+
HEADER to arrayOf(RobotColors.HEADER),
45+
SETTING to arrayOf(RobotColors.SETTING),
46+
KEYWORD_CALL to arrayOf(RobotColors.KEYWORD_CALL),
47+
CONTROL_FLOW to arrayOf(RobotColors.CONTROL_FLOW),
48+
VARIABLE to arrayOf(RobotColors.VARIABLE),
49+
OPERATOR to arrayOf(RobotColors.OPERATOR),
50+
ARGUMENT to arrayOf(RobotColors.ARGUMENT),
51+
CONTINUATION to arrayOf(RobotColors.CONTINUATION),
52+
)
53+
54+
val PLAIN_SYNTAX_HIGHLIGHTER: PlainSyntaxHighlighter = PlainSyntaxHighlighter()
55+
}
56+
57+
private val myLexer = RobotTextMateHighlightingLexer()
58+
59+
override fun getHighlightingLexer(): Lexer {
60+
return myLexer
61+
}
62+
63+
override fun getTokenHighlights(tokenType: IElementType?): Array<TextAttributesKey> {
64+
val result = elementTypeMap[tokenType]
65+
if (result != null) return result
66+
67+
if (tokenType !is RobotTextMateElementType) return PLAIN_SYNTAX_HIGHLIGHTER.getTokenHighlights(tokenType)
68+
69+
val service = TextMateService.getInstance()
70+
val customHighlightingColors = service.customHighlightingColors
71+
72+
val highlightingRules = ContainerUtil.union(customHighlightingColors.keys, TextMateTheme.INSTANCE.rules)
73+
74+
val textMateScope = trimEmbeddedScope(tokenType)
75+
val selectors: List<CharSequence> = ContainerUtil.reverse(
76+
TextMateScopeComparator(textMateScope, Function.identity())
77+
.sortAndFilter(highlightingRules)
78+
)
79+
val result1 = ContainerUtil.map2Array(
80+
selectors,
81+
TextAttributesKey::class.java
82+
) { rule: CharSequence ->
83+
val customTextAttributes = customHighlightingColors[rule]
84+
customTextAttributes?.getTextAttributesKey(TextMateTheme.INSTANCE)
85+
?: TextMateTheme.INSTANCE.getTextAttributesKey(rule)
86+
}
87+
88+
return result1
89+
}
90+
91+
private fun trimEmbeddedScope(tokenType: RobotTextMateElementType): TextMateScope {
92+
var current: TextMateScope? = tokenType.scope
93+
val trail: MutableList<CharSequence?> = ArrayList()
94+
while (current != null) {
95+
val scopeName = current.scopeName
96+
if (scopeName != null && scopeName.contains(".embedded.")) {
97+
var result = TextMateScope.EMPTY
98+
for (i in trail.indices.reversed()) {
99+
result = result.add(trail[i])
100+
}
101+
return result
102+
}
103+
trail.add(scopeName)
104+
current = current.parent
105+
}
106+
return tokenType.scope
107+
}
108+
}
109+

0 commit comments

Comments
 (0)