給Emacs寫插件有種痛並快樂着的感受。雖然這個發揮創意的過程頗有趣,可是Elisp寫起來總有種彆扭的感受。一方面,我把它當成是Common Lisp,寫的時候沒有以爲「這個用法可能會有問題」;另外一方面,它又不是普通的寫lisp代碼,還要一邊寫一邊摸索Emacs中的一些概念。不過整體而言,仍是挺好玩的,除了沒有一個像模像樣的REPL以外。git
我用Emacs記錄了很多的「筆記」。雖然說我本身將其稱爲筆記,可是它們更像是我把遇到的一些問題和解決方法給記錄下來,而沒有太多本身的感悟。它們的外觀卻是高度的一致,見下圖github
(第一次嘗試給本身的圖片打水印,有點好玩)每個一級條目都是一個問題,而且這個文件中只有一級條目。而條目下的內容則是對標題的問題的回答。其中還有代碼塊——也就是寫着BEGIN_SRC和END_SRC的那部分。用org-mode來記錄筆記有幾個好處,其中一個即是能夠在筆記中插入任何Emacs支持的編程語言代碼片斷並具有語法高亮。固然了,還有一個巨大的優點,即是org-mode儘管看似花裏胡哨,骨子裏倒是正統的純文本文件,它能夠很方便地在其它工具中處理。編程
而我用來處理的其中一個工具即是ElasticSearch。好比說,上圖的第一條筆記,在ElasticSearch中存成了下面這樣的結構json
原本我是寫了一個Alfred的Workflow來查詢ElasticSearch的,可是奈何Workflow那種一行行的方式展現org-mode格式的筆記不太友好,所以便打算直接在Emacs中查詢並查看筆記內容。瀏覽器
爲了能夠在Emacs中查看筆記內容,我打算藉助於Helm的力量。Helm是Emacs的一個補全的框架,能夠用來呈現一系列的候選項,而後選中後觸發一些什麼動做。我指望的形式,是在Emacs中按下某種快捷鍵或者輸入某個命令行,能夠在minibuffer中輸入本身要查詢的內容,而後Emacs查詢ElasticSearch並最終經過Helm來呈現這些查詢內容匹配的筆記條目。目前的成果是下面這樣子的app
具體的作法其實也很簡單。首先,要知道Helm是如何被使用的。經過這篇文檔,初步瞭解到只須要定一個變量,並經過:sources
關鍵字參數傳遞給helm
這個函數便可。我所定義的傳遞給helm
函數的「source」以下框架
(setq faq-helm-sources `((name . "FAQ at Emacs") (candidates . faq-candidates) (action . (lambda (candidate) (let ((url (format "http://localhost:9200/faq/_doc/%s" candidate))) (browse-url url))))))
其中faq-candidates
的做用即是根據minibuffer中的關鍵字查詢ElasticSearch並組織好一個結構返回給helm
。須要注意的是,faq-candidates
必須是一個無參的函數才行,但輸入的數據又恰恰須要從minibuffer中獲取。所以,個人作法是約定一個變量faq-query
,在調用helm
以前首先調用read-from-minibuffer
函數讀取輸入,而後將輸入的字符串賦值給faq-query
,以後當helm
開始使用這個source的時候,faq-candidates
函數便不須要參數,而能夠直接從faq-query
中拿到本身須要的搜索內容向ElasticSearch請求了。固然了,若是有像Common Lisp動態做用域的話,也就不須要定義這麼一個全局變量了,對Emacs全局的侵入會更少一點。編程語言
目前可以作到的也僅僅是查詢ElasticSearch,並在選中某個條目並按下回車的時候打開瀏覽器來查看而已,以後應該會繼續完善。目前的完整代碼以下函數
;;; 調用ElasticSearch查詢筆記 (require 'request) (defun faq (query) "向ElasticSearch查詢QUERY匹配的筆記" (let ((response)) (request "http://localhost:9200/faq/_search" :data (encode-coding-string (json-encode (list (cons "query" (list (cons "multi_match" (list (cons "fields" (list "answer" "question")) (cons "query" query))))))) 'utf-8) :headers '(("Content-Type" . "application/json")) :parser 'buffer-string :success (cl-function (lambda (&key data &allow-other-keys) (setq data (decode-coding-string data 'utf-8)) (setq response (json-read-from-string data)))) :sync t) response)) (defun make-faq-candidates (response) "將查詢ElasticSearch的結果構造爲helm能夠識別的candidates格式" (let ((hits (cdr (assoc 'hits (cdr (assoc 'hits response)))))) (mapcar (lambda (doc) (let ((_source (cdr (assoc '_source doc)))) (cons (cdr (assoc 'question _source)) (cdr (assoc '_id doc))))) hits))) (defvar faq-query nil) (defun faq-candidates () (make-faq-candidates (faq faq-query))) (setq faq-helm-sources `((name . "FAQ at Emacs") (candidates . faq-candidates) (action . (lambda (candidate) (let ((url (format "http://localhost:9200/faq/_doc/%s" candidate))) (browse-url url)))))) (defun lt-ask () "交互式地從minibuffer中讀取筆記的關鍵詞並展現選項" (interactive) (let ((content (read-from-minibuffer "筆記關鍵詞:"))) (setq faq-query content) (helm :sources '(faq-helm-sources))))
有很多值得吐槽的地方,不過都先按下不表吧,各位讀者有興趣的話能夠留言交流一下XD工具