[gnome-builder/wip/vim] vim: make paste act more like VIM



commit d62677d7f81eb2a9a5c8efce9bd259d6995a28f9
Author: Christian Hergert <christian hergert me>
Date:   Mon Sep 29 23:22:05 2014 -0700

    vim: make paste act more like VIM
    
     * `xp` should paste the current character after the next character.
     * `Vxp` should paste the current line after the next line.

 src/editor/gb-editor-vim.c |   94 ++++++++++++++++++++++++++++++++++++++++++--
 1 files changed, 90 insertions(+), 4 deletions(-)
---
diff --git a/src/editor/gb-editor-vim.c b/src/editor/gb-editor-vim.c
index 7b23622..f6b1a2c 100644
--- a/src/editor/gb-editor-vim.c
+++ b/src/editor/gb-editor-vim.c
@@ -543,16 +543,28 @@ gb_editor_vim_select_line (GbEditorVim *vim)
   insert = gtk_text_buffer_get_insert (buffer);
   gtk_text_buffer_get_iter_at_mark (buffer, &iter, insert);
 
+  /*
+   * Move to the start iter to the beginning of the line.
+   */
   gtk_text_iter_assign (&begin, &iter);
   while (!gtk_text_iter_starts_line (&begin))
     if (!gtk_text_iter_backward_char (&begin))
       break;
 
+  /*
+   * Move to the end cursor to the end of the line.
+   */
   gtk_text_iter_assign (&end, &iter);
   while (!gtk_text_iter_ends_line (&end))
     if (!gtk_text_iter_forward_char (&end))
       break;
 
+  /*
+   * We actually want to select the \n befire the line.
+   */
+  if (gtk_text_iter_ends_line (&end))
+    gtk_text_iter_forward_char (&end);
+
   gtk_text_buffer_select_range (buffer, &begin, &end);
 }
 
@@ -650,7 +662,8 @@ gb_editor_vim_insert_nl_before (GbEditorVim *vim)
 }
 
 static void
-gb_editor_vim_insert_nl_after (GbEditorVim *vim)
+gb_editor_vim_insert_nl_after (GbEditorVim *vim,
+                               gboolean     auto_indent)
 {
   GtkTextBuffer *buffer;
   GtkTextMark *insert;
@@ -678,7 +691,8 @@ gb_editor_vim_insert_nl_after (GbEditorVim *vim)
   /*
    * We might need to auto-indent after the newline.
    */
-  gb_editor_vim_maybe_auto_indent (vim);
+  if (auto_indent)
+    gb_editor_vim_maybe_auto_indent (vim);
 
   vim->priv->target_line_offset = gb_editor_vim_get_line_offset (vim);
 
@@ -749,6 +763,78 @@ gb_editor_vim_delete_to_line_start (GbEditorVim *vim)
   vim->priv->target_line_offset = gb_editor_vim_get_line_offset (vim);
 }
 
+static void
+gb_editor_vim_paste (GbEditorVim *vim)
+{
+  GtkClipboard *clipboard;
+  GtkTextBuffer *buffer;
+  gchar *text;
+
+  g_return_if_fail (GB_IS_EDITOR_VIM (vim));
+
+  buffer = gtk_text_view_get_buffer (vim->priv->text_view);
+
+  gtk_text_buffer_begin_user_action (buffer);
+
+  /*
+   * Fetch the clipboard contents so we can check to see if we are pasting a
+   * whole line (which needs to be treated differently).
+   */
+  clipboard = gtk_widget_get_clipboard (GTK_WIDGET (vim->priv->text_view),
+                                        GDK_SELECTION_CLIPBOARD);
+  text = gtk_clipboard_wait_for_text (clipboard);
+
+  /*
+   * If we are pasting an entire line, we don't want to paste it at the current
+   * location. We want to insert a new line after the current line, and then
+   * paste it there (so move the insert mark first).
+   */
+  if (text && g_str_has_suffix (text, "\n"))
+    {
+      gchar *trimmed;
+
+      /*
+       * WORKAROUND:
+       *
+       * This is a hack so that we can continue to use the paste code from
+       * within GtkTextBuffer.
+       *
+       * We needed to keep the trailing \n in the text so that we know when
+       * we are selecting whole lines. We also need to insert a new line
+       * manually based on the context. Further more, we need to remove the
+       * trailing line since we already added one.
+       *
+       * Terriby annoying, but the result is something that feels very nice,
+       * just like VIM.
+       */
+
+      trimmed = g_strndup (text, strlen (text) - 1);
+      gb_editor_vim_insert_nl_after (vim, FALSE);
+      gtk_clipboard_set_text (clipboard, trimmed, -1);
+      g_signal_emit_by_name (vim->priv->text_view, "paste-clipboard");
+      gtk_clipboard_set_text (clipboard, text, -1);
+      g_free (trimmed);
+    }
+  else
+    {
+      /*
+       * By default, GtkTextBuffer will paste at our current position.
+       * While VIM will paste after the current position. Let's advance the
+       * buffer a single character on the current line if possible. We switch
+       * to insert mode so that we can move past the last character in the
+       * buffer. Possibly should consider an alternate design for this.
+       */
+      gb_editor_vim_set_mode (vim, GB_EDITOR_VIM_INSERT);
+      gb_editor_vim_move_forward (vim);
+      g_signal_emit_by_name (vim->priv->text_view, "paste-clipboard");
+      gb_editor_vim_set_mode (vim, GB_EDITOR_VIM_NORMAL);
+    }
+
+  gtk_text_buffer_end_user_action (buffer);
+
+  g_free (text);
+}
+
 static gboolean
 gb_editor_vim_has_selection (GbEditorVim *vim)
 {
@@ -893,14 +979,14 @@ gb_editor_vim_handle_normal (GbEditorVim *vim,
        * Insert a new line, and then begin insertion.
        */
       gb_editor_vim_set_mode (vim, GB_EDITOR_VIM_INSERT);
-      gb_editor_vim_insert_nl_after (vim);
+      gb_editor_vim_insert_nl_after (vim, TRUE);
       return TRUE;
 
     case GDK_KEY_p:
       /*
        * Paste the current clipboard selection.
        */
-      g_signal_emit_by_name (vim->priv->text_view, "paste-clipboard");
+      gb_editor_vim_paste (vim);
       return TRUE;
 
     case GDK_KEY_r:


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