15

In GNU Emacs for OSX, how can I keep the kill ring and OSX clipboard separate? (Such that I essentially have two separate kill rings.)

With desired behavior, this would work:
1. C to copy text from the web to OSX clipboard.
2. controlk to kill a line in Emacs.
3. controly to yank killed text from Emacs kill ring to current Emacs buffer.
4. v to paste original web text from OSX clipboard to current Emacs buffer.

This works out of the box in Aquamacs. How to make work in GNU Emacs?

This question was discussed as it pertains to Windows here: Emacs: How to separate the kill ring from the system clipboard?

and here: http://lists.gnu.org/archive/html/help-emacs-windows/2010-02/msg00001.HTML

...but this solution does not work in OSX. I would like a solution for Mac OSX.

Community
  • 1
  • 1
incandescentman
  • 6,168
  • 3
  • 46
  • 86
  • Related option which doesn't answer your question, but solved the problem in a way I didn't even expect was possible: [`save-interprogram-paste-before-kill`](https://www.gnu.org/software/emacs/manual/html_node/emacs/Clipboard.html). It keeps the sync between clipboard and kill ring, but saves your OS clipboard on the kill ring before clobbering it. Still accessible, and fully integrated. E.g. your example would only change on step 4: `C-y M-y` instead of `⌘-v`. – hraban May 01 '22 at 01:23

6 Answers6

11

The solution in Emacs: How to separate the kill ring from the system clipboard? does work, though not complete. You may call pbcopy yourself to get clipboard pasting right. For instance, try the following in your .emacs. Note that s-v is for Cmd+V in an OS X window system. Same goes for s-c.

;;; Tested on:
;;; 1.  GNU Emacs 24.3.1 (x86_64-apple-darwin13.0.0)
;;;     of 2013-12-22 on tennine-slave.macports.org
;;;     (MacPorts emacs@24.3_1)
;;;
;;; 2.  GNU Emacs 24.3.1 (x86_64-apple-darwin, NS apple-appkit-1038.36)
;;;     of 2013-03-12 on bob.porkrind.org
;;;     (Emacs For Mac OS X)

(defun isolate-kill-ring()
  "Isolate Emacs kill ring from OS X system pasteboard.
This function is only necessary in window system."
  (interactive)
  (setq interprogram-cut-function nil)
  (setq interprogram-paste-function nil))

(defun pasteboard-copy()
  "Copy region to OS X system pasteboard."
  (interactive)
  (shell-command-on-region
   (region-beginning) (region-end) "pbcopy"))

(defun pasteboard-paste()
  "Paste from OS X system pasteboard via `pbpaste' to point."
  (interactive)
  (shell-command-on-region
   (point) (if mark-active (mark) (point)) "pbpaste" nil t))

(defun pasteboard-cut()
  "Cut region and put on OS X system pasteboard."
  (interactive)
  (pasteboard-copy)
  (delete-region (region-beginning) (region-end)))

(if window-system
    (progn
      (isolate-kill-ring)
      ;; bind CMD+C to pasteboard-copy
      (global-set-key (kbd "s-c") 'pasteboard-copy)
      ;; bind CMD+V to pasteboard-paste
      (global-set-key (kbd "s-v") 'pasteboard-paste)
      ;; bind CMD+X to pasteboard-cut
      (global-set-key (kbd "s-x") 'pasteboard-cut))

  ;; you might also want to assign some keybindings for non-window
  ;; system usage (i.e., in your text terminal, where the
  ;; command->super does not work)
  )

If you ever run into problems with UTF-8, consider the following possible solution:

;; handle emacs utf-8 input
(set-terminal-coding-system 'utf-8)
(set-keyboard-coding-system 'utf-8)
(prefer-coding-system 'utf-8)
(setenv "LANG" "en_US.UTF-8")
Community
  • 1
  • 1
4ae1e1
  • 7,228
  • 8
  • 44
  • 77
  • Well you asked "Well so what's the code to set interprogram-cut-function to nil?" in that question. Of course you put the above code in your `.emacs`, or `M-x`/`M-:` on the go. – 4ae1e1 Jun 16 '14 at 17:40
  • This doesn't work. It fails to paste text from the OSX clipboard. – incandescentman Jun 16 '14 at 20:17
  • I've updated my answer to solve the OS X clipboard pasting problem. Hope it helps. (And hope someone could clear that downvote. The previous solution didn't really deserve a downvote — this is not meta, AFAIK people only down vote totally irrelevant answers, not partial solutions.) – 4ae1e1 Jun 16 '14 at 20:50
  • Also added pbcopy keybinding. – 4ae1e1 Jun 17 '14 at 05:03
  • That works! There is still one problem: after `s-v` to `paste clipboard`, the point is still before the inserted text. The point should be after the inserted/pasted text. Thanks. – incandescentman Jun 18 '14 at 05:45
  • @PeterSalazar Sorry I overlooked this. Now fixed. – 4ae1e1 Jun 18 '14 at 05:58
  • Excellent! That works now. One last thing: we need to create a function for `s-x`, i.e. `pbcut`, which I guess just means pbcopy plus delete region? Maybe after that you could publish this as a package! – incandescentman Jun 18 '14 at 17:08
  • 1
    @PeterSalazar Good point (though I doubt if I'll ever need that). Updated. Also factored out the functions so that you can also call them from tty (or set keybindings for tty). A package is clearly an overkill for something as simple as this. I'll release a gist then. – 4ae1e1 Jun 18 '14 at 17:46
  • There seems to be a problem with the way it pastes whitespace. I just tried to copy and paste five spaces, but it only pasted a single space. It seems to be a problem with the pasting, not the copying. – incandescentman Jun 18 '14 at 21:29
  • @PeterSalazar Could you please give me a minimal working example? – 4ae1e1 Jun 18 '14 at 21:31
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/55873/discussion-between-kevinsayhi-and-peter-salazar). – 4ae1e1 Jun 18 '14 at 21:50
  • I see the problem. It was that the `s-v` key binding was not active in the mini buffer. Adding this seems to resolve the issue: `(define-key minibuffer-local-map (kbd "s-v") 'pasteboard-paste)` – incandescentman Jun 20 '14 at 02:39
  • 1
    @PeterSalazar Yeah, minibuffer has its own set of local maps. – 4ae1e1 Jun 20 '14 at 02:41
  • I've run into a problem with pasteboard-paste: UTF characters such as `“ ” ' ' – —` get pasted as garbage characters. Is there a way to run the clipboard contents through some kind of UTF filter before pasting? – incandescentman Jul 02 '14 at 03:00
  • @PeterSalazar I just tried pasting `“ ”`, and didn't notice any problem. Maybe there's something wrong with the coding of your buffer? – 4ae1e1 Jul 02 '14 at 03:01
  • Try `C-h v buffer-file-coding-system`. Mine is always `utf-8-unix`. – 4ae1e1 Jul 02 '14 at 03:05
  • Thanks for the quick response. Are you able to Copy to Clipboard from Chrome and then pasteboard-paste UTF characters, like, say, the first line of this? http://www.csbruce.com/software/utf-8.html – incandescentman Jul 02 '14 at 06:21
  • @PeterSalazar Yup, no problem at all. Tested both in window system and tty. See screenshots: http://i.imgur.com/GsydVgs.png http://i.imgur.com/oXcxgO9.png – 4ae1e1 Jul 02 '14 at 06:24
  • @PeterSalazar Could you please inspect the value of the variable `buffer-file-coding-system` (as I said above)? – 4ae1e1 Jul 03 '14 at 22:24
  • `Its value is utf-8-unix Local in buffer 'today.org'; global value is nil` – incandescentman Jul 04 '14 at 04:16
  • more details here http://chat.stackoverflow.com/rooms/55873/discussion-between-kevinsayhi-and-peter-salazar – incandescentman Jul 04 '14 at 04:17
  • @PeterSalazar I tried to answer your question in chatroom. – 4ae1e1 Jul 04 '14 at 04:26
  • I found the solution: `(setenv "LANG" "en_US.UTF-8")`. Source: http://stackoverflow.com/questions/24620039/how-to-pbpaste-utf-8-characters-in-gnu-emacs-for-osx/24639415?iemail=1&noredirect=1#24639415 – incandescentman Jul 08 '14 at 22:57
  • @PeterSalazar Okay, so your locale isn't `en_US.UTF-8`. That's the difference between our setups. However, I'd rather be conservative about messing with the locale, so I've separated your workaround from the original code. – 4ae1e1 Jul 09 '14 at 01:51
  • Your solution has been working flawlessly for months. One question: I'm still getting `^M` characters when I copy content from certain apps and use `pasteboard-paste` to paste into Emacs. I can fix this by doing a `replace string` and replacing `^M` with C-q C-j. However, is there a way to catch this within `pasteboard-paste`? – incandescentman Dec 05 '14 at 18:40
  • 1
    @PeterSalazar Glad it helped. Regarding `^M`, most certainly there's a very simple solution. `^M` is CR (carriage return, ASCII `\x0d`). While Unix-like systems (including OS X) uses LF (linefeed) as newline, MS DOS and Windows uses CR+LF, and classic Mac OS uses CR alone. Those are the sources of `^M` you see (for instance, many web page source code uses `CR+LF` as newline — in fact, even some server response headers; and Apple's own script editor uses CR). – 4ae1e1 Dec 05 '14 at 20:31
  • 1
    @PeterSalazar If you're sure you never want to see `^M` (I don't), just replace the `"pbpaste"` in the original code with `"pbpaste | perl -p -e 's/\r$//' | tr '\r' '\n'"`. Should take care of both CRLF and CR alone. I'm using perl here because the BSD sed shipped with OS X is so limited that it doesn't even seem to recognize `\r` escape. (Maybe I'm just not good enough with BSD sed; anyway, I don't have the incentive to be good at this crap when GNU sed is much better.) Please inform me of whether this works. – 4ae1e1 Dec 05 '14 at 20:34
  • Any idea how to copy text from the Emacs kill-ring to the OSX pasteboard? Because some functions (e.g. gist-buffer) push output into the kill ring and I'd like to be able to access it from other OSX apps. Thanks! – incandescentman Jul 29 '15 at 15:20
  • 1
    @incandescentman You might need to create a temporary buffer with kill ring content and then `shell-command-on-region`. – 4ae1e1 Jul 29 '15 at 15:26
  • 1
    @incandescentman Something that might help: http://stackoverflow.com/a/15694531/1944784 – 4ae1e1 Jul 29 '15 at 15:28
1

After much fiddling around, I'm pretty sure that the only way to make this work is to override the x-select-text method. Check out my answer here for all the details: https://stackoverflow.com/a/23254771/71522

Community
  • 1
  • 1
David Wolever
  • 148,955
  • 89
  • 346
  • 502
  • It still fails my main test: `org-kill-line` followed by `clipboard-yank` should be independent of each other. – incandescentman Apr 23 '14 at 22:34
  • I (1) evaluated this code: `(defun x-select-text (text)) (setq x-select-enable-clipboard nil) (setq x-select-enable-primary nil) (setq mouse-drag-copy-region nil) (setq interprogram-cut-function 'ns-set-pasteboard) (setq interprogram-paste-function 'ns-get-pasteboard)`; (2) did `org-kill-line` (3) did `clipboard-yank`. Clipboard yank still inserts the killed text rather than the contents of OSX clipboard... either that or the killed line is going into the OSX clipboard. – incandescentman Apr 23 '14 at 22:59
  • Hrm, annoying. Is the line put on the OS X clipboard? Otherwise, maybe check `org-kill-line` to see what it's doing? (`M-x find-function org-kill-line`) – David Wolever Apr 24 '14 at 01:34
  • I tried it with `M-x kill-line`. The killed text still goes straight into the OSX clipboard. – incandescentman Apr 25 '14 at 07:27
1

NOTE:  This draft solution is not meant to be an Emacs system-wide modification separating the clipboards -- instead, this is a custom solution designed to keep the clipboards separated on an interactive basis only when specifically using these custom functions. Other functions within Emacs that use the kill-ring can be modified using a similar method -- the variables interprogram-cut-function and interprogram-paste-function can be made let-bound to a nil value for the duration of the specific functions (either through advice, or modification of the source itself, or creating new functions and/or using a defalias). However, the latter is beyond the scope of this limited example.


HISTORY

First Draft (December 23, 2014):  This is a first draft, which is based on the idea that the OSX clipboard may be accessed only when using C-u before calling either the copy or paste functions. If C-u is called first, then the OSX clipboard is utilized. As I use the functions more on a daily basis, I may have additional revisions to this code and I will update same from time to time:

EDIT (December 24, 2014):  Removed * from the interactive command statement as to lawlist-copy-selected-region -- that was a read-only check needed for pasting, but not copying. Added a statement regarding the general nature of this example.

EDIT (December 28, 2014):  Revised code to better handle when the user forgot to select a region before calling lawlist-copy-selected-region. Small revisions to make the code more concise.


(defun lawlist-copy-selected-region (&optional arg)
(interactive "P")
  (let* (
      (interprogram-cut-function
        (when (equal arg '(4)) interprogram-cut-function))
      (interprogram-paste-function
        (when (equal arg '(4)) interprogram-paste-function))
      (region-active-p (region-active-p))
      (beg (when region-active-p (region-beginning)))
      (end (when region-active-p (region-end)))
      (copied-string
        (when region-active-p (buffer-substring-no-properties beg end))) )
    (unless region-active-p
      (let ((debug-on-quit nil))
        (signal 'quit `("No region has been selected!"))))
    (copy-region-as-kill beg end)
    (when (not (active-minibuffer-window))
      (message "%s"
        (concat
          (if (and interprogram-cut-function interprogram-paste-function)
            "OSX+Emacs:  "
            "Emacs:  ")
          (truncate-string-to-width copied-string 40)
          (when (> (length copied-string) 40)
            " . . .")))) ))

(defun lawlist-yank (&optional arg)
  (interactive "*P")
  (unless arg (setq arg 1))
  (setq yank-window-start (window-start))
  (setq this-command t)
  (push-mark (point))
  (insert-for-yank
    (lawlist-current-kill
      (cond
        ((listp arg)
          arg)
        ((eq arg '-)
          -2)
        (t
          (1- arg) ))))
  (if (consp arg)
      (goto-char (prog1 (mark t)
       (set-marker (mark-marker) (point) (current-buffer)))))
  (if (eq this-command t)
      (setq this-command 'yank))
  (when (region-active-p)
    (setq mark-active nil))
  nil)

(defun lawlist-current-kill (n &optional do-not-move)
  (let ((interprogram-paste
          (and
            (equal n '(4))
            interprogram-paste-function
            (funcall interprogram-paste-function))))
    (cond
      (interprogram-paste
        (let ((interprogram-cut-function nil))
          (if (listp interprogram-paste)
            (mapc 'kill-new (nreverse interprogram-paste))
            (kill-new interprogram-paste)))
        (car kill-ring))
      ((and (equal n '(4)) (not interprogram-paste))
        (car kill-ring))
      (t
        (or kill-ring 
          (let ((debug-on-quit nil))
            (signal 'quit `("The kill-ring is empty."))))
        (let (
            (ARGth-kill-element
              (nthcdr
                (mod (- n (length kill-ring-yank-pointer)) (length kill-ring))
                kill-ring)))
          (unless do-not-move
            (setq kill-ring-yank-pointer ARGth-kill-element)
            (when
                (and
                  yank-pop-change-selection
                  (> n 0)
                  interprogram-cut-function)
              (funcall interprogram-cut-function (car ARGth-kill-element))))
        (car ARGth-kill-element))))))
lawlist
  • 13,099
  • 3
  • 49
  • 158
0
(global-set-key (kbd "C-x M-y")
              (lambda ()
                (interactive)
                (insert-string (ns-get-pasteboard))))

(global-set-key (kbd "C-x M-w")
              (lambda ()
                (interactive)
                (when (region-active-p)
                  (ns-set-pasteboard
                   (buffer-substring (region-beginning)
                                     (region-end))))))
Nazik
  • 8,696
  • 27
  • 77
  • 123
0

simpleclip might be helpful -

Simplified access to the system clipboard in Emacs.

simpleclip-mode radically simplifies clipboard handling: the system clipboard and the Emacs kill ring are made completely independent, and never influence each other.

The super keybindings are friendly for OS X: super is generally mapped to the "command" key ie ⌘.

Tested on OS X, X11, and MS Windows

https://github.com/rolandwalker/simpleclip

Brian Burns
  • 20,575
  • 8
  • 83
  • 77
-1

Use

(setq select-enable-clipboard nil)

This will only separate the two clipboards, and for ⌘ c and ⌘ v to work like mentioned you will have to rebind them to clipboard-kill-ring-save and clipboard-yank:

(keymap-global-set "s-c" 'clipboard-kill-ring-save)
(keymap-global-set "s-x" 'clipboard-kill-region)
(keymap-global-set "s-v" 'clipboard-yank)

I am using this Emacs: https://github.com/railwaycat/emacs-mac-port, and it also works on Emacs 28 built from source.

hraban
  • 1,819
  • 1
  • 17
  • 27
tungd
  • 14,467
  • 5
  • 41
  • 45
  • I tried this, and no, it does not work. For a solution to work correctly, it should be able to pass the test in steps #1 to #4 above. – incandescentman Apr 11 '14 at 02:30
  • Still does not work. When I do (1) copy text from Chrome to the OSX clipboard, then (2) in Emacs do `kill-line`, then (3) do `clipboard-yank`, it still pastes the killed line from Emacs instead of the desired OSX clipboard text. – incandescentman Jun 16 '14 at 21:31
  • 1
    This is the correct answer. No messing around needed with pbpaste or external packages. I've just tried it on OS X, on Emacs built from source. – hraban Apr 29 '22 at 18:27