[gobject-introspection/issue-328] Add support for element-type to GListModel



commit a43920bd054c73c4b1909394734cba8cbf24f86a
Author: Emmanuele Bassi <ebassi gnome org>
Date:   Sun Apr 26 13:17:15 2020 +0100

    Add support for element-type to GListModel
    
    GListModel is an interface for creating typed, list-like containers. The
    data stored is GObject instances, but it's useful to be able to annotate
    the actual type, for both documentation and code generation purposes.
    
    The annotation should be optional, to maintain backward compatibility.
    
    Fixes: #328

 giscanner/docwriter.py                             | 18 ++++++++----
 giscanner/girparser.py                             |  2 +-
 giscanner/girwriter.py                             | 11 ++++++--
 giscanner/introspectablepass.py                    |  3 +-
 giscanner/transformer.py                           |  3 ++
 .../Regress.test_list_model_none.page              | 30 ++++++++++++++++++++
 .../Regress.test_list_model_object.page            | 31 ++++++++++++++++++++
 .../Regress.test_list_model_none.page              | 32 +++++++++++++++++++++
 .../Regress.test_list_model_object.page            | 33 ++++++++++++++++++++++
 .../Regress.test_list_model_none.page              | 32 +++++++++++++++++++++
 .../Regress.test_list_model_object.page            | 32 +++++++++++++++++++++
 tests/scanner/Regress-1.0-expected.gir             | 31 ++++++++++++++++++++
 tests/scanner/Regress-1.0-sections-expected.txt    |  2 ++
 tests/scanner/regress.c                            | 30 ++++++++++++++++++++
 tests/scanner/regress.h                            |  6 ++++
 15 files changed, 286 insertions(+), 10 deletions(-)
---
diff --git a/giscanner/docwriter.py b/giscanner/docwriter.py
index 786da80d..e4a8f7c5 100644
--- a/giscanner/docwriter.py
+++ b/giscanner/docwriter.py
@@ -793,7 +793,11 @@ class DocFormatterPython(DocFormatterIntrospectableBase):
         return fundamental_types.get(name, name)
 
     def format_type(self, type_, link=False):
