[gtksourceview/wip/search] search: always scan all the buffer



commit d59a973c41b4e76839a519d0677563efb4becc23
Author: Sébastien Wilmet <swilmet gnome org>
Date:   Wed Jun 19 21:43:08 2013 +0200

    search: always scan all the buffer
    
    To count the number of occurrences, we have to scan all the buffer (in
    an idle).
    
    During the scan, the occurrences are highlighted. To navigate through
    the occurrences afterwards, we can thus use the function
    forward_to_tag_toggle(), and also use forward_search() since occurrences
    can be contiguous.
    
    Here only the highlight is done, the number of occurrences is not
    computed.

 gtksourceview/gtksourcesearch.c |  517 +++++++++++++++++++++++++--------------
 1 files changed, 333 insertions(+), 184 deletions(-)
---
diff --git a/gtksourceview/gtksourcesearch.c b/gtksourceview/gtksourcesearch.c
index 8b74c84..1b24ba0 100644
--- a/gtksourceview/gtksourcesearch.c
+++ b/gtksourceview/gtksourcesearch.c
@@ -27,25 +27,30 @@
 
 #include <string.h>
 
+/* Maximum number of lines to scan in one batch. */
+#define SCAN_BATCH_SIZE 50
+
 struct _GtkSourceSearchPrivate
 {
        GtkTextBuffer *buffer;
 
-       /* State of the search */
+       /* State of the search. If text is NULL, the search is disabled. */
        gchar *text;
        gint text_nb_lines;
        GtkTextSearchFlags flags;
 
-       /* The search occurrences are highlighted when they become visible. It
-        * would be useless to highlight all the buffer, if only a small part is
-        * visible, which is usually the case.
-        * region_not_highlighted is NULL if the search is disabled.
-        * region_to_highlight contains the region to highlight later (in an
-        * idle callback), so the highlighting is asynchronous.
+       /* The region to scan and highlight. If NULL, all the occurrences are
+        * highlighted, i.e. the scan is finished (or a scan was not needed, if
+        * the search is disabled).
         */
-       GtkTextRegion *region_not_highlighted;
-       GtkTextRegion *region_to_highlight;
-       gulong idle_highlight_id;
+       GtkTextRegion *region;
+
+       /* The region to scan and highlight in priority. I.e. the visible part
+        * of the buffer on the screen.
+        */
+       GtkTextRegion *high_priority_region;
+
+       gulong idle_scan_id;
 
        guint occurrences_count;
 
@@ -122,52 +127,321 @@ text_tag_set_highest_priority (GtkTextTag    *tag,
        gtk_text_tag_set_priority (tag, n - 1);
 }
 
+/* A TextRegion can contain empty subregions. So checking the number of
+ * subregions is not sufficient.
+ * When calling gtk_text_region_add() with equal iters, the subregion is not
+ * added. But when a subregion becomes empty, due to text deletion, the
+ * subregion is not removed from the TextRegion.
+ */
+static gboolean
+is_text_region_empty (GtkTextRegion *region)
+{
+       GtkTextRegionIterator region_iter;
+
+       if (region == NULL)
+       {
+               return TRUE;
+       }
+
+       gtk_text_region_get_iterator (region, &region_iter, 0);
+
+       while (!gtk_text_region_iterator_is_end (&region_iter))
+       {
+               GtkTextIter region_start;
+               GtkTextIter region_end;
+
+               gtk_text_region_iterator_get_subregion (&region_iter,
+                                                       &region_start,
+                                                       &region_end);
+
+               if (!gtk_text_iter_equal (&region_start, &region_end))
+               {
+                       return FALSE;
+               }
+
+               gtk_text_region_iterator_next (&region_iter);
+       }
+
+       return TRUE;
+}
+
+/* Sets @start and @end to the first non-empty subregion.
+ * Returns FALSE if the region is empty.
+ */
+static gboolean
+get_first_subregion (GtkTextRegion *region,
+                    GtkTextIter   *start,
+                    GtkTextIter   *end)
+{
+       GtkTextRegionIterator region_iter;
+
+       if (region == NULL)
+       {
+               return FALSE;
+       }
+
+       gtk_text_region_get_iterator (region, &region_iter, 0);
+
+       while (!gtk_text_region_iterator_is_end (&region_iter))
+       {
+               gtk_text_region_iterator_get_subregion (&region_iter, start, end);
+
+               if (!gtk_text_iter_equal (start, end))
+               {
+                       return TRUE;
+               }
+
+               gtk_text_region_iterator_next (&region_iter);
+       }
+
+       return FALSE;
+}
+
 static void
 clear_search (GtkSourceSearch *search)
 {
-       if (search->priv->region_not_highlighted != NULL)
+       GtkTextIter start;
+       GtkTextIter end;
+
+       if (search->priv->region != NULL)
        {
-               gtk_text_region_destroy (search->priv->region_not_highlighted, TRUE);
-               search->priv->region_not_highlighted = NULL;
+               gtk_text_region_destroy (search->priv->region, TRUE);
+               search->priv->region = NULL;
        }
 
-       if (search->priv->region_to_highlight != NULL)
+       if (search->priv->high_priority_region != NULL)
        {
-               gtk_text_region_destroy (search->priv->region_to_highlight, TRUE);
-               search->priv->region_to_highlight = NULL;
+               gtk_text_region_destroy (search->priv->high_priority_region, TRUE);
+               search->priv->high_priority_region = NULL;
        }
 
-       if (search->priv->idle_highlight_id != 0)
+       if (search->priv->idle_scan_id != 0)
        {
-               g_source_remove (search->priv->idle_highlight_id);
-               search->priv->idle_highlight_id = 0;
+               g_source_remove (search->priv->idle_scan_id);
+               search->priv->idle_scan_id = 0;
        }
 
-       if (search->priv->found_tag != NULL)
+       gtk_text_buffer_get_bounds (search->priv->buffer, &start, &end);
+
+       gtk_text_buffer_remove_tag (search->priv->buffer,
+                                   search->priv->found_tag,
+                                   &start,
+                                   &end);
+
+       search->priv->occurrences_count = 0;
+}
+
+static void
+highlight_subregion (GtkSourceSearch *search,
+                    GtkTextIter     *start,
+                    GtkTextIter     *end)
+{
+       GtkTextIter iter;
+       GtkTextIter *limit;
+       gboolean found = TRUE;
+
+       /* Make sure the 'found' tag has the priority over syntax highlighting
+        * tags. */
+       text_tag_set_highest_priority (search->priv->found_tag,
+                                      search->priv->buffer);
+
+       g_return_if_fail (search->priv->text_nb_lines > 0);
+
+       if (!gtk_text_iter_starts_line (start))
        {
-               GtkTextIter start;
-               GtkTextIter end;
+               gtk_text_iter_set_line_offset (start, 0);
+       }
 
-               gtk_text_buffer_get_bounds (search->priv->buffer, &start, &end);
+       if (!gtk_text_iter_ends_line (end))
+       {
+               gtk_text_iter_forward_to_line_end (end);
+       }
 
-               gtk_text_buffer_remove_tag (search->priv->buffer,
-                                           search->priv->found_tag,
-                                           &start,
-                                           &end);
+       gtk_text_iter_backward_lines (start, search->priv->text_nb_lines - 1);
+       gtk_text_iter_forward_lines (end, search->priv->text_nb_lines - 1);
+
+       if (gtk_text_iter_has_tag (start, search->priv->found_tag) &&
+           !gtk_text_iter_ends_tag (start, search->priv->found_tag))
+       {
+               gtk_text_iter_forward_to_tag_toggle (start, search->priv->found_tag);
        }
 
-       search->priv->occurrences_count = 0;
+       if (gtk_text_iter_has_tag (end, search->priv->found_tag) &&
+           !gtk_text_iter_begins_tag (end, search->priv->found_tag))
+       {
+               gtk_text_iter_backward_to_tag_toggle (end, search->priv->found_tag);
+       }
+
+       /*
+       g_print ("[%u (%u), %u (%u)]\n", gtk_text_iter_get_line (start), gtk_text_iter_get_offset (start),
+                                        gtk_text_iter_get_line (end), gtk_text_iter_get_offset (end));
+       */
+
+       gtk_text_buffer_remove_tag (search->priv->buffer,
+                                   search->priv->found_tag,
+                                   start,
+                                   end);
+
+       iter = *start;
+
+       if (gtk_text_iter_is_end (end))
+       {
+               limit = NULL;
+       }
+       else
+       {
+               limit = end;
+       }
+
+       do
+       {
+               GtkTextIter match_start;
+               GtkTextIter match_end;
+
+               found = gtk_text_iter_forward_search (&iter,
+                                                     search->priv->text,
+                                                     search->priv->flags,
+                                                     &match_start,
+                                                     &match_end,
+                                                     limit);
+
+               if (found)
+               {
+                       gtk_text_buffer_apply_tag (search->priv->buffer,
+                                                  search->priv->found_tag,
+                                                  &match_start,
+                                                  &match_end);
+               }
+
+               iter = match_end;
+
+       } while (found);
+
+       if (search->priv->region != NULL)
+       {
+               gtk_text_region_subtract (search->priv->region, start, end);
+       }
+       else
+       {
+               g_warning ("search: region is NULL");
+       }
+}
+
+static void
+highlight_all_region (GtkSourceSearch *search,
+                     GtkTextRegion   *region_to_highlight)
+{
+       gint nb_subregions = gtk_text_region_subregions (region_to_highlight);
+       GtkTextIter start_search;
+       GtkTextIter end_search;
+
+       if (nb_subregions == 0)
+       {
+               return;
+       }
+
+       gtk_text_region_nth_subregion (region_to_highlight,
+                                      0,
+                                      &start_search,
+                                      NULL);
+
+       gtk_text_region_nth_subregion (region_to_highlight,
+                                      nb_subregions - 1,
+                                      NULL,
+                                      &end_search);
+
+       gtk_text_iter_order (&start_search, &end_search);
+
+       highlight_subregion (search, &start_search, &end_search);
+}
+
+/* Scan a chunk of the region. If the region is small enough, all the region
+ * will be scanned. But if the region is big, scanning only the chunk will not
+ * block the UI normally.
+ */
+static void
+highlight_chunk_region (GtkSourceSearch *search)
+{
+       gint nb_remaining_lines = SCAN_BATCH_SIZE;
+       GtkTextIter start;
+       GtkTextIter end;
+
+       while (nb_remaining_lines > 0 &&
+              get_first_subregion (search->priv->region, &start, &end))
+       {
+               GtkTextIter limit = start;
+               gint start_line;
+               gint limit_line;
+
+               gtk_text_iter_forward_lines (&limit, nb_remaining_lines);
+
+               if (gtk_text_iter_compare (&end, &limit) < 0)
+               {
+                       limit = end;
+               }
+
+               highlight_subregion (search, &start, &limit);
+
+               start_line = gtk_text_iter_get_line (&start);
+               limit_line = gtk_text_iter_get_line (&limit);
+
+               nb_remaining_lines -= limit_line - start_line;
+       }
+}
+
+static gboolean
+idle_scan_cb (GtkSourceSearch *search)
+{
+       gboolean finished = FALSE;
+
+       if (search->priv->high_priority_region != NULL)
+       {
+               /* Normally the high priority region is not really big, since it
+                * is the visible area on the screen. So we can highlight it in
+                * one batch.
+                */
+               highlight_all_region (search, search->priv->high_priority_region);
+
+               gtk_text_region_destroy (search->priv->high_priority_region, TRUE);
+               search->priv->high_priority_region = NULL;
+       }
+       else
+       {
+               highlight_chunk_region (search);
+       }
+
+       if (is_text_region_empty (search->priv->region))
+       {
+               finished = TRUE;
+
+               if (search->priv->region != NULL)
+               {
+                       gtk_text_region_destroy (search->priv->region, TRUE);
+                       search->priv->region = NULL;
+               }
+       }
+
+       return !finished;
 }
 
 static void
-add_not_highlighted_subregion (GtkSourceSearch *search,
-                              GtkTextIter     *subregion_start,
-                              GtkTextIter     *subregion_end)
+install_idle_scan (GtkSourceSearch *search)
+{
+       if (search->priv->idle_scan_id == 0)
+       {
+               search->priv->idle_scan_id = g_idle_add ((GSourceFunc)idle_scan_cb, search);
+       }
+}
+
+static void
+add_subregion_to_scan (GtkSourceSearch *search,
+                      GtkTextIter     *subregion_start,
+                      GtkTextIter     *subregion_end)
 {
        GtkTextIter start = *subregion_start;
        GtkTextIter end = *subregion_end;
 
-       if (search->priv->region_not_highlighted == NULL)
+       if (is_text_region_empty (search->priv->region))
        {
                return;
        }
@@ -182,10 +456,15 @@ add_not_highlighted_subregion (GtkSourceSearch *search,
                gtk_text_iter_forward_to_line_end (&end);
        }
 
-       gtk_text_region_add (search->priv->region_not_highlighted, &start, &end);
+       gtk_text_region_add (search->priv->region, &start, &end);
 
-       gtk_text_iter_backward_lines (&start, search->priv->text_nb_lines);
-       gtk_text_iter_forward_lines (&end, search->priv->text_nb_lines);
+       install_idle_scan (search);
+
+       /* The highlighting can be modified a bit backward and forward the
+        * region.
+        */
+       gtk_text_iter_backward_lines (&start, search->priv->text_nb_lines - 1);
+       gtk_text_iter_forward_lines (&end, search->priv->text_nb_lines - 1);
 
        g_signal_emit_by_name (search->priv->buffer, "highlight-updated", &start, &end);
 }
@@ -208,10 +487,12 @@ update (GtkSourceSearch *search)
                return;
        }
 
