From 76797820cfb212c3f185efcee2189eed4240ac5a Mon Sep 17 00:00:00 2001
From: guns <self@sungpae.com>
Date: Tue, 3 May 2011 01:51:08 -0500
Subject: [PATCH] Linewise pasting of multi-line selections in X and Risc OS

Upon closer inspection, I have the following observations about vim's
handling of foreign selections.

When pasting from the "* or "+ register, vim requests a selection
from the system clipboard. If the selection is not in a vim
selection format:

    - The mac, macosx, msdos, mswin, and qnx ports detect the presence
      of a NL or CR (whatever is appropriate) _anywhere_ in the text and
      switch the motion_type to MLINE

    - The riscos, gtk-x11, and x11 ports always set motion_type to MCHAR

    - In X, if the selection request fails, CUT_BUFFER0 is used, and is
      also always set to MCHAR

As you can see, vim's behavior wrt pasting foreign text is a little
inconsistent. Most of the non-X ports linewise paste multi-line
selections, while the X-based vims do not.

While the X behavior is not a bug (it is actually explicitly
documented), I believe it is surprising in this instance.

When I copy a large multi-line selection in an application in order to
transfer it into a vim buffer, I don't usually intend on engaging in a
delicate mid-line paste operation. The major non-X GUI ports share this
view, and scan the text for the presence of an EOL character.

I should mention that scanning the selection buffer for an EOL could
be costly if the user tries to paste a single, very long line from the
X clipboard (minified javascript comes to mind). It would be nice in
this instance to be able to use a reverse strchr (strrchr) on the likely
chance that the line ends in a newline.

Unfortunately, vim_strrchr() does not exist; I will leave it to Bram to
decide if it is worth adding that function for this corner case.

Finally, there a couple of other instances in which vim yanks from
foreign sources:

    - When dragging text into gvim (gtk-x11), the selection is always
      yanked as MCHAR

    - Modeless selections are always yanked as MCHAR

With drag and drop, pasting character-wise is justifiable because there
is typically not much context switching involved: there are no separate
yank and paste operations, the two applications are likely to be side
by side, and flashy GUIs often show a live preview of the text as it is
dragged about. It would be more surprising to open a new line for the
paste than to paste it exactly at the cursor.

With modeless selections, it is logical to simply yank as MCHAR since
one of their primary purposes is to transfer text to the command line.

Summary of the following patch:

 * Risc OS, Gtk-X11, and X11:

    Calls vim_strchr(buf, '\n') on the selection buffer to switch
    multi-line strings to MLINE mode before yanking into "* or "+

 * Documentation:

    Update relevant sections regarding pasting multi-line strings from
    foreign applications
---
 runtime/doc/gui.txt     |    7 ++++---
 runtime/doc/gui_x11.txt |    5 +++--
 src/gui_gtk_x11.c       |    4 ++++
 src/gui_riscos.c        |    6 +++++-
 src/ui.c                |   12 ++++++++++--
 5 files changed, 26 insertions(+), 8 deletions(-)

diff --git a/runtime/doc/gui.txt b/runtime/doc/gui.txt
index 213f455..74c3cfc 100644
--- a/runtime/doc/gui.txt
+++ b/runtime/doc/gui.txt
@@ -439,9 +439,10 @@ When using this register under X11, also see |x11-selection|.  This also
 explains the related "+ register.
 
 Note that when pasting text from one Vim into another separate Vim, the type
-of selection (character, line, or block) will also be copied.  For other
-applications the type is always character.  However, if the text gets
-transferred via the |x11-cut-buffer|, the selection type is ALWAYS lost.
+of selection (character, line, or block) will also be copied.  When pasting
+from other applications the type is character, unless the the selection
+consists of multiple lines, in which case the type is line.  This also applies
+to text transferred via the |x11-cut-buffer|.
 
 When the "unnamed" string is included in the 'clipboard' option, the unnamed
 register is the same as the "* register.  Thus you can yank to and paste the
diff --git a/runtime/doc/gui_x11.txt b/runtime/doc/gui_x11.txt
index 6d16500..ad434b6 100644
--- a/runtime/doc/gui_x11.txt
+++ b/runtime/doc/gui_x11.txt
@@ -566,8 +566,9 @@ requested X selection is empty or unavailable, Vim reverts to reading the
 current value of the CUT_BUFFER0.
 
 Note that when text is copied to CUT_BUFFER0 in this way, the type of