-        if isinstance(type_, (ast.List, ast.Array)):
+        if isinstance(type_, ast.List):
+            if type_.name == 'Gio.ListModel':
+                return 'Gio.ListModel(item_type=' + self.format_type(type_.element_type) + ')'
+            return '[' + self.format_type(type_.element_type) + ']'
+        elif isinstance(type_, ast.Array):
             return '[' + self.format_type(type_.element_type) + ']'
         elif isinstance(type_, ast.Map):
             return '{%s: %s}' % (self.format_type(type_.key_type),
@@ -930,10 +934,14 @@ class DocFormatterGjs(DocFormatterIntrospectableBase):
         return fundamental_types.get(name, name)
 
     def format_type(self, type_, link=False):
-        if isinstance(type_, ast.Array) and \
-           type_.element_type.target_fundamental in ('gint8', 'guint8'):
-            return 'ByteArray'
-        elif isinstance(type_, (ast.List, ast.Array)):
+        if isinstance(type_, ast.Array):
+            if type_.element_type.target_fundamental in ('gint8', 'guint8'):
+                return 'ByteArray'
+            else:
+                return 'Array(' + self.format_type(type_.element_type, link) + ')'
+        elif isinstance(type_, ast.List):
+            if type_.name == 'Gio.ListModel':
+                return 'Gio.ListModel({item_type: ' + self.format_type(type_.element_type) + '})'
             return 'Array(' + self.format_type(type_.element_type, link) + ')'
         elif isinstance(type_, ast.Map):
             return '{%s: %s}' % (self.format_type(type_.key_type, link),
diff --git a/giscanner/girparser.py b/giscanner/girparser.py
index 35206a41..d31b26cf 100644
--- a/giscanner/girparser.py
+++ b/giscanner/girparser.py
@@ -492,7 +492,7 @@ class GIRParser(object):
                 if ctype is None:
                     return ast.TypeUnknown()
                 return ast.Type(ctype=ctype)
-            elif name in ['GLib.List', 'GLib.SList']:
+            elif name in ['GLib.List', 'GLib.SList', 'Gio.ListModel']:
                 subchild = self._find_first_child(typenode,
                                                   list(map(_corens, ('callback', 'array',
                                                                 '    varargs', 'type'))))
diff --git a/giscanner/girwriter.py b/giscanner/girwriter.py
index d1333cb7..6eff6526 100644
--- a/giscanner/girwriter.py
+++ b/giscanner/girwriter.py
@@ -378,10 +378,15 @@ class GIRWriter(XMLWriter):
             with self.tagcontext('array', attrs):
                 self._write_type(ntype.element_type)
         elif isinstance(ntype, ast.List):
-            if ntype.name:
+            # GListModel element-type annotations are not mandatory
+            if ntype.name in ('Gio.ListModel', 'GListModel') and ntype.element_type is ast.TYPE_ANY:
                 attrs.insert(0, ('name', ntype.name))
-            with self.tagcontext('type', attrs):
-                self._write_type(ntype.element_type)
+                self.write_tag('type', attrs)
+            else:
+                if ntype.name:
+                    attrs.insert(0, ('name', ntype.name))
+                with self.tagcontext('type', attrs):
+                    self._write_type(ntype.element_type)
         elif isinstance(ntype, ast.Map):
             attrs.insert(0, ('name', 'GLib.HashTable'))
             with self.tagcontext('type', attrs):
diff --git a/giscanner/introspectablepass.py b/giscanner/introspectablepass.py
index e2056b42..8ac50064 100644
--- a/giscanner/introspectablepass.py
+++ b/giscanner/introspectablepass.py
@@ -89,7 +89,8 @@ class IntrospectablePass(object):
             return
 
         if (isinstance(node.type, (ast.List, ast.Array))
-        and node.type.element_type == ast.TYPE_ANY):
+        and node.type.element_type == ast.TYPE_ANY
+        and not (isinstance(node.type, ast.List) and node.type.name == 'Gio.ListModel')):
             self._parameter_warning(parent, node, "Missing (element-type) annotation")
             parent.introspectable = False
             return
diff --git a/giscanner/transformer.py b/giscanner/transformer.py
index bcabdedc..7f230a20 100644
--- a/giscanner/transformer.py
+++ b/giscanner/transformer.py
@@ -698,6 +698,9 @@ raise ValueError."""
         elif base in ('GHashTable', 'GLib.HashTable', 'GObject.HashTable'):
             return ast.Map(ast.TYPE_ANY, ast.TYPE_ANY, ctype=ctype, is_const=is_const,
                            complete_ctype=complete_ctype)
+        elif base in ('GListModel', 'Gio.ListModel'):
+            return ast.List('Gio.ListModel', ast.TYPE_ANY, ctype=ctype,
+                            is_const=is_const, complete_ctype=complete_ctype)
         return None
 
     def create_type_from_ctype_string(self, ctype, is_const=False,
diff --git a/tests/scanner/Regress-1.0-C-expected/Regress.test_list_model_none.page 
b/tests/scanner/Regress-1.0-C-expected/Regress.test_list_model_none.page
new file mode 100644
index 00000000..b1458649
--- /dev/null
+++ b/tests/scanner/Regress-1.0-C-expected/Regress.test_list_model_none.page
@@ -0,0 +1,30 @@
+<?xml version="1.0"?>
+<page id="Regress.test_list_model_none"
+      type="topic"
+      style="function"
+      xmlns="http://projectmallard.org/1.0/";
+      xmlns:api="http://projectmallard.org/experimental/api/";
+      xmlns:ui="http://projectmallard.org/1.0/ui/";>
+  <info>
+    <link xref="index" group="function" type="guide"/>
+    <api:function>
+      <api:returns>
+        <api:type>GListModel*</api:type>
+      </api:returns>
+      <api:name>regress_test_list_model_none</api:name>
+    </api:function>
+  </info>
+  <title>regress_test_list_model_none</title>
+  <synopsis><code mime="text/x-csrc">
+GListModel* regress_test_list_model_none (void);
+  </code></synopsis>
+  <p>Test GListModel with no annotation.</p>
+
+<terms>
+<item>
+<title><code>Returns</code></title>
+  <p>a GListModel</p>
+</item>
+</terms>
+
+</page>
diff --git a/tests/scanner/Regress-1.0-C-expected/Regress.test_list_model_object.page 
b/tests/scanner/Regress-1.0-C-expected/Regress.test_list_model_object.page
new file mode 100644
index 00000000..620789cd
--- /dev/null
+++ b/tests/scanner/Regress-1.0-C-expected/Regress.test_list_model_object.page
@@ -0,0 +1,31 @@
+<?xml version="1.0"?>
+<page id="Regress.test_list_model_object"
+      type="topic"
+      style="function"
+      xmlns="http://projectmallard.org/1.0/";
+      xmlns:api="http://projectmallard.org/experimental/api/";
+      xmlns:ui="http://projectmallard.org/1.0/ui/";>
+  <info>
+    <link xref="index" group="function" type="guide"/>
+    <api:function>
+      <api:returns>
+        <api:type>GListModel*</api:type>
+      </api:returns>
+      <api:name>regress_test_list_model_object</api:name>
+    </api:function>
+  </info>
+  <title>regress_test_list_model_object</title>
+  <synopsis><code mime="text/x-csrc">
+GListModel* regress_test_list_model_object (void);
+  </code></synopsis>
+  <p>Test GListModel return value with an element type annotation.</p>
+
+<terms>
+<item>
+<title><code>Returns</code></title>
+  <p>a GListModel
+  containing RegressTestObj values</p>
+</item>
+</terms>
+
+</page>
diff --git a/tests/scanner/Regress-1.0-Gjs-expected/Regress.test_list_model_none.page 
b/tests/scanner/Regress-1.0-Gjs-expected/Regress.test_list_model_none.page
new file mode 100644
index 00000000..099232e0
--- /dev/null
+++ b/tests/scanner/Regress-1.0-Gjs-expected/Regress.test_list_model_none.page
@@ -0,0 +1,32 @@
+<?xml version="1.0"?>
+<page id="Regress.test_list_model_none"
+      type="topic"
+      style="function"
+      xmlns="http://projectmallard.org/1.0/";
+      xmlns:api="http://projectmallard.org/experimental/api/";
+      xmlns:ui="http://projectmallard.org/1.0/ui/";>
+  <info>
+    <link xref="index" group="function" type="guide"/>
+    <api:function>
+      <api:returns>
+        <api:type>Gio.ListModel({item_type: void})</api:type>
+      </api:returns>
+      <api:name>regress_test_list_model_none</api:name>
+    </api:function>
+  </info>
+  <title>Regress.test_list_model_none</title>
+  <synopsis><code mime="text/x-gjs">
+function test_list_model_none(): Gio.ListModel({item_type: void}) {
+    // Gjs wrapper for regress_test_list_model_none()
+}
+  </code></synopsis>
+  <p>Test GListModel with no annotation.</p>
+
+<terms>
+<item>
+<title><code>Returns</code></title>
+  <p>a GListModel</p>
+</item>
+</terms>
+
+</page>
diff --git a/tests/scanner/Regress-1.0-Gjs-expected/Regress.test_list_model_object.page 
b/tests/scanner/Regress-1.0-Gjs-expected/Regress.test_list_model_object.page
new file mode 100644
index 00000000..618ca7e3
--- /dev/null
+++ b/tests/scanner/Regress-1.0-Gjs-expected/Regress.test_list_model_object.page
@@ -0,0 +1,33 @@
+<?xml version="1.0"?>
+<page id="Regress.test_list_model_object"
+      type="topic"
+      style="function"
+      xmlns="http://projectmallard.org/1.0/";
+      xmlns:api="http://projectmallard.org/experimental/api/";
+      xmlns:ui="http://projectmallard.org/1.0/ui/";>
+  <info>
+    <link xref="index" group="function" type="guide"/>
+    <api:function>
+      <api:returns>
+        <api:type>Gio.ListModel({item_type: Regress.TestObj})</api:type>
+      </api:returns>
+      <api:name>regress_test_list_model_object</api:name>
+    </api:function>
+  </info>
+  <title>Regress.test_list_model_object</title>
+  <synopsis><code mime="text/x-gjs">
+function test_list_model_object(): Gio.ListModel({item_type: Regress.TestObj}) {
+    // Gjs wrapper for regress_test_list_model_object()
+}
+  </code></synopsis>
+  <p>Test GListModel return value with an element type annotation.</p>
+
+<terms>
+<item>
+<title><code>Returns</code></title>
+  <p>a GListModel
+  containing RegressTestObj values</p>
+</item>
+</terms>
+
+</page>
diff --git a/tests/scanner/Regress-1.0-Python-expected/Regress.test_list_model_none.page 
b/tests/scanner/Regress-1.0-Python-expected/Regress.test_list_model_none.page
new file mode 100644
index 00000000..61cb3de1
--- /dev/null
+++ b/tests/scanner/Regress-1.0-Python-expected/Regress.test_list_model_none.page
@@ -0,0 +1,32 @@
+<?xml version="1.0"?>
+<page id="Regress.test_list_model_none"
+      type="topic"
+      style="function"
+      xmlns="http://projectmallard.org/1.0/";
+      xmlns:api="http://projectmallard.org/experimental/api/";
+      xmlns:ui="http://projectmallard.org/1.0/ui/";>
+  <info>
+    <link xref="index" group="function" type="guide"/>
+    <api:function>
+      <api:returns>
+        <api:type>Gio.ListModel(item_type=gpointer)</api:type>
+      </api:returns>
+      <api:name>regress_test_list_model_none</api:name>
+    </api:function>
+  </info>
+  <title>Regress.test_list_model_none</title>
+  <synopsis><code mime="text/x-python">
+@returns(Gio.ListModel(item_type=gpointer))
+def test_list_model_none():
+    # Python wrapper for regress_test_list_model_none()
+  </code></synopsis>
+  <p>Test GListModel with no annotation.</p>
+
+<terms>
+<item>
+<title><code>Returns</code></title>
+{formatter.format(node, node.retval.doc)}
+</item>
+</terms>
+
+</page>
diff --git a/tests/scanner/Regress-1.0-Python-expected/Regress.test_list_model_object.page 
b/tests/scanner/Regress-1.0-Python-expected/Regress.test_list_model_object.page
new file mode 100644
index 00000000..d9dca201
--- /dev/null
+++ b/tests/scanner/Regress-1.0-Python-expected/Regress.test_list_model_object.page
@@ -0,0 +1,32 @@
+<?xml version="1.0"?>
+<page id="Regress.test_list_model_object"
+      type="topic"
+      style="function"
+      xmlns="http://projectmallard.org/1.0/";
+      xmlns:api="http://projectmallard.org/experimental/api/";
+      xmlns:ui="http://projectmallard.org/1.0/ui/";>
+  <info>
+    <link xref="index" group="function" type="guide"/>
+    <api:function>
+      <api:returns>
+        <api:type>Gio.ListModel(item_type=Regress.TestObj)</api:type>
+      </api:returns>
+      <api:name>regress_test_list_model_object</api:name>
+    </api:function>
+  </info>
+  <title>Regress.test_list_model_object</title>
+  <synopsis><code mime="text/x-python">
+@returns(Gio.ListModel(item_type=Regress.TestObj))
+def test_list_model_object():
+    # Python wrapper for regress_test_list_model_object()
+  </code></synopsis>
+  <p>Test GListModel return value with an element type annotation.</p>
+
+<terms>
+<item>
+<title><code>Returns</code></title>
+{formatter.format(node, node.retval.doc)}
+</item>
+</terms>
+
+</page>
diff --git a/tests/scanner/Regress-1.0-expected.gir b/tests/scanner/Regress-1.0-expected.gir
index cef3b124..bcba3fbe 100644
--- a/tests/scanner/Regress-1.0-expected.gir
+++ b/tests/scanner/Regress-1.0-expected.gir
@@ -8041,6 +8041,37 @@ element-type annotation.</doc>
         </parameter>
       </parameters>
     </function>
+    <function name="test_list_model_none"
+              c:identifier="regress_test_list_model_none">
+      <doc xml:space="preserve"
+           filename="regress.c"
+           line="4689">Test GListModel with no annotation.</doc>
+      <source-position filename="regress.h" line="1538"/>
+      <return-value transfer-ownership="full">
+        <doc xml:space="preserve"
+             filename="regress.c"
+             line="4694">a GListModel</doc>
+        <type name="Gio.ListModel" c:type="GListModel*">
+          <type name="gpointer" c:type="gpointer"/>
+        </type>
+      </return-value>
+    </function>
+    <function name="test_list_model_object"
+              c:identifier="regress_test_list_model_object">
+      <doc xml:space="preserve"
+           filename="regress.c"
+           line="4704">Test GListModel return value with an element type annotation.</doc>
+      <source-position filename="regress.h" line="1541"/>
+      <return-value transfer-ownership="full">
+        <doc xml:space="preserve"
+             filename="regress.c"
+             line="4709">a GListModel
+  containing RegressTestObj values</doc>
+        <type name="Gio.ListModel" c:type="GListModel*">
+          <type name="TestObj"/>
+        </type>
+      </return-value>
+    </function>
     <function name="test_long" c:identifier="regress_test_long">
       <source-position filename="regress.h" line="73"/>
       <return-value transfer-ownership="none">
diff --git a/tests/scanner/Regress-1.0-sections-expected.txt b/tests/scanner/Regress-1.0-sections-expected.txt
index b35b3a9a..84f7ec67 100644
--- a/tests/scanner/Regress-1.0-sections-expected.txt
+++ b/tests/scanner/Regress-1.0-sections-expected.txt
@@ -159,6 +159,8 @@ regress_test_int64
 regress_test_int8
 regress_test_int_out_utf8
 regress_test_int_value_arg
+regress_test_list_model_none
+regress_test_list_model_object
 regress_test_long
 regress_test_multi_callback
 regress_test_multi_double_args
diff --git a/tests/scanner/regress.c b/tests/scanner/regress.c
index 3a63436b..e81d1989 100644
--- a/tests/scanner/regress.c
+++ b/tests/scanner/regress.c
@@ -4686,3 +4686,33 @@ regress_test_array_struct_in_none (RegressTestStructA *arr, gsize len)
   g_assert_cmpint (arr[2].some_int, ==, 303);
 }
 
+/**
+ * regress_test_list_model_none:
+ *
+ * Test GListModel with no annotation.
+ *
+ * Returns: (transfer full): a GListModel
+ */
+GListModel *
+regress_test_list_model_none (void)
+{
+  GListStore *res = g_list_store_new (regress_test_obj_get_type ());
+
+  return G_LIST_MODEL (res);
+}
+
+/**
+ * regress_test_list_model_object:
+ *
+ * Test GListModel return value with an element type annotation.
+ *
+ * Returns: (transfer full) (element-type RegressTestObj): a GListModel
+ *   containing RegressTestObj values
+ */
+GListModel *
+regress_test_list_model_object (void)
+{
+  GListStore *res = g_list_store_new (regress_test_obj_get_type ());
+
+  return G_LIST_MODEL (res);
+}
diff --git a/tests/scanner/regress.h b/tests/scanner/regress.h
index 0b239f14..9ff699fb 100644
--- a/tests/scanner/regress.h
+++ b/tests/scanner/regress.h
@@ -1534,4 +1534,10 @@ void regress_test_array_struct_in_full (RegressTestStructA *arr, gsize len);
 _GI_TEST_EXTERN
 void regress_test_array_struct_in_none (RegressTestStructA *arr, gsize len);
 
+_GI_TEST_EXTERN
+GListModel *regress_test_list_model_none (void);
+
+_GI_TEST_EXTERN
+GListModel *regress_test_list_model_object (void);
+
 #endif /* __GITESTTYPES_H__ */


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]