diff --git a/gtkblueprinttool/ast.py b/gtkblueprinttool/ast.py index 9076baf..5120139 100644 --- a/gtkblueprinttool/ast.py +++ b/gtkblueprinttool/ast.py @@ -455,15 +455,16 @@ class BaseAttribute(AstNode): """ A helper class for attribute syntax of the form `name: literal_value;`""" tag_name: str = "" + attr_name: str = "name" def emit_xml(self, xml: XmlEmitter): value = self.children[Value][0] translatable = isinstance(value, TranslatedStringValue) - xml.start_tag( - self.tag_name, - name=self.tokens["name"], - translatable="true" if translatable else None, - ) + attrs = { + self.attr_name: self.tokens["name"], + "translatable": "true" if translatable else None + } + xml.start_tag(self.tag_name, **attrs) if translatable: xml.put_text(value.string) else: diff --git a/gtkblueprinttool/completions.py b/gtkblueprinttool/completions.py index 67ddad6..facbfd4 100644 --- a/gtkblueprinttool/completions.py +++ b/gtkblueprinttool/completions.py @@ -75,7 +75,8 @@ def using_gtk(ast_node, match_variables): def namespace(ast_node, match_variables): yield Completion("Gtk", CompletionItemKind.Module, text="Gtk.") for ns in ast_node.root.children[ast.Import]: - yield Completion(ns.namespace, CompletionItemKind.Module, text=ns.namespace + ".") + if ns.gir_namespace is not None: + yield Completion(ns.gir_namespace.name, CompletionItemKind.Module, text=ns.gir_namespace.name + ".") @completer( diff --git a/gtkblueprinttool/completions_utils.py b/gtkblueprinttool/completions_utils.py index 33da321..575189b 100644 --- a/gtkblueprinttool/completions_utils.py +++ b/gtkblueprinttool/completions_utils.py @@ -40,9 +40,16 @@ def applies_to(*ast_types): return func return decorator -def completer(applies_in: T.List, matches: T.List=[]): +def completer(applies_in: T.List, matches: T.List=[], applies_in_subclass=None): def decorator(func): def inner(prev_tokens: T.List[Token], ast_node: ast.AstNode): + # For completers that apply in ObjectContent nodes, we can further + # check that the object is the right class + if applies_in_subclass is not None: + type = ast_node.root.gir.get_type(applies_in_subclass[1], applies_in_subclass[0]) + if ast_node.gir_class and not ast_node.gir_class.assignable_to(type): + return + any_match = len(matches) == 0 match_variables: T.List[str] = [] diff --git a/gtkblueprinttool/extensions/__init__.py b/gtkblueprinttool/extensions/__init__.py index 47a1c18..9b0cf01 100644 --- a/gtkblueprinttool/extensions/__init__.py +++ b/gtkblueprinttool/extensions/__init__.py @@ -7,7 +7,8 @@ from .gtk_layout import layout from .gtk_menu import menu from .gtk_size_group import widgets from .gtk_styles import styles +from .gtk_combo_box_text import items OBJECT_HOOKS = [menu] -OBJECT_CONTENT_HOOKS = [a11y, styles, layout, mime_types, patterns, suffixes, widgets] +OBJECT_CONTENT_HOOKS = [a11y, styles, layout, mime_types, patterns, suffixes, widgets, items] diff --git a/gtkblueprinttool/extensions/gtk_combo_box_text.py b/gtkblueprinttool/extensions/gtk_combo_box_text.py new file mode 100644 index 0000000..dfb66e1 --- /dev/null +++ b/gtkblueprinttool/extensions/gtk_combo_box_text.py @@ -0,0 +1,89 @@ +# gtk_combo_box_text.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 ..ast import BaseTypedAttribute +from ..ast_utils import AstNode, validate +from ..completions_utils import * +from ..gir import StringType +from ..lsp_utils import Completion, CompletionItemKind +from ..parse_tree import * +from ..parser_utils import * +from ..xml_emitter import XmlEmitter + + +class Items(AstNode): + @validate("items") + def container_is_combo_box_text(self): + self.validate_parent_type("Gtk", "ComboBoxText", "combo box items") + + + def emit_xml(self, xml: XmlEmitter): + xml.start_tag("items") + for child in self.children: + child.emit_xml(xml) + xml.end_tag() + + +class Item(BaseTypedAttribute): + tag_name = "item" + attr_name = "id" + + @property + def value_type(self): + return StringType() + + +item = Group( + Item, + Sequence( + Optional( + Sequence( + UseIdent("name"), + Op(":"), + ) + ), + value, + ) +) + +items = Group( + Items, + Statement( + Keyword("items", True), + OpenBracket(), + Delimited( + item, + Comma() + ), + CloseBracket(), + ) +) + + +@completer( + applies_in=[ast.ObjectContent], + applies_in_subclass=("Gtk", "ComboBoxText"), + matches=new_statement_patterns, +) +def items_completer(ast_node, match_variables): + yield Completion( + "items", CompletionItemKind.Snippet, + snippet="items [$0];" + ) diff --git a/gtkblueprinttool/extensions/gtk_file_filter.py b/gtkblueprinttool/extensions/gtk_file_filter.py index 6e696bf..41c044d 100644 --- a/gtkblueprinttool/extensions/gtk_file_filter.py +++ b/gtkblueprinttool/extensions/gtk_file_filter.py @@ -75,12 +75,11 @@ suffixes = create_node("suffixes", "suffix") @completer( applies_in=[ast.ObjectContent], + applies_in_subclass=("Gtk", "FileFilter"), 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\"];") + 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/extensions/gtk_layout.py b/gtkblueprinttool/extensions/gtk_layout.py index 2c43686..45195cc 100644 --- a/gtkblueprinttool/extensions/gtk_layout.py +++ b/gtkblueprinttool/extensions/gtk_layout.py @@ -70,6 +70,7 @@ layout = Group( @completer( applies_in=[ast.ObjectContent], + applies_in_subclass=("Gtk", "Widget"), matches=new_statement_patterns, ) def layout_completer(ast_node, match_variables): diff --git a/gtkblueprinttool/extensions/gtk_size_group.py b/gtkblueprinttool/extensions/gtk_size_group.py index f2ca9d8..e0ec42d 100644 --- a/gtkblueprinttool/extensions/gtk_size_group.py +++ b/gtkblueprinttool/extensions/gtk_size_group.py @@ -77,9 +77,8 @@ widgets = Group( @completer( applies_in=[ast.ObjectContent], + applies_in_subclass=("Gtk", "SizeGroup"), matches=new_statement_patterns, ) -def file_filter_completer(ast_node, match_variables): - file_filter = ast_node.root.gir.get_type("SizeGroup", "Gtk") - if ast_node.gir_class and ast_node.gir_class.assignable_to(file_filter): - yield Completion("widgets", CompletionItemKind.Snippet, snippet="widgets [$0];") +def size_group_completer(ast_node, match_variables): + yield Completion("widgets", CompletionItemKind.Snippet, snippet="widgets [$0];") diff --git a/gtkblueprinttool/extensions/gtk_styles.py b/gtkblueprinttool/extensions/gtk_styles.py index 04153a5..364c1fd 100644 --- a/gtkblueprinttool/extensions/gtk_styles.py +++ b/gtkblueprinttool/extensions/gtk_styles.py @@ -63,6 +63,7 @@ styles = Group( @completer( applies_in=[ast.ObjectContent], + applies_in_subclass=("Gtk", "Widget"), matches=new_statement_patterns, ) def style_completer(ast_node, match_variables): diff --git a/tests/samples/combo_box_text.blp b/tests/samples/combo_box_text.blp new file mode 100644 index 0000000..954c1ea --- /dev/null +++ b/tests/samples/combo_box_text.blp @@ -0,0 +1,9 @@ +using Gtk 4.0; + +ComboBoxText { + items [ + "Hello, world!", + _("Hello!"), + item_id: "item", + ]; +} diff --git a/tests/samples/combo_box_text.ui b/tests/samples/combo_box_text.ui new file mode 100644 index 0000000..bb234d3 --- /dev/null +++ b/tests/samples/combo_box_text.ui @@ -0,0 +1,11 @@ + + + + + + Hello, world! + Hello! + item + + + diff --git a/tests/test_samples.py b/tests/test_samples.py index 1e73cc4..76adaef 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("combo_box_text") self.assert_sample("file_filter") self.assert_sample("flags") self.assert_sample("id_prop")