Compare commits

...

6 commits

Author SHA1 Message Date
kotontrion
8b9614bf0c Merge branch 'main' into 'main'
compile: fix flag values

Closes #180

See merge request jwestman/blueprint-compiler!220
2024-12-27 08:33:03 +00:00
James Westman
d6f4b88d35
lsp: Fix crash on incomplete detailed signal 2024-12-25 10:31:35 -06:00
James Westman
a6d57cebec language: Add not-swapped flag for signals
This is needed because GtkBuilder defaults to swapped when you specify
the object attribute.
2024-12-23 02:46:52 +00:00
kotontrion
e07da3c339 flags: use nick instead of name 2024-12-18 17:46:26 +00:00
kotontrion
2ae41020ab Fix flag return value type 2024-12-18 17:46:26 +00:00
kotontrion
f48b840cfa compile: fix flag values
gtk builder does not support combining interger values with | in flags
properties, so the short names are used instead.
2024-12-18 17:46:26 +00:00
14 changed files with 84 additions and 12 deletions

View file

@ -27,6 +27,7 @@ from .gtkbuilder_template import Template
class SignalFlag(AstNode): class SignalFlag(AstNode):
grammar = AnyOf( grammar = AnyOf(
UseExact("flag", "swapped"), UseExact("flag", "swapped"),
UseExact("flag", "not-swapped"),
UseExact("flag", "after"), UseExact("flag", "after"),
) )
@ -40,6 +41,27 @@ class SignalFlag(AstNode):
f"Duplicate flag '{self.flag}'", lambda x: x.flag == self.flag f"Duplicate flag '{self.flag}'", lambda x: x.flag == self.flag
) )
@validate()
def swapped_exclusive(self):
if self.flag in ["swapped", "not-swapped"]:
self.validate_unique_in_parent(
"'swapped' and 'not-swapped' flags cannot be used together",
lambda x: x.flag in ["swapped", "not-swapped"],
)
@validate()
def swapped_unnecessary(self):
if self.flag == "not-swapped" and self.parent.object_id is None:
raise CompileWarning(
"'not-swapped' is the default for handlers that do not specify an object",
actions=[CodeAction("Remove 'not-swapped' flag", "")],
)
elif self.flag == "swapped" and self.parent.object_id is not None:
raise CompileWarning(
"'swapped' is the default for handlers that specify an object",
actions=[CodeAction("Remove 'swapped' flag", "")],
)
@docs() @docs()
def ref_docs(self): def ref_docs(self):
return get_docs_section("Syntax Signal") return get_docs_section("Syntax Signal")
@ -92,9 +114,17 @@ class Signal(AstNode):
def flags(self) -> T.List[SignalFlag]: def flags(self) -> T.List[SignalFlag]:
return self.children[SignalFlag] return self.children[SignalFlag]
# Returns True if the "swapped" flag is present, False if "not-swapped" is present, and None if neither are present.
# GtkBuilder's default if swapped is not specified is to not swap the arguments if no object is specified, and to
# swap them if an object is specified.
@property @property
def is_swapped(self) -> bool: def is_swapped(self) -> T.Optional[bool]:
return any(x.flag == "swapped" for x in self.flags) for flag in self.flags:
if flag.flag == "swapped":
return True
elif flag.flag == "not-swapped":
return False
return None
@property @property
def is_after(self) -> bool: def is_after(self) -> bool:
@ -113,12 +143,13 @@ class Signal(AstNode):
@property @property
def document_symbol(self) -> DocumentSymbol: def document_symbol(self) -> DocumentSymbol:
detail = self.ranges["detail_start", "detail_end"]
return DocumentSymbol( return DocumentSymbol(
self.full_name, self.full_name,
SymbolKind.Event, SymbolKind.Event,
self.range, self.range,
self.group.tokens["name"].range, self.group.tokens["name"].range,
self.ranges["detail_start", "detail_end"].text, detail.text if detail is not None else None,
) )
def get_reference(self, idx: int) -> T.Optional[LocationLink]: def get_reference(self, idx: int) -> T.Optional[LocationLink]:
@ -194,15 +225,16 @@ class Signal(AstNode):
@decompiler("signal") @decompiler("signal")
def decompile_signal( def decompile_signal(ctx, gir, name, handler, swapped=None, after="false", object=None):
ctx, gir, name, handler, swapped="false", after="false", object=None
):
object_name = object or "" object_name = object or ""
name = name.replace("_", "-") name = name.replace("_", "-")
line = f"{name} => ${handler}({object_name})" line = f"{name} => ${handler}({object_name})"
if decompile.truthy(swapped): if decompile.truthy(swapped):
line += " swapped" line += " swapped"
elif swapped is not None:
line += " not-swapped"
if decompile.truthy(after): if decompile.truthy(after):
line += " after" line += " after"

View file

