home/.vim/bundle/vim-template/plugin/templates.vim
Salt 58b1e98578 .vim: Dammit
Forgot to trim .git information from plugins
2017-08-16 14:37:46 -05:00

524 lines
16 KiB
VimL

"
" Template system for Vim
"
" Copyright (C) 2012 Adrian Perez de Castro <aperez@igalia.com>
" Copyright (C) 2005 Adrian Perez de Castro <the.lightman@gmail.com>
"
" Distributed under terms of the MIT license.
"
if exists("g:templates_plugin_loaded")
finish
endif
let g:templates_plugin_loaded = 1
if !exists('g:templates_name_prefix')
let g:templates_name_prefix = ".vim-template:"
endif
if !exists('g:templates_global_name_prefix')
let g:templates_global_name_prefix = "=template="
endif
if !exists('g:templates_debug')
let g:templates_debug = 0
endif
if !exists('g:templates_tr_in')
let g:templates_tr_in = [ '.', '*', '?' ]
endif
if !exists('g:templates_tr_out')
let g:templates_tr_out = [ '\.', '.*', '\?' ]
endif
if !exists('g:templates_fuzzy_start')
let g:templates_fuzzy_start = 1
endif
if !exists('g:templates_search_height')
" First try to find the deprecated option
if exists('g:template_max_depth')
echom('g:template_max_depth is deprecated in favor of g:templates_search_height')
let g:templates_search_height = g:template_max_depth != 0 ? g:template_max_depth : -1
endif
if(!exists('g:templates_search_height'))
let g:templates_search_height = -1
endif
endif
if !exists('g:templates_directory')
let g:templates_directory = []
elseif type(g:templates_directory) == type('')
" Convert string value to a list with one element.
let s:tmp = g:templates_directory
unlet g:templates_directory
let g:templates_directory = [ s:tmp ]
unlet s:tmp
endif
if !exists('g:templates_no_builtin_templates')
let g:templates_no_builtin_templates = 0
endif
if !exists('g:templates_user_variables')
let g:templates_user_variables = []
endif
" Put template system autocommands in their own group. {{{1
if !exists('g:templates_no_autocmd')
let g:templates_no_autocmd = 0
endif
if !g:templates_no_autocmd
augroup Templating
autocmd!
autocmd BufNewFile * call <SID>TLoad()
augroup END
endif
function <SID>Debug(mesg)
if g:templates_debug
echom(a:mesg)
endif
endfunction
" normalize the path
" replace the windows path sep \ with /
function <SID>NormalizePath(path)
return substitute(a:path, "\\", "/", "g")
endfunction
" Template searching. {{{1
" Returns a string containing the path of the parent directory of the given
" path. Works like dirname(3). It also simplifies the given path.
function <SID>DirName(path)
let l:tmp = <SID>NormalizePath(a:path)
return substitute(l:tmp, "[^/][^/]*/*$", "", "")
endfunction
" Default templates directory
let s:default_template_dir = <SID>DirName(<SID>DirName(expand("<sfile>"))) . "templates"
" Find the target template in windows
"
" In windows while we clone the symbol link from github
" it will turn to normal file, so we use this function
" to figure out the destination file
function <SID>TFindLink(path, template)
if !filereadable(a:path . a:template)
return a:template
endif
let l:content = readfile(a:path . a:template, "b")
if len(l:content) != 1
return a:template
endif
if filereadable(a:path . l:content[0])
return <SID>TFindLink(a:path, l:content[0])
else
return a:template
endif
endfunction
" Translate a template file name into a regular expression to test for matching
" against a given filename. As of writing this behavior is something like this:
" (with a g:templates_name_prefix set as 'template.')
"
" template.py -> ^.*py$
"
" template.test.py -> ^.*test.py$
"
function <SID>TemplateToRegex(template, prefix)
let l:template_base_name = fnamemodify(a:template,":t")
let l:template_glob = strpart(l:template_base_name, len(a:prefix))
" Translate the template's glob into a normal regular expression
let l:in_escape_mode = 0
let l:template_regex = ""
for l:c in split(l:template_glob, '\zs')
if l:in_escape_mode == 1
if l:c == '\'
let l:template_regex = l:template_regex . '\\'
else
let l:template_regex = l:template_regex . l:c
endif
let l:in_escape_mode = 0
else
if l:c == '\'
let l:in_escape_mode = 1
else
let l:tr_index = index(g:templates_tr_in, l:c)
if l:tr_index != -1
let l:template_regex = l:template_regex . g:templates_tr_out[l:tr_index]
else
let l:template_regex = l:template_regex . l:c
endif
endif
endif
endfor
if g:templates_fuzzy_start
return l:template_regex . '$'
else
return '^' . l:template_regex . '$'
endif
endfunction
" Given a template and filename, return a score on how well the template matches
" the given filename. If the template does not match the file name at all,
" return 0
function <SID>TemplateBaseNameTest(template, prefix, filename)
let l:tregex = <SID>TemplateToRegex(a:template, a:prefix)
" Ensure that we got a valid regex
if l:tregex == ""
return 0
endif
" For now only use the base of the filename.. this may change later
" *Note* we also have to be careful because a:filename may also be the passed
" in text from TLoadCmd...
let l:filename_chopped = fnamemodify(a:filename,":t")
" Check for a match
let l:regex_result = match(l:filename_chopped,l:tregex)
if l:regex_result != -1
" For a match return a score based on the regex length
return len(l:tregex)
else
" No match
return 0
endif
endfunction
" Returns the most specific / highest scored template file found in the given
" path. Template files are found by using a glob operation on the current path
" and the setting of g:templates_name_prefix. If no template is found in the
" given directory, return an empty string
function <SID>TDirectorySearch(path, template_prefix, file_name)
let l:picked_template = ""
let l:picked_template_score = 0
" Use find if possible as it will also get hidden files on nix systems. Use
" builtin glob as a fallback
if executable("find") && !has("win32") && !has("win64")
let l:find_cmd = '`find -L ' . shellescape(a:path) . ' -maxdepth 1 -type f -name ' . shellescape(a:template_prefix . '*' ) . '`'
call <SID>Debug("Executing " . l:find_cmd)
let l:glob_results = glob(l:find_cmd)
if v:shell_error != 0
call <SID>Debug("Could not execute find command")
unlet l:glob_results
endif
endif
if !exists("l:glob_results")
call <SID>Debug("Using fallback glob")
let l:glob_results = glob(a:path . a:template_prefix . "*")
endif
let l:templates = split(l:glob_results, "\n")
for template in l:templates
" Make sure the template is readable
if filereadable(template)
let l:current_score =
\<SID>TemplateBaseNameTest(template, a:template_prefix, a:file_name)
call <SID>Debug("template: " . template . " got scored: " . l:current_score)
" Pick that template only if it beats the currently picked template
" (here we make the assumption that template name length ~= template
" specifity / score)
if l:current_score > l:picked_template_score
let l:picked_template = template
let l:picked_template_score = l:current_score
endif
endif
endfor
if l:picked_template != ""
call <SID>Debug("Picked template: " . l:picked_template)
else
call <SID>Debug("No template found")
endif
return l:picked_template
endfunction
" Searches for a [template] in a given [path].
"
" If [height] is [-1] the template is searched for in the given directory and
" all parents in its directory structure
"
" If [height] is [0] no searching is done in the given directory or any
" parents
"
" If [height] is [1] only the given directory is searched
"
" If [height] is greater than one, n parents and the given directory will be
" searched where n is equal to height - 1
"
" If no template is found an empty string is returned.
"
function <SID>TSearch(path, template_prefix, file_name, height)
if (a:height != 0)
" pick a template from the current path
let l:picked_template = <SID>TDirectorySearch(a:path, a:template_prefix, a:file_name)
if l:picked_template != ""
return l:picked_template
else
let l:pathUp = <SID>DirName(a:path)
if l:pathUp != a:path
let l:new_height = a:height >= 0 ? a:height - 1 : a:height
return <SID>TSearch(l:pathUp, a:template_prefix, a:file_name, l:new_height)
endif
endif
endif
" Ooops, either we cannot go up in the path or [height] reached 0
return ""
endfunction
" Tries to find valid templates using the global g:templates_name_prefix as a glob
" matcher for template files. The search is done as follows:
" 1. The [path] passed to the function, [upwards] times up.
" 2. The g:templates_directory directory, if it exists.
" 3. Built-in templates from s:default_template_dir.
" Returns an empty string if no template is found.
"
function <SID>TFind(path, name, up)
let l:tmpl = <SID>TSearch(a:path, g:templates_name_prefix, a:name, a:up)
if l:tmpl != ''
return l:tmpl
endif
for l:directory in g:templates_directory
let l:directory = <SID>NormalizePath(expand(l:directory) . '/')
if isdirectory(l:directory)
let l:tmpl = <SID>TSearch(l:directory, g:templates_global_name_prefix, a:name, 1)
if l:tmpl != ''
return l:tmpl
endif
endif
endfor
if g:templates_no_builtin_templates
return ''
endif
return <SID>TSearch(<SID>NormalizePath(expand(s:default_template_dir) . '/'), g:templates_global_name_prefix, a:name, 1)
endfunction
" Escapes a string for use in a regex expression where the regex uses / as the
" delimiter. Must be used with Magic Mode off /V
"
function <SID>EscapeRegex(raw)
return escape(a:raw, '/')
endfunction
" Template variable expansion. {{{1
" Makes a single [variable] expansion, using [value] as replacement.
"
function <SID>TExpand(variable, value)
silent! execute "%s/\\V%" . <SID>EscapeRegex(a:variable) . "%/" . <SID>EscapeRegex(a:value) . "/g"
endfunction
" Performs variable expansion in a template once it was loaded {{{2
"
function <SID>TExpandVars()
" Date/time values
let l:day = strftime("%d")
let l:year = strftime("%Y")
let l:month = strftime("%m")
let l:monshort = strftime("%b")
let l:monfull = strftime("%B")
let l:time = strftime("%H:%M")
let l:date = exists("g:dateformat") ? strftime(g:dateformat) :
\ (l:year . "-" . l:month . "-" . l:day)
let l:fdate = l:date . " " . l:time
let l:filen = expand("%:t:r:r:r")
let l:filex = expand("%:e")
let l:filec = expand("%:t")
let l:fdir = expand("%:p:h:t")
let l:hostn = hostname()
let l:user = exists("g:username") ? g:username :
\ (exists("g:user") ? g:user : $USER)
let l:email = exists("g:email") ? g:email : (l:user . "@" . l:hostn)
let l:guard = toupper(substitute(l:filec, "[^a-zA-Z0-9]", "_", "g"))
let l:class = substitute(l:filen, "\\([a-zA-Z]\\+\\)", "\\u\\1\\e", "g")
let l:macroclass = toupper(l:class)
let l:camelclass = substitute(l:class, "_", "", "g")
" Finally, perform expansions
call <SID>TExpand("DAY", l:day)
call <SID>TExpand("YEAR", l:year)
call <SID>TExpand("DATE", l:date)
call <SID>TExpand("TIME", l:time)
call <SID>TExpand("USER", l:user)
call <SID>TExpand("FDATE", l:fdate)
call <SID>TExpand("MONTH", l:month)
call <SID>TExpand("MONTHSHORT", l:monshort)
call <SID>TExpand("MONTHFULL", l:monfull)
call <SID>TExpand("FILE", l:filen)
call <SID>TExpand("FFILE", l:filec)
call <SID>TExpand("FDIR", l:fdir)
call <SID>TExpand("EXT", l:filex)
call <SID>TExpand("MAIL", l:email)
call <SID>TExpand("HOST", l:hostn)
call <SID>TExpand("GUARD", l:guard)
call <SID>TExpand("CLASS", l:class)
call <SID>TExpand("MACROCLASS", l:macroclass)
call <SID>TExpand("CAMELCLASS", l:camelclass)
call <SID>TExpand("LICENSE", exists("g:license") ? g:license : "MIT")
" Perform expansions for user-defined variables
for [l:varname, l:funcname] in g:templates_user_variables
let l:value = function(funcname)()
call <SID>TExpand(l:varname, l:value)
endfor
endfunction
" }}}2
" Puts the cursor either at the first line of the file or in the place of
" the template where the %HERE% string is found, removing %HERE% from the
" template.
"
function <SID>TPutCursor()
0 " Go to first line before searching
if search("%HERE%", "W")
let l:column = col(".")
let l:lineno = line(".")
s/%HERE%//
call cursor(l:lineno, l:column)
endif
endfunction
" File name utils
"
" Ensures that the given file name is safe to be opened and will not be shell
" expanded
function <SID>NeuterFileName(filename)
let l:neutered = fnameescape(a:filename)
call <SID>Debug("Neutered " . a:filename . " to " . l:neutered)
return l:neutered
endfunction
" Template application. {{{1
" Loads a template for the current buffer, substitutes variables and puts
" cursor at %HERE%. Used to implement the BufNewFile autocommand.
"
function <SID>TLoad()
if !line2byte( line( '$' ) + 1 ) == -1
return
endif
let l:file_name = expand("%:p")
let l:file_dir = <SID>DirName(l:file_name)
let l:depth = g:templates_search_height
let l:tFile = <SID>TFind(l:file_dir, l:file_name, l:depth)
call <SID>TLoadTemplate(l:tFile, 0)
endfunction
" Like the previous one, TLoad(), but intended to be called with an argument
" that either is a filename (so the file is loaded as a template) or
" a template suffix (and the template is searched as usual). Of course this
" makes variable expansion and cursor positioning.
"
function <SID>TLoadCmd(template, position)
if filereadable(a:template)
let l:tFile = a:template
else
let l:height = g:templates_search_height
let l:tName = g:templates_global_name_prefix . a:template
let l:file_name = expand("%:p")
let l:file_dir = <SID>DirName(l:file_name)
let l:tFile = <SID>TFind(l:file_dir, a:template, l:height)
endif
call <SID>TLoadTemplate(l:tFile, a:position)
endfunction
" Load the given file as a template
function <SID>TLoadTemplate(template, position)
if a:template != ""
let l:deleteLastLine = 0
if line('$') == 1 && getline(1) == ''
let l:deleteLastLine = 1
endif
" Read template file and expand variables in it.
let l:safeFileName = <SID>NeuterFileName(a:template)
if a:position == 0 || l:deleteLastLine == 1
execute "keepalt 0r " . l:safeFileName
else
execute "keepalt r " . l:safeFileName
endif
call <SID>TExpandVars()
if l:deleteLastLine == 1
" Loading a template into an empty buffer leaves an extra blank line at the bottom, delete it
execute line('$') . "d _"
endif
call <SID>TPutCursor()
setlocal nomodified
endif
endfunction
" Commands {{{1
" Just calls the above function, pass either a filename or a template
" suffix, as explained before =)
"
fun ListTemplateSuffixes(A,P,L)
let l:templates = split(globpath(s:default_template_dir, g:templates_global_name_prefix . a:A . "*"), "\n")
let l:res = []
for t in templates
let l:suffix = substitute(t, ".*\\.", "", "")
call add(l:res, l:suffix)
endfor
return l:res
endfun
command -nargs=1 -complete=customlist,ListTemplateSuffixes Template call <SID>TLoadCmd("<args>", 0)
command -nargs=1 -complete=customlist,ListTemplateSuffixes TemplateHere call <SID>TLoadCmd("<args>", 1)
" Syntax autocommands {{{1
"
" Enable the vim-template syntax for template files
" Usually we'd put this in the ftdetect folder, but because
" g:templates_name_prefix doesn't get defined early enough we have to add the
" template detection from the plugin itself
execute "au BufNewFile,BufRead " . g:templates_name_prefix . "* "
\. "let b:vim_template_subtype = &filetype | "
\. "set ft=vim-template"
if !g:templates_no_builtin_templates
execute "au BufNewFile,BufRead "
\. s:default_template_dir . "/" . g:templates_global_name_prefix . "* "
\. "let b:vim_template_subtype = &filetype | "
\. "set ft=vim-template"
endif
for s:directory in g:templates_directory
let s:directory = <SID>NormalizePath(expand(s:directory) . '/')
if isdirectory(s:directory)
execute "au BufNewFile,BufRead "
\. s:directory . "/" . g:templates_global_name_prefix . "* "
\. "let b:vim_template_subtype = &filetype | "
\. "set ft=vim-template"
endif
unlet s:directory
endfor
" vim: fdm=marker