Improve matchparen and skip, start fixing transmute

This commit is contained in:
Andy K. Massimino
2017-11-05 17:22:46 -05:00
parent b553b05f51
commit 68c1b74805
9 changed files with 223 additions and 90 deletions

View File

@@ -1,4 +1,3 @@
" vim match-up - matchit replacement and more
"
" Maintainer: Andy Massimino

View File

@@ -71,8 +71,7 @@ function! s:init_default_mappings()
function! s:map(mode, lhs, rhs, ...)
if !hasmapto(a:rhs, a:mode)
\ && ((a:0 > 0) || (maparg(a:lhs, a:mode) ==# ''))
silent execute a:mode . 'map ' a:lhs a:rhs
" <silent> XXX
silent execute a:mode . 'map <silent> ' a:lhs a:rhs
endif
endfunction
@@ -126,7 +125,7 @@ function! s:init_default_mappings()
endif
if get(g:, 'matchup_imap_enabled', 0)
call s:map('i', '<c-x><cr>', '<plug>(matchup-delim-close)')
" call s:map('i', '<c-x><cr>', '<plug>(matchup-delim-close)')
" XXX other maps..?
endif
endfunction

View File

@@ -8,13 +8,10 @@ let s:save_cpo = &cpo
set cpo&vim
function! matchup#delim#init_module() " {{{1
" nnoremap <silent><buffer> <plug>(matchup-delim-delete)
" nnoremap <silent> <plug>(matchup-delim-delete)
" \ :call matchup#delim#delete()<cr>
" <silent> XXX
inoremap <plug>(matchup-delim-close)
\ <c-r>=matchup#delim#close()<cr>
" inoremap <silent> <plug>(matchup-delim-close)
" \ <c-r>=matchup#delim#close()<cr>
augroup matchup_filetype
au!
@@ -22,7 +19,6 @@ function! matchup#delim#init_module() " {{{1
augroup END
call matchup#delim#init_buffer()
endfunction
" }}}1
@@ -40,11 +36,6 @@ function! matchup#delim#init_buffer() " {{{1
" enable/disable for this buffer
let b:matchup_delim_enabled = 1
" surround? XXX
" let b:surround_37 = b:matchup_delim_re.all.open
" \ . '\r' . b:matchup_delim_re.all.close
endfunction
" }}}1
@@ -116,7 +107,7 @@ function! matchup#delim#get_matching(delim, ...) " {{{1
" get all the matching position(s)
" *important*: in the case of mid, we search up before searching down
" this gives us a context object which we use for the other side
" XXX: what if no open is found here?
" TODO: what if no open is found here?
let l:matches = []
for l:down in {'open': [1], 'close': [0], 'mid': [0,1]}[a:delim.side]
let l:save_pos = matchup#pos#get_cursor()
@@ -196,8 +187,7 @@ function! matchup#delim#get_matching(delim, ...) " {{{1
if a:0
return l:matching_list
else
" return a:delim.links.next
" XXX old syntax: open-close, close-open
" old syntax: open->close, close->open
return a:delim.side ==# 'open' ? l:matching_list[-1]
\ : l:matching_list[0]
endif
@@ -324,20 +314,25 @@ function! s:get_delim(opts) " {{{1
" let c = c_before
let l:cursorpos = col('.')
if l:cursorpos > 1 && (mode() ==# 'i' || mode() ==# 'R')
" if l:cursorpos > 1 && (mode() ==# 'i' || mode() ==# 'R')
let l:insertmode = get(a:opts, 'insertmode', 0)
if l:cursorpos > 1 && l:insertmode
let l:cursorpos -= 1
endif
" echo l:cursorpos
" echo l:cursorpos mode() v:insertmode expand('<amatch>')
let a:opts.cursorpos = l:cursorpos
" TODO XXX does this even make any sense?
"
" for current, we want to find matches that end after the cursor
if a:opts.direction ==# 'current'
let l:re .= '\%>'.(l:cursorpos).'c'
" let l:re = '\%<'.(l:cursorpos+1).'c' . l:re
endif
" let l:re .= '\%>'.(col('.')).'c'
" let g:re = l:re
@@ -348,6 +343,11 @@ function! s:get_delim(opts) " {{{1
" use b:match_ignorecase
call s:ignorecase_start()
" move cursor one left for searchpos if necessary
if l:insertmode
call matchup#pos#set_cursor(line('.'), col('.')-1)
endif
" in the first pass, we get matching line and column numbers
" this is intended to be as fast as possible, with no capture groups
" we look for a match on this line (if direction == current)
@@ -370,7 +370,7 @@ function! s:get_delim(opts) " {{{1
" \ || matchup#util#in_string(l:lnum, l:cnum)
" XXX get rid of this..
call matchup#pos#set_cursor([l:lnum, l:cnum])
"call matchup#pos#set_cursor([l:lnum, l:cnum])
" note: this function should never be called
" in 'current' mode, but be explicit
@@ -473,7 +473,6 @@ endfunction
"}}}1
function! s:parser_delim_new(lnum, cnum, opts) " {{{1
let l:time_start = reltime()
let l:cursorpos = a:opts.cursorpos
" XXX TODO stuff this in opts instead
@@ -664,9 +663,9 @@ function! s:get_matching_delims(down) dict " {{{1
" echo s:foo | sleep 1
" let s:foo+= 1
" improves perceptual performance
" improves perceptual performance in insert mode
" XXX use s: mode flag
if mode() ==# 'i'
if mode() ==# 'i' || mode() ==# 'R'
sleep 1m
endif
@@ -1242,10 +1241,6 @@ function! s:init_delim_regexes() " {{{1
let l:re.all[l:k] = l:re.delim_all[l:k]
endfor
" for l:type in values(l:re)
" for l:side in keys(l:type)
" endfor
" be explicit about regex mode (set magic mode)
for l:type in values(l:re)
for l:side in keys(l:type)
@@ -1391,26 +1386,23 @@ function! matchup#delim#skip(...) " {{{1
\ || matchup#util#in_string(l:lnum, l:cnum)
endif
" call s:set_effective_curpos(l:lnum, l:cnum)
" call matchup#pos#set_cursor([l:lnum, l:cnum])
let s:eff_curpos = [l:lnum, l:cnum]
execute 'return (' b:matchup_delim_skip ')'
endfunction
function! s:set_effective_curpos(lnum, cnum)
endfunction
let s:eff_curpos = [1, 1]
" effective column/line
function! s:effcol(expr)
return col(a:expr)
function! s:effline(expr)
return a:expr ==# '.' ? s:eff_curpos[0] : line(a:expr)
endfunction
function! s:effline(expr)
return line(a:expr)
function! s:effcol(expr)
return a:expr ==# '.' ? s:eff_curpos[1] : col(a:expr)
endfunction
function! s:geteffline(expr)
return getline(a:expr)
return a:expr ==# '.' ? getline(s:effline(a:expr)) : getline(a:expr)
endfunction
" }}}1
@@ -1436,12 +1428,6 @@ endfunction
" initialize script variables
let s:stopline = get(g:, 'matchup_delim_stopline', 400)
" whether we're behaving like in insert mode; changes
" 1) effective cursor position for highlight
" 2) which is timeout used
" XXX should really be in matchparen
let s:insertmode = 0
let s:sidedict = {
\ 'open' : ['open'],
\ 'mid' : ['mid'],

View File

@@ -10,7 +10,6 @@ set cpo&vim
function! matchup#matchparen#init_module() " {{{1
if !g:matchup_matchparen_enabled | return | endif
"XXX make buffer?
call matchup#matchparen#enable()
endfunction
@@ -20,11 +19,12 @@ function! matchup#matchparen#enable() " {{{1
augroup matchup_matchparen
autocmd!
autocmd CursorMoved,CursorMovedI * call s:matchparen.highlight()
" autocmd WinEnter * call s:matchparen.highlight()
autocmd WinEnter * call s:matchparen.highlight(1)
" autocmd TextChanged,TextChangedI * call s:matchparen.highlight()
autocmd WinLeave * call s:matchparen.clear()
" autocmd BufLeave * call s:matchparen.clear()
" autocmd InsertEnter,InsertLeave * call s:matchparen.highlight()
autocmd InsertEnter * call s:matchparen.highlight(1, 1)
augroup END
let s:pi_paren_sid = 0
@@ -80,7 +80,7 @@ function! s:matchparen.clear() abort dict " {{{1
endfunction
" }}}1
function! s:matchparen.highlight() abort dict " {{{1
function! s:matchparen.highlight(...) abort dict " {{{1
if !g:matchup_matchparen_enabled | return | endif
if !get(b:, 'matchup_matchparen_enabled', 1)
@@ -92,22 +92,39 @@ function! s:matchparen.highlight() abort dict " {{{1
if pumvisible() | return | endif
let l:force_update = a:0 >= 1 ? a:1 : 0
let l:entering_insert = a:0 >= 2 ? a:2 : 0
if !l:force_update
\ && exists('w:last_changedtick') && exists('w:last_cursor')
\ && matchup#pos#equal(w:last_cursor, matchup#pos#get_cursor())
\ && w:last_changedtick == b:changedtick
return
endif
let w:last_changedtick = b:changedtick
let w:last_cursor = matchup#pos#get_cursor()
call matchup#perf#tic('matchparen.highlight')
call self.clear()
" if matchup#util#in_comment() || matchup#util#in_string()
if matchup#delim#skip()
" in insert mode, cursor is treated as being one behind
let l:insertmode = l:entering_insert
\ || (mode() ==# 'i' || mode() ==# 'R')
" skip if inside string or comment (by default)
if matchup#delim#skip(line('.'), col('.') - l:insertmode)
return
endif
" start the timeout period XXX use effective mode
let l:timeout = (mode() ==# 'i')
" start the timeout period
let l:timeout = l:insertmode
\ ? g:matchup_matchparen_insert_timeout
\ : g:matchup_matchparen_timeout
call matchup#perf#timeout_start(l:timeout)
let l:current = matchup#delim#get_current('all', 'both_all')
let l:current = matchup#delim#get_current('all', 'both_all',
\ { 'insertmode': l:insertmode })
call matchup#perf#toc('matchparen.highlight', 'get_current')
if empty(l:current) | return | endif
@@ -125,18 +142,52 @@ function! s:matchparen.highlight() abort dict " {{{1
" echo 'vv' map(copy(l:corrlist), 'v:val.match')
" \ map(copy(l:corrlist), 'v:val.lnum')
let w:matchparen_current = l:current
if !exists('w:matchup_matchparen_context')
let w:matchup_matchparen_context = {
\ 'insert': {
\ 'current': {},
\ 'corrlist': [],
\ },
\ 'normal': {
\ 'current': {},
\ 'corrlist': [],
\ },
\ 'prior': {},
\ 'counter': 0,
\}
endif
let w:matchup_matchparen_context.counter += 1
let w:matchup_matchparen_context.prior
\ = deepcopy(w:matchup_matchparen_context.normal)
let l:mode_name = l:insertmode && !l:entering_insert
\ ? 'insert' : 'normal'
let w:matchup_matchparen_context[l:mode_name].current = l:current
let w:matchup_matchparen_context[l:mode_name].corrlist = l:corrlist
" echo w:matchup_matchparen_context.normal.current.match
" \ w:matchup_matchparen_context.prior.current.match
" \ b:changedtick
" echo w:matchup_matchparen_context.counter
" if transmuted, highlight again (will reset timeout)
if matchup#transmute#tick(l:insertmode, l:entering_insert)
return s:matchparen.highlight(l:force_update, l:entering_insert)
endif
if len(l:corrlist) <= 1 && !g:matchup_matchparen_singleton
return
endif
let w:matchparen_corrlist = l:corrlist
" if l:need_update
" endif
" echo l:corrlist
" echo map(deepcopy(l:corrlist), 'v:val.lnum')
" for l:c in l:corrlist
" echom l:c.match
" endfor
@@ -266,11 +317,12 @@ function! matchup#matchparen#offscreen(current) " {{{1
let l:line = getline(l:offscreen.lnum)
let l:lasthi = ''
for l:idx in range(min([winwidth(0), strchars(l:line)]))
let l:c = strlen(strcharpart(l:line, 0, l:idx))
"for l:idx in range(min([winwidth(0), strchars(l:line)]))
" let l:c = strlen(strcharpart(l:line, 0, l:idx))
" echo l:idx l:c | sleep 1
endfor
"endfor
" TODO use character indexing
for l:c in range(min([winwidth(0), strlen(l:line)]))
if l:offscreen.cnum <= l:c+1 && l:c+1 <= l:offscreen.cnum
\ - 1 + strlen(l:offscreen.match)
@@ -301,6 +353,15 @@ function! matchup#matchparen#offscreen(current) " {{{1
endfunction
" }}}1
function! s:format_statusline(offscreen) " {{{1
endfunction
function! s:char_virtpos(lnum, cnum)
return matchstr(getline(a:lnum), '\%'.a:cnum.'v.')
endfunction
" }}}1
let &cpo = s:save_cpo

View File

@@ -15,7 +15,7 @@ function! matchup#text_obj#init_module() " {{{1
\]
let l:p1 = 'noremap <silent> <plug>(matchup-'
let l:p2 = l:map . ') :<c-u>call matchup#text_obj#' . l:name
let l:p3 = empty(l:opt) ? ')<cr>' : ',''' . l:opt . ''')<cr>'
let l:p3 = empty(l:opt) ? ')<cr>' : ', ''' . l:opt . ''')<cr>'
execute 'x' . l:p1 . 'i' . l:p2 . '(1, 1' . l:p3
execute 'x' . l:p1 . 'a' . l:p2 . '(0, 1' . l:p3
execute 'o' . l:p1 . 'i' . l:p2 . '(1, 0' . l:p3

View File

@@ -4,6 +4,9 @@
" Email: a@normed.space
"
let s:save_cpo = &cpo
set cpo&vim
function! matchup#transmute#init_module() " {{{1
if !g:matchup_transmute_enabled | return | endif
@@ -15,9 +18,9 @@ endfunction
function! matchup#transmute#enable() " {{{1
augroup matchup_transmute
autocmd!
autocmd InsertEnter * call s:transmute.setup()
autocmd InsertLeave * call s:transmute.commit()
autocmd TextChanged * call s:transmute.textchanged()
" autocmd InsertEnter * call s:transmute.setup()
" autocmd InsertLeave * call s:transmute.commit()
autocmd TextChanged,TextChangedI * call s:transmute.textchanged()
augroup END
endfunction
@@ -30,8 +33,29 @@ endfunction
let s:transmute = {}
function! matchup#transmute#tick(insertmode, entering_insert)
if changenr() > get(w:, 'matchup_transmute_last_changenr', 0)
\ && !empty('w:matchup_matchparen_context.prior')
let w:matchup_transmute_last_changenr = changenr()
return s:transmute.dochange(
\ w:matchup_matchparen_context.prior.corrlist,
\ w:matchup_matchparen_context.prior.current,
\ w:matchup_matchparen_context.normal.current)
endif
" \ changenr()
return 0
endfunction
" let s:cancel_next = 0
function! s:transmute.textchanged() abort dict " {{{1
if !g:matchup_transmute_enabled | return | endif
" if exists('w:matchparen_current')
" echo w:matchparen_current.links.close.match
" endif
@@ -40,8 +64,18 @@ function! s:transmute.textchanged() abort dict " {{{1
" let s:cancel_next = 0
" endif
call s:transmute.setup()
call s:transmute.commit()
" call s:transmute.setup()
" call s:transmute.commit()
if !exists('w:matchup_matchparen_context')
return
endif
" echo w:matchup_matchparen_context.normal.singleton.match
" \ w:matchup_matchparen_context.insert.singleton.match
" echo w:matchup_matchparen_context.counter
" \ w:matchup_matchparen_context.normal.current.match
" \ w:matchup_matchparen_context.prior.current.match
endfunction
@@ -72,34 +106,38 @@ function! s:transmute.setup() abort dict " {{{1
endfunction
" }}}1
function! s:transmute.commit() abort dict " {{{1
"echo 'commit' reltime() | sleep 1
if !g:matchup_transmute_enabled | return | endif
if !exists('w:transmute_state') | return | endif
endfunction
" TODO ensure cursor position
let l:current = matchup#delim#get_current('all', 'both_all')
" }}} 1
let l:corrlist = w:transmute_state.corrlist
let l:prior = w:transmute_state.prior
function! s:transmute.dochange(list, pri, cur) abort dict " {{{1
" if !g:matchup_transmute_enabled | return | endif
" echo 'doing change' a:pri.class a:cur.class
" echo l:current.match l:prior.match
if empty(a:list) || empty(a:pri) || empty(a:cur) | return 0 | endif
if empty(l:current) | return | endif
"let l:threshold = l:prior.cnum +
" so far only same-class changes are supported
if a:pri.class[0] != a:cur.class[0]
return 0
endif
let l:delta = strdisplaywidth(l:current.match)
\ - strdisplaywidth(l:prior.match)
let l:num_changes = 0
for l:i in range(len(l:corrlist))
if l:i == l:prior.match_index | continue | endif
let l:delta = strdisplaywidth(a:cur.match)
\ - strdisplaywidth(a:pri.match)
let l:corr = l:corrlist[l:i]
for l:i in range(len(a:list))
if l:i == a:pri.match_index | continue | endif
let l:corr = a:list[l:i]
let l:line = getline(l:corr.lnum)
let l:column = l:corr.cnum
if l:corr.lnum == l:current.lnum && l:i > l:prior.match_index
if l:corr.lnum == a:cur.lnum && l:i > a:pri.match_index
let l:column += l:delta
endif
@@ -108,38 +146,43 @@ function! s:transmute.commit() abort dict " {{{1
let l:groups = copy(l:corr.groups)
for l:grp in keys(l:groups)
let l:count = len(split(l:re_anchored, s:notslash.'\\'.l:grp))-1
let l:count = len(split(l:re_anchored,
\ g:matchup#re#not_bslash.'\\'.l:grp))-1
if l:count == 0 | continue | endif
if l:current.groups[l:grp] ==# l:groups[l:grp]
if a:cur.groups[l:grp] ==# l:groups[l:grp]
continue
endif
for l:dummy in range(len(l:count))
let l:pattern = substitute(l:re_anchored,
\ s:notslash.'\\'.l:grp,
\ g:matchup#re#not_bslash.'\\'.l:grp,
\ '\=''\zs'.(l:groups[l:grp]).'\ze''', '')
let l:pattern = matchup#delim#fill_backrefs(l:pattern,
\ l:groups)
let l:string = l:current.groups[l:grp]
let l:string = a:cur.groups[l:grp]
let l:line = substitute(l:line, l:pattern,
\ '\='''.l:string."'", '')
" echo l:current.cnum l:pattern
endfor
let l:groups[l:grp] = l:current.groups[l:grp]
let l:groups[l:grp] = a:cur.groups[l:grp]
endfor
if getline(l:corr.lnum) !=# l:line
" TODO break undo option
" exe "normal! a\<c-g>u"
call setline(l:corr.lnum, l:line)
let l:num_changes += 1
endif
endfor
endfunction
return l:num_changes
let s:notslash = '\\\@<!\%(\\\\\)*'
endfunction
" }}}1
let &cpo = s:save_cpo
" vim: fdm=marker sw=2

View File

@@ -1,3 +1,10 @@
(
dib
dvib
dVib
)
----
{
{

19
test/tabs.vim Normal file
View File

@@ -0,0 +1,19 @@
for l:pattern in l:completer.patterns
if l:line =~# l:pattern
let s:completer = l:completer
while l:pos > 0
if l:line[l:pos - 1] =~# '{\|,\|\[\|\\'
\ || l:line[l:pos-2:l:pos-1] ==# ', '
let s:completer.context = matchstr(l:line, '\S*$')
return l:pos
else
let l:pos -= 1
endif
endwhile
return -2
endif
endfor

19
test/transmute2.html Normal file
View File

@@ -0,0 +1,19 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Title of the document</title>
</head>
<boody>
<budy>
<body>
Content of the document......
</body>
</budy>
</boody>
</html>