Separate output into its own module

This commit is contained in:
James Westman 2022-10-14 21:04:37 -05:00
parent 8cf793023d
commit a24f16109f
No known key found for this signature in database
GPG key ID: CE2DBA0ADB654EA6
33 changed files with 407 additions and 291 deletions

View file

@ -1,16 +1,13 @@
""" Contains all the syntax beyond basic objects, properties, signal, and
templates. """
from .attributes import BaseAttribute, BaseTypedAttribute
from .expression import Expr
from .expression import IdentExpr, LookupOp, Expr
from .gobject_object import Object, ObjectContent
from .gobject_property import Property
from .gobject_signal import Signal
from .gtk_a11y import A11y
from .gtk_combo_box_text import Items
from .gtk_file_filter import mime_types, patterns, suffixes
from .gtk_file_filter import mime_types, patterns, suffixes, Filters
from .gtk_layout import Layout
from .gtk_menu import menu
from .gtk_menu import menu, Menu, MenuAttribute
from .gtk_size_group import Widgets
from .gtk_string_list import Strings
from .gtk_styles import Styles
@ -18,7 +15,8 @@ from .gtkbuilder_child import Child
from .gtkbuilder_template import Template
from .imports import GtkDirective, Import
from .ui import UI
from .values import TypeValue, IdentValue, TranslatedStringValue, FlagsValue, QuotedValue, NumberValue
from .types import ClassName
from .values import TypeValue, IdentValue, TranslatedStringValue, FlagsValue, Flag, QuotedValue, NumberValue, Value
from .common import *

View file

@ -32,17 +32,6 @@ class BaseAttribute(AstNode):
def name(self):
return self.tokens["name"]
def emit_xml(self, xml: XmlEmitter):
value = self.children[Value][0]
attrs = { self.attr_name: self.name }
if isinstance(value, TranslatedStringValue):
attrs = { **attrs, **value.attrs }
xml.start_tag(self.tag_name, **attrs)
value.emit_xml(xml)
xml.end_tag()
class BaseTypedAttribute(BaseAttribute):
""" A BaseAttribute whose parent has a value_type property that can assist

View file

@ -27,7 +27,6 @@ from ..decompiler import DecompileCtx, decompiler
from ..gir import StringType, BoolType, IntType, FloatType, GirType, Enumeration
from ..lsp_utils import Completion, CompletionItemKind, SemanticToken, SemanticTokenType
from ..parse_tree import *
from ..xml_emitter import XmlEmitter
OBJECT_CONTENT_HOOKS = AnyOf()

View file

@ -27,9 +27,6 @@ expr = Pratt()
class Expr(AstNode):
grammar = expr
def emit_xml(self, xml: XmlEmitter):
self.children[-1].emit_xml(xml)
class InfixExpr(AstNode):
@property
@ -41,19 +38,17 @@ class InfixExpr(AstNode):
class IdentExpr(AstNode):
grammar = UseIdent("ident")
def emit_xml(self, xml: XmlEmitter):
xml.start_tag("constant")
xml.put_text(self.tokens["ident"])
xml.end_tag()
@property
def ident(self) -> str:
return self.tokens["ident"]
class LookupOp(InfixExpr):
grammar = [".", UseIdent("property")]
def emit_xml(self, xml: XmlEmitter):
xml.start_tag("lookup", name=self.tokens["property"])
self.lhs.emit_xml(xml)
xml.end_tag()
@property
def property_name(self) -> str:
return self.tokens["property"]
expr.children = [

View file

@ -33,10 +33,6 @@ class ObjectContent(AstNode):
def gir_class(self):
return self.parent.gir_class
def emit_xml(self, xml: XmlEmitter):
for x in self.children:
x.emit_xml(xml)
class Object(AstNode):
grammar: T.Any = [
ConcreteClassName,
@ -44,9 +40,21 @@ class Object(AstNode):
ObjectContent,
]
@property
def id(self) -> str:
return self.tokens["id"]
@property
def class_name(self) -> ClassName | None:
return self.children[ClassName][0]
@property
def content(self) -> ObjectContent:
return self.children[ObjectContent][0]
@property
def gir_class(self):
return self.children[ClassName][0].gir_type
return self.class_name.gir_type
@cached_property
def action_widgets(self) -> T.List[ResponseId]:
@ -62,28 +70,6 @@ class Object(AstNode):
if child.response_id
]
def emit_start_tag(self, xml: XmlEmitter):
xml.start_tag("object", **{
"class": self.children[ClassName][0].glib_type_name,
"id": self.tokens["id"],
})
def emit_xml(self, xml: XmlEmitter):
self.emit_start_tag(xml)
for child in self.children:
child.emit_xml(xml)
# List action widgets
action_widgets = self.action_widgets
if action_widgets:
xml.start_tag("action-widgets")
for action_widget in action_widgets:
action_widget.emit_action_widget(xml)
xml.end_tag()
xml.end_tag()
def validate_parent_type(node, ns: str, name: str, err_msg: str):
parent = node.root.gir.get_type(name, ns)

View file

@ -133,44 +133,3 @@ class Property(AstNode):
def property_docs(self):
if self.gir_property is not None:
return self.gir_property.doc
def emit_xml(self, xml: XmlEmitter):
values = self.children[Value]
value = values[0] if len(values) == 1 else None
bind_flags = []
if self.tokens["bind_source"] and not self.tokens["no_sync_create"]:
bind_flags.append("sync-create")
if self.tokens["inverted"]:
bind_flags.append("invert-boolean")
if self.tokens["bidirectional"]:
bind_flags.append("bidirectional")
bind_flags_str = "|".join(bind_flags) or None
props = {
"name": self.tokens["name"],
"bind-source": self.tokens["bind_source"],
"bind-property": self.tokens["bind_property"],
"bind-flags": bind_flags_str,
}
if isinstance(value, TranslatedStringValue):
props = { **props, **value.attrs }
if len(self.children[Object]) == 1:
xml.start_tag("property", **props)
self.children[Object][0].emit_xml(xml)
xml.end_tag()
elif value is None:
if self.tokens["binding"]:
xml.start_tag("binding", **props)
for x in self.children:
x.emit_xml(xml)
xml.end_tag()
else:
xml.put_self_closing("property", **props);
else:
xml.start_tag("property", **props)
value.emit_xml(xml)
xml.end_tag()

View file

@ -40,6 +40,30 @@ class Signal(AstNode):
)),
)
@property
def name(self) -> str:
return self.tokens["name"]
@property
def detail_name(self) -> str | None:
return self.tokens["detail_name"]
@property
def handler(self) -> str:
return self.tokens["handler"]
@property
def object_id(self) -> str | None:
return self.tokens["object"]
@property
def is_swapped(self) -> bool:
return self.tokens["swapped"] or False
@property
def is_after(self) -> bool:
return self.tokens["after"] or False
@property
def gir_signal(self):
@ -89,19 +113,6 @@ class Signal(AstNode):
return self.gir_signal.doc
def emit_xml(self, xml: XmlEmitter):
name = self.tokens["name"]
if self.tokens["detail_name"]:
name += "::" + self.tokens["detail_name"]
xml.put_self_closing(
"signal",
name=name,
handler=self.tokens["handler"],
swapped="true" if self.tokens["swapped"] else None,
object=self.tokens["object"]
)
@decompiler("signal")
def decompile_signal(ctx, gir, name, handler, swapped="false", object=None):
object_name = object or ""

View file

@ -167,12 +167,6 @@ class A11y(AstNode):
def unique_in_parent(self):
self.validate_unique_in_parent("Duplicate accessibility block")
def emit_xml(self, xml: XmlEmitter):
xml.start_tag("accessibility")
for child in self.children:
child.emit_xml(xml)
xml.end_tag()
@completer(
applies_in=[ObjectContent],

View file

@ -60,12 +60,6 @@ class Items(AstNode):
def unique_in_parent(self):
self.validate_unique_in_parent("Duplicate items block")
def emit_xml(self, xml: XmlEmitter):
xml.start_tag("items")
for child in self.children:
child.emit_xml(xml)
xml.end_tag()
@completer(
applies_in=[ObjectContent],

View file

@ -39,18 +39,9 @@ class Filters(AstNode):
)
wrapped_validator(self)
def emit_xml(self, xml: XmlEmitter):
xml.start_tag(self.tokens["tag_name"])
for child in self.children:
child.emit_xml(xml)
xml.end_tag()
class FilterString(AstNode):
def emit_xml(self, xml):
xml.start_tag(self.tokens["tag_name"])
xml.put_text(self.tokens["name"])
xml.end_tag()
pass
def create_node(tag_name: str, singular: str):

View file

@ -64,12 +64,6 @@ class Layout(AstNode):
def unique_in_parent(self):
self.validate_unique_in_parent("Duplicate layout block")
def emit_xml(self, xml: XmlEmitter):
xml.start_tag("layout")
for child in self.children:
child.emit_xml(xml)
xml.end_tag()
@completer(
applies_in=[ObjectContent],

View file

@ -19,22 +19,26 @@
import typing as T
from blueprintcompiler.language.values import Value
from .attributes import BaseAttribute
from .gobject_object import Object, ObjectContent
from .common import *
class Menu(Object):
def emit_xml(self, xml: XmlEmitter):
xml.start_tag(self.tokens["tag"], id=self.tokens["id"])
for child in self.children:
child.emit_xml(xml)
xml.end_tag()
class Menu(AstNode):
@property
def gir_class(self):
return self.root.gir.namespaces["Gtk"].lookup_type("Gio.Menu")
@property
def id(self) -> str:
return self.tokens["id"]
@property
def tag(self) -> str:
return self.tokens["tag"]
class MenuAttribute(BaseAttribute):
tag_name = "attribute"
@ -43,6 +47,10 @@ class MenuAttribute(BaseAttribute):
def value_type(self):
return None
@property
def value(self) -> Value:
return self.children[Value][0]
menu_contents = Sequence()

View file

@ -39,9 +39,6 @@ class Widget(AstNode):
f"Cannot assign {object.gir_class.full_name} to {type.full_name}"
)
def emit_xml(self, xml: XmlEmitter):
xml.put_self_closing("widget", name=self.tokens["name"])
class Widgets(AstNode):
grammar = [
@ -59,12 +56,6 @@ class Widgets(AstNode):
def unique_in_parent(self):
self.validate_unique_in_parent("Duplicate widgets block")
def emit_xml(self, xml: XmlEmitter):
xml.start_tag("widgets")
for child in self.children:
child.emit_xml(xml)
xml.end_tag()
@completer(
applies_in=[ObjectContent],

View file

@ -31,13 +31,6 @@ class Item(AstNode):
def value_type(self):
return StringType()
def emit_xml(self, xml: XmlEmitter):
value = self.children[Value][0]
attrs = value.attrs if isinstance(value, TranslatedStringValue) else {}
xml.start_tag("item", **attrs)
value.emit_xml(xml)
xml.end_tag()
class Strings(AstNode):
grammar = [
@ -55,12 +48,6 @@ class Strings(AstNode):
def unique_in_parent(self):
self.validate_unique_in_parent("Duplicate strings block")
def emit_xml(self, xml: XmlEmitter):
xml.start_tag("items")
for child in self.children:
child.emit_xml(xml)
xml.end_tag()
@completer(
applies_in=[ObjectContent],

View file

@ -25,9 +25,6 @@ from .common import *
class StyleClass(AstNode):
grammar = UseQuoted("name")
def emit_xml(self, xml):
xml.put_self_closing("class", name=self.tokens["name"])
class Styles(AstNode):
grammar = [
@ -45,12 +42,6 @@ class Styles(AstNode):
def unique_in_parent(self):
self.validate_unique_in_parent("Duplicate styles block")
def emit_xml(self, xml: XmlEmitter):
xml.start_tag("style")
for child in self.children:
child.emit_xml(xml)
xml.end_tag()
@completer(
applies_in=[ObjectContent],

View file

@ -41,6 +41,10 @@ class Child(AstNode):
Object,
]
@property
def object(self) -> Object:
return self.children[Object][0]
@validate()
def parent_can_have_child(self):
if gir_class := self.parent.gir_class:
@ -70,17 +74,6 @@ class Child(AstNode):
else:
return None
def emit_xml(self, xml: XmlEmitter):
child_type = internal_child = None
if self.tokens["internal_child"]:
internal_child = self.tokens["child_type"]
else:
child_type = self.tokens["child_type"]
xml.start_tag("child", type=child_type, internal_child=internal_child)
for child in self.children:
child.emit_xml(xml)
xml.end_tag()
@decompiler("child")
def decompile_child(ctx, gir, type=None, internal_child=None):

View file

@ -34,28 +34,27 @@ class Template(Object):
ObjectContent,
]
@property
def id(self) -> str:
return self.tokens["id"]
@property
def class_name(self) -> ClassName | None:
if len(self.children[ClassName]):
return self.children[ClassName][0]
else:
return None
@property
def gir_class(self):
# Templates might not have a parent class defined
if len(self.children[ClassName]):
return self.children[ClassName][0].gir_type
if class_name := self.class_name:
return class_name.gir_type
@validate("id")
def unique_in_parent(self):
self.validate_unique_in_parent(f"Only one template may be defined per file, but this file contains {len(self.parent.children[Template])}",)
def emit_start_tag(self, xml: XmlEmitter):
if len(self.children[ClassName]):
parent = self.children[ClassName][0].glib_type_name
else:
parent = None
xml.start_tag(
"template",
**{"class": self.tokens["id"]},
parent=parent
)
@decompiler("template")
def decompile_template(ctx: DecompileCtx, gir, klass, parent="Widget"):

View file

@ -61,10 +61,6 @@ class GtkDirective(AstNode):
return gir.get_namespace("Gtk", self.tokens["version"])
def emit_xml(self, xml: XmlEmitter):
xml.put_self_closing("requires", lib="gtk", version=self.tokens["version"])
class Import(AstNode):
grammar = Statement(
"using",
@ -82,6 +78,3 @@ class Import(AstNode):
return gir.get_namespace(self.tokens["namespace"], self.tokens["version"])
except CompileError:
return None
def emit_xml(self, xml):
pass

View file

@ -120,6 +120,14 @@ class ResponseId(AstNode):
if widget.tokens["is_default"]:
raise CompileError("Default response is already set")
@property
def response_id(self) -> str:
return self.tokens["response_id"]
@property
def is_default(self) -> bool:
return self.tokens["is_default"] or False
@property
def widget_id(self) -> str:
"""Get action widget ID."""
@ -128,25 +136,3 @@ class ResponseId(AstNode):
_object: Object = self.parent.children[Object][0]
return _object.tokens["id"]
def emit_xml(self, xml: XmlEmitter) -> None:
"""Emit nothing.
Response ID don't have to emit any XML in place,
but have to emit action-widget tag in separate
place (see `ResponseId.emit_action_widget`)
"""
def emit_action_widget(self, xml: XmlEmitter) -> None:
"""Emit action-widget XML.
Must be called while <action-widgets> tag is open.
For more details see `GtkDialog` and `GtkInfoBar` docs.
"""
xml.start_tag(
"action-widget",
response=self.tokens["response_id"],
default=self.tokens["is_default"]
)
xml.put_text(self.widget_id)
xml.end_tag()

View file

@ -76,9 +76,6 @@ class TypeName(AstNode):
if self.gir_type:
return self.gir_type.doc
def emit_xml(self, xml: XmlEmitter):
pass
class ClassName(TypeName):
@validate("namespace", "class_name")

View file

@ -86,10 +86,3 @@ class UI(AstNode):
token = obj.group.tokens["id"]
raise CompileError(f"Duplicate object ID '{obj.tokens['id']}'", token.start, token.end)
passed[obj.tokens["id"]] = obj
def emit_xml(self, xml: XmlEmitter):
xml.start_tag("interface")
for x in self.children:
x.emit_xml(xml)
xml.end_tag()

View file

@ -46,14 +46,12 @@ class TranslatedStringValue(Value):
)
@property
def attrs(self):
attrs = { "translatable": "true" }
if "context" in self.tokens:
attrs["context"] = self.tokens["context"]
return attrs
def string(self) -> str:
return self.tokens["value"]
def emit_xml(self, xml: XmlEmitter):
xml.put_text(self.tokens["value"])
@property
def context(self) -> str | None:
return self.tokens["context"]
class TypeValue(Value):
@ -68,9 +66,6 @@ class TypeValue(Value):
def type_name(self):
return self.children[TypeName][0]
def emit_xml(self, xml: XmlEmitter):
xml.put_text(self.type_name.glib_type_name)
@validate()
def validate_for_type(self):
type = self.parent.value_type
@ -81,8 +76,9 @@ class TypeValue(Value):
class QuotedValue(Value):
grammar = UseQuoted("value")
def emit_xml(self, xml: XmlEmitter):
xml.put_text(self.tokens["value"])
@property
def value(self) -> str:
return self.tokens["value"]
@validate()
def validate_for_type(self):
@ -119,8 +115,9 @@ class QuotedValue(Value):
class NumberValue(Value):
grammar = UseNumber("value")
def emit_xml(self, xml: XmlEmitter):
xml.put_text(self.tokens["value"])
@property
def value(self) -> int | float:
return self.tokens["value"]
@validate()
def validate_for_type(self):
@ -179,19 +176,10 @@ class FlagsValue(Value):
if type is not None and not isinstance(type, gir.Bitfield):
raise CompileError(f"{type.full_name} is not a bitfield type")
def emit_xml(self, xml: XmlEmitter):
xml.put_text("|".join([flag.tokens["value"] for flag in self.children[Flag]]))
class IdentValue(Value):
grammar = UseIdent("value")
def emit_xml(self, xml: XmlEmitter):
if isinstance(self.parent.value_type, gir.Enumeration):
xml.put_text(self.parent.value_type.members[self.tokens["value"]].nick)
else:
xml.put_text(self.tokens["value"])
@validate()
def validate_for_type(self):
type = self.parent.value_type