[gtksourceview/wip/chergert/vim] add s&r parser with tests



commit 837d49661329ca383d5a65202532476b7ea338c1
Author: Christian Hergert <chergert redhat com>
Date:   Mon Nov 8 15:33:49 2021 -0800

    add s&r parser with tests

 gtksourceview/vim/gtk-source-vim-command.c | 141 ++++++++++++++++++++++++++++-
 gtksourceview/vim/gtk-source-vim-command.h |  24 +++--
 testsuite/test-vim-input.c                 |  57 ++++++++++++
 3 files changed, 211 insertions(+), 11 deletions(-)
---
diff --git a/gtksourceview/vim/gtk-source-vim-command.c b/gtksourceview/vim/gtk-source-vim-command.c
index 0975481f..fbb2e59f 100644
--- a/gtksourceview/vim/gtk-source-vim-command.c
+++ b/gtksourceview/vim/gtk-source-vim-command.c
@@ -764,6 +764,144 @@ gtk_source_vim_command_line_number (GtkSourceVimCommand *self)
        }
 }
 
+gboolean
+gtk_source_vim_command_parse_search_and_replace (const char  *str,
+                                                 char       **search,
+                                                 char       **replace,
+                                                 char       **options)
+{
+       const char *c;
+       GString *build = NULL;
+       gunichar sep;
+       gboolean escaped;
+
+       g_assert (search != NULL);
+       g_assert (replace != NULL);
+       g_assert (options != NULL);
+
+       *search = NULL;
+       *replace = NULL;
+       *options = NULL;
+
+       if (str == NULL || *str == 0)
+               return FALSE;
+
+       sep = g_utf8_get_char (str);
+       str = g_utf8_next_char (str);
+
+       /* Check for something like "s/" */
+       if (*str == 0)
+               return TRUE;
+
+       build = g_string_new (NULL);
+       escaped = FALSE;
+       for (c = str; *c; c = g_utf8_next_char (c))
+       {
+               gunichar ch = g_utf8_get_char (c);
+
+               if (escaped)
+               {
+                       escaped = FALSE;
+
+                       if (ch == sep)
+                       {
+                               /* don't escape separator in output string */
+                               g_string_truncate (build, build->len - 1);
+                       }
+               }
+               else if (ch == '\\')
+               {
+                       escaped = TRUE;
+               }
+               else if (ch == sep)
+               {
+                       *search = g_string_free (g_steal_pointer (&build), FALSE);
+                       str = g_utf8_next_char (c);
+                       break;
+               }
+
+               g_string_append_unichar (build, ch);
+       }
+
+       if (escaped)
+               return FALSE;
+
+       /* Handle s/foobar (imply //) */
+       if (build != NULL)
+       {
+               *search = g_string_free (g_steal_pointer (&build), FALSE);
+               return TRUE;
+       }
+
+       if (*str == 0)
+               return TRUE;
+
+       build = g_string_new (NULL);
+       escaped = FALSE;
+       for (c = str; *c; c = g_utf8_next_char (c))
+       {
+               gunichar ch = g_utf8_get_char (c);
+
+               if (escaped)
+               {
+                       escaped = FALSE;
+
+                       if (ch == sep)
+                       {
+                               /* don't escape separator in output string */
+                               g_string_truncate (build, build->len - 1);
+                       }
+               }
+               else if (ch == '\\')
+               {
+                       escaped = TRUE;
+               }
+               else if (ch == sep)
+               {
+                       *replace = g_string_free (g_steal_pointer (&build), FALSE);
+                       str = g_utf8_next_char (c);
+                       break;
+               }
+
+               g_string_append_unichar (build, ch);
+       }
+
+       if (escaped)
+               return FALSE;
+
+       /* Handle s/foo/bar (imply trailing /) */
+       if (build != NULL)
+       {
+               *replace = g_string_free (g_steal_pointer (&build), FALSE);
+               return TRUE;
+       }
+
+       if (*str != 0)
+               *options = g_strdup (str);
+
+       return TRUE;
+}
+
+static void
+gtk_source_vim_command_search_replace (GtkSourceVimCommand *self)
+{
+       char *search = NULL;
+       char *replace = NULL;
+       char *options = NULL;
+
+       g_assert (GTK_SOURCE_IS_VIM_COMMAND (self));
+
+       if (gtk_source_vim_command_parse_search_and_replace (self->options, &search, &replace, &options))
+       {
+               g_print ("Search: %s\nReplace: %s\nOptions: %s\n",
+                        search, replace, options);
+       }
+
+       g_free (search);
+       g_free (replace);
+       g_free (options);
+}
+
 static void
 gtk_source_vim_command_append_command (GtkSourceVimState *state,
                                        GString           *string)
@@ -1052,6 +1190,7 @@ gtk_source_vim_command_class_init (GtkSourceVimCommandClass *klass)
        ADD_COMMAND ("line-number",    gtk_source_vim_command_line_number);
        ADD_COMMAND ("format",         gtk_source_vim_command_format);
        ADD_COMMAND ("search",         gtk_source_vim_command_search);
+       ADD_COMMAND ("search-replace", gtk_source_vim_command_search_replace);
        ADD_COMMAND ("search-reverse", gtk_source_vim_command_search_reverse);
 #undef ADD_COMMAND
 
