feat(server): add TCL signature help support

Adds a signature help system for TCL commands.
The LSP server now exposes signature help for custom and
built-in MOM procedures, enabling parameter hints while editing.
Tests and documentation were added to cover common usage.

- Adds signature_help module to parse and present signatures
- Integrates with LSP server to provide signature help on the client
- Adds tests for custom and built-in procedures
This commit is contained in:
Christoph Brandau
2026-08-17 08:31:15 +02:00
parent 4ca1b0cce9
commit a39aee1b9d
5 changed files with 309 additions and 0 deletions
@@ -0,0 +1,92 @@
import sys
from pathlib import Path
THIS_DIR = Path(__file__).parent
SRC_DIR = THIS_DIR.parent.parent / "src"
if str(SRC_DIR) not in sys.path:
sys.path.insert(0, str(SRC_DIR))
import lsprotocol.types as lsp # type: ignore
from common.load_data import standard_items
from tools.parser import CustomParser
from tools.signature_help import build_signature_help, find_active_call
CUSTOM_SIGNATURES = {
"SERVICE_spacer_output": ["type", "length", "line_num", "output"]
}
def test_custom_proc_signature_and_active_parameter():
source = 'SERVICE_spacer_output "*" '
tree = CustomParser().parse(source)
result = build_signature_help(
source,
tree,
lsp.Position(line=0, character=len(source)),
CUSTOM_SIGNATURES,
{"SERVICE_spacer_output": "Outputs a spacer line."},
[],
)
assert result is not None
assert result.active_parameter == 1
signature = result.signatures[0]
assert signature.label == "SERVICE_spacer_output type length line_num output"
assert ( # type: ignore[union-attr]
signature.documentation.value == "Outputs a spacer line."
)
assert signature.parameters is not None
assert signature.parameters[3].label == (43, 49)
def test_innermost_command_is_used_for_nested_call():
source = 'set result [SERVICE_spacer_output "*" 20]'
tree = CustomParser().parse(source)
position = lsp.Position(line=0, character=source.index("20"))
call = find_active_call(source, tree, position)
assert call is not None
assert call.name == "SERVICE_spacer_output"
assert call.active_argument == 1
def test_builtin_signature_includes_parameter_documentation():
source = "MOM_abort "
tree = CustomParser().parse(source)
result = build_signature_help(
source,
tree,
lsp.Position(line=0, character=len(source)),
{},
{},
standard_items.json_data["MOM_procs"],
)
assert result is not None
assert result.active_parameter == 0
signature = result.signatures[0]
assert signature.label == "MOM_abort <message>"
assert signature.parameters is not None
assert signature.parameters[0].documentation
def test_unknown_command_has_no_signature_help():
source = "unknown_custom_command "
tree = CustomParser().parse(source)
result = build_signature_help(
source,
tree,
lsp.Position(line=0, character=len(source)),
{},
{},
standard_items.json_data["MOM_procs"],
)
assert result is None