Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a857dab85a | ||
|
|
28bd6557c3 | ||
|
|
0a8e8a7368 |
@@ -47,20 +47,9 @@ def _document(path: Path, source: str) -> TextDocument:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def _completion_server(
|
def _completion_server(tmp_path: Path, monkeypatch) -> tuple[TclLanguageServer, TextDocument, str]:
|
||||||
tmp_path: Path, monkeypatch
|
|
||||||
) -> tuple[TclLanguageServer, TextDocument, str]:
|
|
||||||
declared_builtin = standard_items.nx_variables[0].label
|
declared_builtin = standard_items.nx_variables[0].label
|
||||||
current_source = (
|
current_source = f"set globalValue 1\nproc localProc {{}} {{ return }}\nproc caller {{argument}} {{\n global {declared_builtin}\n set localValue 2\n puts $local\n localP\n}}\n"
|
||||||
"set globalValue 1\n"
|
|
||||||
"proc localProc {} { return }\n"
|
|
||||||
"proc caller {argument} {\n"
|
|
||||||
f" global {declared_builtin}\n"
|
|
||||||
" set localValue 2\n"
|
|
||||||
" puts $local\n"
|
|
||||||
" localP\n"
|
|
||||||
"}\n"
|
|
||||||
)
|
|
||||||
workspace_source = """set ::workspaceValue 1
|
workspace_source = """set ::workspaceValue 1
|
||||||
proc workspaceProc {} { return }
|
proc workspaceProc {} { return }
|
||||||
"""
|
"""
|
||||||
@@ -113,14 +102,10 @@ def test_unset_space_shows_options_then_variables(tmp_path, monkeypatch):
|
|||||||
items = _complete(document, lsp.Position(line=1, character=len(tail)))
|
items = _complete(document, lsp.Position(line=1, character=len(tail)))
|
||||||
labels = {item.label for item in items}
|
labels = {item.label for item in items}
|
||||||
if tail in {"unset ", "unset -"}:
|
if tail in {"unset ", "unset -"}:
|
||||||
assert labels == {"-nocomplain", "--"}
|
assert labels == {"nocomplain"}
|
||||||
else:
|
else:
|
||||||
assert "globalValue" in labels
|
assert "globalValue" in labels
|
||||||
assert "-nocomplain" not in labels
|
assert "-nocomplain" not in labels
|
||||||
if tail == "unset -nocomplain ":
|
|
||||||
assert "--" in labels
|
|
||||||
else:
|
|
||||||
assert "--" not in labels
|
|
||||||
|
|
||||||
|
|
||||||
def test_array_keys_complete_in_set_and_substitution(tmp_path: Path, monkeypatch):
|
def test_array_keys_complete_in_set_and_substitution(tmp_path: Path, monkeypatch):
|
||||||
@@ -130,19 +115,16 @@ def test_array_keys_complete_in_set_and_substitution(tmp_path: Path, monkeypatch
|
|||||||
"set ::lib_flag(enabled) 1\nset ::lib_flag(external) 1\n",
|
"set ::lib_flag(enabled) 1\nset ::lib_flag(external) 1\n",
|
||||||
)
|
)
|
||||||
assert server.update_poco_completion_for_file(workspace)
|
assert server.update_poco_completion_for_file(workspace)
|
||||||
source = (
|
source = "set lib_flag(enabled) 0\nset lib_flag(empty) 1\nset other(wrong) 1\nproc hidden {} { set lib_flag(private) 1 }\nset lib_flag()\nputs $lib_flag(en)\nputs 😀; set lib_flag(em\n"
|
||||||
"set lib_flag(enabled) 0\n"
|
|
||||||
"set lib_flag(empty) 1\n"
|
|
||||||
"set other(wrong) 1\n"
|
|
||||||
"proc hidden {} { set lib_flag(private) 1 }\n"
|
|
||||||
"set lib_flag()\n"
|
|
||||||
"puts $lib_flag(en)\n"
|
|
||||||
"puts 😀; set lib_flag(em\n"
|
|
||||||
)
|
|
||||||
current = _document(tmp_path / "arrays-current.tcl", source)
|
current = _document(tmp_path / "arrays-current.tcl", source)
|
||||||
server.workspace.put_text_document(lsp.TextDocumentItem(
|
server.workspace.put_text_document(
|
||||||
uri=current.uri, language_id="tcl", version=1, text=source,
|
lsp.TextDocumentItem(
|
||||||
))
|
uri=current.uri,
|
||||||
|
language_id="tcl",
|
||||||
|
version=1,
|
||||||
|
text=source,
|
||||||
|
)
|
||||||
|
)
|
||||||
assert server.update_poco_completion_for_file(current)
|
assert server.update_poco_completion_for_file(current)
|
||||||
items = _complete(current, _position_after(source, "set lib_flag(", 3))
|
items = _complete(current, _position_after(source, "set lib_flag(", 3))
|
||||||
assert [item.label for item in items] == ["empty", "enabled", "external"]
|
assert [item.label for item in items] == ["empty", "enabled", "external"]
|
||||||
@@ -179,32 +161,28 @@ def test_dynamic_array_index_keeps_variable_identity(tmp_path: Path, monkeypatch
|
|||||||
assert variable_name(tree.children[3].args[0]) is None
|
assert variable_name(tree.children[3].args[0]) is None
|
||||||
path = tmp_path / "dynamic.tcl"
|
path = tmp_path / "dynamic.tcl"
|
||||||
index = build_file_symbol_index(str(path), path.as_uri(), tree)
|
index = build_file_symbol_index(str(path), path.as_uri(), tree)
|
||||||
definition = next(
|
definition = next(item for item in index.occurrences if item.identity.name == "::custom_flag" and item.is_definition)
|
||||||
item for item in index.occurrences
|
|
||||||
if item.identity.name == "::custom_flag" and item.is_definition
|
|
||||||
)
|
|
||||||
assert definition.range.start.character == 4
|
assert definition.range.start.character == 4
|
||||||
assert definition.range.end.character == 15
|
assert definition.range.end.character == 15
|
||||||
assert definition.array_element is None
|
assert definition.array_element is None
|
||||||
assert any(item.identity.name == "::mom_path_name" for item in index.occurrences)
|
assert any(item.identity.name == "::mom_path_name" for item in index.occurrences)
|
||||||
highlighter = _Highlighter([], {})
|
highlighter = _Highlighter([], {})
|
||||||
tree.accept(highlighter, recurse=True)
|
tree.accept(highlighter, recurse=True)
|
||||||
assert any(
|
assert any(position == (0, 4) and length == 11 and kind == "variable" for position, length, kind, _ in highlighter._tokens)
|
||||||
position == (0, 4) and length == 11 and kind == "variable"
|
|
||||||
for position, length, kind, _ in highlighter._tokens
|
|
||||||
)
|
|
||||||
server, _, _ = _completion_server(tmp_path, monkeypatch)
|
server, _, _ = _completion_server(tmp_path, monkeypatch)
|
||||||
current = _document(path, source)
|
current = _document(path, source)
|
||||||
server.workspace.put_text_document(lsp.TextDocumentItem(
|
server.workspace.put_text_document(
|
||||||
uri=current.uri, language_id="tcl", version=1, text=source,
|
lsp.TextDocumentItem(
|
||||||
))
|
uri=current.uri,
|
||||||
|
language_id="tcl",
|
||||||
|
version=1,
|
||||||
|
text=source,
|
||||||
|
)
|
||||||
|
)
|
||||||
assert server.update_poco_completion_for_file(current)
|
assert server.update_poco_completion_for_file(current)
|
||||||
items = _complete(current, _position_after(source, "puts $custom"))
|
items = _complete(current, _position_after(source, "puts $custom"))
|
||||||
assert "custom_flag" in {item.label for item in items}
|
assert "custom_flag" in {item.label for item in items}
|
||||||
workspace_items = next(
|
workspace_items = next(items for item_path, items in server.completion_items_by_file_snapshot().items() if server.paths_equal(item_path, str(path)))
|
||||||
items for item_path, items in server.completion_items_by_file_snapshot().items()
|
|
||||||
if server.paths_equal(item_path, str(path))
|
|
||||||
)
|
|
||||||
assert {"quoted_flag", "command_flag"} <= {item.label for item in workspace_items}
|
assert {"quoted_flag", "command_flag"} <= {item.label for item in workspace_items}
|
||||||
|
|
||||||
|
|
||||||
@@ -236,18 +214,25 @@ def test_literal_array_components_complete_around_substitutions(tmp_path: Path,
|
|||||||
offset = marked.index("|")
|
offset = marked.index("|")
|
||||||
line = marked.replace("|", "")
|
line = marked.replace("|", "")
|
||||||
items = array_element_completions(
|
items = array_element_completions(
|
||||||
[line], lsp.Position(line=0, character=offset),
|
[line],
|
||||||
server.navigation_snapshot().values(), str(tmp_path / "caller.tcl"),
|
lsp.Position(line=0, character=offset),
|
||||||
|
server.navigation_snapshot().values(),
|
||||||
|
str(tmp_path / "caller.tcl"),
|
||||||
)
|
)
|
||||||
assert {item.label for item in items} == labels
|
assert {item.label for item in items} == labels
|
||||||
edit = next(item.text_edit for item in items if item.label == selected)
|
edit = next(item.text_edit for item in items if item.label == selected)
|
||||||
item = next(item for item in items if item.label == selected)
|
item = next(item for item in items if item.label == selected)
|
||||||
assert item.insert_text_format == lsp.InsertTextFormat.PlainText
|
assert item.insert_text_format == lsp.InsertTextFormat.PlainText
|
||||||
assert line[:edit.range.start.character] + edit.new_text + line[edit.range.end.character:] == expected
|
assert line[: edit.range.start.character] + edit.new_text + line[edit.range.end.character :] == expected
|
||||||
assert array_element_completions(
|
assert (
|
||||||
["set custom_flag(from_move,$::mom"], lsp.Position(line=0, character=31),
|
array_element_completions(
|
||||||
server.navigation_snapshot().values(), str(tmp_path / "caller.tcl"),
|
["set custom_flag(from_move,$::mom"],
|
||||||
) is None
|
lsp.Position(line=0, character=31),
|
||||||
|
server.navigation_snapshot().values(),
|
||||||
|
str(tmp_path / "caller.tcl"),
|
||||||
|
)
|
||||||
|
is None
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def _argument_completion_request(source: str):
|
def _argument_completion_request(source: str):
|
||||||
@@ -277,9 +262,7 @@ def test_variable_completion_filters_and_ranks_candidates(tmp_path: Path, monkey
|
|||||||
other_builtin = standard_items.nx_variables[1]
|
other_builtin = standard_items.nx_variables[1]
|
||||||
assert declared_builtin.label in by_label
|
assert declared_builtin.label in by_label
|
||||||
assert other_builtin.label in by_label
|
assert other_builtin.label in by_label
|
||||||
assert by_label[declared_builtin.label].documentation == (
|
assert by_label[declared_builtin.label].documentation == (declared_builtin.documentation)
|
||||||
declared_builtin.documentation
|
|
||||||
)
|
|
||||||
assert by_label["localValue"].sort_text.startswith("000:")
|
assert by_label["localValue"].sort_text.startswith("000:")
|
||||||
assert by_label["globalValue"].sort_text.startswith("100:")
|
assert by_label["globalValue"].sort_text.startswith("100:")
|
||||||
assert by_label["workspaceValue"].sort_text.startswith("200:")
|
assert by_label["workspaceValue"].sort_text.startswith("200:")
|
||||||
@@ -307,18 +290,9 @@ def test_command_completion_filters_and_ranks_candidates(tmp_path: Path, monkeyp
|
|||||||
|
|
||||||
|
|
||||||
def test_completion_context_handles_nested_commands_and_utf16():
|
def test_completion_context_handles_nested_commands_and_utf16():
|
||||||
assert (
|
assert completion_context(["set result [work"], lsp.Position(line=0, character=16)) == CompletionContext.COMMAND
|
||||||
completion_context(["set result [work"], lsp.Position(line=0, character=16))
|
assert completion_context(["😀 puts $value"], lsp.Position(line=0, character=14)) == CompletionContext.VARIABLE
|
||||||
== CompletionContext.COMMAND
|
assert completion_context(["puts value"], lsp.Position(line=0, character=10)) == CompletionContext.GENERAL
|
||||||
)
|
|
||||||
assert (
|
|
||||||
completion_context(["😀 puts $value"], lsp.Position(line=0, character=14))
|
|
||||||
== CompletionContext.VARIABLE
|
|
||||||
)
|
|
||||||
assert (
|
|
||||||
completion_context(["puts value"], lsp.Position(line=0, character=10))
|
|
||||||
== CompletionContext.GENERAL
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def test_string_subcommands_and_compare_options_are_context_aware():
|
def test_string_subcommands_and_compare_options_are_context_aware():
|
||||||
@@ -330,9 +304,7 @@ def test_string_subcommands_and_compare_options_are_context_aware():
|
|||||||
"-length",
|
"-length",
|
||||||
"-nocase",
|
"-nocase",
|
||||||
}
|
}
|
||||||
assert _argument_completion_labels("string compare -nocase ") == {
|
assert _argument_completion_labels("string compare -nocase ") == {"-length"}
|
||||||
"-length"
|
|
||||||
}
|
|
||||||
assert _argument_completion_labels("string compare -length ") is None
|
assert _argument_completion_labels("string compare -length ") is None
|
||||||
|
|
||||||
|
|
||||||
@@ -356,22 +328,14 @@ def test_string_completion_inside_braced_conditions_and_bodies():
|
|||||||
subcommands = _argument_completion_labels(prefix + "[string ")
|
subcommands = _argument_completion_labels(prefix + "[string ")
|
||||||
assert subcommands is not None
|
assert subcommands is not None
|
||||||
assert {"compare", "equal", "is"} <= subcommands
|
assert {"compare", "equal", "is"} <= subcommands
|
||||||
assert _argument_completion_labels(prefix + "[string compare -") == {
|
assert _argument_completion_labels(prefix + "[string compare -") == {"-length", "-nocase"}
|
||||||
"-length", "-nocase"
|
assert _argument_completion_labels(prefix + "[string compare -nocase ") == {"-length"}
|
||||||
}
|
|
||||||
assert _argument_completion_labels(
|
|
||||||
prefix + "[string compare -nocase "
|
|
||||||
) == {"-length"}
|
|
||||||
|
|
||||||
|
|
||||||
def test_closed_braced_arguments_do_not_change_completion_context():
|
def test_closed_braced_arguments_do_not_change_completion_context():
|
||||||
assert _argument_completion_labels("puts {[string compare }") is None
|
assert _argument_completion_labels("puts {[string compare }") is None
|
||||||
assert _argument_completion_labels(
|
assert _argument_completion_labels("if {[string equal a b]} {string compare ") == {"-length", "-nocase"}
|
||||||
"if {[string equal a b]} {string compare "
|
assert _argument_completion_labels("if {[string equal a b] && [string is integer ") == {"-failindex", "-strict"}
|
||||||
) == {"-length", "-nocase"}
|
|
||||||
assert _argument_completion_labels(
|
|
||||||
"if {[string equal a b] && [string is integer "
|
|
||||||
) == {"-failindex", "-strict"}
|
|
||||||
|
|
||||||
|
|
||||||
def test_dict_array_namespace_file_and_info_subcommands():
|
def test_dict_array_namespace_file_and_info_subcommands():
|
||||||
@@ -413,9 +377,7 @@ def test_dict_array_namespace_file_and_info_subcommands():
|
|||||||
assert {"args", "body", "commands", "exists", "procs", "vars"} <= info_items
|
assert {"args", "body", "commands", "exists", "procs", "vars"} <= info_items
|
||||||
|
|
||||||
|
|
||||||
def test_variable_context_still_takes_priority_inside_tcl_command(
|
def test_variable_context_still_takes_priority_inside_tcl_command(tmp_path: Path, monkeypatch):
|
||||||
tmp_path: Path, monkeypatch
|
|
||||||
):
|
|
||||||
_, current, source = _completion_server(tmp_path, monkeypatch)
|
_, current, source = _completion_server(tmp_path, monkeypatch)
|
||||||
command_source = source.replace(
|
command_source = source.replace(
|
||||||
" puts $local\n",
|
" puts $local\n",
|
||||||
@@ -437,9 +399,7 @@ def test_variable_context_still_takes_priority_inside_tcl_command(
|
|||||||
assert "localValue" in {item.label for item in items}
|
assert "localValue" in {item.label for item in items}
|
||||||
|
|
||||||
|
|
||||||
def test_lsp_completion_returns_only_matching_command_options(
|
def test_lsp_completion_returns_only_matching_command_options(tmp_path: Path, monkeypatch):
|
||||||
tmp_path: Path, monkeypatch
|
|
||||||
):
|
|
||||||
_, current, _ = _completion_server(tmp_path, monkeypatch)
|
_, current, _ = _completion_server(tmp_path, monkeypatch)
|
||||||
source = "string compare "
|
source = "string compare "
|
||||||
current = _document(tmp_path / "current.tcl", source)
|
current = _document(tmp_path / "current.tcl", source)
|
||||||
@@ -459,9 +419,7 @@ def test_lsp_completion_returns_only_matching_command_options(
|
|||||||
assert all(item.sort_text.startswith("000:") 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(
|
def test_space_trigger_does_not_open_broad_fallback_completion(tmp_path: Path, monkeypatch):
|
||||||
tmp_path: Path, monkeypatch
|
|
||||||
):
|
|
||||||
_, current, _ = _completion_server(tmp_path, monkeypatch)
|
_, current, _ = _completion_server(tmp_path, monkeypatch)
|
||||||
source = "set value "
|
source = "set value "
|
||||||
current = _document(tmp_path / "current.tcl", source)
|
current = _document(tmp_path / "current.tcl", source)
|
||||||
@@ -550,9 +508,7 @@ def test_path_completion_is_relative_filtered_and_tcl_safe(tmp_path: Path):
|
|||||||
assert items[0].text_edit.range.start.character == len("source ")
|
assert items[0].text_edit.range.start.character == len("source ")
|
||||||
|
|
||||||
|
|
||||||
def test_lsp_source_completion_reads_paths_from_document_directory(
|
def test_lsp_source_completion_reads_paths_from_document_directory(tmp_path: Path, monkeypatch):
|
||||||
tmp_path: Path, monkeypatch
|
|
||||||
):
|
|
||||||
server, current, _ = _completion_server(tmp_path, monkeypatch)
|
server, current, _ = _completion_server(tmp_path, monkeypatch)
|
||||||
scripts = tmp_path / "scripts"
|
scripts = tmp_path / "scripts"
|
||||||
scripts.mkdir()
|
scripts.mkdir()
|
||||||
@@ -576,9 +532,7 @@ def test_lsp_source_completion_reads_paths_from_document_directory(
|
|||||||
assert "scripts/ignored.txt" not in labels
|
assert "scripts/ignored.txt" not in labels
|
||||||
|
|
||||||
|
|
||||||
def test_command_and_dict_for_snippets_use_lsp_snippet_placeholders(
|
def test_command_and_dict_for_snippets_use_lsp_snippet_placeholders(tmp_path: Path, monkeypatch):
|
||||||
tmp_path: Path, monkeypatch
|
|
||||||
):
|
|
||||||
_, current, source = _completion_server(tmp_path, monkeypatch)
|
_, current, source = _completion_server(tmp_path, monkeypatch)
|
||||||
command_items = _complete(current, _position_after(source, "localP", occurrence=1))
|
command_items = _complete(current, _position_after(source, "localP", occurrence=1))
|
||||||
command_by_label = {item.label: item for item in command_items}
|
command_by_label = {item.label: item for item in command_items}
|
||||||
@@ -595,14 +549,10 @@ def test_command_and_dict_for_snippets_use_lsp_snippet_placeholders(
|
|||||||
|
|
||||||
switch_arguments = _argument_completion_request("switch ")
|
switch_arguments = _argument_completion_request("switch ")
|
||||||
assert switch_arguments is not None
|
assert switch_arguments is not None
|
||||||
assert {"switch block", "-exact", "-glob", "-regexp"} <= {
|
assert {"switch block", "-exact", "-glob", "-regexp"} <= {item.label for item in switch_arguments.items}
|
||||||
item.label for item in switch_arguments.items
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def test_semantic_variable_and_procedure_argument_completion(
|
def test_semantic_variable_and_procedure_argument_completion(tmp_path: Path, monkeypatch):
|
||||||
tmp_path: Path, monkeypatch
|
|
||||||
):
|
|
||||||
_, current, source = _completion_server(tmp_path, monkeypatch)
|
_, current, source = _completion_server(tmp_path, monkeypatch)
|
||||||
|
|
||||||
variable_items = _complete(current, _position_after(source, " set "))
|
variable_items = _complete(current, _position_after(source, " set "))
|
||||||
@@ -633,9 +583,7 @@ def test_semantic_variable_and_procedure_argument_completion(
|
|||||||
assert "string" not in procedure_labels
|
assert "string" not in procedure_labels
|
||||||
|
|
||||||
|
|
||||||
def test_namespace_argument_completion_uses_navigation_index(
|
def test_namespace_argument_completion_uses_navigation_index(tmp_path: Path, monkeypatch):
|
||||||
tmp_path: Path, monkeypatch
|
|
||||||
):
|
|
||||||
server, current, _ = _completion_server(tmp_path, monkeypatch)
|
server, current, _ = _completion_server(tmp_path, monkeypatch)
|
||||||
namespace_source = "namespace eval tools { proc helper {} { return } }\n"
|
namespace_source = "namespace eval tools { proc helper {} { return } }\n"
|
||||||
namespace_document = _document(tmp_path / "namespaces.tcl", namespace_source)
|
namespace_document = _document(tmp_path / "namespaces.tcl", namespace_source)
|
||||||
|
|||||||
Reference in New Issue
Block a user