随着 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 还是有几个不太方便的地方
- 由于是使用单词边界自动触发,因此无法定义非单词的 abbrev ,例如,不能使用 abbrev 让 “(” 自动展开为 “()”。
- 以 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
,在终端下是不区分 Tab
和 C-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:深入了解Python库的使用与管理》 PyPI(Python Package Index),是Python开发者的重要资源库,它提供了大量的开源Python软件包,方便用户下载、安装和分享...
**PyPI 官网下载 | djangocms-snippet-1.0.1.tar.gz** 在Python开发中,PyPI(Python Package Index)是官方的软件仓库,它为Python开发者提供了一个集中发布和下载第三方库的平台。"djangocms-snippet-1.0.1.tar....
【Laravel 开发 - lkng-snippet 概述】 `lkng-snippet` 是一个针对 Laravel 开发者设计的代码片段集合,它包含了在 Laravel 应用开发过程中常用的功能、方法和模式,旨在提高开发效率并保持代码一致性。通过这个...
本教程将详细讲解如何在Sublime Text中利用插件weapp-snippet-for-sublime-text-2-3-master来提升微信小程序的开发效率。 首先,安装weapp-snippet-for-sublime-text-2-3-master插件。在Sublime Text中,你可以通过...
资源分类:Python库 所属语言:Python 资源全名:python-snippet-0.1.6.tar.gz 资源来源:官方 安装方法:https://lanzao.blog.csdn.net/article/details/101784059
在这个名为"algorithm-snippet-master"的文件夹中,我们可以期待找到不同类型的算法代码片段,如排序算法(快速排序、归并排序、冒泡排序、插入排序等)、查找算法(二分查找、哈希查找等)、图算法(Dijkstra算法、...
R_语言常用的_代码片段_R-Snippet
TOAST UI 工具:代码片段一组实用方法,可轻松开发 javascript 应用程序。 :triangular_flag: 目录 :hammer: 用法捆 :globe_showing_Asia-Australia: 浏览器支持 :wrench: 拉取请求步骤设置发展运行测试拉取请求 :...
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.zip,Atom文本编辑器的有用netsuite suitescript snippets.cson文件NetSuite原子代码段,atom是一个用web技术构建的开源文本编辑器。
在项目中,`ruby-snippet-master`可能是这个扩展的源代码目录,包含了所有Ruby代码片段的定义。如果你对插件的工作原理或想要自定义代码片段感兴趣,可以查看这个目录下的文件,通常会包含`.snippets`文件,里面定义...
### bs-snippet-injector写入并删除BrowserSync代码段到文件 这是使用BrowserSync代理的替代方法。 ##安装 $ npm install browser-sync bs-snippet-injector ###例子 // requires version 1.3.3 of ...
安装用安装$ npm i inject-snippet --save用法 var inject = require ( 'inject-snippet' ) ; 保留占位符将一段代码插入具有占位符的字符串中(用于后续插入): var str = 'before <!-- snippet --> after' ;...
在"js--code-snippet-master"这个文件列表中,可能包含了以上知识点的具体应用实例,学习者可以通过分析和运行这些代码来加深理解和掌握。记住,实践是学习编程的最好方式,通过动手实验,你会更深入地理解...
"Insert PHP Code Snippet"是一款WordPress插件,它允许用户在不修改主题文件或不具备编程知识的情况下,在WordPress内容中安全地添加PHP代码。这款插件对于那些希望轻松管理自定义功能,而又不想直接编辑模板文件的...
ts-snippet是适用于任何测试框架的TypeScript代码段编译器。 它不会运行已编译的代码段。 相反,它提供了可用于测试从代码片段编译的TypeScript程序的断言方法。 但是,如果尚未开始为TypeScript类型编写测试,则...
安装用安装$ npm i extract-snippet --save用法 var extract = require ( 'extract-snippet' ) ; 摘录片段 var a = 'a <!-- snippet -->\nfoo\n<!-- endsnippet --> b' ;extract ( a ) ;//=> 'foo'var b...
在“test-vhdl-snippet-main”中,可能包含了一个或多个实体定义,明确了设计的I/O端口。 2. **架构(Architecture)**:架构是实体的具体实现,它定义了系统内部的工作流程。架构内部可以包括进程(Process)、...
Cocos-Creator-Snippet Cocos-Creator-Snippet是游戏引擎cocos创建者的代码段。 Creator的代码段是根据创建者项目中的文件creator.d.ts生成的。 当前创作者版本为1.3.0 当您将js文件保存为sublime时,插件将分析其...
100793-0-snippet-rest-java Snippet_Rest_JAVA ----------------- 网址: ://codes-sources.commentcamarche.net/source/100793-snippet-rest-java 日期:01/30/2021 许可证:创用CC ========= ...