[gtksourceview/wip/chergert/gsv-gtk4: 244/259] snippets: add support for snippet tooltips




commit d28943efa816ef7f23c26ecfd356e02bc44645b4
Author: Christian Hergert <chergert redhat com>
Date:   Wed Sep 9 16:48:30 2020 -0700

    snippets: add support for snippet tooltips
    
    Long term we still need to handle translations for these, but this gets
    things started.

 data/snippets/licenses.snippets                |  15 +++
 data/snippets/snippets.rng                     |   6 ++
 gtksourceview/gtksourcesnippetbundle-private.h |   1 +
 gtksourceview/gtksourcesnippetbundle.c         | 142 ++++++++++++++++++++-----
 4 files changed, 136 insertions(+), 28 deletions(-)
---
diff --git a/data/snippets/licenses.snippets b/data/snippets/licenses.snippets
index 83456c6c..6ef197dd 100644
--- a/data/snippets/licenses.snippets
+++ b/data/snippets/licenses.snippets
@@ -23,6 +23,9 @@
 -->
 <snippets _group="Licenses">
   <snippet _name="GPLv3 or later" trigger="gpl3" _description="File header with GPLv3+ license">
+    <tooltip position="1" text="The name of the file belongs here"/>
+    <tooltip position="2" text="The name of the author belongs here"/>
+    <tooltip position="3" text="The email address of the author belongs here"/>
     <text languages="python;python3;"><![CDATA[# ${1:$TM_FILENAME}
 #
 # Copyright $CURRENT_YEAR ${2:$NAME} <${3:$EMAIL}>
@@ -84,6 +87,9 @@ $0]]></text>
 $0]]></text>
   </snippet>
   <snippet _name="LGPLv3 or later" trigger="lgpl3" _description="File header with LGPLv3 or later license">
+    <tooltip position="1" text="The name of the file belongs here"/>
+    <tooltip position="2" text="The name of the author belongs here"/>
+    <tooltip position="3" text="The email address of the author belongs here"/>
     <text languages="python;python3;"><![CDATA[# ${1:$TM_FILENAME}
 #
 # Copyright $CURRENT_YEAR ${2:$NAME} <${3:$EMAIL}>
@@ -145,6 +151,9 @@ $0]]></text>
 $0]]></text>
   </snippet>
   <snippet _name="LGPLv2.1 or later" trigger="lgpl2" _description="File header with LGPL 2.1 or later 
license">
+    <tooltip position="1" text="The name of the file belongs here"/>
+    <tooltip position="2" text="The name of the author belongs here"/>
+    <tooltip position="3" text="The email address of the author belongs here"/>
     <text languages="python;python3;"><![CDATA[# ${1:$TM_FILENAME}
 #
 # Copyright $CURRENT_YEAR ${2:$NAME} <${3:$EMAIL}>
@@ -206,6 +215,9 @@ $0]]></text>
 $0]]></text>
   </snippet>
   <snippet _name="Apache 2.0" trigger="apache2" _description="File header with Apache 2.0 license">
+    <tooltip position="1" text="The name of the file belongs here"/>
+    <tooltip position="2" text="The name of the author belongs here"/>
+    <tooltip position="3" text="The email address of the author belongs here"/>
     <text languages="python;python3;"><![CDATA[# ${1:$TM_FILENAME}
 #
 # Copyright $CURRENT_YEAR ${2:$NAME} <${3:$EMAIL}>
@@ -264,6 +276,9 @@ $0]]></text>
 $0]]></text>
   </snippet>
   <snippet _name="MIT" trigger="mit" _description="File header with MIT license">
