[gtksourceview/wip/chergert/snippets] snippets: more XML parsing and query/lookup of snippets



commit 5c3c1ff38360d67710f9d694c3861f964fed23fc
Author: Christian Hergert <chergert redhat com>
Date:   Tue Jan 28 15:14:53 2020 -0800

    snippets: more XML parsing and query/lookup of snippets

 gtksourceview/gtksourcesnippetbundle-private.h |  24 ++-
 gtksourceview/gtksourcesnippetbundle.c         | 229 ++++++++++++++++++++++++-
 gtksourceview/gtksourcesnippetchunk-private.h  |   3 +
 gtksourceview/gtksourcesnippetmanager.c        | 159 +++++++++++++----
 gtksourceview/gtksourcesnippetmanager.h        |  21 ++-
 tests/test-snippets.c                          |  24 ++-
 6 files changed, 410 insertions(+), 50 deletions(-)
---
diff --git a/gtksourceview/gtksourcesnippetbundle-private.h b/gtksourceview/gtksourcesnippetbundle-private.h
index 0b1e0d75..1cb71d79 100644
--- a/gtksourceview/gtksourcesnippetbundle-private.h
+++ b/gtksourceview/gtksourcesnippetbundle-private.h
@@ -19,7 +19,7 @@
 
 #pragma once
 
-#include <glib-object.h>
+#include <gio/gio.h>
 
 #include "gtksourcetypes.h"
 #include "gtksourcetypes-private.h"
@@ -40,7 +40,25 @@ typedef struct
 
 G_DECLARE_FINAL_TYPE (GtkSourceSnippetBundle, _gtk_source_snippet_bundle, GTK_SOURCE, SNIPPET_BUNDLE, 
GObject)
 
-GtkSourceSnippetBundle *_gtk_source_snippet_bundle_new_from_file (const gchar             *path,
-                                                                  GtkSourceSnippetManager *manager);
+G_GNUC_INTERNAL
+GtkSourceSnippetBundle  *_gtk_source_snippet_bundle_new           (void);
+G_GNUC_INTERNAL
+GtkSourceSnippetBundle  *_gtk_source_snippet_bundle_new_from_file (const gchar             *path,
+                                                                   GtkSourceSnippetManager *manager);
+G_GNUC_INTERNAL
+void                     _gtk_source_snippet_bundle_merge         (GtkSourceSnippetBundle  *self,
+                                                                   GtkSourceSnippetBundle  *other);
+G_GNUC_INTERNAL
+const gchar            **_gtk_source_snippet_bundle_list_groups   (GtkSourceSnippetBundle  *self);
+G_GNUC_INTERNAL
+GtkSourceSnippet        *_gtk_source_snippet_bundle_get_snippet   (GtkSourceSnippetBundle  *self,
+                                                                   const gchar             *group,
+                                                                   const gchar             *language_id,
+                                                                   const gchar             *trigger);
+G_GNUC_INTERNAL
+GListModel              *_gtk_source_snippet_bundle_list_matching (GtkSourceSnippetBundle  *self,
+                                                                   const gchar             *group,
+                                                                   const gchar             *language_id,
+                                                                   const gchar             *trigger_prefix);
 
 G_END_DECLS
diff --git a/gtksourceview/gtksourcesnippetbundle.c b/gtksourceview/gtksourcesnippetbundle.c
index 24ac6e08..2ee758aa 100644
--- a/gtksourceview/gtksourcesnippetbundle.c
+++ b/gtksourceview/gtksourcesnippetbundle.c
@@ -41,7 +41,37 @@ typedef struct
        GString                 *text;
 } ParseState;
 
