Skip to content

Commit

Permalink
big change:
Browse files Browse the repository at this point in the history
Add a function collecting snippets on demand.
.snippets files are parsed only once and cached for performance reasons.
However if the timestamp changes they are reread automatically.
I tried to preserve behaviour. However I may have failed.
The main reason for this is that it is possible now to define new script
sources easily. Eg there could be a plugin which reads Eclipse plugins,
translating them to snipMate snippets on the fly etc.

This patch adds a dependency on my vim-addon-mw-utils package.
Therefor installation is more difficult unless you use vim-addon-manager

Send bug reports about this change to marco-oweber@gmx.de
  • Loading branch information
MarcWeber committed Jan 21, 2010
1 parent 30fae8e commit d9f239c
Show file tree
Hide file tree
Showing 4 changed files with 154 additions and 137 deletions.
3 changes: 0 additions & 3 deletions after/plugin/snipMate.vim
Expand Up @@ -29,7 +29,4 @@ if empty(snippets_dir)
finish
endif

call GetSnippets(snippets_dir, '_') " Get global snippets

au FileType * if &ft != 'help' | call GetSnippets(snippets_dir, &ft) | endif
" vim:noet:sw=4:ts=4:ft=vim
114 changes: 114 additions & 0 deletions autoload/snipMate.vim
@@ -1,3 +1,14 @@
" config which can be overridden (shared lines)
if !exists('g:snipMate')
let g:snipMate = {}
endif
let s:snipMate = g:snipMate

" if filetype is objc, cpp, or cs also append snippets from scope 'c'
" you can add multiple by separating scopes by ',', see s:ScopeAliases
" TODO add documentation to doc/*
let s:snipMate['scope_aliases'] = get(s:snipMate,'scope_aliases', {'objc' :'c', 'cpp': 'c', 'cs':'c'} )

fun! Filename(...)
let filename = expand('%:t:r')
if filename == '' | return a:0 == 2 ? a:2 : '' | endif
Expand Down Expand Up @@ -432,4 +443,107 @@ fun s:UpdateVars()
let s:oldWord = newWord
let g:snipPos[s:curPos][2] = newWordLen
endf

" should be moved to utils or such?
fun! snipMate#SetByPath(dict, path, value)
let d = a:dict
for p in a:path[:-2]
if !has_key(d,p) | let d[p] = {} | endif
let d = d[p]
endfor
let d[a:path[-1]] = a:value
endf

" reads a .snippets file
" returns list of
" ['triggername', 'name', 'contents']
fun! snipMate#ReadSnippetsFile(file)
let result = []
if !filereadable(a:file) | return result | endif
let text = readfile(a:file)

let text = readfile(a:file)
let inSnip = 0
for line in text + ["\n"]
if inSnip && (line[0] == "\t" || line == '')
let content .= strpart(line, 1)."\n"
continue
elseif inSnip
call add(result, [trigger, name == '' ? 'default' : name, content[:-2]])
let inSnip = 0
endif

if line[:6] == 'snippet'
let inSnip = 1
let trigger = strpart(line, 8)
let name = ''
let space = stridx(trigger, ' ') + 1
if space " Process multi snip
let name = strpart(trigger, space)
let trigger = strpart(trigger, 0, space - 1)
endif
let content = ''
endif
endfor
return result
endf


let s:read_snippets_cached = {'func' : function('snipMate#ReadSnippetsFile'), 'version': 3, 'use_file_cache':1}

fun! s:ScopeAliases(list)
let result = []
let scope_aliases = get(s:snipMate,'scope_aliases', {})
for i in a:list
if has_key(scope_aliases, i)
call add(result, split(scope_aliases[i],','))
endif
endfor
return result
endf

" 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)
let result = {}
let triggerR = substitute(a:trigger,'*','.*','g')
let scopes = a:scopes + s:ScopeAliases(a:scopes)
for scope in scopes

for r in split(&runtimepath,',')

" .snippets files (many snippets per file). cache result for
" performance reason
" assume everything is a file..
for snippetsF in split(glob(r.'/snippets/'.scope.'.snippets'),"\n")
for [trigger, name, contents] in cached_file_contents#CachedFileContents(snippetsF, s:read_snippets_cached, 0)

This comment has been minimized.

Copy link
@mikesmullin

mikesmullin Aug 21, 2010

not all snippets are lazily loaded

if trigger !~ triggerR | continue | endif
call snipMate#SetByPath(result, [trigger, name], contents)
endfor
endfor

" == one file per snippet: ==

