[gtksourceview/wip/search] search: count the number of occurrences (not finished)
- From: Sébastien Wilmet <swilmet src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtksourceview/wip/search] search: count the number of occurrences (not finished)
- Date: Sat, 22 Jun 2013 09:26:28 +0000 (UTC)
commit 8222036982febcb33dc69907665faa40016c8152
Author: Sébastien Wilmet <swilmet gnome org>
Date: Fri Jun 21 22:44:50 2013 +0200
search: count the number of occurrences (not finished)
What is missing is the udpate of occurrences_count on text deletion. But
it's difficult to say if it's correct for the text insertion. There are
most probably corner cases where it doesn't work. Next step: write unit
tests.
gtksourceview/gtksourcesearch.c | 188 +++++++++++++++++++++++++++++++-------
tests/test-search.c | 23 +++++
tests/test-search.ui | 13 +++
3 files changed, 189 insertions(+), 35 deletions(-)
---
diff --git a/gtksourceview/gtksourcesearch.c b/gtksourceview/gtksourcesearch.c
index 7253b9a..bdf15bb 100644
--- a/gtksourceview/gtksourcesearch.c
+++ b/gtksourceview/gtksourcesearch.c
@@ -92,9 +92,9 @@
* Example where forward_to_tag_toggle() is really nice:
* <occurrence> [1 GB of text] <next-occurrence>
* Once the buffer has been scanned once, switching between the occurrences is
- * almost instantaneous.
+ * almost instantaneous (O(1) complexity).
*
- * If the code is too complicated and contains strange bugs, you have two
+ * If the code seems too complicated and contains strange bugs, you have two
* choices:
* - Write more unit tests, understand correctly the code and fix it.
* - Rewrite the code to implement the simpler solution explained above :-)
@@ -109,8 +109,13 @@ struct _GtkSourceSearchPrivate
gint text_nb_lines;
GtkTextSearchFlags flags;
- /* The region to scan and highlight. If NULL, the scan is finished. */
- GtkTextRegion *region;
+ /* The region not already scanned since the last search state update. */
+ GtkTextRegion *region_not_scanned;
+
+ /* The region to scan and highlight. If NULL, the scan is finished. It
+ * can contains regions already scanned, but must be rescanned.
+ */
+ GtkTextRegion *region_to_scan;
/* The region to scan and highlight in priority. I.e. the visible part
* of the buffer on the screen.
@@ -267,10 +272,16 @@ get_first_subregion (GtkTextRegion *region,
static void
clear_search (GtkSourceSearch *search)
{
- if (search->priv->region != NULL)
+ if (search->priv->region_not_scanned != NULL)
+ {
+ gtk_text_region_destroy (search->priv->region_not_scanned, TRUE);
+ search->priv->region_not_scanned = NULL;
+ }
+
+ if (search->priv->region_to_scan != NULL)
{
- gtk_text_region_destroy (search->priv->region, TRUE);
- search->priv->region = NULL;
+ gtk_text_region_destroy (search->priv->region_to_scan, TRUE);
+ search->priv->region_to_scan = NULL;
}
if (search->priv->high_priority_region != NULL)
@@ -322,9 +333,9 @@ adjust_subregion (GtkSourceSearch *search,
if (gtk_text_iter_has_tag (start, search->priv->found_tag))
{
- if (search->priv->region != NULL)
+ if (search->priv->region_to_scan != NULL)
{
- GtkTextRegion *region = gtk_text_region_intersect (search->priv->region,
+ GtkTextRegion *region = gtk_text_region_intersect (search->priv->region_to_scan,
start,
&initial_start);
@@ -363,9 +374,9 @@ adjust_subregion (GtkSourceSearch *search,
if (gtk_text_iter_has_tag (end, search->priv->found_tag))
{
- if (search->priv->region != NULL)
+ if (search->priv->region_to_scan != NULL)
{
- GtkTextRegion *region = gtk_text_region_intersect (search->priv->region,
+ GtkTextRegion *region = gtk_text_region_intersect (search->priv->region_to_scan,
&initial_end,
end);
@@ -402,9 +413,9 @@ adjust_subregion (GtkSourceSearch *search,
}
static void
-highlight_subregion (GtkSourceSearch *search,
- GtkTextIter *start,
- GtkTextIter *end)
+scan_subregion (GtkSourceSearch *search,
+ GtkTextIter *start,
+ GtkTextIter *end)
{
GtkTextIter iter;
GtkTextIter *limit;
@@ -427,9 +438,14 @@ highlight_subregion (GtkSourceSearch *search,
start,
end);
- if (search->priv->region != NULL)
+ if (search->priv->region_not_scanned != NULL)
+ {
+ gtk_text_region_subtract (search->priv->region_not_scanned, start, end);
+ }
+
+ if (search->priv->region_to_scan != NULL)
{
- gtk_text_region_subtract (search->priv->region, start, end);
+ gtk_text_region_subtract (search->priv->region_to_scan, start, end);
}
if (search->priv->text == NULL)
@@ -467,6 +483,8 @@ highlight_subregion (GtkSourceSearch *search,
search->priv->found_tag,
&match_start,
&match_end);
+
+ search->priv->occurrences_count++;
}
iter = match_end;
@@ -475,8 +493,8 @@ highlight_subregion (GtkSourceSearch *search,
}
static void
-highlight_all_region (GtkSourceSearch *search,
- GtkTextRegion *region_to_highlight)
+scan_all_region (GtkSourceSearch *search,
+ GtkTextRegion *region_to_highlight)
{
gint nb_subregions = gtk_text_region_subregions (region_to_highlight);
GtkTextIter start_search;
@@ -499,7 +517,7 @@ highlight_all_region (GtkSourceSearch *search,
gtk_text_iter_order (&start_search, &end_search);
- highlight_subregion (search, &start_search, &end_search);
+ scan_subregion (search, &start_search, &end_search);
}
/* Scan a chunk of the region. If the region is small enough, all the region
@@ -507,14 +525,14 @@ highlight_all_region (GtkSourceSearch *search,
* block the UI normally.
*/
static void
-highlight_chunk_region (GtkSourceSearch *search)
+scan_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))
+ get_first_subregion (search->priv->region_to_scan, &start, &end))
{
GtkTextIter limit = start;
gint start_line;
@@ -527,7 +545,7 @@ highlight_chunk_region (GtkSourceSearch *search)
limit = end;
}
- highlight_subregion (search, &start, &limit);
+ scan_subregion (search, &start, &limit);
start_line = gtk_text_iter_get_line (&start);
limit_line = gtk_text_iter_get_line (&limit);
@@ -547,28 +565,37 @@ idle_scan_cb (GtkSourceSearch *search)
* is the visible area on the screen. So we can highlight it in
* one batch.
*/
- highlight_all_region (search, search->priv->high_priority_region);
+ scan_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);
+ scan_chunk_region (search);
}
- if (is_text_region_empty (search->priv->region))
+ if (is_text_region_empty (search->priv->region_to_scan))
{
finished = TRUE;
search->priv->idle_scan_id = 0;
- if (search->priv->region != NULL)
+ g_object_notify (G_OBJECT (search->priv->buffer), "search-occurrences-count");
+
+ if (search->priv->region_to_scan != NULL)
{
- gtk_text_region_destroy (search->priv->region, TRUE);
- search->priv->region = NULL;
+ gtk_text_region_destroy (search->priv->region_to_scan, TRUE);
+ search->priv->region_to_scan = NULL;
}
}
+ if (is_text_region_empty (search->priv->region_not_scanned) &&
+ search->priv->region_not_scanned != NULL)
+ {
+ gtk_text_region_destroy (search->priv->region_not_scanned, TRUE);
+ search->priv->region_not_scanned = NULL;
+ }
+
return !finished;
}
@@ -589,7 +616,7 @@ add_subregion_to_scan (GtkSourceSearch *search,
GtkTextIter start = *subregion_start;
GtkTextIter end = *subregion_end;
- if (search->priv->region == NULL)
+ if (search->priv->region_to_scan == NULL)
{
return;
}
@@ -604,7 +631,7 @@ add_subregion_to_scan (GtkSourceSearch *search,
gtk_text_iter_forward_to_line_end (&end);
}
- gtk_text_region_add (search->priv->region, &start, &end);
+ gtk_text_region_add (search->priv->region_to_scan, &start, &end);
install_idle_scan (search);
@@ -630,13 +657,69 @@ update (GtkSourceSearch *search)
clear_search (search);
- search->priv->region = gtk_text_region_new (search->priv->buffer);
+ search->priv->region_not_scanned = gtk_text_region_new (search->priv->buffer);
+ search->priv->region_to_scan = gtk_text_region_new (search->priv->buffer);
gtk_text_buffer_get_bounds (search->priv->buffer, &start, &end);
+
+ gtk_text_region_add (search->priv->region_not_scanned, &start, &end);
add_subregion_to_scan (search, &start, &end);
}
static void
+insert_text_before_cb (GtkSourceSearch *search,
+ GtkTextIter *location,
+ gchar *text,
+ gint length)
+{
+ GtkTextIter start = *location;
+ GtkTextIter end = *location;
+ GtkTextIter iter;
+ GtkTextIter match_start;
+ GtkTextIter match_end;
+
+ if (search->priv->text == NULL)
+ {
+ return;
+ }
+
+ adjust_subregion (search, &start, &end);
+
+ iter = start;
+
+ while (gtk_text_iter_forward_search (&iter,
+ search->priv->text,
+ search->priv->flags,
+ &match_start,
+ &match_end,
+ &end))
+ {
+ if (search->priv->region_not_scanned == NULL)
+ {
+ search->priv->occurrences_count--;
+ }
+ else
+ {
+ GtkTextRegion *region = gtk_text_region_intersect (search->priv->region_not_scanned,
+ &match_start,
+ &match_end);
+
+ if (is_text_region_empty (region))
+ {
+ search->priv->occurrences_count--;
+ }
+
+ if (region != NULL)
+ {
+ gtk_text_region_destroy (region, TRUE);
+ }
+ }
+
+ iter = match_end;
+ }
+}
+
+static void
insert_text_after_cb (GtkSourceSearch *search,
GtkTextIter *location,
gchar *text,
@@ -654,6 +737,24 @@ insert_text_after_cb (GtkSourceSearch *search,
}
static void
+delete_range_before_cb (GtkSourceSearch *search,
+ GtkTextIter *start,
+ GtkTextIter *end)
+{
+ GtkTextIter start_buffer;
+ GtkTextIter end_buffer;
+
+ gtk_text_buffer_get_bounds (search->priv->buffer, &start_buffer, &end_buffer);
+
+ if (gtk_text_iter_equal (start, &start_buffer) &&
+ gtk_text_iter_equal (end, &end_buffer))
+ {
+ /* Special case when removing all the text. */
+ search->priv->occurrences_count = 0;
+ }
+}
+
+static void
delete_range_after_cb (GtkSourceSearch *search,
GtkTextIter *start,
GtkTextIter *end)
@@ -672,12 +773,24 @@ set_buffer (GtkSourceSearch *search,
g_signal_connect_object (buffer,
"insert-text",
+ G_CALLBACK (insert_text_before_cb),
+ search,
+ G_CONNECT_SWAPPED);
+
+ g_signal_connect_object (buffer,
+ "insert-text",
G_CALLBACK (insert_text_after_cb),
search,
G_CONNECT_AFTER | G_CONNECT_SWAPPED);
g_signal_connect_object (buffer,
"delete-range",
+ G_CALLBACK (delete_range_before_cb),
+ search,
+ G_CONNECT_SWAPPED);
+
+ g_signal_connect_object (buffer,
+ "delete-range",
G_CALLBACK (delete_range_after_cb),
search,
G_CONNECT_AFTER | G_CONNECT_SWAPPED);
@@ -834,23 +947,28 @@ _gtk_source_search_update_highlight (GtkSourceSearch *search,
g_return_if_fail (end != NULL);
if (dispose_has_run (search) ||
- is_text_region_empty (search->priv->region))
+ is_text_region_empty (search->priv->region_to_scan))
{
return;
}
- region_to_highlight = gtk_text_region_intersect (search->priv->region,
+ region_to_highlight = gtk_text_region_intersect (search->priv->region_to_scan,
start,
end);
- if (region_to_highlight == NULL)
+ if (is_text_region_empty (region_to_highlight))
{
+ if (region_to_highlight != NULL)
+ {
+ gtk_text_region_destroy (region_to_highlight, TRUE);
+ }
+
return;
}
if (synchronous)
{
- highlight_all_region (search, region_to_highlight);
+ scan_all_region (search, region_to_highlight);
gtk_text_region_destroy (region_to_highlight, TRUE);
}
else
diff --git a/tests/test-search.c b/tests/test-search.c
index 3f10245..8cc11a4 100644
--- a/tests/test-search.c
+++ b/tests/test-search.c
@@ -46,6 +46,22 @@ open_file (GtkSourceBuffer *buffer,
}
static void
+on_occurrences_count_notify_cb (GtkSourceBuffer *buffer,
+ GParamSpec *spec,
+ GtkLabel *label)
+{
+ guint occurrences_count;
+ gchar *text;
+
+ g_object_get (buffer, "search-occurrences-count", &occurrences_count, NULL);
+
+ text = g_strdup_printf ("%u occurrences", occurrences_count);
+
+ gtk_label_set_text (label, text);
+ g_free (text);
+}
+
+static void
create_window (void)
{
GtkBuilder *builder;
@@ -54,6 +70,7 @@ create_window (void)
GtkSourceView *source_view;
GtkSourceBuffer *source_buffer;
GtkSearchEntry *search_entry;
+ GtkLabel *label_occurrences_count;
PangoFontDescription *font;
builder = gtk_builder_new ();
@@ -70,6 +87,7 @@ create_window (void)
window = GTK_WINDOW (gtk_builder_get_object (builder, "window"));
source_view = GTK_SOURCE_VIEW (gtk_builder_get_object (builder, "source_view"));
search_entry = GTK_SEARCH_ENTRY (gtk_builder_get_object (builder, "search_entry"));
+ label_occurrences_count = GTK_LABEL (gtk_builder_get_object (builder, "label_occurrences_count"));
font = pango_font_description_from_string ("Monospace 10");
gtk_widget_override_font (GTK_WIDGET (source_view), font);
@@ -97,6 +115,11 @@ create_window (void)
source_buffer, "search-text",
G_BINDING_DEFAULT);
+ g_signal_connect (source_buffer,
+ "notify::search-occurrences-count",
+ G_CALLBACK (on_occurrences_count_notify_cb),
+ label_occurrences_count);
+
g_object_unref (builder);
}
diff --git a/tests/test-search.ui b/tests/test-search.ui
index b175151..3b52dab 100644
--- a/tests/test-search.ui
+++ b/tests/test-search.ui
@@ -77,6 +77,19 @@
<property name="height">1</property>
</packing>
</child>
+ <child>
+ <object class="GtkLabel" id="label_occurrences_count">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label">0 occurrences</property>
+ </object>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="top_attach">0</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
</object>
<packing>
<property name="left_attach">0</property>
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]