[gtksourceview/gnome-3-16] SearchContext: fix bug look-ahead regex



commit 63ebe6f2c6fe2989ae99c52664befec3e5deef10
Author: Sébastien Wilmet <swilmet gnome org>
Date:   Thu Aug 20 14:34:32 2015 +0200

    SearchContext: fix bug look-ahead regex
    
    - Write unit tests.
    - Adapt basic_forward_regex_search() to support partial matches.
    
    The gedit code calls gtk_source_search_context_get_occurrence_position()
    to know if the selected text matches. get_occerrence_position()
    traverses the occurrences by first going to tag toggles, and then inside
    the tagged text, find the exact matches with basic_forward_search().
    
    Finding the exact matches is needed because when contiguous matches are
    found, when applying the tag the previous match boundary is lost.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=752719

 gtksourceview/gtksourcesearchcontext.c |  112 ++++++++++++++++++++++---------
 tests/test-search-context.c            |   89 ++++++++++++++++++++++++-
 2 files changed, 165 insertions(+), 36 deletions(-)
---
diff --git a/gtksourceview/gtksourcesearchcontext.c b/gtksourceview/gtksourcesearchcontext.c
index 5f1a4fb..a6144a8 100644
--- a/gtksourceview/gtksourcesearchcontext.c
+++ b/gtksourceview/gtksourcesearchcontext.c
@@ -715,13 +715,8 @@ basic_forward_regex_search (GtkSourceSearchContext *search,
        GtkTextIter real_start;
        GtkTextIter end;
        gint start_pos;
-       gchar *subject;
-       gssize subject_length;
-       GRegexMatchFlags match_options;
-       GMatchInfo *match_info;
-       GtkTextIter iter;
-       gint iter_byte_pos;
-       gboolean found;
+       gboolean found = FALSE;
+       gint nb_lines = 1;
 
        if (search->priv->regex == NULL ||
            search->priv->regex_error != NULL)
@@ -740,38 +735,89 @@ basic_forward_regex_search (GtkSourceSearchContext *search,
                end = *limit;
        }
 
-       match_options = regex_search_get_match_options (&real_start, &end);
+       while (TRUE)
+       {
+               GRegexMatchFlags match_options;
+               gchar *subject;
+               gssize subject_length;
+               GMatchInfo *match_info;
+               GtkTextIter iter;
+               gint iter_byte_pos;
+               GtkTextIter m_start;
+               GtkTextIter m_end;
 
-       subject = gtk_text_iter_get_visible_text (&real_start, &end);
-       subject_length = strlen (subject);
+               match_options = regex_search_get_match_options (&real_start, &end);
 
-       g_regex_match_full (search->priv->regex,
-                           subject,
-                           subject_length,
-                           start_pos,
-                           match_options,
-                           &match_info,
-                           &search->priv->regex_error);
+               if (!gtk_text_iter_is_end (&end))
+               {
+                       match_options |= G_REGEX_MATCH_PARTIAL_HARD;
+               }
 
-       iter = real_start;
-       iter_byte_pos = 0;
+               subject = gtk_text_iter_get_visible_text (&real_start, &end);
+               subject_length = strlen (subject);
+
+               g_regex_match_full (search->priv->regex,
+                                   subject,
+                                   subject_length,
+                                   start_pos,
+                                   match_options,
+                                   &match_info,
+                                   &search->priv->regex_error);
+
+               iter = real_start;
+               iter_byte_pos = 0;
+
+               found = regex_search_fetch_match (match_info,
+                                                 subject,
+                                                 subject_length,
+                                                 &iter,
+                                                 &iter_byte_pos,
+                                                 &m_start,
+                                                 &m_end);
+
+               if (!found && g_match_info_is_partial_match (match_info))
+               {
+                       gtk_text_iter_forward_lines (&end, nb_lines);
+                       nb_lines <<= 1;
 
-       found = regex_search_fetch_match (match_info,
-                                         subject,
-                                         subject_length,
-                                         &iter,
-                                         &iter_byte_pos,
-                                         match_start,
-                                         match_end);
+                       g_free (subject);
+                       g_match_info_free (match_info);
+                       continue;
+               }
 
-       if (search->priv->regex_error != NULL)
-       {
-               g_object_notify (G_OBJECT (search), "regex-error");
-               found = FALSE;
-       }
+               /* Check that the match is not beyond the limit. This can happen
+                * if a partial match is found on the first iteration. Then the
+                * partial match was actually not a good match, but a second
+                * good match is found.
+                */
+               if (found && limit != NULL && gtk_text_iter_compare (limit, &m_end) < 0)
+               {
+                       found = FALSE;
+               }
 
-       g_free (subject);
-       g_match_info_free (match_info);
+               if (search->priv->regex_error != NULL)
+               {
+                       g_object_notify (G_OBJECT (search), "regex-error");
+                       found = FALSE;
+               }
+
+               if (found)
+               {
+                       if (match_start != NULL)
+                       {
+                               *match_start = m_start;
+                       }
+
+                       if (match_end != NULL)
+                       {
+                               *match_end = m_end;
+                       }
+               }
+
+               g_free (subject);
+               g_match_info_free (match_info);
+               break;
+       }
 
        return found;
 }