" without name snippets/<filetype>/<trigger>.snippet
for f in split(glob(r.'/snippets/'.scope.'/'.a:trigger.'.snippet'),"\n")
let trigger = fnamemodify(f,':t:r')
" lazily read files as needed
call snipMate#SetByPath(result, [trigger, 'default'], funcref#Function('return readfile('.string(f).')'))
endfor
" add /snippets/trigger/*.snippet files (TODO)

" with name (multi-snip) snippets/<filetype>/<trigger>/<name>.snippet
for f in split(glob(r.'/snippets/'.scope.'/'.a:trigger.'/*.snippet'),"\n")
let name = fnamemodify(f,':t:r')
let trigger = fnamemodify(f,':h:t')
" lazily read files as needed
call snipMate#SetByPath(result, [trigger, name], funcref#Function('return readfile('.string(f).')'))
endfor
endfor
endfor
return result
endf


" vim:noet:sw=4:ts=4:ft=vim
164 changes: 30 additions & 134 deletions plugin/snipMate.vim
Expand Up @@ -18,47 +18,18 @@ if !exists('snips_author') | let snips_author = 'Me' | endif
au BufRead,BufNewFile *.snippets\= set ft=snippet
au FileType snippet setl noet fdm=indent

" bind local dict to global dict (debugging purposes)
" you should use MakeSnip to add custom snippets
if !exists('g:multi_snips')
let g:multi_snips = {}
" config which can be overridden (shared lines)
if !exists('g:snipMate')
let g:snipMate = {}
endif
let s:multi_snips = g:multi_snips
let s:snipMate = g:snipMate