-       search->priv->region_not_highlighted = gtk_text_region_new (search->priv->buffer);
+       search->priv->region = gtk_text_region_new (search->priv->buffer);
 
        gtk_text_buffer_get_bounds (search->priv->buffer, &start, &end);
-       add_not_highlighted_subregion (search, &start, &end);
+       add_subregion_to_scan (search, &start, &end);
+
+       install_idle_scan (search);
 }
 
 static void
@@ -228,7 +509,7 @@ insert_text_after_cb (GtkSourceSearch *search,
        gtk_text_iter_backward_chars (&start,
                                      g_utf8_strlen (text, length));
 
-       add_not_highlighted_subregion (search, &start, &end);
+       add_subregion_to_scan (search, &start, &end);
 }
 
 static void
@@ -236,7 +517,7 @@ delete_range_after_cb (GtkSourceSearch *search,
                       GtkTextIter     *start,
                       GtkTextIter     *end)
 {
-       add_not_highlighted_subregion (search, start, end);
+       add_subregion_to_scan (search, start, end);
 }
 
 static void
@@ -259,6 +540,8 @@ set_buffer (GtkSourceSearch *search,
                                 G_CALLBACK (delete_range_after_cb),
                                 search,
                                 G_CONNECT_AFTER | G_CONNECT_SWAPPED);
