visual selection and X primary behavior

Linus Arver linusarver at gmail.com
Thu Nov 13 08:09:41 CET 2014


On Sun, Jul 27, 2014 at 03:00:10PM +0200, Sanel Zukan wrote:
> Here is nice post how to do it:
> http://hugoheden.wordpress.com/2009/03/08/copypaste-with-emacs-in-terminal/
> 
> Good thing about above approach is that all Evil yanks and cuts will
> work with clipboard out of the box (no need to change anything, just
> load that code).

It appears that Evil yanks into the clipboard by default (at least in
GUI Emacs).

> Bad thing is if you use tramp for editing remote file, emacs will
> expect 'xsel' to be installed on that host too. This can be easily
> hacked with tramp hooks.

I don't use tramp (yet?), so all is good for now. ;)

Anyway, sorry for the late response, but I finally got GUI emacs and
terminal emacs to have the EXACT same behavior, when using visual
selections and also yanking/pasting!

As a refresher, this is how Vim (GVim and terminal vim) works:

1) Visual selection: any visual selection updates the X primary buffer
(for Shift-Inserting into any other X application).

2) Yank: yanks into vim's own buffer; does not modify X primary or X
clipboard.

3) "+y: yanks into special X clipboard (the '+') register.

With my settings, in both terminal and GUI Emacs, you get (1), and (2)
becomes (3)'s behavior (where plain yanking with 'y' yanks into
clipboard as well as yanking into Evil's own default buffer).

The first part involves redefining the default
`evil-visual-update-x-selection` function provided by Evil, as this is
what is called when we use visual selection mode (both in gui and
terminal Emacs). With this, we make the visual selection update the X
primary selection buffer:

    ; Vim-like visual selection. Make the visual selection update the X primary
    ; buffer.
    (defun evil-visual-update-x-selection (&optional buffer)
      "Update the X selection with the current visual region."
      (let ((buf (or buffer (current-buffer))))
        (when (buffer-live-p buf)
          (with-current-buffer buf
            (when (and (evil-visual-state-p)
                       (fboundp 'x-select-text)
                       (or (not (boundp 'ns-initialized))
                           (with-no-warnings ns-initialized))
                       (not (eq evil-visual-selection 'block)))
    ;           ; Vanilla evil, as of commit
    ;           ; 1def26dc9c4d084e766364eff2d9f3aa51613cf7.

    ;          (x-select-text (buffer-substring-no-properties
    ;                          evil-visual-beginning
    ;                          evil-visual-end))

    ;           ; from Hans-Peter Deifel's email on Nov 1 2012, to the
    ;           ; implementations-list at lists.ourproject.org mailing list
    ;           ; (http://permalink.gmane.org/gmane.emacs.vim-emulation/1715), which
    ;           ; makes visual selections from Evil update the X11 primary
    ;           ; selection; this version does not work with the latest Evil
    ;           ; 1def26dc9c4d084e766364eff2d9f3aa51613cf7 because of a "X selection
    ;           ; unavailable for this frame" error.

    ;           (x-set-selection 'PRIMARY
    ;               (buffer-substring-no-properties
    ;                   evil-visual-beginning
    ;                   evil-visual-end))
    ;           (setq x-last-selected-text-primary)

    ;           ; We explicitly use 'xsel' to update the primary selection; this
    ;           ; way, we can emulate Vim's behavior in both terminal and GUI emacs.
                (call-process-region
                    evil-visual-beginning
                    evil-visual-end
                    "xsel"
                    nil
                        0
                    nil
                    "--primary" "--input"
                )
    )))))

Please excuse the ugly formatting; I use tabs for indentation (heretic!)
for emacs --- I would make it consistent, but for purposes of this email
I wanted to preserve some of the whitespace so you can diff it if you
want against the original Evil version. I've also left a lot of comments
and preserved Hans-Peter Deifel's version for comparison.

The second part involves making terminal Emacs yank into the clipboard
when we press 'y'. This mirrors the behavior of GUI Emacs/Evil, so I
suppose everyone should be using this part if you use terminal
Emacs/Evil.

    ; When yanking ('y' key) in terminal Emacs, copy into the X clipboard. As
    ; suggested from http://permalink.gmane.org/gmane.emacs.vim-emulation/2023, the
    ; blog post at
    ; http://hugoheden.wordpress.com/2009/03/08/copypaste-with-emacs-in-terminal/
    ; touches on this topic; here is a modified version of that blog post:
    (unless window-system
        (when (getenv "DISPLAY")
            ; Callback for when user cuts
            (defun xsel-cut-function (text &optional push)
                ; Insert text to temp-buffer, and "send" content to xsel stdin
                (with-temp-buffer
                    (insert text)
                    ; I prefer using the "clipboard" selection (the one the
                    ; typically is used by c-c/c-v) before the primary selection
                    ; (that uses mouse-select/middle-button-click)
                    (call-process-region
                        (point-min)
                        (point-max)
                        "xsel"
                        nil
                        0
                        nil
                        "--clipboard" "--input"
                    )
                )
            )
            ; Call back for when user pastes
            (defun xsel-paste-function()
                ; Find out what is current selection by xsel. If it is different
                ; from the top of the kill-ring (car kill-ring), then return
                ; it. Else, nil is returned, so whatever is in the top of the
                ; kill-ring will be used.
                (let
                    (
                        (xsel-output
                            (shell-command-to-string "xsel --clipboard --output")
                        )
                    )
                (unless (string= (car kill-ring) xsel-output) xsel-output))
            )
            ; Attach callbacks to hooks
            (setq interprogram-cut-function 'xsel-cut-function)
            (setq interprogram-paste-function 'xsel-paste-function)
            ; Idea from
            ; http://shreevatsa.wordpress.com/2006/10/22/emacs-copypaste-and-x/
            ; http://www.mail-archive.com/[email protected]/msg03577.html
        )
    )

Please let me know if what I've done can be improved upon.



More information about the implementations-list mailing list