let s:snipMate['get_snippets'] = get(s:snipMate, 'get_snippets', funcref#Function("snipMate#GetSnippets"))

if !exists('snippets_dir')
let snippets_dir = substitute(globpath(&rtp, 'snippets/'), "\n", ',', 'g')
endif

fun! MakeSnip(scope, trigger, content, ...)
let description = a:0 > 0 ? a:1 : "default"
if description == ""
let description = "default"
endif
let s:multi_snips[a:scope] = get(s:multi_snips, a:scope, {})
let scopeDict = s:multi_snips[a:scope]
let scopeDict[a:trigger] = get(scopeDict, a:trigger, {})
let triggerDict = scopeDict[a:trigger]
if has_key(triggerDict, description)
echom 'Warning in snipMate.vim: Snippet '.a:trigger.' is already defined.'
\ .' See :h multi_snip for help on snippets with multiple matches.'
endif
" override existing snippet. User may have reloaded something
let triggerDict[description] = a:content
endf

fun! ExtractSnips(dir, ft)
for path in split(globpath(a:dir, '*'), "\n")
if isdirectory(path)
let pathname = fnamemodify(path, ':t')
for snipFile in split(globpath(path, '*.snippet'), "\n")
call s:ProcessFile(snipFile, a:ft, pathname)
endfor
elseif fnamemodify(path, ':e') == 'snippet'
call s:ProcessFile(path, a:ft)
endif
endfor
endf

" Processes a single-snippet file; optionally add the name of the parent
" directory for a snippet with multiple matches.
fun s:ProcessFile(file, ft, ...)
Expand All @@ -73,88 +44,6 @@ fun s:ProcessFile(file, ft, ...)
\ : MakeSnip(a:ft, keyword, text)
endf

fun! ExtractSnipsFile(file, ft)
if !filereadable(a:file) | return | endif
let text = readfile(a:file)
let inSnip = 0
for line in text + ["\n"]
if inSnip && (line[0] == "\t" || line == '')
let content .= strpart(line, 1)."\n"
continue
elseif inSnip
call MakeSnip(a:ft, trigger, content[:-2], name)
let inSnip = 0
endif

if line[:6] == 'snippet'
let inSnip = 1
let trigger = strpart(line, 8)
let name = ''
let space = stridx(trigger, ' ') + 1
if space " Process multi snip
let name = strpart(trigger, space)
let trigger = strpart(trigger, 0, space - 1)
endif
let content = ''
endif
endfor
endf

" Reset snippets for filetype.
fun! ResetSnippets(ft)
let ft = a:ft == '' ? '_' : a:ft
for dict in [s:multi_snips, g:did_ft]
if has_key(dict, ft)
unlet dict[ft]
endif
endfor
endf

" Reset snippets for all filetypes.
fun! ResetAllSnippets()
let s:multi_snips = {} | let g:did_ft = {}
endf

" Reload snippets for filetype.
fun! ReloadSnippets(ft)
let ft = a:ft == '' ? '_' : a:ft
call ResetSnippets(ft)
call GetSnippets(g:snippets_dir, ft)
endf

" Reload snippets for all filetypes.
fun! ReloadAllSnippets()
for ft in keys(g:did_ft)
call ReloadSnippets(ft)
endfor
endf

let g:did_ft = {}
fun! GetSnippets(dir, filetypes)
for ft in split(a:filetypes, '\.')
if has_key(g:did_ft, ft) | continue | endif
call s:DefineSnips(a:dir, ft, ft)
if ft == 'objc' || ft == 'cpp' || ft == 'cs'
call s:DefineSnips(a:dir, 'c', ft)
elseif ft == 'xhtml'
call s:DefineSnips(a:dir, 'html', 'xhtml')
endif
let g:did_ft[ft] = 1
endfor
endf

" Define "aliasft" snippets for the filetype "realft".
fun s:DefineSnips(dir, aliasft, realft)
for path in split(globpath(a:dir, a:aliasft.'/')."\n".
\ globpath(a:dir, a:aliasft.'-*/'), "\n")
call ExtractSnips(path, a:realft)
endfor
for path in split(globpath(a:dir, a:aliasft.'.snippets')."\n".
\ globpath(a:dir, a:aliasft.'-*.snippets'), "\n")
call ExtractSnipsFile(path, a:realft)
endfor
endf

fun! TriggerSnippet()
if exists('g:SuperTabMappingForward')
if g:SuperTabMappingForward == "<tab>"
Expand Down Expand Up @@ -217,8 +106,10 @@ endf
fun s:GetSnippet(word, scope)
let word = a:word | let snippet = ''
while snippet == ''
if exists('s:multi_snips["'.a:scope.'"]["'.escape(word, '\"').'"]')
let snippet = s:ChooseSnippet(a:scope, word)
let snippetD = get(snipMate#GetSnippets([a:scope], word),word, {})
if !empty(snippetD)
let s = s:ChooseSnippet(snippetD)
let snippet = s
if snippet == '' | break | endif
else
if match(word, '\W') == -1 | break | endif
Expand All @@ -231,10 +122,11 @@ fun s:GetSnippet(word, scope)
return [word, snippet]
endf

fun s:ChooseSnippet(scope, trigger)
" snippets: dict containing snippets by name
" usually this is just {'default' : snippet_contents }
fun s:ChooseSnippet(snippets)
let snippet = []
let triggerDict = get(s:multi_snips[a:scope], a:trigger, {})
let keys = keys(triggerDict)
let keys = keys(a:snippets)
let i = 1
for snip in keys
let snippet += [i.'. '.snip]
Expand All @@ -245,7 +137,12 @@ fun s:ChooseSnippet(scope, trigger)
else
let idx = inputlist(snippet) - 1
endif
return idx == -1 ? '' : triggerDict[keys[idx]]
" if a:snippets[..] is a String Call returns it
" If it's a function or a function string the result is returned
if idx == -1
return ''
endif
return funcref#Call(a:snippets[keys(a:snippets)[idx]])
endf

fun! ShowAvailableSnips()
Expand All @@ -258,17 +155,16 @@ fun! ShowAvailableSnips()
endif
let matchlen = 0
let matches = []
for scope in [bufnr('%')] + split(&ft, '\.') + ['_']
for trigger in keys(get(s:multi_snips, scope, {}))
for word in words
if word == ''
let matches += [trigger] " Show all matches if word is empty
elseif trigger =~ '^'.word
let matches += [trigger]
let len = len(word)
if len > matchlen | let matchlen = len | endif
endif
endfor
let snips = snipMate#GetSnippets([bufnr('%')] + split(&ft, '\.') + ['_'], word.'*')
for trigger in keys(snips)
for word in words
if word == ''
let matches += [trigger] " Show all matches if word is empty
elseif trigger =~ '^'.word
let matches += [trigger]
let len = len(word)
if len > matchlen | let matchlen = len | endif
endif
endfor
endfor

Expand Down
10 changes: 10 additions & 0 deletions snipMate-addon-info.txt
@@ -0,0 +1,10 @@
{
"name" : "snipMate",
"author" : "Michael Sanders -> original project http://github.com/msanders/snipmate.vim",
"maintainer" : "Marc Weber <marco-oweber@gmx.de> (I mantain this fork only)",
"repository" : {"type": "git", "url": "git://github.com/MarcWeber/snipMate.vim.git"},
"dependencies" : {
"vim-addon-mw-utils": {}
},
"description" : "TextMate-style snippets for Vim - this fork loads snippets on demand lazily thus no need to reload any snippets ever"
}

0 comments on commit d9f239c

Please sign in to comment.