[gtksourceview/wip/search: 10/19] Asynchronous forward search



commit fca72bf18e4ae3f85821e7171ae358ec45d8ac09
Author: Sébastien Wilmet <swilmet gnome org>
Date:   Tue Jun 25 23:27:39 2013 +0200

    Asynchronous forward search

 gtksourceview/gtksourcebuffer.c |   32 ++++
 gtksourceview/gtksourcebuffer.h |   12 ++
 gtksourceview/gtksourcesearch.c |  366 +++++++++++++++++++++++++++++++--------
 gtksourceview/gtksourcesearch.h |   14 ++
 tests/test-search-ui.c          |   43 ++++--
 5 files changed, 385 insertions(+), 82 deletions(-)
---
diff --git a/gtksourceview/gtksourcebuffer.c b/gtksourceview/gtksourcebuffer.c
index 2037c69..fb24cbc 100644
--- a/gtksourceview/gtksourcebuffer.c
+++ b/gtksourceview/gtksourcebuffer.c
@@ -2705,6 +2705,38 @@ gtk_source_buffer_forward_search (GtkSourceBuffer   *buffer,
                                           match_end);
 }
 
+void
+gtk_source_buffer_forward_search_async (GtkSourceBuffer     *buffer,
+                                       const GtkTextIter   *iter,
+                                       GCancellable        *cancellable,
+                                       GAsyncReadyCallback  callback,
+                                       gpointer             user_data)
+{
+       g_return_if_fail (GTK_SOURCE_IS_BUFFER (buffer));
+
+       _gtk_source_search_forward_async (buffer->priv->search,
+                                         iter,
+                                         cancellable,
+                                         callback,
+                                         user_data);
+}
+
+gboolean
+gtk_source_buffer_forward_search_finish (GtkSourceBuffer  *buffer,
+                                        GAsyncResult     *result,
+                                        GtkTextIter      *match_start,
+                                        GtkTextIter      *match_end,
+                                        GError          **error)
+{
+       g_return_val_if_fail (GTK_SOURCE_IS_BUFFER (buffer), FALSE);
+
+       return _gtk_source_search_forward_finish (buffer->priv->search,
+                                                 result,
+                                                 match_start,
+                                                 match_end,
+                                                 error);
+}
+
 /**
  * gtk_source_buffer_backward_search:
  * @buffer: a #GtkSourceBuffer.
diff --git a/gtksourceview/gtksourcebuffer.h b/gtksourceview/gtksourcebuffer.h
index ea079ad..15e7f0e 100644
--- a/gtksourceview/gtksourcebuffer.h
+++ b/gtksourceview/gtksourcebuffer.h
@@ -210,6 +210,18 @@ gboolean            gtk_source_buffer_forward_search       (GtkSourceBuffer        
*buffer,
                                                                 GtkTextIter            *match_start,
                                                                 GtkTextIter            *match_end);
 
+void                    gtk_source_buffer_forward_search_async (GtkSourceBuffer        *buffer,
+                                                                const GtkTextIter      *iter,
+                                                                GCancellable           *cancellable,
+                                                                GAsyncReadyCallback     callback,
+                                                                gpointer                user_data);
+
+gboolean                gtk_source_buffer_forward_search_finish(GtkSourceBuffer        *buffer,
+                                                                GAsyncResult           *result,
+                                                                GtkTextIter            *match_start,
+                                                                GtkTextIter            *match_end,
+                                                                GError                **error);
+
 gboolean                gtk_source_buffer_backward_search      (GtkSourceBuffer        *buffer,
                                                                 const GtkTextIter      *iter,
                                                                 GtkTextIter            *match_start,
diff --git a/gtksourceview/gtksourcesearch.c b/gtksourceview/gtksourcesearch.c
index a5696f7..98f3324 100644
--- a/gtksourceview/gtksourcesearch.c
+++ b/gtksourceview/gtksourcesearch.c
@@ -130,6 +130,12 @@ struct _GtkSourceSearchPrivate
         */
        GtkTextRegion *high_priority_region;
 
+       /* An asynchronous running task. task_region has a higher priority than
+        * scan_region, but a lower priority than high_priority_region.
+        */
+       GTask *task;
+       GtkTextRegion *task_region;
+
        gulong idle_scan_id;
 
        guint occurrences_count;
@@ -139,78 +145,22 @@ struct _GtkSourceSearchPrivate
        guint at_word_boundaries : 1;
 };
 
-G_DEFINE_TYPE_WITH_PRIVATE (GtkSourceSearch, _gtk_source_search, G_TYPE_OBJECT);
-
-static gboolean
-dispose_has_run (GtkSourceSearch *search)
+typedef struct
 {
-       return search->priv->buffer == NULL;
-}
-
-static gboolean
-basic_forward_search (GtkSourceSearch   *search,
-                     const GtkTextIter *iter,
-                     GtkTextIter       *match_start,
-                     GtkTextIter       *match_end,
-                     const GtkTextIter *limit)
-{
-       GtkTextIter begin_search = *iter;
-
-       while (TRUE)
-       {
-               gboolean found = gtk_text_iter_forward_search (&begin_search,
-                                                              search->priv->text,
-                                                              search->priv->flags,
-                                                              match_start,
-                                                              match_end,
-                                                              limit);
-
-               if (!found || !search->priv->at_word_boundaries)
-               {
-                       return found;
-               }
+       GtkTextMark *start_at;
+       GtkTextIter match_start;
+       GtkTextIter match_end;
+       guint found : 1;
+} ForwardBackwardData;
 
-               if (gtk_text_iter_starts_word (match_start) &&
-                   gtk_text_iter_ends_word (match_end))
-               {
-                       return TRUE;
-               }
+G_DEFINE_TYPE_WITH_PRIVATE (GtkSourceSearch, _gtk_source_search, G_TYPE_OBJECT);
 
-               begin_search = *match_end;
-       }
-}
+static void            install_idle_scan               (GtkSourceSearch *search);
 
 static gboolean
-basic_backward_search (GtkSourceSearch   *search,
-                      const GtkTextIter *iter,
-                      GtkTextIter       *match_start,
-                      GtkTextIter       *match_end,
-                      const GtkTextIter *limit)
+dispose_has_run (GtkSourceSearch *search)
 {
-       GtkTextIter begin_search = *iter;
-
-       while (TRUE)
-       {
-               gboolean found = gtk_text_iter_backward_search (&begin_search,
-                                                               search->priv->text,
-                                                               search->priv->flags,
-                                                               match_start,
-                                                               match_end,
-                                                               limit);
-
-               if (!found || !search->priv->at_word_boundaries)
-               {
-                       return found;
-               }
-
-               if (gtk_text_iter_starts_word (match_start) &&
-                   gtk_text_iter_ends_word (match_end))
-               {
-                       return TRUE;
-               }
-
-               begin_search = *match_start;
-       }
+       return search->priv->buffer == NULL;
 }
 
 static void
@@ -339,6 +289,28 @@ get_first_subregion (GtkTextRegion *region,
 }
 
 static void
+clear_task (GtkSourceSearch *search)
+{
+       if (search->priv->task_region != NULL)
+       {
+               gtk_text_region_destroy (search->priv->task_region, TRUE);
+               search->priv->task_region = NULL;
+       }
+
+       if (search->priv->task != NULL)
+       {
+               GCancellable *cancellable = g_task_get_cancellable (search->priv->task);
+
+               if (cancellable != NULL)
+               {
+                       g_cancellable_cancel (cancellable);
+               }
+
+               g_clear_object (&search->priv->task);
+       }
+}
+
+static void
 clear_search (GtkSourceSearch *search)
 {
        if (search->priv->scan_region != NULL)
@@ -359,9 +331,181 @@ clear_search (GtkSourceSearch *search)
                search->priv->idle_scan_id = 0;
        }
 
+       clear_task (search);
+
        search->priv->occurrences_count = 0;
 }
 
