diff --git a/blueprintcompiler/ast.py b/blueprintcompiler/ast.py index 323455b..555e7bf 100644 --- a/blueprintcompiler/ast.py +++ b/blueprintcompiler/ast.py @@ -328,68 +328,6 @@ class Property(AstNode): xml.end_tag() -class Signal(AstNode): - @property - def gir_signal(self): - if self.gir_class is not None: - return self.gir_class.signals.get(self.tokens["name"]) - - - @property - def gir_class(self): - return self.parent.parent.gir_class - - - @validate("name") - def signal_exists(self): - if self.gir_class is None: - # Objects that we have no gir data on should not be validated - # This happens for classes defined by the app itself - return - - if isinstance(self.parent.parent, Template): - # If the signal is part of a template, it might be defined by - # the application and thus not in gir - return - - if self.gir_signal is None: - raise CompileError( - f"Class {self.gir_class.full_name} does not contain a signal called {self.tokens['name']}", - did_you_mean=(self.tokens["name"], self.gir_class.signals.keys()) - ) - - - @validate("object") - def object_exists(self): - object_id = self.tokens["object"] - if object_id is None: - return - - if self.root.objects_by_id.get(object_id) is None: - raise CompileError( - f"Could not find object with ID '{object_id}'" - ) - - - @docs("name") - def signal_docs(self): - if self.gir_signal is not None: - 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"] - ) - - class Value(ast.AstNode): pass diff --git a/blueprintcompiler/decompiler.py b/blueprintcompiler/decompiler.py index 7a6ea81..58accbc 100644 --- a/blueprintcompiler/decompiler.py +++ b/blueprintcompiler/decompiler.py @@ -22,7 +22,7 @@ from enum import Enum import typing as T from dataclasses import dataclass -from .extensions import gtk_a11y +from .language import gtk_a11y from .xml_reader import Element, parse from .gir import * from .utils import Colors diff --git a/blueprintcompiler/extensions/__init__.py b/blueprintcompiler/language/__init__.py similarity index 79% rename from blueprintcompiler/extensions/__init__.py rename to blueprintcompiler/language/__init__.py index e7d72ee..ad4c6bc 100644 --- a/blueprintcompiler/extensions/__init__.py +++ b/blueprintcompiler/language/__init__.py @@ -1,6 +1,7 @@ """ Contains all the syntax beyond basic objects, properties, signal, and templates. """ +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 @@ -13,6 +14,6 @@ from .gtk_styles import Styles OBJECT_HOOKS = [menu] OBJECT_CONTENT_HOOKS = [ - A11y, Styles, Layout, mime_types, patterns, suffixes, Widgets, Items, + Signal, A11y, Styles, Layout, mime_types, patterns, suffixes, Widgets, Items, Strings, ] diff --git a/blueprintcompiler/language/common.py b/blueprintcompiler/language/common.py new file mode 100644 index 0000000..e6e2d9e --- /dev/null +++ b/blueprintcompiler/language/common.py @@ -0,0 +1,28 @@ +# common.py +# +# Copyright 2022 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 ..ast import BaseTypedAttribute, Value, Template +from ..ast_utils import AstNode, validate, docs +from ..completions_utils import * +from ..gir import StringType, BoolType, IntType, FloatType, GirType +from ..lsp_utils import Completion, CompletionItemKind +from ..parse_tree import * +from ..parser_utils import * +from ..xml_emitter import XmlEmitter diff --git a/blueprintcompiler/language/gobject_signal.py b/blueprintcompiler/language/gobject_signal.py new file mode 100644 index 0000000..b2ca164 --- /dev/null +++ b/blueprintcompiler/language/gobject_signal.py @@ -0,0 +1,101 @@ +# gobject_signal.py +# +# Copyright 2022 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 * + + +class Signal(AstNode): + grammar = Statement( + UseIdent("name"), + Optional([ + "::", + UseIdent("detail_name").expected("a signal detail name"), + ]), + "=>", + UseIdent("handler").expected("the name of a function to handle the signal"), + Match("(").expected("argument list"), + Optional(UseIdent("object")).expected("object identifier"), + Match(")").expected(), + ZeroOrMore(AnyOf( + [Keyword("swapped"), UseLiteral("swapped", True)], + [Keyword("after"), UseLiteral("after", True)], + )), + ) + + + @property + def gir_signal(self): + if self.gir_class is not None: + return self.gir_class.signals.get(self.tokens["name"]) + + + @property + def gir_class(self): + return self.parent.parent.gir_class + + + @validate("name") + def signal_exists(self): + if self.gir_class is None: + # Objects that we have no gir data on should not be validated + # This happens for classes defined by the app itself + return + + if isinstance(self.parent.parent, Template): + # If the signal is part of a template, it might be defined by + # the application and thus not in gir + return + + if self.gir_signal is None: + raise CompileError( + f"Class {self.gir_class.full_name} does not contain a signal called {self.tokens['name']}", + did_you_mean=(self.tokens["name"], self.gir_class.signals.keys()) + ) + + + @validate("object") + def object_exists(self): + object_id = self.tokens["object"] + if object_id is None: + return + + if self.root.objects_by_id.get(object_id) is None: + raise CompileError( + f"Could not find object with ID '{object_id}'" + ) + + + @docs("name") + def signal_docs(self): + if self.gir_signal is not None: + 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"] + ) diff --git a/blueprintcompiler/extensions/gtk_a11y.py b/blueprintcompiler/language/gtk_a11y.py similarity index 100% rename from blueprintcompiler/extensions/gtk_a11y.py rename to blueprintcompiler/language/gtk_a11y.py diff --git a/blueprintcompiler/extensions/gtk_combo_box_text.py b/blueprintcompiler/language/gtk_combo_box_text.py similarity index 100% rename from blueprintcompiler/extensions/gtk_combo_box_text.py rename to blueprintcompiler/language/gtk_combo_box_text.py diff --git a/blueprintcompiler/extensions/gtk_file_filter.py b/blueprintcompiler/language/gtk_file_filter.py similarity index 100% rename from blueprintcompiler/extensions/gtk_file_filter.py rename to blueprintcompiler/language/gtk_file_filter.py diff --git a/blueprintcompiler/extensions/gtk_layout.py b/blueprintcompiler/language/gtk_layout.py similarity index 100% rename from blueprintcompiler/extensions/gtk_layout.py rename to blueprintcompiler/language/gtk_layout.py diff --git a/blueprintcompiler/extensions/gtk_menu.py b/blueprintcompiler/language/gtk_menu.py similarity index 100% rename from blueprintcompiler/extensions/gtk_menu.py rename to blueprintcompiler/language/gtk_menu.py diff --git a/blueprintcompiler/extensions/gtk_size_group.py b/blueprintcompiler/language/gtk_size_group.py similarity index 100% rename from blueprintcompiler/extensions/gtk_size_group.py rename to blueprintcompiler/language/gtk_size_group.py diff --git a/blueprintcompiler/extensions/gtk_string_list.py b/blueprintcompiler/language/gtk_string_list.py similarity index 100% rename from blueprintcompiler/extensions/gtk_string_list.py rename to blueprintcompiler/language/gtk_string_list.py diff --git a/blueprintcompiler/extensions/gtk_styles.py b/blueprintcompiler/language/gtk_styles.py similarity index 100% rename from blueprintcompiler/extensions/gtk_styles.py rename to blueprintcompiler/language/gtk_styles.py diff --git a/blueprintcompiler/parser.py b/blueprintcompiler/parser.py index 04eda48..b9c5e4f 100644 --- a/blueprintcompiler/parser.py +++ b/blueprintcompiler/parser.py @@ -23,7 +23,7 @@ from .errors import MultipleErrors from .parse_tree import * from .parser_utils import * from .tokenizer import TokenType -from .extensions import OBJECT_HOOKS, OBJECT_CONTENT_HOOKS +from .language import OBJECT_HOOKS, OBJECT_CONTENT_HOOKS def parse(tokens) -> T.Tuple[ast.UI, T.Optional[MultipleErrors]]: @@ -64,26 +64,6 @@ def parse(tokens) -> T.Tuple[ast.UI, T.Optional[MultipleErrors]]: ) ) - signal = Group( - ast.Signal, - Statement( - UseIdent("name"), - Optional([ - "::", - UseIdent("detail_name").expected("a signal detail name"), - ]), - "=>", - UseIdent("handler").expected("the name of a function to handle the signal"), - Match("(").expected("argument list"), - Optional(UseIdent("object")).expected("object identifier"), - Match(")").expected(), - ZeroOrMore(AnyOf( - [Keyword("swapped"), UseLiteral("swapped", True)], - [Keyword("after"), UseLiteral("after", True)], - )), - ) - ) - child = Group( ast.Child, [ @@ -105,7 +85,6 @@ def parse(tokens) -> T.Tuple[ast.UI, T.Optional[MultipleErrors]]: *OBJECT_CONTENT_HOOKS, binding, property, - signal, child, ), "}"), ]