分享下 vim 插件 Ultisnips 的使用教程(多图) - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
keelii
V2EX    Vim

分享下 vim 插件 Ultisnips 的使用教程(多图)

  •  1
     
  •   keelii 2018 年 8 月 28 日 2597 次点击
    这是一个创建于 2715 天前的主题,其中的信息可能已经有所发展或是发生改变。

    v2 的 markdown 排版看起来不太方便,可以转到 博客原文

    安装

    Ultisnips 插件安装分两部分,一个是 ultisnips 插件本身,另外一个是代码片段仓库。一般来说把默认的代码片段仓库下载下来按需修改后上传到自己的 github 即可。如果你和我一样也使用 vim-plug 来管理插件的话,添加下面的代码到你的 vimrc 中保存刷新即可

    Plug 'SirVer/ultisnips' # 你自己的代码仓库 git 地址 Plug 'keelii/vim-snippets' 

    上面的示例中所有的代码片段都存放在插件安装目录下面的 vim-snippets/UltiSnips 中,文件命名格式为 ft.snippets, ft 就是 vim 中的 filetype,其中有个 all.snippets 是唯一一个所有文件都适用的代码片段

    配置

    快捷键设置,我一般使用 tab 来触发代码片段补全,且不使用 YCM (官方文档表示使用 YCM 的话就不能使用 tab 补全)

    let g:UltiSnipsExpandTrigger="<tab>" " 使用 tab 切换下一个触发点,shit+tab 上一个触发点 let g:UltiSnipsJumpForwardTrigger="<tab>" let g:UltiSnipsJumpBackwardTrigger="<S-tab>" " 使用 UltiSnipsEdit 命令时垂直分割屏幕 let g:UltiSnipsEditSplit="vertical" 

    依赖

    ultisnips 插件需要你的 vim 支持 python,可以在 vim 命令模式下使用下面的检测你的 vim 版本是否支持 python

    # 1 表示支持 :echo has("python") :echo has("python3") 

    定义一个代码片段

    定义格式

    snippet 触发字符 ["代码片段说明" [参数]] 代码片段内容 endsnippet 

    最小化的一个代码片段

    snippet if "if (condition) { ... }" if (${1:true}) { $0 } endsnippet 

    这时当你在 vim 中输入 if 敲 tab 就会展开一条 if 语句,第一个触发点是 if 条件表达式,最后一个是 if 语句体

    ${1:true} 表示这是第一个触发点,占位符为 true,如果占位符没有默认值可直接使用 $1, $2, $3...

    可视选择区的内容为占位符

    snippet if "if (...)" if (${1:true}) { ${VISUAL} } endsnippet 

    ${VISUAL} 表示在 vim 中使用可视模式下选择的文本,这个在重构代码的时候非常有用(后面会有高级用法),上个图感受一下

    if-with-visual-mode

    代码片段的参数

    • b 表示触发字符应该在一行的开始
    • i 表示触发字符可以在单词内(连续展示会使用这个选项)
    • w 表示触发字符的前后必须是一个字母分界点
    • r 表示触发字符可以是一个正则表达式
    • t 表示展开的代码片段中如果有制表符,原样输出,即使你的 vimrc 里面设置了 expandtab
    • m 表示删除代码片段右边的所有空白字符
    • e 表示自定义上下文
    • A 表示自动触发,不需要按 tab,类似于 VIM 中的 abbr

    内容解释器

    Ultisnips 定义的代码片段中支持三种不同的语言注入:shell, vimscript, python,在代码片段中用反引号表示

    shell 代码

    就是在你的命令行 shell 能执行的代码片段,比如输出当前时间

     date 2018 年 8 月 27 日 星期一 18 时 19 分 38 秒 CST 

    在代码片段中用反引号「`」引用即可

    snippet today Today is the `date`. endsnippet 

    输入 today 按 tab 展开后(格式和上面 shell 中的不一样,估计是因为 vim 语言设置的问题):

    Today is the Mon Aug 27 18:24:51 CST 2018. 

    vimscript 代码

    使用 indent 来输出当前缩进值,使用前缀 !v 表示是 vimscript

    snippet indent Indent is: `!v indent(".")`. endsnippet 

    vimscript-indent-size

    python 代码

    在代码片段中解释执行 python 代码是 ultisnips 最强大的功能,以前缀 !p 开始。系统会向 python 中注入一些变量,可以使用 python 代码直接对其进行操作

    • fn - 表示当前文件名
    • path - 当前文件名的路径
    • t - 占位符的字典,可以使用 t[1], t[2], t.v 来取占位符内容
    • snip - UltiSnips.TextObjects.SnippetUtil 对象的一个实例
    • match - 正则代码片段时返回的匹配元素(非常强大)

    其中最常用的 snip 对象提供了下面一些变量:

    • snip.rv 表示 return value,python 代码执行后处理过的字符串赋给 rv 即可
    • snip.fn 表示当前文件名
    • snip.ft 表示当前文件类型
    • snip.v 表示 VISUAL 模式变量,其中 snip.v.mode 表示模式类型,snip.v.text 表示 VISUAL 模式中选择的字符

    python-snippet-var

    占位符选择

    UltiSnips 支持使用快捷键切换占位符,我使用 <tab><shift-tab> 来切换 下一个上一个 占位符,占位符切换的作用域为当前代码片段内部(即使占位符已被修改过),当光标移动出去以后就不起作用了

    mul-placeholder-editing

    自定义上下文

    自定义上下文可以通过正则匹配来决定代码片断是否可用,比如判断在指定的 if 语句里面才起作用的代码片断,定义格式如下:

    snippet 触发字符 "描述" "表达式" 参数

    比如我们定义一个 只有 在上一行以 if (DEVELOPMENT) { 开头才可以展开的代码片段

    snippet dbg "if (DEVELOPMENT) dbg" "re.match('^if \(DEVELOPMENT\) \{', snip.buffer[snip.line-1])" be debugger; endsnippet 

    常见用法

    行内连续展开

    这个常见于需要连续展开代码片段的情况,比如,有两个片段,一个打印变量,一个处理 JSON 序列化。这时需要使用参数选项 in-word

    continue-expand-snippets

    使用正则代码片段

    通常写代码的时候需要使用 log, print 等来打印上下文中的变量。使用普通片段按 cl 展示 console.log() 然后把变量字符复制进括号,这样操作会比较复杂。使用正则来动态匹配前面的字符可以很好的解决这个问题

    # 展开 console.log snippet "([^\s]\w+)\.log" "console.log(postfix)" r console.log(`!p snip.rv = match.group(1)`)$0 endsnippet # 当前行转换成大写 snippet "([^\s].*)\.upper" "Uppercase(postfix)" r `!p snip.rv = match.group(1).upper()`$0 endsnippet # 上一个单词转换成小写 snippet "([^\s]\w+)\.lower" "Lowercase(postfix)" r `!p snip.rv = match.group(1).lower()`$0 endsnippet 

    动图演示

    expand-regexp-snippets

    注意:正则代码片段只适用于单行文本处理,如果是多行转换还是得用到下面的 python + VISUAL 代码片段来处理

    使用 python 解释器 + VISUAL 模式实现代码注释功能

    通常我们需要使用一大堆插件来实现各种代码的注释功能。不过 Ultisnips 提供了 VISUAL 模式可以提取 vim 可视模式中选择的内容到代码片段里面,于是我们就可以结合起来制作一个具有注释功能的代码片段

    流程大概是这样的:

    1. 进入 vim 可视模式,选择要注释的内容
    2. 按 tab,清除选择内容
    3. 输入代码片段触发字符,按 tab 完成

    由于实现的 python 代码相对复杂一些,主要分成两个方法。单行注释和多行注释,注意 Ultisnips 中可以直接写 python 但是大段的方法建议放在插件目录下面的 pythonx 目录下面,使用的时候在对应的代码片段中的全局 python 代码 global !p 引入即可

    单行注释(pythonx/Javascript_snippets.py):

    def comment(snip, START="", END=""): lines = snip.v.text.split('\n')[:-1] first_line = lines[0] spaces = '' initial_indent = snip._initial_indent # Get the first non-empty line for idx, l in enumerate(lines): if l.strip() != '': first_line = lines[idx] sp = re.findall(r'^\s+', first_line) if len(sp): spaces = sp[0] break # Uncomment if first_line.strip().startswith(START): result = [line.replace(START, "", 1).replace(END, "", 1) if line.strip() else line for line in lines] else: result = [f'{spaces}{START}{line[len(spaces):]}{END}' if line.strip() else line for line in lines ] # Remove initial indent if result[0] and initial_indent: result[0] = result[0].replace(initial_indent, '', 1) if result: return '\n'.join(result) else: return '' 

    多行注释:

    def comment_inline(snip, START="/* ", END=" */"): text = snip.v.text lines = text.split('\n')[:-1] first_line = lines[0] initial_indent = snip._initial_indent spaces = '' # Get the first non-empty line for idx, l in enumerate(lines): if l.strip() != '': first_line = lines[idx] sp = re.findall(r'^\s+', first_line) if len(sp): spaces = sp[0] break if text.strip().startswith(START): result = text.replace(START, '', 1).replace(END, '', 1) else: result = text.replace(spaces, spaces + START, 1).rstrip('\n') + END + '\n' if initial_indent: result = result.replace(initial_indent, '', 1) return result 

    代码片段定义:

    global !p from Javascript_snippets import ( comment, comment_inline ) endglobal # ... snippet c "Toggle comment every single line" `!p snip.rv = comment(snip, START='// ', END='') `$0 endsnippet snippet ci "Toggle comment inline." `!p snip.rv = comment_inline(snip, START="/* ", END=" */") `$0 endsnippet 

    动图演示

    python-snippets-to-comment

    不同的语言可以在对应的片段文件中定义并传入注释符号参数即可,有了这个功能就可以愉快的删除其它的 vim 注释插件了

    heikejia
        1
    heikejia  
       2018 年 8 月 28 日
    必备插件
    ivechan
        3
    ivechan  
       2018 年 9 月 1 日
    支持一下,非常好用的插件,最近正在熟悉
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2674 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 29ms UTC 11:31 PVG 19:31 LAX 03:31 JFK 06:31
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86