1

Regenerate nvim config

This commit is contained in:
2024-06-02 03:29:20 +02:00
parent 75eea0c030
commit ef2e28883d
5576 changed files with 604886 additions and 503 deletions

View File

@ -0,0 +1,60 @@
" clock object - measuring elapsed time in a operation
" variables "{{{
" features
let s:has_reltime_and_float = has('reltime') && has('float')
"}}}
function! sandwich#clock#new() abort "{{{
return deepcopy(s:clock)
endfunction
"}}}
" s:clock "{{{
let s:clock = {
\ 'started' : 0,
\ 'paused' : 0,
\ 'zerotime': reltime(),
\ 'stoptime': reltime(),
\ 'losstime': 0,
\ }
"}}}
function! s:clock.start() dict abort "{{{
if self.started
if self.paused
let self.losstime += str2float(reltimestr(reltime(self.stoptime)))
let self.paused = 0
endif
else
if s:has_reltime_and_float
let self.zerotime = reltime()
let self.started = 1
endif
endif
endfunction
"}}}
function! s:clock.pause() dict abort "{{{
let self.stoptime = reltime()
let self.paused = 1
endfunction
"}}}
function! s:clock.elapsed() dict abort "{{{
if self.started
let total = str2float(reltimestr(reltime(self.zerotime)))
return floor((total - self.losstime)*1000)
else
return 0
endif
endfunction
"}}}
function! s:clock.stop() dict abort "{{{
let self.started = 0
let self.paused = 0
let self.losstime = 0
endfunction
"}}}
" vim:set foldmethod=marker:
" vim:set commentstring="%s:
" vim:set ts=2 sts=2 sw=2:

View File

@ -0,0 +1,42 @@
" constants - storing essential constants
" variables "{{{
" types
let s:type_list = type([])
"}}}
function! sandwich#constants#get(name) abort "{{{
return type(a:name) == s:type_list
\ ? map(copy(a:name), 's:constants[v:val]()')
\ : s:constants[a:name]()
endfunction
"}}}
" s:constants "{{{
let s:constants = {}
"}}}
" The maximum number of columns "{{{
function! s:constants.colmax() dict abort
return s:colmax_obtained ? s:colmax : s:colmax()
endfunction
let s:colmax = 2147483647 " default value in many cases
let s:colmax_obtained = 0
function! s:colmax() abort
let view = winsaveview()
try
normal! $
let colmax = winsaveview().curswant
call winrestview(view)
let s:colmax_obtained = 1
catch
let colmax = s:colmax
let s:colmax_obtained = 0
endtry
return colmax
endfunction
"}}}
" vim:set foldmethod=marker:
" vim:set commentstring="%s:
" vim:set ts=2 sts=2 sw=2:

View File

@ -0,0 +1,34 @@
let g:sandwich#filetype#tex#environments = get(g:, 'sandwich#filetype#tex#environments', [
\ 'array', 'center', 'description', 'enumerate', 'eqnarray', 'equation',
\ 'equation*', 'figure', 'flushleft', 'flushright', 'itemize', 'list',
\ 'minipage', 'picture', 'quotation', 'quote', 'tabbing', 'table',
\ 'tabular', 'tabular*', 'thebibliography', 'theorem', 'titlepage',
\ 'verbatim', 'verse'
\ ])
function! sandwich#filetype#tex#EnvCompl(argread, cmdline, cursorpos) abort
let n = strlen(a:argread)
if exists('b:sandwich#filetype#tex#environments')
let list = copy(b:sandwich#filetype#tex#environments)
else
let list = copy(g:sandwich#filetype#tex#environments)
endif
if n > 0
let list = filter(list, 'v:val[: n-1] ==# a:argread')
endif
return list
endfunction
function! sandwich#filetype#tex#EnvInput() abort
echohl MoreMsg
let env = input('Environment-name: ', '', 'customlist,sandwich#filetype#tex#EnvCompl')
echohl NONE
return [printf('\begin{%s}', env), printf('\end{%s}', env)]
endfunction
function! sandwich#filetype#tex#CmdInput() abort
echohl MoreMsg
let cmd = input('Command: ', '')
echohl NONE
return [printf('\%s{', cmd), '}']
endfunction

View File

@ -0,0 +1,514 @@
" highlight object - managing highlight on a buffer
" variables "{{{
" null valiables
let s:null_pos = [0, 0, 0, 0]
" types
let s:type_list = type([])
" patchs
if v:version > 704 || (v:version == 704 && has('patch237'))
let s:has_patch_7_4_362 = has('patch-7.4.362')
let s:has_patch_7_4_392 = has('patch-7.4.392')
else
let s:has_patch_7_4_362 = v:version == 704 && has('patch362')
let s:has_patch_7_4_392 = v:version == 704 && has('patch392')
endif
" features
let s:has_gui_running = has('gui_running')
let s:has_window_ID = exists('*win_getid')
" SID
function! s:SID() abort
return matchstr(expand('<sfile>'), '<SNR>\zs\d\+\ze_SID$')
endfunction
let s:SID = printf("\<SNR>%s_", s:SID())
delfunction s:SID
"}}}
function! sandwich#highlight#new() abort "{{{
return deepcopy(s:highlight)
endfunction
"}}}
" s:highlight "{{{
let s:highlight = {
\ 'status': 0,
\ 'group' : '',
\ 'id' : [],
\ 'order_list': [],
\ 'region': {},
\ 'linewise': 0,
\ 'bufnr': 0,
\ 'winid': 0,
\ }
"}}}
function! s:highlight.initialize() dict abort "{{{
call self.quench()
let self.status = 0
let self.group = ''
let self.id = []
let self.order_list = []
let self.region = {}
let self.linewise = 0
let self.bufnr = 0
let self.winid = 0
endfunction
"}}}
function! s:highlight.order(target, linewise) dict abort "{{{
let order = []
let order_list = []
for [head, tail, linewise] in [[a:target.head1, a:target.tail1, a:linewise[0]],
\ [a:target.head2, a:target.tail2, a:linewise[1]]]
if linewise
call s:highlight_order_linewise(order_list, order, head, tail)
else
call s:highlight_order_charwise(order_list, order, head, tail)
endif
endfor
if order != []
call add(order_list, order)
endif
let self.order_list += order_list
let self.region = deepcopy(a:target)
let self.linewise = a:linewise
endfunction
"}}}
function! s:highlight.show(...) dict abort "{{{
if self.order_list == []
return 0
endif
if a:0 < 1
if self.group ==# ''
return 0
else
let hi_group = self.group
endif
else
let hi_group = a:1
endif
if self.status
if hi_group ==# self.group
return 0
else
call self.quench()
endif
endif
for order in self.order_list
let self.id += s:matchaddpos(hi_group, order)
endfor
call filter(self.id, 'v:val > 0')
let self.status = 1
let self.group = hi_group
let self.bufnr = bufnr('%')
if s:has_window_ID
let self.winid = win_getid()
endif
return 1
endfunction
"}}}
function! s:highlight.quench() dict abort "{{{
if !self.status
return 0
endif
let tabnr = tabpagenr()
let winnr = winnr()
let view = winsaveview()
if self.highlighted_window()
call s:matchdelete_all(self.id)
let self.status = 0
return 1
endif
if s:is_in_cmdline_window() || s:is_in_popup_terminal_window()
let s:paused += [self]
augroup sandwich-pause-quenching
autocmd!
autocmd WinEnter * call s:got_out_of_cmdwindow()
augroup END
return 0
endif
if self.goto_highlighted_window()
call s:matchdelete_all(self.id)
else
call filter(self.id, 0)
endif
let self.status = 0
call s:goto_window(winnr, tabnr, view)
return 1
endfunction
"}}}
function! s:highlight.quench_timer(time) dict abort "{{{
let id = timer_start(a:time, s:SID . 'quench')
let s:quench_table[string(id)] = self
" this is called when user gets control again
call timer_start(1, {-> s:set_autocmds(id)})
return id
endfunction
"}}}
" function! s:highlight.highlighted_window() dict abort "{{{
if s:has_window_ID
function! s:highlight.highlighted_window() dict abort
return self.winid == win_getid()
endfunction
else
function! s:highlight.highlighted_window() dict abort
if self.id == []
return 0
endif
let id = self.id[0]
return filter(getmatches(), 'v:val.id == id') != [] ? 1 : 0
endfunction
endif
"}}}
" function! s:highlight.goto_highlighted_window() dict abort "{{{
if s:has_window_ID
function! s:highlight.goto_highlighted_window() dict abort
noautocmd let reached = win_gotoid(self.winid)
return reached
endfunction
else
function! s:highlight.goto_highlighted_window() dict abort
return s:search_highlighted_windows(self.id, tabpagenr()) != [0, 0]
endfunction
endif
"}}}
" for delayed quenching "{{{
let s:quench_table = {}
let s:paused = []
function! s:quench(id) abort "{{{
let options = s:shift_options()
let highlight = s:get(a:id)
try
if highlight != {}
call highlight.quench()
endif
catch /^Vim\%((\a\+)\)\=:E523/
" NOTE: For "textlock"
if highlight != {}
call highlight.quench_timer(50)
endif
return 1
finally
unlet s:quench_table[a:id]
call timer_stop(a:id)
call s:clear_autocmds()
call s:restore_options(options)
endtry
endfunction
"}}}
function! s:get(id) abort "{{{
return get(s:quench_table, string(a:id), {})
endfunction
"}}}
function! sandwich#highlight#cancel(...) abort "{{{
if a:0 > 0
let id_list = type(a:1) == s:type_list ? a:1 : a:000
else
let id_list = map(keys(s:quench_table), 'str2nr(v:val)')
endif
for id in id_list
call s:quench(id)
endfor
endfunction
"}}}
function! s:quench_paused(...) abort "{{{
if s:is_in_cmdline_window() || s:is_in_popup_terminal_window()
return
endif
augroup sandwich-pause-quenching
autocmd!
augroup END
for highlight in s:paused
call highlight.quench()
endfor
let s:paused = []
endfunction
"}}}
function! s:got_out_of_cmdwindow() abort "{{{
augroup sandwich-pause-quenching
autocmd!
autocmd CursorMoved * call s:quench_paused()
augroup END
endfunction
"}}}
function! s:set_autocmds(id) abort "{{{
augroup sandwich-highlight
autocmd!
execute printf('autocmd TextChanged,InsertEnter,BufUnload <buffer> call s:cancel_highlight(%s)', a:id)
execute printf('autocmd BufEnter * call s:switch_highlight(%s)', a:id)
augroup END
endfunction
"}}}
function! s:clear_autocmds() abort "{{{
augroup sandwich-highlight
autocmd!
augroup END
endfunction
"}}}
function! s:cancel_highlight(id) abort "{{{
call s:quench(a:id)
endfunction
"}}}
function! s:switch_highlight(id) abort "{{{
let highlight = s:get(a:id)
if highlight == {} || !highlight.highlighted_window()
return
endif
if highlight.bufnr == bufnr('%')
call highlight.show()
else
call highlight.quench()
endif
endfunction
"}}}
"}}}
" private functions
function! s:highlight_order_charwise(order_list, order, head, tail) abort "{{{
let n = len(a:order)
if a:head != s:null_pos && a:tail != s:null_pos && s:is_equal_or_ahead(a:tail, a:head)
if a:head[1] == a:tail[1]
call add(a:order, a:head[1:2] + [a:tail[2] - a:head[2] + 1])
let n += 1
else
for lnum in range(a:head[1], a:tail[1])
if lnum == a:head[1]
call add(a:order, a:head[1:2] + [col([a:head[1], '$']) - a:head[2] + 1])
elseif lnum == a:tail[1]
call add(a:order, [a:tail[1], 1] + [a:tail[2]])
else
call add(a:order, [lnum])
endif
if n == 7
call add(a:order_list, deepcopy(a:order))
call filter(a:order, 0)
let n = 0
else
let n += 1
endif
endfor
endif
endif
endfunction
"}}}
function! s:highlight_order_linewise(order_list, order, head, tail) abort "{{{
let n = len(a:order)
if a:head != s:null_pos && a:tail != s:null_pos && a:head[1] <= a:tail[1]
for lnum in range(a:head[1], a:tail[1])
call add(a:order, [lnum])
if n == 7
call add(a:order_list, deepcopy(a:order))
call filter(a:order, 0)
let n = 0
else
let n += 1
endif
endfor
endif
endfunction
"}}}
" function! s:matchaddpos(group, pos) abort "{{{
if s:has_patch_7_4_362
function! s:matchaddpos(group, pos) abort
return [matchaddpos(a:group, a:pos)]
endfunction
else
function! s:matchaddpos(group, pos) abort
let id_list = []
for pos in a:pos
if len(pos) == 1
let id_list += [matchadd(a:group, printf('\%%%dl', pos[0]))]
else
let id_list += [matchadd(a:group, printf('\%%%dl\%%>%dc.*\%%<%dc', pos[0], pos[1]-1, pos[1]+pos[2]))]
endif
endfor
return id_list
endfunction
endif
"}}}
function! s:matchdelete_all(ids) abort "{{{
if empty(a:ids)
return
endif
let alive_ids = map(getmatches(), 'v:val.id')
" Return if another plugin called clearmatches() which clears *ALL*
" highlights including others set.
if empty(alive_ids)
return
endif
if !count(alive_ids, a:ids[0])
return
endif
for id in a:ids
try
call matchdelete(id)
catch
endtry
endfor
call filter(a:ids, 0)
endfunction
"}}}
function! s:is_ahead(pos1, pos2) abort "{{{
return a:pos1[1] > a:pos2[1] || (a:pos1[1] == a:pos2[1] && a:pos1[2] > a:pos2[2])
endfunction
"}}}
function! s:is_equal_or_ahead(pos1, pos2) abort "{{{
return a:pos1[1] > a:pos2[1] || (a:pos1[1] == a:pos2[1] && a:pos1[2] >= a:pos2[2])
endfunction
"}}}
" function! s:is_in_cmdline_window() abort "{{{
if s:has_patch_7_4_392
function! s:is_in_cmdline_window() abort
return getcmdwintype() !=# ''
endfunction
else
function! s:is_in_cmdline_window() abort
let is_in_cmdline_window = 0
try
execute 'tabnext ' . tabpagenr()
catch /^Vim\%((\a\+)\)\=:E11/
let is_in_cmdline_window = 1
catch
finally
return is_in_cmdline_window
endtry
endfunction
endif
"}}}
" function! s:is_in_popup_terminal_window() abort "{{{
if exists('*popup_list')
function! s:is_in_popup_terminal_window() abort
return &buftype is# 'terminal' && count(popup_list(), win_getid())
endfunction
else
function! s:is_in_popup_terminal_window() abort
return 0
endfunction
endif
" }}}
function! s:shift_options() abort "{{{
let options = {}
""" tweak appearance
" hide_cursor
if s:has_gui_running
let options.cursor = &guicursor
set guicursor+=a:block-NONE
else
let options.cursor = &t_ve
set t_ve=
endif
return options
endfunction
"}}}
function! s:restore_options(options) abort "{{{
if s:has_gui_running
set guicursor&
let &guicursor = a:options.cursor
else
let &t_ve = a:options.cursor
endif
endfunction
"}}}
function! s:search_highlighted_windows(id, ...) abort "{{{
if a:id == []
return 0
endif
let original_winnr = winnr()
let original_tabnr = tabpagenr()
let original_view = winsaveview()
let tablist = range(1, tabpagenr('$'))
if a:0 > 0
let tabnr = a:1
let [tabnr, winnr] = s:scan_windows(a:id, tabnr)
if tabnr != 0
return [tabnr, winnr]
endif
call filter(tablist, 'v:val != tabnr')
endif
for tabnr in tablist
let [tabnr, winnr] = s:scan_windows(a:id, tabnr)
if tabnr != 0
return [tabnr, winnr]
endif
endfor
call s:goto_window(original_winnr, original_tabnr, original_view)
return [0, 0]
endfunction
"}}}
function! s:scan_windows(id, tabnr) abort "{{{
if s:goto_tab(a:tabnr)
for winnr in range(1, winnr('$'))
if s:goto_window(winnr) && s:is_highlighted_window(a:id)
return [a:tabnr, winnr]
endif
endfor
endif
return [0, 0]
endfunction
"}}}
function! s:is_highlighted_window(id) abort "{{{
if a:id != []
let id = a:id[0]
if filter(getmatches(), 'v:val.id == id') != []
return 1
endif
endif
return 0
endfunction
"}}}
function! s:goto_window(winnr, ...) abort "{{{
if a:0 > 0
if !s:goto_tab(a:1)
return 0
endif
endif
try
if a:winnr != winnr()
execute printf('noautocmd %swincmd w', a:winnr)
endif
catch /^Vim\%((\a\+)\)\=:E16/
return 0
endtry
if a:0 > 1
noautocmd call winrestview(a:2)
endif
return 1
endfunction
"}}}
function! s:goto_tab(tabnr) abort "{{{
if a:tabnr != tabpagenr()
execute 'noautocmd tabnext ' . a:tabnr
endif
return tabpagenr() == a:tabnr ? 1 : 0
endfunction
"}}}
" vim:set foldmethod=marker:
" vim:set commentstring="%s:
" vim:set ts=2 sts=2 sw=2:

View File

@ -0,0 +1,438 @@
let s:save_cpo = &cpo
set cpo&vim
" variables "{{{
let s:FALSE = 0
let s:TRUE = 1
let s:null_pos = [0, 0]
" patchs
if v:version > 704 || (v:version == 704 && has('patch237'))
let s:has_patch_7_4_1685 = has('patch-7.4.1685')
let s:has_patch_7_4_2011 = has('patch-7.4.2011')
else
let s:has_patch_7_4_1685 = v:version == 704 && has('patch1685')
let s:has_patch_7_4_2011 = v:version == 704 && has('patch2011')
endif
"}}}
" default patterns
let g:sandwich#magicchar#f#default_patterns = [
\ {
\ 'header' : '\<\h\k*',
\ 'bra' : '(',
\ 'ket' : ')',
\ 'footer' : '',
\ },
\ ]
function! sandwich#magicchar#f#fname() abort "{{{
call operator#sandwich#show()
try
echohl MoreMsg
if &filetype ==# 'vim'
let funcname = input('funcname: ', '', 'custom,sandwich#magicchar#f#fnamecompl_vim')
else
let funcname = input('funcname: ', '', 'custom,sandwich#magicchar#f#fnamecompl')
endif
" flash prompt
echo ''
finally
echohl NONE
call operator#sandwich#quench()
endtry
if funcname ==# ''
throw 'OperatorSandwichCancel'
endif
return funcname . '('
endfunction
"}}}
function! sandwich#magicchar#f#fnamecompl(ArgLead, CmdLine, CursorPos) abort "{{{
return join(uniq(sort(s:buffer_completion())), "\n")
endfunction
"}}}
function! sandwich#magicchar#f#fnamecompl_vim(ArgLead, CmdLine, CursorPos) abort "{{{
if s:has_patch_7_4_2011
let getcomp = map(filter(getcompletion(a:ArgLead, 'function'), 'v:val =~# ''\C^[a-z][a-zA-Z0-9_]*($'''), 'matchstr(v:val, ''\C^[a-z][a-zA-Z0-9_]*'')')
else
let getcomp = []
endif
let buffer = s:buffer_completion()
return join(uniq(sort(getcomp + buffer)), "\n")
endfunction
"}}}
function! s:buffer_completion() abort "{{{
" NOTE: This func does neither sort nor uniq.
let list = []
let lines = getline(1, '$')
let pattern_list = s:resolve_patterns()
for func in pattern_list
let pat = printf('%s\ze%s', func.header, func.bra)
for line in lines
let list += s:extract_pattern(line, pat)
endfor
endfor
return list
endfunction
"}}}
function! s:extract_pattern(string, pat) abort "{{{
let list = []
let end = 0
while 1
let [str, start, end] = s:matchstrpos(a:string, a:pat, end)
if start < 0
break
endif
let list += [str]
endwhile
return list
endfunction
"}}}
" function! s:matchstrpos(expr, pat, ...) abort "{{{
if s:has_patch_7_4_1685
let s:matchstrpos = function('matchstrpos')
else
function! s:matchstrpos(expr, pat, ...) abort
if a:0 == 0
return [matchstr(a:expr, a:pat), match(a:expr, a:pat), matchend(a:expr, a:pat)]
elseif a:0 == 1
return [matchstr(a:expr, a:pat, a:1), match(a:expr, a:pat, a:1), matchend(a:expr, a:pat, a:1)]
else
return [matchstr(a:expr, a:pat, a:1, a:2), match(a:expr, a:pat, a:1, a:2), matchend(a:expr, a:pat, a:1, a:2)]
endif
endfunction
endif
"}}}
" textobj-functioncall (bundle version)
" NOTE: https://github.com/machakann/vim-textobj-functioncall
function! sandwich#magicchar#f#i(mode) abort "{{{
call sandwich#magicchar#f#textobj('i', a:mode)
endfunction
"}}}
function! sandwich#magicchar#f#a(mode) abort "{{{
call sandwich#magicchar#f#textobj('a', a:mode)
endfunction
"}}}
function! sandwich#magicchar#f#ip(mode) abort "{{{
call sandwich#magicchar#f#textobj('ip', a:mode)
endfunction
"}}}
function! sandwich#magicchar#f#ap(mode) abort "{{{
call sandwich#magicchar#f#textobj('ap', a:mode)
endfunction
"}}}
function! sandwich#magicchar#f#textobj(kind, mode, ...) abort "{{{
let l:count = v:count1
if a:0
let pattern_list = a:1
else
let pattern_list = s:resolve_patterns()
endif
let searchlines = s:get('textobj_sandwich_function_searchlines' , 30)
let stopline = {}
if searchlines < 0
let stopline.upper = 1
let stopline.lower = line('$')
else
let stopline.upper = max([1, line('.') - searchlines])
let stopline.lower = min([line('.') + searchlines, line('$')])
endif
let [start, end] = [s:null_pos, s:null_pos]
let view = winsaveview()
try
let candidates = s:gather_candidates(a:kind, a:mode, l:count, pattern_list, stopline)
let elected = s:elect(candidates, l:count)
if elected != {}
let [start, end] = s:to_range(a:kind, elected)
endif
finally
call winrestview(view)
endtry
call s:select(start, end)
endfunction
"}}}
function! s:Candidate(head, bra, ket, tail, pat, rank) abort
return {
\ 'head': a:head,
\ 'bra': a:bra,
\ 'ket': a:ket,
\ 'tail': a:tail,
\ 'rank': a:rank,
\ 'pattern': a:pat,
\ 'len': s:buflen(a:head, a:tail),
\ }
endfunction
function! s:gather_candidates(kind, mode, count, pattern_list, stopline) abort "{{{
let curpos = getpos('.')[1:2]
let rank = 0
let candidates = []
for pattern in a:pattern_list
let rank += 1
let candidates += s:search_pattern(pattern, a:kind, a:mode, a:count, rank, curpos, a:stopline)
call cursor(curpos)
endfor
return a:kind[0] is# 'a' && candidates == []
\ ? s:gather_candidates('i', a:mode, a:count, a:pattern_list, a:stopline)
\ : candidates
endfunction
"}}}
function! s:search_pattern(pat, kind, mode, count, rank, curpos, stopline) abort "{{{
let a:pat.head = a:pat.header . a:pat.bra
let a:pat.tail = a:pat.ket . a:pat.footer
let brapos = s:search_key_bra(a:kind, a:curpos, a:pat, a:stopline)
if brapos == s:null_pos | return [] | endif
let is_string = s:is_string_syntax(brapos)
let candidates = []
while len(candidates) < a:count
let c = s:get_candidate(a:pat, a:kind, a:mode, a:rank, brapos, is_string, a:stopline)
if c != {}
call add(candidates, c)
endif
call cursor(brapos)
" move to the next 'bra'
let brapos = searchpairpos(a:pat.bra, '', a:pat.ket, 'b', '', a:stopline.upper)
if brapos == s:null_pos | break | endif
let is_string = s:is_string_syntax(brapos)
endwhile
return candidates
endfunction
"}}}
function! s:search_key_bra(kind, curpos, pat, stopline) abort "{{{
let brapos = s:null_pos
if a:kind[0] is# 'a'
" search for the first 'bra'
if searchpos(a:pat.tail, 'cn', a:stopline.lower) == a:curpos
let brapos = searchpairpos(a:pat.bra, '', a:pat.ket, 'b', '', a:stopline.upper)
endif
let brapos = searchpairpos(a:pat.bra, '', a:pat.ket, 'b', '', a:stopline.upper)
elseif a:kind[0] is# 'i'
let head_start = searchpos(a:pat.head, 'bc', a:stopline.upper)
let head_end = searchpos(a:pat.head, 'ce', a:stopline.lower)
call cursor(a:curpos)
let tail_start = searchpos(a:pat.tail, 'bc', a:stopline.upper)
let tail_end = searchpos(a:pat.tail, 'ce', a:stopline.lower)
" check the initial position
if s:is_in_between(a:curpos, head_start, head_end)
" cursor is on a header
call cursor(head_end)
elseif s:is_in_between(a:curpos, tail_start, tail_end)
" cursor is on a footer
call cursor(tail_start)
if tail_start[1] == 1
normal! k$
else
normal! h
endif
else
" cursor is in between a bra and a ket
call cursor(a:curpos)
endif
" move to the corresponded 'bra'
let brapos = searchpairpos(a:pat.bra, '', a:pat.ket, 'bc', '', a:stopline.upper)
endif
return brapos
endfunction
"}}}
function! s:get_candidate(pat, kind, mode, rank, brapos, is_string, stopline) abort "{{{
" 'bra' should accompany with 'header'
let headstart = searchpos(a:pat.head, 'bc', a:stopline.upper)
let headend = searchpos(a:pat.head, 'ce', a:stopline.lower)
call cursor(a:brapos)
if !s:is_in_between(a:brapos, headstart, headend)
return {}
endif
let headpos = headstart
" search for the paired 'ket'
let skip = 's:is_string_syntax(getpos(".")[1:2]) != a:is_string'
let ketpos = searchpairpos(a:pat.bra, '', a:pat.ket, '', skip, a:stopline.lower)
if ketpos == s:null_pos
return {}
endif
let tailpos = searchpos(a:pat.tail, 'ce', a:stopline.lower)
if searchpos(a:pat.tail, 'bcn', a:stopline.upper) != ketpos
return {}
endif
let c = s:Candidate(headpos, a:brapos, ketpos, tailpos, a:pat, a:rank)
if !s:is_valid_candidate(c, a:kind, a:mode, a:is_string)
return {}
endif
return c
endfunction
"}}}
function! s:elect(candidates, count) abort "{{{
if a:candidates == []
return {}
endif
let filter = 'v:val.head != s:null_pos && v:val.bra != s:null_pos && v:val.ket != s:null_pos && v:val.tail != s:null_pos'
call filter(a:candidates, filter)
call sort(a:candidates, 's:compare')
if len(a:candidates) < a:count
return {}
endif
return a:candidates[a:count - 1]
endfunction
"}}}
function! s:to_range(kind, candidate) abort "{{{
if a:kind[1] is# 'p'
let [start, end] = s:parameter_region(a:candidate)
else
let start = a:candidate.head
let end = a:candidate.tail
endif
return [start, end]
endfunction
"}}}
function! s:select(start, end) abort "{{{
if a:start == s:null_pos || a:end == s:null_pos
return
endif
normal! v
call cursor(a:start)
normal! o
call cursor(a:end)
if &selection is# 'exclusive'
normal! l
endif
endfunction
"}}}
function! s:is_in_between(pos, start, end) abort "{{{
return (a:pos != s:null_pos) && (a:start != s:null_pos) && (a:end != s:null_pos)
\ && ((a:pos[0] > a:start[0]) || ((a:pos[0] == a:start[0]) && (a:pos[1] >= a:start[1])))
\ && ((a:pos[0] < a:end[0]) || ((a:pos[0] == a:end[0]) && (a:pos[1] <= a:end[1])))
endfunction
"}}}
function! s:is_string_syntax(pos) abort "{{{
return match(map(synstack(a:pos[0], a:pos[1]), 'synIDattr(synIDtrans(v:val), "name")'), 'String') > -1
endfunction
"}}}
function! s:is_continuous_syntax(brapos, ketpos) abort "{{{
let start_col = a:brapos[1]
for lnum in range(a:brapos[0], a:ketpos[0])
if lnum == a:ketpos[0]
let end_col= a:ketpos[1]
else
let end_col= col([lnum, '$'])
endif
for col in range(start_col, end_col)
if match(map(synstack(lnum, col), 'synIDattr(synIDtrans(v:val), "name")'), 'String') < 0
return 0
endif
endfor
let start_col = 1
endfor
return 1
endfunction
"}}}
function! s:is_valid_syntax(candidate, is_string) abort "{{{
return !a:is_string || s:is_continuous_syntax(a:candidate.bra, a:candidate.ket)
endfunction
"}}}
function! s:is_same_or_adjacent(p1, p2) abort "{{{
return a:p1 == a:p2 || (a:p1[0] == a:p2[0] && a:p1[1]+1 == a:p2[1])
endfunction
"}}}
function! s:is_wider(candidate, start, end) abort "{{{
return (s:is_ahead(a:start, a:candidate.head) && s:is_same_or_ahead(a:candidate.tail, a:end)) ||
\ (s:is_same_or_ahead(a:start, a:candidate.head) && s:is_ahead(a:candidate.tail, a:end))
endfunction
"}}}
function! s:is_ahead(p1, p2) abort "{{{
return (a:p1[0] > a:p2[0]) || (a:p1[0] == a:p2[0] && a:p1[1] > a:p2[1])
endfunction
"}}}
function! s:is_same_or_ahead(p1, p2) abort "{{{
return (a:p1[0] > a:p2[0]) || (a:p1[0] == a:p2[0] && a:p1[1] >= a:p2[1])
endfunction
"}}}
function! s:is_valid_candidate(c, kind, mode, is_string) abort "{{{
if a:kind[1] is# 'p' && s:is_same_or_adjacent(a:c.bra, a:c.ket)
return s:FALSE
endif
if a:mode is# 'x' && !s:is_wider(a:c, getpos("'<")[1:2], getpos("'>")[1:2])
return s:FALSE
endif
if !s:is_valid_syntax(a:c, a:is_string)
return s:FALSE
endif
return s:TRUE
endfunction
"}}}
function! s:resolve_patterns() abort "{{{
return deepcopy(get(b:, 'sandwich_magicchar_f_patterns',
\ get(g:, 'sandwich#magicchar#f#patterns',
\ g:sandwich#magicchar#f#default_patterns)))
endfunction
"}}}
function! s:parameter_region(candidate) abort "{{{
let whichwrap = &whichwrap
let &whichwrap = 'h,l'
let [visualhead, visualtail] = [getpos("'<"), getpos("'>")]
try
normal! v
call cursor(a:candidate.bra)
call search(a:candidate.pattern.bra, 'ce', a:candidate.ket[0])
normal! l
let head = getpos('.')[1:2]
normal! o
call cursor(a:candidate.ket)
normal! h
let tail = getpos('.')[1:2]
execute "normal! \<Esc>"
finally
let &whichwrap = whichwrap
call setpos("'<", visualhead)
call setpos("'>", visualtail)
endtry
return [head, tail]
endfunction
"}}}
function! s:buflen(start, end) abort "{{{
" start, end -> [lnum, col]
if a:start[0] == a:end[0]
let len = a:end[1] - a:start[1] + 1
else
let len = (line2byte(a:end[0]) + a:end[1]) - (line2byte(a:start[0]) + a:start[1]) + 1
endif
return len
endfunction
"}}}
function! s:compare(i1, i2) abort "{{{
" i1, i2 -> Candidate
if a:i1.len < a:i2.len
return -1
elseif a:i1.len > a:i2.len
return 1
else
return a:i2.rank - a:i1.rank
endif
endfunction
"}}}
function! s:get(name, default) abort "{{{
return exists('b:' . a:name) ? b:[a:name]
\ : exists('g:' . a:name) ? g:[a:name]
\ : a:default
endfunction
"}}}
let &cpo = s:save_cpo
unlet s:save_cpo
" vim:set foldmethod=marker:
" vim:set commentstring="%s:

View File

@ -0,0 +1,102 @@
let s:last_inserted = []
let s:last_searched = []
" variables "{{{
" patchs
if v:version > 704 || (v:version == 704 && has('patch237'))
let s:has_patch_7_4_1685 = has('patch-7.4.1685')
else
let s:has_patch_7_4_1685 = v:version == 704 && has('patch1685')
endif
"}}}
function! sandwich#magicchar#i#input(kind, ...) abort "{{{
let storepoint = a:kind ==# 'operator' ? 'last_inserted' : 'last_searched'
let former = s:input(a:kind . '-sandwich:head: ')
let quit_if_empty = get(a:000, 0, 0)
if quit_if_empty && former ==# ''
let s:{storepoint} = []
return ['', '']
endif
let latter = s:input(a:kind . '-sandwich:tail: ')
let s:{storepoint} = [former, latter]
return copy(s:{storepoint})
endfunction
"}}}
function! sandwich#magicchar#i#lastinput(kind, ...) abort "{{{
let storepoint = a:kind ==# 'operator' ? 'last_inserted' : 'last_searched'
let cancel_if_invalid = get(a:000, 0, 0)
if cancel_if_invalid && s:{storepoint} == []
throw 'OperatorSandwichCancel'
endif
return s:{storepoint} != [] ? copy(s:{storepoint}) : ['', '']
endfunction
"}}}
function! sandwich#magicchar#i#compl(ArgLead, CmdLine, CursorPos) abort "{{{
let list = []
let lines = getline(1, '$')
if a:ArgLead ==# ''
let list += s:extract_patterns_from_lines(lines, '\<\k\{3,}\>')
else
let list += s:extract_patterns_from_lines(lines, printf('\<%s.\{-}\>', a:ArgLead))
if list == []
let tail = matchstr(a:ArgLead, '\<\k\+$')
if tail !=# '' && tail !=# a:ArgLead
let list += s:extract_patterns_from_lines(lines, tail . '.\{-}\>')
endif
endif
endif
return join(uniq(sort(list)), "\n")
endfunction
"}}}
function! s:input(mes) abort "{{{
echohl MoreMsg
try
let input = input(a:mes, '', 'custom,sandwich#magicchar#i#compl')
" flash prompt
echo ''
finally
echohl NONE
endtry
return input
endfunction
"}}}
function! s:extract_patterns_from_lines(lines, pat) abort "{{{
let list = []
for line in a:lines
let list += s:extract_pattern(line, a:pat)
endfor
return list
endfunction
"}}}
function! s:extract_pattern(string, pat) abort "{{{
let list = []
let end = 0
while 1
let [str, start, end] = s:matchstrpos(a:string, a:pat, end)
if start < 0
break
endif
let list += [str]
endwhile
return list
endfunction
"}}}
" function! s:matchstrpos(expr, pat, ...) abort "{{{
if s:has_patch_7_4_1685
let s:matchstrpos = function('matchstrpos')
else
function! s:matchstrpos(expr, pat, ...) abort
if a:0 == 0
return [matchstr(a:expr, a:pat), match(a:expr, a:pat), matchend(a:expr, a:pat)]
elseif a:0 == 1
return [matchstr(a:expr, a:pat, a:1), match(a:expr, a:pat, a:1), matchend(a:expr, a:pat, a:1)]
else
return [matchstr(a:expr, a:pat, a:1, a:2), match(a:expr, a:pat, a:1, a:2), matchend(a:expr, a:pat, a:1, a:2)]
endif
endfunction
endif
"}}}
" vim:set foldmethod=marker:
" vim:set commentstring="%s:

View File

@ -0,0 +1,446 @@
" variables "{{{
let s:null_coord = [0, 0]
" types
let s:type_num = type(0)
let s:type_str = type('')
let s:type_list = type([])
"}}}
function! sandwich#magicchar#t#tag() abort "{{{
call operator#sandwich#show()
try
echohl MoreMsg
let old_imsearch = &l:imsearch
let &l:imsearch = 0
let tag = input('Input tag: ')
let &l:imsearch = old_imsearch
" flash prompt
echo ''
finally
echohl NONE
call operator#sandwich#quench()
endtry
if tag ==# ''
throw 'OperatorSandwichCancel'
endif
let [open, close] = s:expand(tag)
return [printf('<%s>', open), printf('</%s>', close)]
endfunction
"}}}
function! sandwich#magicchar#t#tagname() abort "{{{
call operator#sandwich#show()
try
echohl MoreMsg
let old_imsearch = &l:imsearch
let &l:imsearch = 0
let tagname = input('Input tag name: ')
let &l:imsearch = old_imsearch
" flash prompt
echo ''
finally
echohl NONE
call operator#sandwich#quench()
endtry
if tagname ==# ''
throw 'OperatorSandwichCancel'
endif
return s:expand(tagname)
endfunction
"}}}
function! s:tokenize(text) abort "{{{
let j = 0
let n = strlen(a:text)
let tokenlist = []
while j >= 0 && j < n
let i = j
let j = match(a:text, '\m[[[:blank:]#.]', i)
if i < j
let tokenlist += [a:text[i : j-1]]
elseif i == j
let char = a:text[i]
if char =~# '\m\s'
" space is not allowed
throw 'SandwichMagiccharTIncorrectSyntax'
elseif char ==# '['
" tokenize custom attributes
let tokenlist += [char]
let j += 1
let [j, tokenlist] += s:tokenize_custom_attributes(a:text, j)
else
let tokenlist += [char]
let j += 1
endif
endif
endwhile
let i = j >= 0 ? j : i
if i < n
let tokenlist += [a:text[i :]]
endif
return tokenlist
endfunction
"}}}
function! s:tokenize_custom_attributes(text, j) abort "{{{
let j = a:j
let n = strlen(a:text)
let closed = 0
let tokenlist = []
while j >= 0 && j < n
let i = j
let j = match(a:text, '\m[][:blank:]="'']', i)
if i < j
let string = a:text[i : j-1]
let tokenlist += [string]
elseif i == j
let char = a:text[i]
if char =~# '\m\s'
" skip space
let j = matchend(a:text, '\m\s\+', i)
if j > i
let tokenlist += [a:text[i : j-1]]
endif
elseif char =~# '\m["'']'
" skip string literal
let j = match(a:text, char, i+1)
if j > 0
let tokenlist += [a:text[i : j]]
let j += 1
else
" unclosed string literal
throw 'SandwichMagiccharTIncorrectSyntax'
endif
elseif char ==# ']'
" the end of custom attributes region
let tokenlist += [char]
let j += 1
let closed = 1
break
else
let tokenlist += [char]
let j += 1
endif
endif
endwhile
if !closed
" unclosed braket
throw 'SandwichMagiccharTIncorrectSyntax'
endif
return [j - a:j, tokenlist]
endfunction
"}}}
function! s:matches_filetype(filetype, list) abort "{{{
return index(a:list, a:filetype) != -1
endfunction
" }}}
function! s:parse(tokenlist) abort "{{{
let itemdict = deepcopy(s:itemdict)
let itemlist = map(copy(a:tokenlist), '{"is_operator": v:val =~# ''^\%([][#.=]\|\s\+\)$'' ? 1 : 0, "string": v:val}')
let i = 0
let n = len(itemlist)
let item = itemlist[i]
if item.is_operator
call itemdict.queue_element('div')
else
call itemdict.queue_element(item.string)
let i += 1
endif
while i < n
let item = itemlist[i]
if item.is_operator
if item.string ==# '#'
let i = s:handle_id(itemdict, itemlist, i)
elseif item.string ==# '.'
if s:matches_filetype(&filetype, g:sandwich#jsx_filetypes)
let i = s:handle_className(itemdict, itemlist, i)
else
let i = s:handle_class(itemdict, itemlist, i)
endif
elseif item.string ==# '['
let i = s:parse_custom_attributes(itemdict, itemlist, i)
else
" unanticipated operator (should not reach here)
throw 'SandwichMagiccharTIncorrectSyntax'
endif
else
" successive operand is not allowed here (should not reach here)
throw 'SandwichMagiccharTIncorrectSyntax'
endif
endwhile
let parsed = deepcopy(itemdict.queue)
for item in parsed
call filter(item, 'v:key =~# ''\%(name\|value\)''')
endfor
return parsed
endfunction
"}}}
function! s:parse_custom_attributes(itemdict, itemlist, i) abort "{{{
" check ket
let i_ket = s:check_ket(a:itemlist, a:i)
if i_ket < 0
" ket does not exist (should not reach here)
throw 'SandwichMagiccharTIncorrectSyntax'
endif
let i = a:i + 1
while i < i_ket
let item = a:itemlist[i]
if item.is_operator
if item.string ==# '='
let i = s:handle_equal(a:itemdict, a:itemlist, i)
else
let i += 1
endif
else
let i = s:handle_custom_attr_name(a:itemdict, a:itemlist, i)
endif
endwhile
call a:itemdict.queue_custom_attr()
return i_ket + 1
endfunction
"}}}
function! s:handle_id(itemdict, itemlist, i) abort "{{{
let i = a:i + 1
let item = get(a:itemlist, i, {'is_operator': 1})
if item.is_operator
call a:itemdict.queue_id()
else
call a:itemdict.queue_id(item.string)
let i += 1
endif
return i
endfunction
"}}}
function! s:handle_class(itemdict, itemlist, i) abort "{{{
let i = a:i + 1
let item = get(a:itemlist, i, {'is_operator': 1})
if item.is_operator
call a:itemdict.queue_class()
else
call a:itemdict.queue_class(item.string)
let i += 1
endif
return i
endfunction
"}}}
function! s:handle_className(itemdict, itemlist, i) abort "{{{
let i = a:i + 1
let item = get(a:itemlist, i, {'is_operator': 1})
if item.is_operator
call a:itemdict.queue_className()
else
call a:itemdict.queue_className(item.string)
let i += 1
endif
return i
endfunction
"}}}
function! s:handle_equal(itemdict, itemlist, i, ...) abort "{{{
let i = a:i + 1
let item = a:itemlist[i]
let name = get(a:000, 0, '')
if item.is_operator
if item.string ==# '='
call a:itemdict.list_custom_attr(name)
else
call a:itemdict.list_custom_attr(name)
let i += 1
endif
else
call a:itemdict.list_custom_attr(name, item.string)
let i += 1
endif
return i
endfunction
"}}}
function! s:handle_custom_attr_name(itemdict, itemlist, i) abort "{{{
let item = a:itemlist[a:i]
let name = item.string
let i = a:i + 1
let item = a:itemlist[i]
if item.is_operator
if item.string ==# '='
let i = s:handle_equal(a:itemdict, a:itemlist, i, name)
else
call a:itemdict.list_custom_attr(name)
let i += 1
endif
else
call a:itemdict.list_custom_attr(name)
endif
return i
endfunction
"}}}
function! s:check_ket(itemlist, i) abort "{{{
let i = a:i + 1
let n = len(a:itemlist)
let i_ket = -1
while i < n
let item = a:itemlist[i]
if item.is_operator && item.string ==# ']'
let i_ket = i
break
endif
let i += 1
endwhile
return i_ket
endfunction
"}}}
function! s:build(itemlist) abort "{{{
let fragments = [a:itemlist[0]['value']]
if len(a:itemlist) > 1
for item in a:itemlist[1:]
let name = item.name
let value = type(item.value) == s:type_list ? join(filter(item.value, 'v:val !=# ""')) : item.value
let value = value !~# '^\(["'']\).*\1$' ? printf('"%s"', value) : value
let fragments += [printf('%s=%s', name, value)]
endfor
endif
return join(fragments)
endfunction
"}}}
function! s:expand(text) abort "{{{
try
let tokenlist = s:tokenize(a:text)
let itemlist = s:parse(tokenlist)
catch /^SandwichMagiccharTIncorrectSyntax$/
return [a:text, matchstr(a:text, '^\s*\zs\a[^[:blank:]>/]*')]
endtry
let element = itemlist[0]['value']
return [s:build(itemlist), element]
endfunction
"}}}
function! sandwich#magicchar#t#i() abort "{{{
call s:prototype('i')
endfunction
"}}}
function! sandwich#magicchar#t#a() abort "{{{
call s:prototype('a')
endfunction
"}}}
function! s:prototype(kind) abort "{{{
let view = winsaveview()
let visualhead = getpos("'<")
let visualtail = getpos("'>")
execute printf('silent! normal! v%dat', v:count1)
execute "normal! \<Esc>"
if getpos("'<") != getpos("'>")
normal! gv
let pat = a:kind ==# 'i' ? '</\ze\a[^[:blank:]>/]*' : '</\a[^[:blank:]>/]*\ze\s*>'
call search(pat, 'be', line("'<"))
normal! o
let pat = a:kind ==# 'i' ? '<\a[^[:blank:]>/]*\zs\_.' : '<\zs\a[^[:blank:]>/]*'
call search(pat, '', line("'>"))
else
call winrestview(view)
call setpos("'<", visualhead)
call setpos("'>", visualtail)
endif
endfunction
"}}}
function! sandwich#magicchar#t#it() abort "{{{
call s:textobj('i')
endfunction
"}}}
function! sandwich#magicchar#t#at() abort "{{{
call s:textobj('a')
endfunction
"}}}
function! s:textobj(a_or_i) abort "{{{
let head = searchpos('<\a[^[:blank:]>/]*', 'bcn', 0, 50)
if head != s:null_coord
let tagname = matchstr(getline(head[0])[head[1]-1 :], '^<\zs\a[^[:blank:]>/]*')
if search(printf('</%s>', s:escape(tagname)), 'cn', 0, 50)
" add :silent! to suppress errorbell
if a:a_or_i ==# 'i'
silent! normal! vit
else
silent! normal! vat
endif
endif
endif
endfunction
"}}}
function! s:escape(string) abort "{{{
return escape(a:string, '~"\.^$[]*')
endfunction
"}}}
" itemdict object "{{{
let s:itemdict = {
\ 'element': {'name': 'element', 'value': ''},
\ 'id' : {'name': 'id', 'value': '', 'queued': 0},
\ 'class' : {'name': 'class', 'value': [], 'queued': 0},
\ 'className' : {'name': 'className', 'value': [], 'queued': 0},
\ 'queue' : [],
\ 'customlist': [],
\ }
function! s:itemdict.queue_element(value) dict abort "{{{
let self.element.value = a:value
call add(self.queue, self.element)
endfunction
"}}}
function! s:itemdict.queue_id(...) dict abort "{{{
let self.id.value = get(a:000, 0, '')
if !self.id.queued
call add(self.queue, self.id)
let self.id.queued = 1
endif
endfunction
"}}}
function! s:itemdict.queue_class(...) dict abort "{{{
call add(self.class.value, get(a:000, 0, ''))
if !self.class.queued
call add(self.queue, self.class)
let self.class.queued = 1
endif
endfunction
"}}}
function! s:itemdict.queue_className(...) dict abort "{{{
call add(self.className.value, get(a:000, 0, ''))
if !self.className.queued
call add(self.queue, self.className)
let self.className.queued = 1
endif
endfunction
"}}}
function! s:itemdict.queue_custom_attr() dict abort "{{{
if self.customlist != []
call extend(self.queue, remove(self.customlist, 0, -1))
endif
endfunction
"}}}
function! s:itemdict.list_custom_attr(name, ...) dict abort "{{{
let value = get(a:000, 0, '')
if a:name ==# 'id'
call self.queue_id(value)
elseif a:name ==# 'class'
call self.queue_class(value)
endif
if a:name ==# ''
let custom_attr = {'kind': 'custom', 'name': a:name, 'value': value}
call add(self.customlist, custom_attr)
else
if !has_key(self, a:name)
let self[a:name] = {'kind': 'custom', 'name': a:name, 'value': value}
call add(self.customlist, self[a:name])
else
let self[a:name]['value'] = value
endif
endif
endfunction
"}}}
"}}}
" vim:set foldmethod=marker:
" vim:set commentstring="%s:

View File

@ -0,0 +1,67 @@
" messenger object - managing messages and errors.
function! sandwich#messenger#new() abort "{{{
let s:messenger = deepcopy(s:messenger_prototype)
return s:messenger
endfunction
"}}}
function! sandwich#messenger#get() abort "{{{
return s:messenger
endfunction
"}}}
" s:messenger_prototype "{{{
let s:messenger_prototype = {
\ 'error' : {'flag': 0, 'string': ''},
\ 'notice': {'flag': 0, 'list': []},
\ }
"}}}
function! s:messenger_prototype.initialize() dict abort "{{{
let self.error.flag = 0
let self.error.string = ''
let self.notice.flag = 0
let self.notice.list = []
endfunction
"}}}
function! s:messenger_prototype.notify(prefix) dict abort "{{{
if self.error.flag
call self.error.echoerr(a:prefix)
elseif self.notice.flag
call self.notice.echo(a:prefix)
endif
endfunction
"}}}
function! s:messenger_prototype.error.echoerr(prefix) dict abort "{{{
echoerr a:prefix . self.string
endfunction
"}}}
function! s:messenger_prototype.notice.echo(prefix) dict abort "{{{
let queue = []
if self.list != []
for line in self.list
let queue += [[a:prefix, 'MoreMsg']]
let queue += [line]
let queue += [["\n", 'NONE']]
endfor
call remove(queue, -1)
endif
call sandwich#util#echo(queue)
endfunction
"}}}
function! s:messenger_prototype.error.set(errmes) dict abort "{{{
let self.flag = 1
let self.string = a:errmes
endfunction
"}}}
function! s:messenger_prototype.notice.queue(line) dict abort "{{{
let self.flag = 1
call add(self.list, a:line)
endfunction
"}}}
call sandwich#messenger#new()
" vim:set foldmethod=marker:
" vim:set commentstring="%s:
" vim:set ts=2 sts=2 sw=2:

View File

@ -0,0 +1,346 @@
" opt object - managing options
function! sandwich#opt#new(kind, defaultopt, argopt) abort "{{{
let opt = deepcopy(s:opt)
if a:kind ==# 'auto' || a:kind ==# 'query'
let opt.recipe = {}
let opt.of = function('s:of_for_textobj')
let opt.filter = s:default_values['textobj']['filter']
call opt.update('arg', a:argopt)
call opt.update('default', a:defaultopt)
else
let opt.recipe_add = {}
let opt.recipe_delete = {}
let opt.of = function('s:of_for_' . a:kind)
let opt.filter = s:default_values[a:kind]['filter']
call opt.update('arg', a:argopt)
endif
return opt
endfunction
"}}}
function! sandwich#opt#defaults(kind, ...) abort "{{{
if a:kind ==# 'auto' || a:kind ==# 'query'
return deepcopy(s:default_values['textobj'][a:kind])
elseif a:kind ==# 'add' || a:kind ==# 'delete' || a:kind ==# 'replace'
let motionwise = get(a:000, 0, 'char')
return deepcopy(s:default_values[a:kind][motionwise])
else
return {}
endif
endfunction
"}}}
" opt "{{{
let s:opt = {
\ 'default' : {},
\ 'arg' : {},
\ 'filter' : '',
\ }
"}}}
function! s:opt.clear(target) dict abort "{{{
call filter(self[a:target], 0)
endfunction
"}}}
function! s:opt.update(target, dict) dict abort "{{{
call self.clear(a:target)
call extend(self[a:target], filter(deepcopy(a:dict), self.filter), 'force')
endfunction
"}}}
function! s:opt.has(opt_name) dict abort "{{{
return has_key(self.default, a:opt_name)
endfunction
"}}}
function! s:opt.max(opt_name, ...) dict abort "{{{
let minimum = get(a:000, 0, 0)
let list = []
if has_key(self['recipe_delete'], a:opt_name)
let list += [self['recipe_delete'][a:opt_name]]
endif
if has_key(self['recipe_add'], a:opt_name)
let list += [self['recipe_add'][a:opt_name]]
endif
if list == []
let list += [self._of(a:opt_name)]
endif
return max([minimum] + list)
endfunction
"}}}
function! s:opt.get(opt_name, kind, ...) dict abort "{{{
return self.has(a:opt_name) ? self.of(a:opt_name, a:kind) : get(a:000, 0, 0)
endfunction
"}}}
function! s:opt._of(opt_name, ...) dict abort "{{{
let kind = get(a:000, 0, '')
if kind !=# '' && has_key(self[kind], a:opt_name)
return self[kind][a:opt_name]
elseif has_key(self['arg'], a:opt_name)
return self['arg'][a:opt_name]
else
return self['default'][a:opt_name]
endif
endfunction
"}}}
function! s:of_for_add(opt_name, ...) dict abort "{{{
return self._of(a:opt_name, 'recipe_add')
endfunction
"}}}
function! s:of_for_delete(opt_name, ...) dict abort "{{{
return self._of(a:opt_name, 'recipe_delete')
endfunction
"}}}
function! s:of_for_replace(opt_name, ...) dict abort "{{{
let kind = get(a:000, 0, '')
if kind !=# ''
return self._of(a:opt_name, kind)
else
if a:opt_name ==# 'cursor'
" recipe_add > recipe_delete > arg > default
if has_key(self.recipe_add, a:opt_name)
return self.recipe_add[a:opt_name]
else
return self._of(a:opt_name, 'recipe_delete')
endif
elseif a:opt_name ==# 'query_once'
return self._of(a:opt_name, 'recipe_add')
elseif a:opt_name ==# 'regex'
return self._of(a:opt_name, 'recipe_delete')
elseif a:opt_name ==# 'expr'
return self._of(a:opt_name, 'recipe_add')
elseif a:opt_name ==# 'listexpr'
return self._of(a:opt_name, 'recipe_add')
elseif a:opt_name ==# 'noremap'
return self._of(a:opt_name, '')
elseif a:opt_name ==# 'skip_space'
return self._of(a:opt_name, 'recipe_delete')
elseif a:opt_name ==# 'skip_char'
return self._of(a:opt_name, 'recipe_delete')
elseif a:opt_name ==# 'highlight'
return self._of(a:opt_name, '')
elseif a:opt_name ==# 'hi_duration'
return self._of(a:opt_name, '')
elseif a:opt_name ==# 'command'
let commands = []
let commands += get(self.recipe_delete, a:opt_name, [])
let commands += get(self.recipe_add, a:opt_name, [])
if commands == []
let commands += self._of(a:opt_name)
endif
return commands
elseif a:opt_name ==# 'linewise'
return self.max(a:opt_name)
elseif a:opt_name ==# 'autoindent'
return self._of(a:opt_name, 'recipe_add')
elseif a:opt_name ==# 'indentkeys'
return self._of(a:opt_name, 'recipe_add')
elseif a:opt_name ==# 'indentkeys+'
return self._of(a:opt_name, 'recipe_add')
elseif a:opt_name ==# 'indentkeys-'
return self._of(a:opt_name, 'recipe_add')
else
" should not reach here!
throw 'OperatorSandwichError:Replace:InvalidOption:' . a:opt_name
endif
endif
endfunction
"}}}
function! s:of_for_textobj(opt_name) dict abort "{{{
if has_key(self['recipe'], a:opt_name)
return self['recipe'][a:opt_name]
elseif has_key(self['arg'], a:opt_name)
return self['arg'][a:opt_name]
else
return self['default'][a:opt_name]
endif
endfunction
"}}}
" default values "{{{
let s:default_values = {}
let s:default_values.add = {}
let s:default_values.add.char = {
\ 'cursor' : 'default',
\ 'query_once' : 0,
\ 'expr' : 0,
\ 'listexpr' : 0,
\ 'noremap' : 1,
\ 'skip_space' : 0,
\ 'highlight' : 3,
\ 'hi_duration': 200,
\ 'command' : [],
\ 'linewise' : 0,
\ 'autoindent' : -1,
\ 'indentkeys' : 0,
\ 'indentkeys+': 0,
\ 'indentkeys-': 0,
\ }
let s:default_values.add.line = {
\ 'cursor' : 'default',
\ 'query_once' : 0,
\ 'expr' : 0,
\ 'listexpr' : 0,
\ 'noremap' : 1,
\ 'skip_space' : 1,
\ 'highlight' : 3,
\ 'hi_duration': 200,
\ 'command' : [],
\ 'linewise' : 1,
\ 'autoindent' : -1,
\ 'indentkeys' : 0,
\ 'indentkeys+': 0,
\ 'indentkeys-': 0,
\ }
let s:default_values.add.block = {
\ 'cursor' : 'default',
\ 'query_once' : 0,
\ 'expr' : 0,
\ 'listexpr' : 0,
\ 'noremap' : 1,
\ 'skip_space' : 1,
\ 'highlight' : 3,
\ 'hi_duration': 200,
\ 'command' : [],
\ 'linewise' : 0,
\ 'autoindent' : -1,
\ 'indentkeys' : 0,
\ 'indentkeys+': 0,
\ 'indentkeys-': 0,
\ }
let s:default_values.add.filter = printf('v:key =~# ''\%%(%s\)''', join(keys(s:default_values['add']['char']), '\|'))
let s:default_values.delete = {}
let s:default_values.delete.char = {
\ 'cursor' : 'default',
\ 'noremap' : 1,
\ 'regex' : 0,
\ 'skip_space' : 1,
\ 'skip_char' : 0,
\ 'highlight' : 3,
\ 'hi_duration': 200,
\ 'command' : [],
\ 'linewise' : 0,
\ }
let s:default_values.delete.line = {
\ 'cursor' : 'default',
\ 'noremap' : 1,
\ 'regex' : 0,
\ 'skip_space' : 2,
\ 'skip_char' : 0,
\ 'highlight' : 3,
\ 'hi_duration': 200,
\ 'command' : [],
\ 'linewise' : 1,
\ }
let s:default_values.delete.block = {
\ 'cursor' : 'default',
\ 'noremap' : 1,
\ 'regex' : 0,
\ 'skip_space' : 1,
\ 'skip_char' : 0,
\ 'highlight' : 3,
\ 'hi_duration': 200,
\ 'command' : [],
\ 'linewise' : 0,
\ }
let s:default_values.delete.filter = printf('v:key =~# ''\%%(%s\)''', join(keys(s:default_values['delete']['char']), '\|'))
let s:default_values.replace = {}
let s:default_values.replace.char = {
\ 'cursor' : 'default',
\ 'query_once' : 0,
\ 'regex' : 0,
\ 'expr' : 0,
\ 'listexpr' : 0,
\ 'noremap' : 1,
\ 'skip_space' : 1,
\ 'skip_char' : 0,
\ 'highlight' : 3,
\ 'hi_duration': 200,
\ 'command' : [],
\ 'linewise' : 0,
\ 'autoindent' : -1,
\ 'indentkeys' : 0,
\ 'indentkeys+': 0,
\ 'indentkeys-': 0,
\ }
let s:default_values.replace.line = {
\ 'cursor' : 'default',
\ 'query_once' : 0,
\ 'regex' : 0,
\ 'expr' : 0,
\ 'listexpr' : 0,
\ 'noremap' : 1,
\ 'skip_space' : 2,
\ 'skip_char' : 0,
\ 'highlight' : 3,
\ 'hi_duration': 200,
\ 'command' : [],
\ 'linewise' : 0,
\ 'autoindent' : -1,
\ 'indentkeys' : 0,
\ 'indentkeys+': 0,
\ 'indentkeys-': 0,
\ }
let s:default_values.replace.block = {
\ 'cursor' : 'default',
\ 'query_once' : 0,
\ 'regex' : 0,
\ 'expr' : 0,
\ 'listexpr' : 0,
\ 'noremap' : 1,
\ 'skip_space' : 1,
\ 'skip_char' : 0,
\ 'highlight' : 3,
\ 'hi_duration': 200,
\ 'command' : [],
\ 'linewise' : 0,
\ 'autoindent' : -1,
\ 'indentkeys' : 0,
\ 'indentkeys+': 0,
\ 'indentkeys-': 0,
\ }
let s:default_values.replace.filter = printf('v:key =~# ''\%%(%s\)''', join(keys(s:default_values['replace']['char']), '\|'))
let s:default_values.textobj = {}
let s:default_values.textobj.auto = {
\ 'expr' : 0,
\ 'listexpr' : 0,
\ 'regex' : 0,
\ 'skip_regex' : [],
\ 'skip_regex_head': [],
\ 'skip_regex_tail': [],
\ 'quoteescape' : 0,
\ 'expand_range' : -1,
\ 'nesting' : 0,
\ 'synchro' : 1,
\ 'noremap' : 1,
\ 'syntax' : [],
\ 'inner_syntax' : [],
\ 'match_syntax' : 0,
\ 'skip_break' : 0,
\ 'skip_expr' : [],
\ }
let s:default_values.textobj.query = {
\ 'expr' : 0,
\ 'listexpr' : 0,
\ 'regex' : 0,
\ 'skip_regex' : [],
\ 'skip_regex_head': [],
\ 'skip_regex_tail': [],
\ 'quoteescape' : 0,
\ 'expand_range' : -1,
\ 'nesting' : 0,
\ 'synchro' : 1,
\ 'noremap' : 1,
\ 'syntax' : [],
\ 'inner_syntax' : [],
\ 'match_syntax' : 0,
\ 'skip_break' : 0,
\ 'skip_expr' : [],
\ }
let s:default_values.textobj.filter = printf('v:key =~# ''\%%(%s\)''', join(keys(s:default_values.textobj.auto), '\|'))
"}}}
" vim:set foldmethod=marker:
" vim:set commentstring="%s:
" vim:set ts=2 sts=2 sw=2:

View File

@ -0,0 +1,41 @@
function! sandwich#util#echo(messages) abort "{{{
echo ''
for [mes, hi_group] in a:messages
execute 'echohl ' . hi_group
echon mes
echohl NONE
endfor
endfunction
"}}}
function! sandwich#util#addlocal(recipes) abort "{{{
return s:extend_local(a:recipes)
endfunction
"}}}
function! sandwich#util#insertlocal(recipes) abort "{{{
return s:extend_local(a:recipes, 0)
endfunction
"}}}
function! sandwich#util#ftrevert(filetype) abort "{{{
if exists('b:sandwich_recipes')
call filter(b:sandwich_recipes, 'get(v:val, "__filetype__", "") !=# a:filetype')
endif
endfunction
"}}}
function! s:extend_local(recipes, ...) abort "{{{
if !exists('b:sandwich_recipes')
if exists('g:sandwich#recipes')
let b:sandwich_recipes = deepcopy(g:sandwich#recipes)
else
let b:sandwich_recipes = deepcopy(g:sandwich#default_recipes)
endif
endif
let i = get(a:000, 0, len(b:sandwich_recipes))
call extend(b:sandwich_recipes, copy(a:recipes), i)
return b:sandwich_recipes
endfunction "}}}
" vim:set foldmethod=marker:
" vim:set commentstring="%s:
" vim:set ts=2 sts=2 sw=2: