发我之前博客上的一篇老贴,原本准备贴到 emacer.com, 我通过邮件发送了这篇文章的 org-mode 源码给了管理员,可是一直没动静,邮件问了两次,也没给我明确答复。直到大概八个月之后,我收到了他的一封邮件,告诉我这个这个文章太简单了,不符合条件,我晕~~
这里再重新贴一次。主要是这篇帖子在快捷键方面的表达了我的 Emacs 配置的一些基本理念吧。现在很多方面比当时已经复杂很多很多了。不过快捷键的部分仍然是通用的。而这一部分也 (应该) 是初次接触 Emacs 的新人最希望了解的部分吧。
而且一两天准备上传我的全套 Emacs 配置,先读一读这个,肯定容易上手的多,就当提前做个铺垫吧
因为 org-mode 转化到 markdown-mode, 太费劲儿,所以简单的编辑了下,先凑合合看。看不懂也没关系,你真正看代码,就知道定制自己的 Emacs 有多简单了。(你甚至不需要任何 lisp 知识,就可以几乎实现一切。
使用 Emacs 不知不觉已经半年多了。最初 Emacs 进入我的视野,还是因为 VI 的缘故。
一再听说 VI 作为 UNIX 下的一大神器如何如何,一直心向往之!2011 年春节过完年那会儿开始,下决心一定要熟练的使用 VI, 我甚至专门打印了 VI 的按键图,并贴在电脑面前的墙上,当时就抱着本 O'reilly 出版的 Learning the vi and Vim Editors 啃个大半个月,结果除了会用 HJKL(还不太熟), 完全没有感觉,使用 VI, 给我的感觉就是:总是想起不来该按那个键 (这可能是所有 Vi 新人的感受吧), 完全不着调。也许是在 Windows 下混太久了,手部肌肉无法适应这种操作方式,我想很重要的一个原因,受 Windows 的组合快捷键体系影响太深了吧。因此当时也就没有坚持下来,结果导致 VI 半途而废吧。不过没有这个半途而废,也就没有现在这篇帖子了。
但是现在我知道,其实还有一个更重要的原因: 而且
当时,抱着 VI 铁打不换的心理,想试试 Emacs 到底是什么样子的。结果大家都知道了,一发不可收拾,从我开始尝试 Emacs 那天起,我没有一天不鼓捣一会儿 Emacs, 从最简单的显示设置,到开始尝试自定义按键,再到后来的 org-mode, GTD, ido, w3m, plantuml 等等,兴趣越来越浓厚,不可自拔的陷入了 Emacs 的洪流中,在不断的尝试过程中,我深深感受到了自由的力量,以及 Emacs 的高度可定制及博大精深 (虽然我还很菜,甚至 LISP 也只是刚刚入门).
说了这么多,其实我只是想从不同的角度来表达自己的一个观点:
VI 及 Emacs 给人的感觉是那样的完全不同,Vi 的操作方式可以用千篇一律来形容,如果你无法高效率的驾驭它,你会感觉到深受约束。他的学习曲线是那样的陡峭,以至于大多数人都会在面对 Vim 的时候最终望而却步。而 Emacs 与 Vim 是那么的不同,以至于每个人都会有一个属于自己的,完全不一样的 Emacs.
所以我认为,网上流传的,这个世界上只有两种编辑器,一个叫做 Vi, 另一个叫做 Emacs(包括 textmate 等,都算), 这个说法我是很认同的。
以至于我认为,两者一定有一个更适合你,更极端点说,如果你一开始就深深的喜欢其中一个,那另一个必定非常不习惯,很难适应。所以,我建议,有兴趣的朋友不妨两个都试过后,再来选择吧。
对于我来说:应该是骨子里的性格决定了最终我选择了 Emacs.
不过,而作为一个程序员而言,Emacs 的包容万象 (emacs wiki 几乎有你想到的一切解决方案) 以及一体式操作界面,很显然是更好的选择. 而 VI 则更似乎适合于在纯文本终端(text-terminal) 下编辑系统脚本,是 linux 下系统管理员的最佳选择。(不过自从有了 emacs daemon, Emacs 启动慢这顶帽子,彻底被摘去了。所有文件都是秒开~~ 我觉得除了特殊的情况下 (例如某些嵌入式系统) 无法运行 Emacs, 其他情况下,Emacs 一点也不比 VI 差这一点,这篇文章你看到后面就知道。)
以上一大堆废话,是菜鸟讲给菜鸟听的,老鸟可以无视之。下面言归正传。
正如标题所述,这篇文章主要是讲 Emacs 的快捷键设置。
我想很多新手一定和我一样,刚开始使用 Emacs,很多快捷键很不适应. Emacs 的默认快捷键布局也的确有很多不和谐的地方,以下列出一些我认为很不好的地方。
太多的键了 Ctrl,长时间使用会使左手的小手指很不舒服。据说 Emacs 发明那会儿的键盘,Ctrl 键的位置其实在现在键盘 Alt 键的位置,那明显用 Alt 比用 Ctrl 输入,一点都不奇怪了。
某个组合键对于东方人种的小手来说,间距貌似有点远。
对于我这种习惯于使用键盘左侧功能键的用户 (多数都是吧?) 有些键不是一般的绕.....
而我重新设定大量快捷键所遵循的标准如下:
四个手指尽量不离开或者少离开键盘标准位置。
因为我经常在纯文本终端下使用 Emacs, 需要保证快捷键在图形 Terminal 下和 console 下的文本终端下,常用组合键都尽可能一致。
保证在所有平台 (WIN32,X,Console) 按键的一致性。
尽量少用 Ctrl, 多用 Alt. (__备注:自从将 Ctrl 换到 Capslock 位置之后,用 Ctrl 更多了,即回归了 Emacs 的标准使用方式)
以下所讲的快捷键主要是根据个人的喜好,针对默认快捷键做了大幅的修改。个人认为,至少在两点上对于新手来说上手的确应该比默认容易的多,而且,键位更加合理,有效。适合长时间写作。
你可以熟练的盲打。并且知道标准盲打你的手指该放的位置。
这篇文章中,用到的功能键前缀约定。
| 简写 | 表达的组合键 | |------+-------------------------------------------------------------------------------------| | C-c | 表示 Ctrl-c, 指按下 Control 同时再按下 c, 如果是 C-c C-f, 表示按下 Ctrl 不放,先按 c, 再按 f | |------+-------------------------------------------------------------------------------------| | M-c | 表示 Meta-c, Meta 就是键盘上的 Alt 键 | |------+-------------------------------------------------------------------------------------| | C-C | 表示 Ctrl-大写的 C, 即 Ctrl-Shift-c |
如果你运行过 Emacs,在你的家目录一定会存在一个.emacs.d 目录. 首先使用你熟悉的编辑器,VI 也好,nano, ed, cat 也行,无论什么办法,在.emacs.d 目录下创建两个文件
;;; init.el --- Where all the magic begins # => ; 在lisp中表示注释.
(if (file-exists-p "~/.emacs.d/.emacs") (load "~/.emacs.d/.emacs")) # => 如果存在~/.emacs.d/.emacs, 则调用它.
;;; init.el ends here
;; -*-Emacs-Lisp-*-
;; 你如果按下M-RET, 其实相当于按下了C-j.
(define-key key-translation-map [(meta ?\r)] [(control j)])
(define-key key-translation-map [(meta return)] [(control j)])
;; 你如果按下C-j, 其实相当于按下了C-S-j, 有特殊用途.
(define-key key-translation-map [(control j)] [(control J)])
(define-key key-translation-map [(control l)] [(control L)])
;; 下面这两个键模拟Vi的光标不动屏幕动效果, 我很喜欢, 几乎总在使用.
(global-set-key [(meta n)] 'window-move-up)
(global-set-key [(meta p)] 'window-move-DOWN)
;; 同上, 但是是另一个buffer窗口上下移动. 常常查看帮助用这个.
(global-set-key [(control N)] 'other-window-move-down)
(global-set-key [(control P)] 'other-window-move-up)
;; C-l 向右移动一个字符, C-j向左移动一个字符.
(global-set-key [(Control l)] 'forward-char)
(global-set-key [(Control j)] 'backward-char)
;; M-l 向右移动一个单词, M-j向左移动一个单词.
(global-set-key [(meta l)] 'forward-word)
(global-set-key [(meta j)] 'backward-word)
;; 上下翻页, M-v, M-q.
(global-set-key [(meta v)] 'scroll-up)
(global-set-key [(meta q)] 'scroll-down)
;; 行首, 设定mark以及行尾.
(global-set-key [(meta \1)] 'move-beginning-of-line)
(global-set-key [(meta \2)] 'set-mark-command)
(global-set-key [(meta \3)] 'move-end-of-line)
;; 切换buffer, 由默认的C-x b换成了C-x n.
(global-set-key [(control x) (n)] 'switch-to-buffer)
;; 演示了如何定义一个新的按键前缀. 这里定义了M-c作为按键前缀.
(define-prefix-command 'meta-c-map)
(global-set-key [(meta c)] 'meta-c-map)
;; 演示了如何在一个模式下(这里是isearch模式), 定义快捷键. 退出isearch-mode, 所有按键失效.
(add-hook 'isearch-mode-hook
'(lambda ()
(define-key isearch-mode-map [(meta n)] 'isearch-repeat-forward)
(define-key isearch-mode-map [(meta p)] 'isearch-repeat-backward)
(define-key isearch-mode-map [(control r)] 'isearch-query-replace)
(define-key isearch-mode-map [(meta 5)] 'isearch-query-replace-regexp)
(define-key isearch-mode-map [(meta f)] 'isearch-yank-word-or-char)
(define-key isearch-mode-map [(meta y)] 'isearch-yank-kill)
(define-key isearch-mode-map [(meta k)] 'isearch-yank-line)
(define-key isearch-mode-map [(hyper l)] 'isearch-yank-char)
(define-key isearch-mode-map [(hyper j)] 'isearch-delete-char)
(define-key isearch-mode-map [(meta o)] 'isearch-occur)
(define-key isearch-mode-map [(meta w)] 'isearch-forward-word)
(define-key isearch-mode-map [(meta s)] 'isearch-repeat-forward)
))
;; ------------------------------下面是上面用到的函数定义------------------------------
(defun window-move-up (&optional arg)
"Current window move-up 2 lines."
(interactive "P")
(if arg
(scroll-up arg)
(scroll-up 2)))
(defun window-move-down (&optional arg)
"Current window move-down 3 lines."
(interactive "P")
(if arg
(scroll-down arg)
(scroll-down 3)))
(defun other-window-move-up (&optional arg)
"Other window move-up 1 lines."
(interactive "p")
(scroll-other-window arg))
(defun other-window-move-down (&optional arg)
"Other window move-down 2 lines."
(interactive "P")
(if arg
(scroll-other-window-down arg)
(scroll-other-window-down 2)))
#+end_src
有了以上两个文件,你就可以使用 Emacs 开始基本的工作了。
功能 | 默认快捷键 | 改动后的快捷键 | 函数 |
---|---|---|---|
向右移动一个字符 | C-f | C-l | forward-char |
------------------+------------+----------------+--------------- | |||
向右移动一个单词 | M-f | M-l | forward-word |
------------------+------------+----------------+--------------- | |||
向左移动一个字符 | C-b | C-j | backward-char |
------------------+------------+----------------+--------------- | |||
向左移动一个单词 | M-b | M-j | backward-word |
------------------+------------+----------------+--------------- |
其实对于多数人来说,C-f 和 C-b 还好了,不难受,不过 C-f 对于手小一点的使用者,的确时间长了有点累,而 C-b, 因为我自己使用笔记本电脑,而且我不用鼠标,主要使用键盘中间的指点杆,按下 b 键的时候,总是碰小红帽,不是很舒服,改成这两个快捷键以后,彻底释放了左手,左手只需要管一个 Ctrl, 而右手单手操作 C-n, C-p, C-j, C-l.也许有些使用者会说,C-j 和 C-;不是更好么?使用起来感觉更像 VIM 了。我最初也是这样设定的,不过后来到文本终端下,发现 C-;是无效的按键,所以选择了 C-l.
因为 C-j 与 C-l 在很多 Emacs 的模式下都进行了重定义,为了一劳永逸,我使用了一个技巧,就是将 C-l,C-j 使用 key-translation-map 重新定义了这两个键的 keymap, 这两个键在任何模式都映射到 C-L, C-J, 然后对 C-L, C-J 再重新定义为左右移动的快捷键。而默认的 C-j(linux, 通常用于换行),
我直接映射到了alt+回车
, 如果你希望像之前 C-j 那样换行,不得不按下alt-回车
,否则
只可能是向左移动一个字符。
功能 | 默认快捷键 | 改动后的快捷键 | 函数 |
---|---|---|---|
向下翻页 | C-v | M-v | scroll-up |
------------------------------+------------+----------------+------------------ | |||
向上翻页 | M-v | M-q | scroll-down |
------------------------------+------------+----------------+------------------ | |||
光标位置不变,内容向上移动 2 行 | C-u 2 C-v | M-N | window-move-up |
------------------------------+------------+----------------+------------------ | |||
光标位置不变,内容向下移动 3 行 | C-u 3 M-v | M-P | window-move-down |
------------------------------+------------+----------------+------------------ |
功能 | 默认快捷键 | 改动后的快捷键 | 函数 |
---|---|---|---|
设定标记 | C-空格 | M-2 | set-mark-command |
------------+------------+----------------+------------------------ | |||
移动到行首 | C-a | M-1 | move-beginning-of-line |
------------+------------+----------------+------------------------ | |||
移动到行尾 | C-e | M-3 | Move-end-of-line |
------------+------------+----------------+------------------------ | |||
标记单词 | M-@ | M-f | mark-word |
首先说明,一下快捷键都是只在 isearch 模式下有效。要随时注意模式条上 isearsh 的标记。
功能 | 快捷键 | 函数 |
---|---|---|
搜索下一个结果 | M-n | repeat-forward |
-------------------------------------------+---------+-------------------------- | ||
搜索前一个结果 | M-p | repeat-backward |
-------------------------------------------+---------+-------------------------- | ||
正则替换 | M-5 | query-replace-regexp |
-------------------------------------------+---------+-------------------------- | ||
将光标到行尾作为搜索内容 | C-k | isearch-yank-line |
-------------------------------------------+---------+-------------------------- | ||
剪切板作为搜索内容 | C-y | isearch-yank-kill |
-------------------------------------------+---------+-------------------------- | ||
向左或向右 (选择/取消) 单个字符作为搜索内容 | C-l/C-j | isearch-yank/delete-char |
-------------------------------------------+---------+-------------------------- | ||
向左或向右 (选择/取消) 单个单词作为搜索内容 | M-l/M-j | |
-------------------------------------------+---------+-------------------------- | ||
显示 occur 视图 | C-o | isearch-occur |
-------------------------------------------+---------+-------------------------- | ||
单词搜索 | M-w | isearch-word |
-------------------------------------------+---------+-------------------------- |
默认的快捷键搜索,感觉很是绕手,还好,我们使用的 Emacs. 只有你想不到,没有做不到。
下面把我使用 isearch 的流程按照图片的形式显示出来。会更直观.用好 M-n M-p,会使搜索更有效率. isearch 快捷搜索示意图:
还有两句有必要说下: #+begin_src emacs-lisp (define-prefix-command 'meta-c-map) (global-set-key [(meta c)] 'meta-c-map) #+end_src
以上表示,定义 M-c 作为新的按键序列前缀。这样,我们可以方便定义 M-c a M-c b 等等,我新定义了这个 M-c 前缀,主要是把跟编程有关的命令都单独定义在这个按键序列中,你也可以自己这样做。当然,不一定非要 M-c.谁说 M-o 不可以?
以上只是简单的介绍了一下这半年来本人有关 Emacs 快捷键的心得体会。
当然本人对快捷键的改动及新增远远不止这些,但是以上代码这些足够给 Emacs 新手足够的提示,去自己修改属于自己的按键序列。
有什么不合适或不对的地方,欢迎指正。有任何建议,欢迎提出!