Come trovare e sostituire in Vim senza dover digitare la parola originale?


53

Vorrei ottimizzare il mio flusso di lavoro "trova e sostituisci" in Vim. È qualcosa che faccio spesso, dato che sono sicuro che anche molti di voi lo fanno. Di solito qualcosa sulla falsariga di: copia un blocco e cambia il nome di una variabile in alcuni punti. Lo so, lo so, che probabilmente fa scattare il tuo riflesso "perché stai copiando e incollando il codice", ma non seguiamo quella strada ... Ci sono molti casi d'uso validi :)

Sono ben consapevole della ricerca e sostituisci i comandi: :so :%sma non mi piacciono. Mi costringe a digitare sia il nome completo della variabile che sto cercando sia quello in cui lo sto cambiando. Forse c'è un modo migliore per risolvere la quantità di battitura con :%s? Uso spesso nomi di variabili descrittivi lunghi, quindi per me è davvero un grosso problema. Inoltre, non mi piace il modo in cui scrivere un nome di variabile da zero è incline a errori e può consumare tempo e potenza mentale a caccia di errori di battitura. Preferisco di gran lunga digitarlo una volta, quindi copiarlo e incollarlo per evitarlo del tutto se possibile.

Il mio flusso di lavoro attuale utilizza una combinazione di movimento / yank / select / search / put per spostarsi nel file e sostituirlo uno per uno. Non è eccezionale ma ha il vantaggio di evitare di digitare nomi di variabili complete. Potrei aver solo bisogno di digitare le prime lettere con /o utilizzare un altro comando di movimento (ad esempio fx) a seconda di cosa c'è intorno e quindi vepremere per selezionare l'intera parola. Inoltre, non mi dispiace dover ripetere per ogni istanza. Non faccio mai una sostituzione completa senza confermare ogni modifica. Ma sarebbe molto preferibile se potessi ripetere l'azione di sostituzione con un singolo tasto (cosa che non posso fare con questo metodo). ogni sostituzione è di solito qualcosa come nallora vepoi p(o peggio ancora "0p)

C'è un modo più veloce?


1
.ripete l'ultimo "blocco comandi" (non sono sicuro del termine esatto. ad esempio: sposta + azione + testo. per esempio: cwtoto<Esc>(cambia da cursore a fine parola con "toto"), c/foo<enter>bar<Esc>(cambia da cursore a appena prima " pippo ", e sostituisci con" barra "). Puoi quindi spostarti (con cursore, hjkl o un numero + hjkl (lo fa n volte), G (vai alla fine del file), / qualcosa (vai poco prima" qualcosa "), ecc.), quindi premere .per ripetere lo stesso" blocco comandi "
Olivier Dulac,

Lo uso abbastanza! Tuttavia, non funziona se si desidera strappare una parola e sostituirla con un'altra in più punti. Questo è il caso d'uso principale con cui ho un problema e va qualcosa del genere n ve "0p. Il che sfortunatamente non può essere replicato solo con un.
Joel

5
n ce ctrl-r 0 <esc> n.n.n.n.n.n.
Ricco

@Joel puoi anche registrare una macro (qa quindi tutto quello che vuoi, quindi q: crea la macro a), quindi usa: @a per riprodurla (e, come al solito, 134 @ a per farlo 134 volte). la macro può essere elaborata quanto basta (esempio: trova qualcosa, spostati nel posto giusto, quindi cambia parola in quello, quindi passa al posto giusto per poter essere richiamato in una posizione migliore)
Olivier Dulac,

1
Ho imparato trucchi VIM più utili da questa domanda che da qualsiasi altra domanda sul sito. Grazie!
dotancohen,

Risposte:


81

In realtà ho un flusso di lavoro abbastanza simile al tuo, (copiare e incollare blocchi simili, quindi usando :sper cambiare i nomi delle variabili) specialmente quando sto scrivendo molte righe simili ad eccezione di quale variabile usano. Ma ci sono un paio di cose che faccio che potresti trovare utili.