+static gboolean
+basic_forward_search (GtkSourceSearch   *search,
+                     const GtkTextIter *iter,
+                     GtkTextIter       *match_start,
+                     GtkTextIter       *match_end,
+                     const GtkTextIter *limit)
+{
+       GtkTextIter begin_search = *iter;
+
+       while (TRUE)
+       {
+               gboolean found = gtk_text_iter_forward_search (&begin_search,
+                                                              search->priv->text,
+                                                              search->priv->flags,
+                                                              match_start,
+                                                              match_end,
+                                                              limit);
+
+               if (!found || !search->priv->at_word_boundaries)
+               {
+                       return found;
+               }
+
+               if (gtk_text_iter_starts_word (match_start) &&
+                   gtk_text_iter_ends_word (match_end))
+               {
+                       return TRUE;
+               }
+
+               begin_search = *match_end;
+       }
+}
+
+static gboolean
+basic_backward_search (GtkSourceSearch   *search,
+                      const GtkTextIter *iter,
+                      GtkTextIter       *match_start,
+                      GtkTextIter       *match_end,
+                      const GtkTextIter *limit)
+{
+       GtkTextIter begin_search = *iter;
+
+       while (TRUE)
+       {
+               gboolean found = gtk_text_iter_backward_search (&begin_search,
+                                                               search->priv->text,
+                                                               search->priv->flags,
+                                                               match_start,
+                                                               match_end,
+                                                               limit);
+
+               if (!found || !search->priv->at_word_boundaries)
+               {
+                       return found;
+               }
+
+               if (gtk_text_iter_starts_word (match_start) &&
+                   gtk_text_iter_ends_word (match_end))
+               {
+                       return TRUE;
+               }
+
+               begin_search = *match_start;
+       }
+}
+
+static void
+forward_backward_data_free (ForwardBackwardData *data)
+{
+       if (data->start_at != NULL)
+       {
+               GtkTextBuffer *buffer = gtk_text_mark_get_buffer (data->start_at);
+               gtk_text_buffer_delete_mark (buffer, data->start_at);
+       }
+
+       g_slice_free (ForwardBackwardData, data);
+}
+
+static void
+begin_smart_forward_search (GtkSourceSearch   *search,
+                           const GtkTextIter *start_at,
+                           GTask             *task)
+{
+       GtkTextIter iter = *start_at;
+       GtkTextIter limit;
+       GtkTextRegion *region = NULL;
+       ForwardBackwardData *task_data;
+
+       if (gtk_text_iter_is_end (start_at) ||
+           search->priv->text == NULL)
+       {
+               task_data = g_slice_new0 (ForwardBackwardData);
+               task_data->found = FALSE;
+
+               g_task_return_pointer (task,
+                                      task_data,
+                                      (GDestroyNotify)forward_backward_data_free);
+
+               g_object_unref (task);
+               return;
+       }
+
+       if (search->priv->found_tag == NULL)
+       {
+               init_found_tag (search);
+       }
+
+       if (!gtk_text_iter_has_tag (&iter, search->priv->found_tag) ||
+           gtk_text_iter_ends_tag (&iter, search->priv->found_tag))
+       {
+               gtk_text_iter_forward_to_tag_toggle (&iter, search->priv->found_tag);
+       }
+
+       limit = iter;
+       gtk_text_iter_forward_to_tag_toggle (&limit, search->priv->found_tag);
+
+       if (search->priv->scan_region != NULL)
+       {
+               region = gtk_text_region_intersect (search->priv->scan_region, start_at, &limit);
+       }
+
+       if (is_text_region_empty (region))
+       {
+               GtkTextIter match_start;
+               GtkTextIter match_end;
+               gboolean found;
+
+               found = basic_forward_search (search, &iter, &match_start, &match_end, &limit);
+
+               if (region != NULL)
+               {
+                       gtk_text_region_destroy (region, TRUE);
+               }
+
+               if (found)
+               {
+                       task_data = g_slice_new0 (ForwardBackwardData);
+                       task_data->found = TRUE;
+                       task_data->match_start = match_start;
+                       task_data->match_end = match_end;
+
+                       g_task_return_pointer (task,
+                                              task_data,
+                                              (GDestroyNotify)forward_backward_data_free);
+
+                       g_object_unref (task);
+                       return;
+               }
+
+               begin_smart_forward_search (search, &limit, task);
+               return;
+       }
+
+       task_data = g_slice_new0 (ForwardBackwardData);
+       task_data->start_at = gtk_text_buffer_create_mark (search->priv->buffer,
+                                                          NULL,
+                                                          start_at,
+                                                          TRUE);
+
+       g_task_set_task_data (task,
+                             task_data,
+                             (GDestroyNotify)forward_backward_data_free);
+
+       clear_task (search);
+       search->priv->task = g_object_ref (task);
+       search->priv->task_region = region;
+
+       install_idle_scan (search);
+}
+
 /* Adjust the subregion so we are sure that all matches that are visible or
  * partially visible between @start and @end are highlighted.
  */
@@ -622,6 +766,11 @@ scan_subregion (GtkSourceSearch *search,
                });
        }
 
