feat(def-navigation): surface last-loaded .def declaration for hover/GoTo

When a block template or address is declared in multiple PSC layers, hover and
Go to Definition now resolve to the effective declaration (the one from the
last-loaded .def file). Find References and Rename still include all
declarations.

- add effective_def_locations(documents, target, uris) helper (returns the last declaration)
- use effective_def_locations in lsp_server for goto/definition endpoints
- change def_hover_markdown to show the effective declaration and append a short
  "_Overrides ..._" note listing overridden declarations
- update changelog and add a test that verifies hover/GoTo reference the last
  loaded declaration while references include all declarations
This commit is contained in:
2026-09-25 21:38:06 +02:00
parent 525a08c289
commit c8466d2e32
4 changed files with 74 additions and 13 deletions
@@ -240,3 +240,50 @@ def test_def_rename_updates_tcl_callers(tmp_path, monkeypatch):
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
LAYERED_PSC = """<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
<Sourcing><Sequence>
<Layer Name="Controller" SubFolder="ctrl"><DefinedEvents><Filename Name="ctrl"/></DefinedEvents></Layer>
<Layer Name="OEM" SubFolder="oem"><DefinedEvents><Filename Name="oem"/></DefinedEvents></Layer>
</Sequence></Sourcing>
</Configuration>
"""
def test_template_in_several_layers_shows_the_last_loaded_one(tmp_path, monkeypatch):
for folder, text in (("ctrl", "Text[M17]"), ("oem", "Text[RET]")):
(tmp_path / folder).mkdir()
(tmp_path / folder / f"{folder}.def").write_text(
f"MACHINE X\n\nFORMATTING\n{{\n BLOCK_TEMPLATE end_of_subprogram\n {{\n {text}\n }}\n}}\n", encoding="utf-8"
)
(tmp_path / "post.psc").write_text(LAYERED_PSC, encoding="utf-8")
source = "MOM_do_template end_of_subprogram\n"
caller = tmp_path / "caller.tcl"
caller.write_text(source, encoding="utf-8")
server = TclLanguageServer(name="def-layer-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=caller.as_uri(), language_id="tcl", version=1, text=source))
server.update_poco_completion_for_file(server.workspace.get_text_document(caller.as_uri()))
document = lsp.TextDocumentIdentifier(uri=caller.as_uri())
position = lsp.Position(line=0, character=20)
hover = lsp_server.hover(lsp.HoverParams(text_document=document, position=position)).contents.value
assert hover.startswith("**Block template** `end_of_subprogram` — oem.def:5")
assert "Text[RET]" in hover and "Text[M17]" not in hover
assert hover.endswith("_Overrides ctrl.def:5_")
[location] = lsp_server.goto_definition(lsp.DefinitionParams(text_document=document, position=position))
assert location.uri.endswith("oem/oem.def")
references = lsp_server.references(
lsp.ReferenceParams(text_document=document, position=position, context=lsp.ReferenceContext(include_declaration=True))
)
assert {Path(location.uri).name for location in references} == {"ctrl.def", "oem.def", "caller.tcl"}