2 " Author: Michael Sanders
3 " Last Updated: July 13, 2009
5 " Description: snipMate.vim implements some of TextMate's snippets features in
6 " Vim. A snippet is a piece of often-typed text that you can
7 " insert into your document using a trigger word followed by a "<tab>".
9 " For more help see snipMate.txt; you can do this by using:
10 " :helptags ~/.vim/doc
13 if exists('loaded_snips') || &cp || version < 700
17 if !exists('snips_author') | let snips_author = 'Me' | endif
19 au BufRead,BufNewFile *.snippets\= set ft=snippet
20 au FileType snippet setl noet fdm=indent
22 let s:snippets = {} | let s:multi_snips = {}
24 if !exists('snippets_dir')
25 let snippets_dir = substitute(globpath(&rtp, 'snippets/'), "\n", ',', 'g')
28 fun! MakeSnip(scope, trigger, content, ...)
29 let multisnip = a:0 && a:1 != ''
30 let var = multisnip ? 's:multi_snips' : 's:snippets'
31 if !has_key({var}, a:scope) | let {var}[a:scope] = {} | endif
32 if !has_key({var}[a:scope], a:trigger)
33 let {var}[a:scope][a:trigger] = multisnip ? [[a:1, a:content]] : a:content
34 elseif multisnip | let {var}[a:scope][a:trigger] += [[a:1, a:content]]
36 echom 'Warning in snipMate.vim: Snippet '.a:trigger.' is already defined.'
37 \ .' See :h multi_snip for help on snippets with multiple matches.'
41 fun! ExtractSnips(dir, ft)
42 for path in split(globpath(a:dir, '*'), "\n")
44 let pathname = fnamemodify(path, ':t')
45 for snipFile in split(globpath(path, '*.snippet'), "\n")
46 call s:ProcessFile(snipFile, a:ft, pathname)
48 elseif fnamemodify(path, ':e') == 'snippet'
49 call s:ProcessFile(path, a:ft)
54 " Processes a single-snippet file; optionally add the name of the parent
55 " directory for a snippet with multiple matches.
56 fun s:ProcessFile(file, ft, ...)
57 let keyword = fnamemodify(a:file, ':t:r')
58 if keyword == '' | return | endif
60 let text = join(readfile(a:file), "\n")
62 echom "Error in snipMate.vim: couldn't read file: ".a:file
64 return a:0 ? MakeSnip(a:ft, a:1, text, keyword)
65 \ : MakeSnip(a:ft, keyword, text)
68 fun! ExtractSnipsFile(file, ft)
69 if !filereadable(a:file) | return | endif
70 let text = readfile(a:file)
72 for line in text + ["\n"]
73 if inSnip && (line[0] == "\t" || line == '')
74 let content .= strpart(line, 1)."\n"
77 call MakeSnip(a:ft, trigger, content[:-2], name)
81 if line[:6] == 'snippet'
83 let trigger = strpart(line, 8)
85 let space = stridx(trigger, ' ') + 1
86 if space " Process multi snip
87 let name = strpart(trigger, space)
88 let trigger = strpart(trigger, 0, space - 1)
96 let s:snippets = {} | let s:multi_snips = {} | let g:did_ft = {}
100 fun! GetSnippets(dir, filetypes)
101 for ft in split(a:filetypes, '\.')
102 if has_key(g:did_ft, ft) | continue | endif
103 call s:DefineSnips(a:dir, ft, ft)
104 if ft == 'objc' || ft == 'cpp' || ft == 'cs'
105 call s:DefineSnips(a:dir, 'c', ft)
107 call s:DefineSnips(a:dir, 'html', 'xhtml')
113 " Define "aliasft" snippets for the filetype "realft".
114 fun s:DefineSnips(dir, aliasft, realft)
115 for path in split(globpath(a:dir, a:aliasft.'/')."\n".
116 \ globpath(a:dir, a:aliasft.'-*/'), "\n")
117 call ExtractSnips(path, a:realft)
119 for path in split(globpath(a:dir, a:aliasft.'.snippets')."\n".
120 \ globpath(a:dir, a:aliasft.'-*.snippets'), "\n")
121 call ExtractSnipsFile(path, a:realft)
125 fun! TriggerSnippet()
126 if exists('g:SuperTabMappingForward')
127 if g:SuperTabMappingForward == "<tab>"
128 let SuperTabKey = "\<c-n>"
129 elseif g:SuperTabMappingBackward == "<tab>"
130 let SuperTabKey = "\<c-p>"
134 if pumvisible() " Update snippet if completion is used, or deal with supertab
135 if exists('SuperTabKey')
136 call feedkeys(SuperTabKey) | return ''
138 call feedkeys("\<esc>a", 'n') " Close completion menu
139 call feedkeys("\<tab>") | return ''
142 if exists('g:snipPos') | return snipMate#jumpTabStop(0) | endif
144 let word = matchstr(getline('.'), '\S\+\%'.col('.').'c')
145 for scope in [bufnr('%')] + split(&ft, '\.') + ['_']
146 let [trigger, snippet] = s:GetSnippet(word, scope)
147 " If word is a trigger for a snippet, delete the trigger & expand
150 let col = col('.') - len(trigger)
151 sil exe 's/\V'.escape(trigger, '/.').'\%#//'
152 return snipMate#expandSnip(snippet, col)
156 if exists('SuperTabKey')
157 call feedkeys(SuperTabKey)
163 fun! BackwardsSnippet()
164 if exists('g:snipPos') | return snipMate#jumpTabStop(1) | endif
166 if exists('g:SuperTabMappingForward')
167 if g:SuperTabMappingBackward == "<s-tab>"
168 let SuperTabKey = "\<c-p>"
169 elseif g:SuperTabMappingForward == "<s-tab>"
170 let SuperTabKey = "\<c-n>"
173 if exists('SuperTabKey')
174 call feedkeys(SuperTabKey)
180 " Check if word under cursor is snippet trigger; if it isn't, try checking if
181 " the text after non-word characters is (e.g. check for "foo" in "bar.foo")
182 fun s:GetSnippet(word, scope)
183 let word = a:word | let snippet = ''
185 if exists('s:snippets["'.a:scope.'"]["'.escape(word, '\"').'"]')
186 let snippet = s:snippets[a:scope][word]
187 elseif exists('s:multi_snips["'.a:scope.'"]["'.escape(word, '\"').'"]')
188 let snippet = s:ChooseSnippet(a:scope, word)
189 if snippet == '' | break | endif
191 if match(word, '\W') == -1 | break | endif
192 let word = substitute(word, '.\{-}\W', '', '')
195 if word == '' && a:word != '.' && stridx(a:word, '.') != -1
196 let [word, snippet] = s:GetSnippet('.', a:scope)
198 return [word, snippet]
201 fun s:ChooseSnippet(scope, trigger)
204 for snip in s:multi_snips[a:scope][a:trigger]
205 let snippet += [i.'. '.snip[0]]
208 if i == 2 | return s:multi_snips[a:scope][a:trigger][0][1] | endif
209 let num = inputlist(snippet) - 1
210 return num == -1 ? '' : s:multi_snips[a:scope][a:trigger][num][1]
213 fun! ShowAvailableSnips()
214 let line = getline('.')
216 let word = matchstr(getline('.'), '\S\+\%'.col.'c')
219 let words += split(word, '\.', 1)
223 for scope in [bufnr('%')] + split(&ft, '\.') + ['_']
224 let triggers = has_key(s:snippets, scope) ? keys(s:snippets[scope]) : []
225 if has_key(s:multi_snips, scope)
226 let triggers += keys(s:multi_snips[scope])
228 for trigger in triggers
231 let matches += [trigger] " Show all matches if word is empty
232 elseif trigger =~ '^'.word
233 let matches += [trigger]
235 if len > matchlen | let matchlen = len | endif
241 " This is to avoid a bug with Vim when using complete(col - matchlen, matches)
242 " (Issue#46 on the Google Code snipMate issue tracker).
243 call setline(line('.'), substitute(line, repeat('.', matchlen).'\%'.col.'c', '', ''))
244 call complete(col, matches)
247 " vim:noet:sw=4:ts=4:ft=vim