Files
nx_post_support/server/src/tools/completion_items.py
T
Christoph Brandau f5bd79f067 feat(lsp): add incremental indexing and file ops support
Adds a thread-safe incremental index and snapshot API for LSP.
Introduces cache invalidation and file operation hooks for delete
and rename. This keeps indices in sync with disk changes.
Supports reindexing TCL files from disk when needed.

- Adds workspace file change handlers to sync indices on delete/rename.
- Introduces locking and snapshot helpers to safely access shared state.
- Refactors to invalidate caches on edits and reindex TCL files.
2026-08-17 08:45:09 +02:00

89 lines
3.4 KiB
Python

from tclint.syntax_tree import Visitor, Command, BareWord, List
import lsprotocol.types as lsp
from common.load_data import standard_items
BUILTIN_VAR_LABELS = {ci.label for ci in standard_items.nx_variables}
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._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
if not any(ci.label == item.label for ci in self._custom_functions):
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 any(item.label == first_arg.value for item in standard_items.nx_procs):
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))