[gtksourceview/wip/fix-bug-search-context] SearchContext: fix bug regex replace with look-ahead



commit aef0c5b7cf644f4acfd9766b4f8f95bc530c3fd3
Author: Sébastien Wilmet <swilmet gnome org>
Date:   Thu Aug 20 19:11:50 2015 +0200

    SearchContext: fix bug regex replace with look-ahead

 gtksourceview/gtksourcesearchcontext.c |   92 +++++++++++++++++++++++++-------
 tests/test-search-context.c            |   20 +++++++
 2 files changed, 92 insertions(+), 20 deletions(-)
---
diff --git a/gtksourceview/gtksourcesearchcontext.c b/gtksourceview/gtksourcesearchcontext.c
index 1aeadc1..6915d80 100644
--- a/gtksourceview/gtksourcesearchcontext.c
+++ b/gtksourceview/gtksourcesearchcontext.c
@@ -644,6 +644,11 @@ regex_search_get_match_options (const GtkTextIter *real_start,
                match_options |= G_REGEX_MATCH_NOTEOL;
        }
 
+       if (!gtk_text_iter_is_end (end))
+       {
+               match_options |= G_REGEX_MATCH_PARTIAL_HARD;
+       }
+
        return match_options;
 }
 
@@ -705,11 +710,20 @@ regex_search_fetch_match (GMatchInfo  *match_info,
        return TRUE;
 }
 
+/* If you retrieve only [match_start, match_end] from the GtkTextBuffer, it
+ * does not match the regex if the regex contains look-ahead assertions. For
+ * that, get the @real_end. Note that [match_start, real_end] is not the minimum
+ * amount of text that still matches the regex, it can contain several
+ * occurrences, so you can add the G_REGEX_MATCH_ANCHORED option to match only
+ * the first occurrence.
+ * Note that @limit is the limit for @match_end, not @real_end.
+ */
 static gboolean
 basic_forward_regex_search (GtkSourceSearchContext *search,
                            const GtkTextIter      *start_at,
                            GtkTextIter            *match_start,
                            GtkTextIter            *match_end,
+                           GtkTextIter            *real_end,
                            const GtkTextIter      *limit)
 {
        GtkTextIter real_start;
@@ -747,12 +761,6 @@ basic_forward_regex_search (GtkSourceSearchContext *search,
                GtkTextIter m_end;
 
                match_options = regex_search_get_match_options (&real_start, &end);
-
-               if (!gtk_text_iter_is_end (&end))
-               {
-                       match_options |= G_REGEX_MATCH_PARTIAL_HARD;
-               }
-
                subject = gtk_text_iter_get_visible_text (&real_start, &end);
                subject_length = strlen (subject);
 
@@ -812,6 +820,11 @@ basic_forward_regex_search (GtkSourceSearchContext *search,
                        {
                                *match_end = m_end;
                        }
+
+                       if (real_end != NULL)
+                       {
+                               *real_end = end;
+                       }
                }
 
                g_free (subject);
@@ -844,6 +857,7 @@ basic_forward_search (GtkSourceSearchContext *search,
                                                   iter,
                                                   match_start,
                                                   match_end,
+                                                  NULL,
                                                   limit);
        }
 
@@ -903,7 +917,7 @@ basic_backward_regex_search (GtkSourceSearchContext *search,
                lower_bound = *limit;
        }
 
-       while (basic_forward_regex_search (search, &lower_bound, &m_start, &m_end, start_at))
+       while (basic_forward_regex_search (search, &lower_bound, &m_start, &m_end, NULL, start_at))
        {
                found = TRUE;
 
@@ -1918,10 +1932,8 @@ regex_search_scan_segment (GtkSourceSearchContext *search,
                });
        }
 
-       if (!gtk_text_iter_is_end (segment_end))
+       if (match_options & G_REGEX_MATCH_PARTIAL_HARD)
        {
-               match_options |= G_REGEX_MATCH_PARTIAL_HARD;
-
                DEBUG ({
                       g_print ("match partial hard\n");
                });
@@ -3533,7 +3545,9 @@ gtk_source_search_context_backward_finish (GtkSourceSearchContext  *search,
                                                         error);
 }
 
