博客原文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 註釋插件了 😀