Bypassing the clipboard in Emacs Evil mode

Emacs Evil is pretty fun. It’s a Vim emulation mode for Emacs. As Slashdot says:

Evil is a new Emacs major mode intended to implement full Vim emulation for Emacs editor, and it's reached its first stable release. Evil implements many Vim features and has support for plugins, so there is port for rails.vim, NERDCommenter and mapleader among others.

So, Emacs + evil-mode ~= Vim. It’s almost Vim, with the full power of Emacs in insert mode. Yes, call me a heretic. And yes, I testify for it, and I sure hope it gets even better.

The problem: Don’t use the clipboard

The problem, for me, is that with both Vim and Emacs’ evil-mode, almost every text modification action affects the clipboard. Okay so with Vim, it goes to a register and not the system clipboard, unless you changed Vim’s options, which I did. But for evil-mode, it does go to the system clipboard.

And I kind of like this behavior in Emacs.

But this raises the problem whereby every Vim command in evil-mode writes and therefore destroy’s my lovely and familiar system clipboard. Imagine (cursor represented as |):

The quick brown fox jumps over |the lazy dog
(Vim command: cw, text: "my")
>> The quick brown fox jumps over |the lazy dog

And my clipboard is gone. Not sure about you but I don’t like being restricted to only doing a copy right before I do a paste, lest my clipboard gets clobbered. Cramps my style, heh.

That goes for almost all the Vim actions: x c d p and their capital cousins.

And so, what would be good would be a series of evil-mode commands that perform the Vim actions, without touching the blasted clipboard. In short, don’t use the clipboard unless I want it to.

My solution

Hence, what I have here, is those replacements, what you can override or map to another key, depending on your preference.

<code class="lisp">;;;; Support

(defmacro without-evil-mode (&rest do-this)
  ;; Check if evil-mode is on, and disable it temporarily
  `(let ((evil-mode-is-on (evil-mode?)))
     (if evil-mode-is-on
         (disable-evil-mode))
     (ignore-errors
       ,@do-this)
     (if evil-mode-is-on
         (enable-evil-mode))))

(defmacro evil-mode? ()
  "Checks if evil-mode is active. Uses Evil's state to check."
  `evil-state)

(defmacro disable-evil-mode ()
  "Disable evil-mode with visual cues."
  `(progn
     (evil-mode 0)
     (message "Evil mode disabled")))

(defmacro enable-evil-mode ()
  "Enable evil-mode with visual cues."
  `(progn
     (evil-mode 1)
     (message "Evil mode enabled")))

;;;; Clipboard bypass

;; delete: char
(evil-define-operator evil-destroy-char (beg end type register yank-handler)
  :motion evil-forward-char
  (evil-delete-char beg end type ?_))

;; delete: char (backwards)
(evil-define-operator evil-destroy-backward-char (beg end type register yank-handler)
  :motion evil-forward-char
  (evil-delete-backward-char beg end type ?_))

;; delete: text object
(evil-define-operator evil-destroy (beg end type register yank-handler)
  "Vim's 's' without clipboard."
  (evil-delete beg end type ?_ yank-handler))

;; delete: to end of line
(evil-define-operator evil-destroy-line (beg end type register yank-handler)
  :motion nil
  :keep-visual t
  (interactive "<R><x>")
  (evil-delete-line beg end type ?_ yank-handler))

;; delete: whole line
(evil-define-operator evil-destroy-whole-line (beg end type register yank-handler)
  :motion evil-line
  (interactive "<R><x>")
  (evil-delete-whole-line beg end type ?_ yank-handler))

;; change: text object
(evil-define-operator evil-destroy-change (beg end type register yank-handler delete-func)
  (evil-change beg end type ?_ yank-handler delete-func))

;; paste: before
(defun evil-destroy-paste-before ()
  (interactive)
  (without-evil-mode
     (delete-region (point) (mark))
     (evil-paste-before 1)))

;; paste: after
(defun evil-destroy-paste-after ()
  (interactive)
  (without-evil-mode
     (delete-region (point) (mark))
     (evil-paste-after 1)))

;; paste: text object
(evil-define-operator evil-destroy-replace (beg end type register yank-handler)
  (evil-destroy beg end type register yank-handler)
  (evil-paste-before 1 register))

And here are my key re-bindings, purely for your reference. It’s really personal, so its sort of to make it easy for copy-and-paste and then modification, if you so desire.

<code class="lisp">;; Clipboard bypass key rebindings
(define-key evil-normal-state-map "s" 'evil-destroy)
(define-key evil-normal-state-map "S" 'evil-destroy-line)
(define-key evil-normal-state-map "c" 'evil-destroy-change)
(define-key evil-normal-state-map "x" 'evil-destroy-char)
(define-key evil-normal-state-map "X" 'evil-destroy-whole-line)
(define-key evil-normal-state-map "Y" 'evil-copy-to-end-of-line)
(define-key evil-visual-state-map "P" 'evil-destroy-paste-before)
(define-key evil-visual-state-map "p" 'evil-destroy-paste-after)

I’ve tried to rely on Evil’s own implementations as much as possible. If somebody sees a better way of doing something, please tell me! If not, I hope it solves someone’s clipboard woes :)