1
0
mirror of https://github.com/SpaceVim/SpaceVim.git synced 2025-03-22 08:45:42 +08:00
2023-03-26 13:44:47 +08:00

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: