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.
89 lines
3.4 KiB
Python
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))
|