@ -211,12 +211,12 @@ class Flag(AstNode):
return self.tokens["value"] return self.tokens["value"]
@property @property
def value(self) -> T.Optional[int]: def value(self) -> T.Optional[str]:
type = self.context[ValueTypeCtx].value_type type = self.context[ValueTypeCtx].value_type
if not isinstance(type, Enumeration): if not isinstance(type, Enumeration):
return None return None
elif member := type.members.get(self.name): elif member := type.members.get(self.name):
return member.value return member.nick
else: else:
return None return None

View file

@ -169,7 +169,7 @@ class XmlOutput(OutputFormat):
"signal", "signal",
name=name, name=name,
handler=signal.handler, handler=signal.handler,
swapped=signal.is_swapped or None, swapped=signal.is_swapped,
after=signal.is_after or None, after=signal.is_after or None,
object=( object=(
self._object_id(signal, signal.object_id) if signal.object_id else None self._object_id(signal, signal.object_id) if signal.object_id else None

View file

@ -91,7 +91,7 @@ Signal Handlers
.. rst-class:: grammar-block .. rst-class:: grammar-block
Signal = <name::ref:`IDENT<Syntax IDENT>`> ('::' <detail::ref:`IDENT<Syntax IDENT>`>)? '=>' '$' <handler::ref:`IDENT<Syntax IDENT>`> '(' <object::ref:`IDENT<Syntax IDENT>`>? ')' (SignalFlag)* ';' Signal = <name::ref:`IDENT<Syntax IDENT>`> ('::' <detail::ref:`IDENT<Syntax IDENT>`>)? '=>' '$' <handler::ref:`IDENT<Syntax IDENT>`> '(' <object::ref:`IDENT<Syntax IDENT>`>? ')' (SignalFlag)* ';'
SignalFlag = 'after' | 'swapped' SignalFlag = 'after' | 'swapped' | 'not-swapped'
Signals are one way to respond to user input (another is `actions <https://docs.gtk.org/gtk4/actions.html>`_, which use the `action-name property <https://docs.gtk.org/gtk4/property.Actionable.action-name.html>`_). Signals are one way to respond to user input (another is `actions <https://docs.gtk.org/gtk4/actions.html>`_, which use the `action-name property <https://docs.gtk.org/gtk4/property.Actionable.action-name.html>`_).
@ -99,6 +99,8 @@ Signals provide a handle for your code to listen to events in the UI. The handle
Optionally, you can provide an object ID to use when connecting the signal. Optionally, you can provide an object ID to use when connecting the signal.
The ``swapped`` flag is used to swap the order of the object and userdata arguments in C applications. If an object argument is specified, then this is the default behavior, so the ``not-swapped`` flag can be used to prevent the swap.
Example Example
~~~~~~~ ~~~~~~~
@ -108,7 +110,6 @@ Example
clicked => $on_button_clicked(); clicked => $on_button_clicked();
} }
.. _Syntax Child: .. _Syntax Child:
Children Children

View file

@ -0,0 +1,5 @@
using Gtk 4.0;
Label {
notify::
}

View file

@ -0,0 +1,2 @@
5,1,0,Expected a signal detail name
4,9,3,Unexpected tokens

View file

@ -0,0 +1,5 @@
using Gtk 4.0;
$MyObject obj {
signal1 => $handler() swapped not-swapped;
}

View file

@ -0,0 +1 @@
4,33,11,'swapped' and 'not-swapped' flags cannot be used together

View file

@ -0,0 +1,6 @@
using Gtk 4.0;
$MyObject obj {
signal1 => $handler() not-swapped;
signal2 => $handler(obj) swapped;
}

View file

@ -0,0 +1,2 @@
4,25,11,'not-swapped' is the default for handlers that do not specify an object
5,28,7,'swapped' is the default for handlers that specify an object

View file

@ -7,7 +7,7 @@ corresponding .blp file and regenerate this file with blueprint-compiler.
<interface> <interface>
<requires lib="gtk" version="4.0"/> <requires lib="gtk" version="4.0"/>
<object class="GApplication"> <object class="GApplication">
<property name="flags">1|4</property> <property name="flags">is-service|handles-open</property>
</object> </object>
<object class="GtkEventControllerScroll"> <object class="GtkEventControllerScroll">
<property name="flags">1</property> <property name="flags">1</property>

View file

@ -0,0 +1,5 @@
using Gtk 4.0;
Button obj {
clicked => $handler(obj) not-swapped;
}

View file

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
DO NOT EDIT!
This file was @generated by blueprint-compiler. Instead, edit the
corresponding .blp file and regenerate this file with blueprint-compiler.
-->
<interface>
<requires lib="gtk" version="4.0"/>
<object class="GtkButton" id="obj">
<signal name="clicked" handler="handler" swapped="False" object="obj"/>
</object>
</interface>

View file

@ -200,6 +200,7 @@ class TestSamples(unittest.TestCase):
"expr_closure_args", "expr_closure_args",
"parseable", "parseable",
"signal", "signal",
"signal_not_swapped",
"template", "template",
"template_binding", "template_binding",
"template_binding_extern", "template_binding_extern",