completions: Add completions for response IDs

This commit is contained in:
James Westman 2025-01-17 16:44:21 -06:00
parent 866092ccf7
commit b26433d865
No known key found for this signature in database
GPG key ID: CE2DBA0ADB654EA6
16 changed files with 123 additions and 42 deletions

View file

@ -76,9 +76,13 @@ def complete(
next_token = tokens[next_token_idx]
# 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]:
if tokens[token_idx].type == TokenType.IDENT:
idx = tokens[token_idx].start
token_idx -= 1
else:
while tokens[token_idx].type == TokenType.WHITESPACE:
idx = tokens[token_idx].start
token_idx -= 1
yield from _complete(lsp, ast_node, tokens, idx, token_idx, next_token)
@ -236,7 +240,7 @@ def property_completer(ctx: CompletionContext):
for prop_name, prop in ctx.ast_node.gir_class.properties.items():
yield get_property_completion(
prop_name,
prop,
prop.type,
ctx,
annotations.is_property_translated(prop),
prop.doc,
@ -245,7 +249,11 @@ def property_completer(ctx: CompletionContext):
@completer(
applies_in=[language.Property, language.A11yProperty],
matches=[[(TokenType.IDENT, None), (TokenType.OP, ":")]],
matches=[
[(TokenType.IDENT, None), (TokenType.OP, ":")],
[(TokenType.PUNCTUATION, ",")],
[(TokenType.PUNCTUATION, "[")],
],
)
def prop_value_completer(ctx: CompletionContext):
if isinstance(ctx.ast_node, language.Property):
@ -367,3 +375,58 @@ def template_completer(_ctx: CompletionContext):
CompletionItemKind.Snippet,
snippet="template ${1:ClassName} : ${2:ParentClass} {\n $0\n}",
)
@completer(
applies_in=[language.ObjectContent, language.ChildType],
matches=[[(TokenType.PUNCTUATION, "[")]],
applies_in_subclass=[("Gtk", "Dialog"), ("Gtk", "InfoBar")],
)
def response_id_completer(ctx: CompletionContext):
yield Completion(
"action",
CompletionItemKind.Snippet,
sort_text=get_sort_key(CompletionPriority.KEYWORD, "action"),
snippet="action response=$0",
)
@completer(
[language.ChildAnnotation, language.ExtResponse],
[[(TokenType.IDENT, "action"), (TokenType.IDENT, "response"), (TokenType.OP, "=")]],
)
def complete_response_id(ctx: CompletionContext):
gir = ctx.ast_node.root.gir
response_type = gir.get_type("ResponseType", "Gtk")
yield from [
Completion(
name,
kind=CompletionItemKind.EnumMember,
docs=member.doc,
)
for name, member in response_type.members.items()
]
@completer(
[language.ChildAnnotation, language.ExtResponse],
[
[
(TokenType.IDENT, "action"),
(TokenType.IDENT, "response"),
(TokenType.OP, "="),
(TokenType.IDENT, None),
],
[
(TokenType.IDENT, "action"),
(TokenType.IDENT, "response"),
(TokenType.OP, "="),
(TokenType.NUMBER, None),
],
],
)
def complete_response_default(ctx: CompletionContext):
yield Completion(
"default",
kind=CompletionItemKind.Keyword,
)

View file

@ -68,10 +68,22 @@ def completer(applies_in: T.List, matches: T.List = [], applies_in_subclass=None
# For completers that apply in ObjectContent nodes, we can further
# check that the object is the right class
if applies_in_subclass is not None:
type = ast_node.root.gir.get_type(
applies_in_subclass[1], applies_in_subclass[0]
)
if not ast_node.gir_class or not ast_node.gir_class.assignable_to(type):
parent_obj = ast_node
while parent_obj is not None and not hasattr(parent_obj, "gir_class"):
parent_obj = parent_obj.parent
if (
parent_obj is None
or not parent_obj.gir_class
or not any(
[
parent_obj.gir_class.assignable_to(
parent_obj.root.gir.get_type(c[1], c[0])
)
for c in applies_in_subclass
]
)
):
return
any_match = len(matches) == 0

View file

@ -34,9 +34,16 @@ from .gtk_scale import ExtScaleMarks
from .gtk_size_group import ExtSizeGroupWidgets
from .gtk_string_list import ExtStringListStrings
from .gtk_styles import ExtStyles
from .gtkbuilder_child import Child, ChildExtension, ChildInternal, ChildType
from .gtkbuilder_child import (
Child,
ChildAnnotation,
ChildExtension,
ChildInternal,
ChildType,
)
from .gtkbuilder_template import Template
from .imports import GtkDirective, Import
from .response_id import ExtResponse
from .types import ClassName
from .ui import UI
from .values import (

View file

@ -140,7 +140,7 @@ class ExtAdwResponseDialog(AstNode):
@completer(
applies_in=[ObjectContent],
applies_in_subclass=("Adw", "MessageDialog"),
applies_in_subclass=[("Adw", "AlertDialog"), ("Adw", "MessageDialog")],
matches=new_statement_patterns,
)
def complete_adw_message_dialog(_ctx: CompletionContext):
@ -149,20 +149,6 @@ def complete_adw_message_dialog(_ctx: CompletionContext):
)
@completer(
applies_in=[ObjectContent],
applies_in_subclass=("Adw", "AlertDialog"),
matches=new_statement_patterns,
)
def complete_adw_alert_dialog(_ctx: CompletionContext):
yield Completion(
"responses",
CompletionItemKind.Keyword,
snippet="responses [\n\t$0\n]",
sort_text=get_sort_key(CompletionPriority.OBJECT_MEMBER, "responses"),
)
@decompiler("responses")
def decompile_responses(ctx, gir):
ctx.print(f"responses [")

View file

@ -91,7 +91,7 @@ class ExtComboBoxItems(AstNode):
@completer(
applies_in=[ObjectContent],
applies_in_subclass=("Gtk", "ComboBoxText"),
applies_in_subclass=[("Gtk", "ComboBoxText")],
matches=new_statement_patterns,
)
def items_completer(_ctx: CompletionContext):

View file

@ -98,7 +98,7 @@ ext_file_filter_suffixes = create_node("suffixes", "suffix")
@completer(
applies_in=[ObjectContent],
applies_in_subclass=("Gtk", "FileFilter"),
applies_in_subclass=[("Gtk", "FileFilter")],
matches=new_statement_patterns,
)
def file_filter_completer(_ctx: CompletionContext):

View file

@ -90,7 +90,7 @@ class ExtLayout(AstNode):
@completer(
applies_in=[ObjectContent],
applies_in_subclass=("Gtk", "Widget"),
applies_in_subclass=[("Gtk", "Widget")],
matches=new_statement_patterns,
)
def layout_completer(_ctx: CompletionContext):

View file

@ -134,7 +134,7 @@ class ExtScaleMarks(AstNode):
@completer(
applies_in=[ObjectContent],
applies_in_subclass=("Gtk", "Scale"),
applies_in_subclass=[("Gtk", "Scale")],
matches=new_statement_patterns,
)
def complete_marks(_ctx: CompletionContext):

View file

@ -101,7 +101,7 @@ class ExtSizeGroupWidgets(AstNode):
@completer(
applies_in=[ObjectContent],
applies_in_subclass=("Gtk", "SizeGroup"),
applies_in_subclass=[("Gtk", "SizeGroup")],
matches=new_statement_patterns,
)
def size_group_completer(_ctx: CompletionContext):

View file

@ -72,7 +72,7 @@ class ExtStringListStrings(AstNode):
@completer(
applies_in=[ObjectContent],
applies_in_subclass=("Gtk", "StringList"),
applies_in_subclass=[("Gtk", "StringList")],
matches=new_statement_patterns,
)
def strings_completer(_ctx: CompletionContext):

View file

@ -77,7 +77,7 @@ class ExtStyles(AstNode):
@completer(
applies_in=[ObjectContent],
applies_in_subclass=("Gtk", "Widget"),
applies_in_subclass=[("Gtk", "Widget")],
matches=new_statement_patterns,
)
def style_completer(_ctx: CompletionContext):

View file

@ -31,7 +31,12 @@ ALLOWED_PARENTS: T.List[T.Tuple[str, str]] = [
class ChildInternal(AstNode):
grammar = ["internal-child", UseIdent("internal_child")]
grammar = [
"[",
"internal-child",
UseIdent("internal_child").expected("internal child name"),
Match("]").expected(),
]
@property
def internal_child(self) -> str:
@ -39,7 +44,7 @@ class ChildInternal(AstNode):
class ChildType(AstNode):
grammar = UseIdent("child_type").expected("a child type")
grammar = ["[", UseIdent("child_type").expected("a child type"), "]"]
@property
def child_type(self) -> str:
@ -59,7 +64,7 @@ class ChildExtension(AstNode):
class ChildAnnotation(AstNode):
grammar = ["[", AnyOf(ChildInternal, ChildExtension, ChildType), "]"]
grammar = AnyOf(ChildInternal, ChildExtension, ChildType)
@property
def child(self) -> T.Union[ChildInternal, ChildExtension, ChildType]:

View file

@ -28,19 +28,21 @@ class ExtResponse(AstNode):
ALLOWED_PARENTS: T.List[T.Tuple[str, str]] = [("Gtk", "Dialog"), ("Gtk", "InfoBar")]
grammar = [
grammar = Statement(
"[",
Keyword("action"),
Keyword("response"),
"=",
Match("=").expected(),
AnyOf(
UseIdent("response_id"),
[
Optional(UseExact("sign", "-")),
UseNumber("response_id"),
],
),
).expected("response ID"),
Optional([Keyword("default"), UseLiteral("is_default", True)]),
]
end="]",
)
@validate()
def parent_has_action_widgets(self) -> None:

View file

@ -329,8 +329,9 @@ class Statement(ParseNode):
"""ParseNode that attempts to match all of its children in sequence. If any
child raises an error, the error will be logged but parsing will continue."""
def __init__(self, *children):
def __init__(self, *children, end: str = ";"):
self.children = [to_parse_node(child) for child in children]
self.end = end
def _parse(self, ctx) -> bool:
for child in self.children:
@ -340,11 +341,16 @@ class Statement(ParseNode):
except CompileError as e:
ctx.errors.append(e)
ctx.set_group_incomplete()
token = ctx.peek_token()
if str(token) == self.end:
ctx.next_token()
return True
token = ctx.peek_token()
if str(token) != ";":
ctx.errors.append(CompileError("Expected `;`", token.range))
if str(token) != self.end:
ctx.errors.append(CompileError(f"Expected `{self.end}`", token.range))
else:
ctx.next_token()
return True

View file

@ -1 +1 @@
4,6,22,Action widget must have ID
4,5,24,Action widget must have ID

View file

@ -1 +1 @@
4,6,18,Gtk.Box doesn't have action widgets
4,5,20,Gtk.Box doesn't have action widgets