mirror of
https://github.com/SpaceVim/SpaceVim.git
synced 2025-03-22 08:45:42 +08:00
819 lines
28 KiB
VimL
819 lines
28 KiB
VimL
"=============================================================================
|
|
" iedit.vim --- iedit mode for SpaceVim
|
|
" Copyright (c) 2016-2023 Wang Shidong & Contributors
|
|
" Author: Shidong Wang < wsdjeg@outlook.com >
|
|
" URL: https://spacevim.org
|
|
" License: GPLv3
|
|
"=============================================================================
|
|
|
|
""
|
|
" @section iedit, plugins-iedit
|
|
" @parentsection plugins
|
|
" The `iedit` plugin provides multiple cursor support for SpaceVim.
|
|
"
|
|
" @subsection Key bindings
|
|
" >
|
|
" Key binding Description
|
|
" SPC s e string iedit mode
|
|
" <
|
|
"
|
|
" After starting iedit, the following key bindings can be used:
|
|
" >
|
|
" Mode Key binding Description
|
|
" Iedit-Normal a start iedit-insert mode after cursor
|
|
" Iedit-Normal e forward to the end of word
|
|
" Iedit-Normal w forward to the begin of next word
|
|
" Iedit-Normal b move to the begin of current word
|
|
" Iedit-Normal Ctrl-n forward and active next match
|
|
" Iedit-Normal Ctrl-x inactivate current match and move forward
|
|
" Iedit-Normal Ctrl-p inactivate current match and move backward
|
|
" <
|
|
|
|
let s:index = -1
|
|
let s:cursor_col = -1
|
|
let s:mode = ''
|
|
let s:hi_id = ''
|
|
let s:Operator = ''
|
|
let s:iedit_cursor_hi_info = {}
|
|
|
|
let s:VIMH = SpaceVim#api#import('vim#highlight')
|
|
let s:STRING = SpaceVim#api#import('data#string')
|
|
let s:CMP = SpaceVim#api#import('vim#compatible')
|
|
let s:VIM = SpaceVim#api#import('vim')
|
|
|
|
let s:LOGGER =SpaceVim#logger#derive('iedit')
|
|
|
|
|
|
" The object in cursor_stack should be:
|
|
" {
|
|
" begin : string,
|
|
" cursor : char
|
|
" end : string
|
|
" active : boolean
|
|
" lnum : number
|
|
" col : number
|
|
" len : number
|
|
" }
|
|
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,
|
|
\ },{
|
|
\ 'name' : 'IeditInactive',
|
|
\ 'guibg' : '#3c3836',
|
|
\ 'guifg' : '#abb2bf',
|
|
\ 'ctermbg' : '',
|
|
\ 'ctermfg' : 145,
|
|
\ 'bold' : 1,
|
|
\ },
|
|
\ ]
|
|
|
|
function! s:highlight_cursor() abort
|
|
call s:VIMH.hi(s:iedit_cursor_hi_info)
|
|
for i in range(len(s:cursor_stack))
|
|
if s:cursor_stack[i].active
|
|
if i == s:index
|
|
call s:CMP.matchaddpos('IeditPurpleBold',
|
|
\ [[
|
|
\ s:cursor_stack[i].lnum,
|
|
\ s:cursor_stack[i].col,
|
|
\ s:cursor_stack[i].len,
|
|
\ ]])
|
|
else
|
|
call s:CMP.matchaddpos('IeditBlueBold',
|
|
\ [[
|
|
\ s:cursor_stack[i].lnum,
|
|
\ s:cursor_stack[i].col,
|
|
\ s:cursor_stack[i].len,
|
|
\ ]])
|
|
endif
|
|
call matchadd('SpaceVimGuideCursor', '\%' . s:cursor_stack[i].lnum . 'l\%'
|
|
\ . (s:cursor_stack[i].col + len(s:cursor_stack[i].begin)) . 'c', 99999)
|
|
else
|
|
call s:CMP.matchaddpos('IeditInactive',
|
|
\ [[
|
|
\ s:cursor_stack[i].lnum,
|
|
\ s:cursor_stack[i].col,
|
|
\ s:cursor_stack[i].len,
|
|
\ ]])
|
|
endif
|
|
endfor
|
|
endfunction
|
|
|
|
function! s:remove_cursor_highlight() abort
|
|
call clearmatches()
|
|
endfunction
|
|
|
|
""
|
|
" This is public function to evoke iedit with [options]. The default
|
|
" [firstline] is 1, and the default [lastline] is `line('$')`.
|
|
" The following key are supported in [options]:
|
|
" >
|
|
" KEY:
|
|
" expr match expression
|
|
" word match word
|
|
" stack cursor pos stack
|
|
" selectall boolean
|
|
" <
|
|
" if only argv 1 is given, use selected word as pattern
|
|
function! SpaceVim#plugins#iedit#start(...) abort
|
|
" do not start iedit if symbol is empty
|
|
let argv = get(a:000, 0, '')
|
|
let selectall = 1
|
|
if empty(argv) &&
|
|
\ (
|
|
\ matchstr(getline('.'), '\%' . col('.') . 'c.') ==# ''
|
|
\ || matchstr(getline('.'), '\%' . col('.') . 'c.') ==# ' '
|
|
\ )
|
|
echo 'no pattern found under cursor'
|
|
return
|
|
endif
|
|
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])
|
|
call s:VIMH.hi(s:iedit_hi_info[2])
|
|
let cursor_hi = s:VIMH.group2dict('Cursor')
|
|
let s:iedit_cursor_hi_info = deepcopy(cursor_hi)
|
|
let s:iedit_cursor_hi_info.name = 'SpaceVimGuideCursor'
|
|
let lcursor_hi = s:VIMH.group2dict('lCursor')
|
|
let guicursor = &guicursor
|
|
call s:VIMH.hide_in_normal('Cursor')
|
|
call s:VIMH.hide_in_normal('lCursor')
|
|
" hi Cursor ctermbg=16 ctermfg=16 guifg=#282c34 guibg=#282c34
|
|
" hi lCursor ctermbg=16 ctermfg=16 guifg=#282c34 guibg=#282c34
|
|
if has('nvim')
|
|
set guicursor+=a:Cursor/lCursor
|
|
endif
|
|
let s:mode = 'n'
|
|
let w:spacevim_iedit_mode = s:mode
|
|
let w:spacevim_statusline_mode = 'in'
|
|
if empty(s:cursor_stack)
|
|
let curpos = getpos('.')
|
|
let save_reg_k = @k
|
|
" the register " is cleared
|
|
" save the register context before run following command
|
|
let save_reg_default = @"
|
|
let use_expr = 0
|
|
if !empty(argv) && type(argv) == 4
|
|
let selectall = get(argv, 'selectall', selectall)
|
|
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')
|
|
else
|
|
normal! viw"ky
|
|
let symbol = split(@k, "\n")[0]
|
|
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
|
|
let @" = save_reg_default
|
|
call setpos('.', curpos)
|
|
let begin = get(a:000, 1, 1)
|
|
let end = get(a:000, 2, line('$'))
|
|
if use_expr
|
|
call s:LOGGER.debug('iedit symbol:>' . symbol . '<')
|
|
call s:LOGGER.debug('iedit use_expr:' . use_expr)
|
|
call s:LOGGER.debug('iedit begin:' . begin)
|
|
call s:LOGGER.debug('iedit end:' . end)
|
|
call s:parse_symbol(begin, end, symbol, 1, selectall)
|
|
else
|
|
call s:LOGGER.debug('iedit symbol:>' . symbol . '<')
|
|
call s:LOGGER.debug('iedit use_expr:' . use_expr)
|
|
call s:LOGGER.debug('iedit begin:' . begin)
|
|
call s:LOGGER.debug('iedit end:' . end)
|
|
call s:parse_symbol(begin, end, symbol, 0, selectall)
|
|
endif
|
|
endif
|
|
call s:highlight_cursor()
|
|
redrawstatus!
|
|
while s:mode !=# '' && len(s:cursor_stack) > 0
|
|
redraw!
|
|
let char = s:VIM.getchar()
|
|
if s:mode ==# 'n' && char ==# "\<Esc>"
|
|
let s:mode = ''
|
|
else
|
|
let symbol = s:handle(s:mode, char)
|
|
endif
|
|
endwhile
|
|
if len(s:cursor_stack) == 0
|
|
normal! :
|
|
echo 'Pattern not found:' . symbol
|
|
endif
|
|
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:VIMH.hi(cursor_hi)
|
|
call s:VIMH.hi(lcursor_hi)
|
|
let &guicursor = guicursor
|
|
normal! :
|
|
call s:remove_cursor_highlight()
|
|
try
|
|
call matchdelete(s:hi_id)
|
|
catch
|
|
endtry
|
|
let s:hi_id = ''
|
|
let &l:cursorline = save_cl
|
|
return symbol
|
|
endfunction
|
|
|
|
|
|
function! s:handle(mode, char) abort
|
|
if a:mode ==# 'n' && s:Operator ==# 'f'
|
|
return s:handle_f_char(a:char)
|
|
elseif a:mode ==# 'n'
|
|
return s:handle_normal(a:char)
|
|
elseif a:mode ==# 'i' && s:Operator ==# 'r'
|
|
return s:handle_register(a:char)
|
|
elseif a:mode ==# 'i'
|
|
return s:handle_insert(a:char)
|
|
endif
|
|
endfunction
|
|
|
|
function! s:handle_f_char(char) abort
|
|
silent! call s:remove_cursor_highlight()
|
|
" map(rang(32,126), 'nr2char(v:val)')
|
|
" [' ', '!', '"', '#', '$', '%', '&', '''', '(', ')', '*', '+', ',', '-', '.', '/', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\', ']', '^', '_', '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '{', '|', '}', '~']
|
|
if a:char >= 32 && a:char <= 126
|
|
let s:Operator = ''
|
|
for i in range(len(s:cursor_stack))
|
|
let matchedstr = matchstr(s:cursor_stack[i].end, printf('[^%s]*', nr2char(a:char)))
|
|
let s:cursor_stack[i].begin = s:cursor_stack[i].begin . s:cursor_stack[i].cursor . matchedstr
|
|
let s:cursor_stack[i].end = matchstr(s:cursor_stack[i].end, printf('[%s]\zs.*', nr2char(a:char)))
|
|
let s:cursor_stack[i].cursor = nr2char(a:char)
|
|
endfor
|
|
endif
|
|
silent! call s:highlight_cursor()
|
|
return s:cursor_stack[0].begin . s:cursor_stack[0].cursor . s:cursor_stack[0].end
|
|
endfunction
|
|
|
|
function! s:handle_register(char) abort
|
|
let char = nr2char(a:char)
|
|
if char =~# '[a-zA-Z0-9"+:/]'
|
|
silent! call s:remove_cursor_highlight()
|
|
let s:Operator = ''
|
|
let reg = '@' . char
|
|
let paste = get(split(eval(reg), "\n"), 0, '')
|
|
for i in range(len(s:cursor_stack))
|
|
if s:cursor_stack[i].active
|
|
let s:cursor_stack[i].begin = s:cursor_stack[i].begin . paste
|
|
endif
|
|
endfor
|
|
call s:replace_symbol()
|
|
silent! call s:highlight_cursor()
|
|
endif
|
|
return s:cursor_stack[0].begin . s:cursor_stack[0].cursor . s:cursor_stack[0].end
|
|
endfunction
|
|
|
|
function! s:handle_normal(char) abort
|
|
silent! call s:remove_cursor_highlight()
|
|
if a:char ==# 'i'
|
|
" i: switch to iedit insert mode
|
|
let s:mode = 'i'
|
|
let w:spacevim_iedit_mode = s:mode
|
|
let w:spacevim_statusline_mode = 'ii'
|
|
redrawstatus!
|
|
elseif a:char ==# 'I'
|
|
" I: move surcor to the begin, and switch to iedit insert mode
|
|
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))
|
|
if s:cursor_stack[i].active
|
|
let old_cursor_char = s:cursor_stack[i].cursor
|
|
let s:cursor_stack[i].cursor = matchstr(
|
|
\ s:cursor_stack[i].begin
|
|
\ . s:cursor_stack[i].cursor
|
|
\ . s:cursor_stack[i].end,
|
|
\ '^.')
|
|
let s:cursor_stack[i].end = substitute(
|
|
\ s:cursor_stack[i].begin
|
|
\ . old_cursor_char
|
|
\ . s:cursor_stack[i].end,
|
|
\ '^.', '', 'g')
|
|
let s:cursor_stack[i].begin = ''
|
|
endif
|
|
endfor
|
|
redrawstatus!
|
|
elseif a:char ==# "\<Tab>"
|
|
let s:cursor_stack[s:index].active = s:cursor_stack[s:index].active ? 0 : 1
|
|
elseif a:char ==# 'a'
|
|
" a: goto iedit insert mode after cursor char
|
|
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))
|
|
if s:cursor_stack[i].active
|
|
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')
|
|
endif
|
|
endfor
|
|
redrawstatus!
|
|
elseif a:char ==# '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))
|
|
if s:cursor_stack[i].active
|
|
let s:cursor_stack[i].begin = s:cursor_stack[i].begin . s:cursor_stack[i].cursor . s:cursor_stack[i].end
|
|
let s:cursor_stack[i].cursor = ''
|
|
let s:cursor_stack[i].end = ''
|
|
endif
|
|
endfor
|
|
redrawstatus!
|
|
elseif a:char ==# 'C'
|
|
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))
|
|
if s:cursor_stack[i].active
|
|
let s:cursor_stack[i].cursor = ''
|
|
let s:cursor_stack[i].end = ''
|
|
endif
|
|
endfor
|
|
call s:replace_symbol()
|
|
elseif a:char ==# '~'
|
|
for i in range(len(s:cursor_stack))
|
|
if s:cursor_stack[i].active
|
|
let s:cursor_stack[i].cursor = s:STRING.toggle_case(s:cursor_stack[i].cursor)
|
|
endif
|
|
endfor
|
|
call s:replace_symbol()
|
|
elseif a:char ==# 'f'
|
|
let s:Operator = 'f'
|
|
call s:timeout()
|
|
elseif a:char ==# 's'
|
|
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))
|
|
if s:cursor_stack[i].active
|
|
" let s:cursor_stack[i].begin = s:cursor_stack[i].begin
|
|
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')
|
|
endif
|
|
endfor
|
|
call s:replace_symbol()
|
|
elseif a:char ==# 'x'
|
|
for i in range(len(s:cursor_stack))
|
|
if s:cursor_stack[i].active
|
|
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')
|
|
endif
|
|
endfor
|
|
call s:replace_symbol()
|
|
elseif a:char ==# 'X'
|
|
for i in range(len(s:cursor_stack))
|
|
if s:cursor_stack[i].active
|
|
let s:cursor_stack[i].begin = substitute(s:cursor_stack[i].begin, '.$', '', 'g')
|
|
endif
|
|
endfor
|
|
call s:replace_symbol()
|
|
elseif a:char ==# "\<Left>" || a:char ==# 'h'
|
|
for i in range(len(s:cursor_stack))
|
|
if s:cursor_stack[i].active
|
|
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
|
|
endif
|
|
endfor
|
|
elseif a:char ==# "\<Right>" || a:char ==# 'l'
|
|
for i in range(len(s:cursor_stack))
|
|
if s:cursor_stack[i].active
|
|
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')
|
|
endif
|
|
endfor
|
|
elseif a:char ==# 'e'
|
|
for i in range(len(s:cursor_stack))
|
|
|
|
if s:cursor_stack[i].active
|
|
let word = matchstr(s:cursor_stack[i].end, '^\s*\S*')
|
|
let s:cursor_stack[i].begin =
|
|
\ s:cursor_stack[i].begin
|
|
\ . s:cursor_stack[i].cursor
|
|
\ . word
|
|
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')
|
|
let s:cursor_stack[i].end = substitute(s:cursor_stack[i].end, '^\s*\S*', '', 'g')
|
|
endif
|
|
endfor
|
|
elseif a:char ==# 'b'
|
|
" b: move to the begin of current word
|
|
for i in range(len(s:cursor_stack))
|
|
if s:cursor_stack[i].active
|
|
let word = matchstr(s:cursor_stack[i].begin, '\S*\s*$')
|
|
let s:cursor_stack[i].end =
|
|
\ word
|
|
\ . s:cursor_stack[i].cursor
|
|
\ . s:cursor_stack[i].end
|
|
let s:cursor_stack[i].begin = substitute(s:cursor_stack[i].begin, '\S*\s*$', '', 'g')
|
|
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')
|
|
endif
|
|
endfor
|
|
elseif a:char ==# 'w'
|
|
for i in range(len(s:cursor_stack))
|
|
if s:cursor_stack[i].active
|
|
let word = matchstr(s:cursor_stack[i].end, '^\S*\s*')
|
|
let s:cursor_stack[i].begin =
|
|
\ s:cursor_stack[i].begin
|
|
\ . s:cursor_stack[i].cursor
|
|
\ . word
|
|
let s:cursor_stack[i].end = substitute(s:cursor_stack[i].end, '^\S*\s*', '', 'g')
|
|
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')
|
|
endif
|
|
endfor
|
|
elseif a:char ==# '0' || a:char ==# "\<Home>" " 0 or <Home>
|
|
for i in range(len(s:cursor_stack))
|
|
if s:cursor_stack[i].active
|
|
let old_cursor_char = s:cursor_stack[i].cursor
|
|
let s:cursor_stack[i].cursor = matchstr(s:cursor_stack[i].begin . s:cursor_stack[i].cursor . s:cursor_stack[i].end, '^.')
|
|
let s:cursor_stack[i].end = substitute(s:cursor_stack[i].begin . old_cursor_char . s:cursor_stack[i].end , '^.', '', 'g')
|
|
let s:cursor_stack[i].begin = ''
|
|
endif
|
|
endfor
|
|
elseif a:char ==# '$' || a:char ==# "\<End>" " $ or <End>
|
|
for i in range(len(s:cursor_stack))
|
|
if s:cursor_stack[i].active
|
|
let old_cursor_char = s:cursor_stack[i].cursor
|
|
let s:cursor_stack[i].cursor = matchstr(s:cursor_stack[i].begin . s:cursor_stack[i].cursor . s:cursor_stack[i].end, '.$')
|
|
let s:cursor_stack[i].begin = substitute(s:cursor_stack[i].begin . old_cursor_char . s:cursor_stack[i].end , '.$', '', 'g')
|
|
let s:cursor_stack[i].end = ''
|
|
endif
|
|
endfor
|
|
elseif a:char ==# 'D'
|
|
for i in range(len(s:cursor_stack))
|
|
if s:cursor_stack[i].active
|
|
let s:cursor_stack[i].begin = ''
|
|
let s:cursor_stack[i].cursor = ''
|
|
let s:cursor_stack[i].end = ''
|
|
endif
|
|
endfor
|
|
call s:replace_symbol()
|
|
elseif a:char ==# 'p'
|
|
for i in range(len(s:cursor_stack))
|
|
if s:cursor_stack[i].active
|
|
let s:cursor_stack[i].begin = @"
|
|
let s:cursor_stack[i].cursor = ''
|
|
let s:cursor_stack[i].end = ''
|
|
endif
|
|
endfor
|
|
call s:replace_symbol()
|
|
elseif a:char ==# 'S'
|
|
for i in range(len(s:cursor_stack))
|
|
if s:cursor_stack[i].active
|
|
let s:cursor_stack[i].begin = ''
|
|
let s:cursor_stack[i].cursor = ''
|
|
let s:cursor_stack[i].end = ''
|
|
endif
|
|
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 ==# 'G'
|
|
exe s:cursor_stack[-1].lnum
|
|
let s:index = len(s:cursor_stack) - 1
|
|
elseif a:char ==# 'g'
|
|
if s:Operator ==# 'g'
|
|
exe s:cursor_stack[0].lnum
|
|
let s:Operator = ''
|
|
let s:index = 0
|
|
else
|
|
let s:Operator = 'g'
|
|
call s:timeout()
|
|
endif
|
|
elseif a:char ==# "\<C-n>"
|
|
if s:index == len(s:cursor_stack) - 1
|
|
let s:index = 0
|
|
else
|
|
let s:index += 1
|
|
endif
|
|
let s:cursor_stack[s:index].active = 1
|
|
call cursor(s:cursor_stack[s:index].lnum,
|
|
\ s:cursor_stack[s:index].col + len(s:cursor_stack[s:index].begin))
|
|
elseif a:char ==# "\<C-x>"
|
|
let s:cursor_stack[s:index].active = 0
|
|
if s:index == len(s:cursor_stack) - 1
|
|
let s:index = 0
|
|
else
|
|
let s:index += 1
|
|
endif
|
|
let s:cursor_stack[s:index].active = 1
|
|
call cursor(s:cursor_stack[s:index].lnum,
|
|
\ s:cursor_stack[s:index].col + len(s:cursor_stack[s:index].begin))
|
|
elseif a:char ==# "\<C-p>"
|
|
let s:cursor_stack[s:index].active = 0
|
|
if s:index == 0
|
|
let s:index = len(s:cursor_stack) - 1
|
|
else
|
|
let s:index -= 1
|
|
endif
|
|
let s:cursor_stack[s:index].active = 1
|
|
silent! call s:highlight_cursor()
|
|
call cursor(s:cursor_stack[s:index].lnum,
|
|
\ s:cursor_stack[s:index].col + len(s:cursor_stack[s:index].begin))
|
|
elseif a:char ==# 'n'
|
|
let origin_index = s:index
|
|
if s:index == len(s:cursor_stack) - 1
|
|
let s:index = 0
|
|
else
|
|
let s:index += 1
|
|
endif
|
|
while !s:cursor_stack[s:index].active
|
|
let s:index += 1
|
|
if s:index == len(s:cursor_stack)
|
|
let s:index = 0
|
|
endif
|
|
if s:index ==# origin_index
|
|
break
|
|
endif
|
|
endwhile
|
|
call cursor(s:cursor_stack[s:index].lnum,
|
|
\ s:cursor_stack[s:index].col + len(s:cursor_stack[s:index].begin))
|
|
elseif a:char ==# 'N'
|
|
if s:index == 0
|
|
let s:index = len(s:cursor_stack) - 1
|
|
else
|
|
let s:index -= 1
|
|
endif
|
|
call cursor(s:cursor_stack[s:index].lnum, s:cursor_stack[s:index].col + len(s:cursor_stack[s:index].begin))
|
|
endif
|
|
silent! call s:highlight_cursor()
|
|
return s:cursor_stack[0].begin . s:cursor_stack[0].cursor . s:cursor_stack[0].end
|
|
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()
|
|
let is_movement = 0
|
|
if a:char ==# "\<Esc>" || a:char ==# "\<C-g>"
|
|
" Ctrl-g / <Esc>: switch to iedit normal mode
|
|
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 s:cursor_stack[0].begin . s:cursor_stack[0].cursor . s:cursor_stack[0].end
|
|
elseif a:char ==# "\<C-w>"
|
|
" ctrl-w: delete word before cursor
|
|
for i in range(len(s:cursor_stack))
|
|
if s:cursor_stack[i].active
|
|
let s:cursor_stack[i].begin = substitute(s:cursor_stack[i].begin, '\S*\s*$', '', 'g')
|
|
endif
|
|
endfor
|
|
elseif a:char ==# "\<C-u>"
|
|
" ctrl-u: delete all words before cursor
|
|
for i in range(len(s:cursor_stack))
|
|
if s:cursor_stack[i].active
|
|
let s:cursor_stack[i].begin = ''
|
|
endif
|
|
endfor
|
|
elseif a:char ==# "\<C-k>"
|
|
" Ctrl-k: delete all words after cursor
|
|
for i in range(len(s:cursor_stack))
|
|
if s:cursor_stack[i].active
|
|
let s:cursor_stack[i].cursor = ''
|
|
let s:cursor_stack[i].end = ''
|
|
endif
|
|
endfor
|
|
elseif a:char ==# "\<bs>" || a:char ==# "\<C-h>"
|
|
" BackSpace or Ctrl-h: delete char before cursor
|
|
for i in range(len(s:cursor_stack))
|
|
if s:cursor_stack[i].active
|
|
let s:cursor_stack[i].begin = substitute(s:cursor_stack[i].begin, '.$', '', 'g')
|
|
endif
|
|
endfor
|
|
elseif a:char ==# "\<Delete>" || a:char ==# "\<C-?>" " <Delete>
|
|
" Delete: delete char after cursor
|
|
for i in range(len(s:cursor_stack))
|
|
if s:cursor_stack[i].active
|
|
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')
|
|
endif
|
|
endfor
|
|
elseif a:char ==# "\<C-b>" || a:char ==# "\<Left>"
|
|
" ctrl-b / <Left>: moves the cursor back one character
|
|
let is_movement = 1
|
|
for i in range(len(s:cursor_stack))
|
|
if s:cursor_stack[i].active
|
|
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
|
|
endif
|
|
endfor
|
|
elseif a:char ==# "\<C-f>" || a:char ==# "\<Right>"
|
|
" ctrl-f / <Right>: moves the cursor forward one character
|
|
let is_movement = 1
|
|
for i in range(len(s:cursor_stack))
|
|
if s:cursor_stack[i].active
|
|
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')
|
|
endif
|
|
endfor
|
|
elseif a:char ==# "\<C-r>"
|
|
let s:Operator = 'r'
|
|
call s:timeout()
|
|
elseif a:char ==# "\<C-a>" || a:char ==# "\<Home>"
|
|
" Ctrl-a or <Home>
|
|
let is_movement = 1
|
|
for i in range(len(s:cursor_stack))
|
|
if s:cursor_stack[i].active
|
|
let old_cursor_char = s:cursor_stack[i].cursor
|
|
let s:cursor_stack[i].cursor = matchstr(
|
|
\ s:cursor_stack[i].begin
|
|
\ . s:cursor_stack[i].cursor
|
|
\ . s:cursor_stack[i].end,
|
|
\ '^.')
|
|
let s:cursor_stack[i].end = substitute(
|
|
\ s:cursor_stack[i].begin
|
|
\ . old_cursor_char
|
|
\ . s:cursor_stack[i].end,
|
|
\ '^.', '', 'g')
|
|
let s:cursor_stack[i].begin = ''
|
|
endif
|
|
endfor
|
|
elseif a:char ==# "\<C-e>" || a:char ==# "\<End>"
|
|
" Ctrl-e or <End>
|
|
let is_movement = 1
|
|
for i in range(len(s:cursor_stack))
|
|
if s:cursor_stack[i].active
|
|
let old_cursor_char = s:cursor_stack[i].cursor
|
|
let s:cursor_stack[i].cursor = matchstr(
|
|
\ s:cursor_stack[i].begin
|
|
\ . s:cursor_stack[i].cursor
|
|
\ . s:cursor_stack[i].end,
|
|
\ '.$')
|
|
let s:cursor_stack[i].begin = substitute(
|
|
\ s:cursor_stack[i].begin
|
|
\ . old_cursor_char
|
|
\ . s:cursor_stack[i].end,
|
|
\ '.$', '', 'g')
|
|
let s:cursor_stack[i].end = ''
|
|
endif
|
|
endfor
|
|
else
|
|
for i in range(len(s:cursor_stack))
|
|
if s:cursor_stack[i].active
|
|
let s:cursor_stack[i].begin .= a:char
|
|
endif
|
|
endfor
|
|
endif
|
|
if !is_movement
|
|
call s:replace_symbol()
|
|
endif
|
|
silent! call s:highlight_cursor()
|
|
return s:cursor_stack[0].begin . s:cursor_stack[0].cursor . s:cursor_stack[0].end
|
|
endfunction
|
|
|
|
" begin: the first line for parse
|
|
" end: the last line for parse
|
|
" symbol: the word
|
|
" use_expr: use expr or not
|
|
" selectall: select all or not
|
|
function! s:parse_symbol(begin, end, symbol, use_expr, selectall) abort
|
|
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, a:use_expr)
|
|
for [pos_a, pos_b] in idx
|
|
call add(s:cursor_stack,
|
|
\ {
|
|
\ 'begin' : line[pos_a : pos_b - 2],
|
|
\ 'cursor' : line[pos_b - 1 : pos_b - 1],
|
|
\ 'end' : '',
|
|
\ 'active' : a:selectall,
|
|
\ 'lnum' : l,
|
|
\ 'col' : pos_a + 1,
|
|
\ 'len' : pos_b - pos_a,
|
|
\ }
|
|
\ )
|
|
if l == cursor[0] && pos_a + 1 <= cursor[1] && pos_b >= cursor[1]
|
|
let s:index = len(s:cursor_stack) - 1
|
|
endif
|
|
endfor
|
|
endfor
|
|
if s:index == -1 && !empty(s:cursor_stack)
|
|
let s:index = 0
|
|
call cursor(s:cursor_stack[0].lnum, s:cursor_stack[0].col)
|
|
endif
|
|
if !empty(s:cursor_stack)
|
|
let s:cursor_stack[s:index].active = 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:cursor_stack))
|
|
if s:cursor_stack[i].lnum != line
|
|
if !empty(idxs)
|
|
let end = getline(line)[s:cursor_stack[i-1].col + s:cursor_stack[i-1].len - 1: ]
|
|
let pre .= end
|
|
endif
|
|
call s:fixstack(idxs)
|
|
call setline(line, pre)
|
|
let idxs = []
|
|
let line = s:cursor_stack[i].lnum
|
|
let begin = s:cursor_stack[i].col == 1 ? '' : getline(line)[:s:cursor_stack[i].col - 2]
|
|
let pre = begin . s:cursor_stack[i].begin . s:cursor_stack[i].cursor . s:cursor_stack[i].end
|
|
else
|
|
let line = s:cursor_stack[i].lnum
|
|
if i == 0
|
|
let pre = (s:cursor_stack[i].col == 1 ? '' : getline(line)[:s:cursor_stack[i].col - 2]) . s:cursor_stack[i].begin . s:cursor_stack[i].cursor . s:cursor_stack[i].end
|
|
else
|
|
let a = s:cursor_stack[i-1].col + s:cursor_stack[i-1].len - 1
|
|
let b = s:cursor_stack[i].col - 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:cursor_stack[i].col + s:cursor_stack[i].len - 1: ]
|
|
let pre .= end
|
|
endif
|
|
call s:fixstack(idxs)
|
|
call setline(line, pre)
|
|
endfunction
|
|
|
|
function! s:fixstack(idxs) abort
|
|
let change = 0
|
|
for i in range(len(a:idxs))
|
|
let s:cursor_stack[a:idxs[i][0]].col += change
|
|
let change += a:idxs[i][1] - s:cursor_stack[a:idxs[i][0]].len
|
|
let s:cursor_stack[a:idxs[i][0]].len = a:idxs[i][1]
|
|
endfor
|
|
endfunction
|
|
|
|
function! SpaceVim#plugins#iedit#paser(begin, end, symbol, expr) abort
|
|
let s:cursor_stack = []
|
|
call s:parse_symbol(a:begin, a:end, a:symbol, a:expr, 1)
|
|
return [s:cursor_stack, s:index]
|
|
endfunction
|
|
|
|
" vim:set et sw=2 cc=80 nowrap:
|