如何从VIM中的字符串列表中替换?

Ksi*_*esh 5 vim substitution

我是一个vim用户,我希望能够在替换时循环一系列子串.我如何使用一些vim魔法来从这样的一组行:

Afoo
Bfoo
Cfoo
Dfoo
Run Code Online (Sandbox Code Playgroud)

Abar
Bbar
Cbaz
Dbaz
Run Code Online (Sandbox Code Playgroud)

我想从一开始就为下一次数搜索我的文件foo,并替换在前两种情况下bar,后两个用baz.使用for循环是最好的选择吗?如果是这样,那么如何在替换命令中使用循环变量?

Luc*_*tte 9

我会使用一个具有状态的函数,并从%s调用此函数.就像是:

" untested code
function! InitRotateSubst()
    let s:rs_idx = 0
endfunction

function! RotateSubst(list)
    let res = a:list[s:rs_idx]
    let s:rs_idx += 1
    if s:rs_idx == len(a:list)
        let s:rs_idx = 0
    endif
    return res
endfunction
Run Code Online (Sandbox Code Playgroud)

并使用它们:

:call InitRotateSubst()
:%s/foo/\=RotateSubst(['bar', 'bar', 'baz', 'baz'])/
Run Code Online (Sandbox Code Playgroud)

如果您愿意,可以将对这两个命令的调用封装到单个命令中.


编辑:这是一个集成为命令的版本:

  • 接受尽可能多的替换,所有替换都需要用分隔符分隔;
  • 支持反向引用;
  • 只能替换N个第一次出现,N ==如果命令调用被绑定指定的替换次数(用!)
  • 不支持像g,i(:h :s_flags)那样的通常标志- 为此,我们将例如强制执行命令调用总是以/(或任何分隔符)结束,如果不是最后一个文本被解释为标志.

这是命令定义:

:command! -bang -nargs=1 -range RotateSubstitute <line1>,<line2>call s:RotateSubstitute("<bang>", <f-args>)

function! s:RotateSubstitute(bang, repl_arg) range
  let do_loop = a:bang != "!"
  " echom "do_loop=".do_loop." -> ".a:bang
  " reset internal state
  let s:rs_idx = 0
  " obtain the separator character
  let sep = a:repl_arg[0]
  " obtain all fields in the initial command
  let fields = split(a:repl_arg, sep)

  " prepare all the backreferences
  let replacements = fields[1:]
  let max_back_ref = 0
  for r in replacements
    let s = substitute(r, '.\{-}\(\\\d\+\)', '\1', 'g')
    " echo "s->".s
    let ls = split(s, '\\')
    for d in ls
      let br = matchstr(d, '\d\+')
      " echo '##'.(br+0).'##'.type(0) ." ~~ " . type(br+0)
      if !empty(br) && (0+br) > max_back_ref
    let max_back_ref = br
      endif
    endfor
  endfor
  " echo "max back-ref=".max_back_ref
  let sm = ''
  for i in range(0, max_back_ref)
    let sm .= ','. 'submatch('.i.')' 
    " call add(sm,)
  endfor

  " build the action to execute
  let action = '\=s:DoRotateSubst('.do_loop.',' . string(replacements) . sm .')'
  " prepare the :substitute command
  let args = [fields[0], action ]
  let cmd = a:firstline . ',' . a:lastline . 's' . sep . join(args, sep)
  " echom cmd
  " and run it
  exe cmd
endfunction

function! s:DoRotateSubst(do_loop, list, replaced, ...)
  " echom string(a:000)
  if ! a:do_loop && s:rs_idx == len(a:list)
    return a:replaced
  else
    let res0 = a:list[s:rs_idx]
    let s:rs_idx += 1
    if a:do_loop && s:rs_idx == len(a:list)
        let s:rs_idx = 0
    endif

    let res = ''
    while strlen(res0)
      let ml = matchlist(res0, '\(.\{-}\)\(\\\d\+\)\(.*\)')
      let res .= ml[1]
      let ref = eval(substitute(ml[2], '\\\(\d\+\)', 'a:\1', ''))
      let res .= ref
      let res0 = ml[3]
    endwhile

    return res
  endif
endfunction
Run Code Online (Sandbox Code Playgroud)

可以这样使用:

:%RotateSubstitute#foo#bar#bar#baz#baz#
Run Code Online (Sandbox Code Playgroud)

甚至,考虑到初始文本:

AfooZ
BfooE
CfooR
DfooT
Run Code Online (Sandbox Code Playgroud)

命令

%RotateSubstitute/\(.\)foo\(.\)/\2bar\1/\1bar\2/
Run Code Online (Sandbox Code Playgroud)

会产生:

ZbarA
BbarE
RbarC
DbarT
Run Code Online (Sandbox Code Playgroud)