add new semantic tokens

This commit is contained in:
Christoph Brandau
2025-08-13 09:26:35 +02:00
parent 5b2bf39cba
commit 1a542cc9e9
+31 -13
View File
@@ -1,6 +1,6 @@
import enum import enum
from typing import List from typing import List
from tclint.syntax_tree import Visitor, QuotedWord, Command, BareWord from tclint.syntax_tree import Visitor, QuotedWord, Command, BareWord, VarSub
from tclint.commands import get_commands from tclint.commands import get_commands
import attrs import attrs
from common.load_data import standard_items from common.load_data import standard_items
@@ -14,6 +14,9 @@ class TokenModifier(enum.IntFlag):
definition = enum.auto() definition = enum.auto()
declaration = enum.auto() declaration = enum.auto()
builtin = enum.auto() builtin = enum.auto()
globalvar = enum.auto()
reference = enum.auto()
write = enum.auto()
@attrs.define @attrs.define
@@ -101,6 +104,10 @@ class _Highlighter(Visitor):
if in_custom or in_standard: if in_custom or in_standard:
line, col = routine.contents_pos line, col = routine.contents_pos
self._tokens.append((((line - 1, col - 1), len(name), "function", []))) self._tokens.append((((line - 1, col - 1), len(name), "function", [])))
# Highlight 'global' itself as a keyword
if name == "global":
line, col = routine.contents_pos
self._tokens.append((((line - 1, col - 1), len(name), "keyword", [])))
if routine.contents == "puts": if routine.contents == "puts":
line, col = routine.contents_pos line, col = routine.contents_pos
@@ -114,21 +121,20 @@ class _Highlighter(Visitor):
) )
) )
) )
# Variable declaration vs reference for 'set'
if routine.contents == "set" and command.args: if routine.contents == "set" and command.args:
first_arg = command.args[0] first_arg = command.args[0]
token_info = self._get_token_info(first_arg) token_info = self._get_token_info(first_arg)
if token_info: if token_info:
(line, col), length = token_info (line, col), length = token_info
self._tokens.append( mods = [TokenModifier.declaration, TokenModifier.write]
( self._tokens.append(((line, col), length, "variable", mods))
( # If there is exactly one argument, 'set var' is a read/reference
(line, col), if len(command.args) == 1 and token_info:
length, (line, col), length = token_info
"variable", mods = [TokenModifier.reference]
[TokenModifier.declaration], # Mark as reference (read); remove write/declaration (keep simple: we still keep declaration above for consistency)
) self._tokens.append(((line, col), length, "variable", mods))
)
)
if routine.contents == "proc" and command.args: if routine.contents == "proc" and command.args:
first_arg = command.args[0] first_arg = command.args[0]
if hasattr(first_arg, "pos") and hasattr(first_arg, "value"): if hasattr(first_arg, "pos") and hasattr(first_arg, "value"):
@@ -153,11 +159,12 @@ class _Highlighter(Visitor):
# Parameter kann einfaches Wort sein # Parameter kann einfaches Wort sein
if hasattr(child, "value") and child.value is not None: if hasattr(child, "value") and child.value is not None:
line, col = child.pos line, col = child.pos
# Parameters are variables (locals): mark as variable with declaration
self._tokens.append( self._tokens.append(
( (
(line - 1, col - 1), (line - 1, col - 1),
len(child.value), len(child.value),
"parameter", "variable",
[TokenModifier.declaration], [TokenModifier.declaration],
) )
) )
@@ -171,7 +178,7 @@ class _Highlighter(Visitor):
( (
(line - 1, col - 1), (line - 1, col - 1),
len(name_node.value), len(name_node.value),
"parameter", "variable",
[TokenModifier.declaration], [TokenModifier.declaration],
) )
) )
@@ -181,6 +188,17 @@ class _Highlighter(Visitor):
line, col = first_arg.pos line, col = first_arg.pos
self._tokens.append((((line - 1, col - 1), len(first_arg.value), "class", []))) self._tokens.append((((line - 1, col - 1), len(first_arg.value), "class", [])))
# Highlight variables used via $var and $::var as references (read)
for arg in getattr(command, "args", []):
for child in getattr(arg, "children", []):
if isinstance(child, VarSub) and hasattr(child, "pos") and child.value:
line, col = child.pos
# Determine if this looks like a global reference ($::var)
mods = [TokenModifier.reference]
if child.value.startswith("::"):
mods.append(TokenModifier.globalvar)
self._tokens.append((((line - 1, col - 1), len(child.value), "variable", mods)))
def tokens(self) -> list[Token]: def tokens(self) -> list[Token]:
"""Encode tokens as described in """Encode tokens as described in
https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_semanticTokens. https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_semanticTokens.