snipMate.vim 7.8 KB

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