From a075b26769e6f6c2c0aed6e62320f91655dd59fd Mon Sep 17 00:00:00 2001 From: Sonny Piers Date: Sun, 16 Jun 2024 14:18:08 +0200 Subject: [PATCH] lsp: Extend completion documentation --- blueprintcompiler/completions.py | 37 +++++++++++++++++++++++++------- blueprintcompiler/gir.py | 9 ++++++++ blueprintcompiler/lsp_utils.py | 5 ++++- 3 files changed, 42 insertions(+), 9 deletions(-) diff --git a/blueprintcompiler/completions.py b/blueprintcompiler/completions.py index 0a43388..cfe5c51 100644 --- a/blueprintcompiler/completions.py +++ b/blueprintcompiler/completions.py @@ -17,6 +17,7 @@ # # SPDX-License-Identifier: LGPL-3.0-or-later +import sys import typing as T from . import gir, language @@ -30,6 +31,9 @@ from .tokenizer import Token, TokenType Pattern = T.List[T.Tuple[TokenType, T.Optional[str]]] +def debug(*args, **kwargs): + print(*args, file=sys.stderr, **kwargs) + def _complete( lsp, ast_node: AstNode, tokens: T.List[Token], idx: int, token_idx: int ) -> T.Iterator[Completion]: @@ -72,7 +76,9 @@ def complete( @completer([language.GtkDirective]) def using_gtk(lsp, ast_node, match_variables): - yield Completion("using Gtk 4.0;", CompletionItemKind.Keyword) + yield Completion( + "using Gtk 4.0", CompletionItemKind.Keyword, snippet="using Gtk 4.0;\n" + ) @completer( @@ -101,7 +107,7 @@ def object_completer(lsp, ast_node, match_variables): ns = ast_node.root.gir.namespaces.get(match_variables[0]) if ns is not None: for c in ns.classes.values(): - yield Completion(c.name, CompletionItemKind.Class, docs=c.doc) + yield Completion(c.name, CompletionItemKind.Class, docs=c.doc, detail=c.detail) @completer( @@ -112,7 +118,7 @@ def gtk_object_completer(lsp, ast_node, match_variables): ns = ast_node.root.gir.namespaces.get("Gtk") if ns is not None: for c in ns.classes.values(): - yield Completion(c.name, CompletionItemKind.Class, docs=c.doc) + yield Completion(c.name, CompletionItemKind.Class, docs=c.doc, detail=c.detail) @completer( @@ -131,6 +137,8 @@ def property_completer(lsp, ast_node, match_variables): CompletionItemKind.Property, sort_text=f"0 {prop_name}", snippet=f"{prop_name}: ${{1|true,false|}};", + docs=prop.doc, + detail=prop.detail, ) elif isinstance(prop.type, gir.StringType): yield Completion( @@ -138,6 +146,8 @@ def property_completer(lsp, ast_node, match_variables): CompletionItemKind.Property, sort_text=f"0 {prop_name}", snippet=f'{prop_name}: "$0";', + docs=prop.doc, + detail=prop.detail, ) elif ( isinstance(prop.type, gir.Enumeration) @@ -150,6 +160,8 @@ def property_completer(lsp, ast_node, match_variables): CompletionItemKind.Property, sort_text=f"0 {prop_name}", snippet=f"{prop_name}: ${{1|{choices}|}};", + docs=prop.doc, + detail=prop.detail, ) else: yield Completion( @@ -157,6 +169,8 @@ def property_completer(lsp, ast_node, match_variables): CompletionItemKind.Property, sort_text=f"0 {prop_name}", snippet=f"{prop_name}: $0;", + docs=prop.doc, + detail=prop.detail, ) @@ -168,7 +182,12 @@ def prop_value_completer(lsp, ast_node, match_variables): if (vt := ast_node.value_type) is not None: if isinstance(vt.value_type, gir.Enumeration): for name, member in vt.value_type.members.items(): - yield Completion(name, CompletionItemKind.EnumMember, docs=member.doc) + yield Completion( + name, + CompletionItemKind.EnumMember, + docs=member.doc, + detail=member.detail, + ) elif isinstance(vt.value_type, gir.BoolType): yield Completion("true", CompletionItemKind.Constant) @@ -181,7 +200,7 @@ def prop_value_completer(lsp, ast_node, match_variables): ) def signal_completer(lsp, ast_node, match_variables): if ast_node.gir_class and not isinstance(ast_node.gir_class, gir.ExternType): - for signal in ast_node.gir_class.signals: + for signal_name, signal in ast_node.gir_class.signals.items(): if not isinstance(ast_node.parent, language.Object): name = "on" else: @@ -192,10 +211,12 @@ def signal_completer(lsp, ast_node, match_variables): .lower() ) yield Completion( - signal, + signal_name, CompletionItemKind.Event, - sort_text=f"1 {signal}", - snippet=f"{signal} => \$${{1:{name}_{signal.replace('-', '_')}}}()$0;", + sort_text=f"1 {signal_name}", + snippet=f"{signal_name} => \$${{1:{name}_{signal_name.replace('-', '_')}}}()$0;", + docs=signal.doc, + detail=signal.detail, ) diff --git a/blueprintcompiler/gir.py b/blueprintcompiler/gir.py index 35be84e..f569f0a 100644 --- a/blueprintcompiler/gir.py +++ b/blueprintcompiler/gir.py @@ -336,6 +336,15 @@ class GirNode: def available_in(self) -> str: return self.xml.get("version") + @cached_property + def detail(self) -> T.Optional[str]: + try: + el = self.xml.get_elements("doc") + if len(el) == 1: + return el[0].cdata.strip().partition("\n")[0] + except: + return None + @cached_property def doc(self) -> T.Optional[str]: sections = [] diff --git a/blueprintcompiler/lsp_utils.py b/blueprintcompiler/lsp_utils.py index 34b0949..9362e8c 100644 --- a/blueprintcompiler/lsp_utils.py +++ b/blueprintcompiler/lsp_utils.py @@ -84,6 +84,7 @@ class Completion: docs: T.Optional[str] = None text: T.Optional[str] = None snippet: T.Optional[str] = None + detail: T.Optional[str] = None def to_json(self, snippets: bool): insert_text = self.text or self.label @@ -96,7 +97,8 @@ class Completion: "label": self.label, "kind": self.kind, "tags": [CompletionItemTag.Deprecated] if self.deprecated else None, - "detail": self.signature, + # https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#completionItemLabelDetails + "labelDetails": ({"detail": self.signature} if self.signature else None), "documentation": ( { "kind": "markdown", @@ -109,6 +111,7 @@ class Completion: "sortText": self.sort_text, "insertText": insert_text, "insertTextFormat": insert_text_format, + "detail": self.detail if self.detail else None, } return {k: v for k, v in result.items() if v is not None}