乍听之下,不无道理;仔细揣摩,胡说八道

0%

改良在Emacs中浏览笔记的方式

“实战Elisp”系列旨在讲述我使用Elisp定制Emacs的经验,抛砖引玉,还请广大Emacs同好不吝赐教——如果真的有广大Emacs用户的话,哈哈哈。

半年前我在这篇文章中展示了在Emacs中查阅笔记的效果——用emacs-request请求ElasticSearch查询关键字、基于helm以下拉菜单的方式展示查询结果的标题(即问题),最后打开浏览器查看笔记内容。稍加使用就发现一些不足之处:

  1. 需要离开Emacs打开浏览器。我更希望能在Emacs中查看;
  2. ES的响应内容为JSON,要快速地辨认出questionanswer字段比较困难;
  3. 一个问题的答案往往是多行的,但在浏览器中answer字段是以一行的形式展示的,不利于阅读。

为了解决这些问题,我实现了一种新的查看笔记内容的方式。

改良后的效果

实现方法

我的目标是:

  1. 在不脱离Emacs的情况下浏览笔记内容;
  2. 提供org-mode的语法高亮。

为此,先用被选中笔记的_id请求ES,取回完整的JSON。接着,将_source中的questionanswer字段的内容拼接在一起(以\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
;;; 调用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))))

与之前版本的差异主要在于:

  1. 变量faq-helm-sources中的action部分多了很多内容,主要是请求ES和拼接字段;
  2. 新增了show-faq函数用于显示问题及其答案。

后记

挺好奇各位读者朋友是怎么记笔记和看笔记的XD

Liutos wechat
欢迎您扫一扫上面的微信公众号,订阅我的博客!
你的一点心意,我的十分动力。