" vim-metarepeat provides an operator (like) mapping which runs dot repeat for | |
" every occurence matched by last pattern (@/) in range (specified by motion or | |
" textobject). | |
" | |
" vim-metarepeat is inspired by 'Occurrence modifier, preset-occurrence, | |
" persistence-selection' feature | |
" http://qiita.com/t9md/items/0bc7eaff726d099943eb#occurrence-modifier-preset-occurrence-persistence-selection | |
" | |
" But it's not a port of these feature. In similar to vim-mode-plus terms, | |
" vim-metarepeat provides 'preset-operation-for-occurence' feature (it's just | |
" a operator + 'gn') and provides a way to apply the operation for each | |
" occurences in textobject. | |
" | |
" TODO: | |
" - highlight changed text to make changed texts clear for users? | |
nnoremap <expr> <Plug>(metarepeat) <SID>metarepeat() | |
xnoremap <Plug>(metarepeat) <Esc>:<C-u>call <SID>setview() <bar> call <SID>selected()<CR> | |
map g. <Plug>(metarepeat) | |
let s:saveview = {} | |
function! s:setview() abort | |
let s:saveview = winsaveview() | |
endfunction | |
" s:metarepeat() is expected to be called with <expr> mapping. | |
" It registers CursorMoved event and returns 'v'. | |
" By using 'v' (starting visual mode) and exiting visual mode with first | |
" CursorMoved, it emulates 'operator' behavior without breaking dot repeat. | |
" (g@ breaks dot repeat) | |
function! s:metarepeat() abort | |
augroup metarepeat-move-once | |
autocmd! | |
autocmd CursorMoved * call s:hook_after_move() | |
augroup END | |
call s:setview() | |
return 'v' | |
endfunction | |
function! s:hook_after_move() abort | |
autocmd! metarepeat-move-once | |
execute 'normal!' "\<Esc>" | |
call s:selected() | |
endfunction | |
" s:selected is expected to be called in normal mode and target has been | |
" selected. | |
function! s:selected() abort | |
call s:metaoperate(getpos("'<"), getpos("'>"), @/) | |
endfunction | |
" s:metaoperate() execute dot repeat for every pattern occurence between start | |
" and end position. | |
" start: [bufnum, lnum, col, off] | |
" end: [bufnum, lnum, col, off] | |
" pattern: [bufnum, lnum, col, off] | |
function! s:metaoperate(start, end, pattern) abort | |
call setpos('.', a:start) | |
let first = v:true | |
let endline = a:end[1] | |
let endcol = a:end[2] | |
let stopline = endline + 1 | |
while v:true | |
let flag = first ? 'c' : '' | |
let [lnum, col] = searchpos(a:pattern, flag, stopline) | |
if (lnum ==# 0 && col ==# 0) || lnum > endline || (lnum ==# endline && col > endcol) | |
break | |
endif | |
normal! . | |
let first = v:false | |
endwhile | |
if s:saveview !=# {} | |
call winrestview(s:saveview) | |
let s:saveview = {} | |
endif | |
endfunction | |
" --- | |
" support multiple preset-occurrence like feature by providing append preset | |
" occurence mapping. It only supports 'appending' because we cannot reset | |
" preset occurence after operation. | |
nnoremap <silent> <Plug>(append-preset-occurence) :<C-u>call <SID>append_preset_occurence() <bar> set hlsearch<CR> | |
nmap go <Plug>(append-preset-occurence) | |
function! s:append_preset_occurence() abort | |
let @/ = @/ . '\V\|\<' . escape(expand('<cword>'), '\') . '\>' | |
endfunction |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment