]> git.r.bdr.sh - rbdr/dotfiles/blob - vim/plugin/snipMate.vim
ef03b12b58b816f2ba9b54c832690d7684be2c8b
[rbdr/dotfiles] / vim / plugin / snipMate.vim
1 " File: snipMate.vim
2 " Author: Michael Sanders
3 " Version: 0.84
4 " Description: snipMate.vim implements some of TextMate's snippets features in
5 " Vim. A snippet is a piece of often-typed text that you can
6 " insert into your document using a trigger word followed by a "<tab>".
7 "
8 " For more help see snipMate.txt; you can do this by using:
9 " :helptags ~/.vim/doc
10 " :h snipMate.txt
11
12 if exists('loaded_snips') || &cp || version < 700
13 finish
14 endif
15 let loaded_snips = 1
16 if !exists('snips_author') | let snips_author = 'Me' | endif
17
18 au BufRead,BufNewFile *.snippets\= set ft=snippet
19 au FileType snippet setl noet fdm=indent
20
21 let s:snippets = {} | let s:multi_snips = {}
22
23 if !exists('snippets_dir')
24 let snippets_dir = substitute(globpath(&rtp, 'snippets/'), "\n", ',', 'g')
25 endif
26
27 fun! MakeSnip(scope, trigger, content, ...)
28 let multisnip = a:0 && a:1 != ''
29 let var = multisnip ? 's:multi_snips' : 's:snippets'
30 if !has_key({var}, a:scope) | let {var}[a:scope] = {} | endif
31 if !has_key({var}[a:scope], a:trigger)
32 let {var}[a:scope][a:trigger] = multisnip ? [[a:1, a:content]] : a:content
33 elseif multisnip | let {var}[a:scope][a:trigger] += [[a:1, a:content]]
34 else
35 echom 'Warning in snipMate.vim: Snippet '.a:trigger.' is already defined.'
36 \ .' See :h multi_snip for help on snippets with multiple matches.'
37 endif
38 endf
39
40 fun! ExtractSnips(dir, ft)
41 for path in split(globpath(a:dir, '*'), "\n")
42 if isdirectory(path)
43 let pathname = fnamemodify(path, ':t')
44 for snipFile in split(globpath(path, '*.snippet'), "\n")
45 call s:ProcessFile(snipFile, a:ft, pathname)
46 endfor
47 elseif fnamemodify(path, ':e') == 'snippet'
48 call s:ProcessFile(path, a:ft)
49 endif
50 endfor
51 endf
52
53 " Processes a single-snippet file; optionally add the name of the parent
54 " directory for a snippet with multiple matches.
55 fun s:ProcessFile(file, ft, ...)
56 let keyword = fnamemodify(a:file, ':t:r')
57 if keyword == '' | return | endif
58 try
59 let text = join(readfile(a:file), "\n")
60 catch /E484/
61 echom "Error in snipMate.vim: couldn't read file: ".a:file
62 endtry
63 return a:0 ? MakeSnip(a:ft, a:1, text, keyword)
64 \ : MakeSnip(a:ft, keyword, text)
65 endf
66
67 fun! ExtractSnipsFile(file, ft)
68 if !filereadable(a:file) | return | endif
69 let text = readfile(a:file)
70 let inSnip = 0
71 for line in text + ["\n"]
72 if inSnip && (line[0] == "\t" || line == '')
73 let content .= strpart(line, 1)."\n"
74 continue
75 elseif inSnip
76 call MakeSnip(a:ft, trigger, content[:-2], name)
77 let inSnip = 0
78 endif
79
80 if line[:6] == 'snippet'
81 let inSnip = 1
82 let trigger = strpart(line, 8)
83 let name = ''
84 let space = stridx(trigger, ' ') + 1
85 if space " Process multi snip
86 let name = strpart(trigger, space)
87 let trigger = strpart(trigger, 0, space - 1)
88 endif
89 let content = ''
90 endif
91 endfor
92 endf
93
94 " Reset snippets for filetype.
95 fun! ResetSnippets(ft)
96 let ft = a:ft == '' ? '_' : a:ft
97 for dict in [s:snippets, s:multi_snips, g:did_ft]
98 if has_key(dict, ft)
99 unlet dict[ft]
100 endif
101 endfor
102 endf
103
104 " Reset snippets for all filetypes.
105 fun! ResetAllSnippets()
106 let s:snippets = {} | let s:multi_snips = {} | let g:did_ft = {}
107 endf
108
109 " Reload snippets for filetype.
110 fun! ReloadSnippets(ft)
111 let ft = a:ft == '' ? '_' : a:ft
112 call ResetSnippets(ft)
113 call GetSnippets(g:snippets_dir, ft)
114 endf
115
116 " Reload snippets for all filetypes.
117 fun! ReloadAllSnippets()
118 for ft in keys(g:did_ft)
119 call ReloadSnippets(ft)
120 endfor
121 endf
122
123 let g:did_ft = {}
124 fun! GetSnippets(dir, filetypes)
125 for ft in split(a:filetypes, '\.')
126 if has_key(g:did_ft, ft) | continue | endif
127 call s:DefineSnips(a:dir, ft, ft)
128 if ft == 'objc' || ft == 'cpp' || ft == 'cs'
129 call s:DefineSnips(a:dir, 'c', ft)
130 elseif ft == 'xhtml'
131 call s:DefineSnips(a:dir, 'html', 'xhtml')
132 endif
133 let g:did_ft[ft] = 1
134 endfor
135 endf
136
137 " Define "aliasft" snippets for the filetype "realft".
138 fun s:DefineSnips(dir, aliasft, realft)
139 for path in split(globpath(a:dir, a:aliasft.'/')."\n".
140 \ globpath(a:dir, a:aliasft.'-*/'), "\n")
141 call ExtractSnips(path, a:realft)
142 endfor
143 for path in split(globpath(a:dir, a:aliasft.'.snippets')."\n".
144 \ globpath(a:dir, a:aliasft.'-*.snippets'), "\n")
145 call ExtractSnipsFile(path, a:realft)
146 endfor
147 endf
148
149 fun! TriggerSnippet()
150 if exists('g:SuperTabMappingForward')
151 if g:SuperTabMappingForward == "<tab>"
152 let SuperTabKey = "\<c-n>"
153 elseif g:SuperTabMappingBackward == "<tab>"
154 let SuperTabKey = "\<c-p>"
155 endif
156 endif
157
158 if pumvisible() " Update snippet if completion is used, or deal with supertab
159 if exists('SuperTabKey')
160 call feedkeys(SuperTabKey) | return ''
161 endif
162 call feedkeys("\<esc>a", 'n') " Close completion menu
163 call feedkeys("\<tab>") | return ''
164 endif
165
166 if exists('g:snipPos') | return snipMate#jumpTabStop(0) | endif
167
168 let word = matchstr(getline('.'), '\S\+\%'.col('.').'c')
169 for scope in [bufnr('%')] + split(&ft, '\.') + ['_']
170 let [trigger, snippet] = s:GetSnippet(word, scope)
171 " If word is a trigger for a snippet, delete the trigger & expand
172 " the snippet.
173 if snippet != ''
174 let col = col('.') - len(trigger)
175 sil exe 's/\V'.escape(trigger, '/\.').'\%#//'
176 return snipMate#expandSnip(snippet, col)
177 endif
178 endfor
179
180 if exists('SuperTabKey')
181 call feedkeys(SuperTabKey)
182 return ''
183 endif
184 return "\<tab>"
185 endf
186
187 fun! BackwardsSnippet()
188 if exists('g:snipPos') | return snipMate#jumpTabStop(1) | endif
189
190 if exists('g:SuperTabMappingForward')
191 if g:SuperTabMappingBackward == "<s-tab>"
192 let SuperTabKey = "\<c-p>"
193 elseif g:SuperTabMappingForward == "<s-tab>"
194 let SuperTabKey = "\<c-n>"
195 endif
196 endif
197 if exists('SuperTabKey')
198 call feedkeys(SuperTabKey)
199 return ''
200 endif
201 return "\<s-tab>"
202 endf
203
204 " Check if word under cursor is snippet trigger; if it isn't, try checking if
205 " the text after non-word characters is (e.g. check for "foo" in "bar.foo")
206 fun s:GetSnippet(word, scope)
207 let word = a:word | let snippet = ''
208 while snippet == ''
209 if exists('s:snippets["'.a:scope.'"]["'.escape(word, '\"').'"]')
210 let snippet = s:snippets[a:scope][word]
211 elseif exists('s:multi_snips["'.a:scope.'"]["'.escape(word, '\"').'"]')
212 let snippet = s:ChooseSnippet(a:scope, word)
213 if snippet == '' | break | endif
214 else
215 if match(word, '\W') == -1 | break | endif
216 let word = substitute(word, '.\{-}\W', '', '')
217 endif
218 endw
219 if word == '' && a:word != '.' && stridx(a:word, '.') != -1
220 let [word, snippet] = s:GetSnippet('.', a:scope)
221 endif
222 return [word, snippet]
223 endf
224
225 fun s:ChooseSnippet(scope, trigger)
226 let snippet = []
227 let i = 1
228 for snip in s:multi_snips[a:scope][a:trigger]
229 let snippet += [i.'. '.snip[0]]
230 let i += 1
231 endfor
232 if i == 2 | return s:multi_snips[a:scope][a:trigger][0][1] | endif
233 let num = inputlist(snippet) - 1
234 return num == -1 ? '' : s:multi_snips[a:scope][a:trigger][num][1]
235 endf
236
237 fun! ShowAvailableSnips()
238 let line = getline('.')
239 let col = col('.')
240 let word = matchstr(getline('.'), '\S\+\%'.col.'c')
241 let words = [word]
242 if stridx(word, '.')
243 let words += split(word, '\.', 1)
244 endif
245 let matchlen = 0
246 let matches = []
247 for scope in [bufnr('%')] + split(&ft, '\.') + ['_']
248 let triggers = has_key(s:snippets, scope) ? keys(s:snippets[scope]) : []
249 if has_key(s:multi_snips, scope)
250 let triggers += keys(s:multi_snips[scope])
251 endif
252 for trigger in triggers
253 for word in words
254 if word == ''
255 let matches += [trigger] " Show all matches if word is empty
256 elseif trigger =~ '^'.word
257 let matches += [trigger]
258 let len = len(word)
259 if len > matchlen | let matchlen = len | endif
260 endif
261 endfor
262 endfor
263 endfor
264
265 " This is to avoid a bug with Vim when using complete(col - matchlen, matches)
266 " (Issue#46 on the Google Code snipMate issue tracker).
267 call setline(line('.'), substitute(line, repeat('.', matchlen).'\%'.col.'c', '', ''))
268 call complete(col, matches)
269 return ''
270 endf
271 " vim:noet:sw=4:ts=4:ft=vim