completions: Complete available namespaces

Add completions for namespaces in the typelib path that can be imported.
Accepting the completion automatically adds an import statement.
This commit is contained in:
James Westman 2025-01-04 16:05:27 -06:00
parent bf4d8579b6
commit 3d0593bc2b
No known key found for this signature in database
GPG key ID: CE2DBA0ADB654EA6
3 changed files with 37 additions and 7 deletions

View file

@ -23,7 +23,7 @@ from . import annotations, gir, language
from .ast_utils import AstNode
from .completions_utils import *
from .language.types import ClassName
from .lsp_utils import Completion, CompletionItemKind
from .lsp_utils import Completion, CompletionItemKind, TextEdit
from .parser import SKIP_TOKENS
from .tokenizer import Token, TokenType
@ -96,14 +96,32 @@ def using_gtk(_ctx: CompletionContext):
)
def namespace(ctx: CompletionContext):
yield Completion("Gtk", CompletionItemKind.Module, text="Gtk.")
imported_namespaces = set(["Gtk"])
for ns in ctx.ast_node.root.children[language.Import]:
if ns.gir_namespace is not None:
imported_namespaces.add(ns.gir_namespace.name)
yield Completion(
ns.gir_namespace.name,
CompletionItemKind.Module,
text=ns.gir_namespace.name + ".",
)
for ns, version in gir.get_available_namespaces():
if ns not in imported_namespaces:
yield Completion(
ns,
CompletionItemKind.Module,
text=ns + ".",
signature=f" using {ns} {version}",
additional_text_edits=[
TextEdit(
ctx.ast_node.root.import_range(ns), f"\nusing {ns} {version};"
)
],
)
@completer(
applies_in=[language.UI, language.ObjectContent, language.Template],

View file

@ -110,16 +110,22 @@ class UI(AstNode):
and self.template.class_name.glib_type_name == id
)
def import_code_action(self, ns: str, version: str) -> CodeAction:
if len(self.children[Import]):
pos = self.children[Import][-1].range.end
else:
pos = self.children[GtkDirective][0].range.end
def import_range(self, ns: str):
"""Returns a range to insert a new import statement"""
pos = self.children[GtkDirective][0].range.end
# try to insert alphabetically
for import_ in self.children[Import]:
if ns.lower() > import_.namespace.lower():
pos = import_.range.end
return Range(pos, pos, self.group.text)
def import_code_action(self, ns: str, version: str) -> CodeAction:
return CodeAction(
f"Import {ns} {version}",
f"\nusing {ns} {version};",
Range(pos, pos, self.group.text),
self.import_range(ns),
)
@cached_property

View file

@ -87,6 +87,7 @@ class Completion:
text: T.Optional[str] = None
snippet: T.Optional[str] = None
detail: T.Optional[str] = None
additional_text_edits: T.Optional[T.List["TextEdit"]] = None
def to_json(self, snippets: bool):
insert_text = self.text or self.label
@ -114,6 +115,11 @@ class Completion:
"insertText": insert_text,
"insertTextFormat": insert_text_format,
"detail": self.detail if self.detail else None,
"additionalTextEdits": (
[edit.to_json() for edit in self.additional_text_edits]
if self.additional_text_edits
else None
),
}
return {k: v for k, v in result.items() if v is not None}