From ebfa72d94f9e872b830d6672065b6bb20d036f21 Mon Sep 17 00:00:00 2001 From: James Westman Date: Fri, 12 Nov 2021 09:23:25 -0600 Subject: [PATCH] Add GtkFileFilter properties --- gtkblueprinttool/ast.py | 42 +++------- gtkblueprinttool/ast_utils.py | 8 ++ gtkblueprinttool/extensions/__init__.py | 3 +- .../extensions/gtk_file_filter.py | 81 +++++++++++++++++++ gtkblueprinttool/gir.py | 11 ++- gtkblueprinttool/lsp_utils.py | 2 +- tests/sample_errors/class_dne.err | 2 +- tests/sample_errors/not_a_class.err | 2 +- tests/sample_errors/ns_not_imported.err | 2 +- tests/samples/accessibility.blp | 1 + tests/samples/file_filter.blp | 8 ++ tests/samples/file_filter.ui | 17 ++++ tests/test_samples.py | 1 + 13 files changed, 141 insertions(+), 39 deletions(-) create mode 100644 gtkblueprinttool/extensions/gtk_file_filter.py create mode 100644 tests/samples/file_filter.blp create mode 100644 tests/samples/file_filter.ui diff --git a/gtkblueprinttool/ast.py b/gtkblueprinttool/ast.py index 52f0491..9076baf 100644 --- a/gtkblueprinttool/ast.py +++ b/gtkblueprinttool/ast.py @@ -135,37 +135,6 @@ class Import(AstNode): pass -class Template(AstNode): - @validate("namespace", "class_name") - def gir_class_exists(self): - if not self.tokens["ignore_gir"]: - self.root.gir.validate_class(self.tokens["class_name"], self.tokens["namespace"]) - - @property - def gir_class(self): - return self.root.gir.get_class(self.tokens["class_name"], self.tokens["namespace"]) - - - @docs("namespace") - def namespace_docs(self): - return self.root.gir.namespaces[self.tokens["namespace"]].doc - - @docs("class_name") - def class_docs(self): - if self.gir_class: - return self.gir_class.doc - - - def emit_xml(self, xml: XmlEmitter): - xml.start_tag("template", **{ - "class": self.tokens["name"], - "parent": self.gir_class.glib_type_name if self.gir_class else self.tokens["class_name"], - }) - for child in self.children: - child.emit_xml(xml) - xml.end_tag() - - class Object(AstNode): @validate("namespace") def gir_ns_exists(self): @@ -210,6 +179,17 @@ class Object(AstNode): xml.end_tag() +class Template(Object): + def emit_xml(self, xml: XmlEmitter): + xml.start_tag("template", **{ + "class": self.tokens["name"], + "parent": self.gir_class.glib_type_name if self.gir_class else self.tokens["class_name"], + }) + for child in self.children: + child.emit_xml(xml) + xml.end_tag() + + class Child(AstNode): def emit_xml(self, xml: XmlEmitter): xml.start_tag("child", type=self.tokens["child_type"]) diff --git a/gtkblueprinttool/ast_utils.py b/gtkblueprinttool/ast_utils.py index 502582f..169465a 100644 --- a/gtkblueprinttool/ast_utils.py +++ b/gtkblueprinttool/ast_utils.py @@ -64,6 +64,14 @@ class AstNode: else: return self.parent.root + def parent_by_type(self, type): + if self.parent is None: + return None + elif isinstance(self.parent, type): + return self.parent + else: + return self.parent.parent_by_type(type) + @lazy_prop def errors(self): return list(self._get_errors()) diff --git a/gtkblueprinttool/extensions/__init__.py b/gtkblueprinttool/extensions/__init__.py index 779bac6..79988ca 100644 --- a/gtkblueprinttool/extensions/__init__.py +++ b/gtkblueprinttool/extensions/__init__.py @@ -5,7 +5,8 @@ from .gtk_a11y import a11y from .gtk_menu import menu from .gtk_styles import styles from .gtk_layout import layout +from .gtk_file_filter import mime_types, patterns, suffixes OBJECT_HOOKS = [menu] -OBJECT_CONTENT_HOOKS = [a11y, styles, layout] +OBJECT_CONTENT_HOOKS = [a11y, styles, layout, mime_types, patterns, suffixes] diff --git a/gtkblueprinttool/extensions/gtk_file_filter.py b/gtkblueprinttool/extensions/gtk_file_filter.py new file mode 100644 index 0000000..a6dfaf6 --- /dev/null +++ b/gtkblueprinttool/extensions/gtk_file_filter.py @@ -0,0 +1,81 @@ +# gtk_file_filter.py +# +# Copyright 2021 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 .. import ast +from ..ast_utils import AstNode +from ..completions_utils import * +from ..lsp_utils import Completion, CompletionItemKind +from ..parse_tree import * +from ..parser_utils import * +from ..xml_emitter import XmlEmitter + + +class Filters(AstNode): + 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() + + +def create_node(tag_name: str, singular: str): + return Group( + Filters, + Statement( + Keyword(tag_name), + UseLiteral("tag_name", tag_name), + Op(":"), + Delimited( + Group( + FilterString, + Sequence( + UseQuoted("name"), + UseLiteral("tag_name", singular), + ) + ), + Comma(), + ), + ) + ) + + +mime_types = create_node("mime-types", "mime-type") +patterns = create_node("patterns", "pattern") +suffixes = create_node("suffixes", "suffix") + + +@completer( + applies_in=[ast.ObjectContent], + matches=new_statement_patterns, +) +def file_filter_completer(ast_node, match_variables): + file_filter = ast_node.root.gir.get_type("FileFilter", "Gtk") + if ast_node.gir_class and ast_node.gir_class.assignable_to(file_filter): + yield Completion("mime-types", CompletionItemKind.Snippet, snippet="mime-types \"$0\";") + yield Completion("patterns", CompletionItemKind.Snippet, snippet="patterns \"$0\";") + yield Completion("suffixes", CompletionItemKind.Snippet, snippet="suffixes \"$0\";") + diff --git a/gtkblueprinttool/gir.py b/gtkblueprinttool/gir.py index 103df35..77dc81c 100644 --- a/gtkblueprinttool/gir.py +++ b/gtkblueprinttool/gir.py @@ -60,12 +60,17 @@ class GirType: def doc(self): return None -class BasicType(GirType): - name: str = "unknown type" - def assignable_to(self, other) -> bool: raise NotImplementedError() + @property + def full_name(self) -> str: + raise NotImplementedError() + + +class BasicType(GirType): + name: str = "unknown type" + @property def full_name(self) -> str: return self.name diff --git a/gtkblueprinttool/lsp_utils.py b/gtkblueprinttool/lsp_utils.py index c14d2fa..062b7e4 100644 --- a/gtkblueprinttool/lsp_utils.py +++ b/gtkblueprinttool/lsp_utils.py @@ -91,7 +91,7 @@ class Completion: "documentation": { "kind": "markdown", "value": self.docs, - }, + } if self.docs else None, "deprecated": self.deprecated, "insertText": insert_text, "insertTextFormat": insert_text_format, diff --git a/tests/sample_errors/class_dne.err b/tests/sample_errors/class_dne.err index 0000a1e..573baf3 100644 --- a/tests/sample_errors/class_dne.err +++ b/tests/sample_errors/class_dne.err @@ -1 +1 @@ -3,25,17,Namespace Gtk does not contain a class called NotARealClass +3,29,13,Namespace Gtk does not contain a class called NotARealClass diff --git a/tests/sample_errors/not_a_class.err b/tests/sample_errors/not_a_class.err index 02287a2..d0a7260 100644 --- a/tests/sample_errors/not_a_class.err +++ b/tests/sample_errors/not_a_class.err @@ -1 +1 @@ -3,25,14,Gtk.Orientable is not a class +3,29,10,Gtk.Orientable is not a class diff --git a/tests/sample_errors/ns_not_imported.err b/tests/sample_errors/ns_not_imported.err index 4a4bef8..15a24d1 100644 --- a/tests/sample_errors/ns_not_imported.err +++ b/tests/sample_errors/ns_not_imported.err @@ -1 +1 @@ -3,25,21,Namespace Adw was not imported +3,25,3,Namespace Adw was not imported diff --git a/tests/samples/accessibility.blp b/tests/samples/accessibility.blp index 648b7c3..117457a 100644 --- a/tests/samples/accessibility.blp +++ b/tests/samples/accessibility.blp @@ -8,3 +8,4 @@ Gtk.Widget { } } Gtk.Label my_label {} + diff --git a/tests/samples/file_filter.blp b/tests/samples/file_filter.blp new file mode 100644 index 0000000..1164434 --- /dev/null +++ b/tests/samples/file_filter.blp @@ -0,0 +1,8 @@ +using Gtk 4.0; + +FileFilter { + name: "File Filter Name"; + mime-types: "text/plain", "image/ *"; + patterns: "*.txt"; + suffixes: "png"; +} diff --git a/tests/samples/file_filter.ui b/tests/samples/file_filter.ui new file mode 100644 index 0000000..1cb0114 --- /dev/null +++ b/tests/samples/file_filter.ui @@ -0,0 +1,17 @@ + + + + + File Filter Name + + text/plain + image/ * + + + *.txt + + + png + + + diff --git a/tests/test_samples.py b/tests/test_samples.py index cff20c7..3db1406 100644 --- a/tests/test_samples.py +++ b/tests/test_samples.py @@ -95,6 +95,7 @@ class TestSamples(unittest.TestCase): self.assert_sample("accessibility") self.assert_sample("binding") self.assert_sample("child_type") + self.assert_sample("file_filter") self.assert_sample("flags") self.assert_sample("id_prop") self.assert_sample("layout")