From 24bfe2d2252d5ff879498f3f88914372463f2074 Mon Sep 17 00:00:00 2001 From: James Westman Date: Mon, 22 May 2023 20:01:15 -0500 Subject: [PATCH 001/155] Mention syntax highlighters in MAINTENANCE.md --- MAINTENANCE.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/MAINTENANCE.md b/MAINTENANCE.md index 3f62476..220c117 100644 --- a/MAINTENANCE.md +++ b/MAINTENANCE.md @@ -5,8 +5,14 @@ in the NEWS file. 2. Update the version number, according to semver: - At the top of meson.build - In docs/flatpak.rst -3. Make a new commit with just these two changes. Use `Release v{version}` -as the commit message. Tag the commit as `v{version}` and push the tag. +3. Make a new commit with just these two changes. Use `Release v{version}` as the commit message. Tag the commit as `v{version}` and push the tag. 4. Create a "Post-release version bump" commit. 5. Go to the Releases page in GitLab and create a new release from the tag. 6. Announce the release through relevant channels (Twitter, TWIG, etc.) + +## Related projects + +Blueprint is supported by the following syntax highlighters. If changes are made to the syntax, remember to update these projects as well. + +- Pygments (https://github.com/pygments/pygments/blob/master/pygments/lexers/blueprint.py) +- GtkSourceView (https://gitlab.gnome.org/GNOME/gtksourceview/-/blob/master/data/language-specs/blueprint.lang) \ No newline at end of file From 93392e5e0213c7ede0e56aa9d3d9ee568b71e99a Mon Sep 17 00:00:00 2001 From: James Westman Date: Tue, 23 May 2023 20:27:31 -0500 Subject: [PATCH 002/155] docs: Fix Extension grammar It was missing ExtAdwBreakpoint --- docs/reference/extensions.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/reference/extensions.rst b/docs/reference/extensions.rst index f7b42b1..fb5a46b 100644 --- a/docs/reference/extensions.rst +++ b/docs/reference/extensions.rst @@ -16,6 +16,7 @@ Properties are the main way to set values on objects, but they are limited by th Extension = :ref:`ExtAccessibility` | :ref:`ExtAdwMessageDialog` + | :ref:`ExtAdwBreakpoint` | :ref:`ExtComboBoxItems` | :ref:`ExtFileFilterMimeTypes` | :ref:`ExtFileFilterPatterns` From 9c567fe0392b2734bac591aad623185131985c19 Mon Sep 17 00:00:00 2001 From: seshotake Date: Mon, 5 Jun 2023 05:15:51 +0300 Subject: [PATCH 003/155] lsp: Make SemanticTokenServerCapabilities match the LSP spec SemanticTokenSeverCapabilities doesn't deserealize because legend requires a tokenModifiers array, which not provided. See: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#semanticTokensLegend --- blueprintcompiler/lsp.py | 1 + 1 file changed, 1 insertion(+) diff --git a/blueprintcompiler/lsp.py b/blueprintcompiler/lsp.py index b44d631..5f6b5a2 100644 --- a/blueprintcompiler/lsp.py +++ b/blueprintcompiler/lsp.py @@ -194,6 +194,7 @@ class LanguageServer: "semanticTokensProvider": { "legend": { "tokenTypes": ["enumMember"], + "tokenModifiers": [], }, "full": True, }, From 4e02c34a5bc68f4260d71adfa23501d75211a129 Mon Sep 17 00:00:00 2001 From: James Westman Date: Tue, 13 Jun 2023 17:39:59 -0500 Subject: [PATCH 004/155] Minor performance optimizations --- blueprintcompiler/gir.py | 24 +++++++++++++++--------- blueprintcompiler/language/ui.py | 4 +++- blueprintcompiler/typelib.py | 4 +--- 3 files changed, 19 insertions(+), 13 deletions(-) diff --git a/blueprintcompiler/gir.py b/blueprintcompiler/gir.py index e85190b..b289b5e 100644 --- a/blueprintcompiler/gir.py +++ b/blueprintcompiler/gir.py @@ -638,29 +638,35 @@ class Namespace(GirNode): def __init__(self, repo: "Repository", tl: typelib.Typelib) -> None: super().__init__(repo, tl) - self.entries: T.Dict[str, GirType] = {} + @cached_property + def entries(self) -> T.Mapping[str, GirType]: + entries: dict[str, GirType] = {} + + n_local_entries: int = self.tl.HEADER_N_ENTRIES + directory: typelib.Typelib = self.tl.HEADER_DIRECTORY + blob_size: int = self.tl.header.HEADER_ENTRY_BLOB_SIZE - n_local_entries: int = tl.HEADER_N_ENTRIES - directory: typelib.Typelib = tl.HEADER_DIRECTORY for i in range(n_local_entries): - entry = directory[i * tl.HEADER_ENTRY_BLOB_SIZE] + entry = directory[i * blob_size] entry_name: str = entry.DIR_ENTRY_NAME entry_type: int = entry.DIR_ENTRY_BLOB_TYPE entry_blob: typelib.Typelib = entry.DIR_ENTRY_OFFSET if entry_type == typelib.BLOB_TYPE_ENUM: - self.entries[entry_name] = Enumeration(self, entry_blob) + entries[entry_name] = Enumeration(self, entry_blob) elif entry_type == typelib.BLOB_TYPE_FLAGS: - self.entries[entry_name] = Bitfield(self, entry_blob) + entries[entry_name] = Bitfield(self, entry_blob) elif entry_type == typelib.BLOB_TYPE_OBJECT: - self.entries[entry_name] = Class(self, entry_blob) + entries[entry_name] = Class(self, entry_blob) elif entry_type == typelib.BLOB_TYPE_INTERFACE: - self.entries[entry_name] = Interface(self, entry_blob) + entries[entry_name] = Interface(self, entry_blob) elif ( entry_type == typelib.BLOB_TYPE_BOXED or entry_type == typelib.BLOB_TYPE_STRUCT ): - self.entries[entry_name] = Boxed(self, entry_blob) + entries[entry_name] = Boxed(self, entry_blob) + + return entries @cached_property def xml(self): diff --git a/blueprintcompiler/language/ui.py b/blueprintcompiler/language/ui.py index 033e2ca..533d21b 100644 --- a/blueprintcompiler/language/ui.py +++ b/blueprintcompiler/language/ui.py @@ -17,6 +17,8 @@ # # SPDX-License-Identifier: LGPL-3.0-or-later +from functools import cached_property + from .. import gir from .imports import GtkDirective, Import from .gtkbuilder_template import Template @@ -42,7 +44,7 @@ class UI(AstNode): ), ] - @property + @cached_property def gir(self) -> gir.GirContext: gir_ctx = gir.GirContext() self._gir_errors = [] diff --git a/blueprintcompiler/typelib.py b/blueprintcompiler/typelib.py index 6babc10..54ee341 100644 --- a/blueprintcompiler/typelib.py +++ b/blueprintcompiler/typelib.py @@ -237,9 +237,7 @@ class Typelib: if loc == 0: return None - end = loc - while self._typelib_file[end] != 0: - end += 1 + end = self._typelib_file.find(b"\0", loc) return self._typelib_file[loc:end].decode("utf-8") def _int(self, size, signed) -> int: From f526cfa4d92a02be1eeb539076e22b06bc9ab887 Mon Sep 17 00:00:00 2001 From: AkshayWarrier Date: Mon, 12 Jun 2023 22:23:30 +0530 Subject: [PATCH 005/155] lsp: Decompile empty XML docs to empty strings --- blueprintcompiler/lsp.py | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/blueprintcompiler/lsp.py b/blueprintcompiler/lsp.py index 5f6b5a2..58d6fac 100644 --- a/blueprintcompiler/lsp.py +++ b/blueprintcompiler/lsp.py @@ -292,16 +292,19 @@ class LanguageServer: def decompile(self, id, params): text = params.get("text") blp = None - - try: - blp = decompiler.decompile_string(text) - except decompiler.UnsupportedError as e: - self._send_error(id, ErrorCode.RequestFailed, e.message) - return - except: - printerr(traceback.format_exc()) - self._send_error(id, ErrorCode.RequestFailed, "Invalid input") - return + if text.strip() == "": + blp = "" + printerr("Decompiled to empty blueprint because input was empty") + else: + try: + blp = decompiler.decompile_string(text) + except decompiler.UnsupportedError as e: + self._send_error(id, ErrorCode.RequestFailed, e.message) + return + except: + printerr(traceback.format_exc()) + self._send_error(id, ErrorCode.RequestFailed, "Invalid input") + return self._send_response(id, {"blp": blp}) From 3730e2e726b514c738aea472eb3849d5a8607bb3 Mon Sep 17 00:00:00 2001 From: gregorni Date: Sun, 9 Jul 2023 14:26:37 +0000 Subject: [PATCH 006/155] Add isort to CI and run on files --- .gitlab-ci.yml | 1 + blueprint-compiler.py | 3 +- blueprintcompiler/ast_utils.py | 2 +- blueprintcompiler/completions.py | 4 +-- blueprintcompiler/completions_utils.py | 3 +- blueprintcompiler/decompiler.py | 5 ++- blueprintcompiler/errors.py | 6 ++-- blueprintcompiler/gir.py | 7 +++-- blueprintcompiler/interactive_port.py | 7 ++--- blueprintcompiler/language/__init__.py | 23 +++++++------- .../language/adw_message_dialog.py | 2 +- blueprintcompiler/language/binding.py | 2 +- blueprintcompiler/language/common.py | 31 +++++++++---------- blueprintcompiler/language/contexts.py | 2 +- blueprintcompiler/language/expression.py | 3 +- blueprintcompiler/language/gobject_object.py | 1 - .../language/gobject_property.py | 6 ++-- blueprintcompiler/language/gobject_signal.py | 4 +-- blueprintcompiler/language/gtk_a11y.py | 6 ++-- .../language/gtk_combo_box_text.py | 2 +- blueprintcompiler/language/gtk_file_filter.py | 2 +- blueprintcompiler/language/gtk_layout.py | 2 +- .../language/gtk_list_item_factory.py | 6 ++-- blueprintcompiler/language/gtk_scale.py | 2 +- blueprintcompiler/language/gtk_size_group.py | 2 +- blueprintcompiler/language/gtk_string_list.py | 2 +- blueprintcompiler/language/gtk_styles.py | 2 +- .../language/gtkbuilder_child.py | 2 +- .../language/gtkbuilder_template.py | 4 +-- blueprintcompiler/language/types.py | 2 +- blueprintcompiler/language/ui.py | 8 ++--- blueprintcompiler/language/values.py | 4 +-- blueprintcompiler/lsp.py | 8 +++-- blueprintcompiler/lsp_utils.py | 2 +- blueprintcompiler/main.py | 11 ++++--- blueprintcompiler/outputs/xml/__init__.py | 2 +- blueprintcompiler/outputs/xml/xml_emitter.py | 1 - blueprintcompiler/parse_tree.py | 8 ++--- blueprintcompiler/parser.py | 2 +- blueprintcompiler/tokenizer.py | 2 +- blueprintcompiler/typelib.py | 6 ++-- blueprintcompiler/xml_reader.py | 3 +- build-aux/Dockerfile | 2 +- tests/fuzz.py | 11 ++++--- tests/test_samples.py | 9 +++--- 45 files changed, 112 insertions(+), 113 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 93ef254..7e9ee9c 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -7,6 +7,7 @@ build: stage: build script: - black --check --diff blueprintcompiler tests + - isort --check --diff --profile black blueprintcompiler tests - mypy --python-version=3.9 blueprintcompiler - G_DEBUG=fatal-warnings xvfb-run coverage run -m unittest - coverage report diff --git a/blueprint-compiler.py b/blueprint-compiler.py index 61eae79..0c5c3fd 100755 --- a/blueprint-compiler.py +++ b/blueprint-compiler.py @@ -19,7 +19,8 @@ # # SPDX-License-Identifier: LGPL-3.0-or-later -import os, sys +import os +import sys # These variables should be set by meson. If they aren't, we're running # uninstalled, and we might have to guess some values. diff --git a/blueprintcompiler/ast_utils.py b/blueprintcompiler/ast_utils.py index 13f6eb1..7eebe45 100644 --- a/blueprintcompiler/ast_utils.py +++ b/blueprintcompiler/ast_utils.py @@ -17,9 +17,9 @@ # # SPDX-License-Identifier: LGPL-3.0-or-later +import typing as T from collections import ChainMap, defaultdict from functools import cached_property -import typing as T from .errors import * from .lsp_utils import SemanticToken diff --git a/blueprintcompiler/completions.py b/blueprintcompiler/completions.py index fc6eeee..b189bf1 100644 --- a/blueprintcompiler/completions.py +++ b/blueprintcompiler/completions.py @@ -20,12 +20,12 @@ import typing as T from . import gir, language -from .language.types import ClassName from .ast_utils import AstNode from .completions_utils import * +from .language.types import ClassName from .lsp_utils import Completion, CompletionItemKind from .parser import SKIP_TOKENS -from .tokenizer import TokenType, Token +from .tokenizer import Token, TokenType Pattern = T.List[T.Tuple[TokenType, T.Optional[str]]] diff --git a/blueprintcompiler/completions_utils.py b/blueprintcompiler/completions_utils.py index 094e449..2aae874 100644 --- a/blueprintcompiler/completions_utils.py +++ b/blueprintcompiler/completions_utils.py @@ -20,9 +20,8 @@ import typing as T -from .tokenizer import Token, TokenType from .lsp_utils import Completion - +from .tokenizer import Token, TokenType new_statement_patterns = [ [(TokenType.PUNCTUATION, "{")], diff --git a/blueprintcompiler/decompiler.py b/blueprintcompiler/decompiler.py index d3bf773..98e00af 100644 --- a/blueprintcompiler/decompiler.py +++ b/blueprintcompiler/decompiler.py @@ -18,14 +18,13 @@ # SPDX-License-Identifier: LGPL-3.0-or-later import re -from enum import Enum import typing as T from dataclasses import dataclass +from enum import Enum -from .xml_reader import Element, parse, parse_string from .gir import * from .utils import Colors - +from .xml_reader import Element, parse, parse_string __all__ = ["decompile"] diff --git a/blueprintcompiler/errors.py b/blueprintcompiler/errors.py index 3fc2666..2a14040 100644 --- a/blueprintcompiler/errors.py +++ b/blueprintcompiler/errors.py @@ -17,9 +17,11 @@ # # SPDX-License-Identifier: LGPL-3.0-or-later -from dataclasses import dataclass +import sys +import traceback import typing as T -import sys, traceback +from dataclasses import dataclass + from . import utils from .utils import Colors diff --git a/blueprintcompiler/gir.py b/blueprintcompiler/gir.py index b289b5e..2986447 100644 --- a/blueprintcompiler/gir.py +++ b/blueprintcompiler/gir.py @@ -17,17 +17,18 @@ # # SPDX-License-Identifier: LGPL-3.0-or-later -from functools import cached_property +import os +import sys import typing as T -import os, sys +from functools import cached_property import gi # type: ignore gi.require_version("GIRepository", "2.0") from gi.repository import GIRepository # type: ignore -from .errors import CompileError, CompilerBugError from . import typelib, xml_reader +from .errors import CompileError, CompilerBugError _namespace_cache: T.Dict[str, "Namespace"] = {} _xml_cache = {} diff --git a/blueprintcompiler/interactive_port.py b/blueprintcompiler/interactive_port.py index 1286f0b..43bb03f 100644 --- a/blueprintcompiler/interactive_port.py +++ b/blueprintcompiler/interactive_port.py @@ -18,16 +18,15 @@ # SPDX-License-Identifier: LGPL-3.0-or-later -import typing as T import difflib import os +import typing as T -from . import decompiler, tokenizer, parser +from . import decompiler, parser, tokenizer +from .errors import CompilerBugError, MultipleErrors, PrintableError from .outputs.xml import XmlOutput -from .errors import MultipleErrors, PrintableError, CompilerBugError from .utils import Colors - # A tool to interactively port projects to blueprints. diff --git a/blueprintcompiler/language/__init__.py b/blueprintcompiler/language/__init__.py index d785e56..f849c75 100644 --- a/blueprintcompiler/language/__init__.py +++ b/blueprintcompiler/language/__init__.py @@ -1,12 +1,12 @@ -from .gtk_list_item_factory import ExtListItemFactory +from .adw_breakpoint import ( + AdwBreakpointCondition, + AdwBreakpointSetter, + AdwBreakpointSetters, +) from .adw_message_dialog import ExtAdwMessageDialog from .attributes import BaseAttribute -from .adw_breakpoint import ( - AdwBreakpointSetters, - AdwBreakpointSetter, - AdwBreakpointCondition, -) from .binding import Binding +from .common import * from .contexts import ScopeCtx, ValueTypeCtx from .expression import ( CastExpr, @@ -23,23 +23,24 @@ from .gobject_signal import Signal from .gtk_a11y import ExtAccessibility from .gtk_combo_box_text import ExtComboBoxItems from .gtk_file_filter import ( + Filters, ext_file_filter_mime_types, ext_file_filter_patterns, ext_file_filter_suffixes, - Filters, ) from .gtk_layout import ExtLayout -from .gtk_menu import menu, Menu, MenuAttribute +from .gtk_list_item_factory import ExtListItemFactory +from .gtk_menu import Menu, MenuAttribute, menu 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, ChildType, ChildInternal, ChildExtension +from .gtkbuilder_child import Child, ChildExtension, ChildInternal, ChildType from .gtkbuilder_template import Template from .imports import GtkDirective, Import from .property_binding import PropertyBinding -from .ui import UI from .types import ClassName +from .ui import UI from .values import ( Flag, Flags, @@ -54,8 +55,6 @@ from .values import ( Value, ) -from .common import * - OBJECT_CONTENT_HOOKS.children = [ Signal, Property, diff --git a/blueprintcompiler/language/adw_message_dialog.py b/blueprintcompiler/language/adw_message_dialog.py index 2823f77..98c40cd 100644 --- a/blueprintcompiler/language/adw_message_dialog.py +++ b/blueprintcompiler/language/adw_message_dialog.py @@ -18,7 +18,7 @@ # SPDX-License-Identifier: LGPL-3.0-or-later -from ..decompiler import truthy, decompile_translatable +from ..decompiler import decompile_translatable, truthy from .common import * from .contexts import ValueTypeCtx from .gobject_object import ObjectContent, validate_parent_type diff --git a/blueprintcompiler/language/binding.py b/blueprintcompiler/language/binding.py index b13d5da..e6bffd0 100644 --- a/blueprintcompiler/language/binding.py +++ b/blueprintcompiler/language/binding.py @@ -20,7 +20,7 @@ from dataclasses import dataclass from .common import * -from .expression import Expression, LookupOp, LiteralExpr +from .expression import Expression, LiteralExpr, LookupOp class Binding(AstNode): diff --git a/blueprintcompiler/language/common.py b/blueprintcompiler/language/common.py index 9938bec..f1c6bb9 100644 --- a/blueprintcompiler/language/common.py +++ b/blueprintcompiler/language/common.py @@ -18,36 +18,35 @@ # SPDX-License-Identifier: LGPL-3.0-or-later -from .. import gir -from ..ast_utils import AstNode, validate, docs, context -from ..errors import ( - CompileError, - MultipleErrors, - UpgradeWarning, - CompileWarning, - CodeAction, -) -from ..completions_utils import * from .. import decompiler as decompile +from .. import gir +from ..ast_utils import AstNode, context, docs, validate +from ..completions_utils import * from ..decompiler import ( DecompileCtx, + decompile_translatable, decompiler, escape_quote, truthy, - decompile_translatable, +) +from ..errors import ( + CodeAction, + CompileError, + CompileWarning, + MultipleErrors, + UpgradeWarning, ) from ..gir import ( - StringType, BoolType, - IntType, - FloatType, - GirType, Enumeration, ExternType, + FloatType, + GirType, + IntType, + StringType, ) from ..lsp_utils import Completion, CompletionItemKind, SemanticToken, SemanticTokenType from ..parse_tree import * - OBJECT_CONTENT_HOOKS = AnyOf() LITERAL = AnyOf() diff --git a/blueprintcompiler/language/contexts.py b/blueprintcompiler/language/contexts.py index 29d95de..fb24f5a 100644 --- a/blueprintcompiler/language/contexts.py +++ b/blueprintcompiler/language/contexts.py @@ -38,8 +38,8 @@ class ScopeCtx: @cached_property def template(self): - from .ui import UI from .gtk_list_item_factory import ExtListItemFactory + from .ui import UI if isinstance(self.node, UI): return self.node.template diff --git a/blueprintcompiler/language/expression.py b/blueprintcompiler/language/expression.py index 314c753..a2185a3 100644 --- a/blueprintcompiler/language/expression.py +++ b/blueprintcompiler/language/expression.py @@ -20,9 +20,8 @@ from .common import * from .contexts import ScopeCtx, ValueTypeCtx -from .types import TypeName from .gtkbuilder_template import Template - +from .types import TypeName expr = Sequence() diff --git a/blueprintcompiler/language/gobject_object.py b/blueprintcompiler/language/gobject_object.py index 183ad8e..d247c61 100644 --- a/blueprintcompiler/language/gobject_object.py +++ b/blueprintcompiler/language/gobject_object.py @@ -25,7 +25,6 @@ from .common import * from .response_id import ExtResponse from .types import ClassName, ConcreteClassName - RESERVED_IDS = {"this", "self", "template", "true", "false", "null", "none"} diff --git a/blueprintcompiler/language/gobject_property.py b/blueprintcompiler/language/gobject_property.py index 09873bc..f65c5ca 100644 --- a/blueprintcompiler/language/gobject_property.py +++ b/blueprintcompiler/language/gobject_property.py @@ -18,12 +18,12 @@ # SPDX-License-Identifier: LGPL-3.0-or-later -from .gtkbuilder_template import Template -from .values import Value, ObjectValue +from .binding import Binding from .common import * from .contexts import ValueTypeCtx +from .gtkbuilder_template import Template from .property_binding import PropertyBinding -from .binding import Binding +from .values import ObjectValue, Value class Property(AstNode): diff --git a/blueprintcompiler/language/gobject_signal.py b/blueprintcompiler/language/gobject_signal.py index f08168f..2fc4699 100644 --- a/blueprintcompiler/language/gobject_signal.py +++ b/blueprintcompiler/language/gobject_signal.py @@ -19,9 +19,9 @@ import typing as T -from .gtkbuilder_template import Template -from .contexts import ScopeCtx from .common import * +from .contexts import ScopeCtx +from .gtkbuilder_template import Template class SignalFlag(AstNode): diff --git a/blueprintcompiler/language/gtk_a11y.py b/blueprintcompiler/language/gtk_a11y.py index c378927..f7f89d6 100644 --- a/blueprintcompiler/language/gtk_a11y.py +++ b/blueprintcompiler/language/gtk_a11y.py @@ -17,12 +17,12 @@ # # SPDX-License-Identifier: LGPL-3.0-or-later -from .gobject_object import ObjectContent, validate_parent_type +from ..decompiler import escape_quote from .attributes import BaseAttribute -from .values import Value from .common import * from .contexts import ValueTypeCtx -from ..decompiler import escape_quote +from .gobject_object import ObjectContent, validate_parent_type +from .values import Value def get_property_types(gir): diff --git a/blueprintcompiler/language/gtk_combo_box_text.py b/blueprintcompiler/language/gtk_combo_box_text.py index 275327a..e6c804e 100644 --- a/blueprintcompiler/language/gtk_combo_box_text.py +++ b/blueprintcompiler/language/gtk_combo_box_text.py @@ -18,9 +18,9 @@ # SPDX-License-Identifier: LGPL-3.0-or-later -from .gobject_object import ObjectContent, validate_parent_type from .common import * from .contexts import ValueTypeCtx +from .gobject_object import ObjectContent, validate_parent_type from .values import StringValue diff --git a/blueprintcompiler/language/gtk_file_filter.py b/blueprintcompiler/language/gtk_file_filter.py index 492f19e..53cd102 100644 --- a/blueprintcompiler/language/gtk_file_filter.py +++ b/blueprintcompiler/language/gtk_file_filter.py @@ -18,8 +18,8 @@ # SPDX-License-Identifier: LGPL-3.0-or-later -from .gobject_object import ObjectContent, validate_parent_type from .common import * +from .gobject_object import ObjectContent, validate_parent_type class Filters(AstNode): diff --git a/blueprintcompiler/language/gtk_layout.py b/blueprintcompiler/language/gtk_layout.py index cceb6c6..7632c7a 100644 --- a/blueprintcompiler/language/gtk_layout.py +++ b/blueprintcompiler/language/gtk_layout.py @@ -18,9 +18,9 @@ # SPDX-License-Identifier: LGPL-3.0-or-later -from .gobject_object import ObjectContent, validate_parent_type from .common import * from .contexts import ValueTypeCtx +from .gobject_object import ObjectContent, validate_parent_type from .values import Value diff --git a/blueprintcompiler/language/gtk_list_item_factory.py b/blueprintcompiler/language/gtk_list_item_factory.py index bbb3bda..f2f9d63 100644 --- a/blueprintcompiler/language/gtk_list_item_factory.py +++ b/blueprintcompiler/language/gtk_list_item_factory.py @@ -1,9 +1,9 @@ -from .gobject_object import ObjectContent, validate_parent_type -from ..parse_tree import Keyword from ..ast_utils import AstNode, validate +from ..parse_tree import Keyword from .common import * -from .types import TypeName from .contexts import ScopeCtx +from .gobject_object import ObjectContent, validate_parent_type +from .types import TypeName class ExtListItemFactory(AstNode): diff --git a/blueprintcompiler/language/gtk_scale.py b/blueprintcompiler/language/gtk_scale.py index a81e03d..18452e1 100644 --- a/blueprintcompiler/language/gtk_scale.py +++ b/blueprintcompiler/language/gtk_scale.py @@ -17,8 +17,8 @@ # # SPDX-License-Identifier: LGPL-3.0-or-later -from .gobject_object import validate_parent_type, ObjectContent from .common import * +from .gobject_object import ObjectContent, validate_parent_type from .values import StringValue diff --git a/blueprintcompiler/language/gtk_size_group.py b/blueprintcompiler/language/gtk_size_group.py index 5ba4325..2a10a35 100644 --- a/blueprintcompiler/language/gtk_size_group.py +++ b/blueprintcompiler/language/gtk_size_group.py @@ -18,9 +18,9 @@ # SPDX-License-Identifier: LGPL-3.0-or-later -from .gobject_object import ObjectContent, validate_parent_type from .common import * from .contexts import ScopeCtx +from .gobject_object import ObjectContent, validate_parent_type class Widget(AstNode): diff --git a/blueprintcompiler/language/gtk_string_list.py b/blueprintcompiler/language/gtk_string_list.py index 78d9c67..455960e 100644 --- a/blueprintcompiler/language/gtk_string_list.py +++ b/blueprintcompiler/language/gtk_string_list.py @@ -18,9 +18,9 @@ # SPDX-License-Identifier: LGPL-3.0-or-later +from .common import * from .gobject_object import ObjectContent, validate_parent_type from .values import StringValue -from .common import * class Item(AstNode): diff --git a/blueprintcompiler/language/gtk_styles.py b/blueprintcompiler/language/gtk_styles.py index 6591aa5..8152b82 100644 --- a/blueprintcompiler/language/gtk_styles.py +++ b/blueprintcompiler/language/gtk_styles.py @@ -18,8 +18,8 @@ # SPDX-License-Identifier: LGPL-3.0-or-later -from .gobject_object import ObjectContent, validate_parent_type from .common import * +from .gobject_object import ObjectContent, validate_parent_type class StyleClass(AstNode): diff --git a/blueprintcompiler/language/gtkbuilder_child.py b/blueprintcompiler/language/gtkbuilder_child.py index 4de13f2..52c8ad4 100644 --- a/blueprintcompiler/language/gtkbuilder_child.py +++ b/blueprintcompiler/language/gtkbuilder_child.py @@ -20,9 +20,9 @@ from functools import cached_property +from .common import * from .gobject_object import Object from .response_id import ExtResponse -from .common import * ALLOWED_PARENTS: T.List[T.Tuple[str, str]] = [ ("Gtk", "Buildable"), diff --git a/blueprintcompiler/language/gtkbuilder_template.py b/blueprintcompiler/language/gtkbuilder_template.py index 149152a..9de4d66 100644 --- a/blueprintcompiler/language/gtkbuilder_template.py +++ b/blueprintcompiler/language/gtkbuilder_template.py @@ -21,9 +21,9 @@ import typing as T from blueprintcompiler.language.common import GirType -from .gobject_object import Object, ObjectContent -from .common import * from ..gir import TemplateType +from .common import * +from .gobject_object import Object, ObjectContent from .types import ClassName, TemplateClassName diff --git a/blueprintcompiler/language/types.py b/blueprintcompiler/language/types.py index dbce44f..e75ebdc 100644 --- a/blueprintcompiler/language/types.py +++ b/blueprintcompiler/language/types.py @@ -18,8 +18,8 @@ # SPDX-License-Identifier: LGPL-3.0-or-later -from .common import * from ..gir import Class, ExternType, Interface +from .common import * class TypeName(AstNode): diff --git a/blueprintcompiler/language/ui.py b/blueprintcompiler/language/ui.py index 533d21b..1b7e6e9 100644 --- a/blueprintcompiler/language/ui.py +++ b/blueprintcompiler/language/ui.py @@ -20,12 +20,12 @@ from functools import cached_property from .. import gir -from .imports import GtkDirective, Import -from .gtkbuilder_template import Template -from .gobject_object import Object -from .gtk_menu import menu, Menu from .common import * from .contexts import ScopeCtx +from .gobject_object import Object +from .gtk_menu import Menu, menu +from .gtkbuilder_template import Template +from .imports import GtkDirective, Import class UI(AstNode): diff --git a/blueprintcompiler/language/values.py b/blueprintcompiler/language/values.py index d0f3be5..6981141 100644 --- a/blueprintcompiler/language/values.py +++ b/blueprintcompiler/language/values.py @@ -20,9 +20,9 @@ import typing as T from .common import * -from .types import TypeName -from .gobject_object import Object from .contexts import ScopeCtx, ValueTypeCtx +from .gobject_object import Object +from .types import TypeName class Translated(AstNode): diff --git a/blueprintcompiler/lsp.py b/blueprintcompiler/lsp.py index 58d6fac..e8c37f3 100644 --- a/blueprintcompiler/lsp.py +++ b/blueprintcompiler/lsp.py @@ -18,14 +18,16 @@ # SPDX-License-Identifier: LGPL-3.0-or-later +import json +import sys +import traceback import typing as T -import json, sys, traceback +from . import decompiler, parser, tokenizer, utils, xml_reader from .completions import complete -from .errors import PrintableError, CompileError, MultipleErrors +from .errors import CompileError, MultipleErrors, PrintableError from .lsp_utils import * from .outputs.xml import XmlOutput -from . import tokenizer, parser, utils, xml_reader, decompiler def printerr(*args, **kwargs): diff --git a/blueprintcompiler/lsp_utils.py b/blueprintcompiler/lsp_utils.py index 219cade..7f46680 100644 --- a/blueprintcompiler/lsp_utils.py +++ b/blueprintcompiler/lsp_utils.py @@ -18,9 +18,9 @@ # SPDX-License-Identifier: LGPL-3.0-or-later -from dataclasses import dataclass import enum import typing as T +from dataclasses import dataclass from .errors import * from .utils import * diff --git a/blueprintcompiler/main.py b/blueprintcompiler/main.py index 6ac7a11..db9fb65 100644 --- a/blueprintcompiler/main.py +++ b/blueprintcompiler/main.py @@ -18,15 +18,18 @@ # SPDX-License-Identifier: LGPL-3.0-or-later +import argparse +import json +import os +import sys import typing as T -import argparse, json, os, sys -from .errors import PrintableError, report_bug, MultipleErrors, CompilerBugError +from . import decompiler, interactive_port, parser, tokenizer +from .errors import CompilerBugError, MultipleErrors, PrintableError, report_bug from .gir import add_typelib_search_path from .lsp import LanguageServer -from . import parser, tokenizer, decompiler, interactive_port -from .utils import Colors from .outputs import XmlOutput +from .utils import Colors VERSION = "uninstalled" LIBDIR = None diff --git a/blueprintcompiler/outputs/xml/__init__.py b/blueprintcompiler/outputs/xml/__init__.py index 7d450d1..3ef7375 100644 --- a/blueprintcompiler/outputs/xml/__init__.py +++ b/blueprintcompiler/outputs/xml/__init__.py @@ -1,7 +1,7 @@ import typing as T -from .. import OutputFormat from ...language import * +from .. import OutputFormat from .xml_emitter import XmlEmitter diff --git a/blueprintcompiler/outputs/xml/xml_emitter.py b/blueprintcompiler/outputs/xml/xml_emitter.py index 3dd09a0..44013da 100644 --- a/blueprintcompiler/outputs/xml/xml_emitter.py +++ b/blueprintcompiler/outputs/xml/xml_emitter.py @@ -18,7 +18,6 @@ # SPDX-License-Identifier: LGPL-3.0-or-later import typing as T - from xml.sax import saxutils from blueprintcompiler.gir import GirType diff --git a/blueprintcompiler/parse_tree.py b/blueprintcompiler/parse_tree.py index ff080ea..48f60b1 100644 --- a/blueprintcompiler/parse_tree.py +++ b/blueprintcompiler/parse_tree.py @@ -20,21 +20,19 @@ """ Utilities for parsing an AST from a token stream. """ import typing as T - from collections import defaultdict from enum import Enum -from .ast_utils import AstNode +from .ast_utils import AstNode from .errors import ( - assert_true, - CompilerBugError, CompileError, + CompilerBugError, CompileWarning, UnexpectedTokenError, + assert_true, ) from .tokenizer import Token, TokenType - SKIP_TOKENS = [TokenType.COMMENT, TokenType.WHITESPACE] diff --git a/blueprintcompiler/parser.py b/blueprintcompiler/parser.py index edef840..8b50de5 100644 --- a/blueprintcompiler/parser.py +++ b/blueprintcompiler/parser.py @@ -19,9 +19,9 @@ from .errors import MultipleErrors, PrintableError +from .language import OBJECT_CONTENT_HOOKS, UI, Template from .parse_tree import * from .tokenizer import TokenType -from .language import OBJECT_CONTENT_HOOKS, Template, UI def parse( diff --git a/blueprintcompiler/tokenizer.py b/blueprintcompiler/tokenizer.py index f68f5a7..3c98b94 100644 --- a/blueprintcompiler/tokenizer.py +++ b/blueprintcompiler/tokenizer.py @@ -18,8 +18,8 @@ # SPDX-License-Identifier: LGPL-3.0-or-later -import typing as T import re +import typing as T from enum import Enum from .errors import CompileError, CompilerBugError diff --git a/blueprintcompiler/typelib.py b/blueprintcompiler/typelib.py index 54ee341..8de06c5 100644 --- a/blueprintcompiler/typelib.py +++ b/blueprintcompiler/typelib.py @@ -17,15 +17,15 @@ # # SPDX-License-Identifier: LGPL-3.0-or-later +import math +import mmap +import os import sys import typing as T -import math from ctypes import * -import mmap, os from .errors import CompilerBugError - BLOB_TYPE_STRUCT = 3 BLOB_TYPE_BOXED = 4 BLOB_TYPE_ENUM = 5 diff --git a/blueprintcompiler/xml_reader.py b/blueprintcompiler/xml_reader.py index b2d579b..a3c0e3e 100644 --- a/blueprintcompiler/xml_reader.py +++ b/blueprintcompiler/xml_reader.py @@ -18,12 +18,11 @@ # SPDX-License-Identifier: LGPL-3.0-or-later +import typing as T from collections import defaultdict from functools import cached_property -import typing as T from xml import sax - # To speed up parsing, we ignore all tags except these PARSE_GIR = set( [ diff --git a/build-aux/Dockerfile b/build-aux/Dockerfile index b060765..aa26f43 100644 --- a/build-aux/Dockerfile +++ b/build-aux/Dockerfile @@ -4,7 +4,7 @@ RUN dnf install -y meson gcc g++ python3-pip gobject-introspection-devel \ python3-devel python3-gobject git diffutils xorg-x11-server-Xvfb \ appstream-devel "dnf-command(builddep)" RUN dnf build-dep -y gtk4 libadwaita -RUN pip3 install furo mypy sphinx coverage black +RUN pip3 install furo mypy sphinx coverage black isort COPY install_deps.sh . RUN ./install_deps.sh diff --git a/tests/fuzz.py b/tests/fuzz.py index ad1c764..81a9058 100644 --- a/tests/fuzz.py +++ b/tests/fuzz.py @@ -1,20 +1,21 @@ -import os, sys +import os +import sys + from pythonfuzz.main import PythonFuzz from blueprintcompiler.outputs.xml import XmlOutput sys.path.insert(0, os.path.dirname(os.path.dirname(__file__))) -from blueprintcompiler import tokenizer, parser, decompiler, gir +from blueprintcompiler import decompiler, gir, parser, tokenizer, utils from blueprintcompiler.completions import complete from blueprintcompiler.errors import ( - PrintableError, - MultipleErrors, CompileError, CompilerBugError, + MultipleErrors, + PrintableError, ) from blueprintcompiler.tokenizer import Token, TokenType, tokenize -from blueprintcompiler import utils @PythonFuzz diff --git a/tests/test_samples.py b/tests/test_samples.py index 30a41aa..7fe7941 100644 --- a/tests/test_samples.py +++ b/tests/test_samples.py @@ -19,20 +19,19 @@ import difflib # I love Python -from pathlib import Path import unittest +from pathlib import Path import gi gi.require_version("Gtk", "4.0") from gi.repository import Gtk -from blueprintcompiler import tokenizer, parser, decompiler +from blueprintcompiler import decompiler, parser, tokenizer, utils from blueprintcompiler.completions import complete -from blueprintcompiler.errors import PrintableError, MultipleErrors, CompileError -from blueprintcompiler.tokenizer import Token, TokenType, tokenize -from blueprintcompiler import utils +from blueprintcompiler.errors import CompileError, MultipleErrors, PrintableError from blueprintcompiler.outputs.xml import XmlOutput +from blueprintcompiler.tokenizer import Token, TokenType, tokenize class TestSamples(unittest.TestCase): From 3d79f9560c82b5060f9303b1b1893c0b115b18c7 Mon Sep 17 00:00:00 2001 From: James Westman Date: Sat, 15 Jul 2023 17:34:04 -0500 Subject: [PATCH 007/155] ci: Fix Dockerfile --- build-aux/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build-aux/Dockerfile b/build-aux/Dockerfile index aa26f43..d7093ff 100644 --- a/build-aux/Dockerfile +++ b/build-aux/Dockerfile @@ -2,7 +2,7 @@ FROM fedora:latest RUN dnf install -y meson gcc g++ python3-pip gobject-introspection-devel \ python3-devel python3-gobject git diffutils xorg-x11-server-Xvfb \ - appstream-devel "dnf-command(builddep)" + appstream-devel dbus-x11 "dnf-command(builddep)" RUN dnf build-dep -y gtk4 libadwaita RUN pip3 install furo mypy sphinx coverage black isort From 4eaf735732561e4fbcc0739669d47a62e77a6fed Mon Sep 17 00:00:00 2001 From: James Westman Date: Thu, 6 Jul 2023 20:13:49 -0500 Subject: [PATCH 008/155] gir: Fix signatures for properties and signals Add arguments to signal signatures and fix property signatures --- blueprintcompiler/gir.py | 49 ++++++++++++++++++++++++++++-------- blueprintcompiler/typelib.py | 13 ++++++++++ 2 files changed, 52 insertions(+), 10 deletions(-) diff --git a/blueprintcompiler/gir.py b/blueprintcompiler/gir.py index 2986447..6e37626 100644 --- a/blueprintcompiler/gir.py +++ b/blueprintcompiler/gir.py @@ -318,7 +318,7 @@ class Property(GirNode): @cached_property def signature(self): - return f"{self.full_name} {self.container.name}.{self.name}" + return f"{self.type.full_name} {self.container.name}:{self.name}" @property def writable(self) -> bool: @@ -329,27 +329,56 @@ class Property(GirNode): return self.tl.PROP_CONSTRUCT_ONLY == 1 -class Parameter(GirNode): +class Argument(GirNode): def __init__(self, container: GirNode, tl: typelib.Typelib) -> None: super().__init__(container, tl) + @cached_property + def name(self) -> str: + return self.tl.ARG_NAME + + @cached_property + def type(self) -> GirType: + return self.get_containing(Repository)._resolve_type_id(self.tl.ARG_TYPE) + + +class Signature(GirNode): + def __init__(self, container: GirNode, tl: typelib.Typelib) -> None: + super().__init__(container, tl) + + @cached_property + def args(self) -> T.List[Argument]: + n_arguments = self.tl.SIGNATURE_N_ARGUMENTS + blob_size = self.tl.header.HEADER_ARG_BLOB_SIZE + result = [] + for i in range(n_arguments): + entry = self.tl.SIGNATURE_ARGUMENTS[i * blob_size] + result.append(Argument(self, entry)) + return result + + @cached_property + def return_type(self) -> GirType: + return self.get_containing(Repository)._resolve_type_id( + self.tl.SIGNATURE_RETURN_TYPE + ) + class Signal(GirNode): def __init__( self, klass: T.Union["Class", "Interface"], tl: typelib.Typelib ) -> None: super().__init__(klass, tl) - # if parameters := xml.get_elements('parameters'): - # self.params = [Parameter(self, child) for child in parameters[0].get_elements('parameter')] - # else: - # self.params = [] + + @cached_property + def gir_signature(self) -> Signature: + return Signature(self, self.tl.SIGNAL_SIGNATURE) @property def signature(self): - # TODO: fix - # args = ", ".join([f"{p.type_name} {p.name}" for p in self.params]) - args = "" - return f"signal {self.container.name}.{self.name} ({args})" + args = ", ".join( + [f"{a.type.full_name} {a.name}" for a in self.gir_signature.args] + ) + return f"signal {self.container.full_name}::{self.name} ({args})" class Interface(GirNode, GirType): diff --git a/blueprintcompiler/typelib.py b/blueprintcompiler/typelib.py index 8de06c5..2ce3e32 100644 --- a/blueprintcompiler/typelib.py +++ b/blueprintcompiler/typelib.py @@ -118,6 +118,7 @@ class Typelib: HEADER_FUNCTION_BLOB_SIZE = Field(0x3E, "u16") HEADER_CALLBACK_BLOB_SIZE = Field(0x40, "u16") HEADER_SIGNAL_BLOB_SIZE = Field(0x42, "u16") + HEADER_ARG_BLOB_SIZE = Field(0x46, "u16") HEADER_PROPERTY_BLOB_SIZE = Field(0x48, "u16") HEADER_FIELD_BLOB_SIZE = Field(0x4A, "u16") HEADER_VALUE_BLOB_SIZE = Field(0x4C, "u16") @@ -132,6 +133,13 @@ class Typelib: DIR_ENTRY_OFFSET = Field(0x8, "pointer") DIR_ENTRY_NAMESPACE = Field(0x8, "string") + ARG_NAME = Field(0x0, "string") + ARG_TYPE = Field(0xC, "u32") + + SIGNATURE_RETURN_TYPE = Field(0x0, "u32") + SIGNATURE_N_ARGUMENTS = Field(0x6, "u16") + SIGNATURE_ARGUMENTS = Field(0x8, "offset") + ATTR_OFFSET = Field(0x0, "u32") ATTR_NAME = Field(0x0, "string") ATTR_VALUE = Field(0x0, "string") @@ -180,6 +188,11 @@ class Typelib: PROP_CONSTRUCT_ONLY = Field(0x4, "u32", 4, 1) PROP_TYPE = Field(0xC, "u32") + SIGNAL_DEPRECATED = Field(0x0, "u16", 0, 1) + SIGNAL_DETAILED = Field(0x0, "u16", 5, 1) + SIGNAL_NAME = Field(0x4, "string") + SIGNAL_SIGNATURE = Field(0xC, "pointer") + VALUE_NAME = Field(0x4, "string") VALUE_VALUE = Field(0x8, "i32") From e1b7410e51926882d87e0abc53ec3450bc74d916 Mon Sep 17 00:00:00 2001 From: James Westman Date: Thu, 6 Jul 2023 20:40:56 -0500 Subject: [PATCH 009/155] docs: Add link to online documentation --- blueprintcompiler/gir.py | 74 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 73 insertions(+), 1 deletion(-) diff --git a/blueprintcompiler/gir.py b/blueprintcompiler/gir.py index 6e37626..816b58b 100644 --- a/blueprintcompiler/gir.py +++ b/blueprintcompiler/gir.py @@ -92,6 +92,23 @@ def get_xml(namespace: str, version: str): return _xml_cache[filename] +ONLINE_DOCS = { + "Adw-1": "https://gnome.pages.gitlab.gnome.org/libadwaita/doc/1-latest/", + "Gdk-4.0": "https://docs.gtk.org/gdk4/", + "GdkPixbuf-2.0": "https://docs.gtk.org/gdk-pixbuf/", + "Gio-2.0": "https://docs.gtk.org/gio/", + "GLib-2.0": "https://docs.gtk.org/glib/", + "GModule-2.0": "https://docs.gtk.org/gmodule/", + "GObject-2.0": "https://docs.gtk.org/gobject/", + "Gsk-4.0": "https://docs.gtk.org/gsk4/", + "Gtk-4.0": "https://docs.gtk.org/gtk4/", + "GtkSource-5": "https://gnome.pages.gitlab.gnome.org/gtksourceview/gtksourceview5", + "Pango-1.0": "https://docs.gtk.org/Pango/", + "Shumate-1.0": "https://gnome.pages.gitlab.gnome.org/libshumate/", + "WebKit2-4.1": "https://webkitgtk.org/reference/webkit2gtk/stable/", +} + + class GirType: @property def doc(self) -> T.Optional[str]: @@ -291,10 +308,17 @@ class GirNode: except: # Not a huge deal, but if you want docs in the language server you # should ensure .gir files are installed - pass + sections.append("Documentation is not installed") + + if self.online_docs: + sections.append(f"[Online documentation]({self.online_docs})") return "\n\n---\n\n".join(sections) + @property + def online_docs(self) -> T.Optional[str]: + return None + @property def signature(self) -> T.Optional[str]: return None @@ -328,6 +352,14 @@ class Property(GirNode): def construct_only(self) -> bool: return self.tl.PROP_CONSTRUCT_ONLY == 1 + @property + def online_docs(self) -> T.Optional[str]: + if ns := self.get_containing(Namespace).online_docs: + assert self.container is not None + return f"{ns}property.{self.container.name}.{self.name}.html" + else: + return None + class Argument(GirNode): def __init__(self, container: GirNode, tl: typelib.Typelib) -> None: @@ -380,6 +412,14 @@ class Signal(GirNode): ) return f"signal {self.container.full_name}::{self.name} ({args})" + @property + def online_docs(self) -> T.Optional[str]: + if ns := self.get_containing(Namespace).online_docs: + assert self.container is not None + return f"{ns}signal.{self.container.name}.{self.name}.html" + else: + return None + class Interface(GirNode, GirType): def __init__(self, ns: "Namespace", tl: typelib.Typelib): @@ -432,6 +472,13 @@ class Interface(GirNode, GirType): return True return False + @property + def online_docs(self) -> T.Optional[str]: + if ns := self.get_containing(Namespace).online_docs: + return f"{ns}interface.{self.name}.html" + else: + return None + class Class(GirNode, GirType): def __init__(self, ns: "Namespace", tl: typelib.Typelib) -> None: @@ -544,6 +591,13 @@ class Class(GirNode, GirType): for impl in self.implements: yield from impl.signals.values() + @property + def online_docs(self) -> T.Optional[str]: + if ns := self.get_containing(Namespace).online_docs: + return f"{ns}class.{self.name}.html" + else: + return None + class TemplateType(GirType): def __init__(self, name: str, parent: T.Optional[GirType]): @@ -646,6 +700,13 @@ class Enumeration(GirNode, GirType): def assignable_to(self, type: GirType) -> bool: return type == self + @property + def online_docs(self) -> T.Optional[str]: + if ns := self.get_containing(Namespace).online_docs: + return f"{ns}enum.{self.name}.html" + else: + return None + class Boxed(GirNode, GirType): def __init__(self, ns: "Namespace", tl: typelib.Typelib) -> None: @@ -658,6 +719,13 @@ class Boxed(GirNode, GirType): def assignable_to(self, type) -> bool: return type == self + @property + def online_docs(self) -> T.Optional[str]: + if ns := self.get_containing(Namespace).online_docs: + return f"{ns}boxed.{self.name}.html" + else: + return None + class Bitfield(Enumeration): def __init__(self, ns: "Namespace", tl: typelib.Typelib) -> None: @@ -753,6 +821,10 @@ class Namespace(GirNode): else: return self.get_type(type_name) + @property + def online_docs(self) -> T.Optional[str]: + return ONLINE_DOCS.get(f"{self.name}-{self.version}") + class Repository(GirNode): def __init__(self, tl: typelib.Typelib) -> None: From c4fc4f3de870aca63698ddec040f3fb87d7ee674 Mon Sep 17 00:00:00 2001 From: James Westman Date: Thu, 6 Jul 2023 20:53:30 -0500 Subject: [PATCH 010/155] docs: Fix bug with colliding names Often a vfunc has the same name as a signal, and the wrong docs would be shown. --- blueprintcompiler/gir.py | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/blueprintcompiler/gir.py b/blueprintcompiler/gir.py index 816b58b..6dab652 100644 --- a/blueprintcompiler/gir.py +++ b/blueprintcompiler/gir.py @@ -253,6 +253,8 @@ TNode = T.TypeVar("TNode", bound="GirNode") class GirNode: + xml_tag: str + def __init__(self, container: T.Optional["GirNode"], tl: typelib.Typelib) -> None: self.container = container self.tl = tl @@ -269,7 +271,8 @@ class GirNode: def xml(self): for el in self.container.xml.children: if el.attrs.get("name") == self.name: - return el + if el.tag == self.xml_tag: + return el @cached_property def glib_type_name(self) -> str: @@ -329,6 +332,8 @@ class GirNode: class Property(GirNode): + xml_tag = "property" + def __init__(self, klass: T.Union["Class", "Interface"], tl: typelib.Typelib): super().__init__(klass, tl) @@ -396,6 +401,8 @@ class Signature(GirNode): class Signal(GirNode): + xml_tag = "glib:signal" + def __init__( self, klass: T.Union["Class", "Interface"], tl: typelib.Typelib ) -> None: @@ -422,6 +429,8 @@ class Signal(GirNode): class Interface(GirNode, GirType): + xml_tag = "interface" + def __init__(self, ns: "Namespace", tl: typelib.Typelib): super().__init__(ns, tl) @@ -481,6 +490,8 @@ class Interface(GirNode, GirType): class Class(GirNode, GirType): + xml_tag = "class" + def __init__(self, ns: "Namespace", tl: typelib.Typelib) -> None: super().__init__(ns, tl) @@ -654,6 +665,8 @@ class TemplateType(GirType): class EnumMember(GirNode): + xml_tag = "member" + def __init__(self, enum: "Enumeration", tl: typelib.Typelib) -> None: super().__init__(enum, tl) @@ -679,6 +692,8 @@ class EnumMember(GirNode): class Enumeration(GirNode, GirType): + xml_tag = "enumeration" + def __init__(self, ns: "Namespace", tl: typelib.Typelib) -> None: super().__init__(ns, tl) @@ -709,6 +724,8 @@ class Enumeration(GirNode, GirType): class Boxed(GirNode, GirType): + xml_tag = "glib:boxed" + def __init__(self, ns: "Namespace", tl: typelib.Typelib) -> None: super().__init__(ns, tl) @@ -728,6 +745,8 @@ class Boxed(GirNode, GirType): class Bitfield(Enumeration): + xml_tag = "bitfield" + def __init__(self, ns: "Namespace", tl: typelib.Typelib) -> None: super().__init__(ns, tl) From 9ff76b65cc381ee5f97c243a43d461a7fae086e0 Mon Sep 17 00:00:00 2001 From: James Westman Date: Thu, 6 Jul 2023 20:54:14 -0500 Subject: [PATCH 011/155] docs: Fix docs for accessibility properties --- blueprintcompiler/language/gtk_a11y.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/blueprintcompiler/language/gtk_a11y.py b/blueprintcompiler/language/gtk_a11y.py index f7f89d6..6a346ec 100644 --- a/blueprintcompiler/language/gtk_a11y.py +++ b/blueprintcompiler/language/gtk_a11y.py @@ -98,6 +98,7 @@ def get_types(gir): def _get_docs(gir, name): + name = name.replace("-", "_") if gir_type := ( gir.get_type("AccessibleProperty", "Gtk").members.get(name) or gir.get_type("AccessibleRelation", "Gtk").members.get(name) @@ -197,7 +198,9 @@ def a11y_completer(ast_node, match_variables): def a11y_name_completer(ast_node, match_variables): for name, type in get_types(ast_node.root.gir).items(): yield Completion( - name, CompletionItemKind.Property, docs=_get_docs(ast_node.root.gir, type) + name, + CompletionItemKind.Property, + docs=_get_docs(ast_node.root.gir, type.name), ) From cb1eb9ba4438a3e338ed01fd60908d19e6b35261 Mon Sep 17 00:00:00 2001 From: James Westman Date: Thu, 6 Jul 2023 21:21:24 -0500 Subject: [PATCH 012/155] lsp: Show better info on IdentLiteral hover Instead of showing the documentation for the expected type, show the signature of the referenced object. --- blueprintcompiler/language/gobject_object.py | 7 +++++++ .../language/gtk_list_item_factory.py | 8 ++++++++ blueprintcompiler/language/gtk_menu.py | 7 +++++++ .../language/gtkbuilder_template.py | 7 +++++++ blueprintcompiler/language/values.py | 18 ++++++++++++------ 5 files changed, 41 insertions(+), 6 deletions(-) diff --git a/blueprintcompiler/language/gobject_object.py b/blueprintcompiler/language/gobject_object.py index d247c61..1a42c0a 100644 --- a/blueprintcompiler/language/gobject_object.py +++ b/blueprintcompiler/language/gobject_object.py @@ -55,6 +55,13 @@ class Object(AstNode): def content(self) -> ObjectContent: return self.children[ObjectContent][0] + @property + def signature(self) -> str: + if self.id: + return f"{self.class_name.gir_type.full_name} {self.id}" + else: + return f"{self.class_name.gir_type.full_name}" + @property def gir_class(self) -> GirType: if self.class_name is None: diff --git a/blueprintcompiler/language/gtk_list_item_factory.py b/blueprintcompiler/language/gtk_list_item_factory.py index f2f9d63..ba2a27f 100644 --- a/blueprintcompiler/language/gtk_list_item_factory.py +++ b/blueprintcompiler/language/gtk_list_item_factory.py @@ -9,6 +9,14 @@ from .types import TypeName class ExtListItemFactory(AstNode): grammar = [UseExact("id", "template"), Optional(TypeName), ObjectContent] + @property + def id(self) -> str: + return "template" + + @property + def signature(self) -> str: + return f"template {self.gir_class.full_name}" + @property def type_name(self) -> T.Optional[TypeName]: if len(self.children[TypeName]) == 1: diff --git a/blueprintcompiler/language/gtk_menu.py b/blueprintcompiler/language/gtk_menu.py index 5698e12..824ec5c 100644 --- a/blueprintcompiler/language/gtk_menu.py +++ b/blueprintcompiler/language/gtk_menu.py @@ -35,6 +35,13 @@ class Menu(AstNode): def id(self) -> str: return self.tokens["id"] + @property + def signature(self) -> str: + if self.id: + return f"Gio.Menu {self.id}" + else: + return "Gio.Menu" + @property def tag(self) -> str: return self.tokens["tag"] diff --git a/blueprintcompiler/language/gtkbuilder_template.py b/blueprintcompiler/language/gtkbuilder_template.py index 9de4d66..7af5cc8 100644 --- a/blueprintcompiler/language/gtkbuilder_template.py +++ b/blueprintcompiler/language/gtkbuilder_template.py @@ -44,6 +44,13 @@ class Template(Object): def id(self) -> str: return "template" + @property + def signature(self) -> str: + if self.parent_type: + return f"template {self.gir_class.full_name} : {self.parent_type.gir_type.full_name}" + else: + return f"template {self.gir_class.full_name}" + @property def gir_class(self) -> GirType: if isinstance(self.class_name.gir_type, ExternType): diff --git a/blueprintcompiler/language/values.py b/blueprintcompiler/language/values.py index 6981141..fd1765b 100644 --- a/blueprintcompiler/language/values.py +++ b/blueprintcompiler/language/values.py @@ -312,14 +312,20 @@ class IdentLiteral(AstNode): @docs() def docs(self) -> T.Optional[str]: - type = self.context[ValueTypeCtx].value_type - if isinstance(type, gir.Enumeration): - if member := type.members.get(self.ident): + expected_type = self.context[ValueTypeCtx].value_type + if isinstance(expected_type, gir.BoolType): + return None + elif isinstance(expected_type, gir.Enumeration): + if member := expected_type.members.get(self.ident): return member.doc else: - return type.doc - elif isinstance(type, gir.GirNode): - return type.doc + return expected_type.doc + elif self.ident == "null" and self.context[ValueTypeCtx].allow_null: + return None + elif object := self.context[ScopeCtx].objects.get(self.ident): + return f"```\n{object.signature}\n```" + elif self.root.is_legacy_template(self.ident): + return f"```\n{self.root.template.signature}\n```" else: return None From abc4e5de65160b63ce15a803be2f177c0650e780 Mon Sep 17 00:00:00 2001 From: James Westman Date: Thu, 6 Jul 2023 21:29:13 -0500 Subject: [PATCH 013/155] lsp: Add docs for Adw.Breakpoint --- blueprintcompiler/language/adw_breakpoint.py | 41 ++++++++++++++++++-- 1 file changed, 38 insertions(+), 3 deletions(-) diff --git a/blueprintcompiler/language/adw_breakpoint.py b/blueprintcompiler/language/adw_breakpoint.py index addbd8a..e71b29a 100644 --- a/blueprintcompiler/language/adw_breakpoint.py +++ b/blueprintcompiler/language/adw_breakpoint.py @@ -24,12 +24,26 @@ from .values import Value class AdwBreakpointCondition(AstNode): - grammar = ["condition", "(", UseQuoted("condition"), Match(")").expected()] + grammar = [ + UseExact("kw", "condition"), + "(", + UseQuoted("condition"), + Match(")").expected(), + ] @property def condition(self) -> str: return self.tokens["condition"] + @docs("kw") + def keyword_docs(self): + klass = self.root.gir.get_type("Breakpoint", "Adw") + if klass is None: + return None + prop = klass.properties.get("condition") + assert isinstance(prop, gir.Property) + return prop.doc + @validate() def unique(self): self.validate_unique_in_parent("Duplicate condition statement") @@ -68,9 +82,16 @@ class AdwBreakpointSetter(AstNode): return None @property - def gir_property(self): - if self.gir_class is not None and not isinstance(self.gir_class, ExternType): + def gir_property(self) -> T.Optional[gir.Property]: + if ( + self.gir_class is not None + and not isinstance(self.gir_class, ExternType) + and self.property_name is not None + ): + assert isinstance(self.gir_class, gir.Class) return self.gir_class.properties.get(self.property_name) + else: + return None @context(ValueTypeCtx) def value_type(self) -> ValueTypeCtx: @@ -81,6 +102,20 @@ class AdwBreakpointSetter(AstNode): return ValueTypeCtx(type, allow_null=True) + @docs("object") + def object_docs(self): + if self.object is not None: + return f"```\n{self.object.signature}\n```" + else: + return None + + @docs("property") + def property_docs(self): + if self.gir_property is not None: + return self.gir_property.doc + else: + return None + @validate("object") def object_exists(self): if self.object is None: From 0a4b5d07a1190e87b99e60735b74291996dc2135 Mon Sep 17 00:00:00 2001 From: James Westman Date: Mon, 22 May 2023 21:26:07 -0500 Subject: [PATCH 014/155] Remove PropertyBinding rule, just use Binding --- blueprintcompiler/language/__init__.py | 1 - blueprintcompiler/language/binding.py | 58 ++++++- blueprintcompiler/language/contexts.py | 1 + blueprintcompiler/language/expression.py | 6 +- .../language/gobject_property.py | 9 +- .../language/property_binding.py | 145 ------------------ blueprintcompiler/language/values.py | 8 +- blueprintcompiler/outputs/xml/__init__.py | 24 ++- tests/sample_errors/binding_flags.blp | 5 + tests/sample_errors/binding_flags.err | 1 + .../binding_object_not_found.blp | 5 + .../binding_object_not_found.err | 1 + tests/sample_errors/warn_old_bind.blp | 5 +- tests/sample_errors/warn_old_bind.err | 4 +- tests/samples/property_binding.blp | 4 +- tests/samples/template_bind_property.blp | 2 +- 16 files changed, 100 insertions(+), 179 deletions(-) delete mode 100644 blueprintcompiler/language/property_binding.py create mode 100644 tests/sample_errors/binding_flags.blp create mode 100644 tests/sample_errors/binding_flags.err create mode 100644 tests/sample_errors/binding_object_not_found.blp create mode 100644 tests/sample_errors/binding_object_not_found.err diff --git a/blueprintcompiler/language/__init__.py b/blueprintcompiler/language/__init__.py index f849c75..39d8336 100644 --- a/blueprintcompiler/language/__init__.py +++ b/blueprintcompiler/language/__init__.py @@ -38,7 +38,6 @@ from .gtk_styles import ExtStyles from .gtkbuilder_child import Child, ChildExtension, ChildInternal, ChildType from .gtkbuilder_template import Template from .imports import GtkDirective, Import -from .property_binding import PropertyBinding from .types import ClassName from .ui import UI from .values import ( diff --git a/blueprintcompiler/language/binding.py b/blueprintcompiler/language/binding.py index e6bffd0..86beec0 100644 --- a/blueprintcompiler/language/binding.py +++ b/blueprintcompiler/language/binding.py @@ -23,16 +23,57 @@ from .common import * from .expression import Expression, LiteralExpr, LookupOp +class BindingFlag(AstNode): + grammar = [ + AnyOf( + UseExact("flag", "inverted"), + UseExact("flag", "bidirectional"), + UseExact("flag", "no-sync-create"), + UseExact("flag", "sync-create"), + ) + ] + + @property + def flag(self) -> str: + return self.tokens["flag"] + + @validate() + def sync_create(self): + if self.flag == "sync-create": + raise UpgradeWarning( + "'sync-create' is now the default. Use 'no-sync-create' if this is not wanted.", + actions=[CodeAction("remove 'sync-create'", "")], + ) + + @validate() + def unique(self): + self.validate_unique_in_parent( + f"Duplicate flag '{self.flag}'", lambda x: x.flag == self.flag + ) + + @validate() + def flags_only_if_simple(self): + if self.parent.simple_binding is None: + raise CompileError( + "Only bindings with a single lookup can have flags", + ) + + class Binding(AstNode): grammar = [ - Keyword("bind"), + AnyOf(Keyword("bind"), UseExact("bind", "bind-property")), Expression, + ZeroOrMore(BindingFlag), ] @property def expression(self) -> Expression: return self.children[Expression][0] + @property + def flags(self) -> T.List[BindingFlag]: + return self.children[BindingFlag] + @property def simple_binding(self) -> T.Optional["SimpleBinding"]: if isinstance(self.expression.last, LookupOp): @@ -40,14 +81,29 @@ class Binding(AstNode): from .values import IdentLiteral if isinstance(self.expression.last.lhs.literal.value, IdentLiteral): + flags = [x.flag for x in self.flags] return SimpleBinding( self.expression.last.lhs.literal.value.ident, self.expression.last.property_name, + no_sync_create="no-sync-create" in flags, + bidirectional="bidirectional" in flags, + inverted="inverted" in flags, ) return None + @validate("bind") + def bind_property(self): + if self.tokens["bind"] == "bind-property": + raise UpgradeWarning( + "'bind-property' is no longer needed. Use 'bind' instead. (blueprint 0.8.2)", + actions=[CodeAction("use 'bind'", "bind")], + ) + @dataclass class SimpleBinding: source: str property_name: str + no_sync_create: bool = False + bidirectional: bool = False + inverted: bool = False diff --git a/blueprintcompiler/language/contexts.py b/blueprintcompiler/language/contexts.py index fb24f5a..2f8e22e 100644 --- a/blueprintcompiler/language/contexts.py +++ b/blueprintcompiler/language/contexts.py @@ -30,6 +30,7 @@ from .gtkbuilder_template import Template class ValueTypeCtx: value_type: T.Optional[GirType] allow_null: bool = False + must_infer_type: bool = False @dataclass diff --git a/blueprintcompiler/language/expression.py b/blueprintcompiler/language/expression.py index a2185a3..10426fa 100644 --- a/blueprintcompiler/language/expression.py +++ b/blueprintcompiler/language/expression.py @@ -114,7 +114,7 @@ class LookupOp(InfixExpr): @context(ValueTypeCtx) def value_type(self) -> ValueTypeCtx: - return ValueTypeCtx(None) + return ValueTypeCtx(None, must_infer_type=True) @property def property_name(self) -> str: @@ -133,6 +133,10 @@ class LookupOp(InfixExpr): @validate("property") def property_exists(self): if self.lhs.type is None: + # Literal values throw their own errors if the type isn't known + if isinstance(self.lhs, LiteralExpr): + return + raise CompileError( f"Could not determine the type of the preceding expression", hints=[ diff --git a/blueprintcompiler/language/gobject_property.py b/blueprintcompiler/language/gobject_property.py index f65c5ca..b24cd07 100644 --- a/blueprintcompiler/language/gobject_property.py +++ b/blueprintcompiler/language/gobject_property.py @@ -22,21 +22,18 @@ from .binding import Binding from .common import * from .contexts import ValueTypeCtx from .gtkbuilder_template import Template -from .property_binding import PropertyBinding from .values import ObjectValue, Value class Property(AstNode): - grammar = Statement( - UseIdent("name"), ":", AnyOf(PropertyBinding, Binding, ObjectValue, Value) - ) + grammar = Statement(UseIdent("name"), ":", AnyOf(Binding, ObjectValue, Value)) @property def name(self) -> str: return self.tokens["name"] @property - def value(self) -> T.Union[PropertyBinding, Binding, ObjectValue, Value]: + def value(self) -> T.Union[Binding, ObjectValue, Value]: return self.children[0] @property @@ -51,7 +48,7 @@ class Property(AstNode): @validate() def binding_valid(self): if ( - (isinstance(self.value, PropertyBinding) or isinstance(self.value, Binding)) + isinstance(self.value, Binding) and self.gir_property is not None and self.gir_property.construct_only ): diff --git a/blueprintcompiler/language/property_binding.py b/blueprintcompiler/language/property_binding.py deleted file mode 100644 index 686d1e8..0000000 --- a/blueprintcompiler/language/property_binding.py +++ /dev/null @@ -1,145 +0,0 @@ -# property_binding.py -# -# Copyright 2023 James Westman -# -# This file is free software; you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as -# published by the Free Software Foundation; either version 3 of the -# License, or (at your option) any later version. -# -# This file is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this program. If not, see . -# -# SPDX-License-Identifier: LGPL-3.0-or-later - -from .common import * -from .contexts import ScopeCtx -from .gobject_object import Object - - -class PropertyBindingFlag(AstNode): - grammar = [ - AnyOf( - UseExact("flag", "inverted"), - UseExact("flag", "bidirectional"), - UseExact("flag", "no-sync-create"), - UseExact("flag", "sync-create"), - ) - ] - - @property - def flag(self) -> str: - return self.tokens["flag"] - - @validate() - def sync_create(self): - if self.flag == "sync-create": - raise UpgradeWarning( - "'sync-create' is now the default. Use 'no-sync-create' if this is not wanted.", - actions=[CodeAction("remove 'sync-create'", "")], - ) - - @validate() - def unique(self): - self.validate_unique_in_parent( - f"Duplicate flag '{self.flag}'", lambda x: x.flag == self.flag - ) - - -class PropertyBinding(AstNode): - grammar = AnyOf( - [ - Keyword("bind-property"), - UseIdent("source"), - ".", - UseIdent("property"), - ZeroOrMore(PropertyBindingFlag), - ], - [ - Keyword("bind"), - UseIdent("source"), - ".", - UseIdent("property"), - PropertyBindingFlag, - ZeroOrMore(PropertyBindingFlag), - ], - ) - - @property - def source(self) -> str: - return self.tokens["source"] - - @property - def source_obj(self) -> T.Optional[Object]: - if self.root.is_legacy_template(self.source): - return self.root.template - return self.context[ScopeCtx].objects.get(self.source) - - @property - def property_name(self) -> str: - return self.tokens["property"] - - @property - def flags(self) -> T.List[PropertyBindingFlag]: - return self.children[PropertyBindingFlag] - - @property - def inverted(self) -> bool: - return any([f.flag == "inverted" for f in self.flags]) - - @property - def bidirectional(self) -> bool: - return any([f.flag == "bidirectional" for f in self.flags]) - - @property - def no_sync_create(self) -> bool: - return any([f.flag == "no-sync-create" for f in self.flags]) - - @validate("source") - def source_object_exists(self) -> None: - if self.source_obj is None: - raise CompileError( - f"Could not find object with ID {self.source}", - did_you_mean=(self.source, self.context[ScopeCtx].objects.keys()), - ) - - @validate("property") - def property_exists(self) -> None: - if self.source_obj is None: - return - - gir_class = self.source_obj.gir_class - - if gir_class is None or gir_class.incomplete: - # Objects that we have no gir data on should not be validated - # This happens for classes defined by the app itself - return - - if ( - isinstance(gir_class, gir.Class) - and gir_class.properties.get(self.property_name) is None - ): - raise CompileError( - f"{gir_class.full_name} does not have a property called {self.property_name}" - ) - - @validate("bind") - def old_bind(self): - if self.tokens["bind"]: - raise UpgradeWarning( - "Use 'bind-property', introduced in blueprint 0.8.0, to use binding flags", - actions=[CodeAction("Use 'bind-property'", "bind-property")], - ) - - @validate("source") - def legacy_template(self): - if self.root.is_legacy_template(self.source): - raise UpgradeWarning( - "Use 'template' instead of the class name (introduced in 0.8.0)", - actions=[CodeAction("Use 'template'", "template")], - ) diff --git a/blueprintcompiler/language/values.py b/blueprintcompiler/language/values.py index fd1765b..888d6a1 100644 --- a/blueprintcompiler/language/values.py +++ b/blueprintcompiler/language/values.py @@ -291,7 +291,7 @@ class IdentLiteral(AstNode): actions=[CodeAction("Use 'template'", "template")], ) - elif expected_type is not None: + elif expected_type is not None or self.context[ValueTypeCtx].must_infer_type: object = self.context[ScopeCtx].objects.get(self.ident) if object is None: if self.ident == "null": @@ -305,7 +305,11 @@ class IdentLiteral(AstNode): self.context[ScopeCtx].objects.keys(), ), ) - elif object.gir_class and not object.gir_class.assignable_to(expected_type): + elif ( + expected_type is not None + and object.gir_class is not None + and not object.gir_class.assignable_to(expected_type) + ): raise CompileError( f"Cannot assign {object.gir_class.full_name} to {expected_type.full_name}" ) diff --git a/blueprintcompiler/outputs/xml/__init__.py b/blueprintcompiler/outputs/xml/__init__.py index 3ef7375..e9f091f 100644 --- a/blueprintcompiler/outputs/xml/__init__.py +++ b/blueprintcompiler/outputs/xml/__init__.py @@ -116,27 +116,21 @@ class XmlOutput(OutputFormat): if simple := value.simple_binding: props["bind-source"] = self._object_id(value, simple.source) props["bind-property"] = simple.property_name - props["bind-flags"] = "sync-create" + flags = [] + if not simple.no_sync_create: + flags.append("sync-create") + if simple.inverted: + flags.append("invert-boolean") + if simple.bidirectional: + flags.append("bidirectional") + props["bind-flags"] = "|".join(flags) or None + xml.put_self_closing("property", **props) else: xml.start_tag("binding", **props) self._emit_expression(value.expression, xml) xml.end_tag() - elif isinstance(value, PropertyBinding): - bind_flags = [] - if not value.no_sync_create: - bind_flags.append("sync-create") - if value.inverted: - bind_flags.append("invert-boolean") - if value.bidirectional: - bind_flags.append("bidirectional") - - props["bind-source"] = self._object_id(value, value.source) - props["bind-property"] = value.property_name - props["bind-flags"] = "|".join(bind_flags) or None - xml.put_self_closing("property", **props) - elif isinstance(value, ObjectValue): xml.start_tag("property", **props) self._emit_object(value.object, xml) diff --git a/tests/sample_errors/binding_flags.blp b/tests/sample_errors/binding_flags.blp new file mode 100644 index 0000000..7e94058 --- /dev/null +++ b/tests/sample_errors/binding_flags.blp @@ -0,0 +1,5 @@ +using Gtk 4.0; + +Label { + label: bind $my_closure() as