Frage

Ich bin ein VIM -Benutzer und möchte in der Lage sein, über eine Reihe von Substrings zu gehen, wenn ich ersetzt. Wie kann ich eine VIM -Magie verwenden, um von einer Reihe von Zeilen wie diesem zu entstehen:

Afoo
Bfoo
Cfoo
Dfoo

zu

Abar
Bbar
Cbaz
Dbaz

?

Ich möchte meine Datei vom Start nach dem nächsten Ereignis von durchsuchen foo, und ersetzen Sie die ersten beiden Fälle durch bar, und die zweiten zwei mit baz. Ist die Verwendung von A for Loop die beste Option? Wenn ja, wie verwende ich dann die Schleifenvariable im Substitutionsbefehl?

War es hilfreich?

Lösung

Ich würde eine Funktion verwenden, die einen Zustand hat, und diese Funktion aus %s aufrufen. Etwas wie:

" 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

Und benutze sie mit:

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

Der Anruf zu den beiden Befehlen könnte in einem einzigen Befehl eingekapselt werden, wenn Sie dies wünschen.


Bearbeiten: Hier ist eine Version, die als Befehl integriert ist, dass:

  • Akzeptiert so viele Ersetzungen, wie wir es wünschen, alle Ersetzungen müssen mit dem Trennzeichen getrennt werden.
  • unterstützt Rückenreferenzen;
  • Kann nur die ersten Vorkommen ersetzen, n == die Anzahl der angegebenen Ersetzungen, wenn der Befehlsaufruf geschlagen wird (mit einem!)
  • unterstützt keine üblichen Flaggen wie G, ich (ich (:h :s_flags)-dafür hätten wir zum Beispiel, dass der Befehlsaufruf immer mit A / (oder allen Trennzeichen) den Befehlsaufruf auferlegt, wenn nicht der letzte Text als Flags interpretiert wird.

Hier ist die Befehlsdefinition:

: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

das könnte auf diese Weise verwendet werden:

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

oder sogar unter Berücksichtigung des ersten Textes:

AfooZ
BfooE
CfooR
DfooT

der Befehl

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

würde produzieren:

ZbarA
BbarE
RbarC
DbarT

Andere Tipps

Dies ist nicht genau das, was Sie wollen, kann aber für Zyklen nützlich sein.

Ich habe einen Plugin -Swapit geschrieben http://www.vim.org/scripts/script.php?script_id=2294 Was unter anderem beim Radfahren durch Stringslisten helfen kann. Z.B.

 :Swaplist foobar foo bar baz

Geben Sie dann ein

 This line is a foo

Erstellen Sie eine einfache Yank-/Paste-Linie, gehen Sie zu Last Word und Strg-A Swap.

 qqyyp$^A

Führen Sie dann das Tauschmuster aus

 100@q

bekommen

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
...

Es könnte wahrscheinlich auf Ihr Problem angewendet werden, obwohl es {cWord} sensitiv ist.

Warum nicht:

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

Ich bin mir nicht sicher, ob es die Breite des Problems abdeckt, hat aber die Tugend, ein einfacher Ersatz zu sein. Ein komplexerer kann die Lösung abdecken, wenn diese nicht der Fall ist.

So würde ich dieses Makro versuchen.

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.

Jeder Rat, wie man es besser macht, ist willkommen.

Danke, Martin.

Es wird wahrscheinlich viel einfacher sein, ein Makro aufzunehmen, das die ersten beiden ersetzen kann, und dann: s für den Rest verwenden.

Das Makro könnte so aussehen /foo^Mcwbar^[. Wenn Sie mit dem Makromodus nicht vertraut sind, klicken Sie einfach q, a (Das Register, um es zu speichern) und dann die Tastenanschläge /foo <Enter> cwbar <Escape>.

Jetzt, sobald Sie das Makro haben, tun Sie es 2@a Um die ersten beiden Vorkommen im aktuellen Puffer zu ersetzen und zu verwenden :s Normalerweise ersetzen Sie den Rest.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top