我是一个vim用户,并且我希望能够循环范围内的子的时候我代.我如何可以使用一些vim神奇的从一组行这样的:

Afoo
Bfoo
Cfoo
Dfoo

Abar
Bbar
Cbaz
Dbaz

?

我想搜我的文件从一开始就为下一个发生的 foo, 和替换第二个实例与 bar, 和第二两个 baz.使用一个循环的最好的选择吗?如果是这样,那么,我怎么使用循环变量的取代的命令?

有帮助吗?

解决方案

我会用一个功能,有一个国家,并呼吁这个功能自%。是这样的:

" 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

并利用它们:

:call InitRotateSubst()
:%s/foo/\=RotateSubst(['bar', 'bar', 'baz', 'baz'])/

呼叫的两个命令可以被封装成一个单一命令,如果你的愿望。


编辑:这里是一个版本的综合为一个命令,该命令:

  • 接受作为许多替代品,因为我们希望,所有替换的需要与分离分离的性质;
  • 支持后参考资料;
  • 可以仅仅替代N第一次出现,N==的替换数规定如果该命令的话是撞(与!)
  • 不支持通常的标志喜欢克,我(:h :s_flags)--对于这一点,我们将为实例,以强制实行命令,叫总结束了a/(或任何分离器字),如果不是最后的文本是被解释为标志。

这里是命令定义:

: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

它可以用这种方式:

:%RotateSubstitute#foo#bar#bar#baz#baz#

或即使考虑到初始文本:

AfooZ
BfooE
CfooR
DfooT

该命令

%RotateSubstitute/\(.\)foo\(.\)/\2bar\1/\1bar\2/

会产生:

ZbarA
BbarE
RbarC
DbarT

其他提示

这不是你想要的严格意义,但可以用于循环。

我已经编写了一个插件swapit http://www.vim。 org / scripts / script.php?script_id = 2294 除了其他功能外,还可以帮助循环查看字符串列表。例如

 :Swaplist foobar foo bar baz

然后输入

 This line is a foo

创建一个简单的抽动/粘贴行,转到最后一个单词并按ctrl-a swap。

 qqyyp$^A

然后执行交换模式

 100@q

获取

This line is foo
This line is bar
This line is baz
This line is foo
This line is bar
This line is baz
This line is foo
This line is bar
This line is baz
This line is foo
This line is bar
This line is baz
...

虽然{cword}敏感,但它可能适用于您的问题。

为什么不:

:%s/\(.\{-}\)foo\(\_.\{-}\)foo\(\_.\{-}\)foo\(\_.\{-}\)foo/\1bar\2bar\3baz\4baz/

我不确定它是否涵盖问题的广度,但确实具有作为简单替代品的优点。如果没有,那么更复杂的解决方案可能会覆盖解决方案。

这就是我尝试使用宏的方法。

qa          Records macro in buffer a
/foo<CR>    Search for the next instance of 'foo'
3s          Change the next three characters
bar         To the word bar
<Esc>       Back to command mode.
n           Get the next instance of foo
.           Repeat last command
n           Get the next instance of foo
3s          Change next three letters
baz         To the word bar
<Esc>       Back to command mode.
.           Repeat last command
q           Stop recording.
1000@a      Do a many times.

欢迎任何有关如何做得更好的建议。

感谢, 马丁。

记录一个可以替换前两个的宏可能要容易得多,然后再使用:s。

宏可能看起来像/foo^Mcwbar^[。如果您不熟悉宏模式,只需按qa(存储它的寄存器)然后按键/foo <Enter> cwbar <Escape>

现在,一旦你有了这个宏,你可以2@a替换当前缓冲区中的前两个匹配项,并正常使用:s来替换其余的宏。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top