@@ -1310,7 +1449,7 @@ gtk_source_vim_command_new_parsed (GtkSourceVimState *current,
 
        if (*command_line == 's')
        {
-               ret = GTK_SOURCE_VIM_COMMAND (gtk_source_vim_command_new ("search-and-replace"));
+               ret = GTK_SOURCE_VIM_COMMAND (gtk_source_vim_command_new ("search-replace"));
                ret->options = g_strdup (command_line+1);
 
                goto finish;
diff --git a/gtksourceview/vim/gtk-source-vim-command.h b/gtksourceview/vim/gtk-source-vim-command.h
index 038e4a32..2e653f50 100644
--- a/gtksourceview/vim/gtk-source-vim-command.h
+++ b/gtksourceview/vim/gtk-source-vim-command.h
@@ -31,15 +31,19 @@ G_BEGIN_DECLS
 
 G_DECLARE_FINAL_TYPE (GtkSourceVimCommand, gtk_source_vim_command, GTK_SOURCE, VIM_COMMAND, 
GtkSourceVimState)
 
-GtkSourceVimState *gtk_source_vim_command_new                  (const char             *command);
-GtkSourceVimState *gtk_source_vim_command_new_parsed           (GtkSourceVimState      *current,
-                                                               const char             *command_line);
-const char        *gtk_source_vim_command_get_command          (GtkSourceVimCommand    *self);
-void               gtk_source_vim_command_set_motion           (GtkSourceVimCommand    *self,
-                                                                GtkSourceVimMotion     *motion);
-void               gtk_source_vim_command_set_selection_motion (GtkSourceVimCommand    *self,
-                                                                GtkSourceVimMotion     *selection_motion);
-void               gtk_source_vim_command_set_text_object      (GtkSourceVimCommand    *self,
-                                                                GtkSourceVimTextObject *text_objet);
+GtkSourceVimState *gtk_source_vim_command_new                      (const char              *command);
+GtkSourceVimState *gtk_source_vim_command_new_parsed               (GtkSourceVimState       *current,
+                                                                    const char              *command_line);
+const char        *gtk_source_vim_command_get_command              (GtkSourceVimCommand     *self);
+void               gtk_source_vim_command_set_motion               (GtkSourceVimCommand     *self,
+                                                                    GtkSourceVimMotion      *motion);
+void               gtk_source_vim_command_set_selection_motion     (GtkSourceVimCommand     *self,
+                                                                    GtkSourceVimMotion      
*selection_motion);
+void               gtk_source_vim_command_set_text_object          (GtkSourceVimCommand     *self,
+                                                                    GtkSourceVimTextObject  *text_objet);
+gboolean           gtk_source_vim_command_parse_search_and_replace (const char              *str,
+                                                                    char                   **search,
+                                                                    char                   **replace,
+                                                                    char                   **options);
 
 G_END_DECLS
diff --git a/testsuite/test-vim-input.c b/testsuite/test-vim-input.c
index 19ec096a..0c935119 100644
--- a/testsuite/test-vim-input.c
+++ b/testsuite/test-vim-input.c
@@ -120,6 +120,62 @@ test_motion (void)
 #endif
 }
 
+static void
+test_search_and_replace (void)
+{
+       static const struct {
+               const char *command;
+               gboolean success;
+               const char *search;
+               const char *replace;
+               const char *options;
+       } parse_s_and_r[] = {
+               { "s/", TRUE, NULL, NULL, NULL },
+               { "s/a", TRUE, "a", NULL, NULL },
+               { "s/a/", TRUE, "a", NULL, NULL },
+               { "s/a/b", TRUE, "a", "b", NULL },
+               { "s/a/b/", TRUE, "a", "b", NULL },
+               { "s/a/b/c", TRUE, "a", "b", "c" },
+               { "s#a#b#c", TRUE, "a", "b", "c" },
+               { "s/^ \\//", TRUE, "^ /", NULL, NULL },
+               { "s/\\/\\/", TRUE, "//", NULL, NULL },
+               { "s/^$//gI", TRUE, "^$", "", "gI" },
+       };
+
+       for (guint i = 0; i < G_N_ELEMENTS (parse_s_and_r); i++)
+       {
+               const char *str = parse_s_and_r[i].command;
+               char *search = NULL;
+               char *replace = NULL;
+               char *options = NULL;
+               gboolean ret;
+
+               g_assert_true (*str == 's');
+
+               str++;
+               ret = gtk_source_vim_command_parse_search_and_replace (str, &search, &replace, &options);
+
+               if (!parse_s_and_r[i].success && ret)
+               {
+                       g_error ("expected %s to fail, but it succeeded",
+                                parse_s_and_r[i].command);
+               }
+               else if (parse_s_and_r[i].success && !ret)
+               {
+                       g_error ("expected %s to pass, but it failed",
+                                parse_s_and_r[i].command);
+               }
+
+               g_assert_cmpstr (search, ==, parse_s_and_r[i].search);
+               g_assert_cmpstr (replace, ==, parse_s_and_r[i].replace);
+               g_assert_cmpstr (options, ==, parse_s_and_r[i].options);
+
+               g_free (search);
+               g_free (replace);
+               g_free (options);
+       }
+}
+
 int
 main (int argc,
       char *argv[])
@@ -130,6 +186,7 @@ main (int argc,
        gtk_source_init ();
        g_test_init (&argc, &argv, NULL);
        g_test_add_func ("/GtkSourceView/vim-input/motion", test_motion);
+       g_test_add_func ("/GtkSourceView/vim-input/search-and-replace", test_search_and_replace);
        ret = g_test_run ();
        gtk_source_finalize ();
        return ret;


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