+    <tooltip position="1" text="The name of the file belongs here"/>
+    <tooltip position="2" text="The name of the author belongs here"/>
+    <tooltip position="3" text="The email address of the author belongs here"/>
     <text languages="python;python3;"><![CDATA[# ${1:$TM_FILENAME}
 #
 # Copyright $CURRENT_YEAR ${2:$NAME} <${3:$EMAIL}>
diff --git a/data/snippets/snippets.rng b/data/snippets/snippets.rng
index 769ea9e5..fff388ed 100644
--- a/data/snippets/snippets.rng
+++ b/data/snippets/snippets.rng
@@ -50,6 +50,12 @@
               <text/>
             </element>
           </oneOrMore>
+          <oneOrMore>
+            <element name="tooltip">
+              <attribute name="position"/>
+              <attribute name="text"/>
+            </element>
+          </oneOrMore>
         </element>
       </oneOrMore>
     </optional>
diff --git a/gtksourceview/gtksourcesnippetbundle-private.h b/gtksourceview/gtksourcesnippetbundle-private.h
index 8c3b9cb6..0e8a99b3 100644
--- a/gtksourceview/gtksourcesnippetbundle-private.h
+++ b/gtksourceview/gtksourcesnippetbundle-private.h
@@ -28,6 +28,7 @@ G_BEGIN_DECLS
 
 typedef struct
 {
+       int          identifier;
        const gchar *group;
        const gchar *name;
        const gchar *trigger;
diff --git a/gtksourceview/gtksourcesnippetbundle.c b/gtksourceview/gtksourcesnippetbundle.c
index 79062967..8a2cc651 100644
--- a/gtksourceview/gtksourcesnippetbundle.c
+++ b/gtksourceview/gtksourcesnippetbundle.c
@@ -20,14 +20,22 @@
 #include "config.h"
 
 #include "gtksourcesnippet.h"
-#include "gtksourcesnippetchunk.h"
+#include "gtksourcesnippetchunk-private.h"
 #include "gtksourcesnippetbundle-private.h"
 #include "gtksourcesnippetmanager-private.h"
 
+typedef struct
+{
+       guint identifier;
+       guint focus_position;
+       const char *text;
+} GtkSourceSnippetTooltip;
+
 struct _GtkSourceSnippetBundle
 {
-       GObject  parent_instance;
-       GArray  *infos;
+       GObject     parent_instance;
+       GArray     *infos;
+       GArray     *tooltips;
 };
 
 typedef struct
@@ -40,6 +48,7 @@ typedef struct
        gchar                   *trigger;
        gchar                  **languages;
        GString                 *text;
+       guint                    last_identifier;
 } ParseState;
 
 static void list_model_iface_init (GListModelInterface *iface);
@@ -47,6 +56,27 @@ 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 const char *
+find_tooltip (GtkSourceSnippetBundle *self,
+              guint                   identifier,
+              guint                   focus_position)
+{
+       g_assert (GTK_SOURCE_IS_SNIPPET_BUNDLE (self));
+
+       for (guint i = 0; i < self->tooltips->len; i++)
+       {
+               const GtkSourceSnippetTooltip *tooltip = &g_array_index (self->tooltips, 
GtkSourceSnippetTooltip, i);
+
+               if (tooltip->identifier == identifier &&
+                   tooltip->focus_position == focus_position)
+               {
+                       return tooltip->text;
+               }
+       }
+
+       return NULL;
+}
+
 static gint
 compare_infos (const GtkSourceSnippetInfo *info_a,
               const GtkSourceSnippetInfo *info_b)
@@ -80,6 +110,7 @@ gtk_source_snippet_bundle_finalize (GObject *object)
        GtkSourceSnippetBundle *self = (GtkSourceSnippetBundle *)object;
 
        g_clear_pointer (&self->infos, g_array_unref);
+       g_clear_pointer (&self->tooltips, g_array_unref);
 
        G_OBJECT_CLASS (_gtk_source_snippet_bundle_parent_class)->finalize (object);
 }
@@ -97,6 +128,7 @@ static void
 _gtk_source_snippet_bundle_init (GtkSourceSnippetBundle *self)
 {
        self->infos = g_array_new (FALSE, FALSE, sizeof (GtkSourceSnippetInfo));
+       self->tooltips = g_array_new (FALSE, FALSE, sizeof (GtkSourceSnippetTooltip));
 }
 
 static void
@@ -150,16 +182,16 @@ elements_start_element (GMarkupParseContext  *context,
 
        if (g_strcmp0 (element_name, "text") == 0)
        {
-               const gchar *languages = NULL;
+               const char *languages = NULL;
 
                if (!g_markup_collect_attributes (element_name, attribute_names, attribute_values, error,
-                                                 G_MARKUP_COLLECT_STRING | G_MARKUP_COLLECT_OPTIONAL, 
"languages", &languages,
+                                                 G_MARKUP_COLLECT_STRING | G_MARKUP_COLLECT_OPTIONAL, 
"languages", &languages,
                                                  G_MARKUP_COLLECT_INVALID))
                        return;
 
                if (languages != NULL && languages[0] != 0)
                {
-                       gchar **strv = g_strsplit (languages, ";", 0);
+                       char **strv = g_strsplit (languages, ";", 0);
 
                        g_strfreev (state->languages);
                        state->languages = g_steal_pointer (&strv);
@@ -167,6 +199,24 @@ elements_start_element (GMarkupParseContext  *context,
 
                g_markup_parse_context_push (context, &text_parser, state);
        }
+       else if (g_strcmp0 (element_name, "tooltip") == 0)
+       {
+               GtkSourceSnippetTooltip tooltip;
+               const char *position = NULL;
+               const char *text = NULL;
+
+               if (!g_markup_collect_attributes (element_name, attribute_names, attribute_values, error,
+                                                 G_MARKUP_COLLECT_STRING, "position", &position,
+                                                 G_MARKUP_COLLECT_STRING, "text", &text,
+                                                 G_MARKUP_COLLECT_INVALID))
+                       return;
+
+               tooltip.identifier = state->last_identifier;
+               tooltip.focus_position = g_ascii_strtoll (position, NULL, 10);
+               tooltip.text = _gtk_source_snippet_manager_intern (state->manager, text);
+
+               g_array_append_val (state->self->tooltips, tooltip);
+       }
        else
        {
                g_set_error (error,
@@ -190,31 +240,33 @@ elements_end_element (GMarkupParseContext  *context,
        g_assert (GTK_SOURCE_IS_SNIPPET_BUNDLE (state->self));
        g_assert (element_name != NULL);
 
-       if (g_strcmp0 (element_name, "text") == 0 &&
-           state->languages != NULL &&
-           state->languages[0] != NULL)
+       if (g_strcmp0 (element_name, "text") == 0)
        {
-               GtkSourceSnippetInfo info;
+               if (state->languages != NULL && state->languages[0] != NULL)
+               {
+                       GtkSourceSnippetInfo info;
 
-               info.group = _gtk_source_snippet_manager_intern (state->manager, state->group);
-               info.name = _gtk_source_snippet_manager_intern (state->manager, state->name);
-               info.description = _gtk_source_snippet_manager_intern (state->manager, state->description);
-               info.trigger = _gtk_source_snippet_manager_intern (state->manager, state->trigger);
-               info.text = _gtk_source_snippet_manager_intern (state->manager, state->text->str);
+                       info.identifier = state->last_identifier;
+                       info.group = _gtk_source_snippet_manager_intern (state->manager, state->group);
+                       info.name = _gtk_source_snippet_manager_intern (state->manager, state->name);
+                       info.description = _gtk_source_snippet_manager_intern (state->manager, 
state->description);
+                       info.trigger = _gtk_source_snippet_manager_intern (state->manager, state->trigger);
+                       info.text = _gtk_source_snippet_manager_intern (state->manager, state->text->str);
 
-               for (guint i = 0; state->languages[i]; i++)
-               {
-                       info.language = _gtk_source_snippet_manager_intern (state->manager, 
state->languages[i]);
+                       for (guint i = 0; state->languages[i]; i++)
+                       {
+                               info.language = _gtk_source_snippet_manager_intern (state->manager, 
state->languages[i]);
 
-                       gtk_source_snippet_bundle_add (state->self, &info);
-               }
+                               gtk_source_snippet_bundle_add (state->self, &info);
+                       }
 
-       }
+               }
 
-       g_clear_pointer (&state->languages, g_strfreev);
-       g_string_truncate (state->text, 0);
+               g_clear_pointer (&state->languages, g_strfreev);
+               g_string_truncate (state->text, 0);
 
-       g_markup_parse_context_pop (context);
+               g_markup_parse_context_pop (context);
+       }
 }
 
 static const GMarkupParser elements_parser = {
@@ -249,6 +301,8 @@ snippet_start_element (GMarkupParseContext  *context,
                return;
        }
 
+       state->last_identifier++;
+
        if (!g_markup_collect_attributes (element_name, attribute_names, attribute_values, error,
                                          G_MARKUP_COLLECT_STRING, "trigger", &trigger,
                                          G_MARKUP_COLLECT_STRING | G_MARKUP_COLLECT_OPTIONAL, "_name", 
&_name,
@@ -391,6 +445,7 @@ gtk_source_snippet_bundle_parse (GtkSourceSnippetBundle  *self,
                state.self = self;
                state.manager = manager;
                state.text = g_string_new (NULL);
+               state.last_identifier = 0;
 
                context = g_markup_parse_context_new (&snippets_parser,
                                                      (G_MARKUP_TREAT_CDATA_AS_TEXT |
@@ -454,6 +509,8 @@ void
 _gtk_source_snippet_bundle_merge (GtkSourceSnippetBundle *self,
                                   GtkSourceSnippetBundle *other)
 {
+       guint max_id = 0;
+
        g_return_if_fail (GTK_SOURCE_IS_SNIPPET_BUNDLE (self));
        g_return_if_fail (!other || GTK_SOURCE_IS_SNIPPET_BUNDLE (other));
 
@@ -462,8 +519,27 @@ _gtk_source_snippet_bundle_merge (GtkSourceSnippetBundle *self,
                return;
        }
 
-       g_array_append_vals (self->infos, other->infos->data, other->infos->len);
+       for (guint i = 0; i < other->infos->len; i++)
+       {
+               const GtkSourceSnippetInfo *info = &g_array_index (other->infos, GtkSourceSnippetInfo, i);
+               max_id = MAX (max_id, info->identifier);
+       }
+
+       for (guint i = 0; i < other->infos->len; i++)
+       {
+               GtkSourceSnippetInfo info = g_array_index (other->infos, GtkSourceSnippetInfo, i);
+               info.identifier += max_id;
+               g_array_append_val (self->infos, info);
+       }
+
        g_array_sort (self->infos, (GCompareFunc) compare_infos);
+
+       for (guint i = 0; i < other->tooltips->len; i++)
+       {
+               GtkSourceSnippetTooltip tooltip = g_array_index (other->tooltips, GtkSourceSnippetTooltip, i);
+               tooltip.identifier += max_id;
+               g_array_append_val (self->tooltips, tooltip);
+       }
 }
 
 const gchar **
@@ -494,7 +570,8 @@ _gtk_source_snippet_bundle_list_groups (GtkSourceSnippetBundle *self)
 }
 
 static GtkSourceSnippet *
-create_snippet_from_info (const GtkSourceSnippetInfo *info)
+create_snippet_from_info (GtkSourceSnippetBundle     *self,
+                          const GtkSourceSnippetInfo *info)
 {
        GtkSourceSnippet *snippet;
        GPtrArray *chunks = NULL;
@@ -530,6 +607,15 @@ create_snippet_from_info (const GtkSourceSnippetInfo *info)
                for (guint i = 0; i < chunks->len; i++)
                {
                        GtkSourceSnippetChunk *chunk = g_ptr_array_index (chunks, i);
+
+                       if (chunk->focus_position >= 0)
+                       {
+                               gtk_source_snippet_chunk_set_tooltip_text (chunk,
+                                                                          find_tooltip (self,
+                                                                                        info->identifier,
+                                                                                        
chunk->focus_position));
+                       }
+
                        gtk_source_snippet_add_chunk (snippet, chunk);
                }
        }
@@ -594,7 +680,7 @@ _gtk_source_snippet_bundle_get_snippet (GtkSourceSnippetBundle *self,
 
                if (info_matches (info, group, language_id, trigger, FALSE))
                {
-                       return create_snippet_from_info (info);
+                       return create_snippet_from_info (self, info);
                }
        }
 
@@ -654,7 +740,7 @@ gtk_source_snippet_bundle_get_item (GListModel *model,
                return NULL;
        }
 
-       return create_snippet_from_info (&g_array_index (self->infos, GtkSourceSnippetInfo, position));
+       return create_snippet_from_info (self, &g_array_index (self->infos, GtkSourceSnippetInfo, position));
 }
 
 static void


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