diff options
Diffstat (limited to 'vim/bundle/vim-snipmate/autoload')
-rw-r--r-- | vim/bundle/vim-snipmate/autoload/snipMate.vim | 603 | ||||
-rw-r--r-- | vim/bundle/vim-snipmate/autoload/snipMate_python_demo.vim | 47 | ||||
-rw-r--r-- | vim/bundle/vim-snipmate/autoload/snipmate/jumping.vim | 228 | ||||
-rw-r--r-- | vim/bundle/vim-snipmate/autoload/snipmate/legacy.vim | 139 | ||||
-rw-r--r-- | vim/bundle/vim-snipmate/autoload/snipmate/parse.vim | 309 | ||||
-rw-r--r-- | vim/bundle/vim-snipmate/autoload/snipmate/util.vim | 30 |
6 files changed, 1356 insertions, 0 deletions
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 |