I broke up with neovim....vim is my best friend now
This commit is contained in:
61
dot_vim/plugged/vim-vsnip/autoload/vsnip/indent.vim
Normal file
61
dot_vim/plugged/vim-vsnip/autoload/vsnip/indent.vim
Normal file
@@ -0,0 +1,61 @@
|
||||
"
|
||||
" vsnip#indent#get_one_indent
|
||||
"
|
||||
function! vsnip#indent#get_one_indent() abort
|
||||
return !&expandtab ? "\t" : repeat(' ', &shiftwidth ? &shiftwidth : &tabstop)
|
||||
endfunction
|
||||
|
||||
"
|
||||
" vsnip#indent#get_base_indent
|
||||
"
|
||||
function! vsnip#indent#get_base_indent(text) abort
|
||||
return matchstr(a:text, '^\s*')
|
||||
endfunction
|
||||
|
||||
"
|
||||
" vsnip#indent#adjust_snippet_body
|
||||
"
|
||||
function! vsnip#indent#adjust_snippet_body(line, text) abort
|
||||
let l:one_indent = vsnip#indent#get_one_indent()
|
||||
let l:base_indent = vsnip#indent#get_base_indent(a:line)
|
||||
let l:text = a:text
|
||||
if l:one_indent !=# "\t"
|
||||
while match(l:text, "\\%(^\\|\n\\)\\s*\\zs\\t") != -1
|
||||
let l:text = substitute(l:text, "\\%(^\\|\n\\)\\s*\\zs\\t", l:one_indent, 'g') " convert \t as one indent
|
||||
endwhile
|
||||
endif
|
||||
let l:text = substitute(l:text, "\n\\zs", l:base_indent, 'g') " add base_indent for all lines
|
||||
let l:text = substitute(l:text, "\n\\s*\\ze\n", "\n", 'g') " remove empty line's indent
|
||||
return l:text
|
||||
endfunction
|
||||
|
||||
"
|
||||
" vsnip#indent#trim_base_indent
|
||||
"
|
||||
function! vsnip#indent#trim_base_indent(text) abort
|
||||
let l:is_char_wise = match(a:text, "\n$") == -1
|
||||
let l:text = substitute(a:text, "\n$", '', 'g')
|
||||
|
||||
let l:is_first_line = v:true
|
||||
let l:base_indent = ''
|
||||
for l:line in split(l:text, "\n", v:true)
|
||||
" Ignore the first line when the text created as char-wise.
|
||||
if l:is_char_wise && l:is_first_line
|
||||
let l:is_first_line = v:false
|
||||
continue
|
||||
endif
|
||||
|
||||
" Ignore empty line.
|
||||
if l:line ==# ''
|
||||
continue
|
||||
endif
|
||||
|
||||
" Detect most minimum base indent.
|
||||
let l:indent = matchstr(l:line, '^\s*')
|
||||
if l:base_indent ==# '' || strlen(l:indent) < strlen(l:base_indent)
|
||||
let l:base_indent = l:indent
|
||||
endif
|
||||
endfor
|
||||
return substitute(l:text, "\\%(^\\|\n\\)\\zs\\V" . l:base_indent, '', 'g')
|
||||
endfunction
|
||||
|
||||
223
dot_vim/plugged/vim-vsnip/autoload/vsnip/parser/combinator.vim
Normal file
223
dot_vim/plugged/vim-vsnip/autoload/vsnip/parser/combinator.vim
Normal file
@@ -0,0 +1,223 @@
|
||||
function! vsnip#parser#combinator#import() abort
|
||||
return {
|
||||
\ 'skip': function('s:skip'),
|
||||
\ 'token': function('s:token'),
|
||||
\ 'many': function('s:many'),
|
||||
\ 'or': function('s:or'),
|
||||
\ 'seq': function('s:seq'),
|
||||
\ 'pattern': function('s:pattern'),
|
||||
\ 'lazy': function('s:lazy'),
|
||||
\ 'option': function('s:option'),
|
||||
\ 'map': function('s:map')
|
||||
\ }
|
||||
endfunction
|
||||
|
||||
"
|
||||
" string.
|
||||
"
|
||||
function! s:skip(stop, escape) abort
|
||||
let l:fn = {}
|
||||
let l:fn.stop = a:stop
|
||||
let l:fn.escape = a:escape
|
||||
function! l:fn.parse(text, pos) abort
|
||||
let l:pos = a:pos
|
||||
let l:value = ''
|
||||
|
||||
let l:len = strchars(a:text)
|
||||
while l:pos < l:len
|
||||
let l:char = s:getchar(a:text, l:pos)
|
||||
|
||||
" check escaped stop chars.
|
||||
if l:char ==# '\'
|
||||
let l:pos += 1
|
||||
let l:char = s:getchar(a:text, l:pos)
|
||||
if index(self.stop + self.escape + ['\'], l:char) == -1
|
||||
let l:value .= '\'
|
||||
continue " ignore invalid escape char.
|
||||
endif
|
||||
let l:pos += 1
|
||||
let l:value .= l:char
|
||||
continue
|
||||
endif
|
||||
|
||||
" check stop char.
|
||||
if index(self.stop, l:char) >= 0
|
||||
if a:pos != l:pos
|
||||
return [v:true, [strcharpart(a:text, a:pos, l:pos - a:pos), l:value], l:pos]
|
||||
else
|
||||
return [v:false, v:null, l:pos]
|
||||
endif
|
||||
endif
|
||||
|
||||
let l:value .= l:char
|
||||
let l:pos += 1
|
||||
endwhile
|
||||
|
||||
" everything was string.
|
||||
return [v:true, [strcharpart(a:text, a:pos), l:value], l:len]
|
||||
endfunction
|
||||
return l:fn
|
||||
endfunction
|
||||
|
||||
"
|
||||
" token.
|
||||
"
|
||||
function! s:token(token) abort
|
||||
let l:fn = {}
|
||||
let l:fn.token = a:token
|
||||
function! l:fn.parse(text, pos) abort
|
||||
let l:token_len = strchars(self.token)
|
||||
let l:value = strcharpart(a:text, a:pos, l:token_len)
|
||||
if l:value ==# self.token
|
||||
return [v:true, self.token, a:pos + l:token_len]
|
||||
endif
|
||||
return [v:false, v:null, a:pos]
|
||||
endfunction
|
||||
return l:fn
|
||||
endfunction
|
||||
|
||||
"
|
||||
" many.
|
||||
"
|
||||
function! s:many(parser) abort
|
||||
let l:fn = {}
|
||||
let l:fn.parser = a:parser
|
||||
function! l:fn.parse(text, pos) abort
|
||||
let l:pos = a:pos
|
||||
let l:values = []
|
||||
|
||||
let l:len = strchars(a:text)
|
||||
while l:pos < l:len
|
||||
let l:parsed = self.parser.parse(a:text, l:pos)
|
||||
if l:parsed[0]
|
||||
call add(l:values, l:parsed[1])
|
||||
let l:pos = l:parsed[2]
|
||||
else
|
||||
break
|
||||
endif
|
||||
endwhile
|
||||
if len(l:values) > 0
|
||||
return [v:true, l:values, l:pos]
|
||||
else
|
||||
return [v:false, v:null, l:pos]
|
||||
endif
|
||||
endfunction
|
||||
return l:fn
|
||||
endfunction
|
||||
|
||||
"
|
||||
" or.
|
||||
"
|
||||
function! s:or(...) abort
|
||||
let l:fn = {}
|
||||
let l:fn.parsers = a:000
|
||||
function! l:fn.parse(text, pos) abort
|
||||
for l:parser in self.parsers
|
||||
let l:parsed = l:parser.parse(a:text, a:pos)
|
||||
if l:parsed[0]
|
||||
return l:parsed
|
||||
endif
|
||||
endfor
|
||||
return [v:false, v:null, a:pos]
|
||||
endfunction
|
||||
return l:fn
|
||||
endfunction
|
||||
|
||||
"
|
||||
" seq.
|
||||
"
|
||||
function! s:seq(...) abort
|
||||
let l:fn = {}
|
||||
let l:fn.parsers = a:000
|
||||
function! l:fn.parse(text, pos) abort
|
||||
let l:pos = a:pos
|
||||
let l:values = []
|
||||
for l:parser in self.parsers
|
||||
let l:parsed = l:parser.parse(a:text, l:pos)
|
||||
if !l:parsed[0]
|
||||
return [v:false, v:null, a:pos]
|
||||
endif
|
||||
call add(l:values, l:parsed[1])
|
||||
let l:pos = l:parsed[2]
|
||||
endfor
|
||||
return [v:true, l:values, l:pos]
|
||||
endfunction
|
||||
return l:fn
|
||||
endfunction
|
||||
|
||||
"
|
||||
" lazy.
|
||||
"
|
||||
function! s:lazy(callback) abort
|
||||
let l:fn = {}
|
||||
let l:fn.callback = a:callback
|
||||
function! l:fn.parse(text, pos) abort
|
||||
if !has_key(self, 'parser')
|
||||
let self.parser = self.callback()
|
||||
endif
|
||||
return self.parser.parse(a:text, a:pos)
|
||||
endfunction
|
||||
return l:fn
|
||||
endfunction
|
||||
|
||||
"
|
||||
" pattern.
|
||||
"
|
||||
function! s:pattern(pattern) abort
|
||||
let l:fn = {}
|
||||
let l:fn.pattern = a:pattern[0] ==# '^' ? a:pattern : '^' . a:pattern
|
||||
function! l:fn.parse(text, pos) abort
|
||||
let l:text = strcharpart(a:text, a:pos)
|
||||
let l:matches = matchstrpos(l:text, self.pattern, 0, 1)
|
||||
if l:matches[0] !=# ''
|
||||
return [v:true, l:matches[0], a:pos + l:matches[2]]
|
||||
endif
|
||||
return [v:false, v:null, a:pos]
|
||||
endfunction
|
||||
return l:fn
|
||||
endfunction
|
||||
|
||||
"
|
||||
" map.
|
||||
"
|
||||
function! s:map(parser, callback) abort
|
||||
let l:fn = {}
|
||||
let l:fn.callback = a:callback
|
||||
let l:fn.parser = a:parser
|
||||
function! l:fn.parse(text, pos) abort
|
||||
let l:parsed = self.parser.parse(a:text, a:pos)
|
||||
if l:parsed[0]
|
||||
return [v:true, self.callback(l:parsed[1]), l:parsed[2]]
|
||||
endif
|
||||
return l:parsed
|
||||
endfunction
|
||||
return l:fn
|
||||
endfunction
|
||||
|
||||
"
|
||||
" option.
|
||||
"
|
||||
function! s:option(parser) abort
|
||||
let l:fn = {}
|
||||
let l:fn.parser = a:parser
|
||||
function! l:fn.parse(text, pos) abort
|
||||
let l:parsed = self.parser.parse(a:text, a:pos)
|
||||
if l:parsed[0]
|
||||
return l:parsed
|
||||
endif
|
||||
return [v:true, v:null, a:pos]
|
||||
endfunction
|
||||
return l:fn
|
||||
endfunction
|
||||
|
||||
"
|
||||
" getchar.
|
||||
"
|
||||
function! s:getchar(text, pos) abort
|
||||
let l:nr = strgetchar(a:text, a:pos)
|
||||
if l:nr != -1
|
||||
return nr2char(l:nr)
|
||||
endif
|
||||
return ''
|
||||
endfunction
|
||||
|
||||
10
dot_vim/plugged/vim-vsnip/autoload/vsnip/range.vim
Normal file
10
dot_vim/plugged/vim-vsnip/autoload/vsnip/range.vim
Normal file
@@ -0,0 +1,10 @@
|
||||
"
|
||||
" vsnip#range#cover
|
||||
"
|
||||
function! vsnip#range#cover(whole_range, target_range) abort
|
||||
let l:cover = v:true
|
||||
let l:cover = l:cover && (a:whole_range.start.line < a:target_range.start.line || a:whole_range.start.line == a:target_range.start.line && a:whole_range.start.character <= a:target_range.start.character)
|
||||
let l:cover = l:cover && (a:target_range.end.line < a:whole_range.end.line || a:target_range.end.line == a:whole_range.end.line && a:target_range.end.character <= a:whole_range.end.character)
|
||||
return l:cover
|
||||
endfunction
|
||||
|
||||
260
dot_vim/plugged/vim-vsnip/autoload/vsnip/session.vim
Normal file
260
dot_vim/plugged/vim-vsnip/autoload/vsnip/session.vim
Normal file
@@ -0,0 +1,260 @@
|
||||
let s:Snippet = vsnip#snippet#import()
|
||||
let s:TextEdit = vital#vsnip#import('VS.LSP.TextEdit')
|
||||
let s:Position = vital#vsnip#import('VS.LSP.Position')
|
||||
let s:Diff = vital#vsnip#import('VS.LSP.Diff')
|
||||
|
||||
"
|
||||
" import.
|
||||
"
|
||||
function! vsnip#session#import() abort
|
||||
return s:Session
|
||||
endfunction
|
||||
|
||||
let s:Session = {}
|
||||
|
||||
"
|
||||
" new.
|
||||
"
|
||||
function! s:Session.new(bufnr, position, text) abort
|
||||
return extend(deepcopy(s:Session), {
|
||||
\ 'bufnr': a:bufnr,
|
||||
\ 'buffer': getbufline(a:bufnr, '^', '$'),
|
||||
\ 'timer_id': -1,
|
||||
\ 'changedtick': getbufvar(a:bufnr, 'changedtick', 0),
|
||||
\ 'snippet': s:Snippet.new(a:position, vsnip#indent#adjust_snippet_body(getline('.'), a:text)),
|
||||
\ 'tabstop': -1,
|
||||
\ 'changenr': changenr(),
|
||||
\ 'changenrs': {},
|
||||
\ })
|
||||
endfunction
|
||||
|
||||
"
|
||||
" expand.
|
||||
"
|
||||
function! s:Session.expand() abort
|
||||
" insert snippet.
|
||||
call s:TextEdit.apply(self.bufnr, [{
|
||||
\ 'range': {
|
||||
\ 'start': self.snippet.position,
|
||||
\ 'end': self.snippet.position
|
||||
\ },
|
||||
\ 'newText': self.snippet.text()
|
||||
\ }])
|
||||
call self.store(changenr())
|
||||
endfunction
|
||||
|
||||
"
|
||||
" merge.
|
||||
"
|
||||
function! s:Session.merge(session) abort
|
||||
call s:TextEdit.apply(self.bufnr, self.snippet.sync())
|
||||
call self.store(self.changenr)
|
||||
|
||||
call a:session.expand()
|
||||
call self.snippet.merge(self.tabstop, a:session.snippet)
|
||||
call self.snippet.insert(deepcopy(a:session.snippet.position), a:session.snippet.children)
|
||||
call s:TextEdit.apply(self.bufnr, self.snippet.sync())
|
||||
call self.store(changenr())
|
||||
endfunction
|
||||
|
||||
"
|
||||
" jumpable.
|
||||
"
|
||||
function! s:Session.jumpable(direction) abort
|
||||
if a:direction == 1
|
||||
let l:jumpable = !empty(self.snippet.get_next_jump_point(self.tabstop))
|
||||
else
|
||||
let l:jumpable = !empty(self.snippet.get_prev_jump_point(self.tabstop))
|
||||
endif
|
||||
return l:jumpable
|
||||
endfunction
|
||||
|
||||
"
|
||||
" jump.
|
||||
"
|
||||
function! s:Session.jump(direction) abort
|
||||
call self.flush_changes()
|
||||
|
||||
if a:direction == 1
|
||||
let l:jump_point = self.snippet.get_next_jump_point(self.tabstop)
|
||||
else
|
||||
let l:jump_point = self.snippet.get_prev_jump_point(self.tabstop)
|
||||
endif
|
||||
|
||||
if empty(l:jump_point)
|
||||
return
|
||||
endif
|
||||
|
||||
let self.tabstop = l:jump_point.placeholder.id
|
||||
|
||||
" choice.
|
||||
if len(l:jump_point.placeholder.choice) > 0
|
||||
call self.choice(l:jump_point)
|
||||
|
||||
" select.
|
||||
elseif l:jump_point.range.start.character != l:jump_point.range.end.character
|
||||
call self.select(l:jump_point)
|
||||
|
||||
" move.
|
||||
else
|
||||
call self.move(l:jump_point)
|
||||
endif
|
||||
|
||||
doautocmd <nomodeline> User vsnip#jump
|
||||
endfunction
|
||||
|
||||
"
|
||||
" choice.
|
||||
"
|
||||
function! s:Session.choice(jump_point) abort
|
||||
call self.move(a:jump_point)
|
||||
|
||||
let l:fn = {}
|
||||
let l:fn.jump_point = a:jump_point
|
||||
function! l:fn.next_tick() abort
|
||||
if mode()[0] ==# 'i'
|
||||
let l:pos = s:Position.lsp_to_vim('%', self.jump_point.range.start)
|
||||
call complete(l:pos[1], map(copy(self.jump_point.placeholder.choice), { k, v -> {
|
||||
\ 'word': v.escaped,
|
||||
\ 'abbr': v.escaped,
|
||||
\ 'menu': '[vsnip]',
|
||||
\ 'kind': 'Choice'
|
||||
\ } }))
|
||||
endif
|
||||
endfunction
|
||||
call timer_start(g:vsnip_choice_delay, { -> l:fn.next_tick() })
|
||||
endfunction
|
||||
|
||||
"
|
||||
" select.
|
||||
"
|
||||
" @NOTE: Must work even if virtualedit=all/onmore or not.
|
||||
"
|
||||
function! s:Session.select(jump_point) abort
|
||||
let l:start_pos = s:Position.lsp_to_vim('%', a:jump_point.range.start)
|
||||
let l:end_pos = s:Position.lsp_to_vim('%', a:jump_point.range.end)
|
||||
|
||||
let l:cmd = ''
|
||||
let l:cmd .= "\<Cmd>set virtualedit=onemore\<CR>"
|
||||
let l:cmd .= mode()[0] ==# 'i' ? "\<Esc>" : ''
|
||||
let l:cmd .= printf("\<Cmd>call cursor(%s, %s)\<CR>", l:start_pos[0], l:start_pos[1])
|
||||
let l:cmd .= 'v'
|
||||
let l:cmd .= printf("\<Cmd>call cursor(%s, %s)\<CR>%s", l:end_pos[0], l:end_pos[1], &selection ==# 'exclusive' ? '' : 'h')
|
||||
if get(g:, 'vsnip_test_mode', v:false)
|
||||
let l:cmd .= "\<Esc>gv"
|
||||
endif
|
||||
let l:cmd .= printf("\<Cmd>set virtualedit=%s\<CR>", &virtualedit)
|
||||
let l:cmd .= "\<C-g>"
|
||||
call feedkeys(l:cmd, 'ni')
|
||||
endfunction
|
||||
|
||||
"
|
||||
" move.
|
||||
"
|
||||
" @NOTE: Must work even if virtualedit=all/onmore or not.
|
||||
"
|
||||
function! s:Session.move(jump_point) abort
|
||||
let l:pos = s:Position.lsp_to_vim('%', a:jump_point.range.end)
|
||||
|
||||
call cursor(l:pos)
|
||||
|
||||
if mode()[0] ==# 'n'
|
||||
if l:pos[1] != getcurpos()[2]
|
||||
call feedkeys('a', 'ni')
|
||||
else
|
||||
call feedkeys('i', 'ni')
|
||||
endif
|
||||
endif
|
||||
endfunction
|
||||
|
||||
"
|
||||
" refresh
|
||||
"
|
||||
function! s:Session.refresh() abort
|
||||
let self.buffer = getbufline(self.bufnr, '^', '$')
|
||||
let self.changedtick = getbufvar(self.bufnr, 'changedtick', 0)
|
||||
endfunction
|
||||
|
||||
"
|
||||
" on_insert_leave
|
||||
"
|
||||
function! s:Session.on_insert_leave() abort
|
||||
call self.flush_changes()
|
||||
endfunction
|
||||
|
||||
"
|
||||
" on_text_changed
|
||||
"
|
||||
function! s:Session.on_text_changed() abort
|
||||
if self.bufnr != bufnr('%')
|
||||
return vsnip#deactivate()
|
||||
endif
|
||||
|
||||
let l:changenr = changenr()
|
||||
|
||||
" save state.
|
||||
if self.changenr != l:changenr
|
||||
call self.store(self.changenr)
|
||||
if has_key(self.changenrs, l:changenr)
|
||||
let self.tabstop = self.changenrs[l:changenr].tabstop
|
||||
let self.snippet = self.changenrs[l:changenr].snippet
|
||||
let self.changenr = l:changenr
|
||||
let self.buffer = getbufline(self.bufnr, '^', '$')
|
||||
return
|
||||
endif
|
||||
endif
|
||||
|
||||
if g:vsnip_sync_delay == 0
|
||||
call self.flush_changes()
|
||||
elseif g:vsnip_sync_delay > 0
|
||||
call timer_stop(self.timer_id)
|
||||
let self.timer_id = timer_start(g:vsnip_sync_delay, { -> self.flush_changes() }, { 'repeat': 1 })
|
||||
endif
|
||||
endfunction
|
||||
|
||||
"
|
||||
" flush_changes
|
||||
"
|
||||
function! s:Session.flush_changes() abort
|
||||
let l:changedtick = getbufvar(self.bufnr, 'changedtick', 0)
|
||||
if self.changedtick == l:changedtick
|
||||
return
|
||||
endif
|
||||
let self.changedtick = l:changedtick
|
||||
|
||||
" compute diff.
|
||||
let l:buffer = getbufline(self.bufnr, '^', '$')
|
||||
let l:diff = s:Diff.compute(self.buffer, l:buffer)
|
||||
let self.buffer = l:buffer
|
||||
if l:diff.rangeLength == 0 && l:diff.text ==# ''
|
||||
return
|
||||
endif
|
||||
|
||||
" if follow succeeded, sync placeholders and write back to the buffer.
|
||||
if self.snippet.follow(self.tabstop, l:diff)
|
||||
try
|
||||
let l:text_edits = self.snippet.sync()
|
||||
if len(l:text_edits) > 0
|
||||
undojoin | call s:TextEdit.apply(self.bufnr, l:text_edits)
|
||||
endif
|
||||
call self.refresh()
|
||||
catch /.*/
|
||||
" TODO: More strict changenrs mangement.
|
||||
call vsnip#deactivate()
|
||||
endtry
|
||||
else
|
||||
call vsnip#deactivate()
|
||||
endif
|
||||
endfunction
|
||||
|
||||
"
|
||||
" save.
|
||||
"
|
||||
function! s:Session.store(changenr) abort
|
||||
let self.changenrs[a:changenr] = {
|
||||
\ 'tabstop': self.tabstop,
|
||||
\ 'snippet': deepcopy(self.snippet)
|
||||
\ }
|
||||
let self.changenr = a:changenr
|
||||
endfunction
|
||||
|
||||
557
dot_vim/plugged/vim-vsnip/autoload/vsnip/snippet.vim
Normal file
557
dot_vim/plugged/vim-vsnip/autoload/vsnip/snippet.vim
Normal file
@@ -0,0 +1,557 @@
|
||||
let s:max_tabstop = 1000000
|
||||
let s:Position = vital#vsnip#import('VS.LSP.Position')
|
||||
|
||||
"
|
||||
" import.
|
||||
"
|
||||
function! vsnip#snippet#import() abort
|
||||
return s:Snippet
|
||||
endfunction
|
||||
|
||||
let s:Snippet = {}
|
||||
|
||||
"
|
||||
" new.
|
||||
"
|
||||
function! s:Snippet.new(position, text) abort
|
||||
let l:pos = s:Position.lsp_to_vim('%', a:position)
|
||||
let l:snippet = extend(deepcopy(s:Snippet), {
|
||||
\ 'type': 'snippet',
|
||||
\ 'position': a:position,
|
||||
\ 'before_text': getline(l:pos[0])[0 : l:pos[1] - 2],
|
||||
\ 'children': vsnip#snippet#node#create_from_ast(
|
||||
\ vsnip#snippet#parser#parse(a:text)
|
||||
\ )
|
||||
\ })
|
||||
call l:snippet.init()
|
||||
call l:snippet.sync()
|
||||
return l:snippet
|
||||
endfunction
|
||||
|
||||
"
|
||||
" init.
|
||||
"
|
||||
" NOTE: Must not use the node range in this method.
|
||||
"
|
||||
function! s:Snippet.init() abort
|
||||
let l:fn = {}
|
||||
let l:fn.self = self
|
||||
let l:fn.group = {}
|
||||
let l:fn.variable_placeholder = {}
|
||||
let l:fn.has_final_tabstop = v:false
|
||||
function! l:fn.traverse(context) abort
|
||||
if a:context.node.type ==# 'placeholder'
|
||||
" Mark as follower placeholder.
|
||||
if !has_key(self.group, a:context.node.id)
|
||||
let self.group[a:context.node.id] = a:context.node
|
||||
else
|
||||
let a:context.node.follower = v:true
|
||||
endif
|
||||
|
||||
" Mark as having final tabstop
|
||||
if a:context.node.is_final
|
||||
let self.has_final_tabstop = v:true
|
||||
endif
|
||||
elseif a:context.node.type ==# 'variable'
|
||||
" TODO refactor
|
||||
" variable placeholder
|
||||
if a:context.node.unknown
|
||||
let a:context.node.type = 'placeholder'
|
||||
let a:context.node.choice = []
|
||||
|
||||
if !has_key(self.variable_placeholder, a:context.node.name)
|
||||
let self.variable_placeholder[a:context.node.name] = s:max_tabstop - (len(self.variable_placeholder) + 1)
|
||||
let a:context.node.id = self.variable_placeholder[a:context.node.name]
|
||||
let a:context.node.follower = v:false
|
||||
let a:context.node.children = empty(a:context.node.children) ? [vsnip#snippet#node#create_text(a:context.node.name)] : a:context.node.children
|
||||
let self.group[a:context.node.id] = a:context.node
|
||||
else
|
||||
let a:context.node.id = self.variable_placeholder[a:context.node.name]
|
||||
let a:context.node.follower = v:true
|
||||
let a:context.node.children = [vsnip#snippet#node#create_text(self.group[a:context.node.id].text())]
|
||||
endif
|
||||
else
|
||||
let l:text = a:context.node.resolve(a:context)
|
||||
let l:text = l:text is# v:null ? a:context.text : l:text
|
||||
let l:index = index(a:context.parent.children, a:context.node)
|
||||
call remove(a:context.parent.children, l:index)
|
||||
call insert(a:context.parent.children, vsnip#snippet#node#create_text(l:text), l:index)
|
||||
endif
|
||||
endif
|
||||
endfunction
|
||||
call self.traverse(self, l:fn.traverse)
|
||||
|
||||
" Append ${MAX_TABSTOP} for the end of snippet.
|
||||
if !l:fn.has_final_tabstop && g:vsnip_append_final_tabstop
|
||||
let self.children += [vsnip#snippet#node#create_from_ast({
|
||||
\ 'type': 'placeholder',
|
||||
\ 'id': 0,
|
||||
\ 'choice': [],
|
||||
\ })]
|
||||
endif
|
||||
endfunction
|
||||
|
||||
"
|
||||
" follow.
|
||||
"
|
||||
function! s:Snippet.follow(current_tabstop, diff) abort
|
||||
if !self.is_followable(a:current_tabstop, a:diff)
|
||||
return v:false
|
||||
endif
|
||||
|
||||
let a:diff.range = [
|
||||
\ self.position_to_offset(a:diff.range.start),
|
||||
\ self.position_to_offset(a:diff.range.end),
|
||||
\ ]
|
||||
|
||||
let l:fn = {}
|
||||
let l:fn.current_tabstop = a:current_tabstop
|
||||
let l:fn.diff = a:diff
|
||||
let l:fn.is_target_context_fixed = v:false
|
||||
let l:fn.target_context = v:null
|
||||
let l:fn.contexts = []
|
||||
function! l:fn.traverse(context) abort
|
||||
if self.diff.range[1] < a:context.range[0]
|
||||
return v:true
|
||||
endif
|
||||
if a:context.node.type !=# 'text'
|
||||
return
|
||||
endif
|
||||
|
||||
let l:included = v:false
|
||||
let l:included = l:included || a:context.range[0] <= self.diff.range[0] && self.diff.range[0] < a:context.range[1] " right
|
||||
let l:included = l:included || a:context.range[0] < self.diff.range[1] && self.diff.range[1] <= a:context.range[1] " left
|
||||
let l:included = l:included || self.diff.range[0] <= a:context.range[0] && a:context.range[1] <= self.diff.range[1] " middle
|
||||
if l:included
|
||||
if !self.is_target_context_fixed && (empty(self.target_context) && a:context.parent.type ==# 'placeholder' || get(a:context.parent, 'id', -1) == self.current_tabstop)
|
||||
let self.is_target_context_fixed = get(a:context.parent, 'id', -1) == self.current_tabstop
|
||||
let self.target_context = a:context
|
||||
endif
|
||||
call add(self.contexts, a:context)
|
||||
endif
|
||||
endfunction
|
||||
call self.traverse(self, l:fn.traverse)
|
||||
|
||||
if empty(l:fn.contexts)
|
||||
return v:false
|
||||
endif
|
||||
|
||||
let l:fn.target_context = empty(l:fn.target_context) ? l:fn.contexts[-1] : l:fn.target_context
|
||||
|
||||
let l:diff_text = a:diff.text
|
||||
for l:context in l:fn.contexts
|
||||
let l:diff_range = [max([a:diff.range[0], l:context.range[0]]), min([a:diff.range[1], l:context.range[1]])]
|
||||
let l:start = l:diff_range[0] - l:context.range[0]
|
||||
let l:end = l:diff_range[1] - l:context.range[0]
|
||||
|
||||
" Create patched new text.
|
||||
let l:new_text = strcharpart(l:context.text, 0, l:start)
|
||||
if l:fn.target_context is# l:context
|
||||
let l:new_text .= l:diff_text
|
||||
let l:followed = v:true
|
||||
endif
|
||||
let l:new_text .= strcharpart(l:context.text, l:end, l:context.length - l:end)
|
||||
|
||||
" Apply patched new text.
|
||||
let l:context.node.value = l:new_text
|
||||
endfor
|
||||
|
||||
" Squash nodes when the edit was unexpected
|
||||
let l:squashed = []
|
||||
for l:context in l:fn.contexts
|
||||
let l:squash_targets = l:context.parents + [l:context.node]
|
||||
for l:i in range(len(l:squash_targets) - 1, 1, -1)
|
||||
let l:node = l:squash_targets[l:i]
|
||||
let l:parent = l:squash_targets[l:i - 1]
|
||||
|
||||
let l:should_squash = v:false
|
||||
let l:should_squash = l:should_squash || get(l:node, 'follower', v:false)
|
||||
let l:should_squash = l:should_squash || get(l:parent, 'id', v:null) is# a:current_tabstop
|
||||
let l:should_squash = l:should_squash || l:context isnot# l:fn.target_context && strlen(l:node.text()) == 0
|
||||
if l:should_squash && index(l:squashed, l:node) == -1
|
||||
let l:index = index(l:parent.children, l:node)
|
||||
call remove(l:parent.children, l:index)
|
||||
call insert(l:parent.children, vsnip#snippet#node#create_text(l:node.text()), l:index)
|
||||
call add(l:squashed, l:node)
|
||||
endif
|
||||
endfor
|
||||
endfor
|
||||
|
||||
return v:true
|
||||
endfunction
|
||||
|
||||
"
|
||||
" sync.
|
||||
"
|
||||
function! s:Snippet.sync() abort
|
||||
let l:fn = {}
|
||||
let l:fn.new_texts = {}
|
||||
let l:fn.targets = []
|
||||
function! l:fn.traverse(context) abort
|
||||
if a:context.node.type ==# 'placeholder'
|
||||
if !has_key(self.new_texts, a:context.node.id)
|
||||
let self.new_texts[a:context.node.id] = a:context.text
|
||||
else
|
||||
if self.new_texts[a:context.node.id] !=# a:context.text
|
||||
call add(self.targets, {
|
||||
\ 'range': a:context.range,
|
||||
\ 'node': a:context.node,
|
||||
\ 'new_text': a:context.node.transform.text(self.new_texts[a:context.node.id]),
|
||||
\ })
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endfunction
|
||||
call self.traverse(self, l:fn.traverse)
|
||||
|
||||
" Create text_edits.
|
||||
let l:text_edits = []
|
||||
for l:target in l:fn.targets
|
||||
call add(l:text_edits, {
|
||||
\ 'node': l:target.node,
|
||||
\ 'range': {
|
||||
\ 'start': self.offset_to_position(l:target.range[0]),
|
||||
\ 'end': self.offset_to_position(l:target.range[1]),
|
||||
\ },
|
||||
\ 'newText': l:target.new_text
|
||||
\ })
|
||||
endfor
|
||||
|
||||
" Sync placeholder text after created text_edits (the reason is to avoid using a modified range).
|
||||
for l:text_edit in l:text_edits
|
||||
let l:text_edit.node.children = [vsnip#snippet#node#create_text(l:text_edit.newText)]
|
||||
endfor
|
||||
|
||||
return l:text_edits
|
||||
endfunction
|
||||
|
||||
"
|
||||
" range.
|
||||
"
|
||||
function! s:Snippet.range() abort
|
||||
return {
|
||||
\ 'start': self.offset_to_position(0),
|
||||
\ 'end': self.offset_to_position(strchars(self.text()))
|
||||
\ }
|
||||
endfunction
|
||||
|
||||
"
|
||||
" text.
|
||||
"
|
||||
function! s:Snippet.text() abort
|
||||
return join(map(copy(self.children), 'v:val.text()'), '')
|
||||
endfunction
|
||||
|
||||
"
|
||||
" is_followable.
|
||||
"
|
||||
function! s:Snippet.is_followable(current_tabstop, diff) abort
|
||||
if g:vsnip#DeactivateOn.OutsideOfSnippet == g:vsnip_deactivate_on
|
||||
return vsnip#range#cover(self.range(), a:diff.range)
|
||||
elseif g:vsnip#DeactivateOn.OutsideOfCurrentTabstop == g:vsnip_deactivate_on
|
||||
let l:context = self.get_placeholder_context_by_tabstop(a:current_tabstop)
|
||||
if empty(l:context)
|
||||
return v:false
|
||||
endif
|
||||
return vsnip#range#cover({
|
||||
\ 'start': self.offset_to_position(l:context.range[0]),
|
||||
\ 'end': self.offset_to_position(l:context.range[1]),
|
||||
\ }, a:diff.range)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
"
|
||||
" get_placeholder_nodes
|
||||
"
|
||||
function! s:Snippet.get_placeholder_nodes() abort
|
||||
let l:fn = {}
|
||||
let l:fn.nodes = []
|
||||
function! l:fn.traverse(context) abort
|
||||
if a:context.node.type ==# 'placeholder'
|
||||
call add(self.nodes, a:context.node)
|
||||
endif
|
||||
endfunction
|
||||
call self.traverse(self, l:fn.traverse)
|
||||
|
||||
return sort(l:fn.nodes, { a, b -> a.id - b.id })
|
||||
endfunction
|
||||
|
||||
"
|
||||
" get_placeholder_context_by_tabstop
|
||||
"
|
||||
function! s:Snippet.get_placeholder_context_by_tabstop(current_tabstop) abort
|
||||
let l:fn = {}
|
||||
let l:fn.current_tabstop = a:current_tabstop
|
||||
let l:fn.context = v:null
|
||||
function! l:fn.traverse(context) abort
|
||||
if a:context.node.type ==# 'placeholder' && a:context.node.id == self.current_tabstop
|
||||
let self.context = a:context
|
||||
return v:true
|
||||
endif
|
||||
endfunction
|
||||
call self.traverse(self, l:fn.traverse)
|
||||
return l:fn.context
|
||||
endfunction
|
||||
|
||||
"
|
||||
" get_next_jump_point.
|
||||
"
|
||||
function! s:Snippet.get_next_jump_point(current_tabstop) abort
|
||||
let l:fn = {}
|
||||
let l:fn.current_tabstop = a:current_tabstop
|
||||
let l:fn.context = v:null
|
||||
function! l:fn.traverse(context) abort
|
||||
if a:context.node.type ==# 'placeholder' && self.current_tabstop < a:context.node.id
|
||||
if !empty(self.context) && self.context.node.id <= a:context.node.id
|
||||
return v:false
|
||||
endif
|
||||
|
||||
let self.context = copy(a:context)
|
||||
endif
|
||||
endfunction
|
||||
call self.traverse(self, l:fn.traverse)
|
||||
|
||||
let l:context = l:fn.context
|
||||
if empty(l:context)
|
||||
return {}
|
||||
endif
|
||||
|
||||
return {
|
||||
\ 'placeholder': l:context.node,
|
||||
\ 'range': {
|
||||
\ 'start': self.offset_to_position(l:context.range[0]),
|
||||
\ 'end': self.offset_to_position(l:context.range[1])
|
||||
\ }
|
||||
\ }
|
||||
endfunction
|
||||
|
||||
"
|
||||
" get_prev_jump_point.
|
||||
"
|
||||
function! s:Snippet.get_prev_jump_point(current_tabstop) abort
|
||||
let l:fn = {}
|
||||
let l:fn.current_tabstop = a:current_tabstop
|
||||
let l:fn.context = v:null
|
||||
function! l:fn.traverse(context) abort
|
||||
if a:context.node.type ==# 'placeholder' && self.current_tabstop > a:context.node.id
|
||||
if !empty(self.context) && self.context.node.id >= a:context.node.id
|
||||
return v:false
|
||||
endif
|
||||
let self.context = copy(a:context)
|
||||
endif
|
||||
endfunction
|
||||
call self.traverse(self, l:fn.traverse)
|
||||
|
||||
let l:context = l:fn.context
|
||||
if empty(l:context)
|
||||
return {}
|
||||
endif
|
||||
|
||||
return {
|
||||
\ 'placeholder': l:context.node,
|
||||
\ 'range': {
|
||||
\ 'start': self.offset_to_position(l:context.range[0]),
|
||||
\ 'end': self.offset_to_position(l:context.range[1])
|
||||
\ }
|
||||
\ }
|
||||
endfunction
|
||||
|
||||
"
|
||||
" normalize
|
||||
"
|
||||
" - merge adjacent text-nodes
|
||||
"
|
||||
function! s:Snippet.normalize() abort
|
||||
let l:fn = {}
|
||||
let l:fn.prev_context = v:null
|
||||
function! l:fn.traverse(context) abort
|
||||
if !empty(self.prev_context)
|
||||
if self.prev_context.node.type ==# 'text' && a:context.node.type ==# 'text' && self.prev_context.parent is# a:context.parent
|
||||
let a:context.node.value = self.prev_context.node.value . a:context.node.value
|
||||
call remove(self.prev_context.parent.children, index(self.prev_context.parent.children, self.prev_context.node))
|
||||
endif
|
||||
endif
|
||||
let self.prev_context = copy(a:context)
|
||||
endfunction
|
||||
call self.traverse(self, l:fn.traverse)
|
||||
endfunction
|
||||
|
||||
"
|
||||
" merge
|
||||
"
|
||||
function! s:Snippet.merge(tabstop, snippet) abort
|
||||
" increase new snippet's tabstop by current snippet's current tabstop
|
||||
let l:offset = 1
|
||||
let l:tabstop_map = {}
|
||||
for l:node in a:snippet.get_placeholder_nodes()
|
||||
if !has_key(l:tabstop_map, l:node.id)
|
||||
let l:tabstop_map[l:node.id] = a:tabstop + l:offset
|
||||
endif
|
||||
let l:node.id = l:tabstop_map[l:node.id]
|
||||
let l:offset += 1
|
||||
endfor
|
||||
if empty(l:tabstop_map)
|
||||
return
|
||||
endif
|
||||
|
||||
let l:tail = l:node
|
||||
|
||||
" re-assign current snippet's tabstop by new snippet's final tabstop
|
||||
let l:offset = 1
|
||||
let l:tabstop_map = {}
|
||||
for l:node in self.get_placeholder_nodes()
|
||||
if l:node.id > a:tabstop
|
||||
if !has_key(l:tabstop_map, l:node.id)
|
||||
let l:tabstop_map[l:node.id] = l:tail.id + l:offset
|
||||
endif
|
||||
let l:node.id = l:tabstop_map[l:node.id]
|
||||
let l:offset += 1
|
||||
endif
|
||||
endfor
|
||||
endfunction
|
||||
|
||||
"
|
||||
" insert
|
||||
"
|
||||
function! s:Snippet.insert(position, nodes_to_insert) abort
|
||||
let l:offset = self.position_to_offset(a:position)
|
||||
|
||||
" Search target node for inserting nodes.
|
||||
let l:fn = {}
|
||||
let l:fn.offset = l:offset
|
||||
let l:fn.context = v:null
|
||||
function! l:fn.traverse(context) abort
|
||||
if a:context.range[0] <= self.offset && self.offset <= a:context.range[1] && a:context.node.type ==# 'text'
|
||||
" prefer more deeper node.
|
||||
if empty(self.context) || self.context.depth <= a:context.depth
|
||||
let self.context = copy(a:context)
|
||||
endif
|
||||
endif
|
||||
endfunction
|
||||
call self.traverse(self, l:fn.traverse)
|
||||
|
||||
" This condition is unexpected normally
|
||||
let l:context = l:fn.context
|
||||
if empty(l:context)
|
||||
return
|
||||
endif
|
||||
|
||||
" Remove target text node
|
||||
let l:index = index(l:context.parent.children, l:context.node)
|
||||
call remove(l:context.parent.children, l:index)
|
||||
|
||||
" Should insert into existing text node when position is middle of node
|
||||
let l:nodes_to_insert = reverse(a:nodes_to_insert)
|
||||
if l:context.node.value !=# ''
|
||||
let l:off = l:offset - l:context.range[0]
|
||||
let l:before = vsnip#snippet#node#create_text(strcharpart(l:context.node.value, 0, l:off))
|
||||
let l:after = vsnip#snippet#node#create_text(strcharpart(l:context.node.value, l:off, strchars(l:context.node.value) - l:off))
|
||||
let l:nodes_to_insert = [l:after] + l:nodes_to_insert + [l:before]
|
||||
endif
|
||||
|
||||
" Insert nodes.
|
||||
for l:node in l:nodes_to_insert
|
||||
call insert(l:context.parent.children, l:node, l:index)
|
||||
endfor
|
||||
|
||||
call self.normalize()
|
||||
endfunction
|
||||
|
||||
"
|
||||
" offset_to_position.
|
||||
"
|
||||
" @param offset 0-based index for snippet text.
|
||||
" @return position buffer position
|
||||
"
|
||||
function! s:Snippet.offset_to_position(offset) abort
|
||||
let l:lines = split(strcharpart(self.text(), 0, a:offset), "\n", v:true)
|
||||
return {
|
||||
\ 'line': self.position.line + len(l:lines) - 1,
|
||||
\ 'character': strchars(l:lines[-1]) + (len(l:lines) == 1 ? self.position.character : 0),
|
||||
\ }
|
||||
endfunction
|
||||
|
||||
"
|
||||
" position_to_offset.
|
||||
"
|
||||
" @param position buffer position
|
||||
" @return 0-based index for snippet text.
|
||||
"
|
||||
function! s:Snippet.position_to_offset(position) abort
|
||||
let l:line = a:position.line - self.position.line
|
||||
let l:char = a:position.character - (l:line == 0 ? self.position.character : 0)
|
||||
let l:lines = split(self.text(), "\n", v:true)[0 : l:line]
|
||||
let l:lines[-1] = strcharpart(l:lines[-1], 0, l:char)
|
||||
return strchars(join(l:lines, "\n"))
|
||||
endfunction
|
||||
|
||||
"
|
||||
" traverse.
|
||||
"
|
||||
function! s:Snippet.traverse(node, callback) abort
|
||||
let l:state = {
|
||||
\ 'offset': 0,
|
||||
\ 'before_text': self.before_text,
|
||||
\ }
|
||||
let l:context = {
|
||||
\ 'depth': 0,
|
||||
\ 'parent': v:null,
|
||||
\ 'parents': [],
|
||||
\ }
|
||||
call s:traverse(a:node, a:callback, l:state, l:context)
|
||||
endfunction
|
||||
function! s:traverse(node, callback, state, context) abort
|
||||
let l:text = ''
|
||||
let l:length = 0
|
||||
if a:node.type !=# 'snippet'
|
||||
let l:text = a:node.text()
|
||||
let l:length = strchars(l:text)
|
||||
if a:callback({
|
||||
\ 'node': a:node,
|
||||
\ 'text': l:text,
|
||||
\ 'length': l:length,
|
||||
\ 'parent': a:context.parent,
|
||||
\ 'parents': a:context.parents,
|
||||
\ 'depth': a:context.depth,
|
||||
\ 'offset': a:state.offset,
|
||||
\ 'before_text': a:state.before_text,
|
||||
\ 'range': [a:state.offset, a:state.offset + l:length],
|
||||
\ })
|
||||
return v:true
|
||||
endif
|
||||
endif
|
||||
|
||||
if len(a:node.children) > 0
|
||||
let l:next_context = {
|
||||
\ 'parent': a:node,
|
||||
\ 'parents': a:context.parents + [a:node],
|
||||
\ 'depth': len(a:context.parents) + 1,
|
||||
\ }
|
||||
for l:child in copy(a:node.children)
|
||||
if s:traverse(l:child, a:callback, a:state, l:next_context)
|
||||
return v:true
|
||||
endif
|
||||
endfor
|
||||
else
|
||||
let a:state.before_text .= l:text
|
||||
let a:state.offset += l:length
|
||||
endif
|
||||
endfunction
|
||||
|
||||
"
|
||||
" debug
|
||||
"
|
||||
function! s:Snippet.debug() abort
|
||||
echomsg 'snippet.text()'
|
||||
for l:line in split(self.text(), "\n", v:true)
|
||||
echomsg string(l:line)
|
||||
endfor
|
||||
echomsg '-----'
|
||||
|
||||
let l:fn = {}
|
||||
function! l:fn.traverse(context) abort
|
||||
echomsg repeat(' ', a:context.depth - 1) . a:context.node.to_string()
|
||||
endfunction
|
||||
call self.traverse(self, l:fn.traverse)
|
||||
echomsg ' '
|
||||
endfunction
|
||||
43
dot_vim/plugged/vim-vsnip/autoload/vsnip/snippet/node.vim
Normal file
43
dot_vim/plugged/vim-vsnip/autoload/vsnip/snippet/node.vim
Normal file
@@ -0,0 +1,43 @@
|
||||
let s:Placeholder = vsnip#snippet#node#placeholder#import()
|
||||
let s:Variable = vsnip#snippet#node#variable#import()
|
||||
let s:Text = vsnip#snippet#node#text#import()
|
||||
let s:Transform = vsnip#snippet#node#transform#import()
|
||||
|
||||
"
|
||||
" vsnip#snippet#node#create_from_ast
|
||||
"
|
||||
function! vsnip#snippet#node#create_from_ast(ast) abort
|
||||
if type(a:ast) == type([])
|
||||
return map(a:ast, 'vsnip#snippet#node#create_from_ast(v:val)')
|
||||
endif
|
||||
|
||||
if a:ast.type ==# 'placeholder'
|
||||
return s:Placeholder.new(a:ast)
|
||||
endif
|
||||
if a:ast.type ==# 'variable'
|
||||
return s:Variable.new(a:ast)
|
||||
endif
|
||||
if a:ast.type ==# 'text'
|
||||
return s:Text.new(a:ast)
|
||||
endif
|
||||
|
||||
throw 'vsnip: invalid node type'
|
||||
endfunction
|
||||
|
||||
"
|
||||
" vsnip#snippet#node#create_text
|
||||
"
|
||||
function! vsnip#snippet#node#create_text(text) abort
|
||||
return s:Text.new({
|
||||
\ 'type': 'text',
|
||||
\ 'raw': a:text,
|
||||
\ 'escaped': a:text
|
||||
\ })
|
||||
endfunction
|
||||
|
||||
"
|
||||
" vsnip#snippet#node#create_transform
|
||||
"
|
||||
function! vsnip#snippet#node#create_transform(transform) abort
|
||||
return s:Transform.new(a:transform)
|
||||
endfunction
|
||||
@@ -0,0 +1,55 @@
|
||||
let s:max_tabstop = 1000000
|
||||
let s:uid = 0
|
||||
|
||||
function! vsnip#snippet#node#placeholder#import() abort
|
||||
return s:Placeholder
|
||||
endfunction
|
||||
|
||||
let s:Placeholder = {}
|
||||
|
||||
"
|
||||
" new.
|
||||
"
|
||||
function! s:Placeholder.new(ast) abort
|
||||
let s:uid += 1
|
||||
|
||||
let l:node = extend(deepcopy(s:Placeholder), {
|
||||
\ 'uid': s:uid,
|
||||
\ 'type': 'placeholder',
|
||||
\ 'id': a:ast.id,
|
||||
\ 'is_final': a:ast.id == 0,
|
||||
\ 'follower': v:false,
|
||||
\ 'choice': get(a:ast, 'choice', []),
|
||||
\ 'children': vsnip#snippet#node#create_from_ast(get(a:ast, 'children', [])),
|
||||
\ 'transform': vsnip#snippet#node#create_transform(get(a:ast, 'transform')),
|
||||
\ })
|
||||
|
||||
if l:node.is_final
|
||||
let l:node.id = s:max_tabstop
|
||||
endif
|
||||
|
||||
if len(l:node.children) == 0
|
||||
let l:node.children = [vsnip#snippet#node#create_text('')]
|
||||
endif
|
||||
|
||||
return l:node
|
||||
endfunction
|
||||
|
||||
"
|
||||
" text.
|
||||
"
|
||||
function! s:Placeholder.text() abort
|
||||
return join(map(copy(self.children), 'v:val.text()'), '')
|
||||
endfunction
|
||||
|
||||
"
|
||||
" to_string
|
||||
"
|
||||
function! s:Placeholder.to_string() abort
|
||||
return printf('%s(id=%s, follower=%s, choise=%s)',
|
||||
\ self.type,
|
||||
\ self.id,
|
||||
\ self.follower ? 'true' : 'false',
|
||||
\ self.choice
|
||||
\ )
|
||||
endfunction
|
||||
@@ -0,0 +1,38 @@
|
||||
let s:uid = 0
|
||||
|
||||
function! vsnip#snippet#node#text#import() abort
|
||||
return s:Text
|
||||
endfunction
|
||||
|
||||
let s:Text = {}
|
||||
|
||||
"
|
||||
" new.
|
||||
"
|
||||
function! s:Text.new(ast) abort
|
||||
let s:uid += 1
|
||||
|
||||
return extend(deepcopy(s:Text), {
|
||||
\ 'uid': s:uid,
|
||||
\ 'type': 'text',
|
||||
\ 'value': a:ast.escaped,
|
||||
\ 'children': [],
|
||||
\ })
|
||||
endfunction
|
||||
|
||||
"
|
||||
" text.
|
||||
"
|
||||
function! s:Text.text() abort
|
||||
return self.value
|
||||
endfunction
|
||||
|
||||
"
|
||||
" to_string
|
||||
"
|
||||
function! s:Text.to_string() abort
|
||||
return printf('%s(%s)',
|
||||
\ self.type,
|
||||
\ self.value
|
||||
\ )
|
||||
endfunction
|
||||
@@ -0,0 +1,112 @@
|
||||
function! vsnip#snippet#node#transform#import() abort
|
||||
return s:Transform
|
||||
endfunction
|
||||
|
||||
let s:Transform = {}
|
||||
|
||||
"
|
||||
" new.
|
||||
"
|
||||
function! s:Transform.new(ast) abort
|
||||
let l:transform = empty(a:ast) ? {} : a:ast
|
||||
|
||||
let l:node = extend(deepcopy(s:Transform), {
|
||||
\ 'type': 'transform',
|
||||
\ 'regex': get(l:transform, 'regex', v:null),
|
||||
\ 'replacements': get(l:transform, 'format', []),
|
||||
\ 'options': get(l:transform, 'option', []),
|
||||
\ })
|
||||
|
||||
let l:node.is_noop = l:node.regex is v:null
|
||||
|
||||
return l:node
|
||||
endfunction
|
||||
|
||||
"
|
||||
" text.
|
||||
"
|
||||
function! s:Transform.text(input_text) abort
|
||||
if empty(a:input_text) || self.is_noop
|
||||
return a:input_text
|
||||
endif
|
||||
|
||||
if self.regex.pattern !=# '(.*)'
|
||||
" TODO: fully support regex
|
||||
return a:input_text
|
||||
endif
|
||||
|
||||
let l:text = ''
|
||||
|
||||
for l:replacement in self.replacements
|
||||
if l:replacement.type ==# 'format'
|
||||
if l:replacement.modifier ==# '/capitalize'
|
||||
let l:text .= s:capitalize(a:input_text)
|
||||
elseif l:replacement.modifier ==# '/downcase'
|
||||
let l:text .= s:downcase(a:input_text)
|
||||
elseif l:replacement.modifier ==# '/upcase'
|
||||
let l:text .= s:upcase(a:input_text)
|
||||
elseif l:replacement.modifier ==# '/camelcase'
|
||||
let l:text .= s:camelcase(a:input_text)
|
||||
elseif l:replacement.modifier ==# '/pascalcase'
|
||||
let l:text .= s:capitalize(s:camelcase(a:input_text))
|
||||
endif
|
||||
elseif l:replacement.type ==# 'text'
|
||||
let l:text .= l:replacement.escaped
|
||||
endif
|
||||
endfor
|
||||
|
||||
return l:text
|
||||
endfunction
|
||||
|
||||
"
|
||||
" to_string
|
||||
"
|
||||
function! s:Transform.to_string() abort
|
||||
if self.is_noop
|
||||
return
|
||||
end
|
||||
|
||||
return printf('%s(regex=%s, total_replacements=%s, options=%s)',
|
||||
\ self.type,
|
||||
\ get(self.regex, 'pattern', ''),
|
||||
\ len(self.replacements),
|
||||
\ join(self.options, ''),
|
||||
\ )
|
||||
endfunction
|
||||
|
||||
"
|
||||
" upcase
|
||||
"
|
||||
function! s:upcase(word) abort
|
||||
let word = toupper(a:word)
|
||||
return word
|
||||
endfunction
|
||||
|
||||
"
|
||||
" downcase
|
||||
"
|
||||
function! s:downcase(word) abort
|
||||
let word = tolower(a:word)
|
||||
return word
|
||||
endfunction
|
||||
|
||||
"
|
||||
" capitalize
|
||||
"
|
||||
function! s:capitalize(word) abort
|
||||
let word = s:upcase(strpart(a:word, 0, 1)) . strpart(a:word, 1)
|
||||
return word
|
||||
endfunction
|
||||
|
||||
"
|
||||
" camelcase
|
||||
" @see https://github.com/tpope/vim-abolish/blob/3f0c8faa/plugin/abolish.vim#L111-L118
|
||||
"
|
||||
function! s:camelcase(word) abort
|
||||
let word = substitute(a:word, '-', '_', 'g')
|
||||
if word !~# '_' && word =~# '\l'
|
||||
return substitute(word,'^.','\l&','')
|
||||
else
|
||||
return substitute(word,'\C\(_\)\=\(.\)','\=submatch(1)==""?tolower(submatch(2)) : toupper(submatch(2))','g')
|
||||
endif
|
||||
endfunction
|
||||
@@ -0,0 +1,63 @@
|
||||
let s:uid = 0
|
||||
|
||||
"
|
||||
" vsnip#snippet#node#variable#import
|
||||
"
|
||||
function! vsnip#snippet#node#variable#import() abort
|
||||
return s:Variable
|
||||
endfunction
|
||||
|
||||
let s:Variable = {}
|
||||
|
||||
"
|
||||
" new.
|
||||
"
|
||||
function! s:Variable.new(ast) abort
|
||||
let s:uid += 1
|
||||
|
||||
let l:resolver = vsnip#variable#get(a:ast.name)
|
||||
return extend(deepcopy(s:Variable), {
|
||||
\ 'uid': s:uid,
|
||||
\ 'type': 'variable',
|
||||
\ 'name': a:ast.name,
|
||||
\ 'unknown': empty(l:resolver),
|
||||
\ 'resolver': l:resolver,
|
||||
\ 'children': vsnip#snippet#node#create_from_ast(get(a:ast, 'children', [])),
|
||||
\ 'transform': vsnip#snippet#node#create_transform(get(a:ast, 'transform')),
|
||||
\ })
|
||||
endfunction
|
||||
|
||||
"
|
||||
" text.
|
||||
"
|
||||
function! s:Variable.text() abort
|
||||
return self.transform.text(join(map(copy(self.children), 'v:val.text()'), ''))
|
||||
endfunction
|
||||
|
||||
"
|
||||
" resolve.
|
||||
"
|
||||
function! s:Variable.resolve(context) abort
|
||||
if !self.unknown
|
||||
let l:resolved = self.transform.text(self.resolver.func({ 'node': self }))
|
||||
if l:resolved isnot v:null
|
||||
" Fix indent when one variable returns multiple lines
|
||||
let l:base_indent = vsnip#indent#get_base_indent(split(a:context.before_text, "\n", v:true)[-1])
|
||||
return substitute(l:resolved, "\n\\zs", l:base_indent, 'g')
|
||||
endif
|
||||
endif
|
||||
return v:null
|
||||
endfunction
|
||||
|
||||
"
|
||||
" to_string
|
||||
"
|
||||
function! s:Variable.to_string() abort
|
||||
return printf('%s(name=%s, unknown=%s, text=%s)',
|
||||
\ self.type,
|
||||
\ self.name,
|
||||
\ self.unknown ? 'true' : 'false',
|
||||
\ self.text()
|
||||
\ )
|
||||
endfunction
|
||||
|
||||
212
dot_vim/plugged/vim-vsnip/autoload/vsnip/snippet/parser.vim
Normal file
212
dot_vim/plugged/vim-vsnip/autoload/vsnip/snippet/parser.vim
Normal file
@@ -0,0 +1,212 @@
|
||||
let s:Combinator = vsnip#parser#combinator#import()
|
||||
|
||||
"
|
||||
" vsnip#snippet#parser#parse.
|
||||
" @see https://github.com/Microsoft/language-server-protocol/blob/master/snippetSyntax.md
|
||||
"
|
||||
function! vsnip#snippet#parser#parse(text) abort
|
||||
if strlen(a:text) == 0
|
||||
return []
|
||||
endif
|
||||
|
||||
let l:parsed = s:parser.parse(a:text, 0)
|
||||
if !l:parsed[0]
|
||||
throw json_encode({ 'text': a:text, 'result': l:parsed })
|
||||
endif
|
||||
return l:parsed[1]
|
||||
endfunction
|
||||
|
||||
let s:skip = s:Combinator.skip
|
||||
let s:token = s:Combinator.token
|
||||
let s:many = s:Combinator.many
|
||||
let s:or = s:Combinator.or
|
||||
let s:seq = s:Combinator.seq
|
||||
let s:lazy = s:Combinator.lazy
|
||||
let s:option = s:Combinator.option
|
||||
let s:pattern = s:Combinator.pattern
|
||||
let s:map = s:Combinator.map
|
||||
|
||||
"
|
||||
" primitives.
|
||||
"
|
||||
let s:dollar = s:token('$')
|
||||
let s:open = s:token('{')
|
||||
let s:close = s:token('}')
|
||||
let s:colon = s:token(':')
|
||||
let s:slash = s:token('/')
|
||||
let s:comma = s:token(',')
|
||||
let s:pipe = s:token('|')
|
||||
let s:varname = s:pattern('[_[:alpha:]]\w*')
|
||||
let s:int = s:map(s:pattern('\d\+'), { value -> str2nr(value[0]) })
|
||||
let s:text = { stop, escape -> s:map(
|
||||
\ s:skip(stop, escape),
|
||||
\ { value -> {
|
||||
\ 'type': 'text',
|
||||
\ 'raw': value[0],
|
||||
\ 'escaped': value[1]
|
||||
\ }
|
||||
\ }) }
|
||||
let s:regex = s:map(s:text(['/'], []), { value -> {
|
||||
\ 'type': 'regex',
|
||||
\ 'pattern': value.raw
|
||||
\ } })
|
||||
|
||||
"
|
||||
" any (without text).
|
||||
"
|
||||
let s:any = s:or(
|
||||
\ s:lazy({ -> s:choice }),
|
||||
\ s:lazy({ -> s:variable }),
|
||||
\ s:lazy({ -> s:tabstop }),
|
||||
\ s:lazy({ -> s:placeholder }),
|
||||
\ )
|
||||
|
||||
"
|
||||
" format.
|
||||
"
|
||||
let s:format1 = s:map(s:seq(s:dollar, s:int), { value -> {
|
||||
\ 'type': 'format',
|
||||
\ 'id': value[1]
|
||||
\ } })
|
||||
let s:format2 = s:map(s:seq(s:dollar, s:open, s:int, s:close), { value -> {
|
||||
\ 'type': 'format',
|
||||
\ 'id': value[2]
|
||||
\ } })
|
||||
let s:format3 = s:map(
|
||||
\ s:seq(
|
||||
\ s:dollar,
|
||||
\ s:open,
|
||||
\ s:int,
|
||||
\ s:colon,
|
||||
\ s:or(
|
||||
\ s:token('/upcase'),
|
||||
\ s:token('/downcase'),
|
||||
\ s:token('/capitalize'),
|
||||
\ s:token('/camelcase'),
|
||||
\ s:token('/pascalcase'),
|
||||
\ s:token('+if'),
|
||||
\ s:token('?if:else'),
|
||||
\ s:token('-else'),
|
||||
\ s:token('else')
|
||||
\ ),
|
||||
\ s:close
|
||||
\ ), { value -> {
|
||||
\ 'type': 'format',
|
||||
\ 'id': value[2],
|
||||
\ 'modifier': value[4]
|
||||
\ } })
|
||||
let s:format = s:or(s:format1, s:format2, s:format3)
|
||||
|
||||
"
|
||||
" transform
|
||||
"
|
||||
let s:transform = s:map(s:seq(
|
||||
\ s:slash,
|
||||
\ s:regex,
|
||||
\ s:slash,
|
||||
\ s:many(s:or(s:format, s:text(['/', '$'], []))),
|
||||
\ s:slash,
|
||||
\ s:option(s:many(s:or(s:token('i'), s:token('g'))))
|
||||
\ ), { value -> {
|
||||
\ 'type': 'transform',
|
||||
\ 'regex': value[1],
|
||||
\ 'format': value[3],
|
||||
\ 'option': value[5]
|
||||
\ } })
|
||||
|
||||
"
|
||||
" variable
|
||||
"
|
||||
let s:variable1 = s:map(s:seq(s:dollar, s:varname), { value -> {
|
||||
\ 'type': 'variable',
|
||||
\ 'name': value[1],
|
||||
\ 'children': [],
|
||||
\ } })
|
||||
let s:variable2 = s:map(s:seq(s:dollar, s:open, s:varname, s:close), { value -> {
|
||||
\ 'type': 'variable',
|
||||
\ 'name': value[2],
|
||||
\ 'children': [],
|
||||
\ } })
|
||||
let s:variable3 = s:map(s:seq(
|
||||
\ s:dollar,
|
||||
\ s:open,
|
||||
\ s:varname,
|
||||
\ s:colon,
|
||||
\ s:many(s:or(s:any, s:text(['$', '}'], []))),
|
||||
\ s:close
|
||||
\ ), { value -> {
|
||||
\ 'type': 'variable',
|
||||
\ 'name': value[2],
|
||||
\ 'children': value[4]
|
||||
\ } })
|
||||
let s:variable4 = s:map(s:seq(s:dollar, s:open, s:varname, s:transform, s:close), { value -> {
|
||||
\ 'type': 'variable',
|
||||
\ 'name': value[2],
|
||||
\ 'transform': value[3],
|
||||
\ 'children': [],
|
||||
\ } })
|
||||
|
||||
let s:variable = s:or(s:variable1, s:variable2, s:variable3, s:variable4)
|
||||
|
||||
"
|
||||
" placeholder.
|
||||
"
|
||||
let s:placeholder = s:map(s:seq(
|
||||
\ s:dollar,
|
||||
\ s:open,
|
||||
\ s:int,
|
||||
\ s:colon,
|
||||
\ s:many(s:or(s:any, s:text(['$', '}'], []))),
|
||||
\ s:close
|
||||
\ ), { value -> {
|
||||
\ 'type': 'placeholder',
|
||||
\ 'id': value[2],
|
||||
\ 'children': value[4]
|
||||
\ } })
|
||||
|
||||
"
|
||||
" tabstop
|
||||
"
|
||||
let s:tabstop1 = s:map(s:seq(s:dollar, s:int), { value -> {
|
||||
\ 'type': 'placeholder',
|
||||
\ 'id': value[1],
|
||||
\ 'children': [],
|
||||
\ } })
|
||||
let s:tabstop2 = s:map(s:seq(s:dollar, s:open, s:int, s:option(s:colon), s:close), { value -> {
|
||||
\ 'type': 'placeholder',
|
||||
\ 'id': value[2],
|
||||
\ 'children': [],
|
||||
\ } })
|
||||
let s:tabstop3 = s:map(s:seq(s:dollar, s:open, s:int, s:transform, s:close), { value -> {
|
||||
\ 'type': 'placeholder',
|
||||
\ 'id': value[2],
|
||||
\ 'children': [],
|
||||
\ 'transform': value[3]
|
||||
\ } })
|
||||
let s:tabstop = s:or(s:tabstop1, s:tabstop2, s:tabstop3)
|
||||
|
||||
"
|
||||
" choice
|
||||
"
|
||||
let s:choice = s:map(s:seq(
|
||||
\ s:dollar,
|
||||
\ s:open,
|
||||
\ s:int,
|
||||
\ s:pipe,
|
||||
\ s:many(
|
||||
\ s:map(s:seq(s:text([',', '|'], []), s:option(s:comma)), { value -> value[0] }),
|
||||
\ ),
|
||||
\ s:pipe,
|
||||
\ s:close
|
||||
\ ), { value -> {
|
||||
\ 'type': 'placeholder',
|
||||
\ 'id': value[2],
|
||||
\ 'choice': value[4],
|
||||
\ 'children': [copy(value[4][0])],
|
||||
\ } })
|
||||
|
||||
"
|
||||
" parser.
|
||||
"
|
||||
let s:parser = s:many(s:or(s:any, s:text(['$'], ['}'])))
|
||||
|
||||
116
dot_vim/plugged/vim-vsnip/autoload/vsnip/source.vim
Normal file
116
dot_vim/plugged/vim-vsnip/autoload/vsnip/source.vim
Normal file
@@ -0,0 +1,116 @@
|
||||
"
|
||||
" vsnip#source#refresh.
|
||||
"
|
||||
function! vsnip#source#refresh(path) abort
|
||||
call vsnip#source#user_snippet#refresh(a:path)
|
||||
call vsnip#source#vscode#refresh(a:path)
|
||||
call vsnip#source#snipmate#refresh(a:path)
|
||||
endfunction
|
||||
|
||||
"
|
||||
" vsnip#source#find.
|
||||
"
|
||||
function! vsnip#source#find(bufnr) abort
|
||||
let l:sources = []
|
||||
let l:sources += vsnip#source#user_snippet#find(a:bufnr)
|
||||
let l:sources += vsnip#source#vscode#find(a:bufnr)
|
||||
let l:sources += vsnip#source#snipmate#find(a:bufnr)
|
||||
return l:sources
|
||||
endfunction
|
||||
|
||||
"
|
||||
" vsnip#source#filetypes
|
||||
"
|
||||
function! vsnip#source#filetypes(bufnr) abort
|
||||
let l:filetype = getbufvar(a:bufnr, '&filetype', '')
|
||||
return split(l:filetype, '\.') + get(g:vsnip_filetypes, l:filetype, []) + ['global']
|
||||
endfunction
|
||||
|
||||
"
|
||||
" vsnip#source#create.
|
||||
"
|
||||
function! vsnip#source#create(path) abort
|
||||
try
|
||||
let l:file = readfile(a:path)
|
||||
let l:file = type(l:file) == type([]) ? join(l:file, "\n") : l:file
|
||||
let l:file = iconv(l:file, 'utf-8', &encoding)
|
||||
let l:json = json_decode(l:file)
|
||||
|
||||
if type(l:json) != type({})
|
||||
throw printf('%s is not valid json.', a:path)
|
||||
endif
|
||||
catch /.*/
|
||||
let l:json = {}
|
||||
echohl ErrorMsg
|
||||
echomsg printf('[vsnip] Parsing error occurred on: %s', a:path)
|
||||
echohl None
|
||||
echomsg string({ 'exception': v:exception, 'throwpint': v:throwpoint })
|
||||
endtry
|
||||
|
||||
" @see https://github.com/microsoft/vscode/blob/0ba9f6631daec96a2b71eeb337e29f50dd21c7e1/src/vs/workbench/contrib/snippets/browser/snippetsFile.ts#L216
|
||||
let l:source = []
|
||||
for [l:key, l:value] in items(l:json)
|
||||
if s:is_snippet(l:value)
|
||||
call add(l:source, s:format_snippet(l:key, l:value))
|
||||
else
|
||||
for [l:key, l:value_] in items(l:value)
|
||||
if s:is_snippet(l:value_)
|
||||
call add(l:source, s:format_snippet(l:key, l:value_))
|
||||
endif
|
||||
endfor
|
||||
endif
|
||||
endfor
|
||||
return sort(l:source, { a, b -> strlen(b.prefix[0]) - strlen(a.prefix[0]) })
|
||||
endfunction
|
||||
|
||||
"
|
||||
" format_snippet
|
||||
"
|
||||
function! s:format_snippet(label, snippet) abort
|
||||
let [l:prefixes, l:prefixes_alias] = vsnip#source#resolve_prefix(a:snippet.prefix)
|
||||
let l:description = get(a:snippet, 'description', '')
|
||||
|
||||
return {
|
||||
\ 'label': a:label,
|
||||
\ 'prefix': l:prefixes,
|
||||
\ 'prefix_alias': l:prefixes_alias,
|
||||
\ 'body': type(a:snippet.body) == type([]) ? a:snippet.body : [a:snippet.body],
|
||||
\ 'description': type(l:description) == type([]) ? join(l:description, '') : l:description,
|
||||
\ }
|
||||
endfunction
|
||||
|
||||
"
|
||||
" is_snippet
|
||||
"
|
||||
function! s:is_snippet(snippet_or_source) abort
|
||||
return type(a:snippet_or_source) == type({}) && has_key(a:snippet_or_source, 'prefix') && has_key(a:snippet_or_source, 'body')
|
||||
endfunction
|
||||
|
||||
"
|
||||
" resolve_prefix.
|
||||
"
|
||||
function! vsnip#source#resolve_prefix(prefix) abort
|
||||
let l:prefixes = []
|
||||
let l:prefixes_alias = []
|
||||
|
||||
for l:prefix in type(a:prefix) == type([]) ? a:prefix : [a:prefix]
|
||||
" namspace.
|
||||
if strlen(g:vsnip_namespace) > 0
|
||||
call add(l:prefixes, g:vsnip_namespace . l:prefix)
|
||||
endif
|
||||
|
||||
" prefix.
|
||||
call add(l:prefixes, l:prefix)
|
||||
|
||||
" alias.
|
||||
if l:prefix =~# '^\h\w*\%(-\w\+\)\+$'
|
||||
call add(l:prefixes_alias, join(map(split(l:prefix, '-'), { i, v -> v[0] }), ''))
|
||||
endif
|
||||
endfor
|
||||
|
||||
return [
|
||||
\ sort(l:prefixes, { a, b -> strlen(b) - strlen(a) }),
|
||||
\ sort(l:prefixes_alias, { a, b -> strlen(b) - strlen(a) })
|
||||
\ ]
|
||||
endfunction
|
||||
|
||||
88
dot_vim/plugged/vim-vsnip/autoload/vsnip/source/snipmate.vim
Normal file
88
dot_vim/plugged/vim-vsnip/autoload/vsnip/source/snipmate.vim
Normal file
@@ -0,0 +1,88 @@
|
||||
let s:cache = {}
|
||||
|
||||
function! vsnip#source#snipmate#refresh(path) abort
|
||||
if has_key(s:cache, a:path)
|
||||
unlet s:cache[a:path]
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! vsnip#source#snipmate#find(bufnr) abort
|
||||
let filetypes = vsnip#source#filetypes(a:bufnr)
|
||||
return s:find(filetypes, a:bufnr)
|
||||
endfunction
|
||||
|
||||
function! s:find(filetypes, bufnr) abort
|
||||
let sources = []
|
||||
for path in s:get_source_paths(a:filetypes, a:bufnr)
|
||||
if !has_key(s:cache, path)
|
||||
let s:cache[path] = s:create(path, a:bufnr)
|
||||
endif
|
||||
call add(sources, s:cache[path])
|
||||
endfor
|
||||
return sources
|
||||
endfunction
|
||||
|
||||
function! s:get_source_paths(filetypes, bufnr) abort
|
||||
let paths = []
|
||||
for dir in s:get_source_dirs(a:bufnr)
|
||||
for filetype in a:filetypes
|
||||
let path = resolve(expand(printf('%s/%s.snippets', dir, filetype)))
|
||||
if has_key(s:cache, path) || filereadable(path)
|
||||
call add(paths, path)
|
||||
endif
|
||||
endfor
|
||||
endfor
|
||||
return paths
|
||||
endfunction
|
||||
|
||||
function! s:get_source_dirs(bufnr) abort
|
||||
let dirs = []
|
||||
let buf_dir = getbufvar(a:bufnr, 'vsnip_snippet_dir', '')
|
||||
if buf_dir !=# ''
|
||||
let dirs += [buf_dir]
|
||||
endif
|
||||
let dirs += getbufvar(a:bufnr, 'vsnip_snippet_dirs', [])
|
||||
let dirs += [g:vsnip_snippet_dir]
|
||||
let dirs += g:vsnip_snippet_dirs
|
||||
return dirs
|
||||
endfunction
|
||||
|
||||
function! s:create(path, bufnr) abort
|
||||
let file = readfile(a:path)
|
||||
let file = type(file) == v:t_list ? file : [file]
|
||||
call map(file, { _, f -> iconv(f, 'utf-8', &encoding) })
|
||||
let source = []
|
||||
let i = -1
|
||||
while i + 1 < len(file)
|
||||
let [i, line] = [i + 1, file[i + 1]]
|
||||
if line =~# '^\(#\|\s*$\)'
|
||||
" Comment, or blank line before snippets
|
||||
elseif line =~# '^extends\s\+\S'
|
||||
let filetypes = map(split(line[7:], ','), 'trim(v:val)')
|
||||
let source += flatten(s:find(filetypes, a:bufnr))
|
||||
elseif line =~# '^snippet\s\+\S' && i + 1 < len(file)
|
||||
let matched = matchlist(line, '^snippet\s\+\(\S\+\)\s*\(.*\)')
|
||||
let [prefix, description] = [matched[1], matched[2]]
|
||||
let body = []
|
||||
let indent = matchstr(file[i + 1], '^\s\+')
|
||||
while i + 1 < len(file) && file[i + 1] =~# '^\(' . indent . '\|\s*$\)'
|
||||
let [i, line] = [i + 1, file[i + 1]]
|
||||
call add(body, line[strlen(indent):])
|
||||
endwhile
|
||||
let [prefixes, prefixes_alias] = vsnip#source#resolve_prefix(prefix)
|
||||
call add(source, {
|
||||
\ 'label': prefix,
|
||||
\ 'prefix': prefixes,
|
||||
\ 'prefix_alias': prefixes_alias,
|
||||
\ 'body': body,
|
||||
\ 'description': description
|
||||
\ })
|
||||
else
|
||||
echohl ErrorMsg
|
||||
echomsg printf('[vsnip] Parsing error occurred on: %s#L%s', a:path, i + 1)
|
||||
echohl None
|
||||
break
|
||||
endif
|
||||
endwhile
|
||||
return sort(source, { a, b -> strlen(b.prefix[0]) - strlen(a.prefix[0]) })
|
||||
endfunction
|
||||
@@ -0,0 +1,69 @@
|
||||
let s:cache = {}
|
||||
|
||||
"
|
||||
" vsnip#source#user_snippet#find.
|
||||
"
|
||||
function! vsnip#source#user_snippet#find(bufnr) abort
|
||||
let l:sources = []
|
||||
for l:path in s:get_source_paths(a:bufnr)
|
||||
if !has_key(s:cache, l:path)
|
||||
let s:cache[l:path] = vsnip#source#create(l:path)
|
||||
endif
|
||||
call add(l:sources, s:cache[l:path])
|
||||
endfor
|
||||
return l:sources
|
||||
endfunction
|
||||
|
||||
"
|
||||
" vsnip#source#user_snippet#refresh.
|
||||
"
|
||||
function! vsnip#source#user_snippet#refresh(path) abort
|
||||
if has_key(s:cache, a:path)
|
||||
unlet s:cache[a:path]
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:get_source_dirs(bufnr) abort
|
||||
let l:dirs = []
|
||||
let l:buf_dir = getbufvar(a:bufnr, 'vsnip_snippet_dir', v:null)
|
||||
if l:buf_dir isnot v:null
|
||||
let l:dirs += [l:buf_dir]
|
||||
endif
|
||||
let l:dirs += getbufvar(a:bufnr, 'vsnip_snippet_dirs', [])
|
||||
let l:dirs += [g:vsnip_snippet_dir]
|
||||
let l:dirs += g:vsnip_snippet_dirs
|
||||
return l:dirs
|
||||
endfunction
|
||||
|
||||
"
|
||||
" get_source_paths.
|
||||
"
|
||||
function! s:get_source_paths(bufnr) abort
|
||||
let l:filetypes = vsnip#source#filetypes(a:bufnr)
|
||||
|
||||
let l:paths = []
|
||||
for l:dir in s:get_source_dirs(a:bufnr)
|
||||
for l:filetype in l:filetypes
|
||||
let l:path = resolve(expand(printf('%s/%s.json', l:dir, l:filetype)))
|
||||
if has_key(s:cache, l:path) || filereadable(l:path)
|
||||
call add(l:paths, l:path)
|
||||
endif
|
||||
endfor
|
||||
endfor
|
||||
return l:paths
|
||||
endfunction
|
||||
|
||||
"
|
||||
" vsnip#source#user_snippet#dirs
|
||||
"
|
||||
fun! vsnip#source#user_snippet#dirs(...) abort
|
||||
return s:get_source_dirs(a:0 ? a:1 : bufnr(''))
|
||||
endfun
|
||||
|
||||
"
|
||||
" vsnip#source#user_snippet#paths
|
||||
"
|
||||
fun! vsnip#source#user_snippet#paths(...) abort
|
||||
return s:get_source_paths(a:0 ? a:1 : bufnr(''))
|
||||
endfun
|
||||
|
||||
104
dot_vim/plugged/vim-vsnip/autoload/vsnip/source/vscode.vim
Normal file
104
dot_vim/plugged/vim-vsnip/autoload/vsnip/source/vscode.vim
Normal file
@@ -0,0 +1,104 @@
|
||||
let s:snippets = {}
|
||||
let s:runtimepaths = {}
|
||||
|
||||
"
|
||||
" vsnip#source#vscode#refresh.
|
||||
"
|
||||
function! vsnip#source#vscode#refresh(path) abort
|
||||
if has_key(s:snippets, a:path)
|
||||
unlet s:snippets[a:path]
|
||||
|
||||
for [l:rtp, l:v] in items(s:runtimepaths)
|
||||
if stridx(l:rtp, a:path) == 0
|
||||
unlet s:runtimepaths[l:rtp]
|
||||
endif
|
||||
endfor
|
||||
endif
|
||||
endfunction
|
||||
|
||||
"
|
||||
" vsnip#source#vscode#find.
|
||||
"
|
||||
function! vsnip#source#vscode#find(bufnr) abort
|
||||
return s:find(map(vsnip#source#filetypes(a:bufnr), 's:get_language(v:val)'))
|
||||
endfunction
|
||||
|
||||
"
|
||||
" find.
|
||||
"
|
||||
function! s:find(languages) abort
|
||||
" Load `package.json#contributes.snippets` if does not exists it's cache.
|
||||
let l:rtp_list = exists('*nvim_list_runtime_paths') ? nvim_list_runtime_paths() : split(&runtimepath, ',')
|
||||
for l:rtp in l:rtp_list
|
||||
if has_key(s:runtimepaths, l:rtp)
|
||||
continue
|
||||
endif
|
||||
let s:runtimepaths[l:rtp] = v:true
|
||||
|
||||
try
|
||||
let l:package_json = resolve(expand(l:rtp . '/package.json'))
|
||||
if !filereadable(l:package_json)
|
||||
continue
|
||||
endif
|
||||
let l:package_json = readfile(l:package_json)
|
||||
let l:package_json = type(l:package_json) == type([]) ? join(l:package_json, "\n") : l:package_json
|
||||
let l:package_json = iconv(l:package_json, 'utf-8', &encoding)
|
||||
let l:package_json = json_decode(l:package_json)
|
||||
|
||||
" if package.json has not `contributes.snippets` fields, skip it.
|
||||
if !has_key(l:package_json, 'contributes')
|
||||
\ || !has_key(l:package_json.contributes, 'snippets')
|
||||
continue
|
||||
endif
|
||||
|
||||
" Create source if does not exists it's cache.
|
||||
for l:snippet in l:package_json.contributes.snippets
|
||||
let l:path = resolve(expand(l:rtp . '/' . l:snippet.path))
|
||||
let l:languages = type(l:snippet.language) == type([]) ? l:snippet.language : [l:snippet.language]
|
||||
|
||||
" if already cached `snippets.json`, add new language.
|
||||
if has_key(s:snippets, l:path)
|
||||
for l:language in l:languages
|
||||
if index(s:snippets[l:path].languages, l:language) == -1
|
||||
call add(s:snippets[l:path].languages, l:language)
|
||||
endif
|
||||
endfor
|
||||
continue
|
||||
endif
|
||||
|
||||
" register new snippet.
|
||||
let s:snippets[l:path] = {
|
||||
\ 'languages': l:languages,
|
||||
\ }
|
||||
endfor
|
||||
catch /.*/
|
||||
endtry
|
||||
endfor
|
||||
|
||||
" filter by language.
|
||||
let l:sources = []
|
||||
for l:language in a:languages
|
||||
for [l:path, l:snippet] in items(s:snippets)
|
||||
if index(l:snippet.languages, l:language) >= 0
|
||||
if !has_key(l:snippet, 'source')
|
||||
let l:snippet.source = vsnip#source#create(l:path)
|
||||
end
|
||||
call add(l:sources, l:snippet.source)
|
||||
endif
|
||||
endfor
|
||||
endfor
|
||||
return l:sources
|
||||
endfunction
|
||||
|
||||
"
|
||||
" get_language.
|
||||
"
|
||||
function! s:get_language(filetype) abort
|
||||
return get({
|
||||
\ 'javascript.jsx': 'javascriptreact',
|
||||
\ 'typescript.tsx': 'typescriptreact',
|
||||
\ 'sh': 'shellscript',
|
||||
\ 'cs': 'csharp',
|
||||
\ }, a:filetype, a:filetype)
|
||||
endfunction
|
||||
|
||||
189
dot_vim/plugged/vim-vsnip/autoload/vsnip/variable.vim
Normal file
189
dot_vim/plugged/vim-vsnip/autoload/vsnip/variable.vim
Normal file
@@ -0,0 +1,189 @@
|
||||
let s:variables = {}
|
||||
|
||||
"
|
||||
" vsnip#variable#register
|
||||
"
|
||||
function! vsnip#variable#register(name, func, ...) abort
|
||||
let l:option = get(a:000, 0, {})
|
||||
let s:variables[a:name] = {
|
||||
\ 'func': a:func,
|
||||
\ 'once': get(l:option, 'once', v:false)
|
||||
\ }
|
||||
endfunction
|
||||
|
||||
"
|
||||
" vsnip#variable#get
|
||||
"
|
||||
function! vsnip#variable#get(name) abort
|
||||
return get(s:variables, a:name, v:null)
|
||||
endfunction
|
||||
|
||||
"
|
||||
" Register built-in variables.
|
||||
"
|
||||
" @see https://code.visualstudio.com/docs/editor/userdefinedsnippets#_variables
|
||||
"
|
||||
|
||||
function! s:TM_SELECTED_TEXT(context) abort
|
||||
let l:selected_text = vsnip#selected_text()
|
||||
if empty(l:selected_text)
|
||||
return v:null
|
||||
endif
|
||||
return vsnip#indent#trim_base_indent(l:selected_text)
|
||||
endfunction
|
||||
call vsnip#variable#register('TM_SELECTED_TEXT', function('s:TM_SELECTED_TEXT'))
|
||||
|
||||
function! s:TM_CURRENT_LINE(context) abort
|
||||
return getline('.')
|
||||
endfunction
|
||||
call vsnip#variable#register('TM_CURRENT_LINE', function('s:TM_CURRENT_LINE'))
|
||||
|
||||
function! s:TM_CURRENT_WORD(context) abort
|
||||
return v:null
|
||||
endfunction
|
||||
call vsnip#variable#register('TM_CURRENT_WORD', function('s:TM_CURRENT_WORD'))
|
||||
|
||||
function! s:TM_LINE_INDEX(context) abort
|
||||
return line('.') - 1
|
||||
endfunction
|
||||
call vsnip#variable#register('TM_LINE_INDEX', function('s:TM_LINE_INDEX'))
|
||||
|
||||
function! s:TM_LINE_NUMBER(context) abort
|
||||
return line('.')
|
||||
endfunction
|
||||
call vsnip#variable#register('TM_LINE_NUMBER', function('s:TM_LINE_NUMBER'))
|
||||
|
||||
function! s:TM_FILENAME(context) abort
|
||||
return expand('%:p:t')
|
||||
endfunction
|
||||
call vsnip#variable#register('TM_FILENAME', function('s:TM_FILENAME'))
|
||||
|
||||
function! s:TM_FILENAME_BASE(context) abort
|
||||
return substitute(expand('%:p:t'), '^\@<!\..*$', '', '')
|
||||
endfunction
|
||||
call vsnip#variable#register('TM_FILENAME_BASE', function('s:TM_FILENAME_BASE'))
|
||||
|
||||
function! s:TM_DIRECTORY(context) abort
|
||||
return expand('%:p:h:t')
|
||||
endfunction
|
||||
call vsnip#variable#register('TM_DIRECTORY', function('s:TM_DIRECTORY'))
|
||||
|
||||
function! s:TM_FILEPATH(context) abort
|
||||
return expand('%:p')
|
||||
endfunction
|
||||
call vsnip#variable#register('TM_FILEPATH', function('s:TM_FILEPATH'))
|
||||
|
||||
function! s:RELATIVE_FILEPATH(context) abort
|
||||
return expand('%')
|
||||
endfunction
|
||||
call vsnip#variable#register('RELATIVE_FILEPATH', function('s:RELATIVE_FILEPATH'))
|
||||
|
||||
function! s:CLIPBOARD(context) abort
|
||||
let l:clipboard = getreg(v:register)
|
||||
if empty(l:clipboard)
|
||||
return v:null
|
||||
endif
|
||||
return vsnip#indent#trim_base_indent(l:clipboard)
|
||||
endfunction
|
||||
call vsnip#variable#register('CLIPBOARD', function('s:CLIPBOARD'))
|
||||
|
||||
function! s:WORKSPACE_NAME(context) abort
|
||||
return v:null
|
||||
endfunction
|
||||
call vsnip#variable#register('WORKSPACE_NAME', function('s:WORKSPACE_NAME'))
|
||||
|
||||
function! s:CURRENT_YEAR(context) abort
|
||||
return strftime('%Y')
|
||||
endfunction
|
||||
call vsnip#variable#register('CURRENT_YEAR', function('s:CURRENT_YEAR'))
|
||||
|
||||
function! s:CURRENT_YEAR_SHORT(context) abort
|
||||
return strftime('%y')
|
||||
endfunction
|
||||
call vsnip#variable#register('CURRENT_YEAR_SHORT', function('s:CURRENT_YEAR_SHORT'))
|
||||
|
||||
function! s:CURRENT_MONTH(context) abort
|
||||
return strftime('%m')
|
||||
endfunction
|
||||
call vsnip#variable#register('CURRENT_MONTH', function('s:CURRENT_MONTH'))
|
||||
|
||||
function! s:CURRENT_MONTH_NAME(context) abort
|
||||
return strftime('%B')
|
||||
endfunction
|
||||
call vsnip#variable#register('CURRENT_MONTH_NAME', function('s:CURRENT_MONTH_NAME'))
|
||||
|
||||
function! s:CURRENT_MONTH_NAME_SHORT(context) abort
|
||||
return strftime('%b')
|
||||
endfunction
|
||||
call vsnip#variable#register('CURRENT_MONTH_NAME_SHORT', function('s:CURRENT_MONTH_NAME_SHORT'))
|
||||
|
||||
function! s:CURRENT_DATE(context) abort
|
||||
return strftime('%d')
|
||||
endfunction
|
||||
call vsnip#variable#register('CURRENT_DATE', function('s:CURRENT_DATE'))
|
||||
|
||||
function! s:CURRENT_DAY_NAME(context) abort
|
||||
return strftime('%A')
|
||||
endfunction
|
||||
call vsnip#variable#register('CURRENT_DAY_NAME', function('s:CURRENT_DAY_NAME'))
|
||||
|
||||
function! s:CURRENT_DAY_NAME_SHORT(context) abort
|
||||
return strftime('%a')
|
||||
endfunction
|
||||
call vsnip#variable#register('CURRENT_DAY_NAME_SHORT', function('s:CURRENT_DAY_NAME_SHORT'))
|
||||
|
||||
function! s:CURRENT_HOUR(context) abort
|
||||
return strftime('%H')
|
||||
endfunction
|
||||
call vsnip#variable#register('CURRENT_HOUR', function('s:CURRENT_HOUR'))
|
||||
|
||||
function! s:CURRENT_MINUTE(context) abort
|
||||
return strftime('%M')
|
||||
endfunction
|
||||
call vsnip#variable#register('CURRENT_MINUTE', function('s:CURRENT_MINUTE'))
|
||||
|
||||
function! s:CURRENT_SECOND(context) abort
|
||||
return strftime('%S')
|
||||
endfunction
|
||||
call vsnip#variable#register('CURRENT_SECOND', function('s:CURRENT_SECOND'))
|
||||
|
||||
function! s:CURRENT_SECONDS_UNIX(context) abort
|
||||
return localtime()
|
||||
endfunction
|
||||
call vsnip#variable#register('CURRENT_SECONDS_UNIX', function('s:CURRENT_SECONDS_UNIX'))
|
||||
|
||||
function! s:BLOCK_COMMENT_START(context) abort
|
||||
return split(&commentstring, '%s')[0]
|
||||
endfunction
|
||||
call vsnip#variable#register('BLOCK_COMMENT_START', function('s:BLOCK_COMMENT_START'))
|
||||
|
||||
function! s:BLOCK_COMMENT_END(context) abort
|
||||
let l:chars = split(&commentstring, '%s')
|
||||
let l:comment = len(l:chars) > 1 ? l:chars[1] : l:chars[0]
|
||||
return trim(l:comment)
|
||||
endfunction
|
||||
call vsnip#variable#register('BLOCK_COMMENT_END', function('s:BLOCK_COMMENT_END'))
|
||||
|
||||
function! s:LINE_COMMENT(context) abort
|
||||
let l:chars = split(&commentstring, '%s')
|
||||
let l:comment = &commentstring =~# '^/\*' ? '//' : substitute(&commentstring, '%s', '', 'g')
|
||||
return trim(l:comment)
|
||||
endfunction
|
||||
call vsnip#variable#register('LINE_COMMENT', function('s:LINE_COMMENT'))
|
||||
|
||||
function! s:VIM(context) abort
|
||||
let l:script = join(map(copy(a:context.node.children), 'v:val.text()'), '')
|
||||
try
|
||||
return eval(l:script)
|
||||
catch /.*/
|
||||
endtry
|
||||
return v:null
|
||||
endfunction
|
||||
call vsnip#variable#register('VIM', function('s:VIM'))
|
||||
|
||||
function! s:VSNIP_CAMELCASE_FILENAME(context) abort
|
||||
let l:basename = substitute(expand('%:p:t'), '^\@<!\..*$', '', '')
|
||||
return substitute(l:basename, '\(\%(\<\l\+\)\%(_\)\@=\)\|_\(\l\)', '\u\1\2', 'g')
|
||||
endfunction
|
||||
call vsnip#variable#register('VSNIP_CAMELCASE_FILENAME', function('s:VSNIP_CAMELCASE_FILENAME'))
|
||||
|
||||
Reference in New Issue
Block a user