Compare commits

...
4 Commits
Author SHA1 Message Date
Christoph d7eb72f417 Merge pull request 'New completions' (#34) from new_completions into main
build_and_puplish.yml / build_and_publish (release) Successful in 27s
Reviewed-on: #34
2026-09-03 08:36:00 +00:00
Christoph Brandau af5acfc946 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.
2026-09-03 10:35:39 +02:00
Christoph Brandau 20f76b6a20 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.
2026-09-03 10:17:16 +02:00
Christoph 081b488fe3 Update version to 2026.9.100 2026-09-03 07:38:12 +00:00
9 changed files with 1597 additions and 42 deletions
+3
View File
@@ -3,6 +3,9 @@
- Add incoming and outgoing call hierarchy for custom TCL procedures and MOM event handlers
- Add document highlights for procedure and variable occurrences
- Make completion context-aware and prioritize local, current-file, workspace, and built-in symbols
- Add command-aware completion for Tcl subcommands, fixed arguments, and valid options
- Add semantic argument completion for variables, procedures, namespaces, and local file paths
- Add placeholder-based snippets for common Tcl structures and `dict for`
- Integrate the NX Tcl Remote Debugger directly into NX Postprocessor Support
- Add `nx-tcl` attach configurations and breakpoint support for TCL and DEF files
- Support breakpoints, stepping, stack frames, variables, watches, evaluation, logpoints, hit conditions, and Tcl error stops
+4 -2
View File
@@ -12,7 +12,8 @@ A comprehensive VS Code extension providing language support and remote debuggin
- **Signature Help** - Shows parameters and documentation for custom and NX procedures
- **Call Hierarchy** - Traces incoming and outgoing calls between custom TCL procedures and MOM event handlers
- **Document Highlights** - Highlights all reads, writes, and calls of the symbol under the cursor
- **Context-aware Completion** - Prioritizes local symbols and suggests variables or commands based on cursor context
- **Context-aware Completion** - Prioritizes local symbols and suggests variables, procedures, namespaces, paths, Tcl subcommands, valid argument values, and options based on cursor context
- **Tcl Snippets** - Inserts placeholder-based structures for `if`, `foreach`, `proc`, `switch`, `try`, and `dict for`
- **NX Tcl Remote Debugger** - Breakpoints, stepping, call stack, scopes, variables, watches, evaluation, logpoints, hit conditions, and Tcl error stops directly in a running NX Post process
## Supported File Types
@@ -101,7 +102,8 @@ Simply open any supported file type and enjoy:
- Signature help while entering procedure arguments
- Incoming and outgoing call hierarchy for custom procedures and MOM event handlers
- Document-wide highlights for procedure and variable occurrences
- Context-aware completion with local symbols ranked before workspace and built-in symbols
- Context-aware completion with local symbols ranked before workspace and built-in symbols, plus semantic arguments, local paths, Tcl subcommands, and options such as `string compare -nocase`
- Placeholder-based snippets for common Tcl control structures and procedures
- Remote NX Tcl debugging with breakpoints and full stepping
## Contributing
+1 -1
View File
@@ -2,7 +2,7 @@
"name": "nx-post-support",
"displayName": "NX Postprocessor Support",
"description": "VS Code extension for NX CAM postprocessor development with language support and remote Tcl debugging for CDL, TCL, and DEF files",
"version": "2026.8.300",
"version": "2026.9.100",
"publisher": "Christoph",
"icon": "images/nx-1.png",
"activationEvents": [
+143 -9
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,13 @@ 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,
DynamicCompletionKind,
path_completion_items,
tcl_argument_completion,
)
WORKSPACE_SETTINGS = {}
GLOBAL_SETTINGS = {}
@@ -82,10 +94,19 @@ 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}
_TCL_COMMAND_ITEMS_BY_LABEL = {
item.label: item for item in TCL_COMMAND_ITEMS
}
_TCL_KEYWORD_ITEMS = [
_TCL_COMMAND_ITEMS_BY_LABEL.get(item.label, item)
for item in standard_items.tcl_keyword_list
]
_STATIC_TCL_LABELS = {item.label for item in standard_items.tcl_keyword_list}
STATIC_COMPLETION_ITEMS = tuple(
standard_items.tcl_keyword_list
_TCL_KEYWORD_ITEMS
+ [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 +271,67 @@ 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.
argument_completion = None
if context != CompletionContext.VARIABLE:
argument_completion = tcl_argument_completion(source_lines, position)
if (
argument_completion is not None
and argument_completion.dynamic_kind is None
):
items = ranked_completion_items(
((0, item) for item in argument_completion.items),
CompletionContext.GENERAL,
)
return lsp.CompletionList(is_incomplete=False, items=items)
if (
argument_completion is not None
and argument_completion.dynamic_kind == DynamicCompletionKind.PATH
):
dynamic_items: tuple[lsp.CompletionItem, ...] = ()
if doc.uri.startswith("file:"):
document_path = pathlib.Path(uris.to_fs_path(doc.uri))
dynamic_items = path_completion_items(
document_path.parent,
argument_completion,
position,
)
path_candidates = [
(0, item) for item in argument_completion.items
]
path_candidates.extend((10, item) for item in dynamic_items)
items = ranked_completion_items(
path_candidates,
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 (
argument_completion is None
and 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
@@ -301,8 +373,70 @@ def on_completion(params: lsp.CompletionParams) -> lsp.CompletionList:
priority = 100 if LSP_SERVER.paths_equal(item_path, filepath) else 200
candidates.extend((priority, item) for item in items_by_file[item_path])
if argument_completion is not None:
static_candidates = [(0, item) for item in argument_completion.items]
if argument_completion.dynamic_kind == DynamicCompletionKind.VARIABLE:
variable_candidates = [*static_candidates, *candidates]
variable_candidates.extend(
(300, item) for item in standard_items.nx_variables
)
items = ranked_completion_items(
variable_candidates,
CompletionContext.VARIABLE,
)
return lsp.CompletionList(is_incomplete=False, items=items)
if argument_completion.dynamic_kind == DynamicCompletionKind.PROCEDURE:
procedure_kinds = {
lsp.CompletionItemKind.Constructor,
lsp.CompletionItemKind.Function,
lsp.CompletionItemKind.Method,
}
procedure_candidates = [
(priority, item)
for priority, item in candidates
if item.kind in procedure_kinds
]
procedure_candidates.extend(
(300, item) for item in standard_items.nx_procs
)
items = ranked_completion_items(
[*static_candidates, *procedure_candidates],
CompletionContext.GENERAL,
)
return lsp.CompletionList(is_incomplete=False, items=items)
if argument_completion.dynamic_kind == DynamicCompletionKind.NAMESPACE:
namespace_candidates = list(static_candidates)
prefix_is_absolute = argument_completion.active_prefix.startswith("::")
for index in LSP_SERVER.navigation_snapshot().values():
priority = (
100
if LSP_SERVER.paths_equal(index.path, filepath)
else 200
)
for occurrence in index.occurrences:
if occurrence.identity.kind != "namespace":
continue
name = occurrence.identity.name
label = name if prefix_is_absolute else name.removeprefix("::")
namespace_candidates.append(
(
priority,
lsp.CompletionItem(
label=label,
kind=lsp.CompletionItemKind.Module,
detail="Tcl namespace",
),
)
)
items = ranked_completion_items(
namespace_candidates,
CompletionContext.GENERAL,
)
return lsp.CompletionList(is_incomplete=False, items=items)
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)
+6 -18
View File
@@ -6,9 +6,11 @@ from collections.abc import Iterable, Sequence
from enum import Enum
import lsprotocol.types as lsp
from common.load_data import standard_items
from tclint.syntax_tree import BareWord, Command, List, Visitor
from common.load_data import standard_items
from tools.tcl_command_completion import line_prefix_at_position
BUILTIN_VAR_LABELS = {ci.label for ci in standard_items.nx_variables}
BUILTIN_PROC_LABELS = {ci.label for ci in standard_items.nx_procs}
@@ -29,33 +31,19 @@ COMMAND_KINDS = {
lsp.CompletionItemKind.Method,
lsp.CompletionItemKind.Constructor,
lsp.CompletionItemKind.Keyword,
lsp.CompletionItemKind.Snippet,
}
_VARIABLE_PREFIX_RE = re.compile(r"(?<!\\)\$(?:\{)?[A-Za-z0-9_:]*$")
_COMMAND_PREFIX_RE = re.compile(r"(?:^|[;\[\{])\s*[^\s;\[\]\{\}]*$")
def _codepoint_offset(line: str, utf16_offset: int) -> int:
"""Translate an LSP UTF-16 character offset into a Python string offset."""
if utf16_offset <= 0:
return 0
units = 0
for offset, character in enumerate(line):
units += 2 if ord(character) > 0xFFFF else 1
if units >= utf16_offset:
return offset + 1
return len(line)
def completion_context(
source_lines: Sequence[str], position: lsp.Position
) -> CompletionContext:
if position.line < 0 or position.line >= len(source_lines):
prefix = line_prefix_at_position(source_lines, position)
if prefix is None:
return CompletionContext.GENERAL
line = source_lines[position.line]
prefix = line[: _codepoint_offset(line, position.character)]
if _VARIABLE_PREFIX_RE.search(prefix):
return CompletionContext.VARIABLE
if _COMMAND_PREFIX_RE.search(prefix):
File diff suppressed because it is too large Load Diff
+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,
@@ -6,18 +6,24 @@ SRC_DIR = THIS_DIR.parent.parent / "src"
if str(SRC_DIR) not in sys.path:
sys.path.insert(0, str(SRC_DIR))
import lsp_server
import lsprotocol.types as lsp # type: ignore
from common.load_data import standard_items
from lsp_tclserver import TclLanguageServer
from pygls.workspace import Workspace
from pygls.workspace.text_document import TextDocument
import lsp_server
from common.load_data import standard_items
from lsp_tclserver import TclLanguageServer
from tools.completion_items import (
COMMAND_KINDS,
VARIABLE_KINDS,
CompletionContext,
completion_context,
)
from tools.tcl_command_completion import (
DynamicCompletionKind,
path_completion_items,
tcl_argument_completion,
)
def _position_after(source: str, token: str, occurrence: int = 0) -> lsp.Position:
@@ -90,6 +96,22 @@ def _complete(document: TextDocument, position: lsp.Position):
).items
def _argument_completion_labels(source: str) -> set[str] | None:
completion = _argument_completion_request(source)
if completion is None:
return None
return {item.label for item in completion.items}
def _argument_completion_request(source: str):
lines = source.split("\n")
character = len(lines[-1].encode("utf-16-le")) // 2
return tcl_argument_completion(
lines,
lsp.Position(line=len(lines) - 1, character=character),
)
def test_variable_completion_filters_and_ranks_candidates(tmp_path: Path, monkeypatch):
_, current, source = _completion_server(tmp_path, monkeypatch)
items = _complete(current, _position_after(source, "$local"))
@@ -150,3 +172,316 @@ def test_completion_context_handles_nested_commands_and_utf16():
completion_context(["puts value"], lsp.Position(line=0, character=10))
== CompletionContext.GENERAL
)
def test_string_subcommands_and_compare_options_are_context_aware():
subcommands = _argument_completion_labels("string ")
assert subcommands is not None
assert {"compare", "equal", "is", "map", "match"} <= subcommands
assert _argument_completion_labels("string compare ") == {
"-length",
"-nocase",
}
assert _argument_completion_labels("string compare -nocase ") == {
"-length"
}
assert _argument_completion_labels("string compare -length ") is None
def test_string_completion_handles_nested_commands_and_is_values():
assert _argument_completion_labels("set result [string compare -") == {
"-length",
"-nocase",
}
classes = _argument_completion_labels("string is ")
assert classes is not None
assert {"boolean", "double", "integer", "wideinteger"} <= classes
assert _argument_completion_labels("string is integer ") == {
"-failindex",
"-strict",
}
def test_dict_array_namespace_file_and_info_subcommands():
dict_items = _argument_completion_labels("dict ")
assert dict_items is not None
assert {"create", "filter", "get", "set", "with"} <= dict_items
assert _argument_completion_labels("dict filter ") == {
"key",
"script",
"value",
}
assert _argument_completion_labels("array names values ") == {
"-exact",
"-glob",
"-regexp",
}
namespace_items = _argument_completion_labels("namespace ")
assert namespace_items is not None
assert {"children", "ensemble", "eval", "which"} <= namespace_items
assert _argument_completion_labels("namespace which ") == {
"-command",
"-variable",
}
assert _argument_completion_labels("namespace ensemble ") == {
"configure",
"create",
"exists",
}
file_items = _argument_completion_labels("file ")
assert file_items is not None
assert {"copy", "delete", "exists", "normalize", "rename"} <= file_items
assert _argument_completion_labels("file copy ") == {"--", "-force"}
info_items = _argument_completion_labels("info ")
assert info_items is not None
assert {"args", "body", "commands", "exists", "procs", "vars"} <= info_items
def test_variable_context_still_takes_priority_inside_tcl_command(
tmp_path: Path, monkeypatch
):
_, current, source = _completion_server(tmp_path, monkeypatch)
command_source = source.replace(
" puts $local\n",
" puts $local\n string compare $localValue other\n",
)
current = _document(tmp_path / "current.tcl", command_source)
lsp_server.LSP_SERVER.workspace.put_text_document(
lsp.TextDocumentItem(
uri=current.uri,
language_id="tcl",
version=2,
text=command_source,
)
)
items = _complete(current, _position_after(command_source, "$localValue"))
assert items
assert all(item.kind in VARIABLE_KINDS for item in items)
assert "localValue" in {item.label for item in items}
def test_lsp_completion_returns_only_matching_command_options(
tmp_path: Path, monkeypatch
):
_, current, _ = _completion_server(tmp_path, monkeypatch)
source = "string compare "
current = _document(tmp_path / "current.tcl", source)
lsp_server.LSP_SERVER.workspace.put_text_document(
lsp.TextDocumentItem(
uri=current.uri,
language_id="tcl",
version=2,
text=source,
)
)
items = _complete(current, _position_after(source, source))
assert {item.label for item in items} == {"-length", "-nocase"}
assert all(item.kind == lsp.CompletionItemKind.Keyword for item in items)
assert all(item.sort_text.startswith("000:") for item in items)
def test_space_trigger_does_not_open_broad_fallback_completion(
tmp_path: Path, monkeypatch
):
_, current, _ = _completion_server(tmp_path, monkeypatch)
source = "set value "
current = _document(tmp_path / "current.tcl", source)
lsp_server.LSP_SERVER.workspace.put_text_document(
lsp.TextDocumentItem(
uri=current.uri,
language_id="tcl",
version=2,
text=source,
)
)
result = lsp_server.on_completion(
lsp.CompletionParams(
text_document=lsp.TextDocumentIdentifier(uri=current.uri),
position=_position_after(source, source),
context=lsp.CompletionContext(
trigger_kind=lsp.CompletionTriggerKind.TriggerCharacter,
trigger_character=" ",
),
)
)
assert result.items == []
def test_argument_values_are_suggested_at_the_expected_position():
assert _argument_completion_labels("return -code ") == {
"break",
"continue",
"error",
"ok",
"return",
}
assert _argument_completion_labels("open output.txt ") == {
"a",
"a+",
"r",
"r+",
"w",
"w+",
}
assert _argument_completion_labels("seek channel 0 ") == {
"current",
"end",
"start",
}
def test_dynamic_argument_categories_are_detected():
variable = _argument_completion_request("set ")
procedure = _argument_completion_request("info body ")
namespace = _argument_completion_request("namespace eval ")
path = _argument_completion_request("source scripts/")
assert variable is not None
assert variable.dynamic_kind == DynamicCompletionKind.VARIABLE
assert procedure is not None
assert procedure.dynamic_kind == DynamicCompletionKind.PROCEDURE
assert namespace is not None
assert namespace.dynamic_kind == DynamicCompletionKind.NAMESPACE
assert path is not None
assert path.dynamic_kind == DynamicCompletionKind.PATH
assert path.path_extensions == (".tcl",)
def test_path_completion_is_relative_filtered_and_tcl_safe(tmp_path: Path):
scripts = tmp_path / "scripts"
scripts.mkdir()
(scripts / "first file.tcl").write_text("puts ok", encoding="utf-8")
(scripts / "ignored.txt").write_text("ignored", encoding="utf-8")
source = "source scripts/f"
completion = _argument_completion_request(source)
assert completion is not None
items = path_completion_items(
tmp_path,
completion,
_position_after(source, source),
)
assert [item.label for item in items] == ["scripts/first\\ file.tcl"]
assert items[0].kind == lsp.CompletionItemKind.File
assert items[0].text_edit is not None
assert items[0].text_edit.new_text == "scripts/first\\ file.tcl"
assert items[0].text_edit.range.start.character == len("source ")
def test_lsp_source_completion_reads_paths_from_document_directory(
tmp_path: Path, monkeypatch
):
server, current, _ = _completion_server(tmp_path, monkeypatch)
scripts = tmp_path / "scripts"
scripts.mkdir()
(scripts / "helper.tcl").write_text("proc helper {} {}", encoding="utf-8")
(scripts / "ignored.txt").write_text("ignored", encoding="utf-8")
source = "source scripts/"
current = _document(tmp_path / "current.tcl", source)
server.workspace.put_text_document(
lsp.TextDocumentItem(
uri=current.uri,
language_id="tcl",
version=2,
text=source,
)
)
items = _complete(current, _position_after(source, source))
labels = {item.label for item in items}
assert "scripts/helper.tcl" in labels
assert "scripts/ignored.txt" not in labels
def test_command_and_dict_for_snippets_use_lsp_snippet_placeholders(
tmp_path: Path, monkeypatch
):
_, current, source = _completion_server(tmp_path, monkeypatch)
command_items = _complete(current, _position_after(source, "localP", occurrence=1))
command_by_label = {item.label: item for item in command_items}
assert command_by_label["if"].kind == lsp.CompletionItemKind.Snippet
assert command_by_label["if"].insert_text_format == lsp.InsertTextFormat.Snippet
assert "${1:condition}" in (command_by_label["if"].insert_text or "")
dict_for = _argument_completion_request("dict f")
assert dict_for is not None
for_item = next(item for item in dict_for.items if item.label == "for")
assert for_item.kind == lsp.CompletionItemKind.Snippet
assert "${3:dictionary}" in (for_item.insert_text or "")
switch_arguments = _argument_completion_request("switch ")
assert switch_arguments is not None
assert {"switch block", "-exact", "-glob", "-regexp"} <= {
item.label for item in switch_arguments.items
}
def test_semantic_variable_and_procedure_argument_completion(
tmp_path: Path, monkeypatch
):
_, current, source = _completion_server(tmp_path, monkeypatch)
variable_items = _complete(current, _position_after(source, " set "))
assert variable_items
assert all(item.kind in VARIABLE_KINDS for item in variable_items)
assert {"argument", "localValue"} <= {item.label for item in variable_items}
procedure_source = source + "info body localProc\n"
current = _document(tmp_path / "current.tcl", procedure_source)
lsp_server.LSP_SERVER.workspace.put_text_document(
lsp.TextDocumentItem(
uri=current.uri,
language_id="tcl",
version=2,
text=procedure_source,
)
)
procedure_items = _complete(
current,
_position_after(procedure_source, "info body "),
)
procedure_labels = {item.label for item in procedure_items}
assert "localProc" in procedure_labels
assert "workspaceProc" in procedure_labels
assert "MOM_abort" in procedure_labels
assert "localValue" not in procedure_labels
assert "string" not in procedure_labels
def test_namespace_argument_completion_uses_navigation_index(
tmp_path: Path, monkeypatch
):
server, current, _ = _completion_server(tmp_path, monkeypatch)
namespace_source = "namespace eval tools { proc helper {} { return } }\n"
namespace_document = _document(tmp_path / "namespaces.tcl", namespace_source)
assert server.update_poco_completion_for_file(namespace_document)
source = "namespace eval "
current = _document(tmp_path / "current.tcl", source)
server.workspace.put_text_document(
lsp.TextDocumentItem(
uri=current.uri,
language_id="tcl",
version=2,
text=source,
)
)
items = _complete(current, _position_after(source, source))
assert "tools" in {item.label for item in items}
assert all(item.kind == lsp.CompletionItemKind.Module for item in items)
-1
View File
@@ -117,7 +117,6 @@ proc SERVICE_get_tool_data {} {
}
}
LIB_GE_command_buffer_edit_replace MOM_end_of_program_LIB END_OF_PROGRAM @END_OF_PROG {
MOM_do_template "end_of_program_rewind"
} EndOfProgramRewind