Skip to content

Commit 7769498

Browse files
committed
Implement basic semantic token encoding
1 parent 7c783c8 commit 7769498

File tree

1 file changed

+51
-4
lines changed

1 file changed

+51
-4
lines changed

server/src/main/kotlin/org/javacs/kt/semantictokens/SemanticTokens.kt

Lines changed: 51 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,12 @@ import org.eclipse.lsp4j.SemanticTokenTypes
44
import org.eclipse.lsp4j.SemanticTokenModifiers
55
import org.eclipse.lsp4j.SemanticTokensLegend
66
import org.eclipse.lsp4j.Range
7-
import org.jetbrains.kotlin.psi.KtElement
7+
import org.javacs.kt.position.range
8+
import org.javacs.kt.util.preOrderTraversal
9+
import org.jetbrains.kotlin.psi.KtProperty
10+
import org.jetbrains.kotlin.psi.KtVariableDeclaration
11+
import org.jetbrains.kotlin.psi.KtNamedDeclaration
12+
import com.intellij.psi.PsiElement
813

914
private enum class SemanticTokenType(val typeName: String) {
1015
VARIABLE(SemanticTokenTypes.Variable),
@@ -22,9 +27,51 @@ val semanticTokensLegend = SemanticTokensLegend(
2227
SemanticTokenModifier.values().map { it.modifierName }
2328
)
2429

25-
private data class SemanticToken(val range: Range, val type: SemanticTokenType, val modifiers: Set<SemanticTokenModifier>)
30+
private data class SemanticToken(val range: Range, val type: SemanticTokenType, val modifiers: Set<SemanticTokenModifier> = setOf())
2631

27-
private fun semanticTokens(element: KtElement): List<Int> {
28-
return listOf() // TODO
32+
fun semanticTokens(element: PsiElement): List<Int> = encodeTokens(elementTokens(element))
33+
34+
private fun encodeTokens(tokens: Sequence<SemanticToken>): List<Int> {
35+
val encoded = mutableListOf<Int>()
36+
var last: SemanticToken? = null
37+
38+
for (token in tokens) {
39+
// Tokens must be on a single line
40+
if (token.range.start.line == token.range.end.line) {
41+
val deltaLine = token.range.start.line - (last?.let { it.range.start.line } ?: 0)
42+
val deltaStart = token.range.start.character - (last?.let { it.range.start.character } ?: 0)
43+
val length = token.range.end.character - token.range.start.character
44+
45+
encoded.add(deltaLine)
46+
encoded.add(deltaStart)
47+
encoded.add(length)
48+
encoded.add(encodeType(token.type))
49+
encoded.add(encodeModifiers(token.modifiers))
50+
51+
last = token
52+
}
53+
}
54+
55+
return encoded
2956
}
3057

58+
private fun encodeType(type: SemanticTokenType): Int = type.ordinal
59+
60+
private fun encodeModifiers(modifiers: Set<SemanticTokenModifier>): Int = modifiers
61+
.map { 1 << it.ordinal }
62+
.fold(0, |)
63+
64+
private fun elementTokens(element: PsiElement): Sequence<SemanticToken> = element
65+
.preOrderTraversal()
66+
.mapNotNull { (it as? KtNamedDeclaration)?.nameIdentifier }
67+
.mapNotNull { elementToken(it) }
68+
69+
private fun elementToken(element: PsiElement): SemanticToken? {
70+
val file = element.containingFile
71+
val elementRange = range(file.text, element.textRange)
72+
return when (element) {
73+
is KtProperty -> SemanticToken(elementRange, SemanticTokenType.PROPERTY)
74+
is KtVariableDeclaration -> SemanticToken(elementRange, SemanticTokenType.VARIABLE)
75+
else -> null
76+
}
77+
}

0 commit comments

Comments
 (0)