Cose vim generali

  1. La prima cosa che ti aiuterà è capire che :s//foo/griempirà qualsiasi termine di ricerca che hai usato l'ultima volta e lo sostituirà con pippo . Solo questo può far risparmiare molto tempo, ma quando lo si combina con il comando asterisco (cercare la parola sotto il cursore corrente) si risparmia tonnellate di tempo. Ad esempio, per cambiare tutto fooin spam, invece di farlo

    :%s/foo/spam/g
    

    Ciò richiede di digitare sia foo che spam (che possono essere variabili lunghe), puoi semplicemente navigare su foo e fare:

    *:%s//spam/g
    

    In questo modo, puoi ottenere rapidamente il nome della variabile che stai modificando cercandola, quindi non dovrai scriverlo. Funzionerà bene anche con il tuo attuale flusso di lavoro di utilizzo nmolto. È anche utile avere hlsearch, perché poi puoi dire visivamente dove sono le variabili che stai sostituendo.

    (nota a margine: il comando asterisco aggiungerà i confini delle parole, cioè \<e \>intorno alla parola sotto il cursore. Se non vuoi questo, usa g*invece.)

  2. sarebbe molto preferibile se potessi ripetere l'azione di sostituzione con un solo tasto

    È possibile utilizzare @:per ripetere l'ultimo comando sostitutivo. È bello se vuoi fare un :scomando su molte linee, ma non tutte, quindi :%snon funzioneranno. Un'altra opzione è la bandiera di conferma, cioè :%s///gc. Ciò ti consentirà di digitare yo nad ogni occorrenza per decidere se desideri sostituirlo o meno.

    Come ha sottolineato Desty , puoi anche eseguire &di nuovo l'ultimo comando sostitutivo, ma nota che questo non manterrà i tuoi flag (come globalo confirm). Che ciò sia positivo o negativo dipende dalla tua opinione personale. Se lo desideri, ma tieni anche le tue bandiere, puoi farlo

    nnoremap & :&&<cr>
    
  3. Se si desidera modificare ogni occorrenza in un blocco, ma non tutte le corrispondenze nel file, è sempre possibile utilizzare una selezione visiva del blocco, che riempirà

    :'<,'>
    

    al tuo sostituto, che lo limita alle linee che hai selezionato. Se combinato con l'approccio asterisco, lo trovo estremamente utile. Inoltre, se si stanno eseguendo più comandi sostitutivi sullo stesso blocco di linee, è possibile utilizzare gvper riselezionare le stesse linee per eseguire un altro comando. (che si tratti di un :substitutecomando normale o)

    Se si esegue un altro comando sostitutivo, non è necessario selezionare nuovamente le stesse righe, poiché :'<,'> continueranno a funzionare sulle stesse righe. Tuttavia, è più conveniente digitare

    gv:s
    

    piuttosto che

    :'<,'>s
    

Plugin / Mapping che mi piacciono

  1. Ho il seguente mapping nel mio .vimrcche estende l' approccio asterisco in modo che devo solo digitare il nuovo nome desiderato:

    "(R)eplace all
    nnoremap <leader>r yiw:%s/\<<C-r>"\>//g<left><left>
    

    Per usarlo, basta posizionare il cursore sulla parola che si desidera modificare e digitare <leader>rNewVarName<cr>e il gioco è fatto. Nota che questo sostituirà tutte le partite senza darti un'opzione per confermarle, quindi potresti non gradire così tanto. Certo, potresti semplicemente cambiarlo in

    nnoremap <leader>r yiw:%s/\<<C-r>"\>//gc<left><left><left>
    

    Per consentire l'opzione di conferma.

    modifica : Dopo sia la risposta della Messa che il commento di rcorre , potresti anche scrivere questo come

    nnoremap <leader>r :%s/\<<C-r><C-w>\>//g<left><left>
    

    Il che ha il vantaggio di mantenere lo stesso registro anonimo.

  2. Se stai rinominando gruppi di variabili simili contemporaneamente, ad esempio, cambiando

    fooSuffix
    PrefixFoo
    foo1
    SHOUTINGFOO
    

    per

    barSuffix
    PrefixBar
    bar1
    SHOUTINGBAR
    

    può essere doloroso sostituirli tutti individualmente. Qui è utile tpope / vim-abolish . Puoi sostituirli tutti in un solo comando con:

    :%S/foo/bar/g
    

    e si occupa della capitalizzazione per te.

Questi dovrebbero aiutarti molto, ma fammi sapere se c'è qualcosa in questi approcci che non ti piacciono o che senti che manchi. Sono felice di parlare di più approcci. :)


Lettura consigliata:


Oh, ho appena imparato diverse cose qui. Bello! Un approccio alternativo che potresti adottare è <leader>rc(o cr) aggiungerlo /calla fine. Inoltre, ho set gdefaultimpostato, perché non voglio quasi mai sostituire un singolo risultato, quindi sarebbe solo yiw:%s/\<<C-r>"\>//<left><left>
Wayne Werner,

