From f48b840cfa478d6bfeb6e16f24fd5b650c70e4f2 Mon Sep 17 00:00:00 2001 From: kotontrion Date: Wed, 20 Nov 2024 10:41:56 +0100 Subject: [PATCH 1/5] compile: fix flag values gtk builder does not support combining interger values with | in flags properties, so the short names are used instead. --- blueprintcompiler/language/values.py | 2 +- tests/samples/flags.ui | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/blueprintcompiler/language/values.py b/blueprintcompiler/language/values.py index 63cf4fc..e060b65 100644 --- a/blueprintcompiler/language/values.py +++ b/blueprintcompiler/language/values.py @@ -216,7 +216,7 @@ class Flag(AstNode): if not isinstance(type, Enumeration): return None elif member := type.members.get(self.name): - return member.value + return member.name else: return None diff --git a/tests/samples/flags.ui b/tests/samples/flags.ui index 2f0a26e..d13c424 100644 --- a/tests/samples/flags.ui +++ b/tests/samples/flags.ui @@ -7,7 +7,7 @@ corresponding .blp file and regenerate this file with blueprint-compiler. - 1|4 + is_service|handles_open 1 From 2ae41020abfb04649e9791f57c84fc708d428283 Mon Sep 17 00:00:00 2001 From: kotontrion Date: Thu, 21 Nov 2024 09:28:40 +0100 Subject: [PATCH 2/5] Fix flag return value type --- blueprintcompiler/language/values.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blueprintcompiler/language/values.py b/blueprintcompiler/language/values.py index e060b65..4cd600d 100644 --- a/blueprintcompiler/language/values.py +++ b/blueprintcompiler/language/values.py @@ -211,7 +211,7 @@ class Flag(AstNode): return self.tokens["value"] @property - def value(self) -> T.Optional[int]: + def value(self) -> T.Optional[str]: type = self.context[ValueTypeCtx].value_type if not isinstance(type, Enumeration): return None From e07da3c33946e7ab4afed9c564a9e7ae0b3fbbb8 Mon Sep 17 00:00:00 2001 From: kotontrion Date: Tue, 26 Nov 2024 10:52:37 +0100 Subject: [PATCH 3/5] flags: use nick instead of name --- blueprintcompiler/language/values.py | 2 +- tests/samples/flags.ui | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/blueprintcompiler/language/values.py b/blueprintcompiler/language/values.py index 4cd600d..96787ee 100644 --- a/blueprintcompiler/language/values.py +++ b/blueprintcompiler/language/values.py @@ -216,7 +216,7 @@ class Flag(AstNode): if not isinstance(type, Enumeration): return None elif member := type.members.get(self.name): - return member.name + return member.nick else: return None diff --git a/tests/samples/flags.ui b/tests/samples/flags.ui index d13c424..44eb2c4 100644 --- a/tests/samples/flags.ui +++ b/tests/samples/flags.ui @@ -7,7 +7,7 @@ corresponding .blp file and regenerate this file with blueprint-compiler. - is_service|handles_open + is-service|handles-open 1 From a6d57cebecaa148a8076521746d7850fc6c872b9 Mon Sep 17 00:00:00 2001 From: James Westman Date: Mon, 9 Dec 2024 20:29:08 -0600 Subject: [PATCH 4/5] language: Add not-swapped flag for signals This is needed because GtkBuilder defaults to swapped when you specify the object attribute. --- blueprintcompiler/language/gobject_signal.py | 41 ++++++++++++++++--- blueprintcompiler/outputs/xml/__init__.py | 2 +- docs/reference/objects.rst | 5 ++- .../sample_errors/signal_exclusive_flags.blp | 5 +++ .../sample_errors/signal_exclusive_flags.err | 1 + .../signal_unnecessary_flags.blp | 6 +++ .../signal_unnecessary_flags.err | 2 + tests/samples/signal_not_swapped.blp | 5 +++ tests/samples/signal_not_swapped.ui | 12 ++++++ tests/test_samples.py | 1 + 10 files changed, 72 insertions(+), 8 deletions(-) create mode 100644 tests/sample_errors/signal_exclusive_flags.blp create mode 100644 tests/sample_errors/signal_exclusive_flags.err create mode 100644 tests/sample_errors/signal_unnecessary_flags.blp create mode 100644 tests/sample_errors/signal_unnecessary_flags.err create mode 100644 tests/samples/signal_not_swapped.blp create mode 100644 tests/samples/signal_not_swapped.ui diff --git a/blueprintcompiler/language/gobject_signal.py b/blueprintcompiler/language/gobject_signal.py index 0e0332e..b052e3c 100644 --- a/blueprintcompiler/language/gobject_signal.py +++ b/blueprintcompiler/language/gobject_signal.py @@ -27,6 +27,7 @@ from .gtkbuilder_template import Template class SignalFlag(AstNode): grammar = AnyOf( UseExact("flag", "swapped"), + UseExact("flag", "not-swapped"), UseExact("flag", "after"), ) @@ -40,6 +41,27 @@ class SignalFlag(AstNode): 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() def ref_docs(self): return get_docs_section("Syntax Signal") @@ -92,9 +114,17 @@ class Signal(AstNode): def flags(self) -> T.List[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 - def is_swapped(self) -> bool: - return any(x.flag == "swapped" for x in self.flags) + def is_swapped(self) -> T.Optional[bool]: + for flag in self.flags: + if flag.flag == "swapped": + return True + elif flag.flag == "not-swapped": + return False + return None @property def is_after(self) -> bool: @@ -194,15 +224,16 @@ class Signal(AstNode): @decompiler("signal") -def decompile_signal( - ctx, gir, name, handler, swapped="false", after="false", object=None -): +def decompile_signal(ctx, gir, name, handler, swapped=None, after="false", object=None): object_name = object or "" name = name.replace("_", "-") line = f"{name} => ${handler}({object_name})" if decompile.truthy(swapped): line += " swapped" + elif swapped is not None: + line += " not-swapped" + if decompile.truthy(after): line += " after" diff --git a/blueprintcompiler/outputs/xml/__init__.py b/blueprintcompiler/outputs/xml/__init__.py index 420f6ef..a21b6fb 100644 --- a/blueprintcompiler/outputs/xml/__init__.py +++ b/blueprintcompiler/outputs/xml/__init__.py @@ -169,7 +169,7 @@ class XmlOutput(OutputFormat): "signal", name=name, handler=signal.handler, - swapped=signal.is_swapped or None, + swapped=signal.is_swapped, after=signal.is_after or None, object=( self._object_id(signal, signal.object_id) if signal.object_id else None diff --git a/docs/reference/objects.rst b/docs/reference/objects.rst index 699db49..09f5af8 100644 --- a/docs/reference/objects.rst +++ b/docs/reference/objects.rst @@ -91,7 +91,7 @@ Signal Handlers .. rst-class:: grammar-block Signal = `> ('::' `>)? '=>' '$' `> '(' `>? ')' (SignalFlag)* ';' - SignalFlag = 'after' | 'swapped' + SignalFlag = 'after' | 'swapped' | 'not-swapped' Signals are one way to respond to user input (another is `actions `_, which use the `action-name property `_). @@ -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. +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 ~~~~~~~ @@ -108,7 +110,6 @@ Example clicked => $on_button_clicked(); } - .. _Syntax Child: Children diff --git a/tests/sample_errors/signal_exclusive_flags.blp b/tests/sample_errors/signal_exclusive_flags.blp new file mode 100644 index 0000000..6432965 --- /dev/null +++ b/tests/sample_errors/signal_exclusive_flags.blp @@ -0,0 +1,5 @@ +using Gtk 4.0; + +$MyObject obj { + signal1 => $handler() swapped not-swapped; +} diff --git a/tests/sample_errors/signal_exclusive_flags.err b/tests/sample_errors/signal_exclusive_flags.err new file mode 100644 index 0000000..5176510 --- /dev/null +++ b/tests/sample_errors/signal_exclusive_flags.err @@ -0,0 +1 @@ +4,33,11,'swapped' and 'not-swapped' flags cannot be used together \ No newline at end of file diff --git a/tests/sample_errors/signal_unnecessary_flags.blp b/tests/sample_errors/signal_unnecessary_flags.blp new file mode 100644 index 0000000..ed95a9b --- /dev/null +++ b/tests/sample_errors/signal_unnecessary_flags.blp @@ -0,0 +1,6 @@ +using Gtk 4.0; + +$MyObject obj { + signal1 => $handler() not-swapped; + signal2 => $handler(obj) swapped; +} diff --git a/tests/sample_errors/signal_unnecessary_flags.err b/tests/sample_errors/signal_unnecessary_flags.err new file mode 100644 index 0000000..7586085 --- /dev/null +++ b/tests/sample_errors/signal_unnecessary_flags.err @@ -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 \ No newline at end of file diff --git a/tests/samples/signal_not_swapped.blp b/tests/samples/signal_not_swapped.blp new file mode 100644 index 0000000..835ab17 --- /dev/null +++ b/tests/samples/signal_not_swapped.blp @@ -0,0 +1,5 @@ +using Gtk 4.0; + +Button obj { + clicked => $handler(obj) not-swapped; +} \ No newline at end of file diff --git a/tests/samples/signal_not_swapped.ui b/tests/samples/signal_not_swapped.ui new file mode 100644 index 0000000..c9dcd8e --- /dev/null +++ b/tests/samples/signal_not_swapped.ui @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/tests/test_samples.py b/tests/test_samples.py index 5eb1538..866488e 100644 --- a/tests/test_samples.py +++ b/tests/test_samples.py @@ -200,6 +200,7 @@ class TestSamples(unittest.TestCase): "expr_closure_args", "parseable", "signal", + "signal_not_swapped", "template", "template_binding", "template_binding_extern", From d6f4b88d35eb6a897eb40741a92f639cecbd0acc Mon Sep 17 00:00:00 2001 From: James Westman Date: Wed, 25 Dec 2024 10:31:35 -0600 Subject: [PATCH 5/5] lsp: Fix crash on incomplete detailed signal --- blueprintcompiler/language/gobject_signal.py | 3 ++- tests/sample_errors/incomplete_signal.blp | 5 +++++ tests/sample_errors/incomplete_signal.err | 2 ++ 3 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 tests/sample_errors/incomplete_signal.blp create mode 100644 tests/sample_errors/incomplete_signal.err diff --git a/blueprintcompiler/language/gobject_signal.py b/blueprintcompiler/language/gobject_signal.py index b052e3c..9c27b97 100644 --- a/blueprintcompiler/language/gobject_signal.py +++ b/blueprintcompiler/language/gobject_signal.py @@ -143,12 +143,13 @@ class Signal(AstNode): @property def document_symbol(self) -> DocumentSymbol: + detail = self.ranges["detail_start", "detail_end"] return DocumentSymbol( self.full_name, SymbolKind.Event, self.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]: diff --git a/tests/sample_errors/incomplete_signal.blp b/tests/sample_errors/incomplete_signal.blp new file mode 100644 index 0000000..4ec693d --- /dev/null +++ b/tests/sample_errors/incomplete_signal.blp @@ -0,0 +1,5 @@ +using Gtk 4.0; + +Label { + notify:: +} diff --git a/tests/sample_errors/incomplete_signal.err b/tests/sample_errors/incomplete_signal.err new file mode 100644 index 0000000..901ef3b --- /dev/null +++ b/tests/sample_errors/incomplete_signal.err @@ -0,0 +1,2 @@ +5,1,0,Expected a signal detail name +4,9,3,Unexpected tokens \ No newline at end of file