Helm 是一個emacs的軟件包,定義了一個通用框架,交互式地、動態縮減式地使用關鍵字選擇、獲取、執行任何東西。比如:
- 執行emacs 命令
- 打開文件
- 查看man文檔
- 執行grep操作
- 執行apt命令
- 相看imenu函數定義
- 切換buffer
Helm軟件包本身包含兩部分,框架本身及應用。以上列表均為應用。基於框架,可以輕松創建新的應用。
基本原理
Helm的三個重要概念:candidate, narrowing, action.
Candidate
Candidate即候選值,是一個列表,保存所有可供選擇的條目。對於打開文件的命令,candidate是所有的文件名稱的列表。
Narrowing
Helm命令啟動后,用戶未輸入任何關鍵字前,會將candidate中的所有條目顯示出來,每行顯示一個項,可通過'C-n', 'C-p'上下移動光標選擇當前條目。
如果candidate的數目較少,此時沒必要輸入關鍵字,通過上下移動光標選擇就行了;但如果candidate數目較多,目標條目沒有被顯示在第一頁,可輸入關鍵字,對candidate的條目進行篩選,只有匹配到關鍵字的條目才會被顯示出來。這就是narrowing。
值得一提的是,這個過程是動態的,即每輸入一個字符,candidate的條目都會被重新篩選。有時只輸入了一個字符,目標條目已經顯示在第一頁,則可停止輸入,通過移動光標選擇當前條目;有時輸入了一個關鍵字,目標條目仍然沒有出現,則可按空格,繼續輸入另一個關鍵字,進行更精確的篩選,直到目標條目出現為止。
輸入的關鍵字越多,candidate的數目會越少,目標條目出現在第一頁第一個條目位置的機率就越大,進而選擇也就越方便。
Action
當一個candidate被選中后,按下Enter后,就會有一個action被執行。對於打開文件,其action對應到emacs命令就是'find-file'。可以為一個條目定義多個action,如對於文件條目action可以為打開文件、重命令文件、刪除文件等。 通過TAB鍵從多個action中選擇,如果直接按Enter會執行第一個action.
定義一個新的helm應用
基本例子
要定義一個新的helm命令,只需定義一個變量指定candidate及對應的action,然后將其作為參數傳給helm就可以了,以下為一個例子。
(setq some-helm-source '((name . "HELM at the Emacs") (candidates . (1 2 3 4)) (action . (lambda (candidate) (message-box "%s" candidate))))) (helm :sources '(some-helm-source))
其中定義candidate、action的變量叫做source, 是一個assoc list。candidates是一個list, action是一個函數,action函數被調用時,當前選擇的candidate會被作為參數傳入。
定義多個action
上面的例子中action的值為一個匿名函數,如果要定義多個action,則需要將action的值設置為一個list,list的元素是一個cons:(說明 . 函數)。如下所示,定義了兩個action。
(setq some-helm-source '((name . "HELM at the Emacs") (candidates . (1 2 3 4)) (action . (("Display" . (lambda (candidate) (message-box "%s" candidate))) ("None" . identify) )) )) (helm :sources '(some-helm-source))
將candidate的選擇值與真實值分離
有時候需要通過不同的值選擇candidate,此時可將candidates設置為cons (KEY . VALUE)的list。其中KEY將用於選擇,VALUE將作為action函數的輸入參數值。
(setq some-helm-source '((name . "HELM at the Emacs") (candidates . (("one" . 1) 2 ("three" . 3) 4)) (action . (("Display" . (lambda (candidate) (message-box "%s" candidate))) ("None" . identify) )) )) (helm :sources '(some-helm-source))
動態candidate
有時candidates需要動態計算,或者靜態計算量會很大,此時可將candidates設置為一個函數,這個函數將被用於計算所有candidates的值。
(defun random-candidates () "Return a list of 4 random numbers from 0 to 10" (loop for i below 4 collect (random 10))) (setq some-helm-source '((name . "HELM at the Emacs") (candidates . random-candidates) (action . (lambda (candidate) (message "%s" candidate))))) (helm :sources '(some-helm-source))
添加一個persistent action
Persistent action是指執行action后,不退出helm,類似於預覽功能,默認綁定在\C-z。通過'persistent-action'來指定,如果未指定,則與第一個action一樣。
(setq some-helm-source '((name . "HELM at the Emacs") (candidates . (1 2 3 4)) (persistent-action . (lambda (candidate) (message "%s" candidate))) (action . (lambda (candidate) (message-box "%s" candidate))))) (helm :sources '(some-helm-source))
helm-org-headlines的定義
這個命令由helm默認提供,其定義如下,可為實現新的命令提供參考。
(setq helm-source-org-headline `((name . "Org Headline") (headline ,@(mapcar (lambda (num) (format "^\\*\\{%d\\} \\(.+?\\)\\([ \t]*:[a-zA-Z0-9_@:]+:\\)?[ \t]*$" num)) (number-sequence 1 8))) (condition . (eq major-mode 'org-mode)) (migemo) (subexp . 1) (persistent-action . (lambda (elm) (helm-action-line-goto elm) (org-cycle))) (action-transformer . (lambda (actions candidate) '(("Go to line" . helm-action-line-goto) ("Refile to this headline" . helm-org-headline-refile) ("Insert link to this headline" . helm-org-headline-insert-link-to-headline)))))) (defun helm-org-headlines () "Preconfigured helm to show org headlines." (interactive) (helm-other-buffer 'helm-source-org-headline "*org headlines*"))