I broke up with neovim....vim is my best friend now
This commit is contained in:
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
|
||||
Reference in New Issue
Block a user