diff options
Diffstat (limited to 'vim/bundle/vim-snipmate')
21 files changed, 2854 insertions, 0 deletions
diff --git a/vim/bundle/vim-snipmate/.gitignore b/vim/bundle/vim-snipmate/.gitignore new file mode 100644 index 0000000..9140e6a --- /dev/null +++ b/vim/bundle/vim-snipmate/.gitignore @@ -0,0 +1,3 @@ +doc/tags +*.swp +.DS_Store diff --git a/vim/bundle/vim-snipmate/Contributors.md b/vim/bundle/vim-snipmate/Contributors.md new file mode 100644 index 0000000..4206472 --- /dev/null +++ b/vim/bundle/vim-snipmate/Contributors.md @@ -0,0 +1,52 @@ +# Contributors # + +SnipMate was originally authored by Michael Sanders +([Vim](http://www.vim.org/account/profile.php?user_id=16544), +[GitHub](https://github.com/msanders)). + +It is currently maintained by [Rok Garbas](rok@garbas.si), [Marc +Weber](marco-oweber@gmx.de), and [Adnan Zafar](https://github.com/ajzafar) with +additional contributions from: + +* [907th](https://github.com/907th) +* [adkron](https://github.com/adkron) +* [alderz](https://github.com/alderz) +* [asymmetric](https://github.com/asymmetric) +* [bpugh](https://github.com/bpugh) +* [bruno-](https://github.com/bruno-) +* [CharlesGueunet](https://github.com/CharlesGueunet) +* [darkwise](https://github.com/darkwise) +* [dreviejo](https://github.com/dreviejo) +* [fish-face](https://github.com/fish-face) +* [henrik](https://github.com/henrik) +* [holizz](https://github.com/holizz) +* [honza](https://github.com/honza) +* [hpesoj](https://github.com/hpesoj) +* [ironcamel](https://github.com/ironcamel) +* [jb55](https://github.com/jb55) +* [jbernard](https://github.com/jbernard) +* [jherdman](https://github.com/jherdman) +* [kozo2](https://github.com/kozo2) +* [lilydjwg](https://github.com/lilydjwg) +* [lpil](https://github.com/lpil) +* [marutanm](https://github.com/marutanm) +* [MicahElliott](https://github.com/MicahElliott) +* [mikeastock](https://github.com/mikeastock) +* [muffinresearch](https://github.com/muffinresearch) +* [munyari](https://github.com/munyari) +* [pielgrzym](https://github.com/pielgrzym) +* [pose](https://github.com/pose) +* [r00k](https://github.com/r00k) +* [radicalbit](https://github.com/radicalbit) +* [redpill](https://github.com/redpill) +* [rglassett](http://github.com/rglassett) +* [robhudson](https://github.com/robhudson) +* [shinymayhem](https://github.com/shinymayhem) +* [Shraymonks](https://github.com/shraymonks) +* [sickill](https://github.com/sickill) +* [statik](https://github.com/statik) +* [steveno](https://github.com/steveno) +* [taq](https://github.com/taq) +* [thisgeek](https://github.com/thisgeek) +* [trusktr](https://github.com/trusktr) +* [Xandaros](https://github.com/Xandaros) diff --git a/vim/bundle/vim-snipmate/README.md b/vim/bundle/vim-snipmate/README.md new file mode 100644 index 0000000..b47b56d --- /dev/null +++ b/vim/bundle/vim-snipmate/README.md @@ -0,0 +1,169 @@ +# SnipMate # + +SnipMate aims to provide support for textual snippets, similar to TextMate or +other Vim plugins like [UltiSnips][ultisnips]. For +example, in C, typing `for<tab>` could be expanded to + + for (i = 0; i < count; i++) { + /* code */ + } + +with successive presses of tab jumping around the snippet. + +Originally authored by [Michael Sanders][msanders], SnipMate was forked in 2011 +after a stagnation in development. This fork is currently maintained by [Rok +Garbas][garbas], [Marc Weber][marcweber], and [Adnan Zafar][ajzafar]. + + +## Installing SnipMate ## + +We recommend one of the following methods for installing SnipMate and its +dependencies. SnipMate depends on [vim-addon-mw-utils][mw-utils] and +[tlib][tlib]. + +> **NOTE:** SnipMate does not ship with any snippets out of the box. We suggest +looking at the [vim-snippets][vim-snippets] repository. + +* Using [VAM][vam], add `vim-snippets` to the list of packages to be installed. + +* Using [Pathogen][pathogen], run the following commands: + + % cd ~/.vim/bundle + % git clone https://github.com/tomtom/tlib_vim.git + % git clone https://github.com/MarcWeber/vim-addon-mw-utils.git + % git clone https://github.com/garbas/vim-snipmate.git + + # Optional: + % git clone https://github.com/honza/vim-snippets.git + +* Using [Vundle][vundle], add the following to your `vimrc` then run + `:PluginInstall` + + Plugin 'MarcWeber/vim-addon-mw-utils' + Plugin 'tomtom/tlib_vim' + Plugin 'garbas/vim-snipmate' + + " Optional: + Plugin 'honza/vim-snippets' + +## FAQ ## + +> SnipMate doesn't work / My snippets aren't triggering + +Try all of the following: + +* Check that SnipMate is loaded. This can be done by looking for + `<Plug>snipMateTrigger` and similar maps in the output of `:imap`. + Additionally make sure either `<Plug>snipMateTrigger` or + `<Plug>snipMateNextOrTrigger` is mapped to the key you expect. + +* Check that the snippets file you mean to use exists, and that it contains the + snippet you're trying to expand. + +* Check that your snippets file is located inside a `foo/snippets` directory, + where `foo` is a path listed in your `runtimepath`. + +* Check that your snippets file is in scope by either the filetype matching the + path of the snippet file or the scope explicitly loaded. + +* Check if any snippets from your snippets file are available. This can be done + with the "show available snips" map, by default bound to `<C-R><Tab>` in + insert mode. + +If all of the above check out, please open an issue stating your Vim version, +a sample snippet, and a description of exactly what happens when you try to +trigger a snippet. + +> How does SnipMate determine which snippets to load? How can I separate, for +> example, my Rails snippets from my Ruby snippets? + +Primarily SnipMate looks at the `'filetype'` and `'syntax'` settings. Taking +"scopes" from these options, it looks in each `snippets/` directory in +`'runtimepath'` for files named `scope.snippets`, `scope/*.snippets`, or +`scope_*.snippets`. + +However we understand this may not allow for the flexibility desired by some +languages. For this we provide two options: scope aliases and the +`:SnipMateLoadScope` command. Scope aliases simply say "whenever this scope is +loaded, also load this other scope: + + let g:snipMate = get(g:, 'snipMate', {}) " Allow for vimrc re-sourcing + let g:snipMate.scope_aliases = {} + let g:snipMate.scope_aliases['ruby'] = 'ruby,rails' + +will load the `ruby-rails` scope whenever the `ruby` scope is active. The +`:SnipMateLoadScope foo` command will always load the foo scope in the current +buffer. The [vim-rails](https://github.com/tpope/vim-rails) plugin automatically +does `:SnipMateLoadScope rails` when editing a Rails project for example. + +## Release Notes ## + +### 0.89 - 2016-05-29 ### + +* Various regex updates to legacy parser +* Addition of double bang syntax to completely remove a snippet from lookup +* Group various SnipMate autocommands +* Support setting 'shiftwidth' to 0 +* Parser now operates linewise, adding some flexibility +* Mirror substitutions are more literal +* Mirror length is calculated correctly when substitutions occur + +### 0.88 - 2015-04-04 ### + +* Implement simple caching +* Remove expansion guards +* Add `:SnipMateLoadScope` command and buffer-local scope aliases +* Load `<scope>_*.snippets` files +* Use CursorMoved autocmd events entirely + +* The nested branch has been merged + * A new snippet parser has been added. The g:snipmate.version as well as + version lines in snippet files determines which is used + * The new parser supports tab stops placed within placeholders, + substitutions, non-consecutive stop numbers, and fewer ambiguities + * The stop jumping code has been updated + * Tests have been added for the jumping code and the new parser + +* The override branch has been merged + * The g:snipMate.override option is added. When enabled, if two snippets + share the same name, the later-loaded one is kept and the other discarded + * Override behavior can be enabled on a per-snippet basis with a bang (!) in + the snippet file + * Otherwise, SnipMate tries to preserve all snippets loaded + +* Fix bug with mirrors in the first column +* Fix bug with tabs in indents ([#143][143]) +* Fix bug with mirrors in placeholders +* Fix reading single snippet files +* Fix the use of the visual map at the end of a line +* Fix expansion of stops containing only the zero tab stop +* Remove select mode mappings +* Indent visual placeholder expansions and remove extraneous lines ([#177][177] + and [#178][178]) + +### 0.87 - 2014-01-04 ### + +* Stop indenting empty lines when expanding snippets +* Support extends keyword in .snippets files +* Fix visual placeholder support +* Add zero tabstop support +* Support negative 'softtabstop' +* Add g:snipMate_no_default_aliases option +* Add <Plug>snipMateTrigger for triggering an expansion inside a snippet +* Add snipMate#CanBeTriggered() function + +[ultisnips]: https://github.com/sirver/ultisnips +[msanders]: https://github.com/msanders +[garbas]: https://github.com/garbas +[marcweber]: https://github.com/marcweber +[ajzafar]: https://github.com/ajzafar +[mw-utils]: https://github.com/marcweber/vim-addon-mw-utils +[tlib]: https://github.com/tomtom/tlib_vim +[vim-snippets]: https://github.com/honza/vim-snippets +[vam]: https://github.com/marcweber/vim-addon-manager +[pathogen]: https://github.com/tpope/vim-pathogen +[vundle]: https://github.com/gmarik/vundle + +[143]: https://github.com/garbas/vim-snipmate/issues/143 +[177]: https://github.com/garbas/vim-snipmate/issues/177 +[178]: https://github.com/garbas/vim-snipmate/issues/178 diff --git a/vim/bundle/vim-snipmate/addon-info.json b/vim/bundle/vim-snipmate/addon-info.json new file mode 100644 index 0000000..8d9930f --- /dev/null +++ b/vim/bundle/vim-snipmate/addon-info.json @@ -0,0 +1,12 @@ +{ + "name" : "snipMate", + "version" : "dev", + "author" : "Michael Sanders -> original project http://github.com/msanders/snipmate.vim", + "maintainer" : "Rok Garbas / Marc Weber", + "repository" : {"type": "git", "url": "git://github.com/garbas/vim-snipmate.git"}, + "dependencies" : { + "vim-addon-mw-utils": {}, + "tlib": {} + }, + "description" : "snipMate.vim aims to be a concise vim script that implements some of TextMate's snippets features in Vim. See README.md to learn about the features this fork adds" +} diff --git a/vim/bundle/vim-snipmate/after/plugin/snipMate.vim b/vim/bundle/vim-snipmate/after/plugin/snipMate.vim new file mode 100644 index 0000000..3d6bac7 --- /dev/null +++ b/vim/bundle/vim-snipmate/after/plugin/snipMate.vim @@ -0,0 +1,46 @@ +" snipMate maps +" These maps are created here in order to make sure we can reliably create maps +" after SuperTab. + +let s:save_cpo = &cpo +set cpo&vim + +function! s:map_if_not_mapped(lhs, rhs, mode) abort + let l:unique = s:overwrite ? '' : ' <unique>' + if !hasmapto(a:rhs, a:mode) + silent! exe a:mode . 'map' . l:unique a:lhs a:rhs + endif +endfunction + +if !exists('g:snips_no_mappings') || !g:snips_no_mappings + if exists('g:snips_trigger_key') + echom 'g:snips_trigger_key is deprecated. See :h snipMate-mappings' + exec 'imap <unique>' g:snips_trigger_key '<Plug>snipMateTrigger' + exec 'smap <unique>' g:snips_trigger_key '<Plug>snipMateSNext' + exec 'xmap <unique>' g:snips_trigger_key '<Plug>snipMateVisual' + else + " Remove SuperTab map if it exists + let s:overwrite = maparg('<Tab>', 'i') ==? '<Plug>SuperTabForward' + call s:map_if_not_mapped('<Tab>', '<Plug>snipMateNextOrTrigger', 'i') + call s:map_if_not_mapped('<Tab>', '<Plug>snipMateNextOrTrigger', 's') + let s:overwrite = 0 + call s:map_if_not_mapped('<Tab>', '<Plug>snipMateVisual', 'x') + endif + + if exists('g:snips_trigger_key_backwards') + echom 'g:snips_trigger_key_backwards is deprecated. See :h snipMate-mappings' + exec 'imap <unique>' g:snips_trigger_key_backwards '<Plug>snipMateIBack' + exec 'smap <unique>' g:snips_trigger_key_backwards '<Plug>snipMateSBack' + else + let s:overwrite = maparg('<S-Tab>', 'i') ==? '<Plug>SuperTabBackward' + call s:map_if_not_mapped('<S-Tab>', '<Plug>snipMateBack', 'i') + call s:map_if_not_mapped('<S-Tab>', '<Plug>snipMateBack', 's') + let s:overwrite = 0 + endif + + call s:map_if_not_mapped('<C-R><Tab>', '<Plug>snipMateShow', 'i') +endif + +let &cpo = s:save_cpo + +" vim:noet: diff --git a/vim/bundle/vim-snipmate/autoload/snipMate.vim b/vim/bundle/vim-snipmate/autoload/snipMate.vim new file mode 100644 index 0000000..74366fa --- /dev/null +++ b/vim/bundle/vim-snipmate/autoload/snipMate.vim @@ -0,0 +1,603 @@ +" config which can be overridden (shared lines) +if !exists('g:snipMate') + let g:snipMate = {} +endif + +try + call tlib#input#List('mi', '', []) +catch /.*/ + echoe "you're missing tlib. See install instructions at ".expand('<sfile>:h:h').'/README.md' +endtry + +fun! Filename(...) abort + let filename = expand('%:t:r') + if filename == '' | return a:0 == 2 ? a:2 : '' | endif + return !a:0 || a:1 == '' ? filename : substitute(a:1, '$1', filename, 'g') +endf + +let s:cache = {} + +function! snipMate#expandSnip(snip, version, col) abort + let lnum = line('.') + let col = a:col + let line = getline(lnum) + let indent = match(line, '\S\|$') + 1 + let b:snip_state = snipmate#jumping#state() + + if a:version == 1 + let [snippet, b:snip_state.stops] = snipmate#parse#snippet(a:snip) + " Build stop/mirror info + let b:snip_state.stop_count = s:build_stops(snippet, b:snip_state.stops, lnum, col, indent) + let snipLines = map(copy(snippet), + \ 'snipMate#sniplist_str(v:val, b:snip_state.stops)') + else + let snippet = snipmate#legacy#process_snippet(a:snip) + let [b:snip_state.stops, b:snip_state.stop_count] = snipmate#legacy#build_stops(snippet, lnum, col - indent, indent) + let snipLines = split(substitute(snippet, printf('%s\d\+\|%s{\d\+.\{-}}', + \ g:snipmate#legacy#sigil, g:snipmate#legacy#sigil), '', 'g'), "\n", 1) + endif + + " Abort if the snippet is empty + if empty(snippet) + return '' + endif + + " Expand snippet onto current position + let afterCursor = strpart(line, col - 1) + " Keep text after the cursor + if afterCursor != "\t" && afterCursor != ' ' + let line = strpart(line, 0, col - 1) + let snipLines[-1] .= afterCursor + else + let afterCursor = '' + " For some reason the cursor needs to move one right after this + if line != '' && col == 1 && &ve != 'all' && &ve != 'onemore' + let col += 1 + endif + endif + + " Insert snippet with proper indentation + call setline(lnum, line . snipLines[0]) + call append(lnum, map(snipLines[1:], "empty(v:val) ? v:val : '" . strpart(line, 0, indent - 1) . "' . v:val")) + + " Open any folds snippet expands into + if &foldenable + silent! exec lnum . ',' . (lnum + len(snipLines) - 1) . 'foldopen' + endif + + aug snipmate_changes + au CursorMoved,CursorMovedI <buffer> if exists('b:snip_state') | + \ call b:snip_state.update_changes() | + \ else | + \ silent! au! snipmate_changes * <buffer> | + \ endif + aug END + + let b:snip_state.stop_no = 0 + return b:snip_state.set_stop(0) +endfunction + +function! snipMate#placeholder_str(num, stops) abort + return snipMate#sniplist_str(a:stops[a:num].placeholder, a:stops) +endfunction + +function! snipMate#sniplist_str(snippet, stops) abort + let str = '' + let pos = 0 + let add_to = 1 + let seen_stops = [] + + while pos < len(a:snippet) + let item = a:snippet[pos] + + if type(item) == type('') + let str .= item + elseif type(item) == type([]) + let str .= snipMate#placeholder_str(item[0], a:stops) + endif + + let pos += 1 + unlet item " avoid E706 + endwhile + + return str +endfunction + +function! s:build_stops(snippet, stops, lnum, col, indent) abort + let stops = a:stops + let lnum = a:lnum + let col = a:col + + for line in a:snippet + let col = s:build_loc_info(line, stops, lnum, col, []) + if line isnot a:snippet[-1] + let lnum += 1 + let col = a:indent + endif + endfor + + " add zero tabstop if it doesn't exist and then link it to the highest stop + " number + let stops[0] = get(stops, 0, + \ { 'placeholder' : [], 'line' : lnum, 'col' : col }) + let stop_count = max(keys(stops)) + 2 + let stops[stop_count - 1] = stops[0] + + return stop_count +endfunction + +function! s:build_loc_info(snippet, stops, lnum, col, seen_items) abort + let stops = a:stops + let lnum = a:lnum + let col = a:col + let pos = 0 + let in_text = 0 + let seen_items = a:seen_items + + for item in a:snippet + if type(item) == type('') + let col += len(item) + elseif type(item) == type([]) + let id = item[0] + let stub = item[-1] + let stub.line = lnum + let stub.col = col + call s:add_update_objects(stub, seen_items) + + if len(item) > 2 && type(item[1]) != type({}) + let col = s:build_loc_info(item[1:-2], stops, lnum, col, seen_items) + else + let col += len(snipMate#placeholder_str(id, stops)) + endif + + let in_text = 0 + endif + unlet item " avoid E706 + endfor + + return col +endfunction + +function! s:add_update_objects(object, targets) abort + let targets = a:targets + + for item in targets + let item.update_objects = get(item, 'update_objects', []) + call add(item.update_objects, a:object) + endfor + + call add(targets, a:object) +endfunction + +" reads a .snippets file +" returns list of +" ['triggername', 'name', 'contents'] +" if triggername is not set 'default' is assumed +" TODO: better error checking +fun! snipMate#ReadSnippetsFile(file) abort + let result = [] + let new_scopes = [] + if !filereadable(a:file) | return [result, new_scopes] | endif + let inSnip = 0 + let line_no = 0 + let snipversion = get(g:snipMate, 'snippet_version', 0) + for line in readfile(a:file) + ["\n"] + let line_no += 1 + + if inSnip && (line[0] == "\t" || line == '') + let content .= strpart(line, 1)."\n" + continue + elseif inSnip + call add(result, [trigger, name, + \ content[:-2], bang, snipversion]) + let inSnip = 0 + endif + + if line[:6] == 'snippet' + let inSnip = 1 + let bang = (line[7] == '!') + if bang + let bang += line[8] == '!' + endif + let trigger = strpart(line, 8 + bang) + 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 = '' + if trigger =~ '^\s*$' " discard snippets with empty triggers + echom 'Invalid snippet in' a:file 'near line' line_no + let inSnip = 0 + endif + elseif line[:6] == 'extends' + call extend(new_scopes, map(split(strpart(line, 8)), + \ "substitute(v:val, ',*$', '', '')")) + elseif line[:6] == 'version' + let snipversion = +strpart(line, 8) + endif + endfor + return [result, new_scopes] +endf + +function! s:GetScopes() abort + let ret = exists('b:snipMate.scope_aliases') ? copy(b:snipMate.scope_aliases) : {} + let global = get(g:snipMate, 'scope_aliases', {}) + for alias in keys(global) + if has_key(ret, alias) + let ret[alias] = join(split(ret[alias], ',') + \ + split(global[alias], ','), ',') + else + let ret[alias] = global[alias] + endif + endfor + return ret +endfunction + +" adds scope aliases to list. +" returns new list +" the aliases of aliases are added recursively +fun! s:AddScopeAliases(list) abort + let did = {} + let scope_aliases = s:GetScopes() + let new = a:list + let new2 = [] + while !empty(new) + for i in new + if !has_key(did, i) + let did[i] = 1 + call extend(new2, split(get(scope_aliases,i,''),',')) + endif + endfor + let new = new2 + let new2 = [] + endwhile + return keys(did) +endf + +augroup SnipMateSource + au SourceCmd *.snippet,*.snippets call s:source_snippet() +augroup END + +function! s:info_from_filename(file) abort + let parts = split(fnamemodify(a:file, ':r'), '/') + let snipidx = len(parts) - index(reverse(copy(parts)), 'snippets') - 1 + let rtp_prefix = join(parts[(snipidx - + \ (parts[snipidx - 1] == 'after' ? 3 : 2)):snipidx - 1], '/') + let trigger = get(parts, snipidx + 2, '') + let desc = get(parts, snipidx + 3, get(g:snipMate, 'override', 0) ? + \ '' : fnamemodify(a:file, ':t')) + return [rtp_prefix, trigger, desc] +endfunction + +function! s:source_snippet() abort + let file = expand('<afile>:p') + let [rtp_prefix, trigger, desc] = s:info_from_filename(file) + let new_snips = [] + if fnamemodify(file, ':e') == 'snippet' + call add(new_snips, [trigger, desc, join(readfile(file), "\n"), 0, + \ get(g:snipMate, 'snippet_version', 0)]) + else + let [snippets, extends] = s:CachedSnips(file) + let new_snips = deepcopy(snippets) + call extend(s:lookup_state.extends, extends) + endif + for snip in new_snips + if get(g:snipMate, 'override', 0) + let snip[1] = join([s:lookup_state.scope, snip[1]]) + else + let snip[1] = join([s:lookup_state.scope, rtp_prefix, + \ empty(snip[1]) ? desc : snip[1]]) + endif + endfor + call extend(s:lookup_state.snips, new_snips) +endfunction + +function! s:CachedSnips(file) abort + let mtime = getftime(a:file) + if has_key(s:cache, a:file) && s:cache[a:file].mtime >= mtime + return s:cache[a:file].contents + endif + let s:cache[a:file] = {} + let s:cache[a:file].mtime = mtime + let s:cache[a:file].contents = snipMate#ReadSnippetsFile(a:file) + return s:cache[a:file].contents +endfunction + +function! s:snippet_filenames(scope, trigger) abort + let mid = ['', '_*', '/*'] + let mid += map(copy(mid), "'/' . a:trigger . '*' . v:val") + call map(mid, "'snippets/' . a:scope . v:val . '.snippet'") + return map(mid[:2], 'v:val . "s"') + mid[3:] +endfunction + +function! snipMate#SetByPath(dict, trigger, path, snippet, bang, snipversion) abort + let d = a:dict + if a:bang == 2 + unlet! d[a:trigger] + return + elseif !has_key(d, a:trigger) || a:bang == 1 + let d[a:trigger] = {} + endif + let d[a:trigger][a:path] = [a:snippet, a:snipversion] +endfunction + +if v:version < 704 || has('win32') + function! s:Glob(path, expr) + let res = [] + for p in split(a:path, ',') + let h = split(fnamemodify(a:expr, ':h'), '/')[0] + if isdirectory(p . '/' . h) + call extend(res, split(glob(p . '/' . a:expr), "\n")) + endif + endfor + return filter(res, 'filereadable(v:val)') + endfunction +else + function! s:Glob(path, expr) + return split(globpath(a:path, a:expr), "\n") + endfunction +endif + +" default triggers based on paths +function! snipMate#DefaultPool(scopes, trigger, result) abort + let scopes = s:AddScopeAliases(a:scopes) + let scopes_done = [] + let s:lookup_state = {} + let s:lookup_state.snips = [] + + while !empty(scopes) + let scope = remove(scopes, 0) + let s:lookup_state.scope = scope + let s:lookup_state.extends = [] + + for expr in s:snippet_filenames(scope, escape(a:trigger, "*[]?{}`'$|#%")) + for path in g:snipMate.snippet_dirs + for file in s:Glob(path, expr) + source `=file` + endfor + endfor + endfor + + call add(scopes_done, scope) + call extend(scopes, s:lookup_state.extends) + call filter(scopes, 'index(scopes_done, v:val) == -1') + endwhile + + for [trigger, desc, contents, bang, snipversion] in s:lookup_state.snips + if trigger =~ '\V\^' . escape(a:trigger, '\') + call snipMate#SetByPath(a:result, trigger, desc, contents, bang, snipversion) + endif + endfor +endfunction + +" return a dict of snippets found in runtimepath matching trigger +" scopes: list of scopes. usually this is the filetype. eg ['c','cpp'] +" trigger may contain glob patterns. Thus use '*' to get all triggers +" +fun! snipMate#GetSnippets(scopes, trigger) abort + let result = {} + + for F in values(g:snipMateSources) + call funcref#Call(F, [a:scopes, a:trigger, result]) + endfor + return result +endf + +function! snipMate#OpenSnippetFiles() abort + let files = [] + let scopes_done = [] + let exists = [] + let notexists = [] + for scope in s:AddScopeAliases(snipMate#ScopesByFile()) + let files += s:snippet_filenames(scope, '') + endfor + call filter(files, "v:val !~# '\\*'") + for path in g:snipMate.snippet_dirs + let fullpaths = map(copy(files), 'printf("%s/%s", path, v:val)') + let exists += filter(copy(fullpaths), 'filereadable(v:val)') + let notexists += map(filter(copy(fullpaths), + \ 'v:val =~# "\.snippets" && !filereadable(v:val)'), + \ '"does not exist: " . v:val') + endfor + let all = exists + notexists + let select = tlib#input#List('mi', 'select files to be opened in splits', all) + for idx in select + exec 'sp' all[idx - 1] + endfor +endfunction + +fun! snipMate#ScopesByFile() abort + " duplicates are removed in AddScopeAliases + return filter(funcref#Call(g:snipMate.get_scopes), "v:val != ''") +endf + +" used by both: completion and insert snippet +fun! snipMate#GetSnippetsForWordBelowCursor(word, exact) abort + " Split non-word characters into their own piece + " so 'foo.bar..baz' becomes ['foo', '.', 'bar', '.', '.', 'baz'] + " First split just after a \W and then split each resultant string just + " before a \W + let parts = filter(tlib#list#Flatten( + \ map(split(a:word, '\W\zs'), 'split(v:val, "\\ze\\W")')), + \ '!empty(v:val)') + " Only look at the last few possibilities. Too many can be slow. + if len(parts) > 5 + let parts = parts[-5:] + endif + let lookups = [a:word] + let lookup = '' + for w in reverse(parts) + let lookup = w . lookup + if index(lookups, lookup) == -1 + call add(lookups, lookup) + endif + endfor + + " Remove empty lookup entries, but only if there are other nonempty lookups + if len(lookups) > 1 + call filter(lookups, 'v:val != ""') + endif + + let matching_snippets = [] + let snippet = '' + " prefer longest word + for word in lookups + let g:snipMate.word = word + for [k,snippetD] in items(funcref#Call(g:snipMate['get_snippets'], [snipMate#ScopesByFile(), word])) + " hack: require exact match + if a:exact && k !=# word + continue + endif + call add(matching_snippets, [k, snippetD]) + if a:exact + break + endif + endfor + endfor + return matching_snippets +endf + +" snippets: dict containing snippets by name +" usually this is just {'default' : snippet_contents } +fun! s:ChooseSnippet(snippets) abort + let snippet = [] + let keys = keys(a:snippets) + let i = 1 + for snip in keys + let snippet += [i.'. '.snip] + let i += 1 + endfor + if len(snippet) == 1 + " there's only a single snippet, choose it + let idx = 0 + else + let idx = tlib#input#List('si','select snippet by name',snippet) -1 + if idx == -1 + return '' + endif + endif + " if a:snippets[..] is a String Call returns it + " If it's a function or a function string the result is returned + return funcref#Call(a:snippets[keys(a:snippets)[idx]]) +endf + +fun! snipMate#WordBelowCursor() abort + return matchstr(getline('.'), '\S\+\%' . col('.') . 'c') +endf + +fun! snipMate#GetSnippetsForWordBelowCursorForComplete(word) abort + let snippets = map(snipMate#GetSnippetsForWordBelowCursor(a:word, 0), 'v:val[0]') + return filter(snippets, 'v:val =~# "\\V\\^' . escape(a:word, '"\') . '"') +endf + +fun! snipMate#CanBeTriggered() abort + let word = snipMate#WordBelowCursor() + let matches = snipMate#GetSnippetsForWordBelowCursorForComplete(word) + return len(matches) > 0 +endf + +fun! snipMate#ShowAvailableSnips() abort + let col = col('.') + let word = snipMate#WordBelowCursor() + let matches = snipMate#GetSnippetsForWordBelowCursorForComplete(word) + + " Pretty hacky, but really can't have the tab swallowed! + if len(matches) == 0 + call feedkeys(g:snipMate['no_match_completion_feedkeys_chars'], 'n') + return "" + endif + + call complete(col - len(word), sort(matches)) + return '' +endf + +" Pass an argument to force snippet expansion instead of triggering or jumping +function! snipMate#TriggerSnippet(...) abort + if exists('g:SuperTabMappingForward') + if g:SuperTabMappingForward == "<tab>" + let SuperTabPlug = maparg('<Plug>SuperTabForward', 'i') + if SuperTabPlug == "" + let SuperTabKey = "\<c-n>" + else + exec "let SuperTabKey = \"" . escape(SuperTabPlug, '<') . "\"" + endif + elseif g:SuperTabMappingBackward == "<tab>" + let SuperTabPlug = maparg('<Plug>SuperTabBackward', 'i') + if SuperTabPlug == "" + let SuperTabKey = "\<c-p>" + else + exec "let SuperTabKey = \"" . escape(SuperTabPlug, '<') . "\"" + endif + endif + endif + + if pumvisible() " Update snippet if completion is used, or deal with supertab + if exists('SuperTabKey') + call feedkeys(SuperTabKey) | return '' + endif + call feedkeys("\<esc>a", 'n') " Close completion menu + call feedkeys("\<tab>") | return '' + endif + + if exists('b:snip_state') && a:0 == 0 " Jump only if no arguments + let jump = b:snip_state.jump_stop(0) + if type(jump) == 1 " returned a string + return jump + endif + endif + + let word = matchstr(getline('.'), '\S\+\%'.col('.').'c') + let list = snipMate#GetSnippetsForWordBelowCursor(word, 1) + if empty(list) + let snippet = '' + else + let [trigger, snippetD] = list[0] + let snippet = s:ChooseSnippet(snippetD) + " Before expanding snippet, create new undo point |i_CTRL-G| + let &undolevels = &undolevels + let col = col('.') - len(trigger) + sil exe 's/\V'.escape(trigger, '/\.').'\%#//' + return snipMate#expandSnip(snippet[0], snippet[1], col) + endif + + " should allow other plugins to register hooks instead (duplicate code) + if exists('SuperTabKey') + call feedkeys(SuperTabKey) + return '' + endif + return word == '' + \ ? "\<tab>" + \ : "\<c-r>=snipMate#ShowAvailableSnips()\<cr>" +endfunction + +fun! snipMate#BackwardsSnippet() abort + if exists('b:snip_state') | return b:snip_state.jump_stop(1) | endif + + if exists('g:SuperTabMappingForward') + if g:SuperTabMappingForward == "<s-tab>" + let SuperTabPlug = maparg('<Plug>SuperTabForward', 'i') + if SuperTabPlug == "" + let SuperTabKey = "\<c-n>" + else + exec "let SuperTabKey = \"" . escape(SuperTabPlug, '<') . "\"" + endif + elseif g:SuperTabMappingBackward == "<s-tab>" + let SuperTabPlug = maparg('<Plug>SuperTabBackward', 'i') + if SuperTabPlug == "" + let SuperTabKey = "\<c-p>" + else + exec "let SuperTabKey = \"" . escape(SuperTabPlug, '<') . "\"" + endif + endif + endif + " should allow other plugins to register hooks instead (duplicate code) + if exists('SuperTabKey') + call feedkeys(SuperTabKey) + return '' + endif + return "\<s-tab>" +endf + +" vim:noet:sw=4:ts=4:ft=vim diff --git a/vim/bundle/vim-snipmate/autoload/snipMate_python_demo.vim b/vim/bundle/vim-snipmate/autoload/snipMate_python_demo.vim new file mode 100644 index 0000000..de495d2 --- /dev/null +++ b/vim/bundle/vim-snipmate/autoload/snipMate_python_demo.vim @@ -0,0 +1,47 @@ +" This file demonstrates +" - how to register your own snippet sources (call snipMate_python_demo#Activate() in ftplugin/python.vim) +" - implents a source which creates snippets based on python function +" definitions found in the current file +" +" Example: +" +" def abc(a,b,c=None) +" will create a snippet on the fly which looks like this: +" abc(${1:a}, ${2:b}, ${3:c=None}) + +fun! snipMate_python_demo#Activate() abort + if !exists('g:snipMateSources') + let g:snipMateSources = {} + endif + + let g:snipMateSources['python'] = funcref#Function('snipMate_python_demo#FunctionsFromCurrentFileAndTags') +endf + +fun! s:Add(dict, line, source, trigger) abort + let matched = matchlist(a:line,'def\s\+\([^( \t]\+\)[ \t]*(\([^)]*\)') + if len(matched) > 2 + let name = matched[1] + " TODO: is this a glob? + if name !~ a:trigger | return | endif + let a:dict[name] = get(a:dict, name, {}) + let sd = a:dict[name] + let args = [] + let nr=1 + for arg in split(matched[2], '\s*,\s*') + call add(args, '${'.nr.':'.arg.'}') + let nr+=1 + endfor + let sd[a:source] = name.'('.join(args,', ').')' + endif +endf +fun! snipMate_python_demo#FunctionsFromCurrentFileAndTags(scopes, trigger, result) abort + " getting all might be too much + if a:trigger == '*' | return | endif + if index(a:scopes, 'python') < 0 | return | endif + for t in taglist('^'.a:trigger) + call s:Add(a:result, t.cmd, 'tags-' . t.filename, a:trigger) + endfor + for l in getline(0, line('$')) + call s:Add(a:result, l, 'current-file', a:trigger) + endfor +endf diff --git a/vim/bundle/vim-snipmate/autoload/snipmate/jumping.vim b/vim/bundle/vim-snipmate/autoload/snipmate/jumping.vim new file mode 100644 index 0000000..aaf65ab --- /dev/null +++ b/vim/bundle/vim-snipmate/autoload/snipmate/jumping.vim @@ -0,0 +1,228 @@ +function! s:sfile() abort + return expand('<sfile>') +endfunction + +let s:state_proto = {} + +function! snipmate#jumping#state() abort + return copy(s:state_proto) +endfunction + +function! s:listize_mirror(mirrors) abort + return map(copy(a:mirrors), '[v:val.line, v:val.col]') +endfunction + +" Removes snippet state info +function! s:state_remove() dict abort + " Remove all autocmds in group snipmate_changes in the current buffer + unlet! b:snip_state + silent! au! snipmate_changes * <buffer> +endfunction + +function! s:state_find_next_stop(backwards) dict abort + let self.stop_no += a:backwards? -1 : 1 + while !has_key(self.stops, self.stop_no) + if self.stop_no == self.stop_count + let self.stop_no = 0 + endif + if self.stop_no <= 0 && a:backwards + let self.stop_no = self.stop_count - 1 + endif + let self.stop_no += a:backwards? -1 : 1 + endwhile +endfunction + +" Update state information to correspond to the given tab stop +function! s:state_set_stop(backwards) dict abort + call self.find_next_stop(a:backwards) + let self.cur_stop = self.stops[self.stop_no] + let self.stop_len = (type(self.cur_stop.placeholder) == type(0)) + \ ? self.cur_stop.placeholder + \ : len(snipMate#placeholder_str(self.stop_no, self.stops)) + let self.start_col = self.cur_stop.col + let self.end_col = self.start_col + self.stop_len + let self.mirrors = get(self.cur_stop, 'mirrors', []) + let self.old_mirrors = deepcopy(self.mirrors) + call cursor(self.cur_stop.line, self.cur_stop.col) + let self.prev_len = col('$') + let self.changed = 0 + let ret = self.select_word() + if (self.stop_no == 0 || self.stop_no == self.stop_count - 1) && !a:backwards + call self.remove() + endif + return ret +endfunction + +" Jump to the next/previous tab stop +function! s:state_jump_stop(backwards) dict abort + " Update changes just in case + " This seems to be only needed because insert completion does not trigger + " the CursorMovedI event + call self.update_changes() + + " Store placeholder/location changes + let self.cur_stop.col = self.start_col + if self.changed + call self.remove_nested() + unlet! self.cur_stop.placeholder " avoid type error for old parsing version + let self.cur_stop.placeholder = [strpart(getline('.'), + \ self.start_col - 1, self.end_col - self.start_col)] + endif + + return self.set_stop(a:backwards) +endfunction + +function! s:state_remove_nested(...) dict abort + let id = a:0 ? a:1 : self.stop_no + if type(self.stops[id].placeholder) == type([]) + for i in self.stops[id].placeholder + if type(i) == type([]) + if type(i[1]) != type({}) + call self.remove_nested(i[0]) + call remove(self.stops, i[0]) + else + call filter(self.stops[i[0]].mirrors, 'v:val isnot i[1]') + endif + endif + unlet i " Avoid E706 + endfor + endif +endfunction + +" Select the placeholder for the current tab stop +function! s:state_select_word() dict abort + let len = self.stop_len + if !len | return '' | endif + let l = col('.') != 1 ? 'l' : '' + if &sel == 'exclusive' + return "\<esc>".l.'v'.len."l\<c-g>" + endif + return len == 1 ? "\<esc>".l.'gh' : "\<esc>".l.'v'.(len - 1)."l\<c-g>" +endfunction + +" Update the snippet as text is typed. The self.update_mirrors() function does +" the actual work. +" If the cursor moves outside of a placeholder, call self.remove() +function! s:state_update_changes() dict abort + let change_len = col('$') - self.prev_len + let self.changed = self.changed || change_len != 0 + let self.end_col += change_len + let col = col('.') + + if line('.') != self.cur_stop.line || col < self.start_col || col > self.end_col + return self.remove() + endif + + call self.update(self.cur_stop, change_len, change_len) + if !empty(self.mirrors) + call self.update_mirrors(change_len) + endif + + let self.prev_len = col('$') +endfunction + +" Actually update the mirrors for any changed text +function! s:state_update_mirrors(change) dict abort + let newWordLen = self.end_col - self.start_col + let newWord = strpart(getline('.'), self.start_col - 1, newWordLen) + let changeLen = a:change + let curLine = line('.') + let curCol = col('.') + let oldStartSnip = self.start_col + let i = 0 + + for mirror in self.mirrors + for stop in values(filter(copy(self.stops), 'v:key != 0')) + if type(stop.placeholder) == type(0) + if mirror.line == stop.line && mirror.col > stop.col + \ && mirror.col < stop.col + stop.placeholder + let stop.placeholder += changeLen + endif + endif + endfor + + if has_key(mirror, 'oldSize') + " recover the old size deduce the endline + let oldSize = mirror.oldSize + else + " first time, we use the intitial size + let oldSize = strlen(newWord) + endif + + " Split the line into three parts: the mirror, what's before it, and + " what's after it. Then combine them using the new mirror string. + " Subtract one to go from column index to byte index + + let theline = getline(mirror.line) + + " part before the current mirror + let beginline = strpart(theline, 0, mirror.col - 1) + + " current mirror transformation, and save size + let wordMirror= substitute(newWord, get(mirror, 'pat', ''), get(mirror, 'sub', ''), get(mirror, 'flags', '')) + let mirror.oldSize = strlen(wordMirror) + + " end of the line, use the oldSize because with the transformation, + " the size of the mirror can be different from those of the snippet + let endline = strpart(theline, mirror.col + oldSize -1) + + " Update other object on the line + call self.update(mirror, changeLen, mirror.oldSize - oldSize) + + " reconstruct the line + let update = beginline.wordMirror.endline + + call setline(mirror.line, update) + endfor + + " Reposition the cursor in case a var updates on the same line but before + " the current tabstop + if oldStartSnip != self.start_col || mode() == 'i' + call cursor(0, curCol + self.start_col - oldStartSnip) + endif +endfunction + +function! s:state_find_update_objects(item) dict abort + let item = a:item + let item.update_objects = [] + + " Filter the zeroth stop because it's duplicated as the last + for stop in values(filter(copy(self.stops), 'v:key != 0')) + if stop.line == item.line && stop.col > item.col + call add(item.update_objects, stop) + endif + + for mirror in get(stop, 'mirrors', []) + if mirror.line == item.line && mirror.col > item.col + call add(item.update_objects, mirror) + endif + endfor + endfor + + return item.update_objects +endfunction + +function! s:state_update(item, change_len, mirror_change) dict abort + let item = a:item + if !exists('item.update_objects') + let item.update_objects = self.find_update_objects(a:item) + endif + let to_update = item.update_objects + + for obj in to_update + " object does not necessarly have the same decalage + " than mirrors if mirrors use regexp + let obj.col += a:mirror_change + if obj is self.cur_stop + let self.start_col += a:change_len + let self.end_col += a:change_len + endif + endfor +endfunction + +call extend(s:state_proto, snipmate#util#add_methods(s:sfile(), 'state', + \ [ 'remove', 'set_stop', 'jump_stop', 'remove_nested', + \ 'select_word', 'update_changes', 'update_mirrors', + \ 'find_next_stop', 'find_update_objects', 'update' ]), 'error') + +" vim:noet:sw=4:ts=4:ft=vim diff --git a/vim/bundle/vim-snipmate/autoload/snipmate/legacy.vim b/vim/bundle/vim-snipmate/autoload/snipmate/legacy.vim new file mode 100644 index 0000000..7ff39cb --- /dev/null +++ b/vim/bundle/vim-snipmate/autoload/snipmate/legacy.vim @@ -0,0 +1,139 @@ +let s:sigil = nr2char(31) +let snipmate#legacy#sigil = s:sigil + +" Prepare snippet to be processed by s:BuildTabStops +function! snipmate#legacy#process_snippet(snip) abort + let snippet = a:snip + let esc_bslash = '\%(\\\@<!\%(\\\\\)*\)\@<=' + + if exists('b:snipmate_visual') + let visual = substitute(b:snipmate_visual, "\n$", '', '') + unlet b:snipmate_visual + else + let visual = '' + endif + let snippet = s:substitute_visual(snippet, visual) + + " Evaluate eval (`...`) expressions. + " Backquotes prefixed with a backslash "\" are ignored. + " And backslash can be escaped by doubling it. + " Using a loop here instead of a regex fixes a bug with nested "\=". + if stridx(snippet, '`') != -1 + let new = [] + let snip = split(snippet, esc_bslash . '`', 1) + let isexp = 0 + for i in snip + if isexp + call add(new, substitute(snipmate#util#eval(i), + \ "\n\\%$", '', '')) + else + call add(new, i) + endif + let isexp = !isexp + endfor + let snippet = join(new, '') + let snippet = substitute(snippet, "\r", "\n", 'g') + let snippet = substitute(snippet, '\\`', "`", 'g') + endif + + " Place all text after a colon in a tab stop after the tab stop + " (e.g. "${#:foo}" becomes "${:foo}foo"). + " This helps tell the position of the tab stops later. + let snippet = substitute(snippet, esc_bslash . '\$\({\d\+:\(.\{-}\)}\|{\d\+}\)', s:sigil . '\1\2', 'g') + let snippet = substitute(snippet, esc_bslash . '\$\(\d\+\)', s:sigil . '\1', 'g') + let snippet = substitute(snippet, esc_bslash . '\\\$', '$', 'g') + let snippet = substitute(snippet, '\\\\', "\\", 'g') + + " Update the a:snip so that all the $# become the text after + " the colon in their associated ${#}. + " (e.g. "${1:foo}" turns all "$1"'s into "foo") + let i = 0 + if snippet !~ s:sigil . '{0' + let snippet .= s:sigil . '{0}' + endif + while snippet =~ s:sigil.'{'.i + let s = matchstr(snippet, s:sigil . '{' . i . ':\zs.\{-}\ze}') + if s != '' + let snippet = substitute(snippet, s:sigil . i, s.'&', 'g') + endif + let i += 1 + endw + + if &et " Expand tabs to spaces if 'expandtab' is set. + return substitute(snippet, '\t', repeat(' ', snipmate#util#tabwidth()), 'g') + endif + return snippet +endfunction + +" Builds a list of a list of each tab stop in the snippet containing: +" 1.) The tab stop's line number. +" 2.) The tab stop's column number +" (by getting the length of the string between the last "\n" and the +" tab stop). +" 3.) The length of the text after the colon for the current tab stop +" (e.g. "${1:foo}" would return 3). +" 4.) If the "${#:}" construct is given, another list containing all +" the matches of "$#", to be replaced with the placeholder. This list is +" composed the same way as the parent; the first item is the line number, +" and the second is the column. +function! snipmate#legacy#build_stops(snip, lnum, col, indent) abort + let stops = {} + let i = 0 + let withoutVars = substitute(a:snip, s:sigil . '\d\+', '', 'g') + while a:snip =~ s:sigil . '{' . i + let beforeTabStop = matchstr(withoutVars, '^.*\ze'.s:sigil .'{'.i.'\D') + let withoutOthers = substitute(withoutVars, ''.s:sigil .'{\('.i.'\D\)\@!\d\+.\{-}}', '', 'g') + + let stops[i] = {} + let stops[i].line = a:lnum + s:count(beforeTabStop, "\n") + let stops[i].col = a:indent + len(matchstr(withoutOthers, '[^\n]\{-}\ze'.s:sigil .'{'.i.'\D')) + let stops[i].placeholder = 0 + let stops[i].mirrors = [] + if stops[i].line == a:lnum + let stops[i].col += a:col + endif + + " Get all $# matches in another list, if ${#:name} is given + if withoutVars =~ printf('%s{%d:', s:sigil, i) + let stops[i].placeholder = len(matchstr(withoutVars, ''.s:sigil .'{'.i.':\zs.\{-}\ze}')) + let withoutOthers = substitute(a:snip, ''.s:sigil .'{\d\+.\{-}}\|'.s:sigil .''.i.'\@!\d\+', '', 'g') + + while match(withoutOthers, ''.s:sigil .''.i.'\(\D\|$\)') != -1 + let stops[i].mirrors = get(stops[i], 'mirrors', []) + let beforeMark = matchstr(withoutOthers, + \ printf('^.\{-}\ze%s%s%d\(\D\|$\)', + \ repeat('.', stops[i].placeholder), s:sigil, i)) + let line = a:lnum + s:count(beforeMark, "\n") + let col = a:indent + (line > a:lnum + \ ? len(matchstr(beforeMark, '.*\n\zs.*')) + \ : a:col + len(beforeMark)) + call add(stops[i].mirrors, { 'line' : line, 'col' : col }) + let withoutOthers = substitute(withoutOthers, ''.s:sigil .''.i.'\ze\(\D\|$\)', '', '') + endw + endif + let i += 1 + endw + let stops[i] = stops[0] + return [stops, i + 1] +endfunction + +function! s:substitute_visual(snippet, visual) abort + let lines = [] + for line in split(a:snippet, "\n") + let indent = matchstr(line, '^\t\+') + call add(lines, substitute(line, '{VISUAL}', + \ substitute(escape(a:visual, '%\'), "\n", "\n" . indent, 'g'), 'g')) + endfor + return join(lines, "\n") +endfunction + +" Counts occurences of haystack in needle +function! s:count(haystack, needle) abort + let counter = 0 + let index = stridx(a:haystack, a:needle) + while index != -1 + let index = stridx(a:haystack, a:needle, index+1) + let counter += 1 + endw + return counter +endfunction diff --git a/vim/bundle/vim-snipmate/autoload/snipmate/parse.vim b/vim/bundle/vim-snipmate/autoload/snipmate/parse.vim new file mode 100644 index 0000000..dd495e9 --- /dev/null +++ b/vim/bundle/vim-snipmate/autoload/snipmate/parse.vim @@ -0,0 +1,309 @@ +" Snippet definition parsing code + +function! s:sfile() abort + return expand('<sfile>') +endfunction + +let s:parser_proto = {} +let s:special_chars = "$`\n" + +function! s:new_parser(text) abort + let ret = copy(s:parser_proto) + let ret.input = a:text + let ret.len = strlen(ret.input) + let ret.pos = -1 + let ret.indent = 0 + let ret.value = [] + let ret.vars = {} + let ret.stored_lines = [] + call ret.advance() + return ret +endfunction + +function! s:parser_advance(...) dict abort + let self.pos += a:0 ? a:1 : 1 + let self.next = self.input[self.pos] +endfunction + +function! s:parser_same(tok) dict abort + if self.next == a:tok + call self.advance() + return 1 + else + return 0 + endif +endfunction + +function! s:parser_id() dict abort + if self.input[(self.pos):(self.pos+5)] == 'VISUAL' + call self.advance(6) + return 'VISUAL' + elseif self.next =~ '\d' + let end = matchend(self.input, '\d\+', self.pos) + let res = strpart(self.input, self.pos, end - self.pos) + call self.advance(end - self.pos) + return +res " force conversion to Number + endif + return -1 +endfunction + +function! s:parser_add_var(var) dict abort + let id = a:var[0] + if !has_key(self.vars, id) + let self.vars[id] = { 'instances' : [] } + endif + call add(self.vars[id].instances, a:var) +endfunction + +function! s:parser_var() dict abort + let ret = [] + if self.same('{') + let id = self.id() + if id >= 0 + call add(ret, id) + call extend(ret, self.varend()) + endif + else + let id = self.id() + if id >= 0 + call add(ret, id) + endif + endif + return ret +endfunction + +function! s:parser_varend() dict abort + let ret = [] + if self.same(':') + call extend(ret, self.placeholder()) + elseif self.same('/') + call add(ret, self.subst()) + endif + call self.same('}') + return ret +endfunction + +function! s:parser_placeholder() dict abort + let ret = self.text('}') + return empty(ret) ? [''] : ret +endfunction + +function! s:parser_subst() dict abort + let ret = {} + let ret.pat = self.pat() + if self.same('/') + let ret.sub = self.pat(1) + endif + if self.same('/') + let ret.flags = self.pat(1) + endif + return ret +endfunction + +function! s:parser_pat(...) dict abort + let val = '' + + while self.pos < self.len + if self.same('\') + if self.next == '/' + let val .= '/' + call self.advance() + elseif a:0 && self.next == '}' + let val .= '}' + call self.advance() + else + let val .= '\' + endif + elseif self.next == '/' || a:0 && self.next == '}' + break + else + let val .= self.next + call self.advance() + endif + endwhile + + return val +endfunction + +function! s:parser_expr() dict abort + let str = self.string('`') + call self.same('`') + return snipmate#util#eval(str) +endfunction + +function! s:parser_string(till, ...) dict abort + let val = '' + let till = '\V\[' . escape(a:till, '\') . ']' + + while self.pos < self.len + if self.same('\') + if self.next != "\n" + let val .= self.next + endif + call self.advance() + elseif self.next =~# till + break + elseif self.next == "\t" + let self.indent += 1 + let val .= s:indent(1) + call self.advance() + else + let val .= self.next + call self.advance() + endif + endwhile + + return val +endfunction + +function! s:join_consecutive_strings(list) abort + let list = a:list + let pos = 0 + while pos + 1 < len(list) + if type(list[pos]) == type('') && type(list[pos+1]) == type('') + let list[pos] .= list[pos+1] + call remove(list, pos + 1) + else + let pos += 1 + endif + endwhile +endfunction + +function! s:parser_text(till) dict abort + let ret = [] + + while self.pos < self.len + let lines = [] + + if self.same('$') + let var = self.var() + if !empty(var) + if var[0] is# 'VISUAL' + let lines = s:visual_placeholder(var, self.indent) + elseif var[0] >= 0 + call add(ret, var) + call self.add_var(var) + endif + endif + elseif self.same('`') + let lines = split(self.expr(), "\n", 1) + else + let lines = [self.string(a:till . s:special_chars)] + endif + + if !empty(lines) + call add(ret, lines[0]) + call extend(self.stored_lines, lines[1:]) + endif + + " Empty lines are ignored if this is tested at the start of an iteration + if self.next ==# a:till + break + endif + endwhile + + call s:join_consecutive_strings(ret) + return ret +endfunction + +function! s:parser_line() dict abort + let ret = [] + if !empty(self.stored_lines) + call add(ret, remove(self.stored_lines, 0)) + else + call extend(ret, self.text("\n")) + call self.same("\n") + endif + let self.indent = 0 + return ret +endfunction + +function! s:parser_parse() dict abort + while self.pos < self.len || !empty(self.stored_lines) + let line = self.line() + call add(self.value, line) + endwhile +endfunction + +function! s:indent(count) abort + if &expandtab + let shift = repeat(' ', snipmate#util#tabwidth()) + else + let shift = "\t" + endif + return repeat(shift, a:count) +endfunction + +function! s:visual_placeholder(var, indent) abort + let arg = get(a:var, 1, {}) + if type(arg) == type({}) + let pat = get(arg, 'pat', '') + let sub = get(arg, 'sub', '') + let flags = get(arg, 'flags', '') + let content = split(substitute(get(b:, 'snipmate_visual', ''), pat, sub, flags), "\n", 1) + else + let content = split(get(b:, 'snipmate_visual', arg), "\n", 1) + endif + + let indent = s:indent(a:indent) + call map(content, '(v:key != 0) ? indent . v:val : v:val') + + return content +endfunction + +function! s:parser_create_stubs() dict abort + + for [id, dict] in items(self.vars) + for i in dict.instances + if len(i) > 1 && type(i[1]) != type({}) + if !has_key(dict, 'placeholder') + let dict.placeholder = i[1:] + call add(i, dict) + else + unlet i[1:] + call s:create_mirror_stub(i, dict) + endif + else + call s:create_mirror_stub(i, dict) + endif + endfor + if !has_key(dict, 'placeholder') + let dict.placeholder = [] + let j = 0 + while len(dict.instances[j]) > 2 + let j += 1 + endwhile + let oldstub = remove(dict.instances[j], 1, -1)[-1] + call add(dict.instances[j], '') + call add(dict.instances[j], dict) + call filter(dict.mirrors, 'v:val isnot oldstub') + endif + unlet dict.instances + endfor + +endfunction + +function! s:create_mirror_stub(mirror, dict) + let mirror = a:mirror + let dict = a:dict + let stub = get(mirror, 1, {}) + call add(mirror, stub) + let dict.mirrors = get(dict, 'mirrors', []) + call add(dict.mirrors, stub) +endfunction + +function! snipmate#parse#snippet(text, ...) abort + let parser = s:new_parser(a:text) + call parser.parse() + if !(a:0 && a:1) + call parser.create_stubs() + endif + unlet! b:snipmate_visual + return [parser.value, parser.vars] +endfunction + +call extend(s:parser_proto, snipmate#util#add_methods(s:sfile(), 'parser', + \ [ 'advance', 'same', 'id', 'add_var', 'var', 'varend', + \ 'line', 'string', 'create_stubs', 'pat', + \ 'placeholder', 'subst', 'expr', 'text', 'parse', + \ ]), 'error') diff --git a/vim/bundle/vim-snipmate/autoload/snipmate/util.vim b/vim/bundle/vim-snipmate/autoload/snipmate/util.vim new file mode 100644 index 0000000..a80d307 --- /dev/null +++ b/vim/bundle/vim-snipmate/autoload/snipmate/util.vim @@ -0,0 +1,30 @@ +" The next function was based on s:function and s:add_methods in fugitive +" <https://github.com/tpope/vim-fugitive/blob/master/plugin/fugitive.vim> +function! snipmate#util#add_methods(sfile, namespace, methods) abort + let dict = {} + for name in a:methods + let dict[name] = function(join([matchstr(a:sfile, '<SNR>\d\+'), + \ a:namespace, name], '_')) + endfor + return dict +endfunction + +function! snipmate#util#eval(arg) + try + let ret = eval(a:arg) + catch + echohl ErrorMsg + echom 'SnipMate:Expression: ' . v:exception + echohl None + let ret = '' + endtry + return type(ret) == type('') ? ret : string(ret) +endfunction + +function! snipmate#util#tabwidth() + if &sts > 0 + return &sts + else + return exists('*shiftwidth') ? shiftwidth() : &sw + endif +endfunction diff --git a/vim/bundle/vim-snipmate/doc/SnipMate.txt b/vim/bundle/vim-snipmate/doc/SnipMate.txt new file mode 100644 index 0000000..230fe68 --- /dev/null +++ b/vim/bundle/vim-snipmate/doc/SnipMate.txt @@ -0,0 +1,646 @@ +*SnipMate.txt* Plugin for using TextMate-style snippets in Vim. + +SnipMate *snippet* *snippets* *SnipMate* + +1. Description |SnipMate-description| +2. Usage |SnipMate-usage| +3. Interface and Settings |SnipMate-interface| |SnipMate-settings| +4. Snippets |SnipMate-snippets| + - Snippet files |SnipMate-snippet-files| + - Snippet syntax |SnipMate-syntax| +5. Snippet sources |SnipMate-snippet-sources| +6. Disadvantages to TextMate |SnipMate-disadvantages| +7. Contact |SnipMate-contact| +8. License |SnipMate-license| + +For Vim version 7.0 or later. +This plugin only works if 'compatible' is not set. +{Vi does not have any of these features.} + +SnipMate depends on vim-addon-mw-utils and tlib. + +============================================================================== +DESCRIPTION *SnipMate-description* + +SnipMate implements snippet features in Vim. A snippet is like a template, +reducing repetitive insertion of pieces of text. Snippets can contain +placeholders for modifying the text if necessary or interpolated code for +evaluation. For example, in C, typing "for" then pushing <Tab> could expand +to: > + + for (i = 0; i < count; i++) { + /* code */ + } + +SnipMate is inspired by TextMate's snippet features. + +============================================================================== +USAGE *SnipMate-usage* + +Every snippet consists of an expansion and a trigger. Typing a trigger into +your buffer and then hitting your trigger key (<Tab> by default, see +|SnipMate-mappings|) will replace the trigger with the expansion text. + +The expansion text can optionally include tab stops. When it does, upon +expansion of the snippet, the cursor is placed at the first one, and the user +can jump between each tab stop. Each of these tab stops can be represented by +default placeholder text. If such a placeholder is provided, then the text of +the placeholder can be repeated in the snippet at specified mirrors. Any edits +to the placeholder are instantly updated at every mirror. + +SnipMate allows multiple snippets to use the same trigger. When triggered, +a list of all snippets with that trigger is provided and prompts for which +snippet to use. + + *SnipMate-scopes* +SnipMate searches for snippets inside a directory named "snippets" inside each +entry in 'runtimepath'. Which files are loaded depends on 'filetype' and +'syntax'; see |SnipMate-syntax| for more information. Snippets are loaded and +refreshed automatically on demand. + +Note: SnipMate does not ship with any snippets. In order to use it, the user +must either write their own snippets or obtain some from a repository like +https://github.com/honza/vim-snippets + +============================================================================== +INTERFACE AND SETTINGS *SnipMate-interface* *SnipMate-settings* + + *SnipMate-commands* +Commands~ + + *:SnipMateOpenSnippetFiles* +:SnipMateOpenSnippetFiles Opens a list of all valid snippet locations + based on the current scope |SnipMate-scopes|. + Only existing files and non-existing .snippets + files will be shown, with the existing files + shown first. + +:SnipMateLoadScope[!] scope [scope ...] + Load snippets from additional scopes. Without + [!] the additional scopes are loaded only in + the current buffer. For example > + :SnipMateLoadScopes rails +< will load all rails.snippets in the current + buffer. + + *SnipMate-options* +Options~ + +g:snips_author A variable used in some snippets in place of + the author's (your) name. Similar to + $TM_FULLNAME in TextMate. For example, > + snippet name + `g:snips_author` +< creates a snippet "name" that expands to your + name. + +g:snipMate This |Dictionary| contains other SnipMate + options. In short add > + let g:snipMate = {} +< to your .vimrc before setting other SnipMate + options. + +g:snipMate.scope_aliases A |Dictionary| associating certain filetypes + with other scopes |SnipMate-scopes|. The + entries consist of a filetype as the key and + a comma-separated list of aliases as the + value. For example, > + let g:snipMate.scope_aliases = {} + let g:snipMate.scope_aliases['ruby'] + \ = 'ruby,ruby-rails' +< tells SnipMate that "ruby-rails" snippets in + addition to "ruby" snippets should be loaded + when editing files with 'filetype' set to + "ruby" or contains "ruby" as an entry in the + case of dotted filetypes. A buffer local + variant b:snipMate_scope_aliases is merged + with the global variant. + +g:snipMate_no_default_aliases Note: This has been renamed to the following. + +g:snipMate.no_default_aliases + When set to 1, prevents SnipMate from loading + default scope aliases. The defaults are: + Filetype Alias ~ + cpp c + cu c + eruby eruby-rails,html + html javascript + mxml actionscript + objc c + php php,html,javascript + ur html,javascript + xhtml html + Individual defaults can be disabled by setting + them to an empty value: > + let g:snipMate.scope_aliases.php = '' +< will disable the default PHP alias. + Note: Setting this option does not disable + scope aliases entirely, only those made by + SnipMate itself. Any scope aliases created by + the user or someone else will still be in + effect. + +g:snipMate.snippet_version + The snippet parser version to use. The + possible values are: + 0 Use the older parser + 1 Use the newer parser + If unset, SnipMate defaults to version 0. The + value of this option is also used for all + .snippet files. + +g:snipMate.override + As detailed below, when two snippets with the + same name and description are loaded, both are + kept and differentiated by the location of the + file they were in. When this option is enabled + (set to 1), the snippet originating in the + last loaded file is kept, similar to how Vim + maps and other settings work. Note: Load order + is determined by 'runtimepath'. + +g:snipMate['no_match_completion_feedkeys_chars'] + A string inserted when no match for a trigger + is found. By default a tab is inserted + according to 'expandtab', 'tabstop', and + 'softtabstop'. Set it to the empty string to + prevent anything from being inserted. + + *SnipMate-mappings* +Mappings~ + +The mappings SnipMate uses can be customized with the |:map| commands. For +example, to change the key that triggers snippets and moves to the next +tab stop, > + + :imap <C-J> <Plug>snipMateNextOrTrigger + :smap <C-J> <Plug>snipMateNextOrTrigger + +Note: The noremap variants of the map commands must NOT be used. + +The list of possible <Plug> mappings is as follows: + +<Plug>snipMateNextOrTrigger Default: <Tab> Mode: Insert, Select + Jumps to the next tab stop or, if none exists, + try to expand a snippet. Use in both insert + and select modes. + +<Plug>snipMateTrigger Default: unmapped Mode: Insert + Try to expand a snippet regardless of any + existing snippet expansion. If done within an + expanded snippet, the outer snippet's tab + stops are lost, unless expansion failed. + +<Plug>snipMateBack Default: <S-Tab> Mode: Insert, Select + Jump to the previous tab stop, if it exists. + Use in both insert and select modes. + +<Plug>snipMateShow Default: <C-R><Tab> Mode: Insert + Show all available snippets (that start with + the previous text, if it exists). Use in + insert mode. + +<Plug>snipMateVisual Default: <Tab> Mode: Visual + See |SnipMate-visual|. + +Additionally, <CR> is mapped in visual mode in .snippets files for retabbing +snippets. + +============================================================================== +SNIPPETS *SnipMate-snippets* + + *SnipMate-snippet-files* +Snippet Files ~ + +Note: SnipMate does not ship with any snippets. + +SnipMate looks inside of each entry of 'rtp' (or |SnipMate-snippet-sources|) +for a directory named /snippets/. Based on the 'filetype' and 'syntax' +settings (dotted filetypes are parsed), the following files are read for +snippets: > + + .../snippets/<scope>.snippets + .../snippets/<scope>_<name>.snippets + .../snippets/<scope>/<name>.snippets + .../snippets/<scope>/<trigger>.snippet + .../snippets/<scope>/<trigger>/<description>.snippet + +where <scope> is a scope or 'filetype' or 'syntax', <name> is an arbitrary +name, <trigger> is the trigger for a snippet, and <description> is +a description used for |SnipMate-multisnip|. + +A .snippet file defines a single snippet with the trigger (and description) +determined by the filename. The entire contents of the file are used as the +snippet expansion text. + +Multiple snippets can be defined in *.snippets files. Each snippet definition +looks something like: > + + snippet trigger optional description + expanded text + more expanded text + +< *SnipMate-multisnip* +The description is optional. If it is left out, the description "default" is +used. When two snippets in the same scope have the same name and the same +description, SnipMate will try to preserve both. The g:snipMate.override +option disables this, in favor of keeping the last-loaded snippet. This can be +overridden on a per-snippet basis by defining the snippet with a bang (!): > + + snippet! trigger optional description + expanded text + more expanded text + +Two bangs will remove the trigger entirely from SnipMate's lookup. In this +case any snippet text is unused. + +Note: Hard tabs in the expansion text are required. When the snippet is +expanded in the text and 'expandtab' is set, each tab will be replaced with +spaces based on 'softtabstop' if nonzero or 'shiftwidth' otherwise. + +Which version parser the snippets in a file should be used with can be +specified with a version line, e.g.: > + + version 1 + +Specification of a version applies to the snippets following it. Multiple +version specifications can appear in a single file to intermix version 0 and +version 1 snippets. + +Comments can be made in .snippets files by starting a line with a # character. +However these can't be used inside of snippet definitions: > + + # this is a correct comment + snippet trigger + expanded text + snippet another_trigger + # this isn't a comment! + expanded text + +This should hopefully be clear with the included syntax highlighting. + + *snipMate-extends* +Borrowing from UltiSnips, .snippets files can also contain an extends +directive, for example: > + + extends html, javascript, css + +will tell SnipMate to also read html, javascript, and css snippets. + +SNIPPET SYNTAX *snippet-syntax* *SnipMate-syntax* + +Anywhere in a snippet, a backslash escapes the character following it, +regardless of whether that character is special or not. That is, '\a' will +always result in an 'a' in the output. A single backslash can be output by +using '\\'. + + *SnipMate-tabstops* +Tab stops~ + +When triggering a snippet, SnipMate will by default jump to the very end of +the snippet text. This can be changed through the use of tab stops: $1, $2, +and so on. After expansion, SnipMate will jump to the first tab stop. From +then on, the <Plug>snipMateNextOrTrigger map will jump to the next higher +numbered tabs top. + +In the case of an ambiguity, for example if a stop occurs just before +a literal number, braces may be placed around the stop number to resolve it: +${3}79 is the third tab stop followed by the string "79". + +NOTE: In the version 0 snippet parser, the braces are mandatory. + + *SnipMate-zero-tabstop* +SnipMate will always stop at the special zero tab stop $0. Once it jumps to +the zero tab stop, snippet expansion is finished. If the zero tab stop is not +present in a definition, it will be put at the end. + +For example, to place the cursor first on the id of a <div> tag, then on its +class, and finally end editing its contents: > + + snippet div + <div id="$1" class="$2"> + $0 + </div> + +< *SnipMate-placeholders* +In addition to being simply a location, each tab stop contains a placeholder, +or some default text. The placeholder can be specified for every tab stop +(including the zero tab stop) with a colon after the stop ID, as in +${1:default text}. The braces are required only when specifying a placeholder. +Once a tab stop with a placeholder is reached, the placeholder will be +selected in |Select-mode|. For example, > + + snippet div + <div id="${1:id}" class="${2:class}"> + $0 + </div> + +Finally, placeholders can contain mirrors and evaluations (detailed below) and +even entire other tab stops. If the placeholder is edited, then these nested +tab stops are removed and skipped entirely. For example, > + + snippet div + <div${1: id="${2:id}"}${3: class="${4:class}"}> + $0 + </div> + +When expanded, this snippet selects the entirety of the id attribute. If this +stop is edited, then the second tab stop is removed and the third tab stop +becomes the next one. If the first tab stop is left unedited, then SnipMate +jumps to the second tab stop. This allows the user to use a single div snippet +that can be used for instances where the id or class attributes are desired +and those where they are not. + + *SnipMate-mirrors* +Mirrors~ + +A mirror is simply a copy of a tab stop's text, updated as the tab stop is +edited. These look like a tab stop without a placeholder; $1 for example. In +the event that no placeholder is specified for a certain tab stop--say $1--the +first instance becomes the tab stop and the rest become mirrors. + +Additionally substitutions similar to |:substitute| can be performed. For +instance ${1/foo/bar/g} will replace all instances of "foo" in the $1 mirror +with "bar". This uses |substitute()| behind the scenes. + +Note: Just like with tab stops, braces can be used to avoid ambiguities: ${1}2 +is a mirror of the first tab stop followed by a 2. Version 0 of the snippet +parser offers no way to resolve such ambiguities. + +As an example, > + + snippet for + for ($1 = ${2:start}; ${1:i} < ${3:end}; $1${4:++}) { + ${0:/* code */} + } + +< *SnipMate-eval* +Expression Evaluation~ + +Snippets can contain Vim script expressions that are evaluated as the snippet +is expanded. Expressions are specified inside backticks: > + + snippet date + `strftime("%Y-%m-%d")` + +If the expression results in any Vim error, the error will be displayed (or +found in :messages) and the result of the expression will be the empty string. + +Filename([{expr}] [, {defaultText}]) *SnipMate-Filename()* + +Since the current filename is used often in snippets, a default function +has been defined for it in SnipMate.vim, appropriately called Filename(). + +With no arguments, the default filename without an extension is returned; +the first argument specifies what to place before or after the filename, +and the second argument supplies the default text to be used if the file +has not been named. "$1" in the first argument is replaced with the filename; +if you only want the filename to be returned, the first argument can be left +blank. Examples: > + + snippet filename + `Filename()` + snippet filename_with_default + `Filename('', 'name')` + snippet filename_foo + `Filename('$1_foo')` + +The first example returns the filename if it the file has been named, and an +empty string if it hasn't. The second returns the filename if it's been named, +and "name" if it hasn't. The third returns the filename followed by "_foo" if +it has been named, and an empty string if it hasn't. + + *SnipMate-visual* +The VISUAL Stop~ + +While tab stops have numeric IDs, a special one exists with the ID 'VISUAL'. +When a snippet is expanded, if any text had been grabbed with the +snipMateVisual mapping (see |SnipMate-mappings|), all instances of the VISUAL +stop will be replaced with it. Both transformations as well as a default +placeholder can be used with the VISUAL stop. + +Note: Both $VISUAL and ${VISUAL} are valid in version 1 of the snippet parser. +In version 0, only {VISUAL} is valid (without the $), and neither +transformations nor a default placeholder can be used. + +Example: > + + snippet div + <div> + ${0:${VISUAL:<!-- content -->}} + </div> + +============================================================================== +SNIPPET SOURCES *SnipMate-snippet-sources* + +SnipMate is configurable. + +plugin/SnipMate.vim assigns a couple important keys: > + + " default implementation collecting snippets by handlers + let g:SnipMate['get_snippets'] = SnipMate#GetSnippets + " default handler: + let g:SnipMateSources['default'] = SnipMate#DefaultPool + +You can override both of those settings. + +You can see that the default set of snippets is determined by Vim's 'rtp'. + +Example 1:~ +autoload/SnipMate_python_demo.vim shows how you can register additional +sources such as creating snippets on the fly representing python function +definitions found in the current file. + +Example 2:~ +Add to your ~/.vimrc: For each know snippet add a second version ending in _ +adding folding markers > + + let g:commentChar = { + \ 'vim': '"', + \ 'c': '//', + \ 'cpp': '//', + \ 'sh': '#', + \ 'python': '#' + \ } + " url https://github.com/garbas/vim-snipmate/issues/49 + fun! AddFolding(text) + return substitute(a:text,'\n'," ".g:commentChar[&ft]." {{{\n",1)."\n".g:commentChar[&ft]." }}}" + endf + + fun! SnippetsWithFolding(scopes, trigger, result) + " hacky: temporarely remove this function to prevent infinite recursion: + call remove(g:SnipMateSources, 'with_folding') + " get list of snippets: + let result = SnipMate#GetSnippets(a:scopes, substitute(a:trigger,'_\(\*\)\?$','\1','')) + let g:SnipMateSources['with_folding'] = funcref#Function('SnippetsWithFolding') + + " add folding: + for k in keys(result) + let a:result[k.'_'] = map(result[k],'AddFolding(v:val)') + endfor + endf + + " force setting default: + runtime plugin/SnipMate.vim + " add our own source + let g:SnipMateSources['with_folding'] = funcref#Function('SnippetsWithFolding') + +See |SnipMate-syntax| for more details about all possible relative locations +to 'rtp' can be found in. + +============================================================================== +KNOWN ISSUES *SnipMate-known-issues* + +SnipMate.vim currently has the following disadvantages to TextMate's snippets: + - Placeholders cannot span multiple lines. + - Activating snippets in different scopes of the same file is + not possible. + - Vim formatting with fo=t or fo=a can mess up SnipMate. + +Perhaps some of these features will be added in a later release. + +============================================================================== +CHANGELOG *SnipMate-changelog* + +0.89 - 2016-05-29 +----------------- + +* Various regex updates to legacy parser Addition of double bang syntax to +* completely remove a snippet from lookup Group various SnipMate autocommands +* Support setting 'shiftwidth' to 0 Parser now operates linewise, adding some +* flexibility Mirror substitutions are more literal Mirror length is +* calculated correctly when substitutions occur + +0.88 - 2015-04-04 +----------------- + +* Implement simple caching +* Remove expansion guards +* Add `:SnipMateLoadScope` command and buffer-local scope aliases +* Load `<scope>_*.snippets` files +* Use CursorMoved autocmd events entirely + +* The nested branch has been merged + * A new snippet parser has been added. The g:snipmate.version as well as + version lines in snippet files determines which is used + * The new parser supports tab stops placed within placeholders, + substitutions, non-consecutive stop numbers, and fewer ambiguities + * The stop jumping code has been updated + * Tests have been added for the jumping code and the new parser + +* The override branch has been merged + * The g:snipMate.override option is added. When enabled, if two snippets + share the same name, the later-loaded one is kept and the other discarded + * Override behavior can be enabled on a per-snippet basis with a bang (!) in + the snippet file + * Otherwise, SnipMate tries to preserve all snippets loaded + +* Fix bug with mirrors in the first column +* Fix bug with tabs in indents + <http://github.com/garbas/vim-snipmate/issues/143> +* Fix bug with mirrors in placeholders +* Fix reading single snippet files +* Fix the use of the visual map at the end of a line +* Fix expansion of stops containing only the zero tab stop +* Remove select mode mappings +* Indent visual placeholder expansions and remove extraneous lines + <http://github.com/garbas/vim-snipmate/issues/177> + <http://github.com/garbas/vim-snipmate/issues/178> + +0.87 - 2014-01-04 +----------------- + +* Stop indenting empty lines when expanding snippets +* Support extends keyword in .snippets files +* Fix visual placeholder support +* Add zero tabstop support +* Support negative 'softtabstop' +* Add g:snipMate_no_default_aliases option +* Add <Plug>snipMateTrigger for triggering an expansion inside a snippet +* Add snipMate#CanBeTriggered() function + +0.86 - 2013-06-15 +----------------- +* Use more idiomatic <Plug> maps +* Remove most select mode mappings + +* Fix disappearing variables bug (hpesoj) +* Fix cursor position bug when a variable is on the same line as the stop +* Fix undo point creation causing problems with Supertab +* Fix bug where SnipMate would use a typed trigger as a regular expression + +0.85 - 2013-04-03 +----------------- + +* Allow trigger key customization +* Enable undoing of snippet expansion +* Support backslash escaping in snippets +* Add support for {VISUAL} +* Expand filetype extension with scope_aliases +* Add expansion guards +* Enable per-buffer expansion of snippets +* Fix 'cpo' compatibility +* Update supertab compatibility +* Enable customization of various things through g:SnipMate + +* Disable spelling in snippet files +* Highlight trigger names in .snippets files + +* Update many snippets +* Separate sample snippets into separate repository + +0.84 +---- + +* Unreleased version by Michael Sanders, available on his GitHub, + <https://github.com/msanders/snipmate.vim> + +0.83 - 2009-07-13 +----------------- + +* Last release done by Michael Sanders, available at + <http://www.vim.org/scripts/script.php?script_id=2540> + +============================================================================== +CONTACT *SnipMate-contact* *SnipMate-author* + +SnipMate is currently maintained by: + - Rok Garbas + - Marc Weber (marco-oweber@gmx.de) + - Adnan Zafar + +For bug reports, issues, or questions, check out the Issues page on GitHub: +https://github.com/garbas/vim-snipmate/issues + +The original author, Michael Sanders, can be reached at: +msanders42+snipmate <at> gmail <dot> com + + +============================================================================== +LICENSE *SnipMate-license* + +SnipMate is released under the MIT license: + +Copyright 2009-2010 Michael Sanders. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +The software is provided "as is", without warranty of any kind, express or +implied, including but not limited to the warranties of merchantability, +fitness for a particular purpose and noninfringement. In no event shall the +authors or copyright holders be liable for any claim, damages or other +liability, whether in an action of contract, tort or otherwise, arising from, +out of or in connection with the software or the use or other dealings in the +software. + +============================================================================== + vim:tw=78:ts=8:ft=help:norl: diff --git a/vim/bundle/vim-snipmate/ftplugin/html_snip_helper.vim b/vim/bundle/vim-snipmate/ftplugin/html_snip_helper.vim new file mode 100644 index 0000000..4d52392 --- /dev/null +++ b/vim/bundle/vim-snipmate/ftplugin/html_snip_helper.vim @@ -0,0 +1,10 @@ +" Helper function for (x)html snippets +if exists('s:did_snip_helper') || &cp || !exists('loaded_snips') + finish +endif +let s:did_snip_helper = 1 + +" Automatically closes tag if in xhtml +fun! Close() abort + return stridx(&ft, 'xhtml') == -1 ? '' : ' /' +endf diff --git a/vim/bundle/vim-snipmate/ftplugin/snippets.vim b/vim/bundle/vim-snipmate/ftplugin/snippets.vim new file mode 100644 index 0000000..dde00e4 --- /dev/null +++ b/vim/bundle/vim-snipmate/ftplugin/snippets.vim @@ -0,0 +1,20 @@ +" Vim filetype plugin for SnipMate snippets (.snippets and .snippet files) + +if exists("b:did_ftplugin") + finish +endif +let b:did_ftplugin = 1 + +let b:undo_ftplugin = "setl et< sts< cms< fdm< fde<" + +" Use hard tabs +setlocal noexpandtab softtabstop=0 + +setlocal foldmethod=expr foldexpr=getline(v:lnum)!~'^\\t\\\\|^$'?'>1':1 + +setlocal commentstring=#\ %s +setlocal nospell + +command! -buffer -range=% RetabSnip + \ echom "This command is deprecated. Use :retab and = instead. Doing that now." + \ | <line1>,<line2>retab! | <line1>,<line2>normal = diff --git a/vim/bundle/vim-snipmate/indent/snippets.vim b/vim/bundle/vim-snipmate/indent/snippets.vim new file mode 100644 index 0000000..0d28f32 --- /dev/null +++ b/vim/bundle/vim-snipmate/indent/snippets.vim @@ -0,0 +1,32 @@ +" Simple indent support for SnipMate snippets files + +if exists('b:did_indent') + finish +endif +let b:did_indent = 1 + +setlocal nosmartindent +setlocal indentkeys=!^F,o,O,=snippet,=version,=extends +setlocal indentexpr=GetSnippetIndent() + +if exists("*GetSnippetIndent") + finish +endif + +function! GetSnippetIndent() + let line = getline(v:lnum) + let prev_lnum = v:lnum - 1 + let prev_line = prev_lnum != 0 ? getline(prev_lnum) : "" + + if line =~# '\v^(snippet|extends|version) ' + return 0 + elseif indent(v:lnum) > 0 + return indent(v:lnum) + elseif prev_line =~# '^snippet ' + return &sw + elseif indent(prev_lnum) > 0 + return indent(prev_lnum) + endif + + return 0 +endfunction diff --git a/vim/bundle/vim-snipmate/plugin/snipMate.vim b/vim/bundle/vim-snipmate/plugin/snipMate.vim new file mode 100644 index 0000000..837f0dc --- /dev/null +++ b/vim/bundle/vim-snipmate/plugin/snipMate.vim @@ -0,0 +1,137 @@ +" File: snipMate.vim +" 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 "<tab>". +" +" For more help see snipMate.txt; you can do this by using: +" :helptags ~/.vim/doc +" :h SnipMate + +if exists('loaded_snips') || &cp || version < 700 + finish +endif +let loaded_snips = 1 + +" Save and reset 'cpo' +let s:save_cpo = &cpo +set cpo&vim + +try + call funcref#Function('') +catch /.*/ + echoe "you're missing vim-addon-mw-utils. See install instructions at ".expand('<sfile>:h:h').'/README.md' +endtry + +if (!exists('g:snipMateSources')) + let g:snipMateSources = {} + " Default source: get snippets based on runtimepath + let g:snipMateSources['default'] = funcref#Function('snipMate#DefaultPool') +endif + +augroup SnipMateDetect + au BufRead,BufNewFile *.snippet,*.snippets setlocal filetype=snippets + au FileType snippets if expand('<afile>:e') =~# 'snippet$' + \ | setlocal syntax=snippet + \ | else + \ | setlocal syntax=snippets + \ | endif +augroup END + +inoremap <silent> <Plug>snipMateNextOrTrigger <C-R>=snipMate#TriggerSnippet()<CR> +snoremap <silent> <Plug>snipMateNextOrTrigger <Esc>a<C-R>=snipMate#TriggerSnippet()<CR> +inoremap <silent> <Plug>snipMateTrigger <C-R>=snipMate#TriggerSnippet(1)<CR> +inoremap <silent> <Plug>snipMateBack <C-R>=snipMate#BackwardsSnippet()<CR> +snoremap <silent> <Plug>snipMateBack <Esc>a<C-R>=snipMate#BackwardsSnippet()<CR> +inoremap <silent> <Plug>snipMateShow <C-R>=snipMate#ShowAvailableSnips()<CR> +xnoremap <silent> <Plug>snipMateVisual :<C-U>call <SID>grab_visual()<CR>gv"_c + +" config variables +if !exists('g:snips_author') + let g:snips_author = 'Me' +endif +if !exists('g:snipMate') + let g:snipMate = {} +endif + +" SnipMate inserts this string when no snippet expansion can be done +let g:snipMate['no_match_completion_feedkeys_chars'] = + \ get(g:snipMate, 'no_match_completion_feedkeys_chars', "\t") + +" Add default scope aliases, without overriding user settings +let g:snipMate.scope_aliases = get(g:snipMate, 'scope_aliases', {}) +if exists('g:snipMate_no_default_aliases') + echom 'The g:snipMate_no_default_aliases option has been renamed.' + \ 'See :h snipMate-options.' +endif +if (!exists('g:snipMate_no_default_aliases') || !g:snipMate_no_default_aliases) + \ && (!exists('g:snipMate.no_default_aliases') + \ || !g:snipMate.no_default_aliases) + let g:snipMate.scope_aliases.objc = + \ get(g:snipMate.scope_aliases, 'objc', 'c') + let g:snipMate.scope_aliases.cpp = + \ get(g:snipMate.scope_aliases, 'cpp', 'c') + let g:snipMate.scope_aliases.cu = + \ get(g:snipMate.scope_aliases, 'cu', 'c') + let g:snipMate.scope_aliases.xhtml = + \ get(g:snipMate.scope_aliases, 'xhtml', 'html') + let g:snipMate.scope_aliases.html = + \ get(g:snipMate.scope_aliases, 'html', 'javascript') + let g:snipMate.scope_aliases.php = + \ get(g:snipMate.scope_aliases, 'php', 'php,html,javascript') + let g:snipMate.scope_aliases.ur = + \ get(g:snipMate.scope_aliases, 'ur', 'html,javascript') + let g:snipMate.scope_aliases.mxml = + \ get(g:snipMate.scope_aliases, 'mxml', 'actionscript') + let g:snipMate.scope_aliases.eruby = + \ get(g:snipMate.scope_aliases, 'eruby', 'eruby-rails,html') + let g:snipMate.scope_aliases.scss = + \ get(g:snipMate.scope_aliases, 'scss', 'css') + let g:snipMate.scope_aliases.less = + \ get(g:snipMate.scope_aliases, 'less', 'css') +endif + +let g:snipMate['get_snippets'] = get(g:snipMate, 'get_snippets', funcref#Function("snipMate#GetSnippets")) + +" List of paths where snippets/ dirs are located +let g:snipMate['snippet_dirs'] = get(g:snipMate, 'snippet_dirs', split(&rtp, ',')) +if type(g:snipMate['snippet_dirs']) != type([]) + echohl WarningMsg + echom "g:snipMate['snippet_dirs'] must be a List" + echohl None +endif + +" _ is default scope added always +" +" &ft honors multiple filetypes and syntax such as in set ft=html.javascript syntax=FOO +let g:snipMate['get_scopes'] = get(g:snipMate, 'get_scopes', funcref#Function('return split(&ft,"\\.")+[&syntax, "_"]')) + +" Modified from Luc Hermitte's function on StackOverflow +" <http://stackoverflow.com/a/1534347> +function! s:grab_visual() abort + let a_save = @a + try + normal! gv"ay + let b:snipmate_visual = @a + finally + let @a = a_save + endtry +endfunction + +" TODO: Allow specifying an arbitrary snippets file +function! s:load_scopes(bang, ...) abort + let gb = a:bang ? g: : b: + let gb.snipMate = get(gb, 'snipMate', {}) + let gb.snipMate.scope_aliases = get(gb.snipMate, 'scope_aliases', {}) + let gb.snipMate.scope_aliases['_'] = join(split(get(gb.snipMate.scope_aliases, '_', ''), ',') + a:000, ',') +endfunction + +command! -bang -bar -nargs=+ SnipMateLoadScope + \ call s:load_scopes(<bang>0, <f-args>) + +" Edit snippet files +command! SnipMateOpenSnippetFiles call snipMate#OpenSnippetFiles() + +" restore 'cpo' +let &cpo = s:save_cpo + +" vim:noet:sw=4:ts=4:ft=vim diff --git a/vim/bundle/vim-snipmate/syntax/snippet.vim b/vim/bundle/vim-snipmate/syntax/snippet.vim new file mode 100644 index 0000000..a947356 --- /dev/null +++ b/vim/bundle/vim-snipmate/syntax/snippet.vim @@ -0,0 +1,11 @@ +" Syntax highlighting for .snippet files (used for snipMate.vim) +" Hopefully this should make snippets a bit nicer to write! +syn match placeHolder '\${\d\+\(:.\{-}\)\=}' contains=snipCommand +syn match tabStop '\$\d\+' +syn match snipEscape '\\\\\|\\`' +syn match snipCommand '\%(\\\@<!\%(\\\\\)*\)\@<=`.\{-}\%(\\\@<!\%(\\\\\)*\)\@<=`' + +hi link placeHolder Special +hi link tabStop Special +hi link snipEscape SpecialChar +hi link snipCommand String diff --git a/vim/bundle/vim-snipmate/syntax/snippets.vim b/vim/bundle/vim-snipmate/syntax/snippets.vim new file mode 100644 index 0000000..63bda4e --- /dev/null +++ b/vim/bundle/vim-snipmate/syntax/snippets.vim @@ -0,0 +1,23 @@ +" Syntax highlighting for .snippets files (used for snipMate.vim) +" Hopefully this should make snippets a bit nicer to write! +syn match snipComment '^#.*' +syn match placeHolder '\${\d\+\(:.\{-}\)\=}' contains=snipCommand +syn match tabStop '\$\d\+' +syn match snipEscape '\\\\\|\\`' +syn match snipCommand '\%(\\\@<!\%(\\\\\)*\)\@<=`.\{-}\%(\\\@<!\%(\\\\\)*\)\@<=`' +syn match snippet '^snippet.*' contains=multiSnipText,snipKeyword +syn match snippet '^extends.*' contains=snipKeyword +syn match snippet '^version.*' contains=snipKeyword +syn match multiSnipText '\S\+ \zs.*' contained +syn match snipKeyword '^(snippet|extends|version)'me=s+8 contained +syn match snipError "^[^#vse\t].*$" + +hi link snippet Identifier +hi link snipComment Comment +hi link multiSnipText String +hi link snipKeyword Keyword +hi link snipEscape SpecialChar +hi link placeHolder Special +hi link tabStop Special +hi link snipCommand String +hi link snipError Error diff --git a/vim/bundle/vim-snipmate/t/jumping.vim b/vim/bundle/vim-snipmate/t/jumping.vim new file mode 100644 index 0000000..1c3ab0d --- /dev/null +++ b/vim/bundle/vim-snipmate/t/jumping.vim @@ -0,0 +1,175 @@ +function! Setup(snip) abort + return snipMate#expandSnip(join(a:snip, "\n"), 1) +endfunction + +function! s:to_be_file(expected) abort + return a:expected == getline(1,'$') +endfunction + +function! s:to_be_in(item, list) abort + return !empty(filter(copy(a:list), 'v:val is a:item')) +endfunction + +call vspec#customize_matcher('to_be_file', function('s:to_be_file')) +call vspec#customize_matcher('to_be_in', function('s:to_be_in')) + +describe 'snippet state' + + before + enew + let b:snip_state = snipmate#jumping#state() + end + + after + bwipeout! + end + + describe '.remove()' + + it 'removes the state object' + Expect exists('b:snip_state') to_be_true + call b:snip_state.remove() + Expect exists('b:snip_state') to_be_false + end + + it 'removes snippet related autocommands' + function! ReadAutocmds() + redir => autocmds + 0verbose au snipmate_changes * <buffer> + redir END + return split(autocmds, "\n") + endfunction + aug snipmate_changes + au CursorMoved,CursorMovedI <buffer> echo 'event' + aug END + + Expect len(ReadAutocmds()) > 1 + call b:snip_state.remove() + Expect len(ReadAutocmds()) == 1 + end + + end + + describe '.find_next_stop()' + + it 'increments/decrements the stop_no' + let b:snip_state.stops = { 1 : {}, 2 : {} } + let b:snip_state.stop_no = 1 + let b:snip_state.stop_count = 4 + + call b:snip_state.find_next_stop(0) + Expect b:snip_state.stop_no == 2 + call b:snip_state.find_next_stop(1) + Expect b:snip_state.stop_no == 1 + end + + it 'continues iterating if the next/previous stop does not exist' + let b:snip_state.stops = { 3 : {} } + let b:snip_state.stop_count = 6 + let b:snip_state.stop_no = 1 + call b:snip_state.find_next_stop(0) + Expect b:snip_state.stop_no == 3 + let b:snip_state.stop_no = 5 + call b:snip_state.find_next_stop(1) + Expect b:snip_state.stop_no == 3 + end + + it 'does something at the ends' + " + end + + end + + describe '.remove_nested()' + + it 'removes nested mirrors and only nested mirrors' + let mirror = { 'line' : 0 } + let b:snip_state.stops = { 1 : { 'placeholder' : [[2, mirror]] }, + \ 2 : { 'mirrors' : [mirror, {}] } } + + call b:snip_state.remove_nested(1) + Expect len(b:snip_state.stops[2].mirrors) == 1 + Expect b:snip_state.stops[2].mirrors[0] isnot mirror + end + + it 'removes nested stops' + let stop = [2, 'abc'] + let b:snip_state.stops = { 1 : { 'placeholder' : [stop] }, + \ 2 : { 'placeholder' : stop[1:1] } } + + call b:snip_state.remove_nested(1) + Expect len(b:snip_state.stops) == 1 + Expect keys(b:snip_state.stops) == ['1'] + end + + end + + describe '.find_update_objects()' + + it 'finds mirrors/stops on the same line and after cur_stop' + let b:snip_state.stops = { + \ 1 : { 'line' : 1, 'col' : 5, + \ 'placeholder' : ['x'] }, + \ 2 : { 'line' : 1, 'col' : 7, + \ 'mirrors' : [{ 'line' : 1, 'col' : 7 }] } + \ } + let stop = b:snip_state.stops[1] + + call b:snip_state.find_update_objects(stop) + for obj in stop.update_objects + Expect obj to_be_in [ b:snip_state.stops[2], + \ b:snip_state.stops[2].mirrors[0] ] + endfor + end + + it 'finds mirrors/stops on the same line and after cur_stop mirrors' + let b:snip_state.stops = { + \ 1 : { 'line' : 1, 'col' : 5, + \ 'mirrors' : [{ 'line' : 2, 'col' : 5 }], + \ 'placeholder' : ['x'] }, + \ 2 : { 'line' : 2, 'col' : 7, + \ 'mirrors' : [{ 'line' : 2, 'col' : 7 }] } + \ } + let stop = b:snip_state.stops[1] + + call b:snip_state.find_update_objects(stop) + for obj in stop.update_objects + Expect obj to_be_in [ b:snip_state.stops[2], + \ b:snip_state.stops[2].mirrors[0] ] + endfor + end + + it 'ignores mirrors/stops on other lines' + let b:snip_state.stops = { + \ 1 : { 'line' : 2, 'col' : 5, + \ 'placeholder' : ['x'] }, + \ 2 : { 'line' : 1, 'col' : 7, + \ 'mirrors' : [{ 'line' : 1, 'col' : 7 }] }, + \ 3 : { 'line' : 3, 'col' : 7, + \ 'mirrors' : [{ 'line' : 3, 'col' : 7 }] } + \ } + let stop = b:snip_state.stops[1] + + call b:snip_state.find_update_objects(stop) + Expect empty(stop.update_objects) to_be_true + end + + it 'ignores mirrors/stops on the same line but before cur_stop/mirrors' + let b:snip_state.stops = { + \ 1 : { 'line' : 1, 'col' : 5, + \ 'mirrors' : [{ 'line' : 2, 'col' : 5 }], + \ 'placeholder' : ['x'] }, + \ 2 : { 'line' : 1, 'col' : 1, + \ 'mirrors' : [{ 'line' : 2, 'col' : 1 }] }, + \ 3 : { 'line' : 2, 'col' : 3, + \ 'mirrors' : [{ 'line' : 1, 'col' : 3 }] }, + \ } + let stop = b:snip_state.stops[1] + + call b:snip_state.find_update_objects(stop) + Expect empty(stop.update_objects) to_be_true + end + + end + +end diff --git a/vim/bundle/vim-snipmate/t/parser.vim b/vim/bundle/vim-snipmate/t/parser.vim new file mode 100644 index 0000000..c5cf873 --- /dev/null +++ b/vim/bundle/vim-snipmate/t/parser.vim @@ -0,0 +1,142 @@ +describe 'snippet parser' + + before + function! Parse(snippet, ...) + let [snip, stops] = snipmate#parse#snippet(a:snippet, (a:0 ? a:1 : 1)) + return (a:0 > 1 && a:2) ? [snip, stops] : snip + endfunction + let b:snipmate_visual = 'testvisual' + end + + it 'parses numeric $id and ${id} vars as [id] lists' + let expect = [[[1234567890]]] + Expect Parse('$1234567890') == expect + Expect Parse('${1234567890}') == expect + end + + it 'disregards $ or ${ followed by a non-id' + Expect Parse('$x1') == [['x1']] + Expect Parse('${x}1') == [['x}1']] + Expect Parse('$VISUA1') == [['VISUA1']] + Expect Parse('${VISUA}1') == [['VISUA}1']] + end + + it 'gathers references to each instance of each stop id' + let [snip, b:stops] = Parse('x$1x${2:x$1x}x$1x${1/a/b}x$VISUALx', 1, 1) + function! InstanceFound(list) + return !empty(filter(copy(b:stops[a:list[0]].instances), + \ 'v:val is a:list')) + endfunction + function! CheckList(list) + for item in a:list + if type(item) == type([]) + Expect InstanceFound(item) to_be_true + call CheckList(item) + endif + unlet item " E732 + endfor + endfunction + call CheckList(snip[0]) + end + + it 'parses mirror substitutions ${n/pat/sub} as [n, {...}]' + let expect = [[[1, { 'pat' : 'abc', 'sub' : 'def' }]]] + Expect Parse('${1/abc/def}') == expect + let expect[0][0][1].flags = '' + Expect Parse('${1/abc/def/}') == expect + let expect[0][0][1].flags = 'g' + Expect Parse('${1/abc/def/g}') == expect + end + + it 'reads patterns literally except for "\/"' + Expect Parse('${1/\a\/b/\c\/d\}}') == [[[1, { 'pat' : '\a/b', 'sub' : '\c/d}' }]]] + end + + it 'parses vars with placeholders as [id, placeholder] lists' + Expect Parse('${1:abc}') == [[[1, 'abc']]] + end + + it 'evaluates backtick expressions' + Expect Parse('`fnamemodify("x.y", ":r")`') == [['x']] + end + + it 'parses placeholders for vars and other specials' + let text = 'a `fnamemodify("x.y", ":r")` ${2:(${3/a/b})}' + let expect = ['a x ', [2, '(', [3, { 'pat' : 'a', 'sub' : 'b' }], ')']] + Expect Parse(text) == [expect] + Expect Parse(printf('${1:%s}', text)) == [[[1] + expect]] + end + + it 'converts tabs according to &et, &sts, &sw, &ts' + " &noet -> leave tabs alone + setl noet + Expect Parse("abc\tdef\n\t\tghi") == [["abc\tdef"], ["\t\tghi"]] + + " &et -> &sts or &sw + setl et sts=2 sw=3 + Expect Parse("abc\tdef\n\t\tghi") == [["abc def"], [" ghi"]] + + setl et sts=0 sw=3 + Expect Parse("abc\tdef\n\t\tghi") == [["abc def"], [" ghi"]] + + setl et sts=-1 sw=3 + Expect Parse("abc\tdef\n\t\tghi") == [["abc def"], [" ghi"]] + + " See #227 + if exists('*shiftwidth') + setl et sts=0 sw=0 ts=3 + Expect Parse("abc\tdef\n\t\tghi") == [["abc def"], [" ghi"]] + endif + end + + it 'parses backslashes as escaping the next character or joining lines' + Expect Parse('x\x') == [['xx']] + Expect Parse('x\\x') == [['x\x']] + Expect Parse("x\\\nx") == [['xx']] + Expect Parse('x\$1') == [['x$1']] + Expect Parse('${1:\}}') == [[[1, '}']]] + Expect Parse('`fnamemodify("\`.x", ":r")`') == [['`']] + Expect Parse('\`x\`') == [['`x`']] + end + + it 'splits text at newlines' + Expect Parse("x\nx") == [['x'], ['x']] + end + + it 'joins evaluated expressions to surrounding text on the same line' + let g:foo = 'bar' + Expect Parse("x`g:foo`x") == [['xbarx']] + Expect Parse("x`g:foo`\nx") == [['xbar'], ['x']] + Expect Parse("x\n`g:foo`x") == [['x'], ['barx']] + end + + it 'expands $VISUAL placeholders with any indents' + Expect Parse("x$VISUALx") == [['xtestvisualx']] + let b:snipmate_visual = " foo\nbar\n baz" + setl noet + Expect Parse("\tx\n\t$VISUAL\nx") == [["\tx"], ["\t foo"], ["\tbar"], + \ ["\t baz"], ["x"]] + end + + it 'determines which var with an id is the stop' + let [snip, stops] = Parse("$1$1$1", 0, 1) + Expect snip == [[[1, "", stops[1]], [1, {}], [1, {}]]] + + let [snip, stops] = Parse("$1${1}$1", 0, 1) + Expect snip == [[[1, "", stops[1]], [1, {}], [1, {}]]] + + let [snip, stops] = Parse("$1${1:}$1", 0, 1) + Expect snip == [[[1, {}], [1, "", stops[1]], [1, {}]]] + + end + + it 'picks the first of many possible stops' + let [snip, stops] = Parse("$1${1:foo}${1:bar}", 0, 1) + Expect snip == [[[1, {}], [1, "foo", stops[1]], [1, {}]]] + end + + it 'represents empty lines as an empty string' + Expect Parse("foo\n\nbar") == [['foo'], [''], ['bar']] + end + +end diff --git a/vim/bundle/vim-snipmate/t/tests.sh b/vim/bundle/vim-snipmate/t/tests.sh new file mode 100755 index 0000000..c1da250 --- /dev/null +++ b/vim/bundle/vim-snipmate/t/tests.sh @@ -0,0 +1,20 @@ +#!/bin/sh + +tmp="$(mktemp || tmpfile)" +vim -Es $tmp <<- EOF + source ~/.vimrc + %delete _ + call append(0, split(&rtp, ',')) + delete _ + wq +EOF + +rtp="$(grep -iE 'vspec|snipmate|tlib|mw-utils' < $tmp | grep -v after)" +vspec="$(grep -iE 'vspec' < $tmp | grep -v after)" +test_files="${*:-parser jumping}" + +for test in $test_files; do + $vspec/bin/vspec $rtp ${test%%.vim}.vim +done + +rm $tmp |