1
0
mirror of https://github.com/SpaceVim/SpaceVim.git synced 2025-02-23 19:01:33 +08:00
SpaceVim/autoload/SpaceVim/plugins/iedit.vim
2018-02-15 22:25:03 +08:00

410 lines
12 KiB
VimL

"=============================================================================
" iedit.vim --- iedit mode for SpaceVim
" Copyright (c) 2016-2017 Shidong Wang & Contributors
" Author: Shidong Wang < wsdjeg at 163.com >
" URL: https://spacevim.org
" License: GPLv3
"=============================================================================
let s:stack = []
let s:index = -1
let s:cursor_col = -1
let s:mode = ''
let s:hi_id = ''
let s:Operator = ''
let s:VIMH = SpaceVim#api#import('vim#highlight')
let s:STRING = SpaceVim#api#import('data#string')
let s:cursor_stack = []
let s:iedit_hi_info = [
\ {
\ 'name' : 'IeditPurpleBold',
\ 'guibg' : '#3c3836',
\ 'guifg' : '#d3869b',
\ 'ctermbg' : '',
\ 'ctermfg' : 175,
\ 'bold' : 1,
\ },
\ {
\ 'name' : 'IeditBlueBold',
\ 'guibg' : '#3c3836',
\ 'guifg' : '#83a598',
\ 'ctermbg' : '',
\ 'ctermfg' : 109,
\ 'bold' : 1,
\ }
\ ]
function! s:highlight_cursor() abort
let info = {
\ 'name' : 'SpaceVimGuideCursor',
\ 'guibg' : synIDattr(synIDtrans(synID(line('.'), col('.'), 1)), 'guifg'),
\ 'guifg' : synIDattr(synIDtrans(synID(line('.'), col('.'), 1)), 'guibg'),
\ 'ctermbg' : synIDattr(synIDtrans(synID(line('.'), col('.'), 1)), 'ctermfg'),
\ 'ctermfg' : synIDattr(synIDtrans(synID(line('.'), col('.'), 1)), 'ctermbg'),
\ }
hi def link SpaceVimGuideCursor Cursor
call s:VIMH.hi(info)
for i in range(len(s:stack))
if i == s:index
call matchaddpos('IeditPurpleBold', [s:stack[i]])
else
call matchaddpos('IeditBlueBold', [s:stack[i]])
endif
call matchadd('SpaceVimGuideCursor', '\%' . s:stack[i][0] . 'l\%' . (s:stack[i][1] + len(s:cursor_stack[i].begin)) . 'c', 99999)
endfor
endfunction
function! s:remove_cursor_highlight() abort
call clearmatches()
endfunction
""
" public API for iedit mode
" >
" KEY:
" expr match expression
" word match word
" stack cursor pos stack
" <
" if only argv 1 is given, use selected word as pattern
function! SpaceVim#plugins#iedit#start(...)
let save_tve = &t_ve
let save_cl = &l:cursorline
setlocal nocursorline
setlocal t_ve=
call s:VIMH.hi(s:iedit_hi_info[0])
call s:VIMH.hi(s:iedit_hi_info[1])
let s:mode = 'n'
let w:spacevim_iedit_mode = s:mode
let w:spacevim_statusline_mode = 'in'
if empty(s:stack)
let curpos = getcurpos()
let argv = get(a:000, 0, '')
let save_reg_k = @k
let use_expr = 0
if !empty(argv) && type(argv) == 4
if has_key(argv, 'expr')
let use_expr = 1
let symbol = argv.expr
elseif has_key(argv, 'word')
let symbol = argv.word
elseif has_key(argv, 'stack')
endif
elseif type(argv) == 0 && argv == 1
normal! gv"ky
let symbol = split(@k, "\n")[0]
else
normal! viw"ky
let symbol = split(@k, "\n")[0]
endif
let @k = save_reg_k
call setpos('.', curpos)
let begin = get(a:000, 1, 1)
let end = get(a:000, 2, line('$'))
if use_expr
call s:parse_symbol(begin, end, symbol, 1)
else
call s:parse_symbol(begin, end, symbol)
endif
endif
call s:highlight_cursor()
redrawstatus!
while s:mode != ''
redraw!
let char = getchar()
if s:mode ==# 'n' && char == 27
let s:mode = ''
else
call s:handle(s:mode, char)
endif
endwhile
let s:stack = []
let s:cursor_stack = []
let s:index = -1
let s:mode = ''
let w:spacevim_iedit_mode = s:mode
let w:spacevim_statusline_mode = 'in'
let &t_ve = save_tve
call s:remove_cursor_highlight()
try
call matchdelete(s:hi_id)
catch
endtry
let s:hi_id = ''
let &l:cursorline = save_cl
endfunction
function! s:handle(mode, char) abort
if a:mode ==# 'n'
call s:handle_normal(a:char)
elseif a:mode ==# 'i'
call s:handle_insert(a:char)
endif
endfunction
let s:toggle_stack = {}
function! s:handle_normal(char) abort
silent! call s:remove_cursor_highlight()
if a:char ==# 105 " i
let s:mode = 'i'
let w:spacevim_iedit_mode = s:mode
let w:spacevim_statusline_mode = 'ii'
redrawstatus!
elseif a:char == 9 " <tab>
if index(keys(s:toggle_stack), s:index . '') == -1
call extend(s:toggle_stack, {s:index : [s:stack[s:index], s:cursor_stack[s:index]]})
call remove(s:stack, s:index)
call remove(s:cursor_stack, s:index)
else
call insert(s:stack, s:toggle_stack[s:index][0] , s:index)
call insert(s:cursor_stack, s:toggle_stack[s:index][1] , s:index)
call remove(s:toggle_stack, s:index)
endif
elseif a:char == 97 " a
let s:mode = 'i'
let w:spacevim_iedit_mode = s:mode
let w:spacevim_statusline_mode = 'ii'
for i in range(len(s:cursor_stack))
let s:cursor_stack[i].begin = s:cursor_stack[i].begin . s:cursor_stack[i].cursor
let s:cursor_stack[i].cursor = matchstr(s:cursor_stack[i].end, '^.')
let s:cursor_stack[i].end = substitute(s:cursor_stack[i].end, '^.', '', 'g')
endfor
redrawstatus!
elseif a:char == "\<Left>"
for i in range(len(s:cursor_stack))
if !empty(s:cursor_stack[i].begin)
let s:cursor_stack[i].end = s:cursor_stack[i].cursor . s:cursor_stack[i].end
let s:cursor_stack[i].cursor = matchstr(s:cursor_stack[i].begin, '.$')
let s:cursor_stack[i].begin = substitute(s:cursor_stack[i].begin, '.$', '', 'g')
endif
endfor
elseif a:char == "\<Right>"
for i in range(len(s:cursor_stack))
let s:cursor_stack[i].begin = s:cursor_stack[i].begin . s:cursor_stack[i].cursor
let s:cursor_stack[i].cursor = matchstr(s:cursor_stack[i].end, '^.')
let s:cursor_stack[i].end = substitute(s:cursor_stack[i].end, '^.', '', 'g')
endfor
elseif a:char == 48 " 0
for i in range(len(s:cursor_stack))
let s:cursor_stack[i].end = substitute(s:cursor_stack[i].begin . s:cursor_stack[i].cursor . s:cursor_stack[i].end , '^.', '', 'g')
let s:cursor_stack[i].cursor = matchstr(s:cursor_stack[i].begin, '^.')
let s:cursor_stack[i].begin = ''
endfor
elseif a:char == 36 " $
for i in range(len(s:cursor_stack))
let s:cursor_stack[i].begin = substitute(s:cursor_stack[i].begin . s:cursor_stack[i].cursor . s:cursor_stack[i].end , '.$', '', 'g')
let s:cursor_stack[i].cursor = matchstr(s:cursor_stack[i].end, '.$')
let s:cursor_stack[i].end = ''
endfor
elseif a:char == 68 " D
for i in range(len(s:cursor_stack))
let s:cursor_stack[i].begin = ''
let s:cursor_stack[i].cursor = ''
let s:cursor_stack[i].end = ''
endfor
call s:replace_symbol()
elseif a:char == 112 " p
for i in range(len(s:cursor_stack))
let s:cursor_stack[i].begin = @"
let s:cursor_stack[i].cursor = ''
let s:cursor_stack[i].end = ''
endfor
call s:replace_symbol()
elseif a:char == 83 " S
for i in range(len(s:cursor_stack))
let s:cursor_stack[i].begin = ''
let s:cursor_stack[i].cursor = ''
let s:cursor_stack[i].end = ''
endfor
let s:mode = 'i'
let w:spacevim_iedit_mode = s:mode
let w:spacevim_statusline_mode = 'ii'
redrawstatus!
call s:replace_symbol()
elseif a:char == 71 " G
exe s:stack[-1][0]
elseif a:char == 103 "g
if s:Operator ==# 'g'
exe s:stack[0][0]
let s:Operator = ''
else
let s:Operator = 'g'
call s:timeout()
endif
elseif a:char == 110 " n
if s:index == len(s:stack) - 1
let s:index = 0
else
let s:index += 1
endif
call cursor(s:stack[s:index][0], s:stack[s:index][1] + len(s:cursor_stack[s:index].begin))
elseif a:char == 78 " N
if s:index == 0
let s:index = len(s:stack) - 1
else
let s:index -= 1
endif
call cursor(s:stack[s:index][0], s:stack[s:index][1] + len(s:cursor_stack[s:index].begin))
endif
silent! call s:highlight_cursor()
endfunction
if exists('*timer_start')
function! s:timeout() abort
call timer_start(1000, function('s:reset_Operator'))
endfunction
else
function! s:timeout() abort
endfunction
endif
function! s:reset_Operator(...) abort
let s:Operator = ''
endfunction
function! s:handle_insert(char) abort
silent! call s:remove_cursor_highlight()
if a:char == 27
let s:mode = 'n'
let w:spacevim_iedit_mode = s:mode
let w:spacevim_statusline_mode = 'in'
silent! call s:highlight_cursor()
redraw!
redrawstatus!
return
elseif a:char == 23 " <c-w>
for i in range(len(s:cursor_stack))
let s:cursor_stack[i].begin = ''
endfor
elseif a:char == 11 " <c-k>
for i in range(len(s:cursor_stack))
let s:cursor_stack[i].cursor = ''
let s:cursor_stack[i].end = ''
endfor
elseif a:char == "\<bs>"
for i in range(len(s:cursor_stack))
let s:cursor_stack[i].begin = substitute(s:cursor_stack[i].begin, '.$', '', 'g')
endfor
elseif a:char == "\<Left>"
for i in range(len(s:cursor_stack))
if !empty(s:cursor_stack[i].begin)
let s:cursor_stack[i].end = s:cursor_stack[i].cursor . s:cursor_stack[i].end
let s:cursor_stack[i].cursor = matchstr(s:cursor_stack[i].begin, '.$')
let s:cursor_stack[i].begin = substitute(s:cursor_stack[i].begin, '.$', '', 'g')
endif
endfor
elseif a:char == "\<Right>"
for i in range(len(s:cursor_stack))
let s:cursor_stack[i].begin = s:cursor_stack[i].begin . s:cursor_stack[i].cursor
let s:cursor_stack[i].cursor = matchstr(s:cursor_stack[i].end, '^.')
let s:cursor_stack[i].end = substitute(s:cursor_stack[i].end, '^.', '', 'g')
endfor
else
for i in range(len(s:cursor_stack))
let s:cursor_stack[i].begin .= nr2char(a:char)
endfor
endif
call s:replace_symbol()
silent! call s:highlight_cursor()
endfunction
function! s:parse_symbol(begin, end, symbol, ...) abort
let use_expr = get(a:000, 0, 0)
let len = len(a:symbol)
let cursor = [line('.'), col('.')]
for l in range(a:begin, a:end)
let line = getline(l)
let idx = s:STRING.strAllIndex(line, a:symbol, use_expr)
for [pos_a, pos_b] in idx
call add(s:stack, [l, pos_a + 1, pos_b - pos_a])
if len(idx) > 1 && l == cursor[0] && pos_a + 1 <= cursor[1] && pos_a + 1 + len >= cursor[1]
let s:index = len(s:stack) - 1
endif
call add(s:cursor_stack,
\ {
\ 'begin' : line[pos_a : pos_b - 2],
\ 'cursor' : line[pos_b - 1 : pos_b - 1],
\ 'end' : '',
\ }
\ )
endfor
endfor
if s:index == -1 && !empty(s:stack)
let s:index = 0
call cursor(s:stack[0][0], s:stack[0][1])
endif
endfunction
" TODO current only support one line symbol
function! s:replace_symbol() abort
let line = 0
let pre = ''
let idxs = []
for i in range(len(s:stack))
if s:stack[i][0] != line
if !empty(idxs)
let end = getline(line)[s:stack[i-1][1] + s:stack[i-1][2] - 1: ]
let pre .= end
endif
call s:fixstack(idxs)
call setline(line, pre)
let idxs = []
let line = s:stack[i][0]
let begin = s:stack[i][1] == 1 ? '' : getline(line)[:s:stack[i][1] - 2]
let pre = begin . s:cursor_stack[i].begin . s:cursor_stack[i].cursor . s:cursor_stack[i].end
else
let line = s:stack[i][0]
if i == 0
let pre = (s:stack[i][1] == 1 ? '' : getline(line)[:s:stack[i][1] - 2]) . s:cursor_stack[i].begin . s:cursor_stack[i].cursor . s:cursor_stack[i].end
else
let a = s:stack[i-1][1] + s:stack[i-1][2] - 1
let b = s:stack[i][1] - 2
if a > b
let next = ''
else
let next = getline(line)[ a : b ]
endif
let pre .= next . s:cursor_stack[i].begin . s:cursor_stack[i].cursor . s:cursor_stack[i].end
endif
endif
call add(idxs, [i, len(s:cursor_stack[i].begin . s:cursor_stack[i].cursor . s:cursor_stack[i].end)])
endfor
if !empty(idxs)
let end = getline(line)[s:stack[i][1] + s:stack[i][2] - 1: ]
let pre .= end
endif
call s:fixstack(idxs)
call setline(line, pre)
endfunction
" [idx, newlen]
" same line
" [[1,6], [2,6], [3,6]]
function! s:fixstack(idxs) abort
" for [idx, len] in idxs
" let s:stack[idx]
" let s:stack[idx][2] = len
" endfor
let change = 0
for i in range(len(a:idxs))
let s:stack[a:idxs[i][0]][1] += change
let change += a:idxs[i][1] - s:stack[a:idxs[i][0]][2]
let s:stack[a:idxs[i][0]][2] = a:idxs[i][1]
endfor
endfunction
function! SpaceVim#plugins#iedit#paser(begin, end, symbol, expr) abort
let s:cursor_stack = []
let s:stack = []
call s:parse_symbol(a:begin, a:end, a:symbol, a:expr)
return [deepcopy(s:stack), s:index]
endfunction
" vim:set et sw=2 cc=80 nowrap: