mirror of
https://gitlab.gnome.org/jwestman/blueprint-compiler.git
synced 2025-05-04 15:59:08 -04:00
completions: Add completions for response IDs
This commit is contained in:
parent
866092ccf7
commit
b26433d865
16 changed files with 123 additions and 42 deletions
|
@ -76,7 +76,11 @@ 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
|
||||
|
||||
|
@ -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,
|
||||
)
|
||||
|
|
|
@ -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]
|
||||
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])
|
||||
)
|
||||
if not ast_node.gir_class or not ast_node.gir_class.assignable_to(type):
|
||||
for c in applies_in_subclass
|
||||
]
|
||||
)
|
||||
):
|
||||
return
|
||||
|
||||
any_match = len(matches) == 0
|
||||
|
|
|
@ -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 (
|
||||
|
|
|
@ -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 [")
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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]:
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1 +1 @@
|
|||
4,6,22,Action widget must have ID
|
||||
4,5,24,Action widget must have ID
|
||||
|
|
|
@ -1 +1 @@
|
|||
4,6,18,Gtk.Box doesn't have action widgets
|
||||
4,5,20,Gtk.Box doesn't have action widgets
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue