Rainbow
[my-vim-dotfolder.git] / plugin / snipMate.vim
blob3efee2a4916153600fa1adf52f632be162730fee
1 " File:          snipMate.vim
2 " Author:        Michael Sanders
3 " Last Updated:  July 13, 2009
4 " Version:       0.83
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
11 "                :h snipMate.txt
13 if exists('loaded_snips') || &cp || version < 700
14         finish
15 endif
16 let loaded_snips = 1
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')
26 endif
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]]
35         else
36                 echom 'Warning in snipMate.vim: Snippet '.a:trigger.' is already defined.'
37                                 \ .' See :h multi_snip for help on snippets with multiple matches.'
38         endif
39 endf
41 fun! ExtractSnips(dir, ft)
42         for path in split(globpath(a:dir, '*'), "\n")
43                 if isdirectory(path)
44                         let pathname = fnamemodify(path, ':t')
45                         for snipFile in split(globpath(path, '*.snippet'), "\n")
46                                 call s:ProcessFile(snipFile, a:ft, pathname)
47                         endfor
48                 elseif fnamemodify(path, ':e') == 'snippet'
49                         call s:ProcessFile(path, a:ft)
50                 endif
51         endfor
52 endf
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
59         try
60                 let text = join(readfile(a:file), "\n")
61         catch /E484/
62                 echom "Error in snipMate.vim: couldn't read file: ".a:file
63         endtry
64         return a:0 ? MakeSnip(a:ft, a:1, text, keyword)
65                         \  : MakeSnip(a:ft, keyword, text)
66 endf
68 fun! ExtractSnipsFile(file, ft)
69         if !filereadable(a:file) | return | endif
70         let text = readfile(a:file)
71         let inSnip = 0
72         for line in text + ["\n"]
73                 if inSnip && (line[0] == "\t" || line == '')
74                         let content .= strpart(line, 1)."\n"
75                         continue
76                 elseif inSnip
77                         call MakeSnip(a:ft, trigger, content[:-2], name)
78                         let inSnip = 0
79                 endif
81                 if line[:6] == 'snippet'
82                         let inSnip = 1
83                         let trigger = strpart(line, 8)
84                         let name = ''
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)
89                         endif
90                         let content = ''
91                 endif
92         endfor
93 endf
95 fun! ResetSnippets()
96         let s:snippets = {} | let s:multi_snips = {} | let g:did_ft = {}
97 endf
99 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)
106                 elseif ft == 'xhtml'
107                         call s:DefineSnips(a:dir, 'html', 'xhtml')
108                 endif
109                 let g:did_ft[ft] = 1
110         endfor
111 endf
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)
118         endfor
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)
122         endfor
123 endf
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>"
131                 endif
132         endif
134         if pumvisible() " Update snippet if completion is used, or deal with supertab
135                 if exists('SuperTabKey')
136                         call feedkeys(SuperTabKey) | return ''
137                 endif
138                 call feedkeys("\<esc>a", 'n') " Close completion menu
139                 call feedkeys("\<tab>") | return ''
140         endif
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
148                 " the snippet.
149                 if snippet != ''
150                         let col = col('.') - len(trigger)
151                         sil exe 's/\V'.escape(trigger, '/.').'\%#//'
152                         return snipMate#expandSnip(snippet, col)
153                 endif
154         endfor
156         if exists('SuperTabKey')
157                 call feedkeys(SuperTabKey)
158                 return ''
159         endif
160         return "\<tab>"
161 endf
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>"
171                 endif
172         endif
173         if exists('SuperTabKey')
174                 call feedkeys(SuperTabKey)
175                 return ''
176         endif
177         return "\<s-tab>"
178 endf
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 = ''
184         while 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
190                 else
191                         if match(word, '\W') == -1 | break | endif
192                         let word = substitute(word, '.\{-}\W', '', '')
193                 endif
194         endw
195         if word == '' && a:word != '.' && stridx(a:word, '.') != -1
196                 let [word, snippet] = s:GetSnippet('.', a:scope)
197         endif
198         return [word, snippet]
199 endf
201 fun s:ChooseSnippet(scope, trigger)
202         let snippet = []
203         let i = 1
204         for snip in s:multi_snips[a:scope][a:trigger]
205                 let snippet += [i.'. '.snip[0]]
206                 let i += 1
207         endfor
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]
211 endf
213 fun! ShowAvailableSnips()
214         let line  = getline('.')
215         let col   = col('.')
216         let word  = matchstr(getline('.'), '\S\+\%'.col.'c')
217         let words = [word]
218         if stridx(word, '.')
219                 let words += split(word, '\.', 1)
220         endif
221         let matchlen = 0
222         let matches = []
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])
227                 endif
228                 for trigger in triggers
229                         for word in words
230                                 if word == ''
231                                         let matches += [trigger] " Show all matches if word is empty
232                                 elseif trigger =~ '^'.word
233                                         let matches += [trigger]
234                                         let len = len(word)
235                                         if len > matchlen | let matchlen = len | endif
236                                 endif
237                         endfor
238                 endfor
239         endfor
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)
245         return ''
246 endf
247 " vim:noet:sw=4:ts=4:ft=vim