Compare commits
3
Commits
2026.9.800
...
2026.9.801
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bd9c73452e | ||
|
|
a393ca3aec | ||
|
|
85a8684254 |
+1
-1
@@ -2,7 +2,7 @@
|
|||||||
"name": "nx-post-support",
|
"name": "nx-post-support",
|
||||||
"displayName": "NX Postprocessor 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",
|
"description": "VS Code extension for NX CAM postprocessor development with language support and remote Tcl debugging for CDL, TCL, and DEF files",
|
||||||
"version": "2026.9.701",
|
"version": "2026.9.800",
|
||||||
"publisher": "Christoph",
|
"publisher": "Christoph",
|
||||||
"icon": "images/nx-1.png",
|
"icon": "images/nx-1.png",
|
||||||
"activationEvents": [
|
"activationEvents": [
|
||||||
|
|||||||
@@ -0,0 +1,191 @@
|
|||||||
|
"""Navigation between Tcl code and the block templates and addresses of .def files."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import lsprotocol.types as lsp
|
||||||
|
|
||||||
|
from tools.def_symbols import ADDRESS, BLOCK_TEMPLATE, FORMAT, DefDeclaration, DefDocument
|
||||||
|
from tools.navigation import FileSymbolIndex, SymbolOccurrence
|
||||||
|
|
||||||
|
DefTarget = tuple[str, str]
|
||||||
|
_KIND_LABELS = {BLOCK_TEMPLATE: "Block template", ADDRESS: "Address", FORMAT: "Format"}
|
||||||
|
# Address properties in display order; others follow as declared.
|
||||||
|
_ADDRESS_PROPERTIES = (
|
||||||
|
("FORMAT", "Format"),
|
||||||
|
("LEADER", "Leader"),
|
||||||
|
("TRAILER", "Trailer"),
|
||||||
|
("MIN", "Min"),
|
||||||
|
("MAX", "Max"),
|
||||||
|
("FORCE", "Modality"),
|
||||||
|
("ZERO_FORMAT", "Zero format"),
|
||||||
|
("INCREMENTAL", "Incremental"),
|
||||||
|
("OMIT", "Omit"),
|
||||||
|
)
|
||||||
|
_MODALITY = {
|
||||||
|
"OFF": "modal, output only on change",
|
||||||
|
"ONCE": "output once, then modal",
|
||||||
|
"ALWAYS": "non-modal, always output",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _range(line: int, start: int, end: int) -> lsp.Range:
|
||||||
|
return lsp.Range(
|
||||||
|
start=lsp.Position(line=line, character=start),
|
||||||
|
end=lsp.Position(line=line, character=end),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _contains(line: int, start: int, end: int, position: lsp.Position) -> bool:
|
||||||
|
return position.line == line and start <= position.character <= end
|
||||||
|
|
||||||
|
|
||||||
|
def def_symbol_at(document: DefDocument, position: lsp.Position) -> tuple[DefTarget, lsp.Range, bool] | None:
|
||||||
|
"""Return the block template or address at ``position`` of a .def document.
|
||||||
|
|
||||||
|
The flag tells whether the position is on a declaration.
|
||||||
|
"""
|
||||||
|
for declaration in document.declarations:
|
||||||
|
if declaration.kind != FORMAT and _contains(declaration.line, declaration.start, declaration.end, position):
|
||||||
|
return (declaration.kind, declaration.name), _range(declaration.line, declaration.start, declaration.end), True
|
||||||
|
for reference in document.references:
|
||||||
|
if _contains(reference.line, reference.start, reference.end, position):
|
||||||
|
return (reference.kind, reference.name), _range(reference.line, reference.start, reference.end), False
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def _uri(path: str, uris: dict[str, str]) -> str:
|
||||||
|
return uris.get(path) or Path(path).as_uri()
|
||||||
|
|
||||||
|
|
||||||
|
def def_declarations(documents: dict[str, DefDocument], target: DefTarget) -> list[tuple[str, DefDeclaration]]:
|
||||||
|
kind, name = target
|
||||||
|
return [
|
||||||
|
(path, declaration)
|
||||||
|
for path, document in documents.items()
|
||||||
|
for declaration in document.declarations
|
||||||
|
if declaration.kind == kind and declaration.name == name
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def def_definition_locations(
|
||||||
|
documents: dict[str, DefDocument], target: DefTarget, uris: dict[str, str] | None = None
|
||||||
|
) -> list[lsp.Location]:
|
||||||
|
uris = uris or {}
|
||||||
|
return [
|
||||||
|
lsp.Location(uri=_uri(path, uris), range=_range(declaration.line, declaration.start, declaration.end))
|
||||||
|
for path, declaration in def_declarations(documents, target)
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def def_reference_locations(
|
||||||
|
documents: dict[str, DefDocument],
|
||||||
|
target: DefTarget,
|
||||||
|
include_declaration: bool,
|
||||||
|
uris: dict[str, str] | None = None,
|
||||||
|
) -> list[lsp.Location]:
|
||||||
|
"""Return .def occurrences: declarations and addresses used in block templates."""
|
||||||
|
kind, name = target
|
||||||
|
uris = uris or {}
|
||||||
|
locations = def_definition_locations(documents, target, uris) if include_declaration else []
|
||||||
|
for path, document in documents.items():
|
||||||
|
for reference in document.references:
|
||||||
|
if reference.kind == kind and reference.name == name:
|
||||||
|
locations.append(
|
||||||
|
lsp.Location(uri=_uri(path, uris), range=_range(reference.line, reference.start, reference.end))
|
||||||
|
)
|
||||||
|
return locations
|
||||||
|
|
||||||
|
|
||||||
|
def tcl_def_occurrences(
|
||||||
|
indexes: dict[str, FileSymbolIndex], target: DefTarget
|
||||||
|
) -> list[tuple[FileSymbolIndex, SymbolOccurrence]]:
|
||||||
|
kind, name = target
|
||||||
|
return [
|
||||||
|
(index, occurrence)
|
||||||
|
for index in indexes.values()
|
||||||
|
for occurrence in index.occurrences
|
||||||
|
if occurrence.identity.kind == kind and occurrence.identity.name == name
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def all_def_target_locations(
|
||||||
|
documents: dict[str, DefDocument],
|
||||||
|
indexes: dict[str, FileSymbolIndex],
|
||||||
|
target: DefTarget,
|
||||||
|
include_declaration: bool,
|
||||||
|
uris: dict[str, str] | None = None,
|
||||||
|
) -> list[lsp.Location]:
|
||||||
|
locations = def_reference_locations(documents, target, include_declaration, uris)
|
||||||
|
locations.extend(
|
||||||
|
lsp.Location(uri=index.uri, range=occurrence.range) for index, occurrence in tcl_def_occurrences(indexes, target)
|
||||||
|
)
|
||||||
|
unique = {}
|
||||||
|
for location in locations:
|
||||||
|
key = (location.uri, location.range.start.line, location.range.start.character)
|
||||||
|
unique.setdefault(key, location)
|
||||||
|
return sorted(unique.values(), key=lambda item: (item.uri, item.range.start.line, item.range.start.character))
|
||||||
|
|
||||||
|
|
||||||
|
def def_rename_edits(
|
||||||
|
documents: dict[str, DefDocument],
|
||||||
|
indexes: dict[str, FileSymbolIndex],
|
||||||
|
target: DefTarget,
|
||||||
|
new_name: str,
|
||||||
|
uris: dict[str, str] | None = None,
|
||||||
|
) -> lsp.WorkspaceEdit | None:
|
||||||
|
"""Rename a block template or address in all .def and Tcl files, if it is declared."""
|
||||||
|
if not def_declarations(documents, target):
|
||||||
|
return None
|
||||||
|
changes: dict[str, list[lsp.TextEdit]] = {}
|
||||||
|
for location in all_def_target_locations(documents, indexes, target, True, uris):
|
||||||
|
changes.setdefault(location.uri, []).append(lsp.TextEdit(range=location.range, new_text=new_name))
|
||||||
|
for edits in changes.values():
|
||||||
|
edits.sort(key=lambda edit: (edit.range.start.line, edit.range.start.character), reverse=True)
|
||||||
|
return lsp.WorkspaceEdit(changes=changes)
|
||||||
|
|
||||||
|
|
||||||
|
def _escape_cell(value: str) -> str:
|
||||||
|
return value.replace("|", "\\|") or " "
|
||||||
|
|
||||||
|
|
||||||
|
def _address_table(declaration: DefDeclaration, formats: dict[str, DefDeclaration]) -> str:
|
||||||
|
properties = dict(declaration.properties)
|
||||||
|
rows = []
|
||||||
|
for key, label in _ADDRESS_PROPERTIES:
|
||||||
|
value = properties.pop(key, None)
|
||||||
|
if value is None:
|
||||||
|
continue
|
||||||
|
cell = f"`{value}`" if value else ""
|
||||||
|
if key == "FORMAT" and value in formats:
|
||||||
|
cell += f" → `{dict(formats[value].properties).get('FORMAT', '')}`"
|
||||||
|
if key == "FORCE":
|
||||||
|
cell += f" ({_MODALITY[value.upper()]})" if value.upper() in _MODALITY else ""
|
||||||
|
rows.append(f"| {label} | {_escape_cell(cell)} |")
|
||||||
|
rows.extend(f"| {key.title()} | {_escape_cell(f'`{value}`' if value else '')} |" for key, value in properties.items())
|
||||||
|
if not rows:
|
||||||
|
return "_No properties_"
|
||||||
|
return "\n".join(["| Property | Value |", "|---|---|", *rows])
|
||||||
|
|
||||||
|
|
||||||
|
def def_hover_markdown(documents: dict[str, DefDocument], target: DefTarget) -> str | None:
|
||||||
|
declarations = def_declarations(documents, target)
|
||||||
|
if not declarations:
|
||||||
|
return None
|
||||||
|
formats = {
|
||||||
|
declaration.name: declaration
|
||||||
|
for document in documents.values()
|
||||||
|
for declaration in document.declarations
|
||||||
|
if declaration.kind == FORMAT
|
||||||
|
}
|
||||||
|
kind, name = target
|
||||||
|
sections = []
|
||||||
|
for path, declaration in declarations:
|
||||||
|
header = f"**{_KIND_LABELS[kind]}** `{name}` — {Path(path).name}:{declaration.line + 1}"
|
||||||
|
if kind == ADDRESS:
|
||||||
|
body = _address_table(declaration, formats)
|
||||||
|
else:
|
||||||
|
body = f"```def\n{declaration.text}\n```"
|
||||||
|
sections.append(f"{header}\n\n{body}")
|
||||||
|
return "\n\n---\n\n".join(sections)
|
||||||
@@ -0,0 +1,242 @@
|
|||||||
|
"""Go to Definition, hover, references and rename between Tcl and .def files."""
|
||||||
|
|
||||||
|
from collections import namedtuple
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import lsprotocol.types as lsp # type: ignore
|
||||||
|
from pygls.workspace import Workspace
|
||||||
|
|
||||||
|
import lsp_server
|
||||||
|
from lsp_tclserver import TclLanguageServer
|
||||||
|
from tools.def_symbols import ADDRESS, BLOCK_TEMPLATE, FORMAT, parse_def_document
|
||||||
|
|
||||||
|
PSC = """<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Configuration>
|
||||||
|
<Sourcing><Sequence>
|
||||||
|
<Layer Name="Service" SubFolder="service">
|
||||||
|
<DefinedEvents><Filename Name="service"/></DefinedEvents>
|
||||||
|
</Layer>
|
||||||
|
</Sequence></Sourcing>
|
||||||
|
</Configuration>
|
||||||
|
"""
|
||||||
|
|
||||||
|
DEF = """MACHINE Default
|
||||||
|
|
||||||
|
FORMATTING
|
||||||
|
{
|
||||||
|
FORMAT Coordinate "&__4.3_"
|
||||||
|
ADDRESS SPOS
|
||||||
|
{
|
||||||
|
FORMAT Coordinate
|
||||||
|
FORCE off
|
||||||
|
MAX 99999.999 Abort
|
||||||
|
MIN -99999.999 Abort
|
||||||
|
LEADER "SPOS="
|
||||||
|
}
|
||||||
|
# ADDRESS commented_out
|
||||||
|
BLOCK_TEMPLATE steady_rest
|
||||||
|
{
|
||||||
|
SPOS[$mom_pos(0)]
|
||||||
|
Text[M60]\\opt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
TCL = """proc MOM_steady {} {
|
||||||
|
MOM_do_template steady_rest
|
||||||
|
MOM_force Once SPOS X
|
||||||
|
MOM_ask_address_value "SPOS"
|
||||||
|
set name steady_rest
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def _project(tmp_path: Path, monkeypatch):
|
||||||
|
(tmp_path / "service").mkdir()
|
||||||
|
(tmp_path / "post.psc").write_text(PSC, encoding="utf-8")
|
||||||
|
def_file = tmp_path / "service" / "service.def"
|
||||||
|
def_file.write_text(DEF, encoding="utf-8")
|
||||||
|
tcl_file = tmp_path / "caller.tcl"
|
||||||
|
tcl_file.write_text(TCL, encoding="utf-8")
|
||||||
|
|
||||||
|
server = TclLanguageServer(name="def-navigation-test", version="1", max_workers=1)
|
||||||
|
server.protocol._workspace = Workspace( # pylint: disable=protected-access
|
||||||
|
root_uri=tmp_path.as_uri(),
|
||||||
|
sync_kind=lsp.TextDocumentSyncKind.Incremental,
|
||||||
|
workspace_folders=[lsp.WorkspaceFolder(uri=tmp_path.as_uri(), name="root")],
|
||||||
|
position_encoding=lsp.PositionEncodingKind.Utf16,
|
||||||
|
)
|
||||||
|
monkeypatch.setattr(lsp_server, "LSP_SERVER", server)
|
||||||
|
server.refresh_psc_scripts([tmp_path])
|
||||||
|
server.workspace.put_text_document(
|
||||||
|
lsp.TextDocumentItem(uri=tcl_file.as_uri(), language_id="tcl", version=1, text=TCL)
|
||||||
|
)
|
||||||
|
server.update_poco_completion_for_file(server.workspace.get_text_document(tcl_file.as_uri()))
|
||||||
|
return server, def_file, tcl_file
|
||||||
|
|
||||||
|
|
||||||
|
def _position(source: str, needle: str, occurrence: int = 0, offset: int = 1) -> lsp.Position:
|
||||||
|
index = -1
|
||||||
|
for _ in range(occurrence + 1):
|
||||||
|
index = source.index(needle, index + 1)
|
||||||
|
line = source.count("\n", 0, index)
|
||||||
|
column = index - (source.rfind("\n", 0, index) + 1)
|
||||||
|
return lsp.Position(line=line, character=column + offset)
|
||||||
|
|
||||||
|
|
||||||
|
def _tcl_params(tcl_file: Path, needle: str, occurrence: int = 0):
|
||||||
|
return lsp.TextDocumentIdentifier(uri=tcl_file.as_uri()), _position(TCL, needle, occurrence)
|
||||||
|
|
||||||
|
|
||||||
|
# The client sends custom .def requests as plain JSON; pygls exposes them as namedtuples.
|
||||||
|
_Doc = namedtuple("Object", ["uri"])
|
||||||
|
_Pos = namedtuple("Object", ["line", "character"])
|
||||||
|
_Params = namedtuple("Object", ["textDocument", "position", "text", "includeDeclaration", "newName"])
|
||||||
|
|
||||||
|
|
||||||
|
def _def_params(def_file: Path, needle: str, occurrence: int = 0, text: str = DEF, offset: int = 1, **extra):
|
||||||
|
position = _position(text, needle, occurrence, offset)
|
||||||
|
return _Params(
|
||||||
|
_Doc(def_file.as_uri()),
|
||||||
|
_Pos(position.line, position.character),
|
||||||
|
text,
|
||||||
|
extra.get("includeDeclaration", True),
|
||||||
|
extra.get("newName", ""),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _lines(locations):
|
||||||
|
return sorted((Path(location.uri).name, location.range.start.line, location.range.start.character) for location in locations)
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_def_document_declarations_and_references():
|
||||||
|
document = parse_def_document(DEF)
|
||||||
|
kinds = [(item.kind, item.name) for item in document.declarations]
|
||||||
|
assert kinds == [(FORMAT, "Coordinate"), (ADDRESS, "SPOS"), (BLOCK_TEMPLATE, "steady_rest")]
|
||||||
|
address = document.declarations[1]
|
||||||
|
assert (address.line, address.start, address.end) == (5, 12, 16)
|
||||||
|
assert dict(address.properties)["LEADER"] == '"SPOS="'
|
||||||
|
assert [(ref.name, ref.line, ref.container) for ref in document.references] == [
|
||||||
|
("SPOS", 16, "steady_rest"),
|
||||||
|
("Text", 17, "steady_rest"),
|
||||||
|
]
|
||||||
|
assert document.declarations[2].text.splitlines()[-1].strip() == "}"
|
||||||
|
|
||||||
|
|
||||||
|
def test_tcl_goto_definition_of_template_and_address(tmp_path, monkeypatch):
|
||||||
|
_, def_file, tcl_file = _project(tmp_path, monkeypatch)
|
||||||
|
document, position = _tcl_params(tcl_file, "steady_rest")
|
||||||
|
result = lsp_server.goto_definition(lsp.DefinitionParams(text_document=document, position=position))
|
||||||
|
assert _lines(result) == [("service.def", 14, 19)]
|
||||||
|
|
||||||
|
for needle, occurrence in (("SPOS", 0), ("SPOS", 1)):
|
||||||
|
document, position = _tcl_params(tcl_file, needle, occurrence)
|
||||||
|
result = lsp_server.goto_definition(lsp.DefinitionParams(text_document=document, position=position))
|
||||||
|
assert _lines(result) == [("service.def", 5, 12)]
|
||||||
|
|
||||||
|
|
||||||
|
def test_tcl_goto_definition_ignores_plain_words_and_unknown_names(tmp_path, monkeypatch):
|
||||||
|
_, _, tcl_file = _project(tmp_path, monkeypatch)
|
||||||
|
# "set name steady_rest" is no template argument.
|
||||||
|
document, position = _tcl_params(tcl_file, "steady_rest", 1)
|
||||||
|
assert lsp_server.goto_definition(lsp.DefinitionParams(text_document=document, position=position)) is None
|
||||||
|
document, position = lsp.TextDocumentIdentifier(uri=tcl_file.as_uri()), _position(TCL, "SPOS X", offset=5)
|
||||||
|
assert lsp_server.goto_definition(lsp.DefinitionParams(text_document=document, position=position)) is None
|
||||||
|
|
||||||
|
|
||||||
|
def test_tcl_hover_shows_template_body_and_address_properties(tmp_path, monkeypatch):
|
||||||
|
_, _, tcl_file = _project(tmp_path, monkeypatch)
|
||||||
|
document, position = _tcl_params(tcl_file, "steady_rest")
|
||||||
|
hover = lsp_server.hover(lsp.HoverParams(text_document=document, position=position))
|
||||||
|
assert "Block template" in hover.contents.value
|
||||||
|
assert "SPOS[$mom_pos(0)]" in hover.contents.value
|
||||||
|
assert "```def" in hover.contents.value
|
||||||
|
|
||||||
|
document, position = _tcl_params(tcl_file, "SPOS")
|
||||||
|
value = lsp_server.hover(lsp.HoverParams(text_document=document, position=position)).contents.value
|
||||||
|
assert "| Format | `Coordinate` → `\"&__4.3_\"` |" in value
|
||||||
|
assert '| Leader | `"SPOS="` |' in value
|
||||||
|
assert "| Min | `-99999.999 Abort` |" in value
|
||||||
|
assert "| Max | `99999.999 Abort` |" in value
|
||||||
|
assert "| Modality | `off` (modal, output only on change) |" in value
|
||||||
|
|
||||||
|
|
||||||
|
def test_tcl_references_include_def_declaration_and_template_elements(tmp_path, monkeypatch):
|
||||||
|
_, _, tcl_file = _project(tmp_path, monkeypatch)
|
||||||
|
document, position = _tcl_params(tcl_file, "SPOS")
|
||||||
|
result = lsp_server.references(
|
||||||
|
lsp.ReferenceParams(
|
||||||
|
text_document=document,
|
||||||
|
position=position,
|
||||||
|
context=lsp.ReferenceContext(include_declaration=True),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
assert _lines(result) == [
|
||||||
|
("caller.tcl", 2, 19),
|
||||||
|
("caller.tcl", 3, 27),
|
||||||
|
("service.def", 5, 12),
|
||||||
|
("service.def", 16, 8),
|
||||||
|
]
|
||||||
|
result = lsp_server.references(
|
||||||
|
lsp.ReferenceParams(
|
||||||
|
text_document=document,
|
||||||
|
position=position,
|
||||||
|
context=lsp.ReferenceContext(include_declaration=False),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
assert ("service.def", 5, 12) not in _lines(result)
|
||||||
|
|
||||||
|
|
||||||
|
def test_tcl_rename_updates_def_and_tcl(tmp_path, monkeypatch):
|
||||||
|
_, def_file, tcl_file = _project(tmp_path, monkeypatch)
|
||||||
|
document, position = _tcl_params(tcl_file, "SPOS")
|
||||||
|
prepared = lsp_server.prepare_rename(lsp.PrepareRenameParams(text_document=document, position=position))
|
||||||
|
assert prepared.placeholder == "SPOS"
|
||||||
|
|
||||||
|
edit = lsp_server.rename(lsp.RenameParams(text_document=document, position=position, new_name="STEADY_POS"))
|
||||||
|
edits = {Path(uri).name: [(e.range.start.line, e.range.start.character) for e in items] for uri, items in edit.changes.items()}
|
||||||
|
assert edits == {"caller.tcl": [(3, 27), (2, 19)], "service.def": [(16, 8), (5, 12)]}
|
||||||
|
assert all(e.new_text == "STEADY_POS" for items in edit.changes.values() for e in items)
|
||||||
|
|
||||||
|
|
||||||
|
def test_undeclared_names_are_not_renamed(tmp_path, monkeypatch):
|
||||||
|
_, _, tcl_file = _project(tmp_path, monkeypatch)
|
||||||
|
document = lsp.TextDocumentIdentifier(uri=tcl_file.as_uri())
|
||||||
|
position = _position(TCL, "SPOS X", offset=5)
|
||||||
|
assert lsp_server.prepare_rename(lsp.PrepareRenameParams(text_document=document, position=position)) is None
|
||||||
|
|
||||||
|
|
||||||
|
def test_def_requests_resolve_declarations_and_elements(tmp_path, monkeypatch):
|
||||||
|
_, def_file, _ = _project(tmp_path, monkeypatch)
|
||||||
|
# Address element inside a block template -> ADDRESS declaration.
|
||||||
|
result = lsp_server.def_definition(_def_params(def_file, "SPOS[", offset=1))
|
||||||
|
assert _lines(result) == [("service.def", 5, 12)]
|
||||||
|
|
||||||
|
hover = lsp_server.def_hover(_def_params(def_file, "steady_rest"))
|
||||||
|
assert "Text[M60]" in hover.contents.value
|
||||||
|
|
||||||
|
references = lsp_server.def_references(_def_params(def_file, "ADDRESS SPOS", offset=9))
|
||||||
|
assert _lines(references) == [
|
||||||
|
("caller.tcl", 2, 19),
|
||||||
|
("caller.tcl", 3, 27),
|
||||||
|
("service.def", 5, 12),
|
||||||
|
("service.def", 16, 8),
|
||||||
|
]
|
||||||
|
assert lsp_server.def_hover(_def_params(def_file, "MACHINE")) is None
|
||||||
|
|
||||||
|
|
||||||
|
def test_def_requests_use_unsaved_text(tmp_path, monkeypatch):
|
||||||
|
_, def_file, _ = _project(tmp_path, monkeypatch)
|
||||||
|
text = DEF.replace("BLOCK_TEMPLATE steady_rest", "BLOCK_TEMPLATE steady_rest_new")
|
||||||
|
params = _def_params(def_file, "steady_rest_new", text=text, newName="rest")
|
||||||
|
assert lsp_server.def_prepare_rename(params).placeholder == "steady_rest_new"
|
||||||
|
edit = lsp_server.def_rename(params)
|
||||||
|
assert list(edit.changes) == [def_file.as_uri()]
|
||||||
|
|
||||||
|
|
||||||
|
def test_def_rename_updates_tcl_callers(tmp_path, monkeypatch):
|
||||||
|
_, def_file, _ = _project(tmp_path, monkeypatch)
|
||||||
|
edit = lsp_server.def_rename(_def_params(def_file, "steady_rest", newName="lunette"))
|
||||||
|
edits = {Path(uri).name: [(e.range.start.line, e.range.start.character) for e in items] for uri, items in edit.changes.items()}
|
||||||
|
assert edits == {"caller.tcl": [(1, 20)], "service.def": [(14, 19)]}
|
||||||
|
assert lsp_server.def_rename(_def_params(def_file, "steady_rest", newName="bad name")) is None
|
||||||
Reference in New Issue
Block a user