2
Che risposta fantastica!
Scott Lundberg,

2
Se lo usi ctrl+r-ctrl+wnella mappatura (come suggerito da @Mass) dovrebbe funzionare allo stesso modo ma non interferirà con il tuo registro yank (che potrebbe essere o non essere desiderabile)
rcorre

1
Una cosa da ricordare quando si lavora con blocchi di testo evidenziati (prima di tagliare / strappare ecc.) È che è possibile inserire gv in modalità normale per evidenziare nuovamente l'ultimo testo evidenziato.

1
Ho scritto github.com/hauleth/sad.vim plugin che funziona in modo simile al metodo 2. hovewer ti consente di utilizzare .per eseguire ulteriori sostituzioni.
Hauleth,

20

La c_CTRL-Rfamiglia di mappe può migliorare un po 'il flusso di lavoro:

  1. Non è necessario digitare i nomi delle variabili durante l'utilizzo :s/, basta usare CTRL-R CTRL-Wper inserire la parola sotto il cursore nella riga di comando (avvertenza: questo non sfugge ai caratteri speciali, come *fa).

  2. Dopo aver apportato una piccola modifica utilizzando ce, è possibile cercare la vecchia parola utilizzando / CTRL-R -. Quindi utilizzare .per ripetere la modifica.

  3. Se è necessario apportare modifiche al modello di ricerca CTRL-R /, la ricerca esistente verrà inserita nella riga di comando.

  4. Infine, puoi mettere il contenuto di qualsiasi registro usando CTRL-R {register}


Buon punto! Dimentico sempre di Ctrl R. Vedrò se ci riesco anche io.
Gioele,

2
Vale la pena menzionare in modo specifico Ctrl-R /? Lo uso abbastanza frequentemente nei :scomandi se voglio usare un termine di ricerca quasi identico a quello precedente.
Ricco

12

Ci sono altri due metodi che (con mia grande sorpresa!) Non sono ancora stati menzionati.

Usando il gncomando

gnfunziona come il ncomando, tranne che oltre a saltare alla partita, entra in modalità visiva, con l'intera partita selezionata.

