build_and_puplish.yml / build_and_publish (release) Successful in 29s
This change adds cached and incremental analysis for the LSP server to improve responsiveness. The client now debounces diagnostic updates to avoid excessive recomputation. The server introduces per-document line caches and various caches for completions, inlay hints, and metadata to support faster, incremental updates. - Debounce diagnostics on text changes to reduce noise. - Add caches for completions, inlay hints, and metadata. - Introduce incremental analysis with per-document line caches.
94 lines
3.6 KiB
Python
94 lines
3.6 KiB
Python
import lsprotocol.types as lsp
|
|
from common.load_data import standard_items
|
|
from tclint.syntax_tree import BareWord, Command, List, Visitor
|
|
|
|
BUILTIN_VAR_LABELS = {ci.label for ci in standard_items.nx_variables}
|
|
BUILTIN_PROC_LABELS = {ci.label for ci in standard_items.nx_procs}
|
|
|
|
|
|
class CompletionItems:
|
|
def __init__(self):
|
|
self._custom_functions: list[lsp.CompletionItem] = []
|
|
|
|
@property
|
|
def custom_functions(self) -> list[lsp.CompletionItem]:
|
|
return self._custom_functions
|
|
|
|
@custom_functions.setter
|
|
def custom_functions(self, value: lsp.CompletionItem):
|
|
self._custom_functions.append(value)
|
|
|
|
|
|
class CompletionCollector(Visitor):
|
|
def __init__(self):
|
|
super().__init__()
|
|
self._custom_functions: list[lsp.CompletionItem] = []
|
|
self._custom_function_keys: set[tuple[str, lsp.CompletionItemKind | None]] = set()
|
|
self._proc_signatures = {}
|
|
|
|
@property
|
|
def custom_functions(self) -> list[lsp.CompletionItem]:
|
|
return self._custom_functions
|
|
|
|
@property
|
|
def proc_signatures(self):
|
|
return self._proc_signatures
|
|
|
|
def _append_unique(self, item: lsp.CompletionItem):
|
|
# Avoid duplicate labels within the same file scan
|
|
key = (item.label, item.kind)
|
|
if key in self._custom_function_keys:
|
|
return
|
|
self._custom_function_keys.add(key)
|
|
self._custom_functions.append(item)
|
|
|
|
def visit_command(self, command: Command):
|
|
routine = command.routine
|
|
|
|
# Collect custom proc names and their signatures
|
|
if routine.contents == "proc" and command.args:
|
|
first_arg = command.args[0]
|
|
if not getattr(first_arg, "value", None):
|
|
return
|
|
|
|
if first_arg.value in BUILTIN_PROC_LABELS:
|
|
return
|
|
|
|
# Record proc name as a completion item
|
|
self._append_unique(lsp.CompletionItem(label=first_arg.value, kind=lsp.CompletionItemKind.Function))
|
|
if len(command.args) < 2:
|
|
return
|
|
|
|
param_list_node = command.args[1]
|
|
if not hasattr(param_list_node, "children"):
|
|
return
|
|
|
|
param_names = []
|
|
for arg in param_list_node.children:
|
|
if isinstance(arg, BareWord):
|
|
param_names.append(arg.value)
|
|
elif isinstance(arg, List) and len(arg.children) >= 1:
|
|
first = arg.children[0]
|
|
if isinstance(first, BareWord):
|
|
param_names.append(first.value)
|
|
|
|
self._proc_signatures[first_arg.value] = param_names
|
|
|
|
# Collect global variables declared with: global var1 var2 ...
|
|
elif routine.contents == "global" and command.args:
|
|
for arg in command.args:
|
|
if isinstance(arg, BareWord) and getattr(arg, "value", None):
|
|
if arg.value not in BUILTIN_VAR_LABELS:
|
|
self._append_unique(lsp.CompletionItem(label=arg.value, kind=lsp.CompletionItemKind.Variable))
|
|
|
|
# Collect variables set with explicit global namespace: set ::var_name ...
|
|
elif routine.contents == "set" and command.args:
|
|
first = command.args[0]
|
|
if isinstance(first, BareWord) and getattr(first, "value", None):
|
|
var_name = first.value
|
|
if var_name.startswith("::"):
|
|
base_name = var_name.split("(", 1)[0]
|
|
clean_name = base_name[2:] # remove leading '::' for completion display
|
|
if clean_name not in BUILTIN_VAR_LABELS:
|
|
self._append_unique(lsp.CompletionItem(label=clean_name, kind=lsp.CompletionItemKind.Variable))
|