-G_DEFINE_TYPE (GtkSourceSnippetBundle, _gtk_source_snippet_bundle, G_TYPE_OBJECT)
+static void list_model_iface_init (GListModelInterface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (GtkSourceSnippetBundle, _gtk_source_snippet_bundle, G_TYPE_OBJECT,
+                        G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, list_model_iface_init))
+
+static gint
+compare_infos (const GtkSourceSnippetInfo *info_a,
+              const GtkSourceSnippetInfo *info_b)
+{
+       gint ret = g_strcmp0 (info_a->language, info_b->language);
+
+       if (ret == 0)
+       {
+               ret = g_strcmp0 (info_a->trigger, info_b->trigger);
+       }
+
+       return ret;
+}
+
+static void
+gtk_source_snippet_bundle_dispose (GObject *object)
+{
+       GtkSourceSnippetBundle *self = (GtkSourceSnippetBundle *)object;
+
+       if (self->infos->len > 0)
+       {
+               g_array_remove_range (self->infos, 0, self->infos->len);
+       }
+
+       G_OBJECT_CLASS (_gtk_source_snippet_bundle_parent_class)->dispose (object);
+}
 
 static void
 gtk_source_snippet_bundle_finalize (GObject *object)
@@ -58,6 +88,7 @@ _gtk_source_snippet_bundle_class_init (GtkSourceSnippetBundleClass *klass)
 {
        GObjectClass *object_class = G_OBJECT_CLASS (klass);
 
+       object_class->dispose = gtk_source_snippet_bundle_dispose;
        object_class->finalize = gtk_source_snippet_bundle_finalize;
 }
 
@@ -361,9 +392,9 @@ gtk_source_snippet_bundle_parse (GtkSourceSnippetBundle  *self,
                state.text = g_string_new (NULL);
 
                context = g_markup_parse_context_new (&snippets_parser,
-                                                     (G_MARKUP_TREAT_CDATA_AS_TEXT |
-                                                      G_MARKUP_PREFIX_ERROR_POSITION),
-                                                     &state, NULL);
+                                                     (G_MARKUP_TREAT_CDATA_AS_TEXT |
+                                                      G_MARKUP_PREFIX_ERROR_POSITION),
+                                                      &state, NULL);
 
                ret = g_markup_parse_context_parse (context, contents, length, NULL);
 
@@ -377,6 +408,8 @@ gtk_source_snippet_bundle_parse (GtkSourceSnippetBundle  *self,
                g_markup_parse_context_free (context);
                g_free (contents);
 
+               g_array_sort (self->infos, (GCompareFunc) compare_infos);
+
 #if 0
                for (guint i = 0; i < self->infos->len; i++)
                {
@@ -390,6 +423,13 @@ gtk_source_snippet_bundle_parse (GtkSourceSnippetBundle  *self,
        return ret;
 }
 
+
+GtkSourceSnippetBundle *
+_gtk_source_snippet_bundle_new (void)
+{
+       return g_object_new (GTK_SOURCE_TYPE_SNIPPET_BUNDLE, NULL);
+}
+
 GtkSourceSnippetBundle *
 _gtk_source_snippet_bundle_new_from_file (const gchar             *path,
                                           GtkSourceSnippetManager *manager)
@@ -399,7 +439,7 @@ _gtk_source_snippet_bundle_new_from_file (const gchar             *path,
        g_return_val_if_fail (path != NULL, NULL);
        g_return_val_if_fail (GTK_SOURCE_IS_SNIPPET_MANAGER (manager), NULL);
 
-       self = g_object_new (GTK_SOURCE_TYPE_SNIPPET_BUNDLE, NULL);
+       self = _gtk_source_snippet_bundle_new ();
 
        if (!gtk_source_snippet_bundle_parse (self, manager, path))
        {
@@ -408,3 +448,182 @@ _gtk_source_snippet_bundle_new_from_file (const gchar             *path,
 
        return g_steal_pointer (&self);
 }
+
+void
+_gtk_source_snippet_bundle_merge (GtkSourceSnippetBundle *self,
+                                  GtkSourceSnippetBundle *other)
+{
+       g_return_if_fail (GTK_SOURCE_IS_SNIPPET_BUNDLE (self));
+       g_return_if_fail (!other || GTK_SOURCE_IS_SNIPPET_BUNDLE (other));
+
+       if (other == NULL || other->infos->len == 0)
+       {
+               return;
+       }
+
+       g_array_append_vals (self->infos, other->infos->data, other->infos->len);
+       g_array_sort (self->infos, (GCompareFunc) compare_infos);
+}
+
+const gchar **
+_gtk_source_snippet_bundle_list_groups (GtkSourceSnippetBundle *self)
+{
+       GHashTable *ht;
+       guint len;
+
+       g_return_val_if_fail (GTK_SOURCE_IS_SNIPPET_BUNDLE (self), NULL);
+
+       ht = g_hash_table_new (NULL, NULL);
+
+       for (guint i = 0; i < self->infos->len; i++)
+       {
+               const GtkSourceSnippetInfo *info = &g_array_index (self->infos, GtkSourceSnippetInfo, i);
+
+               /* We can use pointer comparison because all of these strings
+                * are interned using the same #GStringChunk with
+                * g_string_chunk_insert_const().
+                */
+               if (!g_hash_table_contains (ht, info->group))
+               {
+                       g_hash_table_add (ht, (gchar *)info->group);
+               }
+       }
+
+       return (const gchar **)g_hash_table_get_keys_as_array (ht, &len);
+}
+
+static GtkSourceSnippet *
+create_snippet_from_info (const GtkSourceSnippetInfo *info)
+{
+       GtkSourceSnippet *snippet;
+
+       g_assert (info != NULL);
+
+       snippet = gtk_source_snippet_new (info->trigger, info->language);
+       gtk_source_snippet_set_description (snippet, info->description);
+
+       g_warning ("TODO: Parse snippet text");
+
+       return g_steal_pointer (&snippet);
+}
+
+static gboolean
+info_matches (const GtkSourceSnippetInfo *info,
+              const gchar                *group,
+              const gchar                *language_id,
+              const gchar                *trigger,
+              gboolean                    trigger_prefix_only)
+{
+       g_assert (info != NULL);
+
+       if (group != NULL && g_strcmp0 (group, info->group) != 0)
+               return FALSE;
+
+       if (language_id != NULL && g_strcmp0 (language_id, info->language) != 0)
+               return FALSE;
+
+       if (trigger != NULL)
+       {
+               if (info->trigger == NULL)
+                       return FALSE;
+
+               if (trigger_prefix_only)
+               {
+                       if (!g_str_has_prefix (info->trigger, trigger))
+                               return FALSE;
+               }
+               else
+               {
+                       if (!g_str_equal (trigger, info->trigger))
+                               return FALSE;
+               }
+       }
+
+       return TRUE;
+}
+
+GtkSourceSnippet *
+_gtk_source_snippet_bundle_get_snippet (GtkSourceSnippetBundle *self,
+                                        const gchar            *group,
+                                        const gchar            *language_id,
+                                        const gchar            *trigger)
+{
+       g_return_val_if_fail (GTK_SOURCE_IS_SNIPPET_BUNDLE (self), NULL);
+
+       /* TODO: This could use bsearch(), but the complication here is that
+        *       we want to ignore fields when the key field is NULL and the
+        *       sort order for infos doesn't match what we are querying, so
+        *       we would need an alternate index.
+        */
+
+       for (guint i = 0; i < self->infos->len; i++)
+       {
+               const GtkSourceSnippetInfo *info = &g_array_index (self->infos, GtkSourceSnippetInfo, i);
+
+               if (info_matches (info, group, language_id, trigger, FALSE))
+               {
+                       return create_snippet_from_info (info);
+               }
+       }
+
+       return NULL;
+}
+
+GListModel *
+_gtk_source_snippet_bundle_list_matching (GtkSourceSnippetBundle *self,
+                                          const gchar            *group,
+                                          const gchar            *language_id,
+                                          const gchar            *trigger_prefix)
+{
+       GtkSourceSnippetBundle *ret;
+
+       g_return_val_if_fail (GTK_SOURCE_IS_SNIPPET_BUNDLE (self), NULL);
+
+       ret = _gtk_source_snippet_bundle_new ();
+
+       for (guint i = 0; i < self->infos->len; i++)
+       {
+               const GtkSourceSnippetInfo *info = &g_array_index (self->infos, GtkSourceSnippetInfo, i);
+
+               if (info_matches (info, group, language_id, trigger_prefix, TRUE))
+               {
+                       g_array_append_vals (ret->infos, info, 1);
+               }
+       }
+
+       return G_LIST_MODEL (g_steal_pointer (&ret));
+}
+
+static GType
+gtk_source_snippet_bundle_get_item_type (GListModel *model)
+{
+       return GTK_SOURCE_TYPE_SNIPPET;
+}
+
+static guint
+gtk_source_snippet_bundle_get_n_items (GListModel *model)
+{
+       return GTK_SOURCE_SNIPPET_BUNDLE (model)->infos->len;
+}
+
+static gpointer
+gtk_source_snippet_bundle_get_item (GListModel *model,
+                                    guint       position)
+{
+       GtkSourceSnippetBundle *self = GTK_SOURCE_SNIPPET_BUNDLE (model);
+
+       if (position >= self->infos->len)
+       {
+               return NULL;
+       }
+
+       return create_snippet_from_info (&g_array_index (self->infos, GtkSourceSnippetInfo, position));
+}
+
+static void
+list_model_iface_init (GListModelInterface *iface)
+{
+       iface->get_item_type = gtk_source_snippet_bundle_get_item_type;
+       iface->get_n_items = gtk_source_snippet_bundle_get_n_items;
+       iface->get_item = gtk_source_snippet_bundle_get_item;
+}
diff --git a/gtksourceview/gtksourcesnippetchunk-private.h b/gtksourceview/gtksourcesnippetchunk-private.h
index e19d621f..052aa864 100644
--- a/gtksourceview/gtksourcesnippetchunk-private.h
+++ b/gtksourceview/gtksourcesnippetchunk-private.h
@@ -42,9 +42,12 @@ struct _GtkSourceSnippetChunk
        guint                    text_set : 1;
 };
 
+G_GNUC_INTERNAL
 void     _gtk_source_snippet_chunk_save_text  (GtkSourceSnippetChunk *chunk);
+G_GNUC_INTERNAL
 gboolean _gtk_source_snippet_chunk_contains   (GtkSourceSnippetChunk *chunk,
                                                const GtkTextIter     *iter);
+G_GNUC_INTERNAL
 gboolean _gtk_source_snippet_chunk_get_bounds (GtkSourceSnippetChunk *chunk,
                                                GtkTextIter           *begin,
                                                GtkTextIter           *end);
diff --git a/gtksourceview/gtksourcesnippetmanager.c b/gtksourceview/gtksourcesnippetmanager.c
index 886ee37b..d3a5a21b 100644
--- a/gtksourceview/gtksourcesnippetmanager.c
+++ b/gtksourceview/gtksourcesnippetmanager.c
@@ -47,10 +47,29 @@
 
 struct _GtkSourceSnippetManager
 {
-       GObject        parent_instance;
-       GStringChunk  *strings;
-       gchar        **snippet_dirs;
-       GPtrArray     *bundles;
+       GObject parent_instance;
+
+       /* To reduce the number of duplicated strings, we use a GStringChunk
+        * so that all of the GtkSourceSnippetInfo structs can point to const
+        * data. The bundles use _gtk_source_snippet_manager_intern() to get
+        * an "interned" string inside this string chunk.
+        */
+       GStringChunk *strings;
+
+       /* The snippet search path to look up files containing snippets like
+        * "license.snippets".
+        */
+       gchar **search_path;
+
+       /* The GtkSourceSnippetBundle handles both parsing a single snippet
+        * file on disk as well as collecting all the parsed files together.
+        * The strings contained in it are "const", and reference @strings
+        * to reduce duplicated memory as well as fragmentation.
+        *
+        * When searching for matching snippets, by language, name etc, we
+        * query the @bundle.
+        */
+       GtkSourceSnippetBundle *bundle;
 };
 
 enum {
@@ -64,13 +83,26 @@ static GParamSpec *properties[N_PROPS];
 
 G_DEFINE_TYPE (GtkSourceSnippetManager, gtk_source_snippet_manager, G_TYPE_OBJECT)
 
+static void
+gtk_source_snippet_manager_dispose (GObject *object)
+{
+       GtkSourceSnippetManager *self = GTK_SOURCE_SNIPPET_MANAGER (object);
+
+       if (self->bundle != NULL)
+       {
+               g_object_run_dispose (G_OBJECT (self->bundle));
+       }
+
+       G_OBJECT_CLASS (gtk_source_snippet_manager_parent_class)->dispose (object);
+}
+
 static void
 gtk_source_snippet_manager_finalize (GObject *object)
 {
        GtkSourceSnippetManager *self = GTK_SOURCE_SNIPPET_MANAGER (object);
 
-       g_clear_pointer (&self->bundles, g_ptr_array_unref);
-       g_clear_pointer (&self->snippet_dirs, g_strfreev);
+       g_clear_object (&self->bundle);
+       g_clear_pointer (&self->search_path, g_strfreev);
        g_clear_pointer (&self->strings, g_string_chunk_free);
 
        G_OBJECT_CLASS (gtk_source_snippet_manager_parent_class)->finalize (object);
@@ -121,7 +153,8 @@ gtk_source_snippet_manager_class_init (GtkSourceSnippetManagerClass *klass)
 {
        GObjectClass *object_class = G_OBJECT_CLASS (klass);
 
-       object_class->finalize  = gtk_source_snippet_manager_finalize;
+       object_class->dispose = gtk_source_snippet_manager_dispose;
+       object_class->finalize = gtk_source_snippet_manager_finalize;
        object_class->set_property = gtk_source_snippet_manager_set_property;
        object_class->get_property = gtk_source_snippet_manager_get_property;
 
@@ -148,7 +181,6 @@ gtk_source_snippet_manager_class_init (GtkSourceSnippetManagerClass *klass)
 static void
 gtk_source_snippet_manager_init (GtkSourceSnippetManager *self)
 {
-       self->bundles = g_ptr_array_new_with_free_func (g_object_unref);
 }
 
 /**
@@ -222,18 +254,18 @@ _gtk_source_snippet_manager_intern (GtkSourceSnippetManager *self,
  */
 void
 gtk_source_snippet_manager_set_search_path (GtkSourceSnippetManager *self,
-                                           const gchar * const     *dirs)
+                                            const gchar * const     *dirs)
 {
        gchar **tmp;
 
        g_return_if_fail (GTK_SOURCE_IS_SNIPPET_MANAGER (self));
 
-       tmp = self->snippet_dirs;
+       tmp = self->search_path;
 
        if (dirs == NULL)
-               self->snippet_dirs = _gtk_source_utils_get_default_dirs (SNIPPET_DIR);
+               self->search_path = _gtk_source_utils_get_default_dirs (SNIPPET_DIR);
        else
-               self->snippet_dirs = g_strdupv ((gchar **)dirs);
+               self->search_path = g_strdupv ((gchar **)dirs);
 
        g_strfreev (tmp);
 
@@ -258,15 +290,16 @@ gtk_source_snippet_manager_get_search_path (GtkSourceSnippetManager *self)
 {
        g_return_val_if_fail (GTK_SOURCE_IS_SNIPPET_MANAGER (self), NULL);
 
-       if (self->snippet_dirs == NULL)
-               self->snippet_dirs = _gtk_source_utils_get_default_dirs (SNIPPET_DIR);
+       if (self->search_path == NULL)
+               self->search_path = _gtk_source_utils_get_default_dirs (SNIPPET_DIR);
 
-       return (const gchar * const *)self->snippet_dirs;
+       return (const gchar * const *)self->search_path;
 }
 
 static void
 ensure_snippets (GtkSourceSnippetManager *self)
 {
+       GtkSourceSnippetBundle *bundle;
        GSList *filenames;
 
        g_assert (GTK_SOURCE_IS_SNIPPET_MANAGER (self));
@@ -276,44 +309,112 @@ ensure_snippets (GtkSourceSnippetManager *self)
                SNIPPET_FILE_SUFFIX,
                TRUE);
 
+       bundle = _gtk_source_snippet_bundle_new ();
+
        for (const GSList *f = filenames; f; f = f->next)
        {
                const gchar *filename = f->data;
-               GtkSourceSnippetBundle *bundle;
+               GtkSourceSnippetBundle *parsed;
 
-               bundle = _gtk_source_snippet_bundle_new_from_file (filename, self);
+               parsed = _gtk_source_snippet_bundle_new_from_file (filename, self);
 
-               if (bundle != NULL)
-                       g_ptr_array_add (self->bundles, bundle);
+               if (parsed != NULL)
+                       _gtk_source_snippet_bundle_merge (bundle, parsed);
                else
                        g_warning ("Error reading snippet file '%s'", filename);
+
+               g_clear_object (&parsed);
        }
 
+       g_clear_object (&self->bundle);
+       self->bundle = g_steal_pointer (&bundle);
+
        g_slist_free_full (filenames, g_free);
+
+       g_return_if_fail (GTK_SOURCE_IS_SNIPPET_BUNDLE (self->bundle));
 }
 
 /**
- * gtk_source_snippet_manager_get_snippets:
+ * gtk_source_snippet_manager_list_groups:
  * @self: a #GtkSourceSnippetManager
- * @language_id: (nullable): the language identifier for the snippets or %NULL
- *   for only global snippets
  *
- * Gets the snippets for a specific language.
+ * List all the known groups within the snippet manager.
  *
- * The resulting list also contains global snippets that apply to
- * any language, if any.
+ * The result should be freed with g_free(), and the invidual strings are
+ * owned by @self and should never be freed by the caller.
  *
- * Returns: (transfer full) (nullable): a #GListModel, or %NULL
+ * Returns: (transfer container) (array zero-terminated=1) (element-type utf8):
+ *   An array of strings which should be freed with g_free().
+ *
+ * Since: 5.0
+ */
+const gchar **
+gtk_source_snippet_manager_list_groups (GtkSourceSnippetManager *self)
+{
+       g_return_val_if_fail (GTK_SOURCE_IS_SNIPPET_MANAGER (self), NULL);
+
+       ensure_snippets (self);
+
+       return _gtk_source_snippet_bundle_list_groups (self->bundle);
+}
+
+/**
+ * gtk_source_snippet_manager_list_matching:
+ * @self: a #GtkSourceSnippetManager
+ * @group: (nullable): a group name or %NULL
+ * @language_id: (nullable): a #GtkSourceLanguage:id or %NULL
+ * @trigger_prefix: (nullable): a prefix for a trigger to activate
+ *
+ * Queries the known snippets for those matching @group, @language_id, and/or
+ * @trigger_prefix. If any of these are %NULL, they will be ignored when
+ * filtering the available snippets.
+ *
+ * The #GListModel only contains information about the available snippets until
+ * g_list_model_get_item() is called for a specific snippet. This helps reduce
+ * the number of #GObject's that are created at runtime to those needed by
+ * the calling application.
+ *
+ * Returns: (transfer full): a #GListModel of #GtkSourceSnippet.
  *
  * Since: 5.0
  */
 GListModel *
-gtk_source_snippet_manager_get_snippets (GtkSourceSnippetManager *self,
-                                         const gchar             *language_id)
+gtk_source_snippet_manager_list_matching (GtkSourceSnippetManager *self,
+                                          const gchar             *group,
+                                          const gchar             *language_id,
+                                          const gchar             *trigger_prefix)
+{
+       g_return_val_if_fail (GTK_SOURCE_IS_SNIPPET_MANAGER (self), NULL);
+
+       ensure_snippets (self);
+
+       return _gtk_source_snippet_bundle_list_matching (self->bundle, group, language_id, trigger_prefix);
+}
+
+/**
+ * gtk_source_snippet_manager_get_snippet:
+ * @self: a #GtkSourceSnippetManager
+ * @group: (nullable): a group name or %NULL
+ * @language_id: (nullable): a #GtkSourceLanguage:id or %NULL
+ * @trigger: the trigger for the snippet
+ *
+ * Queries the known snippets for the first matching @group, @language_id,
+ * and/or @trigger. If @group or @language_id are %NULL, they will be ignored.
+ *
+ * Returns: (transfer full) (nullable): a #GtkSourceSnippet or %NULL if no
+ *   matching snippet was found.
+ *
+ * Since: 5.0
+ */
+GtkSourceSnippet *
+gtk_source_snippet_manager_get_snippet (GtkSourceSnippetManager *self,
+                                        const gchar             *group,
+                                        const gchar             *language_id,
+                                        const gchar             *trigger)
 {
        g_return_val_if_fail (GTK_SOURCE_IS_SNIPPET_MANAGER (self), NULL);
 
        ensure_snippets (self);
 
-       return NULL;
+       return _gtk_source_snippet_bundle_get_snippet (self->bundle, group, language_id, trigger);
 }
diff --git a/gtksourceview/gtksourcesnippetmanager.h b/gtksourceview/gtksourcesnippetmanager.h
index ee9b4a95..b6f51b95 100644
--- a/gtksourceview/gtksourcesnippetmanager.h
+++ b/gtksourceview/gtksourcesnippetmanager.h
@@ -35,14 +35,23 @@ GTK_SOURCE_AVAILABLE_IN_ALL
 G_DECLARE_FINAL_TYPE (GtkSourceSnippetManager, gtk_source_snippet_manager, GTK_SOURCE, SNIPPET_MANAGER, 
GObject)
 
 GTK_SOURCE_AVAILABLE_IN_5_0
-GtkSourceSnippetManager *gtk_source_snippet_manager_get_default      (void);
+GtkSourceSnippetManager  *gtk_source_snippet_manager_get_default     (void);
 GTK_SOURCE_AVAILABLE_IN_5_0
-const gchar * const      *gtk_source_snippet_manager_get_search_path (GtkSourceSnippetManager  *self);
+const gchar * const      *gtk_source_snippet_manager_get_search_path (GtkSourceSnippetManager *self);
 GTK_SOURCE_AVAILABLE_IN_5_0
-void                      gtk_source_snippet_manager_set_search_path (GtkSourceSnippetManager  *self,
-                                                                      const gchar * const      *dirs);
+void                      gtk_source_snippet_manager_set_search_path (GtkSourceSnippetManager *self,
+                                                                      const gchar * const     *dirs);
 GTK_SOURCE_AVAILABLE_IN_5_0
-GListModel               *gtk_source_snippet_manager_get_snippets    (GtkSourceSnippetManager  *self,
-                                                                      const gchar              *language_id);
+GtkSourceSnippet         *gtk_source_snippet_manager_get_snippet     (GtkSourceSnippetManager *self,
+                                                                      const gchar             *group,
+                                                                      const gchar             *language_id,
+                                                                      const gchar             *trigger);
+GTK_SOURCE_AVAILABLE_IN_5_0
+const gchar             **gtk_source_snippet_manager_list_groups     (GtkSourceSnippetManager *self);
+GTK_SOURCE_AVAILABLE_IN_5_0
+GListModel               *gtk_source_snippet_manager_list_matching   (GtkSourceSnippetManager *self,
+                                                                      const gchar             *group,
+                                                                      const gchar             *language_id,
+                                                                      const gchar             
*trigger_prefix);
 
 G_END_DECLS
diff --git a/tests/test-snippets.c b/tests/test-snippets.c
index 2d1e26be..4ba45b60 100644
--- a/tests/test-snippets.c
+++ b/tests/test-snippets.c
@@ -22,24 +22,34 @@
 #include <gtksourceview/gtksource.h>
 #include <gtksourceview/gtksourceinit.h>
 
+static const gchar *search_path[] = {
+       TOP_SRCDIR"/data/snippets",
+       NULL
+};
+
 gint
 main (gint argc,
       gchar *argv[])
 {
        GtkSourceSnippetManager *mgr;
-       GListModel *model;
-       static const gchar *search_path[] = {
-               TOP_SRCDIR"/data/snippets",
-               NULL
-       };
+       GtkSourceSnippet *snippet;
+       const gchar **groups;
 
        gtk_source_init ();
 
        mgr = gtk_source_snippet_manager_get_default ();
        gtk_source_snippet_manager_set_search_path (mgr, search_path);
 
-       model = gtk_source_snippet_manager_get_snippets (mgr, "c");
-       g_clear_object (&model);
+       /* Update if you add new groups to data/snippets/ */
+       groups = gtk_source_snippet_manager_list_groups (mgr);
+       g_assert_cmpint (1, ==, g_strv_length ((gchar **)groups));
+       g_assert_cmpstr (groups[0], ==, "Licenses");
+       g_free (groups);
+
+       /* Make sure we can get gpl3 snippet for C language */
+       snippet = gtk_source_snippet_manager_get_snippet (mgr, NULL, "c", "gpl3");
+       g_assert_nonnull (snippet);
+       g_assert_finalize_object (snippet);
 
        gtk_source_finalize ();
 


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