使用Yasnippet创建盒状注释

7

我希望编写一个yasnippet模板,可以在Emacs的脚本缓冲区中添加许可证头。有点像this,但稍微改进了一下:

  1. 头部需要包含每个用户的数据,例如版权所有者的日期名称和电子邮件,这些可以通过从yasnippet获取embedded elisp expansion来获得。
  2. 头部需要根据文件当前所在的编程模式进行语法注释。已经有一个a gist of a snippet that does all that。基本上它相当于将(comment-region (point-min) (point))嵌入到你的代码片段的末尾。
  3. 现在,我想将注释样式更改为框形。请参阅emacs文档中的comment-style变量,或者如果您想查看框形注释的样子,只需在活动区域上调用M-x comment-box:它会使用正确的选项调用comment-region

第一种方法是通过修改先前代码片段的结尾来设置样式:

(let ((comment-style 'box))
            (comment-region (point-min) (point)))

很不幸,缩进出了问题,我的框不是矩形的。如果我从代码片段开始:
Copyright (c) ${1:`(nth 5 (decode-time))`}
All rights reserved.

Redistribution and use in source and binary forms, with or
without modification, are permitted`
      (let ((comment-style 'box))
            (comment-region (point-min) (point)))`

该片段的扩展“破坏了框架”(我正在使用ocaml注释语法调试此片段,但这并不重要):
(**************************************************************)
(* Copyright (c) 2010                                    *)
(* All rights reserved.                                       *)
(*                                                            *)
(* Redistribution and use in source and binary forms, with or *)
(* without modification, are permitted     *)
(**************************************************************)
  • 起初我以为第二行是根据预扩展嵌入代码的大小缩进的,但在这种情况下,它应该使该行的最终*)提前25个空格而不是4个。
  • 如果根据嵌入点没有任何文本缩进,则最终的*)应该晚到4个空格而不是太早。
  • 最后,我不明白最后一行发生了什么,其中没有嵌入代码扩展:通常我没有任何问题从具有短末行的段落中获得方形注释框(使用comment-box或此问题的第一个评论块中的elisp函数)。

我尝试使注释在片段扩展之后发生,以避免任何副作用,方法是将其添加到yas/after-exit-snippet-hook中,将上面片段的最后一个函数替换为:

(add-hook 'yas/after-exit-snippet-hook
      (let ((comment-style 'box))
            (comment-region (point-min) (point))) nil t)

但这并没有帮助。即使有用,它也会留下一个扩展钩子,在该缓冲区中注释所有我想要使用的片段,这是我绝对不想要的。
我还应该补充一点,我尝试将yas/indent-line设置为fixed,通过添加
# expand-env: ((yas/indent-line 'fixed))

我在代码片段的开头加了一个 rectangular,但是什么都没改变。有什么办法可以得到一个矩形框吗?


编辑:我们有一个非常好的答案,以及一个建议的修复方法(致敬和感谢,Seiji!),但问题在于如何将其适应于希望重用字段的情况,例如像在以下代码中重用$1

Copyright (c) ${1:`(nth 5 (decode-time))`}
All rights reserved.

Redistribution and use in $1, in source and binary forms
`(let ((comment-style 'box))
        (comment-region (point-min) (point-at-eol 0)))`

在这种情况下,模板引擎会复制字段$1获取的(可变长度)值,即2011,到最后一行,在模板扩展后(缩进后),产生一个注释行宽度超过2个字符。当编写模板时很难预测应该在该行删除4个字符。也许同时要求字段重用正确缩进太多了。不过,有人能看到解决方法吗?
3个回答

5
将您的代码片段更改为此版本可以修复最后一行。
Copyright (c) ${1:`(nth 5 (decode-time))`}
All rights reserved.

Redistribution and use in source and binary forms, with or
without modification, are permitted
`(let ((comment-style 'box))(comment-region (point-min) (point-at-eol 0)))`

请注意,在许可证(“...允许”)和嵌入的emacs lisp代码之间有一个换行符。
至于第二行,我首先会解释为什么会出现这种情况,然后提供一种(丑陋的)修复方案。当您的片段被插入到文件中时,嵌入的lisp块会按顺序从缓冲区开头到结尾进行顺序评估。在您的片段中,这意味着当下一个块(comment-region ...)进行评估时,(nth 5 (decode-time))已经被2011替换了。
因此,comment-region将第二行视为:
Copyright (c) ${1:2011}

因此,comment-region为该字符串提供了足够的空格。然后,模板引擎将${1:2011}转换为2011,此转换缩短了5个字符的字符串。这解释了第二行提前出现*) 5个字符的原因。
解决此问题的一种方法是在评估comment-region之后再放回5个空格 --- 大致如下:
Copyright (c) ${1:`(nth 5 (decode-time))`} @
All rights reserved.

Redistribution and use in source and binary forms, with or
without modification, are permitted.
`(let ((comment-style 'box))(comment-region (point-min) (point-at-eol 0)))``
(replace-string "@" "      " nil (point-min) (point))`$0

感谢“point-at-eol”建议,它完全解决了最后一行的问题。非常好的解释,谢谢。你的修复方法可行,但是在一行中泛化到多个字段是很痛苦的(需要手动计算每个模板的样板),而且无法扩展到模板中其他地方重用字段$1(在这种情况下,您必须删除所嵌入的lisp评估所创建的所有空格)。我会稍等一段时间,如果没有人提出解决方案,我会“接受”你的答案。 - Francois G
1
我同意我的第二行解决方案不够优秀。如果 yas/after-exit-snipeet-hook 可以在我们看到的文本上工作,那就太好了。然而,它似乎只能在底层文本(例如 ${1:...})上工作。顺便说一下,如果你在 # expand-env 中设置钩子,钩子只会影响片段而不是其他片段 --- 通过添加 # expand-env: ((yas/after-exit-snippet-hook (list (let ((comment-style 'box))(comment-region (point-min) (point-max) nil))))) - Seiji Kumagai

1
这可能对你有些用处:
# -*- mode: snippet -*-
# name: param
# key:  param
# --
m.parameter :${1:arg},${1:$(make-string (- 14 (string-width text)) ?\ 
                         )}:${2:type}$>

它使用具有功能的镜像来创建定位空格。因此,对于您来说,这可能意味着类似于以下内容:
(* Copyright (c) ${1:2011}${1:$(make-string (- 72 (string-width text)) ?\ )} *)
(* ${2}${2:$(make-string (- 72 (string-width text)))}*)

另一个选择可能是使用yas/after-exit-snippet-hook,但似乎已经被弃用了。(哦,我是指自己过滤文本,你已经尝试过的comment-region更聪明。尝试indent-region?)


这很聪明,但不幸的是,正如Seiji的答案中所解释的那样,模板引擎将在块执行后替换字段,这意味着(make-string ...)插入的空格会短两个5个字符(对于${1:}${1:}$)。 (感谢钩子建议,但我已经在原始问题中提到了我的失败尝试) - Francois G

0
这对我有用:
# -*- mode: snippet -*-
# name: box comment block
# key: bbox
# expand-env: ((yas-after-exit-snippet-hook (lambda () (if (buffer-substring yas-snippet-beg yas-snippet-end) (comment-box yas-snippet-beg yas-snippet-end 1)))))
# --
Copyright (c) ${1:`(nth 5 (decode-time))`}
All rights reserved.

Redistribution and use in source and binary forms, with or
without modification, are permitted
$0

我参考了这个问题中的信息 在特定片段后运行函数


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接