-/* Returns %TRUE if replaced. */
+/* If correctly replaced, returns %TRUE and @match_start and @match_end are
+ * updated to point to the replacement.
+ */
 static gboolean
 regex_replace (GtkSourceSearchContext  *search,
               GtkTextIter             *match_start,
@@ -3542,11 +3556,18 @@ regex_replace (GtkSourceSearchContext  *search,
               GError                 **error)
 {
        GtkTextIter real_start;
+       GtkTextIter real_end;
+       GtkTextIter match_start_check;
+       GtkTextIter match_end_check;
        gint start_pos;
        gchar *subject;
+       gchar *suffix;
        gchar *subject_replaced;
        GRegexMatchFlags match_options;
+       gint offset_match_start;
+       gint offset_match_end;
        GError *tmp_error = NULL;
+       gboolean replaced = FALSE;
 
        if (search->priv->regex == NULL ||
            search->priv->regex_error != NULL)
@@ -3556,9 +3577,29 @@ regex_replace (GtkSourceSearchContext  *search,
 
        regex_search_get_real_start (search, match_start, &real_start, &start_pos);
 
-       subject = gtk_text_iter_get_visible_text (&real_start, match_end);
+       if (!basic_forward_regex_search (search,
+                                        match_start,
+                                        &match_start_check,
+                                        &match_end_check,
+                                        &real_end,
+                                        match_end))
+       {
+               g_assert_not_reached ();
+       }
+
+       g_assert (gtk_text_iter_equal (match_start, &match_start_check));
+       g_assert (gtk_text_iter_equal (match_end, &match_end_check));
+
+       subject = gtk_text_iter_get_visible_text (&real_start, &real_end);
+
+       suffix = gtk_text_iter_get_visible_text (match_end, &real_end);
+       if (suffix == NULL)
+       {
+               suffix = g_strdup ("");
+       }
 
-       match_options = regex_search_get_match_options (&real_start, match_end);
+       match_options = regex_search_get_match_options (&real_start, &real_end);
+       match_options |= G_REGEX_MATCH_ANCHORED;
 
        subject_replaced = g_regex_replace (search->priv->regex,
                                            subject,
@@ -3568,22 +3609,33 @@ regex_replace (GtkSourceSearchContext  *search,
                                            match_options,
                                            &tmp_error);
 
-       g_free (subject);
-
        if (tmp_error != NULL)
        {
                g_propagate_error (error, tmp_error);
-               g_free (subject_replaced);
-               return FALSE;
+               goto end;
        }
 
+       g_return_val_if_fail (g_str_has_suffix (subject_replaced, suffix), FALSE);
+
+       offset_match_start = gtk_text_iter_get_offset (match_start);
+
        gtk_text_buffer_begin_user_action (search->priv->buffer);
-       gtk_text_buffer_delete (search->priv->buffer, match_start, match_end);
-       gtk_text_buffer_insert (search->priv->buffer, match_end, subject_replaced + start_pos, -1);
+       gtk_text_buffer_delete (search->priv->buffer, match_start, &real_end);
+       gtk_text_buffer_insert (search->priv->buffer, &real_end, subject_replaced + start_pos, -1);
        gtk_text_buffer_end_user_action (search->priv->buffer);
 
+       gtk_text_buffer_get_iter_at_offset (search->priv->buffer, match_start, offset_match_start);
+
+       offset_match_end = gtk_text_iter_get_offset (&real_end) - g_utf8_strlen (suffix, -1);
+       gtk_text_buffer_get_iter_at_offset (search->priv->buffer, match_end, offset_match_end);
+
+       replaced = TRUE;
+
+end:
+       g_free (subject);
+       g_free (suffix);
        g_free (subject_replaced);
-       return TRUE;
+       return replaced;
 }
 
 /**
diff --git a/tests/test-search-context.c b/tests/test-search-context.c
index f4c9d4d..09ae081 100644
--- a/tests/test-search-context.c
+++ b/tests/test-search-context.c
@@ -1065,6 +1065,8 @@ test_regex_look_behind (void)
        gint pos;
        gint offset;
        gboolean found;
+       gchar *contents;
+       GError *error = NULL;
 
        gtk_text_buffer_set_text (text_buffer, "12\n23\n123\n23\n12", -1);
 
@@ -1100,6 +1102,14 @@ test_regex_look_behind (void)
        pos = gtk_source_search_context_get_occurrence_position (context, &match_start, &match_end);
        g_assert_cmpint (pos, ==, 1);
 
+       /* Replace */
+       gtk_source_search_context_replace (context, &match_start, &match_end, "R", -1, &error);
+       g_assert_no_error (error);
+
+       contents = get_buffer_contents (text_buffer);
+       g_assert_cmpstr (contents, ==, "12\n23\n1R\n23\n12");
+       g_free (contents);
+
        g_object_unref (source_buffer);
        g_object_unref (settings);
        g_object_unref (context);
@@ -1119,6 +1129,8 @@ test_regex_look_ahead (void)
        gint pos;
        gint offset;
        gboolean found;
+       gchar *contents;
+       GError *error = NULL;
 
        gtk_text_buffer_set_text (text_buffer, "12\n23\n123\n23\n12", -1);
 
@@ -1154,6 +1166,14 @@ test_regex_look_ahead (void)
        pos = gtk_source_search_context_get_occurrence_position (context, &match_start, &match_end);
        g_assert_cmpint (pos, ==, 1);
 
+       /* Replace */
+       gtk_source_search_context_replace (context, &match_start, &match_end, "R", -1, &error);
+       g_assert_no_error (error);
+
+       contents = get_buffer_contents (text_buffer);
+       g_assert_cmpstr (contents, ==, "12\n23\nR3\n23\n12");
+       g_free (contents);
+
        g_object_unref (source_buffer);
        g_object_unref (settings);
        g_object_unref (context);


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