“实战Elisp”系列旨在讲述我使用Elisp定制Emacs的经验,抛砖引玉,还请广大Emacs同好不吝赐教——如果真的有广大Emacs用户的话,哈哈哈。
半年前我在这篇文章中展示了在Emacs中查阅笔记的效果——用emacs-request请求ElasticSearch查询关键字、基于helm以下拉菜单的方式展示查询结果的标题(即问题),最后打开浏览器查看笔记内容。稍加使用就发现一些不足之处:
- 需要离开Emacs打开浏览器。我更希望能在Emacs中查看;
- ES的响应内容为JSON,要快速地辨认出
question
和answer
字段比较困难;
- 一个问题的答案往往是多行的,但在浏览器中
answer
字段是以一行的形式展示的,不利于阅读。
为了解决这些问题,我实现了一种新的查看笔记内容的方式。
改良后的效果
实现方法
我的目标是:
- 在不脱离Emacs的情况下浏览笔记内容;
- 提供org-mode的语法高亮。
为此,先用被选中笔记的_id
请求ES,取回完整的JSON。接着,将_source
中的question
和answer
字段的内容拼接在一起(以\n
作为分隔符)。最后,在Emacs中新建一个buffer、启用org-mode、插入拼接后的内容,并设置该buffer为只读。大功告成!
完整的代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
| (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)))
(defun show-faq (text) (let ((buffer (get-buffer-create "*FAQ*"))) (let ((window (display-buffer buffer))) (select-window window) (setq inhibit-read-only t) (org-mode) (erase-buffer) (insert text) (read-only-mode))))
(setq faq-helm-sources `((name . "FAQ at Emacs") (candidates . faq-candidates) (action . (lambda (candidate) (let (response (url (format "http://localhost:9200/faq/_doc/%s" candidate))) (message "url is %s" url) (request url :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) ;; 从文档中提取出问题和答案,拼装成原本在.org文件中的模样 (let ((answer (cdr (assoc 'answer (assoc '_source response)))) (question (cdr (assoc 'question (assoc '_source response))))) (show-faq (concat question "\n" answer))))))))
(defun lt-ask () "交互式地从minibuffer中读取笔记的关键词并展示选项" (interactive) (let ((content (read-from-minibuffer "笔记关键词:"))) (setq faq-query content) (helm :sources '(faq-helm-sources))))
|
与之前版本的差异主要在于:
- 变量
faq-helm-sources
中的action
部分多了很多内容,主要是请求ES和拼接字段;
- 新增了
show-faq
函数用于显示问题及其答案。
后记
挺好奇各位读者朋友是怎么记笔记和看笔记的XD