yasnippent的问题:你们是怎么处理变长参数和嵌套的?

举个例子(let):

(let ((${1:var1} ${2:exp1}))
  ${0:body})

当你打完第一个var-exp对之后,怎么跳到外层打第二个var-exp对?

一种解决方法是:

(let ((${1:var1} ${2:exp1})
      (${3:var2} ${4:exp2}))
  ${0:body})

这种方法只能打两个var-exp对,如果你刚好只有一个var-exp对,或者有3个及以上var-exp对则无能为力(成为多余代码)。

你们是怎么处理这种情况的?(注意:exp本身还可能嵌套)

PS:我知道可以使用paredit(我自己也在用),但感觉paredit还是容易导致思维打断,特别是在写算法的时候希望能一气呵成。

理论上, yasnippet 可以使用任何 LISP 代码来做需要的事,但是你这个需求, 我反正是不知道该怎么搞, 换做是我,会多写 let1 let2 let3 几个 snippet …

1 个赞

let1 let2 let3确实是个好办法。

不过除了let之外,还有比方说:

  1. 函数调用的实参其实也是不定长度的(Lisp毕竟动态语言)
  2. 在写的过程中可能会动态增加一些var-exp对,而不是在写let的时候就确定

snippet 状态很容易遭到干扰/破坏,一旦中断就无法继续,所以不适合做太复杂的事。

如果只有一个 TAB 键,如何区分跳到 $N 还是 $0?这是必须要解决的问题:

(let ((${1:var-val)
      (${2:var-val)
      (${3:var-val)
      ...
      (${N:var-val))
  (${0:body))

嗯,我也是问一下大家有什么好办法。

zw963的方法很不错,我已经写了几个let,目前感觉良好。

我写了一个 letn

# -*- coding: utf-8; mode: snippet -*-
# contributor: [email protected]
# name: let with N var-val forms
# key: letn
# type: command
# --
(let ((yas-good-grace nil)
      (forms '()))
  (while (let ((form (read-string (format "form%d (RET to submit): " (1+ (length forms))))))
           (unless (string-blank-p form)
             (push form forms))))
  (yas-expand-snippet 
   (concat "(let ("
           (s-join "\n" (reverse forms))
           ")\n$0)")))

在 minibuffer 输入不定个 var-val 对,每输入一对,以回车作为结束,开始输入下一对,最后回车提交空白,跳转光标到 body。

相关帖子 请教一个关于yasnippet模板制作的问题 - #11,来自 twlz0ne

试试匿名 snippet ,ultisnips 就是这么实现的。

不过你这个方法没有let1 let2 let3好用,因为在代码里不会出现let的部分形式。

但是还是谢谢你:我原来不知道yasnippent还能用elisp和交互。

我做了一点小小改进:

# -*- coding: utf-8; mode: snippet -*-
# contributor: [email protected]
# name: let with N var-val forms
# key: letn
# type: command
# --
(let ((yas-good-grace nil)
      (forms '())
      (beg (point))
      ov)
  (insert "(let ())")
  (setq ov (make-overlay beg (point) nil nil t))
  (backward-char 2)
  (while (let ((form (read-string
                      (format "Input FORM%d (RET to submit): "
                              (1+ (length forms))))))
           (unless (string-blank-p form)
             (insert form)
             (insert "\n")
             (indent-for-tab-command)
             (push form forms))))
  (yas-expand-snippet (concat "(let ("
                              (mapconcat 'identity (reverse forms) "\n")
                              ")\n$0)")
                      (overlay-start ov)
                      (overlay-end ov))
  (delete-overlay ov))

emacs-yasnippet-letn

没有普遍性吧

用keymap属性绑定不同的快捷键应该可行

我后来发现了一个新的方法,能够解决变长参数

比方说:我现在的let-1

# -*- mode: snippet -*-
# name: (let ([name e]+) e)
# key: let
# --
(let ((${1:var exp})$2)
${3:body})

注意里面的$2,在写好第1个var-exp对之后,可以TAB到$2的位置,这个地方你可以打回车。

然后再配合 company-yasnippet-autoparens 就可以很方便的输入第2个var-exp对,然后tab出来(因为第2个var-exp对现在也是snippet,而不是像原来那样自己打的括号,所以可以TAB出来)

确实。每个人都习惯自己的方式。

比如我用 snippet 是为了生成那些我记不住的代码/结构,比如在写 elisp 的时侯 doc<TAB> 帮我生成文档骨架,省得我去翻以前的文件复制粘贴。

而相 let 这样的基本结构,我通常直接手打,因为情急之下,根本不会去想是否有 snippet 可用。只有想不起来怎么写,才会去找 snippet

另一个我不太使用 snippet 的原因是,它的状态容易被破坏。只有当我编辑 field-n 无误(甚至一些快捷键也会打断展开过程)的情况下,才能顺利跳至 field-n+1

3 个赞

费这么多劲最后成果只是少打几个括号么……是这样的话,你需要改键软件/键盘固件来把左右shift改成“短点输出左右括号,长按/组合按还是shift”