Poor man's snippets

1 Point

Lifepillar Lifepillar

8 months ago

Define your snippets for <filetype> in ~/.vim/snippets/<filetype>.txt and call LoadSnippets() in ~/.vim/after/<filetype>.vim. See the comment for usage.

" Snippets are collected in plain text files named after the file type.
" Example snippet:
"
" snippet for
" for (___) {
"   ___
" }
"
" A snippet definition starts with the `snippet' keyword followed by the name
" of the snippet (for). The remaining lines contain the text that must be put
" (verbatim) into the buffer.
"
" Three underscores are used for placeholders. At least one placeholder must be
" present. The cursor is put at the first placeholder after expanding the
" snippet. Use CTRL-B in Insert mode to jump to the next placeholder.
"
" To expand a snippet, type its name followed by … (alt-. in macOS).

fun! LoadSnippets()
  let l:file = readfile($HOME.'/.vim/snippets/'.&ft.'.txt')
  let b:snippets = {}
  for l:line in l:file
    if l:line =~ '^snippet'
      let l:key = matchstr(l:line, '^snippet\s\+\zs.\+$')
      let b:snippets[l:key] = []
    else
      call add(b:snippets[l:key], l:line)
    endif
  endfor
  inoremap <buffer> <c-b> <esc>gn"_c
endf

fun! ExpandSnippet()
  let l:key = matchstr(getline('.'), '\S\+\%'.col('.').'c')
  let l:snippet = get(b:snippets, l:key, [])
  if !empty(l:snippet)
    let l:indent = matchstr(getline('.'), '^\s\+')
    call setline('.', l:indent . l:snippet[0])
    call append('.', map(l:snippet[1:-1], { _,t -> l:indent . t}))
    call search('___', 'csW')
    let @/ = '___' " Enable moving to the next placeholder with gnc
    normal "_3x
    return ''
  endif
  return '…'
endf

inoremap <silent> … <c-r>=exists('b:snippets') ? ExpandSnippet() : '…'<cr>