Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

emacs terminal mode: how to copy and paste efficiently

I'm having a hard time making this emacs -nw work effectively under the terminal mode (emacs -nw). Some setup information: The working server is connected via SSH, and emacs is running on the server. Usually I'm connecting using SSH and "emacs -nw" to work on my files.

The emacs config is picked up from: https://hugoheden.wordpress.com/2009/03/08/copypaste-with-emacs-in-terminal/

;; make mouse selection to be emacs region marking
(require 'mouse)
(xterm-mouse-mode t)
(defun track-mouse (e)) 
(setq mouse-sel-mode t)

;; enable clipboard in emacs
(setq x-select-enable-clipboard t)

;; enable copy/paste between emacs and other apps (terminal version of emacs)
(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
 ))

The reason to have:

 (require 'mouse)
 (xterm-mouse-mode t)
 (defun track-mouse (e)) 
 (setq mouse-sel-mode t)

is to enable mouse selection over text such that the text region is highlighted just as "C-x SPC" marking the region. Then I can use "M-x w" to copy and "C-x y" to paste text within emacs and between emacs and other apps.

All look perfect except that any operations related to X are REALLY SLOW! My connection to the remote server is smooth -- the latency is usually under 100ms. But to kill one line of text using "C-x k", it takes ~5 seconds! To paste it, it takes another 5 seconds!

When copy/paste is frequent sometimes, this becomes really annoying. I think this is related to the X sever messaging, but not sure if there is good way to fix this.

Any ideas? Thanks!

like image 460
galactica Avatar asked Jan 04 '15 09:01

galactica


1 Answers

You can accomplish this by using a terminal escape code! There is a unique category of terminal escape codes called "Operating System Controls" (OSC) and one of these sequences (\033]52) is meant for interacting with the system clipboard. The great thing is that your terminal doesn't care where the code came from so it will work in remote sessions as well.

Most terminal emulators support it (iTerm2, OS X Terminal, and I think all Linux terminals besides GNOME). You can test if your terminal supports this sequence by simply running:

$ printf "\033]52;c;$(printf "Hello, world" | base64)\a"

Then paste from your system clipboard. If it pastes "Hello, world" then your terminal supports it!

I have this function in my init.el so when I call yank-to-clipboard Emacs will yank the value from my kill ring into the system clipboard:

(defun yank-to-clipboard ()
"Use ANSI OSC 52 escape sequence to attempt clipboard copy"
  (interactive)
  (send-string-to-terminal
    (format "\033]52;c;%s\a"
      (base64-encode-string 
        (encode-coding-string 
          (substring-no-properties 
            (nth 0 kill-ring)) 'utf-8) t))))

As I type this, I stumbled upon an almost-identical script supported by Chromium community: https://chromium.googlesource.com/apps/libapps/+/master/hterm/etc/osc52.el

For those running Emacs inside Tmux: Tmux consumes the sequence, so you'll need to pipe the sequence to the Tmux active tty for this to work. I have a solution in my blog post here: https://justinchips.medium.com/have-vim-emacs-tmux-use-system-clipboard-4c9d901eef40

like image 54
justinokamoto Avatar answered Oct 08 '22 17:10

justinokamoto