-selection (character, line or block) is always lost, even if it is a Vim which
-later pastes it.
+selection (character, line or block) is always lost. However, when pasting
+from CUT_BUFFER0, the selection will be pasted as line if the text consists
+of multiple lines, and as character otherwise.
 
 Xterm, by default, always writes visible selections to both PRIMARY and
 CUT_BUFFER0.  When it pastes, it uses PRIMARY if this is available, or else
diff --git a/src/gui_gtk_x11.c b/src/gui_gtk_x11.c
index 0454afb..a527945 100644
--- a/src/gui_gtk_x11.c
+++ b/src/gui_gtk_x11.c
@@ -1258,6 +1258,10 @@ selection_received_cb(GtkWidget		*widget UNUSED,
 	    if (tmpbuf != NULL)
 		text = tmpbuf;
 	}
+
+	/* Switch multiline strings to MLINE */
+	if (vim_strchr(text, '\n') != NULL)
+	    motion_type = MLINE;
     }
 
     /* Chop off any traiing NUL bytes.  OpenOffice sends these. */
diff --git a/src/gui_riscos.c b/src/gui_riscos.c
index 90634fa..cbce7da 100644
--- a/src/gui_riscos.c
+++ b/src/gui_riscos.c
@@ -2649,6 +2649,7 @@ clip_mch_request_selection(VimClipboard *cbd)
     int		reason;
     char_u	*buffer;
     long_u	length;
+    int		motion_type;
 
     block[0] = 48;			/* Size of block. */
     block[3] = 0;			/* Orinial message. */
@@ -2715,7 +2716,10 @@ clip_mch_request_selection(VimClipboard *cbd)
     if (xswi(OS_File, 16, "<Wimp$Scrap>", buffer, 0) & v_flag)
 	return;
 
-    clip_yank_selection(MCHAR, buffer, length, cbd);
+    /* Determine appropriate motion type */
+    motion_type = (vim_strchr(buffer, '\n') == NULL) ? MCHAR : MLINE;
+
+    clip_yank_selection(motion_type, buffer, length, cbd);
 
     vim_free(buffer);
 
diff --git a/src/ui.c b/src/ui.c
index 9d940b1..67b27ee 100644
--- a/src/ui.c
+++ b/src/ui.c
@@ -2098,6 +2098,10 @@ clip_x11_request_selection_cb(w, success, sel_atom, type, value, length,
 	}
 	p = (char_u *)text_list[0];
 	len = STRLEN(p);
+
+	/* Switch multiline strings to MLINE */
+	if (vim_strchr(p, '\n') != NULL)
+	    motion_type = MLINE;
     }
     clip_yank_selection(motion_type, p, (long)len, cbd);
 
@@ -2401,6 +2405,10 @@ yank_cut_buffer0(dpy, cbd)
 {
     int		nbytes = 0;
     char_u	*buffer = (char_u *)XFetchBuffer(dpy, &nbytes, 0);
+    int		motion_type;
+
+    /* Determine appropriate motion type */
+    motion_type = (vim_strchr(buffer, '\n') == NULL) ? MCHAR : MLINE;
 
     if (nbytes > 0)
     {
@@ -2422,7 +2430,7 @@ yank_cut_buffer0(dpy, cbd)
 		conv_buf = string_convert(&vc, buffer, &nbytes);
 		if (conv_buf != NULL)
 		{
-		    clip_yank_selection(MCHAR, conv_buf, (long)nbytes, cbd);
+		    clip_yank_selection(motion_type, conv_buf, (long)nbytes, cbd);
 		    vim_free(conv_buf);
 		    done = TRUE;
 		}
@@ -2431,7 +2439,7 @@ yank_cut_buffer0(dpy, cbd)
 	}
 	if (!done)  /* use the text without conversion */
 #endif
-	    clip_yank_selection(MCHAR, buffer, (long)nbytes, cbd);
+	    clip_yank_selection(motion_type, buffer, (long)nbytes, cbd);
 	XFree((void *)buffer);
 	if (p_verbose > 0)
 	{
-- 
1.7.5-rc0

