Key-binding(通常翻譯成「按鍵綁定」,或你可能比較熟悉的「快速鍵」) 是操作 Emacs 最重要的管道。使用快速鍵、自訂快速鍵前,尤其自訂遇到問題時,務必先詳讀此篇概念。
章節順序安排無法避免地有點矛盾:這一章請跟第五章的設定檔部份一起參照閱讀。我再想想要怎麼排會更好。
– ono hiroko
只要是 M-x
呼叫得出的 function,你都可以重新綁定成你自己喜歡的按鍵,稱作 key-binding 。分成兩種,一種是全域 Global-key,一種是 mode 自訂的 Local-key。
- 使用
C-h f
(f 代表 function) 查詢某 function 的用途、文件與該 function 所有的 key-binding 等資訊。 - 使用
C-h k
(k 代表 key-binding)、再按下任意 key-binding,可以查詢其 key-binding 在目前 buffer 下所綁定到的 function。 - 使用
C-h m
(m 代表 mode) 查詢目前的 buffer 下、啟動了哪一個 major-mode、哪些 minor-modes、以及所有可用的 key-bindings。 - 使用
C-h v
(v 代表 variable) 查詢某個 variable 的值:- 例如可以查詢
major-mode
這個變數的值,以得知目前的 major-mode 實際上的 symbol 名稱( 在設定 key-binding 和 hook 時需要用到這個值 )
- 例如可以查詢
- 按任意 prefix key 後再按下
C-h
,可以得知目前 buffer 下,以該prefix key為開頭的所有可用的key-bindings。
Symbol 是 Lisp、Ruby、Julia 等語言中有的一種資料型態,目前不要深究也沒關係,我們現在只是要弄設定檔。
假如我覺得 C-z
(在 console 下的作用是把 Emacs 移到背景執行)實在沒啥用還常常按錯,想把他改成「選取文字」(set-mark-command,原本只有綁到 C-@
),可以這樣做:
(global-set-key (kbd "C-z") 'set-mark-command)
你也可以只是把 C-z 取消成沒有用的鍵,讓他變成一個 “prefix key”(詳情請見第四章):
(global-unset-key (kbd "C-z"))
不管是 Major 或 Minor mode,都有自己的 local key (keymap)。Local-key 只在你指定的 mode 下有用。
例如,你希望在 Twitter 的 Emacs client Twittering-mode
下,按大寫 U
可以顯示自己的 timeline,可以使用 define-key
:
(define-key twittering-mode-map (kbd "U") ’ twittering-user-timeline)
各個 mode 儲存 key-bindings 的變數一律是 「該 mode 的正式名稱 + -map
」 。例如名稱叫 twittering-mode
的 major mode,他的 key-map 就是 twittering-mode-map
。這是規則,記下來就對了。
查 mode 的正式名稱最快的方式:
C-h v
major-mode
可以查詢目前 buffer 下 major mode 的正式名稱C-h v
minor-mode-list
查詢目前 buffer 下所有啟動的 minor mode 的正式名稱
Key 的設定是 新的會直接覆蓋舊的 ,在 init.el 有時要注意這點,否則會發現為何自己的 global-key 設定沒有生效,才發現自己原來之前設定過同樣設定。(因為 Emacs Lisp 是直譯式,init.el 的設定是從第一行一行一行執行到檔案尾端,所以後面的設定會蓋掉前面的。例如你改了兩次的 C-@
就會發生這種情形)
設定按鍵時有一些注意事項:
在同個 buffer 下,當一個 key-binding 同時被設為 global key 與 local key 時,local key 會被優先採用。
假如你在設定檔裡面放入:
(global-set-key (kbd "C-r") 'undo-tree-redo)
(global-set-key (kbd "C-r") 'recentf-open-files)
你會發現最後 C-r
執行的是第二行的 recentf-open-files
。所以當你納悶為何定義快速鍵沒用時,檢查一下是否重複設定了。
來看範例,假如你在設定檔裡放入這兩行:
(global-set-key (kbd "C-c m") 'moedict)
(global-set-key (kbd "C-c m r") 'moedict/region)
按鍵衝突發生了。第一行執行起來沒問題,但執行到第二行就炸掉了。因為 Emacs 會不知道你按 C-c m
時到底是想衝三小,到底是打算執行 moedict
呢,還是準備執行 moedict/region
按到一半呢?他是要出來嗎,還是要進去呢?真的很痛苦。所以 Emacs 乾脆不允許這種設定,直接報錯。
讓我們斷開鎖鏈,斷開魂結,斷開 key-binding 的一切牽連:
(global-unset-key (kbd "C-c m")) ; 清除剛才我們設定錯的 C-c m ,這樣所有 C-c m 開頭的綁定都會被清除
(global-set-key (kbd "C-c m m") 'moedict) ; 重新綁定
(global-set-key (kbd "C-c m r") 'moedict/region)
這樣寫就沒問題啦。
C-x
與 C-c
是前綴(prefix)組合鍵,有特殊意義:
- 你無法單獨使用 Prefix key(例如你無法把某個功能綁到只按一個
C-x
就能達成,當你試圖這樣綁定時它會報錯)。 - 反過來說,「預設情況下」,你也無法用這兩個以外的按鍵當作 Prefix key ,例如把某個功能綁定到
C-s C-k
或C-d m
。 - 靠著 Prefix key,你可以綁任意「深度」的 combo「組合技」、「連續技」,例如只要你爽(或者夠無聊),你也可以把查 萌典 的命令綁到
C-x 上 上 下 下 左 右 左 右 a b
,只要他沒跟任何現有key-binding衝突即可。
(global-set-key (kbd "C-x <up> <up> <down> <down> <left> <right> <left> <right> a b") 'moedict)
我自己在設定快速鍵時常利用單字的第一個字母作為設定的規則,可以讓這種組合技變得很好記憶。例如我這樣設定我的 Magit (Git 的 Emacs 版前端):
(global-set-key (kbd "C-x g s") 'magit-status) (global-set-key (kbd "C-x g l") 'magit-log)這樣我就可以按
C-x g s
來看git status
,按C-x g l
來看git log
。– ono hiroko
如果我們要「組合技」,就一定要prefix key。因為所有組合技的開頭一定是一個prefix key。(否則就會直接執行該按鍵的命令了)
假如我覺得只有 C-x
跟 C-c
兩個 prefix 選擇太少了,我想要更多,比如 C-z
可以當作prefix key嗎?
其實是可以的。方法就是:把 C-z
給 unset-key。也就是說, 「一個沒有直接綁定到任何command的key就可以作為prefix key使用」 :
(global-unset-key (kbd "C-z"))
(global-set-key (kbd "C-z a") 'emacs-version)
這概念其實很簡單,但不太好解釋,我們以上面 C-z
的例子可以畫成一個流程圖來看Emacs怎麼接受使用者的key-binding連續技:
C-u
prefix 在 Emacs 裡稱作 universal-argument ,又常稱為 prefix argument ,很多指令在呼叫前,先按一下 C-u
,會提供 與預設行為相關、但不完全相同的功能。
因此, C-u
也跟 C-x
和 C-c
一樣,你無法單獨使用。
Emacs 101 一開始,不是有提過「了解 Emacs 其實是個 Lisp 環境,對於理解 Emacs 的行為是很重要的」嗎?這裡你就可以明白為什麼了。實際上,Emacs 中有內建一個全域變數叫做 current-prefix-arg
。當我們按一下 C-u
時, current-prefix-arg
會變成 (4)
,按兩下會變成 (16)
,再按一次會變 (64)
…以此類推,所以很多 function 會利用這一點,在 function 中檢查目前 current-prefix-arg
的值,來達成「除了本身的功能外額外的功能」。
我們已經知道 C-x C-e
可以 eval Lisp 運算式,並在 minibuffer 中顯示結果。然而如果前面加一個 C-u
prefix 的話,就能把結果插入目前游標位置,而不只是顯示在 minibuffer 中。
另一個例子則是 M-;
我們知道它可以在目前行自動插入該語言的註解。按 C-u M-;
的話 ,則可以把該行註解刪掉、並加入 kill-ring。
再一個例子。在 Org-mode 中,按按 C-c C-l
可以插入各種不同的連結連結,但如果多加一個 C-u
prefix 可以直接插入「檔案」連結。會這樣設計的原因很簡單,因為在 Org-mode 中我們最常需要插入的連結通常就是檔案連結。
在 Vim 中,我們常會先按數字鍵 N 再按指令,代表執行該指令 N 次。
Emacs 裡面也可以這樣,其實就是透過
C-u
prefix。當命令並沒有設計 prefix argument 的對應方式時,C-u
prefix 預設的意義則會變成「重複該命令 4 次」;C-u N
再呼叫指令,則是重複該指令 N 次不過我覺得這樣很難按,其實我都是按
Esc N
再按指令,跟C-u N
的效果完全相同。– ono hiroko
一開始你應該會覺得 Emacs 的 key-binding 很難記,怎麼各種 mode 都有不同按鍵。然而其實有很多常見功能是有慣例可尋的。以下舉出幾個範例:
按鍵 | 功能 | 範例 |
---|---|---|
q | 關閉 buffer | Dired, Package, IBuffer, Magit |
g | 畫面重新整理/更新 | Dired, Package, IBuffer, Magit |
^ | 回到上一層目錄 | Dired, Info, |
D | 刪除 | Dired, Package, IBuffer |
d | 標記為刪除(但尚未真的刪除) | Dired, Package, IBuffer |
x | 將標記為刪除的項目刪掉 | Dired, Package, IBuffer |
m | 標記項目 | Dired, IBuffer |
u | 取消標記項目 | Dired, IBuffer |
C-c C-c | 編譯/執行 | python-mode, lisp-mode, haskell-mode |
套用編輯/送出 | Magit, Message, twittering-mode | |
C-c C-z | 開一個 interpreter | python-mode, ruby-mode, lisp-mode, haskell-mode |
我個人覺得這根本難按死了!我自己是直接按方向鍵的。 原 Vimmer 可能就會覺得手指移動到鍵盤右下角很麻煩吧。試試
Evil
(在 Emacs 中使用 Vi 操作方式)也許你會喜歡。– ono hiroko
這部份太長太雞掰了,對於跟我一樣神經病喜歡用終端機板Emacs的人,請見 附錄B-終端機下的Emacs.org。