feat(server/lsp_server): include .def file changes and offer quoted def/variable completions
This commit is contained in:
@@ -219,21 +219,29 @@ def did_rename_files(params: lsp.RenameFilesParams) -> None:
|
|||||||
def did_change_watched_files(params: lsp.DidChangeWatchedFilesParams) -> None:
|
def did_change_watched_files(params: lsp.DidChangeWatchedFilesParams) -> None:
|
||||||
"""Keep indexes for closed Tcl files synchronized with disk changes."""
|
"""Keep indexes for closed Tcl files synchronized with disk changes."""
|
||||||
for change in params.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()
|
_refresh_psc_index()
|
||||||
continue
|
continue
|
||||||
|
if suffix == ".def":
|
||||||
|
LSP_SERVER.refresh_def_symbols(_workspace_roots(), report=log_warning)
|
||||||
|
continue
|
||||||
if change.type == lsp.FileChangeType.Deleted:
|
if change.type == lsp.FileChangeType.Deleted:
|
||||||
LSP_SERVER.remove_file_state(change.uri)
|
LSP_SERVER.remove_file_state(change.uri)
|
||||||
else:
|
else:
|
||||||
_index_tcl_file_from_disk(change.uri)
|
_index_tcl_file_from_disk(change.uri)
|
||||||
|
|
||||||
|
|
||||||
def _refresh_psc_index():
|
def _workspace_roots() -> list[pathlib.Path]:
|
||||||
folders = LSP_SERVER.workspace.folders
|
folders = LSP_SERVER.workspace.folders
|
||||||
roots = [pathlib.Path(uris.to_fs_path(uri)) for uri in folders]
|
roots = [pathlib.Path(uris.to_fs_path(uri)) for uri in folders]
|
||||||
if not roots and LSP_SERVER.workspace.root_path:
|
if not roots and LSP_SERVER.workspace.root_path:
|
||||||
roots = [pathlib.Path(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(
|
@LSP_SERVER.feature(
|
||||||
@@ -393,6 +401,30 @@ def on_completion(params: lsp.CompletionParams) -> lsp.CompletionList:
|
|||||||
items = [*ranked_completion_items(static_candidates, CompletionContext.GENERAL), *items]
|
items = [*ranked_completion_items(static_candidates, CompletionContext.GENERAL), *items]
|
||||||
return lsp.CompletionList(is_incomplete=False, items=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:
|
if argument_completion.dynamic_kind == DynamicCompletionKind.PROCEDURE:
|
||||||
procedure_kinds = {
|
procedure_kinds = {
|
||||||
lsp.CompletionItemKind.Constructor,
|
lsp.CompletionItemKind.Constructor,
|
||||||
@@ -436,6 +468,35 @@ def on_completion(params: lsp.CompletionParams) -> lsp.CompletionList:
|
|||||||
return lsp.CompletionList(is_incomplete=False, items=items)
|
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_SERVER.feature(
|
||||||
lsp.TEXT_DOCUMENT_SIGNATURE_HELP,
|
lsp.TEXT_DOCUMENT_SIGNATURE_HELP,
|
||||||
lsp.SignatureHelpOptions(
|
lsp.SignatureHelpOptions(
|
||||||
|
|||||||
Reference in New Issue
Block a user