"=============================================================================
" sonictemplate.vim
" Author: Yasuhiro Matsumoto <mattn.jp@gmail.com>
" Last Change: 15-Apr-2012.

let s:save_cpo = &cpo
set cpo&vim

let s:tmpldir = []
if exists('g:sonictemplate_vim_template_dir')
  if type(g:sonictemplate_vim_template_dir) == 3
    let s:tmpldir += map(g:sonictemplate_vim_template_dir, 'fnamemodify(expand(v:val), ":p")')
  else
    call add(s:tmpldir, fnamemodify(expand(g:sonictemplate_vim_template_dir), ":p"))
  endif
endif
call add(s:tmpldir, expand('<sfile>:p:h:h') . '/template/')

function! sonictemplate#select(mode) abort
  let name = input(':Template ', '', 'customlist,sonictemplate#complete')
  if name == ''
    return
  endif
  call sonictemplate#apply(name, a:mode)
endfunction

function! sonictemplate#complete(lead, cmdline, curpos) abort
  let ft = &ft
  let tmp = []
  for tmpldir in s:tmpldir
    let tmp += map(split(globpath(join([tmpldir, ft], '/'), (search('[^ \t]', 'wn') ? 'snip-' : 'base-') . a:lead . '*.*'), "\n"), 'fnamemodify(v:val, ":t:r")[5:]')
  endfor
  if len(tmp) == 0
    let ft = tolower(synIDattr(synID(line("."), col("."), 1), "name"))
    if len(ft) > 0
      for tmpldir in s:tmpldir
        let tmp += map(split(globpath(join([tmpldir, ft], '/'), (search('[^ \t]', 'wn') ? 'snip-' : 'base-') . a:lead . '*.*'), "\n"), 'fnamemodify(v:val, ":t:r")[5:]')
      endfor
    endif
  endif
  let candidate = []
  for c in tmp
    if index(candidate, c) == -1
      call add(candidate, c)
    endif
  endfor
  return candidate
endfunction

function! sonictemplate#apply(name, mode) abort
  let name = matchstr(a:name, '\S\+')
  let buffer_is_not_empty = search('[^ \t]', 'wn')
  let fs = []
  for tmpldir in s:tmpldir
    let ft = &ft
    let fsl = split(globpath(join([tmpldir, ft], '/'), (search('[^ \t]', 'wn') ? 'snip-' : 'base-') . name . '.*'), "\n")
    if len(fsl) == 0
      let ft = tolower(synIDattr(synID(line("."), col("."), 1), "name"))
      if len(ft) > 0
        let fsl = split(globpath(join([tmpldir, ft], '/'), (search('[^ \t]', 'wn') ? 'snip-' : 'base-') . name . '.*'), "\n")
      endif
    endif
    let fs += fsl
  endfor
  if len(fs) == 0
    echomsg 'Template '.name.' is not exists.'
    return
  endif
  let f = fs[0]
  if !filereadable(f)
    echomsg 'Template '.name.' is not exists.'
    return
  endif
  let c = join(readfile(f, "b"), "\n")
  let c = substitute(c, '{{_name_}}', expand('%:t:r:'), 'g')
  let tmp = c
  let mx = '{{_input_:\(.\{-}\)}}'
  let vars = []
  while 1
    let match = matchstr(tmp, mx)
    if len(match) == 0
      break
    endif
    let var = substitute(match, mx, '\1', 'ig')
    if index(vars, var) == -1
      call add(vars, var)
    endif
    let tmp = tmp[stridx(tmp, match) + len(match):]
  endwhile
  for var in vars
    let val = input(var . ":")
    let c = substitute(c, '\V{{_input_:'.var.'}}', '\=val', 'g')
  endfor
  sandbox let c = substitute(c, '{{_if_:\(.\{-}\);\(.\{-}\)\(;\(.\{-}\)\)\{-}}}', '\=eval(submatch(1))?submatch(2):submatch(4)', 'g')
  sandbox let c = substitute(c, '{{_expr_:\(.\{-}\)}}', '\=eval(submatch(1))', 'g')
  if len(c) == 0
    return
  endif
  if !buffer_is_not_empty
    silent! %d _
    silent! put = c
    silent! normal! ggdd
  else
    if c[len(c)-1] == "\n"
      let c = c[:-2]
    endif
    let line = getline('.')
    let indent = matchstr(line, '^\(\s*\)')
    if line =~ '^\s*$' && line('.') != line('$')
      silent! normal dd
    endif
    let c = indent . substitute(c, "\n", "\n".indent, 'g')
    if len(indent) && (&expandtab || indent =~ '^ \+$')
      let c = substitute(c, "\t", repeat(' ', min([len(indent), &tabstop])), 'g')
    endif
    silent! put! = c
  endif
  if stridx(c, '{{_cursor_}}') != -1
    if a:mode == 'n'
      silent! call search('\zs{{_cursor_}}', 'w')
      silent! foldopen
      silent! exe "normal ".repeat("x", 12)
    else
      silent! call search('{{_cursor_}}\zs', 'w')
      silent! foldopen
      call feedkeys(repeat("\<bs>", 12))
    endif
  endif
endfunction

let &cpo = s:save_cpo
unlet s:save_cpo

" vim:set et: