blueprint-compiler/blueprintcompiler/language/property_binding.py
2023-04-08 20:10:16 -05:00

135 lines
4.1 KiB
Python

# property_binding.py
#
# Copyright 2023 James Westman <james@jwestman.net>
#
# 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 <http://www.gnu.org/licenses/>.
#
# SPDX-License-Identifier: LGPL-3.0-or-later
from .common import *
from .contexts import ValueTypeCtx
from .gobject_object import Object
from .gtkbuilder_template import Template
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'", "")],
)
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]:
return self.root.objects_by_id.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.root.objects_by_id.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-property")
def not_bindable(self) -> None:
if binding_error := self.context[ValueTypeCtx].binding_error:
raise binding_error
@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")],
)