From 1a542cc9e994eda8cefbcc12db6b6edb81256ff4 Mon Sep 17 00:00:00 2001 From: Christoph Brandau Date: Wed, 13 Aug 2025 09:26:35 +0200 Subject: [PATCH] add new semantic tokens --- server/src/tools/semantic_tokens.py | 44 ++++++++++++++++++++--------- 1 file changed, 31 insertions(+), 13 deletions(-) diff --git a/server/src/tools/semantic_tokens.py b/server/src/tools/semantic_tokens.py index 597634d..462629c 100644 --- a/server/src/tools/semantic_tokens.py +++ b/server/src/tools/semantic_tokens.py @@ -1,6 +1,6 @@ import enum 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 import attrs from common.load_data import standard_items @@ -14,6 +14,9 @@ class TokenModifier(enum.IntFlag): definition = enum.auto() declaration = enum.auto() builtin = enum.auto() + globalvar = enum.auto() + reference = enum.auto() + write = enum.auto() @attrs.define @@ -101,6 +104,10 @@ class _Highlighter(Visitor): if in_custom or in_standard: line, col = routine.contents_pos 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": 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: first_arg = command.args[0] token_info = self._get_token_info(first_arg) if token_info: (line, col), length = token_info - self._tokens.append( - ( - ( - (line, col), - length, - "variable", - [TokenModifier.declaration], - ) - ) - ) + 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 + if len(command.args) == 1 and token_info: + (line, col), length = token_info + mods = [TokenModifier.reference] + # 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: first_arg = command.args[0] if hasattr(first_arg, "pos") and hasattr(first_arg, "value"): @@ -153,11 +159,12 @@ class _Highlighter(Visitor): # Parameter kann einfaches Wort sein if hasattr(child, "value") and child.value is not None: line, col = child.pos + # Parameters are variables (locals): mark as variable with declaration self._tokens.append( ( (line - 1, col - 1), len(child.value), - "parameter", + "variable", [TokenModifier.declaration], ) ) @@ -171,7 +178,7 @@ class _Highlighter(Visitor): ( (line - 1, col - 1), len(name_node.value), - "parameter", + "variable", [TokenModifier.declaration], ) ) @@ -181,6 +188,17 @@ class _Highlighter(Visitor): line, col = first_arg.pos 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]: """Encode tokens as described in https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_semanticTokens.