给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工具