该插件灵感源自于之前自己提的一个问题:怎么用yasnippent实现优雅的带括号的过程调用?
简单描述就是:
当你输入的语法形式是固定的if
cond
cons
car
cdr
等时,可以自动展开snippet,并将光标移动到括号内。但是当输入的语法形式不是以上固定形式,比如:需要做一个过程调用(foo)
,此时你必须先输入()
,然后把光标移动到括号里面(|)
再输入方法的名字,这非常不方便。
company-yasnippet-autoparens 就是解决这个问题,它能让company自动提示带括号的过程调用。
例子1:输入任意符号,都会在完成列表里附带一个对应的过程调用的候选项
例子2:原本的补完项,现在也会附带一个对应的过程调用的候选项
例子3:综合了let snippet 和 过程调用
注意:在上面第3个例子里,我为let 增加了var-exp对,但没有破坏snippet状态。这使得我们在写Lisp算法的时候可以一气呵成,不需要思考在什么地方加括号(这一点在以前是做不到的)。
安装方法:
-
安装 yasnippet
-
如果你已经安装了company,卸载它:
卸载company最简单的方法是删除company目录(比如:在windows下 C:\Users%YOU-USERNAME%\AppData\Roaming.emacs.d\company-XXXXXXXX.XXXX),并且确保你的Emacs配置不会再次自动安装comapny。
-
拷贝company-yasnippet-autoparens.el 到你的任意脚本能加载到的路径(比如:在windows下 C:\Users\你的用户名\AppData\Roaming.emacs.d\)
-
在你的emacs配置文件(如:init.el)里加入:
(require 'company)
(global-company-mode 1)
(defvar company-mode/enable-yas t
"Enable yasnippet for all backends.")
;; Add yasnippet support for all company backends
(defun company-mode/backend-with-yas (backend)
(if (or (not company-mode/enable-yas) (and (listp backend) (member 'company-yasnippet backend)))
backend
(append (if (consp backend) backend (list backend))
'(:with company-yasnippet))))
(setq company-backends (mapcar #'company-mode/backend-with-yas company-backends))
;; Add company-yasnippet-autoparens
(defun company-mode/backend-with-yas-ap (backend)
(if (or (not company-mode/enable-yas) (and (listp backend) (member 'company-yasnippet-autoparens backend)))
backend
(append (if (consp backend) backend (list backend))
'(:with company-yasnippet-autoparens))))
(setq company-backends (mapcar #'company-mode/backend-with-yas-ap company-backends))
注:以上使用Elisp只是作为一个例子。事实上company-yasnippet-autoparens 支持任何s-exp语言。因为它并不关注语言本身,而只是给完成列表的每一个候选项增加括号。
TODO:
增加一个Hook机制:
最后说一下 company-yasnippet-autoparens 的不足:
回忆之前第3个例子,我有说过company-yasnippet-autoparens能处理像let那样的变长子表达式(var-exp 对),这一点是没错的,比方说,我现在的let snippet是这样的:
# -*- mode: snippet -*-
# name: (let ([name e]+) e)
# key: let
# --
(let ((${1:var exp})$2)
${3:body})
请注意里面的$2,在写好第1个var-exp对之后,可以TAB到$2的位置,在这个地方你可以按回车。
然后输入第2个var-exp对的var部分,company-yasnippet-autoparens会自动展开成带括号的形式(var),当你输入完var-exp对的exp部分后,可以再按一次TAB,跳出这个var-exp对,到达(var exp) |
这个位置,此时你可以再按一次回车,于是就能输入第3个var-exp对,这个过程非常流畅。
然而,在处理cond表达式的时候会有一点不同(虽然cond和let的变长子表达式的形式很像):
这里我为cond 增加了2个条件分支。
请注意我在写完((= x 0) (test-function-1 123)|)
后的光标位置,这个时候如果你按TAB的话,会直接跳到otherwise,这样你就无法写第4个分支了。所以此时你需要手动按一下方向键->
让当前光标移到((= x 0) (test-function-1 123))|
然后再按回车才能输入第4个条件分支。
这个问题的根本原因是 let 和 cond 的语法形式有细微不同:
let的var-exp对里的var必定是一个primitive-exp(一个名字),而cond的pred-conseq对里的pred可以是一个子表达式。因此对于var-exp对来说,其形式上类似于一个单参过程调用(var xxx)
,但对于pred-conseq对,你必须先手动输入一个括号(pred conseq)
(由于这个括号是你手动输入的,因此必须手动跳出,而不能仅仅依靠TAB)