“实战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为只读。大功告成!
完整的代码如下
;;; 调用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 'answer (assoc '_source doc)))
(cdr (assoc '_id doc)))))
hits)))
(defvar faq-query nil)
(defun faq-candidates ()
(make-faq-candidates (faq faq-query)))
;;; 创建新的buffer并将ElasticSearch的内容展示在其中
(defun show-faq (text)
;; 创建一个buffer,显示它并选中这个窗口
(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
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。