Quindi, per cambiare una parola (o qualsiasi cosa tu possa abbinare a un'espressione regolare!) Prima cerca per essa 1 , quindi premi cgnseguito dal testo con cui vuoi sostituire la corrispondenza e Escper tornare alla modalità normale.

(Si dice che a volte il testo sostitutivo che si desidera utilizzare è memorizzato nel "0registro yank - si noti che è possibile immetterlo rapidamente senza doverlo digitare nuovamente premendo Ctrl+R0in modalità di inserimento.)

È quindi possibile passare alla partita successiva e ripetere la modifica con una sola .pressione del tasto!

Se ci sono posti in cui non desideri effettuare la sostituzione, premi semplicemente unper annullare la modifica e passare alla partita successiva.

1: Rapidamente, utilizzando gli altri suggerimenti di questa pagina, come *, Ctrl+RCtrl+Wecc Vedi anche il commento di Peter Rincker per ulteriori suggerimenti su come compilare inizialmente la ricerca.

Utilizzando Seleziona modalità

(Ho imparato questo da romainl 's post sul subreddit vim . Non so se lui ha inventato se stesso o è stato solo di passaggio su.)

La modalità di selezione è un po 'simile alla modalità visiva, tranne se si inizia a digitare il testo selezionato che viene immediatamente sostituito con ciò che si digita (come il modo in cui la selezione funziona nella maggior parte degli altri editor).

È quindi possibile utilizzare questo trio di mappature per eseguire una ricerca / sostituzione molto rapida, che è possibile modificare a metà file, se necessario:

nnoremap § *``gn<C-g>
inoremap § <C-o>gn<C-g>
snoremap <expr> . @.

Mappatura modalità normale : attiva la modalità di selezione con la parola più vicina al cursore selezionato.

È quindi possibile digitare il sostituto per la parola selezionata e ( senza premere Esc) utilizzare:

Mappatura modalità inserimento : passa alla corrispondenza successiva ed entra in modalità selezione.

È quindi possibile digitare un nuovo sostituto o premere .per utilizzare:

Seleziona la modalità mapping : reinserisce l'ultimo testo inserito, essenzialmente ripete l'ultimo inserimento.


Ho menzionato il gnmetodo alla fine della mia risposta vi.stackexchange.com/a/13696/488 ieri per quello che vale! La tua risposta ha una formattazione molto più bella però.
TankorSmash,

1
@TankorSmash Così hai fatto! Nota come mi sono perso. Nota che il metodo effettivo che sto usando è diverso dal tuo, sebbene (invoco solo gnuna volta), ed è il metodo specifico (piuttosto che solo il gncomando) che mi ha sorpreso non era ancora stato menzionato.
Ricco

Consiglierei di avere una sorta di plugin "visual star" da usare con gn. Ti dà più opzioni di ricerca. Povero uomo è mappatura visiva stelle: xnoremap * :<c-u>let @/=@"<cr>gvy:let [@/,@"]=[@",@/]<cr>/\V<c-r>=substitute(escape(@/,'/\'),'\n','\\n','g')<cr><cr>. Episodio di Vimcasts correlati: Operare sulle partite di ricerca usando gn
Peter Rincker,

1
Ho creato plugin sad.vim che aiutano con tale flusso.
Hauleth,

7

Il plugin https://github.com/terryma/vim-multiple-cursors si occupa principalmente di questo problema.

Il mio flusso di lavoro è in genere il seguente:

  1. Spostare il cursore sulla parola da sostituire.
  2. Tenere premuto ctrl-nfino a quando tutto è selezionato.
  3. Premere ce iniziare a digitare la sostituzione.

La cosa comoda qui è che puoi anche fare operazioni più complicate della semplice ricerca-sostituzione, anche se trovo che se vai troppo in là, il plugin inizia a mostrare alcuni difetti.


4

Un altro modo di fare cose che non sono ancora state menzionate: è possibile utilizzare la finestra della riga di comando (aperta con q:in modalità normale - vedere :help q:per ulteriori informazioni), in cui si ha pieno accesso alle funzionalità di completamento automatico di Vim ( i_CTRL-N/ i_CTRL-Pe alle varie i_CTRL-Xsequenze essere tra questi).

Nella finestra della riga di comando puoi anche usare i comandi in modalità normale per modificare la riga di comando, il che, ad esempio, significa che puoi strappare e incollare parti di comandi precedenti nella cronologia della riga di comando.

Si noti che il cursore verrà spostato nella finestra della riga di comando quando lo si utilizza, il che significa che CTRL-R CTRL-We metodi simili che prevedono l'inserimento del testo sotto il cursore non funzioneranno lì, a differenza della normale riga di comando.


1
Puoi anche aprire la finestra della riga di comando quando sei già a metà immissione del :substitutecomando premendo<ctrl-f>
Rich

2

La soluzione a questo è in qualche modo tra le altre risposte per me:

Supponi di avere 6 linee identiche e che desideri sostituire "mela" nelle due centrali:

I have apples for days
I have apples for days
I have apples for days
I have apples for days
I have apples for days
I have apples for days

Quello che faccio è:

  1. Vengo all'inizio del testo che voglio sostituire "mela"
  2. Premi vper entrare in modalità visiva
  3. Vai all'ultima parola che voglio sostituire, in questo caso alcune righe così 2j
  4. :s/<C-R><C-W>/che utilizza la selezione visiva come intervallo per :s, quindi inserisce la parola sotto il cursore
  5. Ho colpito <C-F>che ti mette in una modalità in cui puoi modificare il tuo comando come se fosse un buffer. Quindi puoi utilizzare qualsiasi completamento o plugin desideri scegliere la parola sostitutiva e il gioco è fatto.

Tutto in una riga, il flusso per questo sarebbe /apples<CR>2nv/apples<CR>nn:s/<C-R><C-W/oranges<CR>ma che sembra peggio di quello che è.

Nel tuo caso, è possibile aggiungere il /cal :scomando per ignorare quelli che non si desidera, se l'intervallo contiene alcuni si desidera saltare: :s/<C-R><C-W/orange/c.

In alternativa (e forse più semplicemente), è possibile cercare l'intera parola che si desidera sostituire, modificarla c, quindi gne .ripetere la ricerca e applicare nuovamente la stessa modifica. Sembrerebbe /\<apple\><CR>viwcorange<ESC>gn.gn.gn..ngn.di ripeterlo su tre, quindi saltare un'istanza, quindi ripeterlo un'ultima volta.


Il metodo che descrivi alla fine non funziona per me (in Vim 7.4, invocato con vim -Nu NONE). Emette solo un segnale acustico quando premo il primo .. Qualche idea sul perché? In ogni caso, sarebbe più semplice utilizzare al ciwposto viwe ninvece di gnapportare modifiche in questo modo.
Ricco

2

Questo non è interamente correlato alla domanda, ma dovrebbe comunque aiutare a selezionare la parola da sostituire.

La ricerca incrementale ( set incsearch) ti consente di digitare il prefisso della parola che devi trovare e vim passerà automaticamente alla prima occorrenza del prefisso man mano che digiti sempre di più. La ricerca verrà confermata se si preme Invio e viene scartato su Esc (in quest'ultimo caso il cursore tornerà al punto in cui si trovava prima dell'inizio della ricerca).

Tuttavia, la ricerca incrementale fornisce un'altra funzionalità utile. Quando hai già digitato il prefisso di una parola e il cursore si trova nella posizione corretta, premi <C-L>per aggiungere caratteri dalla parola sotto il cursore alla stringa di ricerca. Ciò consente di inserire la stringa sostituita senza quasi premere i tasti.

Quindi, se voglio sostituire someOddVariableNamecon evenOdderVariableName, io

  1. digitare /someOddVe arrivare al verificarsi di someOddVariableName;
  2. tenere premuto <C-L>finché non someOddVariableNameappare completamente nella riga di ricerca;
  3. premere Invio per confermare la ricerca;
  4. :%s//evenOdderVariableName/go qualunque comando di sostituzione sia necessario; potresti voler seguire altre risposte qui.

2

Puoi prima inserire il nome della variabile nel buffer ( ywper copiare una parola), quindi copiarlo e incollarlo :%scolpendo <C-r>".


2

Se vuoi sostituire una variabile all'interno del nuovo blocco (incollato), ho due suggerimenti:

Sostituzione automatica

  • Vai alla prima riga del nuovo blocco, premi ma. Questo sta segnando una posizione e nominandola a.

  • Vai all'ultima riga del nuovo blocco, premi mb. Ora hai due posizioni nominate.

  • Ora sostituzione tra le due linee, in questo modo: :'a,'bs/oldVarName/newVarName/g.

  • Cioè, l'ambito della sostituzione è tra i due marchi.

Il modo manuale

  • Vai alla prima riga del nuovo blocco.

  • Utilizzare /oldVarNameper trovare la prima istanza di oldVarName.

  • Modificalo in questo modo:, cwnewVarName<Esc>che cambierà parola newVarNamee raggiungerà la fuga.

  • Utilizzare nper passare alla prossima istanza di oldVarName.

  • Utilizzare .per ripetere l'ultima modifica (ovvero ripeterla cw).


2

Molte delle altre risposte qui coprono già i modi integrati per migliorare il flusso di lavoro di ricerca e sostituzione, ma volevo menzionare abolish.vim , un plugin di Tim Pope. Effettua sostituzioni in cui si desidera coprire una gamma più ampia di casi, normalmente causati da variazioni nel caso o perché è necessario trattare sia forme singolari che plurali di una parola.

:S/child{,ren}/adult{,s}/gconverte correttamente childin adult, Childrenin Adultse CHILDin ADULT.

Se hai a che fare con parole in cammello, devi includere le lettere maiuscole nelle parole originali e di sostituzione.

%:S/wordWrap/textBreak/ggestisce correttamente sia wordWrape wordwrap.


0

Come altri hanno già indicato, se hai il cursore posizionato sopra la parola puoi usare la famiglia di comandi Ctrl + r, essendo Ctrl + r Ctrl + w probabilmente il più adatto da usare.

Questo metodo, tuttavia, funziona solo per inserire una parola (quella appena sotto il cursore), ma cosa succede se nel buffer ci sono diverse parti di testo che vogliamo usare? Non puoi muovere il cursore nella riga di comando. Il modo migliore per farlo è copiare parti di testo necessarie in diversi registri, cosa che puoi fare prefissando "{reg_letter} (citazione e la lettera dal registro) all'operatore di copia. In questo modo sarà possibile inserisci varie parole nella riga di comando in seguito con Ctrl + r {reg_letter}. Questo è un po 'imbarazzante ma funziona.

Ma tornando all'idea precedente, sarebbe grandioso spostare la posizione del cursore sul buffer durante la scrittura di un modello. In questo modo potremmo "scegliere" il testo da posizioni diverse con Ctrl + r Ctrl + w, quindi ho creato un plugin che fa proprio questo: EXtend.vim Il plugin ha anche molte altre funzionalità per effettuare operazioni di sostituzione.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.