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,7 +76,11 @@ def complete(
next_token = tokens[next_token_idx] 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]: 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 idx = tokens[token_idx].start
token_idx -= 1 token_idx -= 1
@ -236,7 +240,7 @@ def property_completer(ctx: CompletionContext):
for prop_name, prop in ctx.ast_node.gir_class.properties.items(): for prop_name, prop in ctx.ast_node.gir_class.properties.items():
yield get_property_completion( yield get_property_completion(
prop_name, prop_name,
prop, prop.type,
ctx, ctx,
annotations.is_property_translated(prop), annotations.is_property_translated(prop),
prop.doc, prop.doc,
@ -245,7 +249,11 @@ def property_completer(ctx: CompletionContext):
@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, ":")],
[(TokenType.PUNCTUATION, ",")],
[(TokenType.PUNCTUATION, "[")],
],
) )
def prop_value_completer(ctx: CompletionContext): def prop_value_completer(ctx: CompletionContext):
if isinstance(ctx.ast_node, language.Property): if isinstance(ctx.ast_node, language.Property):
@ -367,3 +375,58 @@ def template_completer(_ctx: CompletionContext):
CompletionItemKind.Snippet, CompletionItemKind.Snippet,
snippet="template ${1:ClassName} : ${2:ParentClass} {\n $0\n}", 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 # 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:
type = ast_node.root.gir.get_type( parent_obj = ast_node
applies_in_subclass[1], applies_in_subclass[0] 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])
) )
if not ast_node.gir_class or not ast_node.gir_class.assignable_to(type): for c in applies_in_subclass
]
)
):
return return
any_match = len(matches) == 0 any_match = len(matches) == 0

View file

@ -34,9 +34,16 @@ from .gtk_scale import ExtScaleMarks
from .gtk_size_group import ExtSizeGroupWidgets from .gtk_size_group import ExtSizeGroupWidgets
from .gtk_string_list import ExtStringListStrings from .gtk_string_list import ExtStringListStrings
from .gtk_styles import ExtStyles 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 .gtkbuilder_template import Template
from .imports import GtkDirective, Import from .imports import GtkDirective, Import
from .response_id import ExtResponse
from .types import ClassName from .types import ClassName
from .ui import UI from .ui import UI
from .values import ( from .values import (

View file

@ -140,7 +140,7 @@ class ExtAdwResponseDialog(AstNode):
@completer( @completer(
applies_in=[ObjectContent], applies_in=[ObjectContent],
applies_in_subclass=("Adw", "MessageDialog"), applies_in_subclass=[("Adw", "AlertDialog"), ("Adw", "MessageDialog")],
matches=new_statement_patterns, matches=new_statement_patterns,
) )
def complete_adw_message_dialog(_ctx: CompletionContext): 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") @decompiler("responses")
def decompile_responses(ctx, gir): def decompile_responses(ctx, gir):
ctx.print(f"responses [") ctx.print(f"responses [")

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -31,7 +31,12 @@ ALLOWED_PARENTS: T.List[T.Tuple[str, str]] = [
class ChildInternal(AstNode): class ChildInternal(AstNode):
grammar = ["internal-child", UseIdent("internal_child")] grammar = [
"[",
"internal-child",
UseIdent("internal_child").expected("internal child name"),
Match("]").expected(),
]
@property @property
def internal_child(self) -> str: def internal_child(self) -> str:
@ -39,7 +44,7 @@ class ChildInternal(AstNode):
class ChildType(AstNode): class ChildType(AstNode):
grammar = UseIdent("child_type").expected("a child type") grammar = ["[", UseIdent("child_type").expected("a child type"), "]"]
@property @property
def child_type(self) -> str: def child_type(self) -> str:
@ -59,7 +64,7 @@ class ChildExtension(AstNode):
class ChildAnnotation(AstNode): class ChildAnnotation(AstNode):
grammar = ["[", AnyOf(ChildInternal, ChildExtension, ChildType), "]"] grammar = AnyOf(ChildInternal, ChildExtension, ChildType)
@property @property
def child(self) -> T.Union[ChildInternal, ChildExtension, ChildType]: 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")] ALLOWED_PARENTS: T.List[T.Tuple[str, str]] = [("Gtk", "Dialog"), ("Gtk", "InfoBar")]
grammar = [ grammar = Statement(
"[",
Keyword("action"), Keyword("action"),
Keyword("response"), Keyword("response"),
"=", Match("=").expected(),
AnyOf( AnyOf(
UseIdent("response_id"), UseIdent("response_id"),
[ [
Optional(UseExact("sign", "-")), Optional(UseExact("sign", "-")),
UseNumber("response_id"), UseNumber("response_id"),
], ],
), ).expected("response ID"),
Optional([Keyword("default"), UseLiteral("is_default", True)]), Optional([Keyword("default"), UseLiteral("is_default", True)]),
] end="]",
)
@validate() @validate()
def parent_has_action_widgets(self) -> None: 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 """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.""" 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.children = [to_parse_node(child) for child in children]
self.end = end
def _parse(self, ctx) -> bool: def _parse(self, ctx) -> bool:
for child in self.children: for child in self.children:
@ -340,11 +341,16 @@ class Statement(ParseNode):
except CompileError as e: except CompileError as e:
ctx.errors.append(e) ctx.errors.append(e)
ctx.set_group_incomplete() ctx.set_group_incomplete()
token = ctx.peek_token()
if str(token) == self.end:
ctx.next_token()
return True return True
token = ctx.peek_token() token = ctx.peek_token()
if str(token) != ";": if str(token) != self.end:
ctx.errors.append(CompileError("Expected `;`", token.range)) ctx.errors.append(CompileError(f"Expected `{self.end}`", token.range))
else: else:
ctx.next_token() ctx.next_token()
return True 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