在yasnippet中,`(some-code)`
可以用来执行elisp。
于是我在web-mode里加了一个snippet:
`(save-excursion (move-beginning-of-line 1) (if (looking-at " *$") "console.log($1);" "class=\"$1\""))`
意图是:expand “cl” 这个snippet时,某一行是0或多个空格时,猜测是js代码,变成console.log()
,否则猜测是html,变成class=""
。
现在的问题是:elisp的字符串里的$1
被literally地输出了,并没有被yas解释为通常的$1
(一个可输入位置的占位符)
可以在 expand-env
中完成复杂的逻辑:
# -*- coding: utf-8; mode: snippet -*-
# key: foo
# name: foo snippet
# expand-env: ((bar (do-something...)))
# --
`bar`
相当于:
(let ((bar (do-something...)))
bar)
哦对了,要不是这里输出$1
之前的部分会破坏(looking-at ...)
,本来是可以抄文档里这种的(yas-prefix是prefix argument):
<p>`(when yas-prefix "\n")`$0`(when yas-prefix "\n")`</p>
把判断重复一下就行
我改成了:
# -*- mode: snippet -*-
# name: cl
# key: cl
# expand-env: ((jjpandari-web-mode-in-js-p (save-excursion (move-beginning-of-line 1) (looking-at " *$"))))
# --
`(if jjpandari-web-mode-in-js-p "console.log(" "class=\"")`$1`(if jjpandari-web-mode-in-js-p ");" "\"")`
现在总是输出class=""
,也就是env总是nil,感觉yas eval env时是不是有什么坑。。
把 env 的代码放到 M-:
执行,看看是不是 nil。
当你在空行执行 (looking-at " *$")
的时候得到 t 没错,但是输入 cl 之后就应该是 nil 了,所以展开 snippet 的时候当然也是 nil。
这样才对吧:
(save-excursion
(move-beginning-of-line 1)
(looking-at "^[ \t]*cl[ \t]*$"))
1 个赞
正则是对了,但是用起来就是只出class
,奇怪。。。
我觉得可以转换下思路:不要把整个处理逻辑都包到尖括号 `` 中,而应该把它们包装成函数。然后在编写 snippet 的时候调用,与 $1
占位符组合。伪代码就像这样:
function newFunc()
do something
end function
-- snippet 调用函数
`newFunc()` $1
相比全部放到尖括号,包装成函数调用更加好看、易懂,而且允许更复杂的处理逻辑。
我个人并不懂 elisp,所以这里只是个建议。你可以朝这个方向找找看,看 yasnippet 是否支持读取 snippet 内自定义的函数。
我觉得你的困惑,主要原因是在 同一函数域 内,elisp 恐怕无法理解 $1
是什么鬼东西罢?换句话说,直接执行 message("$1")
这种脚本,任何语言恐怕都是原样照出 $1
。因为它们不在同一域内。
我试了一下,snippet 展开的时侯,会把 cl 给吃掉,所以你原先的正则是对的(虽然没有考虑 \t
)
Update
# -*- mode: snippet -*-
# name: cl (command)
# key: cl
# type: command
# --
(let ((in-js-p
(save-excursion
(move-beginning-of-line 1)
(looking-at "^[ \t]*[ \t]*$"))))
(yas-expand-snippet
(if in-js-p
(concat "console.log(\"$1\");")
(concat "class=\"$1\""))))
expand-env 写法有问题,我改成 command。
1 个赞
上面给出的expand-env
就是“每次展开snippet时eval一些代码把结果存在变量里”呀,和你说的用函数差不多的。
"$1"
这个,我想了下,没有宏的语言,多半要eval了` `
里面的内容然后和外面的字符串拼起来,再处理一下,替换$1
之类的,这样我(if foo "bar$1" "baz$1")
还是有可能得逞的。但是elisp写的yas多半是整个snippet喂给一个宏,扩展出来的代码再eval这样,于是等我输出bar$1
的时候宏扩展已经结束了,当然就直接输出$1
了。yas的源码里肯定能找到答案,我就胡吹一下,如果有道友有兴趣有精力看源码欢迎指正
然而楼上给的答案里yas-expand-snippet
又是个函数(参数为string)而非宏,不懂。。
啊,看到你楼上调用的 jjpandari-web-mode-in-js-p
了。
之前看过 yasnippet 文档,印象中感觉 expand-env
就只能传递变量?类似于 export envpath="foo"
这样的,毕竟名字包含 env
嘛。难不成真能调用自定义函数?
你可以检查一下,jjpandari-web-mode-in-js-p
返回值是不是固定的。
话说回来,也可以创建两个触发词都为 cl
的 snippet 嘛。顶多输入时多了个弹出框选择的步骤,显得没那么智能罢了。。。不值得在 yasnippet 这么老的插件上花费太多时间,个人感觉。
老不老不知道,还是很强大的,楼上已经解决啦。可不能说“也就多一步/一秒嘛”,每当我向别人展示emacs的编辑超能力的时候,他们也经常这么说。。我可是把别人移动光标的时间都省下来,接了更多的任务,还没找老板要奖金呢
刚才看了下 yasnippet 的 github 源,发现还在正常更新。貌似挺正常的,这么说 yasnippet 并不老……
可能是之前被 yasnippet 坑过吧,再加上有 ultisnips 的使用经历,导致我对 yasnippet 没啥好感,有点老旧不更新的错觉。我记得之前翻过 yasnippet 的自带 snippet,并没有发现令人眼前一亮的。可能学会 elisp 后功能会更强大一些吧,然而我并没有这个动力……
并没有发现 emacs 的 编辑超能力,至少在我蹲坑论坛的这段时间,发现绝大部分人遇到的问题都可以用一个方案解决:换用 Vim 编辑器。包括你这个问题。๑乛ェ乛๑ 没错,我偏向 Vim 党多一点。
两者也面临着同样的问题,可能别人不觉得你有超能力,是因为人家的关注点在别处。比如自动补全、debug、重构等等方面。任何事情都有优点缺点。
1 个赞