[gtksourceview/wip/search: 6/36] Asynchronous forward search
- From: Sébastien Wilmet <swilmet src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtksourceview/wip/search: 6/36] Asynchronous forward search
- Date: Sat, 6 Jul 2013 15:57:14 +0000 (UTC)
commit 24e1e64abed102d95aa972df335674c99546e61a
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]