mirror of
https://gitlab.gnome.org/jwestman/blueprint-compiler.git
synced 2025-05-04 15:59:08 -04:00
Compare commits
8 commits
3ba85d1e47
...
72583be267
Author | SHA1 | Date | |
---|---|---|---|
|
72583be267 | ||
|
bcb8e4ea14 | ||
|
3cdc3a159c | ||
|
2204eaf92d | ||
|
533a49acd7 | ||
|
21f138fa83 | ||
|
461fe25316 | ||
|
29e4a56bfc |
19 changed files with 454 additions and 148 deletions
|
@ -23,7 +23,7 @@ from . import annotations, gir, language
|
||||||
from .ast_utils import AstNode
|
from .ast_utils import AstNode
|
||||||
from .completions_utils import *
|
from .completions_utils import *
|
||||||
from .language.types import ClassName
|
from .language.types import ClassName
|
||||||
from .lsp_utils import Completion, CompletionItemKind
|
from .lsp_utils import Completion, CompletionItemKind, TextEdit, get_docs_section
|
||||||
from .parser import SKIP_TOKENS
|
from .parser import SKIP_TOKENS
|
||||||
from .tokenizer import Token, TokenType
|
from .tokenizer import Token, TokenType
|
||||||
|
|
||||||
|
@ -31,13 +31,18 @@ Pattern = T.List[T.Tuple[TokenType, T.Optional[str]]]
|
||||||
|
|
||||||
|
|
||||||
def _complete(
|
def _complete(
|
||||||
lsp, ast_node: AstNode, tokens: T.List[Token], idx: int, token_idx: int
|
lsp,
|
||||||
|
ast_node: AstNode,
|
||||||
|
tokens: T.List[Token],
|
||||||
|
idx: int,
|
||||||
|
token_idx: int,
|
||||||
|
next_token: Token,
|
||||||
) -> T.Iterator[Completion]:
|
) -> T.Iterator[Completion]:
|
||||||
for child in ast_node.children:
|
for child in ast_node.children:
|
||||||
if child.group.start <= idx and (
|
if child.group.start <= idx and (
|
||||||
idx < child.group.end or (idx == child.group.end and child.incomplete)
|
idx < child.group.end or (idx == child.group.end and child.incomplete)
|
||||||
):
|
):
|
||||||
yield from _complete(lsp, child, tokens, idx, token_idx)
|
yield from _complete(lsp, child, tokens, idx, token_idx, next_token)
|
||||||
return
|
return
|
||||||
|
|
||||||
prev_tokens: T.List[Token] = []
|
prev_tokens: T.List[Token] = []
|
||||||
|
@ -50,7 +55,7 @@ def _complete(
|
||||||
token_idx -= 1
|
token_idx -= 1
|
||||||
|
|
||||||
for completer in ast_node.completers:
|
for completer in ast_node.completers:
|
||||||
yield from completer(prev_tokens, ast_node, lsp)
|
yield from completer(prev_tokens, next_token, ast_node, lsp, idx)
|
||||||
|
|
||||||
|
|
||||||
def complete(
|
def complete(
|
||||||
|
@ -62,35 +67,117 @@ def complete(
|
||||||
if token.start < idx <= token.end:
|
if token.start < idx <= token.end:
|
||||||
token_idx = i
|
token_idx = i
|
||||||
|
|
||||||
|
if tokens[token_idx].type == TokenType.EOF:
|
||||||
|
next_token = tokens[token_idx]
|
||||||
|
else:
|
||||||
|
next_token_idx = token_idx + 1
|
||||||
|
while tokens[next_token_idx].type == TokenType.WHITESPACE:
|
||||||
|
next_token_idx += 1
|
||||||
|
next_token = tokens[next_token_idx]
|
||||||
|
|
||||||
# if the current token is an identifier or whitespace, move to the token before it
|
# if the current token is an identifier or whitespace, move to the token before it
|
||||||
while tokens[token_idx].type in [TokenType.IDENT, TokenType.WHITESPACE]:
|
while tokens[token_idx].type in [TokenType.IDENT, TokenType.WHITESPACE]:
|
||||||
idx = tokens[token_idx].start
|
idx = tokens[token_idx].start
|
||||||
token_idx -= 1
|
token_idx -= 1
|
||||||
|
|
||||||
yield from _complete(lsp, ast_node, tokens, idx, token_idx)
|
yield from _complete(lsp, ast_node, tokens, idx, token_idx, next_token)
|
||||||
|
|
||||||
|
|
||||||
@completer([language.GtkDirective])
|
@completer([language.GtkDirective])
|
||||||
def using_gtk(lsp, ast_node, match_variables):
|
def using_gtk(_ctx: CompletionContext):
|
||||||
yield Completion(
|
yield Completion(
|
||||||
"using Gtk 4.0", CompletionItemKind.Keyword, snippet="using Gtk 4.0;\n"
|
"using Gtk 4.0", CompletionItemKind.Keyword, snippet="using Gtk 4.0;\n"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@completer([language.UI])
|
||||||
|
def using(ctx: CompletionContext):
|
||||||
|
imported_namespaces = set(
|
||||||
|
[import_.namespace for import_ in ctx.ast_node.root.using]
|
||||||
|
)
|
||||||
|
|
||||||
|
# Import statements must be before any content
|
||||||
|
for i in ctx.ast_node.root.children:
|
||||||
|
if not isinstance(i, language.GtkDirective) and not isinstance(
|
||||||
|
i, language.Import
|
||||||
|
):
|
||||||
|
if ctx.index >= i.range.end:
|
||||||
|
return
|
||||||
|
|
||||||
|
for ns, version in gir.get_available_namespaces():
|
||||||
|
if ns not in imported_namespaces and ns != "Gtk":
|
||||||
|
yield Completion(
|
||||||
|
f"using {ns} {version}",
|
||||||
|
CompletionItemKind.Module,
|
||||||
|
text=f"using {ns} {version};",
|
||||||
|
sort_text=get_sort_key(CompletionPriority.NAMESPACE, ns),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@completer([language.UI])
|
||||||
|
def translation_domain(ctx: CompletionContext):
|
||||||
|
if ctx.ast_node.root.translation_domain is not None:
|
||||||
|
return
|
||||||
|
|
||||||
|
# Translation domain must be after the import statements but before any content
|
||||||
|
for i in ctx.ast_node.root.children:
|
||||||
|
if isinstance(i, language.Import):
|
||||||
|
if ctx.index <= i.range.start:
|
||||||
|
return
|
||||||
|
elif not isinstance(i, language.GtkDirective):
|
||||||
|
if ctx.index >= i.range.end:
|
||||||
|
return
|
||||||
|
|
||||||
|
yield Completion(
|
||||||
|
"translation-domain",
|
||||||
|
CompletionItemKind.Keyword,
|
||||||
|
sort_text=get_sort_key(CompletionPriority.KEYWORD, "translation-domain"),
|
||||||
|
snippet='translation-domain "$0";',
|
||||||
|
docs=get_docs_section("Syntax TranslationDomain"),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _available_namespace_completions(ctx: CompletionContext):
|
||||||
|
imported_namespaces = set(
|
||||||
|
[import_.namespace for import_ in ctx.ast_node.root.using]
|
||||||
|
)
|
||||||
|
|
||||||
|
for ns, version in gir.get_available_namespaces():
|
||||||
|
if ns not in imported_namespaces and ns != "Gtk":
|
||||||
|
yield Completion(
|
||||||
|
ns,
|
||||||
|
CompletionItemKind.Module,
|
||||||
|
text=ns + ".",
|
||||||
|
sort_text=get_sort_key(CompletionPriority.IMPORT_NAMESPACE, ns),
|
||||||
|
signature=f" using {ns} {version}",
|
||||||
|
additional_text_edits=[
|
||||||
|
TextEdit(
|
||||||
|
ctx.ast_node.root.import_range(ns), f"\nusing {ns} {version};"
|
||||||
|
)
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@completer(
|
@completer(
|
||||||
applies_in=[language.UI, language.ObjectContent, language.Template],
|
applies_in=[language.UI, language.ObjectContent, language.Template],
|
||||||
matches=new_statement_patterns,
|
matches=new_statement_patterns,
|
||||||
)
|
)
|
||||||
def namespace(lsp, ast_node, match_variables):
|
def namespace(ctx: CompletionContext):
|
||||||
yield Completion("Gtk", CompletionItemKind.Module, text="Gtk.")
|
yield Completion("Gtk", CompletionItemKind.Module, text="Gtk.")
|
||||||
for ns in ast_node.root.children[language.Import]:
|
|
||||||
|
for ns in ctx.ast_node.root.children[language.Import]:
|
||||||
if ns.gir_namespace is not None:
|
if ns.gir_namespace is not None:
|
||||||
yield Completion(
|
yield Completion(
|
||||||
ns.gir_namespace.name,
|
ns.gir_namespace.name,
|
||||||
CompletionItemKind.Module,
|
CompletionItemKind.Module,
|
||||||
text=ns.gir_namespace.name + ".",
|
text=ns.gir_namespace.name + ".",
|
||||||
|
sort_text=get_sort_key(
|
||||||
|
CompletionPriority.NAMESPACE, ns.gir_namespace.name
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
yield from _available_namespace_completions(ctx)
|
||||||
|
|
||||||
|
|
||||||
@completer(
|
@completer(
|
||||||
applies_in=[language.UI, language.ObjectContent, language.Template],
|
applies_in=[language.UI, language.ObjectContent, language.Template],
|
||||||
|
@ -99,14 +186,19 @@ def namespace(lsp, ast_node, match_variables):
|
||||||
[(TokenType.IDENT, None), (TokenType.OP, ".")],
|
[(TokenType.IDENT, None), (TokenType.OP, ".")],
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
def object_completer(lsp, ast_node, match_variables):
|
def object_completer(ctx: CompletionContext):
|
||||||
ns = ast_node.root.gir.namespaces.get(match_variables[0])
|
ns = ctx.ast_node.root.gir.namespaces.get(ctx.match_variables[0])
|
||||||
if ns is not None:
|
if ns is not None:
|
||||||
for c in ns.classes.values():
|
for c in ns.classes.values():
|
||||||
|
snippet = c.name
|
||||||
|
if str(ctx.next_token) != "{":
|
||||||
|
snippet += " {\n $0\n}"
|
||||||
|
|
||||||
yield Completion(
|
yield Completion(
|
||||||
c.name,
|
c.name,
|
||||||
CompletionItemKind.Class,
|
CompletionItemKind.Class,
|
||||||
snippet=f"{c.name} {{\n $0\n}}",
|
sort_text=get_sort_key(CompletionPriority.CLASS, c.name),
|
||||||
|
snippet=snippet,
|
||||||
docs=c.doc,
|
docs=c.doc,
|
||||||
detail=c.detail,
|
detail=c.detail,
|
||||||
)
|
)
|
||||||
|
@ -116,14 +208,19 @@ def object_completer(lsp, ast_node, match_variables):
|
||||||
applies_in=[language.UI, language.ObjectContent, language.Template],
|
applies_in=[language.UI, language.ObjectContent, language.Template],
|
||||||
matches=new_statement_patterns,
|
matches=new_statement_patterns,
|
||||||
)
|
)
|
||||||
def gtk_object_completer(lsp, ast_node, match_variables):
|
def gtk_object_completer(ctx: CompletionContext):
|
||||||
ns = ast_node.root.gir.namespaces.get("Gtk")
|
ns = ctx.ast_node.root.gir.namespaces.get("Gtk")
|
||||||
if ns is not None:
|
if ns is not None:
|
||||||
for c in ns.classes.values():
|
for c in ns.classes.values():
|
||||||
|
snippet = c.name
|
||||||
|
if str(ctx.next_token) != "{":
|
||||||
|
snippet += " {\n $0\n}"
|
||||||
|
|
||||||
yield Completion(
|
yield Completion(
|
||||||
c.name,
|
c.name,
|
||||||
CompletionItemKind.Class,
|
CompletionItemKind.Class,
|
||||||
snippet=f"{c.name} {{\n $0\n}}",
|
sort_text=get_sort_key(CompletionPriority.CLASS, c.name),
|
||||||
|
snippet=snippet,
|
||||||
docs=c.doc,
|
docs=c.doc,
|
||||||
detail=c.detail,
|
detail=c.detail,
|
||||||
)
|
)
|
||||||
|
@ -133,76 +230,38 @@ def gtk_object_completer(lsp, ast_node, match_variables):
|
||||||
applies_in=[language.ObjectContent],
|
applies_in=[language.ObjectContent],
|
||||||
matches=new_statement_patterns,
|
matches=new_statement_patterns,
|
||||||
)
|
)
|
||||||
def property_completer(lsp, ast_node, match_variables):
|
def property_completer(ctx: CompletionContext):
|
||||||
if ast_node.gir_class and hasattr(ast_node.gir_class, "properties"):
|
assert isinstance(ctx.ast_node, language.ObjectContent)
|
||||||
for prop_name, prop in ast_node.gir_class.properties.items():
|
if ctx.ast_node.gir_class and hasattr(ctx.ast_node.gir_class, "properties"):
|
||||||
if (
|
for prop_name, prop in ctx.ast_node.gir_class.properties.items():
|
||||||
isinstance(prop.type, gir.BoolType)
|
yield get_property_completion(
|
||||||
and lsp.client_supports_completion_choice
|
prop_name,
|
||||||
):
|
prop,
|
||||||
yield Completion(
|
ctx,
|
||||||
prop_name,
|
annotations.is_property_translated(prop),
|
||||||
CompletionItemKind.Property,
|
prop.doc,
|
||||||
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):
|
|
||||||
snippet = (
|
|
||||||
f'{prop_name}: _("$0");'
|
|
||||||
if annotations.is_property_translated(prop)
|
|
||||||
else f'{prop_name}: "$0";'
|
|
||||||
)
|
|
||||||
|
|
||||||
yield Completion(
|
|
||||||
prop_name,
|
|
||||||
CompletionItemKind.Property,
|
|
||||||
sort_text=f"0 {prop_name}",
|
|
||||||
snippet=snippet,
|
|
||||||
docs=prop.doc,
|
|
||||||
detail=prop.detail,
|
|
||||||
)
|
|
||||||
elif (
|
|
||||||
isinstance(prop.type, gir.Enumeration)
|
|
||||||
and len(prop.type.members) <= 10
|
|
||||||
and lsp.client_supports_completion_choice
|
|
||||||
):
|
|
||||||
choices = ",".join(prop.type.members.keys())
|
|
||||||
yield Completion(
|
|
||||||
prop_name,
|
|
||||||
CompletionItemKind.Property,
|
|
||||||
sort_text=f"0 {prop_name}",
|
|
||||||
snippet=f"{prop_name}: ${{1|{choices}|}};",
|
|
||||||
docs=prop.doc,
|
|
||||||
detail=prop.detail,
|
|
||||||
)
|
|
||||||
elif prop.type.full_name == "Gtk.Expression":
|
|
||||||
yield Completion(
|
|
||||||
prop_name,
|
|
||||||
CompletionItemKind.Property,
|
|
||||||
sort_text=f"0 {prop_name}",
|
|
||||||
snippet=f"{prop_name}: expr $0;",
|
|
||||||
docs=prop.doc,
|
|
||||||
detail=prop.detail,
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
yield Completion(
|
|
||||||
prop_name,
|
|
||||||
CompletionItemKind.Property,
|
|
||||||
sort_text=f"0 {prop_name}",
|
|
||||||
snippet=f"{prop_name}: $0;",
|
|
||||||
docs=prop.doc,
|
|
||||||
detail=prop.detail,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@completer(
|
@completer(
|
||||||
applies_in=[language.Property, language.A11yProperty],
|
applies_in=[language.Property, language.A11yProperty],
|
||||||
matches=[[(TokenType.IDENT, None), (TokenType.OP, ":")]],
|
matches=[[(TokenType.IDENT, None), (TokenType.OP, ":")]],
|
||||||
)
|
)
|
||||||
def prop_value_completer(lsp, ast_node, match_variables):
|
def prop_value_completer(ctx: CompletionContext):
|
||||||
if (vt := ast_node.value_type) is not None:
|
if isinstance(ctx.ast_node, language.Property):
|
||||||
|
yield Completion(
|
||||||
|
"bind",
|
||||||
|
CompletionItemKind.Keyword,
|
||||||
|
snippet="bind $0",
|
||||||
|
docs=get_docs_section("Syntax Binding"),
|
||||||
|
sort_text=get_sort_key(CompletionPriority.KEYWORD, "bind"),
|
||||||
|
)
|
||||||
|
|
||||||
|
assert isinstance(ctx.ast_node, language.Property) or isinstance(
|
||||||
|
ctx.ast_node, language.A11yProperty
|
||||||
|
)
|
||||||
|
|
||||||
|
if (vt := ctx.ast_node.value_type) is not None:
|
||||||
if isinstance(vt.value_type, gir.Enumeration):
|
if isinstance(vt.value_type, gir.Enumeration):
|
||||||
for name, member in vt.value_type.members.items():
|
for name, member in vt.value_type.members.items():
|
||||||
yield Completion(
|
yield Completion(
|
||||||
|
@ -210,41 +269,99 @@ def prop_value_completer(lsp, ast_node, match_variables):
|
||||||
CompletionItemKind.EnumMember,
|
CompletionItemKind.EnumMember,
|
||||||
docs=member.doc,
|
docs=member.doc,
|
||||||
detail=member.detail,
|
detail=member.detail,
|
||||||
|
sort_text=get_sort_key(CompletionPriority.ENUM_MEMBER, name),
|
||||||
)
|
)
|
||||||
|
|
||||||
elif isinstance(vt.value_type, gir.BoolType):
|
elif isinstance(vt.value_type, gir.BoolType):
|
||||||
yield Completion("true", CompletionItemKind.Constant)
|
yield Completion(
|
||||||
yield Completion("false", CompletionItemKind.Constant)
|
"true",
|
||||||
|
CompletionItemKind.Constant,
|
||||||
|
sort_text=get_sort_key(CompletionPriority.ENUM_MEMBER, "true"),
|
||||||
|
)
|
||||||
|
yield Completion(
|
||||||
|
"false",
|
||||||
|
CompletionItemKind.Constant,
|
||||||
|
sort_text=get_sort_key(CompletionPriority.ENUM_MEMBER, "false"),
|
||||||
|
)
|
||||||
|
|
||||||
|
elif isinstance(vt.value_type, gir.Class) or isinstance(
|
||||||
|
vt.value_type, gir.Interface
|
||||||
|
):
|
||||||
|
yield Completion(
|
||||||
|
"null",
|
||||||
|
CompletionItemKind.Constant,
|
||||||
|
sort_text=get_sort_key(CompletionPriority.KEYWORD, "null"),
|
||||||
|
)
|
||||||
|
|
||||||
|
for id, obj in ctx.ast_node.root.context[language.ScopeCtx].objects.items():
|
||||||
|
if obj.gir_class is not None and obj.gir_class.assignable_to(
|
||||||
|
vt.value_type
|
||||||
|
):
|
||||||
|
yield Completion(
|
||||||
|
id,
|
||||||
|
CompletionItemKind.Variable,
|
||||||
|
signature=" " + obj.signature,
|
||||||
|
sort_text=get_sort_key(CompletionPriority.NAMED_OBJECT, id),
|
||||||
|
)
|
||||||
|
|
||||||
|
if isinstance(ctx.ast_node, language.Property):
|
||||||
|
yield from _available_namespace_completions(ctx)
|
||||||
|
|
||||||
|
for ns in ctx.ast_node.root.gir.namespaces.values():
|
||||||
|
for c in ns.classes.values():
|
||||||
|
if not c.abstract and c.assignable_to(vt.value_type):
|
||||||
|
name = (
|
||||||
|
c.name if ns.name == "Gtk" else ns.name + "." + c.name
|
||||||
|
)
|
||||||
|
snippet = name
|
||||||
|
if str(ctx.next_token) != "{":
|
||||||
|
snippet += " {\n $0\n}"
|
||||||
|
yield Completion(
|
||||||
|
name,
|
||||||
|
CompletionItemKind.Class,
|
||||||
|
sort_text=get_sort_key(CompletionPriority.CLASS, name),
|
||||||
|
snippet=snippet,
|
||||||
|
detail=c.detail,
|
||||||
|
docs=c.doc,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@completer(
|
@completer(
|
||||||
applies_in=[language.ObjectContent],
|
applies_in=[language.ObjectContent],
|
||||||
matches=new_statement_patterns,
|
matches=new_statement_patterns,
|
||||||
)
|
)
|
||||||
def signal_completer(lsp, ast_node, match_variables):
|
def signal_completer(ctx: CompletionContext):
|
||||||
if ast_node.gir_class and hasattr(ast_node.gir_class, "signals"):
|
assert isinstance(ctx.ast_node, language.ObjectContent)
|
||||||
for signal_name, signal in ast_node.gir_class.signals.items():
|
|
||||||
if not isinstance(ast_node.parent, language.Object):
|
if ctx.ast_node.gir_class and hasattr(ctx.ast_node.gir_class, "signals"):
|
||||||
name = "on"
|
for signal_name, signal in ctx.ast_node.gir_class.signals.items():
|
||||||
|
if str(ctx.next_token) == "=>":
|
||||||
|
snippet = signal_name
|
||||||
else:
|
else:
|
||||||
name = "on_" + (
|
if not isinstance(ctx.ast_node.parent, language.Object):
|
||||||
ast_node.parent.children[ClassName][0].tokens["id"]
|
name = "on"
|
||||||
or ast_node.parent.children[ClassName][0]
|
else:
|
||||||
.tokens["class_name"]
|
name = "on_" + (
|
||||||
.lower()
|
ctx.ast_node.parent.children[ClassName][0].tokens["id"]
|
||||||
)
|
or ctx.ast_node.parent.children[ClassName][0]
|
||||||
|
.tokens["class_name"]
|
||||||
|
.lower()
|
||||||
|
)
|
||||||
|
|
||||||
|
snippet = f"{signal_name} => \\$${{1:${name}_{signal_name.replace('-', '_')}}}()$0;"
|
||||||
|
|
||||||
yield Completion(
|
yield Completion(
|
||||||
signal_name,
|
signal_name,
|
||||||
CompletionItemKind.Event,
|
CompletionItemKind.Event,
|
||||||
sort_text=f"1 {signal_name}",
|
sort_text=get_sort_key(CompletionPriority.OBJECT_MEMBER, signal_name),
|
||||||
snippet=f"{signal_name} => \\$${{1:${name}_{signal_name.replace('-', '_')}}}()$0;",
|
snippet=snippet,
|
||||||
docs=signal.doc,
|
docs=signal.doc,
|
||||||
detail=signal.detail,
|
detail=signal.detail,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@completer(applies_in=[language.UI], matches=new_statement_patterns)
|
@completer(applies_in=[language.UI], matches=new_statement_patterns)
|
||||||
def template_completer(lsp, ast_node, match_variables):
|
def template_completer(_ctx: CompletionContext):
|
||||||
yield Completion(
|
yield Completion(
|
||||||
"template",
|
"template",
|
||||||
CompletionItemKind.Snippet,
|
CompletionItemKind.Snippet,
|
||||||
|
|
|
@ -19,10 +19,39 @@
|
||||||
|
|
||||||
|
|
||||||
import typing as T
|
import typing as T
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from enum import Enum
|
||||||
|
|
||||||
from .lsp_utils import Completion
|
from . import gir
|
||||||
|
from .ast_utils import AstNode
|
||||||
|
from .lsp_utils import Completion, CompletionItemKind
|
||||||
from .tokenizer import Token, TokenType
|
from .tokenizer import Token, TokenType
|
||||||
|
|
||||||
|
|
||||||
|
class CompletionPriority(Enum):
|
||||||
|
ENUM_MEMBER = "00"
|
||||||
|
NAMED_OBJECT = "01"
|
||||||
|
OBJECT_MEMBER = "02"
|
||||||
|
CLASS = "03"
|
||||||
|
NAMESPACE = "04"
|
||||||
|
KEYWORD = "05"
|
||||||
|
# An available namespace that hasn't been imported yet
|
||||||
|
IMPORT_NAMESPACE = "99"
|
||||||
|
|
||||||
|
|
||||||
|
def get_sort_key(priority: CompletionPriority, name: str):
|
||||||
|
return f"{priority.value} {name}"
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class CompletionContext:
|
||||||
|
client_supports_completion_choice: bool
|
||||||
|
ast_node: AstNode
|
||||||
|
match_variables: T.List[str]
|
||||||
|
next_token: Token
|
||||||
|
index: int
|
||||||
|
|
||||||
|
|
||||||
new_statement_patterns = [
|
new_statement_patterns = [
|
||||||
[(TokenType.PUNCTUATION, "{")],
|
[(TokenType.PUNCTUATION, "{")],
|
||||||
[(TokenType.PUNCTUATION, "}")],
|
[(TokenType.PUNCTUATION, "}")],
|
||||||
|
@ -32,8 +61,10 @@ new_statement_patterns = [
|
||||||
|
|
||||||
|
|
||||||
def completer(applies_in: T.List, matches: T.List = [], applies_in_subclass=None):
|
def completer(applies_in: T.List, matches: T.List = [], applies_in_subclass=None):
|
||||||
def decorator(func):
|
def decorator(func: T.Callable[[CompletionContext], T.Generator[Completion]]):
|
||||||
def inner(prev_tokens: T.List[Token], ast_node, lsp):
|
def inner(
|
||||||
|
prev_tokens: T.List[Token], next_token: Token, ast_node, lsp, idx: int
|
||||||
|
):
|
||||||
# For completers that apply in ObjectContent nodes, we can further
|
# For completers that apply in ObjectContent nodes, we can further
|
||||||
# check that the object is the right class
|
# check that the object is the right class
|
||||||
if applies_in_subclass is not None:
|
if applies_in_subclass is not None:
|
||||||
|
@ -66,10 +97,51 @@ def completer(applies_in: T.List, matches: T.List = [], applies_in_subclass=None
|
||||||
if not any_match:
|
if not any_match:
|
||||||
return
|
return
|
||||||
|
|
||||||
yield from func(lsp, ast_node, match_variables)
|
context = CompletionContext(
|
||||||
|
client_supports_completion_choice=lsp.client_supports_completion_choice,
|
||||||
|
ast_node=ast_node,
|
||||||
|
match_variables=match_variables,
|
||||||
|
next_token=next_token,
|
||||||
|
index=idx,
|
||||||
|
)
|
||||||
|
yield from func(context)
|
||||||
|
|
||||||
for c in applies_in:
|
for c in applies_in:
|
||||||
c.completers.append(inner)
|
c.completers.append(inner)
|
||||||
return inner
|
return inner
|
||||||
|
|
||||||
return decorator
|
return decorator
|
||||||
|
|
||||||
|
|
||||||
|
def get_property_completion(
|
||||||
|
name: str,
|
||||||
|
type: gir.GirType,
|
||||||
|
ctx: CompletionContext,
|
||||||
|
translated: bool,
|
||||||
|
doc: str,
|
||||||
|
) -> Completion:
|
||||||
|
if str(ctx.next_token) == ":":
|
||||||
|
snippet = name
|
||||||
|
elif isinstance(type, gir.BoolType) and ctx.client_supports_completion_choice:
|
||||||
|
snippet = f"{name}: ${{1|true,false|}};"
|
||||||
|
elif isinstance(type, gir.StringType):
|
||||||
|
snippet = f'{name}: _("$0");' if translated else f'{name}: "$0";'
|
||||||
|
elif (
|
||||||
|
isinstance(type, gir.Enumeration)
|
||||||
|
and len(type.members) <= 10
|
||||||
|
and ctx.client_supports_completion_choice
|
||||||
|
):
|
||||||
|
choices = ",".join(type.members.keys())
|
||||||
|
snippet = f"{name}: ${{1|{choices}|}};"
|
||||||
|
elif type.full_name == "Gtk.Expression":
|
||||||
|
snippet = f"{name}: expr $0;"
|
||||||
|
else:
|
||||||
|
snippet = f"{name}: $0;"
|
||||||
|
|
||||||
|
return Completion(
|
||||||
|
name,
|
||||||
|
CompletionItemKind.Property,
|
||||||
|
sort_text=get_sort_key(CompletionPriority.OBJECT_MEMBER, name),
|
||||||
|
snippet=snippet,
|
||||||
|
docs=doc,
|
||||||
|
)
|
||||||
|
|
|
@ -193,6 +193,9 @@ def format(data, tab_size=2, insert_space=True):
|
||||||
elif prev_line_type in require_extra_newline:
|
elif prev_line_type in require_extra_newline:
|
||||||
newlines = 2
|
newlines = 2
|
||||||
|
|
||||||
|
current_line = "\n".join(
|
||||||
|
[line.rstrip() for line in current_line.split("\n")]
|
||||||
|
)
|
||||||
commit_current_line(LineType.COMMENT, newlines_before=newlines)
|
commit_current_line(LineType.COMMENT, newlines_before=newlines)
|
||||||
|
|
||||||
else: # pragma: no cover
|
else: # pragma: no cover
|
||||||
|
|
|
@ -143,7 +143,7 @@ class ExtAdwResponseDialog(AstNode):
|
||||||
applies_in_subclass=("Adw", "MessageDialog"),
|
applies_in_subclass=("Adw", "MessageDialog"),
|
||||||
matches=new_statement_patterns,
|
matches=new_statement_patterns,
|
||||||
)
|
)
|
||||||
def complete_adw_message_dialog(lsp, ast_node, match_variables):
|
def complete_adw_message_dialog(_ctx: CompletionContext):
|
||||||
yield Completion(
|
yield Completion(
|
||||||
"responses", CompletionItemKind.Keyword, snippet="responses [\n\t$0\n]"
|
"responses", CompletionItemKind.Keyword, snippet="responses [\n\t$0\n]"
|
||||||
)
|
)
|
||||||
|
@ -154,9 +154,12 @@ def complete_adw_message_dialog(lsp, ast_node, match_variables):
|
||||||
applies_in_subclass=("Adw", "AlertDialog"),
|
applies_in_subclass=("Adw", "AlertDialog"),
|
||||||
matches=new_statement_patterns,
|
matches=new_statement_patterns,
|
||||||
)
|
)
|
||||||
def complete_adw_alert_dialog(lsp, ast_node, match_variables):
|
def complete_adw_alert_dialog(_ctx: CompletionContext):
|
||||||
yield Completion(
|
yield Completion(
|
||||||
"responses", CompletionItemKind.Keyword, snippet="responses [\n\t$0\n]"
|
"responses",
|
||||||
|
CompletionItemKind.Keyword,
|
||||||
|
snippet="responses [\n\t$0\n]",
|
||||||
|
sort_text=get_sort_key(CompletionPriority.OBJECT_MEMBER, "responses"),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,11 @@ from .values import ArrayValue, ExprValue, ObjectValue, Value
|
||||||
|
|
||||||
class Property(AstNode):
|
class Property(AstNode):
|
||||||
grammar = Statement(
|
grammar = Statement(
|
||||||
UseIdent("name"), ":", AnyOf(Binding, ExprValue, ObjectValue, Value, ArrayValue)
|
UseIdent("name"),
|
||||||
|
":",
|
||||||
|
AnyOf(Binding, ExprValue, ObjectValue, Value, ArrayValue).expected(
|
||||||
|
"property value"
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|
|
@ -25,7 +25,7 @@ from .gobject_object import ObjectContent, validate_parent_type
|
||||||
from .values import Value
|
from .values import Value
|
||||||
|
|
||||||
|
|
||||||
def get_property_types(gir):
|
def get_property_types(gir: gir.GirContext) -> T.Dict[str, T.Optional[GirType]]:
|
||||||
# from <https://docs.gtk.org/gtk4/enum.AccessibleProperty.html>
|
# from <https://docs.gtk.org/gtk4/enum.AccessibleProperty.html>
|
||||||
return {
|
return {
|
||||||
"autocomplete": gir.get_type("AccessibleAutocomplete", "Gtk"),
|
"autocomplete": gir.get_type("AccessibleAutocomplete", "Gtk"),
|
||||||
|
@ -50,7 +50,7 @@ def get_property_types(gir):
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def get_relation_types(gir):
|
def get_relation_types(gir: gir.GirContext) -> T.Dict[str, T.Optional[GirType]]:
|
||||||
# from <https://docs.gtk.org/gtk4/enum.AccessibleRelation.html>
|
# from <https://docs.gtk.org/gtk4/enum.AccessibleRelation.html>
|
||||||
widget = gir.get_type("Widget", "Gtk")
|
widget = gir.get_type("Widget", "Gtk")
|
||||||
return {
|
return {
|
||||||
|
@ -75,7 +75,7 @@ def get_relation_types(gir):
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def get_state_types(gir):
|
def get_state_types(gir: gir.GirContext) -> T.Dict[str, T.Optional[GirType]]:
|
||||||
# from <https://docs.gtk.org/gtk4/enum.AccessibleState.html>
|
# from <https://docs.gtk.org/gtk4/enum.AccessibleState.html>
|
||||||
return {
|
return {
|
||||||
"busy": BoolType(),
|
"busy": BoolType(),
|
||||||
|
@ -89,6 +89,20 @@ def get_state_types(gir):
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TRANSLATED = set(
|
||||||
|
[
|
||||||
|
"description",
|
||||||
|
"help-text",
|
||||||
|
"label",
|
||||||
|
"placeholder",
|
||||||
|
"role-description",
|
||||||
|
"value-text",
|
||||||
|
"col-index-text",
|
||||||
|
"row-index-text",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def get_types(gir):
|
def get_types(gir):
|
||||||
return {
|
return {
|
||||||
**get_property_types(gir),
|
**get_property_types(gir),
|
||||||
|
@ -121,7 +135,9 @@ class A11yProperty(AstNode):
|
||||||
grammar = Statement(
|
grammar = Statement(
|
||||||
UseIdent("name"),
|
UseIdent("name"),
|
||||||
":",
|
":",
|
||||||
AnyOf(Value, ["[", UseLiteral("list_form", True), Delimited(Value, ","), "]"]),
|
AnyOf(
|
||||||
|
Value, ["[", UseLiteral("list_form", True), Delimited(Value, ","), "]"]
|
||||||
|
).expected("value"),
|
||||||
)
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -232,9 +248,12 @@ class ExtAccessibility(AstNode):
|
||||||
applies_in=[ObjectContent],
|
applies_in=[ObjectContent],
|
||||||
matches=new_statement_patterns,
|
matches=new_statement_patterns,
|
||||||
)
|
)
|
||||||
def a11y_completer(lsp, ast_node, match_variables):
|
def a11y_completer(_ctx: CompletionContext):
|
||||||
yield Completion(
|
yield Completion(
|
||||||
"accessibility", CompletionItemKind.Snippet, snippet="accessibility {\n $0\n}"
|
"accessibility",
|
||||||
|
CompletionItemKind.Snippet,
|
||||||
|
snippet="accessibility {\n $0\n}",
|
||||||
|
sort_text=get_sort_key(CompletionPriority.OBJECT_MEMBER, "accessibility"),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -242,12 +261,14 @@ def a11y_completer(lsp, ast_node, match_variables):
|
||||||
applies_in=[ExtAccessibility],
|
applies_in=[ExtAccessibility],
|
||||||
matches=new_statement_patterns,
|
matches=new_statement_patterns,
|
||||||
)
|
)
|
||||||
def a11y_name_completer(lsp, ast_node, match_variables):
|
def a11y_property_completer(ctx: CompletionContext):
|
||||||
for name, type in get_types(ast_node.root.gir).items():
|
for name, type in get_types(ctx.ast_node.root.gir).items():
|
||||||
yield Completion(
|
yield get_property_completion(
|
||||||
name,
|
name,
|
||||||
CompletionItemKind.Property,
|
type,
|
||||||
docs=_get_docs(ast_node.root.gir, type.name),
|
ctx,
|
||||||
|
name in TRANSLATED,
|
||||||
|
_get_docs(ctx.ast_node.root.gir, name),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -94,8 +94,13 @@ class ExtComboBoxItems(AstNode):
|
||||||
applies_in_subclass=("Gtk", "ComboBoxText"),
|
applies_in_subclass=("Gtk", "ComboBoxText"),
|
||||||
matches=new_statement_patterns,
|
matches=new_statement_patterns,
|
||||||
)
|
)
|
||||||
def items_completer(lsp, ast_node, match_variables):
|
def items_completer(_ctx: CompletionContext):
|
||||||
yield Completion("items", CompletionItemKind.Snippet, snippet="items [$0]")
|
yield Completion(
|
||||||
|
"items",
|
||||||
|
CompletionItemKind.Snippet,
|
||||||
|
snippet="items [$0]",
|
||||||
|
sort_text=get_sort_key(CompletionPriority.OBJECT_MEMBER, "items"),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@decompiler("items", parent_type="Gtk.ComboBoxText")
|
@decompiler("items", parent_type="Gtk.ComboBoxText")
|
||||||
|
|
|
@ -101,12 +101,25 @@ ext_file_filter_suffixes = create_node("suffixes", "suffix")
|
||||||
applies_in_subclass=("Gtk", "FileFilter"),
|
applies_in_subclass=("Gtk", "FileFilter"),
|
||||||
matches=new_statement_patterns,
|
matches=new_statement_patterns,
|
||||||
)
|
)
|
||||||
def file_filter_completer(lsp, ast_node, match_variables):
|
def file_filter_completer(_ctx: CompletionContext):
|
||||||
yield Completion(
|
yield Completion(
|
||||||
"mime-types", CompletionItemKind.Snippet, snippet='mime-types ["$0"]'
|
"mime-types",
|
||||||
|
CompletionItemKind.Snippet,
|
||||||
|
snippet='mime-types ["$0"]',
|
||||||
|
sort_text=get_sort_key(CompletionPriority.OBJECT_MEMBER, "mime-types"),
|
||||||
|
)
|
||||||
|
yield Completion(
|
||||||
|
"patterns",
|
||||||
|
CompletionItemKind.Snippet,
|
||||||
|
snippet='patterns ["$0"]',
|
||||||
|
sort_text=get_sort_key(CompletionPriority.OBJECT_MEMBER, "patterns"),
|
||||||
|
)
|
||||||
|
yield Completion(
|
||||||
|
"suffixes",
|
||||||
|
CompletionItemKind.Snippet,
|
||||||
|
snippet='suffixes ["$0"]',
|
||||||
|
sort_text=get_sort_key(CompletionPriority.OBJECT_MEMBER, "suffixes"),
|
||||||
)
|
)
|
||||||
yield Completion("patterns", CompletionItemKind.Snippet, snippet='patterns ["$0"]')
|
|
||||||
yield Completion("suffixes", CompletionItemKind.Snippet, snippet='suffixes ["$0"]')
|
|
||||||
|
|
||||||
|
|
||||||
@decompiler("mime-types")
|
@decompiler("mime-types")
|
||||||
|
|
|
@ -93,8 +93,13 @@ class ExtLayout(AstNode):
|
||||||
applies_in_subclass=("Gtk", "Widget"),
|
applies_in_subclass=("Gtk", "Widget"),
|
||||||
matches=new_statement_patterns,
|
matches=new_statement_patterns,
|
||||||
)
|
)
|
||||||
def layout_completer(lsp, ast_node, match_variables):
|
def layout_completer(_ctx: CompletionContext):
|
||||||
yield Completion("layout", CompletionItemKind.Snippet, snippet="layout {\n $0\n}")
|
yield Completion(
|
||||||
|
"layout",
|
||||||
|
CompletionItemKind.Snippet,
|
||||||
|
snippet="layout {\n $0\n}",
|
||||||
|
sort_text=get_sort_key(CompletionPriority.OBJECT_MEMBER, "layout"),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@decompiler("layout")
|
@decompiler("layout")
|
||||||
|
|
|
@ -243,7 +243,7 @@ from .ui import UI
|
||||||
applies_in=[UI],
|
applies_in=[UI],
|
||||||
matches=new_statement_patterns,
|
matches=new_statement_patterns,
|
||||||
)
|
)
|
||||||
def menu_completer(lsp, ast_node, match_variables):
|
def menu_completer(_ctx: CompletionContext):
|
||||||
yield Completion("menu", CompletionItemKind.Snippet, snippet="menu {\n $0\n}")
|
yield Completion("menu", CompletionItemKind.Snippet, snippet="menu {\n $0\n}")
|
||||||
|
|
||||||
|
|
||||||
|
@ -251,23 +251,50 @@ def menu_completer(lsp, ast_node, match_variables):
|
||||||
applies_in=[Menu],
|
applies_in=[Menu],
|
||||||
matches=new_statement_patterns,
|
matches=new_statement_patterns,
|
||||||
)
|
)
|
||||||
def menu_content_completer(lsp, ast_node, match_variables):
|
def menu_content_completer(_ctx: CompletionContext):
|
||||||
yield Completion(
|
yield Completion(
|
||||||
"submenu", CompletionItemKind.Snippet, snippet="submenu {\n $0\n}"
|
"submenu",
|
||||||
|
CompletionItemKind.Snippet,
|
||||||
|
snippet="submenu {\n $0\n}",
|
||||||
|
sort_text=get_sort_key(CompletionPriority.CLASS, "1 submenu"),
|
||||||
)
|
)
|
||||||
yield Completion(
|
yield Completion(
|
||||||
"section", CompletionItemKind.Snippet, snippet="section {\n $0\n}"
|
"section",
|
||||||
|
CompletionItemKind.Snippet,
|
||||||
|
snippet="section {\n $0\n}",
|
||||||
|
sort_text=get_sort_key(CompletionPriority.CLASS, "1 section"),
|
||||||
|
)
|
||||||
|
yield Completion(
|
||||||
|
"item",
|
||||||
|
CompletionItemKind.Snippet,
|
||||||
|
snippet="item {\n $0\n}",
|
||||||
|
sort_text=get_sort_key(CompletionPriority.CLASS, "1 item"),
|
||||||
)
|
)
|
||||||
yield Completion("item", CompletionItemKind.Snippet, snippet="item {\n $0\n}")
|
|
||||||
yield Completion(
|
yield Completion(
|
||||||
"item (shorthand)",
|
"item (shorthand)",
|
||||||
CompletionItemKind.Snippet,
|
CompletionItemKind.Snippet,
|
||||||
snippet='item (_("${1:Label}"), "${2:action-name}", "${3:icon-name}")',
|
snippet='item (_("${1:Label}"), "${2:action-name}", "${3:icon-name}")',
|
||||||
|
sort_text=get_sort_key(CompletionPriority.CLASS, "0 item (shorthand)"),
|
||||||
)
|
)
|
||||||
|
|
||||||
yield Completion("label", CompletionItemKind.Snippet, snippet="label: $0;")
|
yield Completion(
|
||||||
yield Completion("action", CompletionItemKind.Snippet, snippet='action: "$0";')
|
"label",
|
||||||
yield Completion("icon", CompletionItemKind.Snippet, snippet='icon: "$0";')
|
CompletionItemKind.Snippet,
|
||||||
|
snippet="label: $0;",
|
||||||
|
sort_text=get_sort_key(CompletionPriority.OBJECT_MEMBER, "label"),
|
||||||
|
)
|
||||||
|
yield Completion(
|
||||||
|
"action",
|
||||||
|
CompletionItemKind.Snippet,
|
||||||
|
snippet='action: "$0";',
|
||||||
|
sort_text=get_sort_key(CompletionPriority.OBJECT_MEMBER, "action"),
|
||||||
|
)
|
||||||
|
yield Completion(
|
||||||
|
"icon",
|
||||||
|
CompletionItemKind.Snippet,
|
||||||
|
snippet='icon: "$0";',
|
||||||
|
sort_text=get_sort_key(CompletionPriority.OBJECT_MEMBER, "icon"),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@decompiler("menu")
|
@decompiler("menu")
|
||||||
|
|
|
@ -137,14 +137,19 @@ class ExtScaleMarks(AstNode):
|
||||||
applies_in_subclass=("Gtk", "Scale"),
|
applies_in_subclass=("Gtk", "Scale"),
|
||||||
matches=new_statement_patterns,
|
matches=new_statement_patterns,
|
||||||
)
|
)
|
||||||
def complete_marks(lsp, ast_node, match_variables):
|
def complete_marks(_ctx: CompletionContext):
|
||||||
yield Completion("marks", CompletionItemKind.Keyword, snippet="marks [\n\t$0\n]")
|
yield Completion(
|
||||||
|
"marks",
|
||||||
|
CompletionItemKind.Keyword,
|
||||||
|
snippet="marks [\n\t$0\n]",
|
||||||
|
sort_text=get_sort_key(CompletionPriority.OBJECT_MEMBER, "marks"),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@completer(
|
@completer(
|
||||||
applies_in=[ExtScaleMarks],
|
applies_in=[ExtScaleMarks],
|
||||||
)
|
)
|
||||||
def complete_mark(lsp, ast_node, match_variables):
|
def complete_mark(_ctx: CompletionContext):
|
||||||
yield Completion("mark", CompletionItemKind.Keyword, snippet="mark ($0),")
|
yield Completion("mark", CompletionItemKind.Keyword, snippet="mark ($0),")
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -104,8 +104,13 @@ class ExtSizeGroupWidgets(AstNode):
|
||||||
applies_in_subclass=("Gtk", "SizeGroup"),
|
applies_in_subclass=("Gtk", "SizeGroup"),
|
||||||
matches=new_statement_patterns,
|
matches=new_statement_patterns,
|
||||||
)
|
)
|
||||||
def size_group_completer(lsp, ast_node, match_variables):
|
def size_group_completer(_ctx: CompletionContext):
|
||||||
yield Completion("widgets", CompletionItemKind.Snippet, snippet="widgets [$0]")
|
yield Completion(
|
||||||
|
"widgets",
|
||||||
|
CompletionItemKind.Snippet,
|
||||||
|
snippet="widgets [$0]",
|
||||||
|
sort_text=get_sort_key(CompletionPriority.OBJECT_MEMBER, "widgets"),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@decompiler("widgets")
|
@decompiler("widgets")
|
||||||
|
|
|
@ -75,8 +75,13 @@ class ExtStringListStrings(AstNode):
|
||||||
applies_in_subclass=("Gtk", "StringList"),
|
applies_in_subclass=("Gtk", "StringList"),
|
||||||
matches=new_statement_patterns,
|
matches=new_statement_patterns,
|
||||||
)
|
)
|
||||||
def strings_completer(lsp, ast_node, match_variables):
|
def strings_completer(_ctx: CompletionContext):
|
||||||
yield Completion("strings", CompletionItemKind.Snippet, snippet="strings [$0]")
|
yield Completion(
|
||||||
|
"strings",
|
||||||
|
CompletionItemKind.Snippet,
|
||||||
|
snippet="strings [$0]",
|
||||||
|
sort_text=get_sort_key(CompletionPriority.OBJECT_MEMBER, "strings"),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@decompiler("items", parent_type="Gtk.StringList")
|
@decompiler("items", parent_type="Gtk.StringList")
|
||||||
|
|
|
@ -80,8 +80,13 @@ class ExtStyles(AstNode):
|
||||||
applies_in_subclass=("Gtk", "Widget"),
|
applies_in_subclass=("Gtk", "Widget"),
|
||||||
matches=new_statement_patterns,
|
matches=new_statement_patterns,
|
||||||
)
|
)
|
||||||
def style_completer(lsp, ast_node, match_variables):
|
def style_completer(_ctx: CompletionContext):
|
||||||
yield Completion("styles", CompletionItemKind.Keyword, snippet='styles ["$0"]')
|
yield Completion(
|
||||||
|
"styles",
|
||||||
|
CompletionItemKind.Keyword,
|
||||||
|
snippet='styles ["$0"]',
|
||||||
|
sort_text=get_sort_key(CompletionPriority.OBJECT_MEMBER, "styles"),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@decompiler("style")
|
@decompiler("style")
|
||||||
|
|
|
@ -110,16 +110,22 @@ class UI(AstNode):
|
||||||
and self.template.class_name.glib_type_name == id
|
and self.template.class_name.glib_type_name == id
|
||||||
)
|
)
|
||||||
|
|
||||||
def import_code_action(self, ns: str, version: str) -> CodeAction:
|
def import_range(self, ns: str):
|
||||||
if len(self.children[Import]):
|
"""Returns a range to insert a new import statement"""
|
||||||
pos = self.children[Import][-1].range.end
|
pos = self.children[GtkDirective][0].range.end
|
||||||
else:
|
|
||||||
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(
|
return CodeAction(
|
||||||
f"Import {ns} {version}",
|
f"Import {ns} {version}",
|
||||||
f"\nusing {ns} {version};",
|
f"\nusing {ns} {version};",
|
||||||
Range(pos, pos, self.group.text),
|
self.import_range(ns),
|
||||||
)
|
)
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
|
|
|
@ -87,6 +87,7 @@ class Completion:
|
||||||
text: T.Optional[str] = None
|
text: T.Optional[str] = None
|
||||||
snippet: T.Optional[str] = None
|
snippet: T.Optional[str] = None
|
||||||
detail: T.Optional[str] = None
|
detail: T.Optional[str] = None
|
||||||
|
additional_text_edits: T.Optional[T.List["TextEdit"]] = None
|
||||||
|
|
||||||
def to_json(self, snippets: bool):
|
def to_json(self, snippets: bool):
|
||||||
insert_text = self.text or self.label
|
insert_text = self.text or self.label
|
||||||
|
@ -114,6 +115,11 @@ class Completion:
|
||||||
"insertText": insert_text,
|
"insertText": insert_text,
|
||||||
"insertTextFormat": insert_text_format,
|
"insertTextFormat": insert_text_format,
|
||||||
"detail": self.detail if self.detail else None,
|
"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}
|
return {k: v for k, v in result.items() if v is not None}
|
||||||
|
|
||||||
|
|
|
@ -1,2 +1,4 @@
|
||||||
using Gtk 4.0;
|
using Gtk 4.0;
|
||||||
//comment
|
//comment
|
||||||
|
// Trailing whitespace:
|
||||||
|
//
|
||||||
|
|
|
@ -1,2 +1,4 @@
|
||||||
using Gtk 4.0;
|
using Gtk 4.0;
|
||||||
// comment
|
// comment
|
||||||
|
// Trailing whitespace:
|
||||||
|
//
|
||||||
|
|
|
@ -64,11 +64,11 @@ class TestSamples(unittest.TestCase):
|
||||||
|
|
||||||
def assert_ast_doesnt_crash(self, text, tokens, ast: AstNode):
|
def assert_ast_doesnt_crash(self, text, tokens, ast: AstNode):
|
||||||
lsp = LanguageServer()
|
lsp = LanguageServer()
|
||||||
for i in range(len(text)):
|
for i in range(len(text) + 1):
|
||||||
ast.get_docs(i)
|
ast.get_docs(i)
|
||||||
for i in range(len(text)):
|
for i in range(len(text) + 1):
|
||||||
list(complete(lsp, ast, tokens, i))
|
list(complete(lsp, ast, tokens, i))
|
||||||
for i in range(len(text)):
|
for i in range(len(text) + 1):
|
||||||
ast.get_reference(i)
|
ast.get_reference(i)
|
||||||
ast.get_document_symbols()
|
ast.get_document_symbols()
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue