"============================================================================= " 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 ==# "\" 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 ==# "\" 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 ==# "\" || 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 ==# "\" || 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 ==# "\" " 0 or 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 ==# "\" " $ or 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 ==# "\" 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 ==# "\" 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 ==# "\" 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 ==# "\" || a:char ==# "\" " Ctrl-g / : 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 ==# "\" " 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 ==# "\" " 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 ==# "\" " 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 ==# "\" || a:char ==# "\" " 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 ==# "\" || a:char ==# "\" " " 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 ==# "\" || a:char ==# "\" " ctrl-b / : 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 ==# "\" || a:char ==# "\" " ctrl-f / : 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 ==# "\" let s:Operator = 'r' call s:timeout() elseif a:char ==# "\" || a:char ==# "\" " Ctrl-a or 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 ==# "\" || a:char ==# "\" " Ctrl-e or 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: