博客原文javascript
Ultisnips 插件安装分两部分,一个是 ultisnips 插件自己,另一个是代码片断仓库。通常来讲把默认的代码片断仓库下载下来按需修改后上传到本身的 github 便可。若是你和我同样也使用 vim-plug 来管理插件的话,添加下面的代码到你的 vimrc 中保存刷新便可java
Plug 'SirVer/ultisnips'
" 你本身的代码仓库 git 地址
Plug 'keelii/vim-snippets'
复制代码
上面的示例中全部的代码片断都存放在插件安装目录下面的 vim-snippets/UltiSnips
中,文件命名格式为 ft
.snippets, ft
就是 vim 中的 filetype
,其中有个 all.snippets
是惟一一个全部文件都适用的代码片断python
快捷键设置,我通常使用 tab 来触发代码片断补全,且不使用 YCM (官方文档表示使用YCM的话就不能使用tab补全)git
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 表示支持github
: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
...shell
snippet if "if (...)"
if (${1:true}) {
${VISUAL}
}
endsnippet
复制代码
${VISUAL}
表示在 vim 中使用可视模式下选择的文本,这个在重构代码的时候很是有用(后面会有高级用法),上个图感觉一下vim
b
表示触发字符应该在一行的开始i
表示触发字符能够在单词内(连续展现会使用这个选项)w
表示触发字符的先后必须是一个字母分界点r
表示触发字符能够是一个正则表达式t
表示展开的代码片断中若是有制表符,原样输出,即便你的 vimrc 里面设置了 expandtabm
表示删除代码片断右边的全部空白字符e
表示自定义上下文A
表示自动触发,不须要按 tab,相似于 VIM 中的 abbrUltisnips 定义的代码片断中支持三种不一样的语言注入:shell, vimscript, python,在代码片断中用反引号表示bash
就是在你的命令行 shell 能执行的代码片断,好比输出当前时间post
➜ 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.
复制代码
使用 indent
来输出当前缩进值,使用前缀 !v
表示是 vimscript
snippet indent
Indent is: `!v indent(".")`.
endsnippet
复制代码
在代码片断中解释执行 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 模式中选择的字符UltiSnips 支持使用快捷键切换占位符,我使用 <tab>
和 <shift-tab>
来切换 下一个
和 上一个
占位符,占位符切换的做用域为当前代码片断内部(即便占位符已被修改过),当光标移动出去之后就不起做用了
自定义上下文能够经过正则匹配来决定代码片段是否可用,好比判断在指定的 if 语句里面才起做用的代码片段,定义格式以下:
snippet 触发字符 "描述" "表达式" 参数
好比咱们定义一个 只有 在上一行以 if (DEVELOPMENT) {
开头才能够展开的代码片断
snippet dbg "if (DEVELOPMENT) dbg" "re.match('^if \(DEVELOPMENT\) \{', snip.buffer[snip.line-1])" be
debugger;
endsnippet
复制代码
这个常见于须要连续展开代码片断的状况,好比,有两个片断,一个打印变量,一个处理 JSON 序列化。这时须要使用参数选项 i
n-word
一般写代码的时候须要使用 log, print 等来打印上下文中的变量。使用普通片断按 cl 展现 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
复制代码
动图演示
注意:正则代码片断只适用于单行文本处理,若是是多行转换仍是得用到下面的 python + VISUAL 代码片断来处理
一般咱们须要使用一大堆插件来实现各类代码的注释功能。不过 Ultisnips 提供了 VISUAL 模式能够提取 vim 可视模式中选择的内容到代码片断里面,因而咱们就能够结合起来制做一个具备注释功能的代码片断
流程大概是这样的:
因为实现的 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
复制代码
动图演示
不一样的语言能够在对应的片断文件中定义并传入注释符号参数便可,有了这个功能就能够愉快的删除其它的 vim 注释插件了 😀