Language

Saturday, February 2, 2013

如何在 GNU Emacs 減少按鍵次數並提升工作效率


GNU Emacs 是一個非常 powerful 的編輯器, 這個就不需要我多作介紹, 但是與 vim 相比, GNU Emacs 有一個比較大的缺點就是按鍵多。只要是採用 modeless 的方式編輯, 又要功能多, 按鍵數很難減少, 但是還是有一些方法, 可以讓我們整體的按鍵數量變少的, 可以減少手的傷害。至於那些買了青軸機械式鍵盤之後, 想辦法多打字讓別人聽按鍵聲音的, 就不在本文的討論範圍內。

GNU Emacs 有兩種方式減少打字數目, 筆者認為值得推薦, 一種是用巨集(macro), 另一種是用 skeleton 套件。當然還有第三種是編寫 Emacs Lisp, 寫點函數, 這個難度比較高, 不太適合初學者。今天筆者介紹的這兩個方式都很適合初學者使用。

Table of Contents


巨集

首先, 我們來看看巨集。要知道 EMACS 這個名字是由 Editing MACroS 來的, 巨集可是 Emacs 的核心價值啊!只要你發現你在做重複性的工作, 就可以把那些一連串的按鍵動作變成巨集。
定義巨集很簡單, 在你的一連串按鍵動作開始之前, 先輸入 C-x (, 然後就開始按照平常的動作按按鍵, 按完所有按鍵之後, 鍵入 C-x ) 就完成巨集的定義了。
要執行剛剛定義完的巨集, 就按 C-x e, 也可以配合 Emacs 的數字前置指令來執行多次的巨集。比如說你想要執行這個巨集四次, 就鍵入 M-4 C-x e 就會執行這個巨集四次。
The quick brown fox jumps over the lazy dog

比方說, 你想要把上面這個檔案改成一個英文單字一行, 可以用巨集來做, 方法如下:
按鍵動作
C-x (開始錄製巨集
M-f把游標往前移一個單字
C-d刪除一個字元
[ENTER]插入換行字元
C-x )結束巨集的錄製

上述的巨集可以讓一個單字單獨存在於一行, 把剩下的文字換到下一行去, 如果要把所有英文單字都單獨存在於一行, 就需要重複作這個巨集。可以先用 M-x count-words-region 來計算這一行的單字數, 九個。所以要處理完所有的單字, 就輸入按鍵 M-9 C-x e, 不過通常我們錄製巨集的時候已經先做完一遍了, 那這時候你只需要執行 8 次就好。執行完結果就會如下所示:
The
quick
brown
fox
jumps
over
the
lazy
dog



Skeleton




如果你的重複性工作, 是經常性的使用到, 比方說寫函數時的註解, 常常需要寫函數名稱、作者、建立的時間等等資訊, 有些是每次都寫一樣的資訊(如作者), 有些則可以由函數執行結果得來(如日期), 有些需要打兩次一樣的字串(如函數名稱), 那麼 skeleton 就是一個不錯的工具, 讓你減少打字數目。


使用 skeleton 的方法如下:

(define-skeleton your-skeleton-name
  "String to describe this skeleton."
  "String that will be inserted when you call your-skeleton")



然後你就可以用 Emacs Lisp 函數一樣的呼叫方式來呼叫:
M-x your-skeleton-name 這函數就會自動從游標位置插入預先定義好的字串。
高階一點的用法就會牽扯到

  • 使用者輸入字串

    • 使用 (skeleton-read)

  • 重複使用者輸入的字串

    • 使用 v1, v2 變數

  • 游標最後所處位置

    • 使用 "_" 來標示

  • 呼叫 Emacs Lisp 函數來插入字串

    • 直接呼叫


筆者就用我自己用的一個例子來說明 skeleton:
; Skeleton for assembly subroutine header
(define-skeleton insert-asm-header
  "Prompt for subroutine name and insert assembly subroutine header"
  "" ?\n
  ";******************************************************************************" ?\n
  "; Name:\t\t" (setq v1 (skeleton-read "Subroutine name: "))?\n
  "; Purpose:" ?\n
  ";   " (skeleton-read "Purpose: ") ?\n
  "; Method:\t" ?\n
  ";   " ?\n
  "; Parameters:\t\t" (skeleton-read "Parameters: ") ?\n
  "; Affected regs:\t" ?\n
  "; Author:\tAlbert Chun-Chieh Huang, " (insert-current-date) ?\n
  ";******************************************************************************" ?\n
  v1 \n
  "" _ \n
  "rts" \n
  )



定義完之後, 我們就可以用 M-x insert-asm-header 來呼叫這個 skeleton, 呼叫之後, 在 mode line 會出現
Subroutine name: 



要求使用者輸入副程式名稱, 除了馬上把這名稱字串放到 "Name:" 之後, 並且存在 v1 這個變數裡。 接下來會要求使用者輸入 Purpose: 說明這個副程式的用途, 並把副程式用途就插入 (skeleton-read "Purpose: ") 的位置上。接下來的 Parameters: 也一樣。到了 Author 的地方就呼叫 Emacs Lisp 函數 (insert-current-date) 把日期填上。
在倒數第四行的 v1 就是把剛剛存在 v1 這個變數的副程式名稱取代 v1 填上去。
倒數第三行的 "_" 就是指示 skeleton 插入完這些字串之後, 游標要回到這個位置來。
所有被雙引號包起來的都是字串, 不會有特殊意義。在雙引號以外的, 就是 Emacs Lisp 函數、變數、或是特殊字元。=\n= 是換行並且依照所處的 mode 縮排, 而 ?\n 則只有單純的換行字元而已。


下面的例子就是執行完後的結果, 游標會停在倒數第二行, 與 rts 同樣的縮排位置上, 方便我打第一個 instruction.
;******************************************************************************
; Name:         Test
; Purpose:
;   Test
; Method:       
;   
; Parameters:           a1
; Affected regs:        
; Author:       Albert Chun-Chieh Huang, 02 February 2013
;******************************************************************************
Test
        
        rts


如果對於 skeleton 想要有更深入的了解, 可以按 C-h f skeleton-insert, 裡面有關於 define-skeleton 的字串該怎麼寫的詳盡說明。






結語




要提升工作效率, 第一要件就是先找出重複的工作, 並且用快速有效率的方法執行這個工作。當你找出重複性的部份之後, 可以把重複不變的部份抽出來變成巨集, 如果有經常性的重複編輯工作, 就可以考慮用 skeleton 來定義, 如果有每一次呼叫變動的部份, 就考慮把這些變動用呼叫函數或是輸入參數的方式來引入, 如此就可以有效的減少按鍵次數, 並且提升工作效率。



--
Albert Huang
Chun-Chieh Huang(黃俊傑)

No comments: