状態を持つ関数を使用し、この関数を %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
そして、それらを次のように使用します。
:call InitRotateSubst()
:%s/foo/\=RotateSubst(['bar', 'bar', 'baz', 'baz'])/
必要に応じて、2 つのコマンドの呼び出しを 1 つのコマンドにカプセル化することもできます。
編集:これは、次のコマンドとして統合されたバージョンです。
- 必要な数の置換を受け入れます。すべての置換は、区切り文字で区切る必要があります。
- 後方参照をサポートします。
- 最初に出現した 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
次のように使用できます。
:%RotateSubstitute#foo#bar#bar#baz#baz#
または、最初のテキストを考慮して:
AfooZ
BfooE
CfooR
DfooT
コマンド
%RotateSubstitute/\(.\)foo\(.\)/\2bar\1/\1bar\2/
生成されます:
ZbarA
BbarE
RbarC
DbarT