`

smart-snippet and smart-skeleton

阅读更多

随着 Ruby on Rails 火起来,TextMate 也突然变得很火。没有 Mac 机器,不能体验到 TextMate 是什么感觉,不过在网络上看到 TextMate 的视频演示,其中有一个功能确实是很不错的。就是那个 snippet 。定义一些模板,然后在合适的时候展开,以减少输入重复的内容,这个是每个稍微强一点的编辑器都有的功能了,同一个“域”,在多处展开也是非常常见的功能。但是通常的编辑器都是对每个会放到多个地方的“域”进行提问(例如,弹出一个对话框),TextMate 让人耳目一新的地方就是它并不弹出对话框进行提问,而是直接让你在“域”的地方输入,并在输入的同时同步更新其他几个相关联的“域”的内容。

同步更新与原来的一个一个提问的方法相比,简直就是太 Cool 了!一时间各大编辑器也开始模仿这个功能。Emacs 自然也不落后,很快就有人做出了 snippet.el ,在 Emacs 里面实现了 TextMate 的那种很 Cool 的功能。

snippet.el 适用 Emacs 内建的 abbrev 的功能来实现。Emacs 的 abbrev 与其他一些编辑器不一样,它不需要某一个特定的快捷键来触发(或者说,有许多不同的“快捷键”可以让它展开),当你打开 abbrev-mode 之后,它会在你键入的过程中自动展开,输入一个单词,然后键入空格、标点符号、回车等所有可以作为单词分界的内容时,abbrev 就会被展开。内置的 abbrev 以 major-mode 为单位,可以为不同的 major-mode 定义不同的 abbrev-table 。然而,内建的 abbrev 还是有几个不太方便的地方

  1. 由于是使用单词边界自动触发,因此无法定义非单词的 abbrev ,例如,不能使用 abbrev 让 “(” 自动展开为 “()”。
  2. 以 major-mode 为单位有时候还是显得粒度不够细。例如,我为 c++-mode 定义了一个把 “class” 展开为一个定义 class 的框架,这样很方便,但是我不想在我写注释的时候输入 “class” 却突然展开出一大堆东西。通常的解决办法有两种:
    • 手工不触发 abbrev ,就是在输入 “class” 之后的一个字符,例如,要输入 “SPACE”,现在使用 “C-q SPACE” 来键入。
    • 更改 abbrev ,例如,定义 “classx” ,在需要展开的时候输入 “classx” ,而不影响正常的输入。msf-abbrev.el 就是选择的这种方法

    然而,实际上,两种方法都不是那么舒服,关键就是 Emacs 没有区分在同一个 major-mode 里面的不同语法上下文。

于是我着手做了一个基于 snippet.el 的 smart-snippet.el ,提供更细粒度的控制,正如其名字那样,它更聪明。允许你定义不同上下文展开为不同的模板,或者干脆不展开。非常好用。

我使用 snippet.el 的展开引擎来解析和展开模板,并做了一些小小的更改。 snippet.el 的模板语法非常简单:

  • $${field} 定义一个域,同名的域在编辑的时候会同步更新。使用 Tab 和 S-Tab 在各个域之间移动。
  • $. 最后光标所处的位置。
  • $> 表示进行一次自动缩进。Emacs 通常对各个 major-mode 都提供非常好的自动缩进功能。

并且这些语法都是可以定制的,如果它们和某一种语言的语法冲突,导致 Emacs 缩进的时候被搞晕了的话,可以更改为其他不会引起混乱的标记,你可以为不同的 major-mode 定义不同的一套模板语法。例如,为 c++-mode ,我可以定义 if 在正常情况下展开为

if ($${cond})
{$>
$>$.
}$>

而在其他情况,例如字符串或者注释里面就不展开。还有其他一些语言,如 Perl 、Ruby 等,同样的关键字有不同的用法。例如,在 Ruby 里面,if 就有这两种写法

# one way
if cond
  do_something
end
 
# another way
do_something if cond

smart-snippet.el 的 smart 就在这里能派上用场了!我在项目主页上上传了一个视频演示,展示了 smart-snippetl.el 的功能。

然而,Emacs 内建的 abbrev 的另外一个不足还没有解决。其实这个很好解决,只需要把引号、括号等键绑定到相应的输入一对引号、一对括号的函数上就可以了。Emacser 们通常都使用 Emacs 自带的 skeleton 功能来解决这个问题。然而 smart-snippet.el 在这里仍然能派上用场。我已经扩展了 smart-snippet.el ,让你能够轻松地把一个 snippet 绑定到一个键上。你可以

  • " 在普通代码里面展开为 "$." ;而如果它本来就在字符串里面,则展开为一个转义的引号 \"
  • < 在正常情况下不展开(作为小于号),而当你已经键入了 template 接下来要写模板参数的时候,展开为 < $. >
  • ……任何你能想到的!

关于具体如何配置以及更多详细的内容,可以参考 smart-snippet.el 的项目主页

最后,我再说一点和 smart-snippet 关系不那么大的内容:在让 " 扩展为 "$." 之后,如何“跳出”引号(也就是把光标移动到引号的后面)呢?我目前所知的有几种解决方案:

  • 直接用方向键,但是使用 Emacs 或者 Vim 的人通常都会觉得方向键太远了,很难按。当然 Emacs 和 Vim 都有专门用于移动光标的快捷键(Emacs 里面可以使用 C-f ,而 Vim 里面虽然直接 l 就可以了,但是却要先按一下 ESC),不过如果是对于非常“懒”的人来说的话,他们仍然是很麻烦的。
  • 一些编辑器会允许你直接输入 " ,它会进行判断,并覆盖掉当前这个引号,并把光标移动到后面,不过这似乎有些丧失了原来自动扩展为一对引号的意义了,到头来还是要自己手工输入后面那个引号。从自动扩展得到的唯一的好处就是不会在输入一个引号的时候,后面整个一片被解释为字符串,显示一片语法高亮,看上去很不爽,让你迫不及待地想赶紧加上另外一个引号,让编辑器能正确地解析语法高亮。
  • 使用一个通用的快捷键来“跳出”。例如 Emacs 的一个扩展 cdlatex 作为一个“让输入变成享受”的典范,就使用的这种方法,它使用一个非常好按的键:Tab 键来完成这个功能。

我在这里也提供一个针对 c++-mode 的“跳出” snippet 的函数。其实 Tab 真的被用的太多了,通常我们写一个包装函数,用于在不同的环境下让 Tab 来完成不同的工作,但是 Emacs 通常有各种扩展,为了避免各个扩展之间的兼容性,对于这种“核心”的功能键,一般还是不要改为妙。幸运的是,Emacs 在 X 模式下(就是说,不是通过 -nw 选项来运行的终端模式)有“两个” Tab 可以用:

  • C-i 也就是通常认为的 Tab ,在终端下是不区分 TabC-i 的。
  • <tab> 这个才是真正的对应到键盘上的那个 Tab 键。

于是我们就可以分别用 <tab>C-i 来做不同的事情了。

;; jump out from a pair(like quote, parenthesis, etc.)
(defun kid-c-escape-pair ()
  (interactive)
  (let ((pair-regexp "[^])}"'>]*[])}"'>]"))
    (if (looking-at pair-regexp)
  (progn
    ;; be sure we can use C-u C-@ to jump back
    ;; if we goto the wrong place
    (push-mark) 
    (goto-char (match-end 0)))
      (c-indent-command))))
;; note TAB can be different to <tab> in X mode(not -nw mode).
;; the formal is C-i while the latter is the real "Tab" key
;; in your keyboard.
(define-key c++-mode-map (kbd "TAB") 'kid-c-escape-pair)
(define-key c++-mode-map (kbd "<tab>") 'c-indent-command)
;; snippet.el use TAB, now we need to use <tab>
(define-key snippet-map  (kbd "<tab>") 'snippet-next-field)

其实编辑器的设计也是一门学问呢!让程序员能够更舒服地写代码,无疑是非常重要的话题。

分享到:
评论

相关推荐

    PyPI 官网下载 | wagtail-draftail-snippet-0.2.1.tar.gz

    《PyPI官网下载 wagtail-draftail-snippet-0.2.1.tar.gz:深入了解Python库的使用与管理》 PyPI(Python Package Index),是Python开发者的重要资源库,它提供了大量的开源Python软件包,方便用户下载、安装和分享...

    PyPI 官网下载 | djangocms-snippet-1.0.1.tar.gz

    **PyPI 官网下载 | djangocms-snippet-1.0.1.tar.gz** 在Python开发中,PyPI(Python Package Index)是官方的软件仓库,它为Python开发者提供了一个集中发布和下载第三方库的平台。"djangocms-snippet-1.0.1.tar....

    Laravel开发-lkng-snippet

    【Laravel 开发 - lkng-snippet 概述】 `lkng-snippet` 是一个针对 Laravel 开发者设计的代码片段集合,它包含了在 Laravel 应用开发过程中常用的功能、方法和模式,旨在提高开发效率并保持代码一致性。通过这个...

    微信小程序使用sublime text插件开发详细教程weapp-snippet-for-sublime-text-2-3-master.zip

    本教程将详细讲解如何在Sublime Text中利用插件weapp-snippet-for-sublime-text-2-3-master来提升微信小程序的开发效率。 首先,安装weapp-snippet-for-sublime-text-2-3-master插件。在Sublime Text中,你可以通过...

    Python库 | python-snippet-0.1.6.tar.gz

    资源分类:Python库 所属语言:Python 资源全名:python-snippet-0.1.6.tar.gz 资源来源:官方 安装方法:https://lanzao.blog.csdn.net/article/details/101784059

    Algorithm-algorithm-snippet.zip

    在这个名为"algorithm-snippet-master"的文件夹中,我们可以期待找到不同类型的算法代码片段,如排序算法(快速排序、归并排序、冒泡排序、插入排序等)、查找算法(二分查找、哈希查找等)、图算法(Dijkstra算法、...

    R_语言常用的_代码片段_R-Snippet.zip

    R_语言常用的_代码片段_R-Snippet

    tui.code-snippet:一组便于开发 javascript 应用程序的实用方法

    TOAST UI 工具:代码片段一组实用方法,可轻松开发 javascript 应用程序。 :triangular_flag: 目录 :hammer: 用法捆 :globe_showing_Asia-Australia: 浏览器支持 :wrench: 拉取请求步骤设置发展运行测试拉取请求 :...

    sublimeSnippet-vscodeSnippet:一个将sublime-snippet转换为vscode片段的项目。 并构建您的vscode扩展

    Descriptiona project to convert multiple sublime-snippet to vscode-snippet or be a extension of vscode js-snippet.And you can publish your package to setup vscode extension.一个用于转换多个sublime的...

    Atom-NetSuite-Atom-Snippet,Atom文本编辑器的有用netsuite suitescript snippets.cson文件.zip

    Atom-NetSuite-Atom-Snippet.zip,Atom文本编辑器的有用netsuite suitescript snippets.cson文件NetSuite原子代码段,atom是一个用web技术构建的开源文本编辑器。

    ruby-snippet:VSCode的ruby-snippet

    在项目中,`ruby-snippet-master`可能是这个扩展的源代码目录,包含了所有Ruby代码片段的定义。如果你对插件的工作原理或想要自定义代码片段感兴趣,可以查看这个目录下的文件,通常会包含`.snippets`文件,里面定义...

    bs-snippet-injector

    ### bs-snippet-injector写入并删除BrowserSync代码段到文件 这是使用BrowserSync代理的替代方法。 ##安装 $ npm install browser-sync bs-snippet-injector ###例子 // requires version 1.3.3 of ...

    inject-snippet:将一小段代码或内容插入到字符串中

    安装用安装$ npm i inject-snippet --save用法 var inject = require ( 'inject-snippet' ) ; 保留占位符将一段代码插入具有占位符的字符串中(用于后续插入): var str = 'before &lt;!-- snippet --&gt; after' ;...

    js--code-snippet:js学习的代码片段

    在"js--code-snippet-master"这个文件列表中,可能包含了以上知识点的具体应用实例,学习者可以通过分析和运行这些代码来加深理解和掌握。记住,实践是学习编程的最好方式,通过动手实验,你会更深入地理解...

    insert-php-code-snippet_wordpressplugin_

    "Insert PHP Code Snippet"是一款WordPress插件,它允许用户在不修改主题文件或不具备编程知识的情况下,在WordPress内容中安全地添加PHP代码。这款插件对于那些希望轻松管理自定义功能,而又不想直接编辑模板文件的...

    ts-snippet:适用于任何测试框架的TypeScript代码段编译器

    ts-snippet是适用于任何测试框架的TypeScript代码段编译器。 它不会运行已编译的代码段。 相反,它提供了可用于测试从代码片段编译的TypeScript程序的断言方法。 但是,如果尚未开始为TypeScript类型编写测试,则...

    extract-snippet:从字符串中提取一段代码

    安装用安装$ npm i extract-snippet --save用法 var extract = require ( 'extract-snippet' ) ; 摘录片段 var a = 'a &lt;!-- snippet --&gt;\nfoo\n&lt;!-- endsnippet --&gt; b' ;extract ( a ) ;//=&gt; 'foo'var b...

    test-vhdl-snippet

    在“test-vhdl-snippet-main”中,可能包含了一个或多个实体定义,明确了设计的I/O端口。 2. **架构(Architecture)**:架构是实体的具体实现,它定义了系统内部的工作流程。架构内部可以包括进程(Process)、...

    Cocos-Creator-Snippet:崇高的cocos创作者片段

    Cocos-Creator-Snippet Cocos-Creator-Snippet是游戏引擎cocos创建者的代码段。 Creator的代码段是根据创建者项目中的文件creator.d.ts生成的。 当前创作者版本为1.3.0 当您将js文件保存为sublime时,插件将分析其...

    100793-0-snippet-rest-java:Snippet_Rest_JAVA ----------------- 网址

    100793-0-snippet-rest-java Snippet_Rest_JAVA ----------------- 网址: ://codes-sources.commentcamarche.net/source/100793-snippet-rest-java 日期:01/30/2021 许可证:创用CC ========= ...

Global site tag (gtag.js) - Google Analytics