feat(server/lsp_server): include .def file changes and offer quoted def/variable completions

This commit is contained in:
2026-09-24 07:53:15 +02:00
parent de310a6604
commit 5d23df8da7
+64 -3
View File
@@ -219,21 +219,29 @@ def did_rename_files(params: lsp.RenameFilesParams) -> None:
def did_change_watched_files(params: lsp.DidChangeWatchedFilesParams) -> None:
"""Keep indexes for closed Tcl files synchronized with disk changes."""
for change in params.changes:
if pathlib.Path(uris.to_fs_path(change.uri)).suffix.lower() == ".psc":
suffix = pathlib.Path(uris.to_fs_path(change.uri)).suffix.lower()
if suffix == ".psc":
_refresh_psc_index()
continue
if suffix == ".def":
LSP_SERVER.refresh_def_symbols(_workspace_roots(), report=log_warning)
continue
if change.type == lsp.FileChangeType.Deleted:
LSP_SERVER.remove_file_state(change.uri)
else:
_index_tcl_file_from_disk(change.uri)
def _refresh_psc_index():
def _workspace_roots() -> list[pathlib.Path]:
folders = LSP_SERVER.workspace.folders
roots = [pathlib.Path(uris.to_fs_path(uri)) for uri in folders]
if not roots and LSP_SERVER.workspace.root_path:
roots = [pathlib.Path(LSP_SERVER.workspace.root_path)]
LSP_SERVER.refresh_psc_scripts(roots, report=log_warning)
return roots
def _refresh_psc_index():
LSP_SERVER.refresh_psc_scripts(_workspace_roots(), report=log_warning)
@LSP_SERVER.feature(
@@ -393,6 +401,30 @@ def on_completion(params: lsp.CompletionParams) -> lsp.CompletionList:
items = [*ranked_completion_items(static_candidates, CompletionContext.GENERAL), *items]
return lsp.CompletionList(is_incomplete=False, items=items)
def_symbol_items = {
DynamicCompletionKind.BLOCK_TEMPLATE: LSP_SERVER.block_template_items,
DynamicCompletionKind.ADDRESS: LSP_SERVER.address_items,
DynamicCompletionKind.VALUE: list,
}.get(argument_completion.dynamic_kind)
if def_symbol_items is not None:
# MOM_do_template, MOM_force, ... take a .def name, a fixed value (e.g.
# Always|Once|Off) or a variable holding one.
def_symbols = [*static_candidates, *((0, item) for item in def_symbol_items())]
variable_candidates = list(candidates)
variable_candidates.extend((300, item) for item in standard_items.nx_variables)
variables = []
for item in ranked_completion_items(variable_candidates, CompletionContext.VARIABLE):
# Without a typed "$" the variable must be substituted.
item.insert_text = f"${item.label}"
item.filter_text = item.label
item.text_edit = None
variables.append(item)
quoted = [
_quoted_item(item, source_lines, position, argument_completion.active_prefix)
for item in ranked_completion_items(def_symbols, CompletionContext.GENERAL)
]
return lsp.CompletionList(is_incomplete=False, items=[*quoted, *variables])
if argument_completion.dynamic_kind == DynamicCompletionKind.PROCEDURE:
procedure_kinds = {
lsp.CompletionItemKind.Constructor,
@@ -436,6 +468,35 @@ def on_completion(params: lsp.CompletionParams) -> lsp.CompletionList:
return lsp.CompletionList(is_incomplete=False, items=items)
def _quoted_item(
item: lsp.CompletionItem,
source_lines,
position: lsp.Position,
active_prefix: str,
) -> lsp.CompletionItem:
"""Insert ``item`` as a quoted word, replacing quotes the user already typed."""
line = source_lines[position.line] if position.line < len(source_lines) else ""
utf16 = line.encode("utf-16-le")
start = position.character - len(active_prefix.encode("utf-16-le")) // 2
end = position.character
opened = start > 0 and utf16[(start - 1) * 2 : start * 2].decode("utf-16-le") == '"'
if opened:
start -= 1
# Also replace a closing quote the editor inserted automatically.
if utf16[end * 2 : (end + 1) * 2].decode("utf-16-le") == '"':
end += 1
item.text_edit = lsp.TextEdit(
range=lsp.Range(
start=lsp.Position(line=position.line, character=start),
end=lsp.Position(line=position.line, character=end),
),
new_text=f'"{item.label}"',
)
item.insert_text = None
item.filter_text = f'"{item.label}' if opened else item.label
return item
@LSP_SERVER.feature(
lsp.TEXT_DOCUMENT_SIGNATURE_HELP,
lsp.SignatureHelpOptions(