feat(lsp): add Tcl command-aware completion and integration

- Introduced a Tcl command-aware completion engine and wired to LSP.
- Added a new module with Tcl commands, options, and contextual matching.
- Updated completion to favor command-aware items and args.
This commit is contained in:
Christoph Brandau
2026-09-03 10:17:16 +02:00
parent 081b488fe3
commit 20f76b6a20
7 changed files with 862 additions and 32 deletions
+43 -8
View File
@@ -38,13 +38,18 @@ update_sys_path(
# Imports needed for the language server goes below this.
# **********************************************************
# pylint: disable=wrong-import-position,import-error
import lsp_jsonrpc as jsonrpc
import lsprotocol.types as lsp
from common.load_data import standard_items
from lsp_tclserver import TclLanguageServer
from pygls import uris
from pygls.workspace.text_document import TextDocument
from tools.completion_items import completion_context, ranked_completion_items
import lsp_jsonrpc as jsonrpc
from common.load_data import standard_items
from lsp_tclserver import TclLanguageServer
from tools.completion_items import (
CompletionContext,
completion_context,
ranked_completion_items,
)
from tools.folding_ranges import build_folding_ranges
from tools.inlay_hint import (
InlayHintGenerator,
@@ -69,6 +74,11 @@ from tools.semantic_tokens import (
_Highlighter,
)
from tools.signature_help import build_signature_help
from tools.tcl_command_completion import (
TCL_COMMAND_ITEMS,
TCL_COMMAND_NAMES,
tcl_argument_completions,
)
WORKSPACE_SETTINGS = {}
GLOBAL_SETTINGS = {}
@@ -82,10 +92,12 @@ LSP_SERVER = TclLanguageServer(
BUILTIN_PROC_NAMES = {
item.label
for item in standard_items.tcl_keyword_list + standard_items.nx_procs
}
} | set(TCL_COMMAND_NAMES)
BUILTIN_VARIABLE_NAMES = {item.label for item in standard_items.nx_variables}
_STATIC_TCL_LABELS = {item.label for item in standard_items.tcl_keyword_list}
STATIC_COMPLETION_ITEMS = tuple(
standard_items.tcl_keyword_list
+ [item for item in TCL_COMMAND_ITEMS if item.label not in _STATIC_TCL_LABELS]
+ standard_items.nx_procs
+ standard_items.nx_variables
)
@@ -250,16 +262,40 @@ def document_diagnostic(params: lsp.DocumentDiagnosticParams):
@LSP_SERVER.feature(
lsp.TEXT_DOCUMENT_COMPLETION,
lsp.CompletionOptions(trigger_characters=["$"]),
lsp.CompletionOptions(trigger_characters=["$", " ", "-"]),
)
def on_completion(params: lsp.CompletionParams) -> lsp.CompletionList:
doc = LSP_SERVER.workspace.get_text_document(params.text_document.uri)
position = params.position
source_lines = LSP_SERVER.get_lines(doc)
context = completion_context(source_lines, position)
# Variable completion wins inside command arguments. Otherwise prefer the
# narrow command grammar when the cursor is at a known subcommand/option.
if context != CompletionContext.VARIABLE:
argument_items = tcl_argument_completions(source_lines, position)
if argument_items is not None:
items = ranked_completion_items(
((0, item) for item in argument_items),
CompletionContext.GENERAL,
)
return lsp.CompletionList(is_incomplete=False, items=items)
# Space and dash are registered only to open command-aware suggestions.
# Do not display the broad fallback list when such a trigger has no match.
if (
params.context is not None
and params.context.trigger_kind
== lsp.CompletionTriggerKind.TriggerCharacter
and params.context.trigger_character in {" ", "-"}
):
return lsp.CompletionList(is_incomplete=False, items=[])
tree = LSP_SERVER.get_tree(doc)
globals_set, procs_locals, proc_ranges = LSP_SERVER.variable_index_for_document(
doc, tree
)
position = params.position
local_names: set[str] = set()
for proc_range in proc_ranges:
end_line = proc_range.end_line or proc_range.start_line
@@ -302,7 +338,6 @@ def on_completion(params: lsp.CompletionParams) -> lsp.CompletionList:
candidates.extend((priority, item) for item in items_by_file[item_path])
candidates.extend((300, item) for item in STATIC_COMPLETION_ITEMS)
context = completion_context(LSP_SERVER.get_lines(doc), position)
items = ranked_completion_items(candidates, context)
return lsp.CompletionList(is_incomplete=False, items=items)