emacs按鍵繫結詳解

IT超人發表於2020-11-25

從本期開始,我會試圖整合專欄之前零零散散的內容,產出更有概覽性和指導性的文章。要做到這一點,一方面需盡力保證結構清晰,文字精煉,文風統一,另一方面也會給出歷史文章分類目錄及超連結,方便大家查閱。以後的文章中,也將視情況給出有必要的配圖。

本人的配置檔案Github地址一併放在最後,暫時還沒寫什麼註釋,後面會跟進改善。

這一期先對鍵繫結的內容做一下總結。


 

概述:Emacs的鍵繫結方式看起來花樣繁多,其本質上都是同一個機制

(define-key keymap key def)

這裡的key是你要繫結的鍵。keymap是這個key所屬的集合,不同的keymap決定了這個key在什麼情況下觸發,什麼情況下隱藏,優先順序如何。def代表了這個key的定義,它可以是很多東西,常見的有

  • 一個interactive function,即一個command,這也是鍵繫結最直接的方式。
  • 一個key,僅對於keymap為'key-translation-map這種情況,意味著對映到另一個鍵。
  • 一個keymap,通過這種方式使用者可以自定義prefix key。
  • 一個nil,代表登出這個key。

keymap也有很多種,如(current-global-map)返回全域性keymap,(current-local-map)返回區域性keymap。對於這兩種情況,Emacs提供了更直接的函式:'global-set-key和'local-set-key。另外還有上面提到的key-translation-map,以及許多minor-mode下定義的keymap。

 

這四種不同種類的keymap,正代表了Emacs裡常見的四種不同的快捷鍵設定方式。雖然看起來較為複雜繁瑣,但如果配合巧妙,完全可以實現一鍵多用,讓使用者做到在不使用Ctrl鍵、不疊加修飾鍵,不連擊超過兩次組合鍵,甚至不開Evil的前提下,執行Emacs連同各種外掛包裡所支援的所有編輯命令。


 

一、key-translation-map,優先順序最高

(define-key key-translation-map (kbd "your-key") (kbd "target-key"))

你在任何時候按下"your-key",編輯器將執行"target-key"所繫結的命令。無論當時處於何種編輯狀態,是否有minor-mode觸發,該對映都不受影響,屬於優先順序最高的鍵繫結方式。

特點:由於對映鍵是完全的跳轉到了另一個鍵上,所以一旦目標鍵的定義發生了變化,該鍵也會隨之受到影響。

登出方式:把這個key重新對映為它自己。

適用情況:

  • 常用的、重要的,但按鍵較為複雜的命令,如"C-x C-s"固定的繫結的'save-buffer,"C-M-i"固定繫結的'completion-at-point,"C-M-%"固定繫結的'query-replace-regexp。你可以用更簡單的鍵來對映它們。
  • 常用的,重要的,但在不同mode下會發生變動的命令,如常規編輯中的"C-g"對應的是'keyboard-quit,在minibuffer下會變成'minibuffer-keyboard-quit。如果你將某個鍵繫結為'keyboard-quit,你會發現它在minibuffer下就沒法用了。對於這種情況,最好的辦法就是直接對映。


 

二、minor-mode-map,優先順序第二

(define-key some-minor-mode-map (kbd "your-key") 'your-command)

一般而言,大部分Emacs使用者沒有自定義minor-mode的需求,往往也不知道怎麼定義,但minor-mode-map對所有使用者而言並不陌生,最常見的便是在isearch-mode下的'isearch-repeat-forward和'isearch-repeat-backward。

當你按"C-s"觸發isearch-mode並搜尋某段文字的時候,重複按"C-s"將跳轉到下一個匹配,看起來是順理成章的事其實對應的是兩個不同的函式。之所以這裡能一鍵多用,是因為在'isearch-mode觸發同時也啟用了它的minor-mode-map,也就是'isearch-mode-map,它裡邊將"C-s"繫結到了'isearch-repeat-forward上,在啟用狀態下便覆蓋掉了它的初始定義'isearch-forward。

特點:僅在minor-mode啟用時有效,定義方便且優先順序高,不用擔心鍵衝突。

登出方式:繫結為nil。

適用情況:

  • 各種系統自帶minor-mode如'isearch-mode,'query-replace,'edmacro-mode
  • 各種常見外掛如'cua--rect,'with-editor
  • 自定義minor-mode


 

三、local-set-key,優先順序第三

(local-set-key (kbd "your-key") 'your-command)

local-set-key主要是在各種major-mode下使用,一般是通過hook設定

(add-hook 'some-major-mode-hook '(lambda () (local-set-key ...)))

特點:通過這種方式設定的鍵繫結僅在該major-mode下生效,不影響其他major-mode,實惠好用。

登出方式:繫結為nil,或者

(local-unset-key (kbd "your-key")

適用情況:

  • 在不同major-mode下呼叫類似的命令,如執行當前檔案,當前選區,當前行等等,在不同的語言做的是類似的事情,所以可通過這種方式把不同的命令繫結到同一個鍵上。
  • 通過hook來local-set-key本質上修改的只是一個變數(如果該變數不存在自動建立),不需要提前載入對應的major-mode,相比直接定義該mode下的各種keymap更加安全。


 

四、global-set-key,優先順序最低

(global-set-key (kbd "your-key") 'your-command)

特點:最簡單的鍵繫結方式,一行搞定,無須關心到底是哪個keymap。然而需小心在某些major-mode時會被覆蓋。

登出方式:繫結為nil,或者

(global-unset-key (kbd "your-key")

適用情況:

  • 一些不常用的命令
  • 一些冷門的按鍵


 

需要注意的是

對於上述不同優先順序的鍵繫結方式,其對應的逆操作(即登出鍵)也遵循同樣的優先順序,例如登出了minor-mode的快捷鍵,它便會恢復為當前major-mode下的定義,如果當前major-mode下沒有定義,那便執行全域性預設的命令。


 

鍵衝突問題

儘管已有如此多的鍵繫結方式,由於Emacs預設的鍵佈局已十分緊張,使用者往往容易碰到各種各樣的鍵衝突問題。

比如你想設定一個快捷鍵,使它在任何情況下都繫結為一個固定的命令。如果你用global-set-key,那它有可能會被其他major-mode覆蓋;如果你用key-translation-map,它所對映的那個鍵同樣可能被覆蓋;如果你用local-set-key,那你不得不在所有出問題的major-mode裡挨個設定。你該怎麼辦?——可以暫時參考本專欄之前的一篇文章:快捷鍵(一)

又比如你寫了一個minor-mode,你想定義一個快捷鍵讓它執行"C-g"的功能,正常編輯時為'keyboard-quit,在minibuffer下則為'minibuffer-keyboard-quit。但遺憾的是minor-mode裡是不支援鍵的對映的,你又該怎麼辦?——我會在新的文章中分享解決方案。


 

相關文章

快捷鍵篇:

Emacs常見鍵繫結方式彙總

快捷鍵(一)(待整合)

 

快捷鍵(二) (待整合)


 

個人配置檔案

https://github.com/wolray/emacs.d

相關文章