" File: snipMate.vim " Author: Michael Sanders " Last Updated: July 13, 2009 " Version: 0.83 " Description: snipMate.vim implements some of TextMate's snippets features in " Vim. A snippet is a piece of often-typed text that you can " insert into your document using a trigger word followed by a "". " " For more help see snipMate.txt; you can do this by using: " :helptags ~/.vim/doc " :h snipMate.txt if exists('loaded_snips') || &cp || version < 700 finish endif let loaded_snips = 1 if !exists('snips_author') | let snips_author = 'Me' | endif au BufRead,BufNewFile *.snippets\= set ft=snippet au FileType snippet setl noet fdm=indent let s:snippets = {} | let s:multi_snips = {} if !exists('snippets_dir') let snippets_dir = substitute(globpath(&rtp, 'snippets/'), "\n", ',', 'g') endif fun! MakeSnip(scope, trigger, content, ...) let multisnip = a:0 && a:1 != '' let var = multisnip ? 's:multi_snips' : 's:snippets' if !has_key({var}, a:scope) | let {var}[a:scope] = {} | endif if !has_key({var}[a:scope], a:trigger) let {var}[a:scope][a:trigger] = multisnip ? [[a:1, a:content]] : a:content elseif multisnip | let {var}[a:scope][a:trigger] += [[a:1, a:content]] else echom 'Warning in snipMate.vim: Snippet '.a:trigger.' is already defined.' \ .' See :h multi_snip for help on snippets with multiple matches.' endif endf fun! ExtractSnips(dir, ft) for path in split(globpath(a:dir, '*'), "\n") if isdirectory(path) let pathname = fnamemodify(path, ':t') for snipFile in split(globpath(path, '*.snippet'), "\n") call s:ProcessFile(snipFile, a:ft, pathname) endfor elseif fnamemodify(path, ':e') == 'snippet' call s:ProcessFile(path, a:ft) endif endfor endf " Processes a single-snippet file; optionally add the name of the parent " directory for a snippet with multiple matches. fun s:ProcessFile(file, ft, ...) let keyword = fnamemodify(a:file, ':t:r') if keyword == '' | return | endif try let text = join(readfile(a:file), "\n") catch /E484/ echom "Error in snipMate.vim: couldn't read file: ".a:file endtry return a:0 ? MakeSnip(a:ft, a:1, text, keyword) \ : MakeSnip(a:ft, keyword, text) endf fun! ExtractSnipsFile(file, ft) if !filereadable(a:file) | return | endif let text = readfile(a:file) let inSnip = 0 for line in text + ["\n"] if inSnip && (line[0] == "\t" || line == '') let content .= strpart(line, 1)."\n" continue elseif inSnip call MakeSnip(a:ft, trigger, content[:-2], name) let inSnip = 0 endif if line[:6] == 'snippet' let inSnip = 1 let trigger = strpart(line, 8) let name = '' let space = stridx(trigger, ' ') + 1 if space " Process multi snip let name = strpart(trigger, space) let trigger = strpart(trigger, 0, space - 1) endif let content = '' endif endfor endf fun! ResetSnippets() let s:snippets = {} | let s:multi_snips = {} | let g:did_ft = {} endf let g:did_ft = {} fun! GetSnippets(dir, filetypes) for ft in split(a:filetypes, '\.') if has_key(g:did_ft, ft) | continue | endif call s:DefineSnips(a:dir, ft, ft) if ft == 'objc' || ft == 'cpp' || ft == 'cs' call s:DefineSnips(a:dir, 'c', ft) elseif ft == 'xhtml' call s:DefineSnips(a:dir, 'html', 'xhtml') endif let g:did_ft[ft] = 1 endfor endf " Define "aliasft" snippets for the filetype "realft". fun s:DefineSnips(dir, aliasft, realft) for path in split(globpath(a:dir, a:aliasft.'/')."\n". \ globpath(a:dir, a:aliasft.'-*/'), "\n") call ExtractSnips(path, a:realft) endfor for path in split(globpath(a:dir, a:aliasft.'.snippets')."\n". \ globpath(a:dir, a:aliasft.'-*.snippets'), "\n") call ExtractSnipsFile(path, a:realft) endfor endf fun! TriggerSnippet() if exists('g:SuperTabMappingForward') if g:SuperTabMappingForward == "" let SuperTabKey = "\" elseif g:SuperTabMappingBackward == "" let SuperTabKey = "\" endif endif if pumvisible() " Update snippet if completion is used, or deal with supertab if exists('SuperTabKey') call feedkeys(SuperTabKey) | return '' endif call feedkeys("\a", 'n') " Close completion menu call feedkeys("\") | return '' endif if exists('g:snipPos') | return snipMate#jumpTabStop(0) | endif let word = matchstr(getline('.'), '\S\+\%'.col('.').'c') for scope in [bufnr('%')] + split(&ft, '\.') + ['_'] let [trigger, snippet] = s:GetSnippet(word, scope) " If word is a trigger for a snippet, delete the trigger & expand " the snippet. if snippet != '' let col = col('.') - len(trigger) sil exe 's/\V'.escape(trigger, '/.').'\%#//' return snipMate#expandSnip(snippet, col) endif endfor if exists('SuperTabKey') call feedkeys(SuperTabKey) return '' endif return "\" endf fun! BackwardsSnippet() if exists('g:snipPos') | return snipMate#jumpTabStop(1) | endif if exists('g:SuperTabMappingForward') if g:SuperTabMappingBackward == "" let SuperTabKey = "\" elseif g:SuperTabMappingForward == "" let SuperTabKey = "\" endif endif if exists('SuperTabKey') call feedkeys(SuperTabKey) return '' endif return "\" endf " Check if word under cursor is snippet trigger; if it isn't, try checking if " the text after non-word characters is (e.g. check for "foo" in "bar.foo") fun s:GetSnippet(word, scope) let word = a:word | let snippet = '' while snippet == '' if exists('s:snippets["'.a:scope.'"]["'.escape(word, '\"').'"]') let snippet = s:snippets[a:scope][word] elseif exists('s:multi_snips["'.a:scope.'"]["'.escape(word, '\"').'"]') let snippet = s:ChooseSnippet(a:scope, word) if snippet == '' | break | endif else if match(word, '\W') == -1 | break | endif let word = substitute(word, '.\{-}\W', '', '') endif endw if word == '' && a:word != '.' && stridx(a:word, '.') != -1 let [word, snippet] = s:GetSnippet('.', a:scope) endif return [word, snippet] endf fun s:ChooseSnippet(scope, trigger) let snippet = [] let i = 1 for snip in s:multi_snips[a:scope][a:trigger] let snippet += [i.'. '.snip[0]] let i += 1 endfor if i == 2 | return s:multi_snips[a:scope][a:trigger][0][1] | endif let num = inputlist(snippet) - 1 return num == -1 ? '' : s:multi_snips[a:scope][a:trigger][num][1] endf fun! ShowAvailableSnips() let line = getline('.') let col = col('.') let word = matchstr(getline('.'), '\S\+\%'.col.'c') let words = [word] if stridx(word, '.') let words += split(word, '\.', 1) endif let matchlen = 0 let matches = [] for scope in [bufnr('%')] + split(&ft, '\.') + ['_'] let triggers = has_key(s:snippets, scope) ? keys(s:snippets[scope]) : [] if has_key(s:multi_snips, scope) let triggers += keys(s:multi_snips[scope]) endif for trigger in triggers for word in words if word == '' let matches += [trigger] " Show all matches if word is empty elseif trigger =~ '^'.word let matches += [trigger] let len = len(word) if len > matchlen | let matchlen = len | endif endif endfor endfor endfor " This is to avoid a bug with Vim when using complete(col - matchlen, matches) " (Issue#46 on the Google Code snipMate issue tracker). call setline(line('.'), substitute(line, repeat('.', matchlen).'\%'.col.'c', '', '')) call complete(col, matches) return '' endf " vim:noet:sw=4:ts=4:ft=vim