+       if (search->priv->task_region != NULL)
+       {
+               gtk_text_region_subtract (search->priv->task_region, start, end);
+       }
+
        if (search->priv->text == NULL)
        {
                /* We have removed the found_tag, we are done. */
@@ -691,17 +840,18 @@ scan_all_region (GtkSourceSearch *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.
+ * block the UI normally. Begin the scan at the beginning of the region.
  */
 static void
-scan_chunk_region (GtkSourceSearch *search)
+scan_region_forward (GtkSourceSearch *search,
+                    GtkTextRegion   *region)
 {
        gint nb_remaining_lines = SCAN_BATCH_SIZE;
        GtkTextIter start;
        GtkTextIter end;
 
        while (nb_remaining_lines > 0 &&
-              get_first_subregion (search->priv->scan_region, &start, &end))
+              get_first_subregion (region, &start, &end))
        {
                GtkTextIter limit = start;
                gint start_line;
@@ -723,6 +873,30 @@ scan_chunk_region (GtkSourceSearch *search)
        }
 }
 
+static void
+scan_task_region (GtkSourceSearch *search)
+{
+       GTask *task = search->priv->task;
+       ForwardBackwardData *task_data = g_task_get_task_data (task);
+       GtkTextIter start_at;
+
+       scan_region_forward (search, search->priv->task_region);
+
+       if (search->priv->task_region != NULL)
+       {
+               gtk_text_region_destroy (search->priv->task_region, TRUE);
+               search->priv->task_region = NULL;
+       }
+
+       g_clear_object (&search->priv->task);
+
+       gtk_text_buffer_get_iter_at_mark (search->priv->buffer,
+                                         &start_at,
+                                         task_data->start_at);
+
+       begin_smart_forward_search (search, &start_at, task);
+}
+
 static gboolean
 idle_scan_cb (GtkSourceSearch *search)
 {
@@ -739,9 +913,13 @@ idle_scan_cb (GtkSourceSearch *search)
                gtk_text_region_destroy (search->priv->high_priority_region, TRUE);
                search->priv->high_priority_region = NULL;
        }
+       else if (search->priv->task_region != NULL)
+       {
+               scan_task_region (search);
+       }
        else
        {
-               scan_chunk_region (search);
+               scan_region_forward (search, search->priv->scan_region);
        }
 
        if (is_text_region_empty (search->priv->scan_region))
@@ -1276,6 +1454,54 @@ _gtk_source_search_forward (GtkSourceSearch   *search,
        return smart_forward_search (search, iter, match_start, match_end);
 }
 
+void
+_gtk_source_search_forward_async (GtkSourceSearch     *search,
+                                 const GtkTextIter   *iter,
+                                 GCancellable        *cancellable,
+                                 GAsyncReadyCallback  callback,
+                                 gpointer             user_data)
+{
+       GTask *task;
+
+       g_return_if_fail (GTK_SOURCE_IS_SEARCH (search));
+
+       task = g_task_new (search->priv->buffer, cancellable, callback, user_data);
+
+       begin_smart_forward_search (search, iter, task);
+}
+
+gboolean
+_gtk_source_search_forward_finish (GtkSourceSearch  *search,
+                                  GAsyncResult     *result,
+                                  GtkTextIter      *match_start,
+                                  GtkTextIter      *match_end,
+                                  GError          **error)
+{
+       ForwardBackwardData *data;
+       gboolean found;
+
+       g_return_val_if_fail (GTK_SOURCE_IS_SEARCH (search), FALSE);
+       g_return_val_if_fail (g_task_is_valid (result, search->priv->buffer), FALSE);
+
+       data = g_task_propagate_pointer (G_TASK (result), error);
+
+       if (data == NULL)
+       {
+               return FALSE;
+       }
+
+       found = data->found;
+
+       if (found)
+       {
+               *match_start = data->match_start;
+               *match_end = data->match_end;
+       }
+
+       forward_backward_data_free (data);
+       return found;
+}
+
 gboolean
 _gtk_source_search_backward (GtkSourceSearch   *search,
                             const GtkTextIter *iter,
diff --git a/gtksourceview/gtksourcesearch.h b/gtksourceview/gtksourcesearch.h
index ce9a143..34d5d04 100644
--- a/gtksourceview/gtksourcesearch.h
+++ b/gtksourceview/gtksourcesearch.h
@@ -87,6 +87,20 @@ gboolean             _gtk_source_search_forward                      (GtkSourceSearch      
  *search,
                                                                         GtkTextIter            *match_end);
 
 G_GNUC_INTERNAL
+void                   _gtk_source_search_forward_async                (GtkSourceSearch        *search,
+                                                                        const GtkTextIter      *iter,
+                                                                        GCancellable           *cancellable,
+                                                                        GAsyncReadyCallback     callback,
+                                                                        gpointer                user_data);
+
+G_GNUC_INTERNAL
+gboolean               _gtk_source_search_forward_finish               (GtkSourceSearch        *search,
+                                                                        GAsyncResult           *result,
+                                                                        GtkTextIter            *match_start,
+                                                                        GtkTextIter            *match_end,
+                                                                        GError                **error);
+
+G_GNUC_INTERNAL
 gboolean               _gtk_source_search_backward                     (GtkSourceSearch        *search,
                                                                         const GtkTextIter      *iter,
                                                                         GtkTextIter            *match_start,
diff --git a/tests/test-search-ui.c b/tests/test-search-ui.c
index 8a4e7b0..da251ca 100644
--- a/tests/test-search-ui.c
+++ b/tests/test-search-ui.c
@@ -30,6 +30,7 @@ open_file (GtkSourceBuffer *buffer,
        GError *error = NULL;
        GtkSourceLanguageManager *language_manager;
        GtkSourceLanguage *language;
+       GtkTextIter iter;
 
        if (!g_file_get_contents (filename, &contents, NULL, &error))
        {
@@ -42,6 +43,9 @@ open_file (GtkSourceBuffer *buffer,
        language = gtk_source_language_manager_get_language (language_manager, "c");
        gtk_source_buffer_set_language (buffer, language);
 
+       gtk_text_buffer_get_start_iter (GTK_TEXT_BUFFER (buffer), &iter);
+       gtk_text_buffer_select_range (GTK_TEXT_BUFFER (buffer), &iter, &iter);
+
        g_free (contents);
 }
 
@@ -107,10 +111,12 @@ on_at_word_boundaries_toggled_cb (GtkToggleButton *button,
 
 static void
 select_search_occurrence (GtkTextView       *view,
-                         GtkTextBuffer     *buffer,
                          const GtkTextIter *match_start,
                          const GtkTextIter *match_end)
 {
+       GtkTextBuffer *buffer = gtk_text_view_get_buffer (view);
+       g_return_if_fail (buffer != NULL);
+
        gtk_text_buffer_select_range (buffer, match_start, match_end);
 
        gtk_text_view_scroll_mark_onscreen (view, gtk_text_buffer_get_insert (buffer));
@@ -132,7 +138,25 @@ on_button_previous_clicked_cb (GtkButton   *button,
                                               &match_start,
                                               &match_end))
        {
-               select_search_occurrence (view, buffer, &match_start, &match_end);
+               select_search_occurrence (view, &match_start, &match_end);
+       }
+}
+
+static void
+forward_search_finished (GtkSourceBuffer *buffer,
+                        GAsyncResult    *result,
+                        GtkTextView     *view)
+{
+       GtkTextIter match_start;
+       GtkTextIter match_end;
+
+       if (gtk_source_buffer_forward_search_finish (buffer,
+                                                    result,
+                                                    &match_start,
+                                                    &match_end,
+                                                    NULL))
+       {
+               select_search_occurrence (view, &match_start, &match_end);
        }
 }
 
@@ -142,21 +166,16 @@ on_button_next_clicked_cb (GtkButton   *button,
 {
        GtkTextBuffer *buffer = gtk_text_view_get_buffer (view);
        GtkTextIter iter;
-       GtkTextIter match_start;
-       GtkTextIter match_end;
 
        gtk_text_buffer_get_selection_bounds (buffer, NULL, &iter);
 
-       if (gtk_source_buffer_forward_search (GTK_SOURCE_BUFFER (buffer),
-                                             &iter,
-                                             &match_start,
-                                             &match_end))
-       {
-               select_search_occurrence (view, buffer, &match_start, &match_end);
-       }
+       gtk_source_buffer_forward_search_async (GTK_SOURCE_BUFFER (buffer),
+                                               &iter,
+                                               NULL,
+                                               (GAsyncReadyCallback)forward_search_finished,
+                                               view);
 }
 
-
 static void
 create_window (void)
 {


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