2009-11-27 25 views
5

Sono un utente Vim e desidero essere in grado di eseguire il loop su una gamma di sottostringhe quando sto sostituendo. Come posso usare qualche magia vim per andare da un insieme di linee come questa:Come si sostituisce da un elenco di stringhe in VIM?

Afoo 
Bfoo 
Cfoo 
Dfoo 

a

Abar 
Bbar 
Cbaz 
Dbaz 

?

Voglio cercare il mio file dall'inizio per il prossimo occurance di foo e sostituire le prime due istanze con bar e le seconde due con baz. Sta usando un ciclo for l'opzione migliore? Se è così, allora come faccio ad usare la variabile loop nel comando di sostituzione?

risposta

9

Vorrei usare una funzione che ha uno stato e chiamare questa funzione da% s. Qualcosa di simile:

" 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 

e utilizzarli con:

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

L'invito ai due comandi potrebbe essere incapsulato in un unico comando, se lo si desidera.


EDIT: Ecco una versione integrata come un comando che:

  • accetta il maggior numero di sostituzioni come vogliamo, tutte le sostituzioni deve essere separato con il separatore caratteri;
  • supporta i riferimenti a ritroso;
  • può sostituire solo le prime occorrenze N, n == il numero di sostituzioni specificato se la chiamata comando viene sbattuto (con un!)
  • non supporta bandiere soliti like g, i (:h :s_flags) - per questo, dovremmo ad esempio imporre la chiamata al comando per finire sempre con un/(o qualsiasi altro carattere separatore), se l'ultimo testo non viene interpretato come flag.

Ecco la definizione del comando:

: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 

che potrebbe essere utilizzato in questo modo:

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

o addirittura, considerando il testo iniziale:

AfooZ 
BfooE 
CfooR 
DfooT 

comando

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

produrrebbe:

ZbarA 
BbarE 
RbarC 
DbarT 
0

Probabilmente sarà molto più semplice registrare una macro che possa sostituire i primi due, e quindi utilizzare: s per il resto.

La macro potrebbe essere simile a /foo^Mcwbar^[. Se non si ha familiarità con la modalità macro, è sufficiente premere q, a (il registro per memorizzarlo in) e quindi le sequenze di tasti /foo <Enter> cwbar <Escape>.

Ora una volta ottenuta questa macro, fare [email protected] per sostituire le prime due occorrenze nel buffer corrente e usare normalmente :s per sostituire il resto.

1

Ecco come proverei quella macro.

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. 
[email protected]  Do a many times. 

Qualche consiglio su come farlo meglio è il benvenuto.

grazie, Martin.

+0

Sembra che manchi un 'n' prima della tua finale' .' – Rich

2

Questo non è strettamente ciò che si vuole, ma può essere utile per cicli.

Ho scritto un plugin swapit http://www.vim.org/scripts/script.php?script_id=2294 che tra le altre cose può aiutare con il ciclismo attraverso gli elenchi di stringhe. Per esempio.

:Swaplist foobar foo bar baz 

quindi digitare

This line is a foo 

creare una semplice linea strattone/incolla, vai a ultima parola e ctrl-uno swap.

qqyyp$^A 

quindi eseguire il modello di swap

[email protected] 

per ottenere

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

Probabilmente potrebbe essere applicato al vostro problema anche se il suo {} cword sensibile.

2

Perché non:

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

non sono sicuro che copre l'ampiezza del problema, ma ha il pregio di essere un semplice sostituto. Uno più complesso può coprire la soluzione se questo non lo fa.

+0

Hey Derek, Volevo solo dire che ho visto alcuni dei tuoi video vim ieri su vimeo. Volevo solo dire che mi sono divertito a guardarli e ho imparato alcune cose. La tua soluzione è ciò che avrei raggiunto, se fossi un po 'meglio con regex. Grazie per questa semplice soluzione. – Ksiresh

Problemi correlati