feat(tcl): add dynamic argument completion and snippets

The changes add dynamic, semantic argument completion for Tcl
commands and snippet support.

- Introduces DynamicCompletionKind, TclArgumentCompletion, and dynamic rules
  for Tcl to provide variable, procedure, namespace, and path suggestions.
- Adds snippet-backed commands and arguments for Tcl blocks and paths.
- Refactors tcl_argument_completion and updates the LSP to use dynamic path,
  variable, and namespace completions with snippet kinds.
This commit is contained in:
Christoph Brandau
2026-09-03 10:35:39 +02:00
parent 20f76b6a20
commit af5acfc946
7 changed files with 781 additions and 56 deletions
+18 -8
View File
@@ -1,9 +1,9 @@
from __future__ import annotations
from dataclasses import dataclass
from typing import Dict, List, Set
from tclint.syntax_tree import Command, Node, Script
from tclint.syntax_tree import List as TclList
@dataclass
@@ -18,13 +18,14 @@ def _normalize_var_name(raw_name: str | None) -> str | None:
return None
base = raw_name.split("(", 1)[0]
if base.startswith("::"):
base = base[2:]
base = base.removeprefix("::")
return base or None
def build_variable_index(source: str, tree: Node | None = None) -> tuple[Set[str], Dict[str, Set[str]], List[ProcRange]]:
def build_variable_index(
source: str, tree: Node | None = None
) -> tuple[set[str], dict[str, set[str]], list[ProcRange]]:
"""
Parse Tcl source text and build:
- globals: set of variable names considered global suggestions
@@ -40,9 +41,9 @@ def build_variable_index(source: str, tree: Node | None = None) -> tuple[Set[str
"""
_ = source # Kept for signature compatibility with callers.
globals_set: Set[str] = set()
procs: Dict[str, Set[str]] = {}
proc_ranges: List[ProcRange] = []
globals_set: set[str] = set()
procs: dict[str, set[str]] = {}
proc_ranges: list[ProcRange] = []
if tree is None:
return globals_set, procs, proc_ranges
@@ -54,7 +55,16 @@ def build_variable_index(source: str, tree: Node | None = None) -> tuple[Set[str
if routine == "proc" and len(node.args) >= 3 and isinstance(node.args[2], Script):
proc_name = _normalize_var_name(getattr(node.args[0], "contents", None))
if proc_name is not None:
procs.setdefault(proc_name, set())
local_variables = procs.setdefault(proc_name, set())
for parameter in getattr(node.args[1], "children", []):
parameter_node = parameter
if isinstance(parameter, TclList) and parameter.children:
parameter_node = parameter.children[0]
parameter_name = _normalize_var_name(
getattr(parameter_node, "contents", None)
)
if parameter_name is not None:
local_variables.add(parameter_name)
proc_ranges.append(
ProcRange(
name=proc_name,