Merge pull request 'Show effective (last-loaded) .def declaration in hover and GoTo' (#50) from enhancements into main
This commit is contained in:
@@ -16,6 +16,10 @@ Versions correspond to the Git tags of this repository.
|
||||
- Warning for block template and address names in NX commands (`MOM_do_template stedy_rest`) that no loaded `.def` file declares; names built at runtime (`$var`, `"CYCLE_$x"`) are not checked, and the warnings update when a `.def` file changes
|
||||
- Completion items for block templates and addresses (`BLOCK_LIST`, `ADDR_LIST`, `MOM_do_template`, ...) show the same preview as the hover
|
||||
|
||||
### Changed
|
||||
|
||||
- When a block template or address is declared in several PSC layers, hover and Go to Definition show only the effective one, the declaration of the last loaded `.def` file, and name the declarations it overrides; Find References and Rename still include all declarations
|
||||
|
||||
### Documentation
|
||||
|
||||
- Document DEF block templates, addresses, `BLOCK_LIST`/`ADDR_LIST`, and the formatting changes in the README
|
||||
|
||||
@@ -55,8 +55,8 @@ from tools.completion_items import (
|
||||
from tools.def_navigation import (
|
||||
all_def_target_locations,
|
||||
def_declarations,
|
||||
def_definition_locations,
|
||||
def_hover_markdown,
|
||||
effective_def_locations,
|
||||
def_rename_edits,
|
||||
def_symbol_at,
|
||||
)
|
||||
@@ -848,12 +848,12 @@ def goto_definition(params: lsp.DefinitionParams):
|
||||
context = _navigation_context(params.text_document.uri, params.position)
|
||||
if context is None:
|
||||
symbol = _tcl_def_symbol(params.text_document.uri, params.position, _word_at(params.text_document.uri, params.position))
|
||||
return symbol and _sorted_locations(def_definition_locations(symbol[0], symbol[1])) or None
|
||||
return symbol and effective_def_locations(symbol[0], symbol[1]) or None
|
||||
|
||||
indexes, definitions, _, identity = context
|
||||
target = _def_target(identity)
|
||||
if target is not None:
|
||||
return _sorted_locations(def_definition_locations(LSP_SERVER.def_documents_snapshot(), target)) or None
|
||||
return effective_def_locations(LSP_SERVER.def_documents_snapshot(), target) or None
|
||||
locations = [lsp.Location(uri=index.uri, range=occurrence.range) for index, occurrence in matching_occurrences(identity, indexes, definitions) if occurrence.is_definition]
|
||||
return _sorted_locations(locations) or None
|
||||
|
||||
@@ -1033,7 +1033,7 @@ def def_definition(params) -> list[lsp.Location] | None:
|
||||
if context is None:
|
||||
return None
|
||||
documents, def_uris, target, _ = context
|
||||
return _sorted_locations(def_definition_locations(documents, target, def_uris)) or None
|
||||
return effective_def_locations(documents, target, def_uris) or None
|
||||
|
||||
|
||||
@LSP_SERVER.feature(DEF_REQUEST_HOVER)
|
||||
|
||||
@@ -79,6 +79,13 @@ def def_definition_locations(
|
||||
]
|
||||
|
||||
|
||||
def effective_def_locations(
|
||||
documents: dict[str, DefDocument], target: DefTarget, uris: dict[str, str] | None = None
|
||||
) -> list[lsp.Location]:
|
||||
"""The declaration NX uses: ``documents`` follow the PSC layer order, the last one wins."""
|
||||
return def_definition_locations(documents, target, uris)[-1:]
|
||||
|
||||
|
||||
def def_reference_locations(
|
||||
documents: dict[str, DefDocument],
|
||||
target: DefTarget,
|
||||
@@ -170,6 +177,7 @@ def _address_table(declaration: DefDeclaration, formats: dict[str, DefDeclaratio
|
||||
|
||||
|
||||
def def_hover_markdown(documents: dict[str, DefDocument], target: DefTarget) -> str | None:
|
||||
"""Describe the effective (last loaded) declaration and name the ones it overrides."""
|
||||
declarations = def_declarations(documents, target)
|
||||
if not declarations:
|
||||
return None
|
||||
@@ -180,12 +188,14 @@ def def_hover_markdown(documents: dict[str, DefDocument], target: DefTarget) ->
|
||||
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)
|
||||
path, declaration = declarations[-1]
|
||||
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```"
|
||||
markdown = f"{header}\n\n{body}"
|
||||
if len(declarations) > 1:
|
||||
overridden = ", ".join(f"{Path(other).name}:{item.line + 1}" for other, item in declarations[:-1])
|
||||
markdown += f"\n\n---\n\n_Overrides {overridden}_"
|
||||
return markdown
|
||||
|
||||
@@ -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"}
|
||||
|
||||
Reference in New Issue
Block a user