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
|
- 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
|
- 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
|
### Documentation
|
||||||
|
|
||||||
- Document DEF block templates, addresses, `BLOCK_LIST`/`ADDR_LIST`, and the formatting changes in the README
|
- 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 (
|
from tools.def_navigation import (
|
||||||
all_def_target_locations,
|
all_def_target_locations,
|
||||||
def_declarations,
|
def_declarations,
|
||||||
def_definition_locations,
|
|
||||||
def_hover_markdown,
|
def_hover_markdown,
|
||||||
|
effective_def_locations,
|
||||||
def_rename_edits,
|
def_rename_edits,
|
||||||
def_symbol_at,
|
def_symbol_at,
|
||||||
)
|
)
|
||||||
@@ -848,12 +848,12 @@ def goto_definition(params: lsp.DefinitionParams):
|
|||||||
context = _navigation_context(params.text_document.uri, params.position)
|
context = _navigation_context(params.text_document.uri, params.position)
|
||||||
if context is None:
|
if context is None:
|
||||||
symbol = _tcl_def_symbol(params.text_document.uri, params.position, _word_at(params.text_document.uri, params.position))
|
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
|
indexes, definitions, _, identity = context
|
||||||
target = _def_target(identity)
|
target = _def_target(identity)
|
||||||
if target is not None:
|
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]
|
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
|
return _sorted_locations(locations) or None
|
||||||
|
|
||||||
@@ -1033,7 +1033,7 @@ def def_definition(params) -> list[lsp.Location] | None:
|
|||||||
if context is None:
|
if context is None:
|
||||||
return None
|
return None
|
||||||
documents, def_uris, target, _ = context
|
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)
|
@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(
|
def def_reference_locations(
|
||||||
documents: dict[str, DefDocument],
|
documents: dict[str, DefDocument],
|
||||||
target: DefTarget,
|
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:
|
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)
|
declarations = def_declarations(documents, target)
|
||||||
if not declarations:
|
if not declarations:
|
||||||
return None
|
return None
|
||||||
@@ -180,12 +188,14 @@ def def_hover_markdown(documents: dict[str, DefDocument], target: DefTarget) ->
|
|||||||
if declaration.kind == FORMAT
|
if declaration.kind == FORMAT
|
||||||
}
|
}
|
||||||
kind, name = target
|
kind, name = target
|
||||||
sections = []
|
path, declaration = declarations[-1]
|
||||||
for path, declaration in declarations:
|
|
||||||
header = f"**{_KIND_LABELS[kind]}** `{name}` — {Path(path).name}:{declaration.line + 1}"
|
header = f"**{_KIND_LABELS[kind]}** `{name}` — {Path(path).name}:{declaration.line + 1}"
|
||||||
if kind == ADDRESS:
|
if kind == ADDRESS:
|
||||||
body = _address_table(declaration, formats)
|
body = _address_table(declaration, formats)
|
||||||
else:
|
else:
|
||||||
body = f"```def\n{declaration.text}\n```"
|
body = f"```def\n{declaration.text}\n```"
|
||||||
sections.append(f"{header}\n\n{body}")
|
markdown = f"{header}\n\n{body}"
|
||||||
return "\n\n---\n\n".join(sections)
|
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()}
|
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 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
|
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