本文主要講解了 Emacs 的使用技巧和高效方法,包括各方面的實用提示。
TRAMP
TRAMP(Transparent Remote Access, Multiple Protocols)是一個擴展名,顧名思義,它可以通過多種協議對遠程文件進行透明訪問。當提示輸入文件名時,輸入一個特定的表單就會調用 TRAMP。舉幾個例子:
以根權限打開 /etc/hosts
前提示輸入根密碼:
C-x C-f /sudo::/etc/hosts
以「你」的身份通過 SSH 連接到 「remotehost」,並打開文件 ~/example.txt
:
C-x C-f /ssh:you@remotehost:~/example.txt
TRAMP 的路徑形式通常為"/[協議]:[[用戶@]主機]:<文件>"。
以「你」的身份連接「myhost」,並使用 sudo 編輯 /etc/hosts
:
/ssh:you@remotehost|sudo:remotehost:/etc/hosts
TRAMP 支持的功能遠不止上面的例子。欲了解更多信息,請參閱隨 Emacs 一起發布的 TRAMP info 手冊。
將 Emacs 用作 git 合併工具
Git 默認支持使用 Emacs 的 Emerge 模式作為合併工具。不過,你可能更喜歡 Ediff 模式。遺憾的是,由於技術原因,git 並不支持這種模式。但還是有辦法在調用 emacs 時評估一些 elisp 代碼來使用它。
.gitconfig
[mergetool.ediff] cmd = emacs --eval \" (progn (defun ediff-write-merge-buffer () (let ((file ediff-merge-store-file)) (set-buffer ediff-buffer-C) (write-region (point-min) (point-max) file) (message \\\"Merge buffer saved in: %s\\\" file) (set-buffer-modified-p nil) (sit-for 1))) (setq ediff-quit-hook 'kill-emacs ediff-quit-merge-hook 'ediff-write-merge-buffer) (ediff-merge-files-with-ancestor \\\"$LOCAL\\\" \\\"$REMOTE\\\" \\\"$BASE\\\" nil \\\"$MERGED\\\"))\" [merge] tool = ediff
注意,命令必須在一行中。 在上例中,我們啟動了一個新的 Emacs 實例。你可能想使用 emacsclient 更快地啟動 Emacs,但不建議這樣做,因為 Ediff 調用並不乾淨:它可能會擾亂你當前的 Emacs 會話。
如果想立即啟動,可以使用 -q
參數。如果想快速啟動 Emacs,同時至少保留部分配置,可以使用:
emacs -q -l ~/.emacs-light
其中的輕量配置文件只加載 Ediff 所需的內容。
有關此技巧和 Ediff 問題的更多詳情,請參閱 kerneltrap.org 和 stackoverflow。
將大寫鎖定鍵用作 Control 鍵
有些用戶喜歡這種行為,以避免出現所謂的 「emacs 小指」。 在圖形用戶界面桌面(Xorg 或 Wayland)、終端甚至控制台中實現這一功能的好方法是使用 keyd包。安裝此軟體包並創建此配置文件:
/etc/keyd/main.conf
[ids] * [main] capslock = overload(control, noop)
然後啟用/啟動 keyd 服務。
這樣大寫鎖定鍵就會啟 Control 鍵之作用。
復用 emacs 和 emacsclient
在同一個 emacs-session
中打開新文件,需要使用 emacsclient
。如果會話存在,emacs
命令本身也可以進行封裝,以更智能地打開文件。
要啟動會話,需要使用 start-server
。該代碼段將在 emacs 的第一個會話中創建伺服器。將其添加到 emacs
配置文件中。
.emacs or .emacs.d/init.el
(require 'server) (unless (server-running-p) (server-start))
Shell 別名方法並不能滿足要求,因為你還需要傳遞變量或啟動自己的獨立會話。在 shell 的 .bashrc
或任何 rc 文件中添加此選項。這樣,如果參數被傳遞,emacs
命令就會像 emacsclient 一樣運行。
function emacs { if [[ $# -eq 0 ]]; then /usr/bin/emacs # "emacs" is function, will cause recursion return fi args=($*) for ((i=0; i <= ${#args}; i++)); do local a=${args[i]} # NOTE: -c for creating new frame if [[ ${a:0:1} == '-' && ${a} != '-c' && ${a} != '--' ]]; then /usr/bin/emacs ${args[*]} return fi done setsid emacsclient -n -a /usr/bin/emacs ${args[*]} }
如果要在新會話中運行,只需執行 emacs file -
。
多種配置
您可以使用多種配置,並告訴 Emacs 加載其中一種或另一種。
例如,讓我們定義兩種配置文件。
.emacs
(load "~/.emacs.d/main" nil t) (load "~/.emacs.d/functions" nil t) (load "~/.emacs.d/modes" nil t) (load "~/.emacs.d/plugins" nil t) (load "~/.emacs.d/theme" nil t)
這是我們針對守護進程加載的全部配置。但 plugins 文件很大,加載速度也很慢。如果我們想生成一個不需要 plugins 功能的新 Emacs 實例,那麼從長遠來看,每次加載都會很麻煩。
.emacs-light
(load "~/.emacs.d/main" nil t) (load "~/.emacs.d/functions" nil t) (load "~/.emacs.d/modes" nil t) (load "~/.emacs.d/theme" nil t)
現在我們用:
emacs -q -l ~/.emacs-light
您可以創建一個別名(如 emacs-light
),以便調用。
局部變量和自定義變量
您可以在配置文件中定義變量,以後可以在本地對文件進行修改。
(defcustom my-compiler "gcc" "Some documentation")
現在,在任何文件中都可以通過兩種方式定義局部變量,詳見手冊。
- 使用
M-x add-file-local-variable-prop-line
可以在開頭添加一行注釋,類似於:
// -*- my-compiler:g++; mode:c++ -*-
- 或者使用
M-x add-file-local-variable
在文件末尾添加行:
// Local Variables: // my-compiler: g++ // mode: c++ // End:
請注意,要使這些值生效,您需要調用 M-x revert-buffer
。
默認情況下,自定義變量被認為是不安全的。如果你試圖打開一個包含重新定義自定義變量的局部變量的文件,Emacs 會要求你確認。
您可以將變量聲明為安全變量,從而消除 Emacs 的確認提示。您需要指定一個謂詞,任何新值都必須經過該謂詞的驗證,這樣才能被認為是安全的。
(defcustom my-compiler "gcc" "Some documentation" :safe 'stringp)
在上例中,如果您試圖設置字符串以外的內容,Emacs 會認為這是不安全的。
自定義色彩和主題
可以使用 face 工具輕鬆定製顏色。
(set-face-background 'region "color-17") (set-face-foreground 'region "white") (set-face-bold-p 'font-lock-builtin-face t )
您可以讓 Emacs 告訴您點所在的 face 的名稱。為此,請使用 customize-face 功能。該功能會告訴你如何設置顏色、粗體、下劃線等。
控制台中的 Emacs 可以處理 256 種顏色,但必須使用合適的終端。例如,URxvt 支持 256 種顏色。你可以使用 list-colors-display 查看支持顏色的完整列表。這在很大程度上取決於終端。
參見:
- https://www.emacswiki.org/emacs/ColorThemes
- https://www.gnu.org/software/emacs/manual/html_node/emacs/Custom-Themes.html
SyncTeX 支持
Emacs 是一款功能強大的 LaTeX 編輯器。這主要是因為你可以調整或創建最適合自己需要的 LaTeX 模式。
不過,也可能會遇到一些挑戰,比如 SyncTeX 支持。首先,你需要確保你的 TeX 發行版支持 SyncTeX。如果你手動安裝了 TeX Live,可能需要安裝 synctex 軟體包。
# umask 022 && tlmgr install synctex
SyncTeX 支持與閱讀器有關。在此,我們將以 Zathura 為例,因此如果您想使用其他 PDF 閱讀器,則需要對代碼進行調整。
(defcustom tex-my-viewer "zathura --fork -s -x \"emacsclient --eval '(progn (switch-to-buffer (file-name-nondirectory \"'\"'\"%{input}\"'\"'\")) (goto-line %{line}))'\"" "PDF Viewer for TeX documents. You may want to fork the viewer so that it detects when the same document is launched twice, and persists when Emacs gets closed. Simple command: zathura --fork We can use emacsclient --eval '(progn (switch-to-buffer (file-name-nondirectory \"%{input}\")) (goto-line %{line}))' to reverse-search a pdf using SyncTeX. Note that the quotes and double-quotes matter and must be escaped appropriately." :safe 'stringp)
在這裡,我們定義了自定義變量。如果您使用的是 AucTeX 或 Emacs 默認的 LaTeX 模式,則必須相應地設置查看器。
現在用 Emacs 打開一個 LaTeX 源文件,編譯文檔,然後啟動查看器。Zathura 進程會自動創建。如果按下 Ctrl+Left click
鍵,Emacs 就會將點放在相應的位置。
systemd 文件的語法高亮顯示
您可以使用 systemd-mode。
或者,你也可以在 init 文件中添加以下內容,讓 emacs 為 systemd 文件(服務、定時器等)著色:
(add-to-list 'auto-mode-alist '("\\.service\\'" . conf-unix-mode)) (add-to-list 'auto-mode-alist '("\\.timer\\'" . conf-unix-mode)) (add-to-list 'auto-mode-alist '("\\.target\\'" . conf-unix-mode)) (add-to-list 'auto-mode-alist '("\\.mount\\'" . conf-unix-mode)) (add-to-list 'auto-mode-alist '("\\.automount\\'" . conf-unix-mode)) (add-to-list 'auto-mode-alist '("\\.slice\\'" . conf-unix-mode)) (add-to-list 'auto-mode-alist '("\\.socket\\'" . conf-unix-mode)) (add-to-list 'auto-mode-alist '("\\.path\\'" . conf-unix-mode)) (add-to-list 'auto-mode-alist '("\\.netdev\\'" . conf-unix-mode)) (add-to-list 'auto-mode-alist '("\\.network\\'" . conf-unix-mode)) (add-to-list 'auto-mode-alist '("\\.link\\'" . conf-unix-mode))
為 emacs-nox 提供剪貼板支持
要在 emacs-nox 中使用 Xorg 剪貼板,請安裝。xclip包 並在 ~/.emacs
中添加以下函數[1]:
;; use xclip to copy/paste in emacs-nox (unless window-system (when (getenv "DISPLAY") (defun xclip-cut-function (text &optional push) (with-temp-buffer (insert text) (call-process-region (point-min) (point-max) "xclip" nil 0 nil "-i" "-selection" "clipboard"))) (defun xclip-paste-function() (let ((xclip-output (shell-command-to-string "xclip -o -selection clipboard"))) (unless (string= (car kill-ring) xclip-output) xclip-output ))) (setq interprogram-cut-function 'xclip-cut-function) (setq interprogram-paste-function 'xclip-paste-function) ))