将Emacs作为X剪切板管理器
一直以来,我都是使用clipit(parcellite的克隆版本)作为X环境下的剪切板管理器(X clipboard manager). 但是我发现当有一个剪切板管理器运行时,Emacs会出现一个bug, 这逼着我去寻找clipit的替代品. 我如愿以偿地找到了一个名为clipmon的package, 它工作的相对还不错,但是美中不足的是,它也有一个 bug. 而且,看起来在Emacs中监测X剪切板的唯一方式似乎只能由Emacs主动去检查X剪切板中的内容. 我不是很喜欢这种每隔一两秒就由Emacs检查一次X剪切板的方案. 我的替代方案是将检查剪切板的动作移出Emacs,由shell脚本来完成.
#!/bin/sh occ=$(xclip -sel clip -o | sed 's/[\"]/\\&/g') while sleep 2; do ncc=$(xclip -sel clip -o | sed 's/[\"]/\\&/g') if [ "${occ}" != "${ncc}" ]; then emacsclient -e "(kill-new \"${ncc}\")" occ=${ncc} fi done
这是一个简单而有效的解决方案,但是它依然要不断的探测剪切板的内容. 若能有一个事件驱动的解决方案就好了. 我的Emacs经历让我有股冲动去为X试着写一个on-clipboard-change hook, 但这个工作量就太大了.
一般来讲, 除了Emacs之外,我只用另外两个应用(一个web浏览器,一个终端模拟器),而且这两款应用都是可定制的. 这使得每次我从这些应用中拷贝东西到X剪切板时都可以同时再将剪切板中的内容拷贝到Emacs的kill ring中去. 我用的浏览器是Conkeror,我将下面这段配置添加到它的初始化文件中去了.
function ekr (cc) { if (typeof cc === 'undefined') { cc = read_from_clipboard(); } cc = cc.replace(/([^\\]*)\\([^\\]*)/g, "$1\\\\$2"); cc = cc.replace('"', '\\"', "g"); cc = cc.replace("'", "'\\''", "g"); var ecc = "emacsclient -e '(kill-new \"" + cc + "\")' > /dev/null"; shell_command_blind(ecc); } interactive( "ekr_cmd_copy", "Copy the selection to the clipboard and the Emacs kill ring", function (I) { call_builtin_command(I.window, "cmd_copy", true); ekr(); } ); undefine_key(caret_keymap,"M-w"); define_key(caret_keymap,"M-w", "ekr_cmd_copy"); undefine_key(content_buffer_normal_keymap,"M-w"); define_key(content_buffer_normal_keymap,"M-w", "ekr_cmd_copy"); undefine_key(special_buffer_keymap,"M-w"); define_key(special_buffer_keymap,"M-w", "ekr_cmd_copy"); undefine_key(text_keymap,"M-w"); define_key(text_keymap,"M-w", "ekr_cmd_copy");
我还在 modules/commands.js
中的 kill-region
和 kill-ring-save
的命令以及 modules/content-buffer-input.js
中的 cut-to-end-of-line
命令后添加了对 ekr()
的调用.
在 modules/elements.js
中的 copy_text
函数最后添加了对 ekr(text)
的调用.
第二个常用的非Emacs应用是urxvt/tmux. 为了完成跟上面一样的效果,我将下面这行配置加入到 ~/.tmux.conf
中.
bind-key -temacs-copy M-w copy-pipe 'c2e -r'
下面是c2e脚本的源代码.
#!/bin/sh # c2e: Copy text to the Emacs kill ring. # # With no arguments, send the contents of the X clipboard to the Emacs kill ring. # With -r, first set the clipboard to the contents read from standard input. # With -s, instead send X primary selection to the Emacs kill ring. if [ "${1}" = '-r' ]; then exec xclip -sel clip -i -f | \ emacsclient -e "(kill-new \"$(sed 's/[\"]/\\&/g')\")" elif [ "${1}" = '-s' ]; then exec xclip -o | \ emacsclient -e "(kill-new \"$(sed 's/[\"]/\\&/g')\")" else exec xclip -sel clip -o | \ emacsclient -e "(kill-new \"$(sed 's/[\"]/\\&/g')\")" fi
为了使这个新剪切板管理器(即Emacs的 kill ring)更容易使用,我为StumpWM添加了一个新的命令和相应的快捷键.
(defcommand eaacm () () "Emacs as a clipboard manager." (run-or-raise "emacsclient -nc" '(:class "Emacs")) (run-shell-command "emacsclient -n -e '(let ((helm-full-frame t)) \ (save-window-excursion (delete-other-windows) (helm-show-kill-ring)))'")) (define-key *root-map* (kbd "c") "eaacm")
现在不管我正在使用哪个应用,只需要按下 C-t c
就会有一个helm界面让我选择kill ring中的内容. 在Helm中甚至有一项功能可以将当前entry移动到kill ring的顶端同时将该entry的内容拷贝到X剪切板中. 该功能的默认快捷键是 C-c C-k
.
偶尔我也会需要在其他应用中拷贝内容到剪切板中,因此我还创建了两个简单的StumpWM命令来调用c2e脚本,并分别分配了快捷键.
(defcommand c2e () () "Copy the X clipboard contents to the Emacs kill ring." (run-shell-command "c2e")) (defcommand s2e () () "Copy the X selection contents to the Emacs kill ring." (run-shell-command "c2e -s")) (define-key *root-map* (kbd "C") "c2e") (define-key *root-map* (kbd "s") "s2e")
仅仅过了几天而已,Emacs就变成了一个很不错的X剪切板管理器了.