+
+       init_found_tag (search);
 }
 
 static void
@@ -397,136 +680,6 @@ _gtk_source_search_get_flags (GtkSourceSearch *search)
        return search->priv->flags;
 }
 
-static void
-highlight_subregion (GtkSourceSearch *search,
-                    GtkTextIter     *start,
-                    GtkTextIter     *end)
-{
-       GtkTextIter iter;
-       gboolean found = TRUE;
-
-       if (search->priv->found_tag == NULL)
-       {
-               init_found_tag (search);
-       }
-
-       /* Make sure the 'found' tag has the priority over syntax highlighting
-        * tags. */
-       text_tag_set_highest_priority (search->priv->found_tag,
-                                      search->priv->buffer);
-
-       g_return_if_fail (search->priv->text_nb_lines > 0);
-
-       gtk_text_iter_backward_lines (start, search->priv->text_nb_lines);
-       gtk_text_iter_forward_lines (end, search->priv->text_nb_lines);
-
-       if (gtk_text_iter_has_tag (start, search->priv->found_tag) &&
-           !gtk_text_iter_begins_tag (start, search->priv->found_tag))
-       {
-               gtk_text_iter_backward_to_tag_toggle (start, search->priv->found_tag);
-       }
-
-       if (gtk_text_iter_has_tag (end, search->priv->found_tag) &&
-           !gtk_text_iter_ends_tag (end, search->priv->found_tag))
-       {
-               gtk_text_iter_forward_to_tag_toggle (end, search->priv->found_tag);
-       }
-
-       /*
-       g_print ("[%u (%u), %u (%u)]\n", gtk_text_iter_get_line (start), gtk_text_iter_get_offset (start),
-                                        gtk_text_iter_get_line (end), gtk_text_iter_get_offset (end));
-       */
-
-       gtk_text_buffer_remove_tag (search->priv->buffer,
-                                   search->priv->found_tag,
-                                   start,
-                                   end);
-
-       iter = *start;
-
-       if (search->priv->region_not_highlighted != NULL)
-       {
-               gtk_text_region_subtract (search->priv->region_not_highlighted, start, end);
-       }
-       else
-       {
-               g_warning ("search: region_not_highlighted is NULL");
-       }
-
-       if (gtk_text_iter_is_end (end))
-       {
-               end = NULL;
-       }
-
-       do
-       {
-               GtkTextIter match_start;
-               GtkTextIter match_end;
-
-               found = gtk_text_iter_forward_search (&iter,
-                                                     search->priv->text,
-                                                     search->priv->flags,
-                                                     &match_start,
-                                                     &match_end,
-                                                     end);
-
-               if (found)
-               {
-                       gtk_text_buffer_apply_tag (search->priv->buffer,
-                                                  search->priv->found_tag,
-                                                  &match_start,
-                                                  &match_end);
-               }
-
-               iter = match_end;
-
-       } while (found);
-}
-
-static void
-highlight_region (GtkSourceSearch *search,
-                 GtkTextRegion   *region_to_highlight)
-{
-       gint nb_subregions = gtk_text_region_subregions (region_to_highlight);
-       GtkTextIter start_search;
-       GtkTextIter end_search;
-
-       if (nb_subregions == 0)
-       {
-               return;
-       }
-
-       gtk_text_region_nth_subregion (region_to_highlight,
-                                      0,
-                                      &start_search,
-                                      NULL);
-
-       gtk_text_region_nth_subregion (region_to_highlight,
-                                      nb_subregions - 1,
-                                      NULL,
-                                      &end_search);
-
-       gtk_text_iter_order (&start_search, &end_search);
-
-       highlight_subregion (search, &start_search, &end_search);
-}
-
-static gboolean
-idle_highlight_cb (GtkSourceSearch *search)
-{
-       search->priv->idle_highlight_id = 0;
-
-       if (search->priv->region_to_highlight != NULL)
-       {
-               highlight_region (search, search->priv->region_to_highlight);
-
-               gtk_text_region_destroy (search->priv->region_to_highlight, TRUE);
-               search->priv->region_to_highlight = NULL;
-       }
-
-       return FALSE;
-}
-
 void
 _gtk_source_search_update_highlight (GtkSourceSearch   *search,
                                     const GtkTextIter *start,
@@ -539,12 +692,13 @@ _gtk_source_search_update_highlight (GtkSourceSearch   *search,
        g_return_if_fail (start != NULL);
        g_return_if_fail (end != NULL);
 
-       if (dispose_has_run (search) || search->priv->region_not_highlighted == NULL)
+       if (dispose_has_run (search) ||
+           is_text_region_empty (search->priv->region))
        {
                return;
        }
 
-       region_to_highlight = gtk_text_region_intersect (search->priv->region_not_highlighted,
+       region_to_highlight = gtk_text_region_intersect (search->priv->region,
                                                         start,
                                                         end);
 
@@ -555,27 +709,22 @@ _gtk_source_search_update_highlight (GtkSourceSearch   *search,
 
        if (synchronous)
        {
-               highlight_region (search, region_to_highlight);
+               highlight_all_region (search, region_to_highlight);
                gtk_text_region_destroy (region_to_highlight, TRUE);
        }
        else
        {
-               if (search->priv->region_to_highlight != NULL)
+               if (search->priv->high_priority_region != NULL)
                {
-                       /* We destroy the old region to highlight, because it is
-                        * now useless to highlight it, the visible region on
-                        * the screen is the new region.
+                       /* The high priority region is used to highlight the
+                        * region visible on screen. So if we are here, that
+                        * means that the visible region has changed. So we can
+                        * destroy the old high_priority_region.
                         */
-                       gtk_text_region_destroy (search->priv->region_to_highlight, TRUE);
+                       gtk_text_region_destroy (search->priv->high_priority_region, TRUE);
                }
 
-               search->priv->region_to_highlight = region_to_highlight;
-
-               if (search->priv->idle_highlight_id == 0)
-               {
-                       search->priv->idle_highlight_id =
-                               g_idle_add ((GSourceFunc)idle_highlight_cb, search);
-               }
+               search->priv->high_priority_region = region_to_highlight;
        }
 }
 


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