diff --git a/tests/test-search-context.c b/tests/test-search-context.c
index 8a2cafe..7f03334 100644
--- a/tests/test-search-context.c
+++ b/tests/test-search-context.c
@@ -921,7 +921,7 @@ test_replace_all (void)
 }
 
 static void
-test_regex (void)
+test_regex_basics (void)
 {
        GtkSourceBuffer *source_buffer = gtk_source_buffer_new (NULL);
        GtkTextBuffer *text_buffer = GTK_TEXT_BUFFER (source_buffer);
@@ -1060,6 +1060,87 @@ test_regex_at_word_boundaries (void)
 }
 
 static void
+test_regex_look_behind (void)
+{
+       GtkSourceBuffer *source_buffer = gtk_source_buffer_new (NULL);
+       GtkTextBuffer *text_buffer = GTK_TEXT_BUFFER (source_buffer);
+       GtkSourceSearchSettings *settings = gtk_source_search_settings_new ();
+       GtkSourceSearchContext *context = gtk_source_search_context_new (source_buffer, settings);
+       GtkTextIter iter;
+       GtkTextIter match_start;
+       GtkTextIter match_end;
+       gint count;
+       gint pos;
+       gint offset;
+       gboolean found;
+
+       gtk_text_buffer_set_text (text_buffer, "12\n23\n123\n23\n12", -1);
+
+       gtk_source_search_settings_set_regex_enabled (settings, TRUE);
+       gtk_source_search_settings_set_search_text (settings, "(?<=1)23");
+       flush_queue ();
+
+       count = gtk_source_search_context_get_occurrences_count (context);
+       g_assert_cmpint (count, ==, 1);
+
+       gtk_text_buffer_get_start_iter (text_buffer, &iter);
+       found = gtk_source_search_context_forward (context, &iter, &match_start, &match_end);
+       g_assert (found);
+
+       offset = gtk_text_iter_get_offset (&match_start);
+       g_assert_cmpint (offset, ==, 7);
+       offset = gtk_text_iter_get_offset (&match_end);
+       g_assert_cmpint (offset, ==, 9);
+
+       pos = gtk_source_search_context_get_occurrence_position (context, &match_start, &match_end);
+       g_assert_cmpint (pos, ==, 1);
+
+       g_object_unref (source_buffer);
+       g_object_unref (settings);
+       g_object_unref (context);
+}
+
+static void
+test_regex_look_ahead (void)
+{
+       GtkSourceBuffer *source_buffer = gtk_source_buffer_new (NULL);
+       GtkTextBuffer *text_buffer = GTK_TEXT_BUFFER (source_buffer);
+       GtkSourceSearchSettings *settings = gtk_source_search_settings_new ();
+       GtkSourceSearchContext *context = gtk_source_search_context_new (source_buffer, settings);
+       GtkTextIter iter;
+       GtkTextIter match_start;
+       GtkTextIter match_end;
+       gint count;
+       gint pos;
+       gint offset;
+       gboolean found;
+
+       gtk_text_buffer_set_text (text_buffer, "12\n23\n123\n23\n12", -1);
+
+       gtk_source_search_settings_set_regex_enabled (settings, TRUE);
+       gtk_source_search_settings_set_search_text (settings, "12(?=3)");
+       flush_queue ();
+       count = gtk_source_search_context_get_occurrences_count (context);
+       g_assert_cmpint (count, ==, 1);
+
+       gtk_text_buffer_get_start_iter (text_buffer, &iter);
+       found = gtk_source_search_context_forward (context, &iter, &match_start, &match_end);
+       g_assert (found);
+
+       offset = gtk_text_iter_get_offset (&match_start);
+       g_assert_cmpint (offset, ==, 6);
+       offset = gtk_text_iter_get_offset (&match_end);
+       g_assert_cmpint (offset, ==, 8);
+
+       pos = gtk_source_search_context_get_occurrence_position (context, &match_start, &match_end);
+       g_assert_cmpint (pos, ==, 1);
+
+       g_object_unref (source_buffer);
+       g_object_unref (settings);
+       g_object_unref (context);
+}
+
+static void
 test_destroy_buffer_during_search (void)
 {
        GtkSourceBuffer *source_buffer = gtk_source_buffer_new (NULL);
@@ -1106,8 +1187,10 @@ main (int argc, char **argv)
        g_test_add_func ("/Search/occurrence-position", test_occurrence_position);
        g_test_add_func ("/Search/replace", test_replace);
        g_test_add_func ("/Search/replace", test_replace_all);
-       g_test_add_func ("/Search/regex", test_regex);
-       g_test_add_func ("/Search/regex-at-word-boundaries", test_regex_at_word_boundaries);
+       g_test_add_func ("/Search/regex/basics", test_regex_basics);
+       g_test_add_func ("/Search/regex/at-word-boundaries", test_regex_at_word_boundaries);
+       g_test_add_func ("/Search/regex/look-behind", test_regex_look_behind);
+       g_test_add_func ("/Search/regex/look-ahead", test_regex_look_ahead);
        g_test_add_func ("/Search/destroy-buffer-during-search", test_destroy_buffer_during_search);
 
        return g_test_run ();


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