Skip to content

Latest commit

 

History

History
215 lines (158 loc) · 13.2 KB

04-關於key-binding的兩三事.org

File metadata and controls

215 lines (158 loc) · 13.2 KB

Key-binding(通常翻譯成「按鍵綁定」,或你可能比較熟悉的「快速鍵」) 是操作 Emacs 最重要的管道。使用快速鍵、自訂快速鍵前,尤其自訂遇到問題時,務必先詳讀此篇概念。

章節順序安排無法避免地有點矛盾:這一章請跟第五章的設定檔部份一起參照閱讀。我再想想要怎麼排會更好。

– ono hiroko

快速鍵綁定 Key-binding

只要是 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 等語言中有的一種資料型態,目前不要深究也沒關係,我們現在只是要弄設定檔。

Global key

假如我覺得 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"))

Local key

不管是 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-@ 就會發生這種情形)

設定按鍵的規則

設定按鍵時有一些注意事項:

小心 local key-binding 優先於 global key-binding

在同個 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 。所以當你納悶為何定義快速鍵沒用時,檢查一下是否重複設定了。

小心 Amibigious

來看範例,假如你在設定檔裡放入這兩行:

(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)

這樣寫就沒問題啦。

Prefix Key (C-x & C-c)

C-xC-c 是前綴(prefix)組合鍵,有特殊意義:

  1. 你無法單獨使用 Prefix key(例如你無法把某個功能綁到只按一個 C-x 就能達成,當你試圖這樣綁定時它會報錯)。
  2. 反過來說,「預設情況下」,你也無法用這兩個以外的按鍵當作 Prefix key ,例如把某個功能綁定到 C-s C-kC-d m
  3. 靠著 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。因為所有組合技的開頭一定是一個prefix key。(否則就會直接執行該按鍵的命令了)

假如我覺得只有 C-xC-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連續技:

pic/key-binding-decide.png

Prefix Argument (C-u, Universal Argument)

C-u prefix 在 Emacs 裡稱作 universal-argument ,又常稱為 prefix argument ,很多指令在呼叫前,先按一下 C-u ,會提供 與預設行為相關、但不完全相同的功能。

因此, C-u 也跟 C-xC-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

不成文的 key-binding 慣例

一開始你應該會覺得 Emacs 的 key-binding 很難記,怎麼各種 mode 都有不同按鍵。然而其實有很多常見功能是有慣例可尋的。以下舉出幾個範例:

按鍵功能範例
q關閉 bufferDired, 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開一個 interpreterpython-mode, ruby-mode, lisp-mode, haskell-mode

應該直接習慣 C-p / C-n / C-f / C-b 的游標移動方法嗎?

我個人覺得這根本難按死了!我自己是直接按方向鍵的。 原 Vimmer 可能就會覺得手指移動到鍵盤右下角很麻煩吧。試試 Evil (在 Emacs 中使用 Vi 操作方式)也許你會喜歡。

– ono hiroko

TTY / Terminal / Console 中使用 Emacs 發現按鍵一堆問題?

這部份太長太雞掰了,對於跟我一樣神經病喜歡用終端機板Emacs的人,請見 附錄B-終端機下的Emacs.org