12

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/help-gnu-emacs@gnu.org/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!

galactica
  • 1,414
  • 1
  • 19
  • 32
  • 1
    Have you tried running emacs locally and using tramp mode? – Squidly Jan 07 '15 at 14:02
  • That was the first attempt I made -- each time I save any edits, it's syncing with the file on the remote server, which is not smooth. Plus, I prefer not to launch emacs often (local machine is a notebook). – galactica Jan 08 '15 at 03:02
  • Out of curiosity, do you have problems with the (setq interprogram-paste...) line not running? I have to run them manually. – Justin Thomas Feb 18 '17 at 00:42

3 Answers3

6

This is not an ideal solution per se, but i figured out a way that I feel better than the previous one.

The idea is to get rid of X which causes heavy latency issues, i.e. keep only the following:

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

The results are:

  1. copy/paste within Emacs is straightforward and fast.

  2. copy from other apps to Emacs: Ctrl+Shift+v

  3. copy from Emacs to other apps: mouse selection is now on X Selection, so right-click and copy shall copy the text into the Selection. Note that 'M-w" now won't copy anything into Selection or system clipboard.

This is again a compromise rather than a solution, but considering the fact that i copy/paste more often than inter-app operations, this is acceptable at the moment.

Still looking forward to a good solution!

galactica
  • 1,414
  • 1
  • 19
  • 32
1

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

justinokamoto
  • 151
  • 1
  • 7
0

To extend on @justinokamoto's answer for use in tmux, it works great and is truly amazing. I haven't debugged it with e.g. tramp or other fancy emacs settings but to get it to work

  1. Follow https://sunaku.github.io/tmux-yank-osc52.html great instructions, modifying your tmux.conf and ~/bin/yank
  2. Make sure terminal access to your clipboard is enabled on your terminal

Then to pull into emacs you can use a function like:

(Caveat emptor, I am very new to elisp. This writes to a temporary file in /tmp/yank)

(defun custom-terminal-yank (&rest args)
  (message "-> CLIP")

  ;; FOR EVIL MODE: UNCOMMENT SO FIRST YANKS TO KILL RING
  ;; need to yank first, with all those args
  ;; ;; https://emacs.stackexchange.com/questions/19215/how-to-write-a-transparent-pass-through-function-wrapper
  ;; (interactive (advice-eval-interactive-spec
  ;;               (cadr (interactive-form #'evil-yank))))
  ;; (apply #'evil-yank args)
  
  ;; https://stackoverflow.com/questions/27764059/emacs-terminal-mode-how-to-copy-and-paste-efficiently
  ;; https://sunaku.github.io/tmux-yank-osc52.html
  (f-write-text (nth 0 kill-ring) 'utf-8 "/tmp/yank")
  (send-string-to-terminal (shell-command-to-string "~/bin/yank /tmp/yank"))
  )

If anyone else uses evil mode as well (just to make things complicated) you can uncomment those lines and use something like (define-key evil-visual-state-map "Y" 'jonah-terminal-yank) So that normal "y" is for normal yanking in visual mode, but "Y" is for cross-clipboard yanking

JZL003
  • 396
  • 4
  • 15