Comment trouver et remplacer dans Vim sans avoir à taper le mot d'origine?

53

J'aimerais optimiser mon flux de travail "trouver et remplacer" dans Vim. C'est quelque chose que je fais souvent, comme la plupart d'entre vous, j'en suis sûr. Généralement, quelque chose comme: copie un bloc et change le nom d'une variable à quelques endroits. Je sais, je sais, cela déclenche probablement votre réflexe "pourquoi copiez-vous et collez-vous du code", mais n'allons pas dans cette voie ... Il existe de nombreux cas d'utilisation valables :)

Je connais bien les commandes de recherche et de remplacement: :sou, :%smais je ne les aime pas. Cela me force à taper à la fois le nom complet de la variable que je recherche et son changement. Peut-être y a-t-il une meilleure façon de régler le nombre de dactylographies :%s? J'utilise souvent des noms de variable descriptifs longs, ce qui est donc vraiment un facteur décisif pour moi. Je n'aime pas non plus que le fait de taper un nom de variable à partir de zéro soit sujet aux fautes de frappe et puisse consommer du temps et de la cervelle à la recherche de fautes de frappe. Je préfère de loin taper une fois, puis copier-coller pour éviter cela, si possible.

Mon flux de travail actuel utilise une combinaison de mouvement / tirer / sélectionner / rechercher / mettre pour déplacer le fichier et le remplacer un par un. Ce n'est pas génial, mais a l'avantage d'éviter de taper des noms de variables complets. Je pourrais juste avoir besoin de taper les premières lettres avec /ou d'utiliser une autre commande de mouvement (par exemple fx) en fonction de ce qui est autour, puis appuyez sur vepour sélectionner le mot entier. Cela ne me dérange pas aussi de devoir répéter chaque fois. Je ne fais jamais un remplacement complet sans confirmer chaque changement. Mais il serait bien préférable que je répète l’action de remplacement avec une seule frappe (ce que je ne peux pas faire avec cette méthode). chaque remplacement est généralement quelque chose comme nalors vealors p(ou pire encore "0p)

Y at-il un moyen plus rapide?

Joel
la source
1
.répète le dernier "bloc de commande" (pas sûr du terme exact. Par exemple: move + action + text. par exemple: cwtoto<Esc>(changement du curseur à la fin du mot avec "toto"), c/foo<enter>bar<Esc>(changement du curseur juste avant " foo ", et remplacez par" bar "). vous pouvez alors vous déplacer (avec le curseur, hjkl ou un nombre + hjkl (le fait n fois), G (aller à la fin du fichier), / quelque chose (aller juste avant" quelque chose "), etc) puis appuyez sur .pour refaire le même" bloc de commande "
Olivier Dulac
Je l'utilise un peu! Cependant, cela ne fonctionne pas si vous voulez tirer un mot puis le remplacer par un autre mot à plusieurs endroits. C'est le principal cas d'utilisation avec lequel j'ai un problème, et va quelque chose comme n ve "0p. Ce qui malheureusement ne peut pas être reproduit avec juste un.
Joel
5
n ce ctrl-r 0 <esc> n.n.n.n.n.n.
Rich le
@Joel vous pouvez aussi enregistrer une macro (qa puis tout ce que vous voulez, puis q: crée la macro a), puis utiliser: @a pour la rejouer (et, comme d'habitude, 134 @ a pour le faire 134 fois). la macro peut être aussi élaborée que nécessaire (exemple: trouver quelque chose, déplacer au bon endroit, puis changer de mot, puis aller au bon endroit pour pouvoir être rappelé dans une meilleure position)
Olivier Dulac
1
J'ai appris plus d'astuces VIM utiles de cette question que de toute autre question sur le site. Je vous remercie!
dotancohen

Réponses:

81

J'ai en fait un flux de travail assez similaire au vôtre (copier et coller des blocs similaires, puis utiliser :spour modifier les noms de variables), en particulier lorsque j'écris beaucoup de lignes similaires, à l'exception de la variable qu'elles utilisent. Mais il y a quelques choses que je fais qui pourraient vous être utiles.

Choses vim général

  1. La première chose qui vous aidera à comprendre est de :s//foo/gremplir le dernier terme de recherche que vous avez utilisé et de le remplacer par foo . Cela seul peut économiser beaucoup de temps, mais lorsque vous le combinez avec la commande astérisque (recherche du mot sous le curseur actuel), vous gagnez un temps précieux. Par exemple, pour tout changer fooen spam, au lieu de faire

    :%s/foo/spam/g
    

    Ce qui vous oblige à taper foo et spam (qui peuvent être de longues variables), vous pouvez simplement naviguer vers foo et faire:

    *:%s//spam/g
    

    De cette façon, vous pouvez obtenir rapidement le nom de la variable que vous modifiez en la recherchant, sans avoir à la saisir. Cela fonctionnera également bien avec votre flux de travail actuel de nbeaucoup d’utilisation . C'est également utile hlsearch, car vous pouvez ainsi visualiser où se trouvent les variables que vous remplacez.

    (Note: La commande astérisque ajoutera les limites de mots, qui est \<et \>autour du mot sous le curseur Si vous ne voulez pas cela, utilisez. à la g*place.)

  2. il serait bien préférable que je répète l'action de remplacement en une seule frappe

    Vous pouvez utiliser @:pour refaire la dernière commande de substitution. C'est bien si vous voulez faire une :scommande sur beaucoup de lignes, mais toutes :%sne fonctionneront donc pas. Une autre option est le drapeau de confirmation, à savoir :%s///gc. Cela vous permettra de taper you nà chaque occurrence de décider si vous souhaitez la remplacer ou non.

    Comme Desty l’a souligné , vous pouvez également &exécuter de nouveau la dernière commande de substitution, mais notez que cela ne conservera pas vos indicateurs (tels que globalou confirm). Que cela soit bon ou mauvais dépend de votre opinion personnelle. Si vous souhaitez utiliser ceci mais que vous le gardiez aussi, vous pouvez le faire.

    nnoremap & :&&<cr>
    
  3. Si vous souhaitez modifier toutes les occurrences d'un bloc, mais pas toutes les correspondances du fichier, vous pouvez toujours utiliser une sélection visuelle du bloc, qui sera remplie.

    :'<,'>
    

    à votre remplaçant, ce qui le limite aux lignes que vous avez sélectionnées. Combiné à l'approche astérisque, je trouve cela extrêmement utile. En outre, si vous effectuez plusieurs commandes de substitution sur le même bloc de lignes, vous pouvez utiliser gvpour resélectionner les mêmes lignes afin d'effectuer une autre commande. (que ce :substitutesoit une commande ou une commande normale)

    Si vous faites une autre commande de substitution, vous n'avez pas besoin de resélectionner les mêmes lignes, car :'<,'> elles fonctionneront toujours sur les mêmes lignes. Cependant, il est plus pratique de taper

    gv:s
    

    plutôt que

    :'<,'>s
    

Plugins / Mappings J'aime

  1. J'ai le mappage suivant dans mon .vimrcqui étend l' approche astérisque de sorte que je n'ai qu'à taper le nouveau nom souhaité:

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

    Pour l'utiliser, placez simplement votre curseur sur le mot que vous souhaitez modifier, puis tapez <leader>rNewVarName<cr>et le tour est joué. Notez que cela remplacera toutes les correspondances sans vous donner l'option de les confirmer, vous pourriez donc ne pas l'aimer autant. Bien sûr, vous pouvez simplement le changer en

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

    Pour permettre l'option de confirmation.

    edit : Suite à la fois à la réponse de la messe et au commentaire de rcorre , vous pouvez aussi écrire ceci comme

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

    Ce qui a l’avantage de garder le même registre que votre nom non nommé.

  2. Si vous renommez des groupes de variables similaires en même temps, par exemple, changez

    fooSuffix
    PrefixFoo
    foo1
    SHOUTINGFOO
    

    à

    barSuffix
    PrefixBar
    bar1
    SHOUTINGBAR
    

    il peut être difficile de les remplacer individuellement. C’est là que tpope / vim-abolish est utile. Vous pouvez remplacer tous ces éléments en une seule commande par:

    :%S/foo/bar/g
    

    et il s’occupe de la capitalisation pour vous.

Celles-ci devraient vous aider beaucoup, mais laissez-moi savoir s'il vous manque quelque chose à propos de ces approches. Je suis heureux de parler de plus d'approches. :)


Lecture recommandée:

DJMcMayhem
la source
Oh, je viens d'apprendre plusieurs choses ici. Agréable! Une autre approche que vous pouvez adopter est <leader>rc(ou cr) d’ajouter cela /cà la fin. De plus, je suis set gdefaultpartant, car je ne veux presque jamais remplacer un seul résultat, alors ce serait juste yiw:%s/\<<C-r>"\>//<left><left>
Wayne Werner
2
Quelle réponse fantastique!
Scott Lundberg
2
Si vous utilisez ctrl+r-ctrl+wle mappage (comme suggéré par @Mass), il devrait fonctionner de la même manière, mais ne gênera pas votre registre de yank (ce qui peut être souhaitable ou non)
rcorre le
1
Une chose à garder à l'esprit lorsque vous travaillez avec des blocs de texte que vous mettez en surbrillance (avant de couper / tirer, etc.) est que vous pouvez entrer dans gv en mode normal pour souligner à nouveau le dernier texte en surbrillance.
1
J'ai écrit le plugin github.com/hauleth/sad.vim qui fonctionne de la même manière que la méthode 2. hovewer vous permet .d'effectuer d'autres substitutions.
Hauleth
20

La c_CTRL-Rfamille de cartes peut considérablement améliorer votre flux de travail:

  1. Vous n’avez pas besoin de taper de nom de variable lors de l’utilisation :s/, vous n'avez CTRL-R CTRL-Wqu’à insérer le mot sous le curseur dans la ligne de commande (attention: cela n’échappe pas aux caractères spéciaux, comme le *fait le cas).

  2. Après avoir effectué une petite modification en utilisant ce, vous pouvez rechercher l'ancien mot en utilisant / CTRL-R -. Puis utilisez .pour répéter le changement.

  3. Si vous devez modifier le modèle de recherche, CTRL-R /la recherche existante sera placée dans la ligne de commande.

  4. Enfin, vous pouvez mettre le contenu de tout registre en utilisant CTRL-R {register}

Masse
la source
Bon point! J'oublie toujours Ctrl R. Je vais voir si je peux y travailler aussi.
Joel
2
À mentionner spécifiquement Ctrl-R /? J'utilise cela assez souvent dans les :scommandes si je veux utiliser un terme de recherche qui est presque mais pas tout à fait le même que le précédent.
Rich le
12

Il y a encore quelques méthodes qui (à ma grande surprise!) N'ont pas encore été mentionnées.

Utiliser la gncommande

gnfonctionne comme la ncommande, sauf qu’en plus de sauter au match, il entre en mode visuel, avec tout le match sélectionné.

Donc, pour changer un mot (ou tout ce que vous pouvez faire correspondre à une expression régulière!), Commencez par chercher 1 , puis appuyez sur cgnsuivi du texte avec lequel vous souhaitez remplacer la correspondance et Escpour revenir au mode normal.

(Vous indiquez que le texte de remplacement que vous souhaitez utiliser est parfois stocké dans le "0registre yank. Notez que vous pouvez le saisir rapidement sans avoir à le ressaisir en appuyant sur Ctrl+R0en mode insertion.)

Vous pouvez ensuite passer au match suivant et répéter le changement en .appuyant simplement sur une touche!

S'il y a des endroits où vous ne voulez pas faire le remplacement, appuyez simplement sur unpour annuler la modification et passer au match suivant.

1: Rapidement, en utilisant les autres suggestions de cette page, telles que *, Ctrl+RCtrl+Wetc. Voir aussi le commentaire de Peter Rincker pour plus de suggestions sur la manière de remplir initialement votre recherche.

Utiliser le mode de sélection

(Je l' ai appris de romainl de post sur le subreddit vim . Je ne sais pas s'il a inventé lui - même ou était juste de le transmettre.)

Le mode de sélection est un peu comme le mode visuel, sauf si vous commencez à taper le texte sélectionné est immédiatement remplacé par ce que vous tapez (comme le mode de sélection dans la plupart des autres éditeurs).

Vous pouvez donc utiliser ce trio de mappages pour effectuer une recherche / remplacement très rapide, que vous pourrez modifier si nécessaire:

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

Mappage en mode normal : entre le mode de sélection avec le mot le plus proche du curseur sélectionné.

Vous pouvez ensuite taper votre remplacement pour le mot sélectionné et ( sans appuyer Esc) utiliser le:

Mappage en mode insertion : passe à la correspondance suivante et passe en mode sélection.

Vous pouvez ensuite taper un nouveau remplacement ou appuyer sur .pour utiliser le:

Mappage de mode de sélection : Rentre le dernier texte inséré - essentiellement, répète la dernière insertion.

Riches
la source
Je l' ai mentionné la gnméthode à la fin de ma réponse vi.stackexchange.com/a/13696/488 hier pour ce que ça vaut! Votre réponse a cependant une mise en forme beaucoup plus agréable.
TankorSmash
1
@ TankorSmash Donc vous l'avez fait! Notez bien comment j'ai raté ça. Notez que la méthode que j'utilise est différente de la vôtre (je n'invoque gnqu'une seule fois) et que c'est la méthode spécifique (plutôt que la gncommande) qui m'a surpris, n'avait pas encore été mentionnée.
Rich le
Je recommanderais d'avoir une sorte de plugin "visual star" à utiliser avec gn. Vous donne plus d'options de recherche. Pauvre homme est la cartographie visuelle étoiles: xnoremap * :<c-u>let @/=@"<cr>gvy:let [@/,@"]=[@",@/]<cr>/\V<c-r>=substitute(escape(@/,'/\'),'\n','\\n','g')<cr><cr>. Épisode de Vimcasts lié: Opération sur les correspondances de recherche en utilisant gn
Peter Rincker
1
J'ai créé le plugin sad.vim qui aide avec un tel flux.
Hauleth
7

Le plugin https://github.com/terryma/vim-multiple-cursors s’occupe en grande partie de ce problème.

Mon flux de travail est généralement comme suit:

  1. Déplacez le curseur sur le mot à remplacer.
  2. Tenez ctrl-njusqu'à ce que tout soit sélectionné.
  3. Appuyez sur cet commencez à taper le remplacement.

La chose pratique ici est que vous pouvez également faire des opérations plus compliquées que la simple recherche-remplacement, bien que je trouve que si vous allez trop loin, le plug-in commence à montrer quelques problèmes.

Chiel ten Brinke
la source
4

Une autre façon de faire des choses qui n’ont pas encore été mentionnées: vous pouvez utiliser la fenêtre de ligne de commande (ouverte avec q:en mode normal - :help q:pour plus d’informations), dans laquelle vous avez un accès complet aux fonctions de complétion automatique de Vim ( i_CTRL-N/ i_CTRL-Pet aux différentes i_CTRL-Xséquences être parmi eux).

Dans la fenêtre de ligne de commande, vous pouvez également utiliser des commandes en mode normal pour modifier la ligne de commande, ce qui signifie par exemple que vous pouvez extraire et coller des parties de commandes précédentes de votre historique de ligne de commande.

Notez que votre curseur sera déplacé vers la fenêtre de ligne de commande lors de son utilisation, ce qui signifie que CTRL-R CTRL-Wles méthodes similaires qui impliquent l'insertion de texte sous le curseur ne fonctionneront pas là, contrairement à la ligne de commande normale.

Slade
la source
1
Vous pouvez également ouvrir la fenêtre de ligne de commande lorsque vous êtes déjà à mi-chemin de votre :substitutecommande en appuyant sur<ctrl-f>
Rich
2

La solution à cela est un peu entre les autres réponses pour moi:

Supposons que vous ayez 6 lignes identiques et que vous souhaitiez remplacer «pomme» par deux au centre:

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

Ce que je fais c'est:

  1. J'arrive au début du texte que je veux remplacer, "pomme"
  2. Frapper vpour entrer en mode visuel
  3. Descendez au dernier mot que je veux remplacer, dans ce cas quelques lignes afin 2j
  4. :s/<C-R><C-W>/qui utilise la sélection visuelle comme plage pour :s, puis insère le mot sous le curseur
  5. Je frappe <C-F>ce qui vous met dans un mode où vous pouvez éditer votre commande comme s'il s'agissait d'un tampon. Ainsi, vous pouvez utiliser la complétion ou les plugins pour lesquels vous souhaitez choisir le mot de remplacement, et vous avez terminé.

Tout en une ligne, le flux pour ce serait /apples<CR>2nv/apples<CR>nn:s/<C-R><C-W/oranges<CR>mais cela semble pire que ce qu'il est.

Dans votre cas, vous pouvez ajouter la /cà la :scommande pour ignorer ceux que vous ne voulez pas, si votre gamme contient quelques - unes que vous voulez sauter: :s/<C-R><C-W/orange/c.

Sinon (et peut - être plus simplement), vous pouvez rechercher le mot entier que vous voulez remplacer, cHänge, puis gnet .de répéter la recherche et appliquer le même changement à nouveau. Cela ressemblerait /\<apple\><CR>viwcorange<ESC>gn.gn.gn..ngn.à le répéter trois fois, puis à ignorer une instance, puis à le répéter une dernière fois.

TankorSmash
la source
La méthode que vous décrivez à la fin ne fonctionne pas pour moi (dans Vim 7.4, invoqué avec vim -Nu NONE). Il émet un bip lorsque j'appuie sur le premier .. Une idée pourquoi c'est? Dans tous les cas, il serait plus simple d'utiliser ciwau lieu de viwet nau lieu d' gnapporter des modifications de cette manière.
Rich le
2

Ceci n’est pas entièrement lié à la question, mais devrait néanmoins aider à sélectionner le mot à remplacer.

La recherche incrémentale ( set incsearch) vous permet de taper le préfixe du mot à rechercher et vim passera automatiquement à la première occurrence du préfixe au fur et à mesure de la frappe. La recherche sera confirmée si vous appuyez sur Entrée et est rejetée sur Échap (dans ce dernier cas, le curseur reviendra à l'endroit où il se trouvait avant le début de la recherche).

Cependant, la recherche incrémentielle offre une fonctionnalité supplémentaire utile. Lorsque vous avez déjà saisi le préfixe d'un mot et que le curseur est à la bonne position, appuyez sur <C-L>pour ajouter des caractères du mot sous le curseur à la chaîne de recherche. Cela vous permet de saisir la chaîne remplacée presque sans appuyer sur une touche.

Donc, si je veux remplacer someOddVariableNamepar evenOdderVariableName, je

  1. taper /someOddVet arriver à l'apparition de someOddVariableName;
  2. maintenez enfoncé <C-L>jusqu'à ce que someOddVariableNameapparaisse complètement dans la ligne de recherche;
  3. appuyez sur Entrée pour confirmer la recherche;
  4. :%s//evenOdderVariableName/gou selon la commande de remplacement dont vous avez besoin; vous voudrez peut-être suivre d'autres réponses ici.
Ivan Smirnov
la source
2

Vous pouvez commencer par mettre le nom de la variable dans le tampon ( ywpour copier un mot), puis le copier-coller :%sen appuyant sur <C-r>".

vdudouyt
la source
2

Si vous souhaitez remplacer une variable dans le nouveau bloc (collé), j'ai deux suggestions:

Remplacement automatisé

  • Allez à la première ligne du nouveau bloc, appuyez sur ma. Ceci marque un emplacement et le nomme a.

  • Allez à la dernière ligne du nouveau bloc, appuyez sur mb. Vous avez maintenant deux emplacements nommés.

  • Maintenant , remplacer entre les deux lignes, comme suit: :'a,'bs/oldVarName/newVarName/g.

  • C'est-à-dire que la portée du remplacement est comprise entre vos deux marques.

La manière manuelle

  • Aller à la première ligne du nouveau bloc.

  • Utilisez /oldVarNamepour trouver la première instance de oldVarName.

  • Changez-le comme suit:, cwnewVarName<Esc>ce qui changera de mot newVarNameet vous échapperez.

  • Utilisez npour aller à la prochaine instance de oldVarName.

  • Utilisez .pour répéter le dernier changement (c'est-à-dire le répéter cw).

Invité caché
la source
2

Beaucoup des autres réponses ici couvrent déjà les moyens intégrés pour améliorer le flux de travail de recherche et remplacement, mais je voulais mentionner abolish.vim , un plugin de Tim Pope. Il effectue des remplacements lorsque vous souhaitez couvrir un plus grand nombre de cas, généralement dus à des variations de cas ou parce que vous devez traiter à la fois les formes au singulier et au pluriel.

:S/child{,ren}/adult{,s}/gconvertit correctement childvers adult, Childrenvers Adultset CHILDvers ADULT.

Si vous avez affaire à des mots traités à la chamelle, vous devez inclure les majuscules où vous en avez besoin dans les mots d'origine et de remplacement.

%:S/wordWrap/textBreak/ggère correctement les deux wordWrapet wordwrap.

Haegin
la source
0

Comme d'autres l'ont déjà souligné, si vous avez le curseur placé au-dessus de votre mot, vous pouvez utiliser la famille de commandes Ctrl + r. Ctrl + r Ctrl + w est probablement la plus appropriée.

Cette méthode, cependant, ne fonctionne que pour insérer un mot (celui juste en dessous du curseur), mais que se passe-t-il si il y a plusieurs portions de texte dans la mémoire tampon que nous voulons utiliser? Comme vous ne pouvez pas déplacer votre curseur en ligne de commande. Pour ce faire, le meilleur moyen est de copier des portions de texte dont vous aurez besoin dans différents registres, ce que vous pouvez faire en ajoutant le préfixe "{reg_letter}" à l'opérateur de copie. insérer différents mots dans la ligne de commande plus tard avec Ctrl + r {reg_letter} Ceci est un peu gênant mais fonctionne.

Mais revenons à l’idée précédente, ce serait bien de déplacer la position du curseur sur le tampon lors de l’écriture d’un motif. De cette façon, nous pourrions "sélectionner" du texte à partir de différentes positions avec Ctrl + r Ctrl + w, j'ai donc créé un plugin qui remplit ce but: EXtend.vim Le plugin dispose également de nombreuses autres fonctionnalités permettant d'effectuer des opérations de substitution.

Saul Axel Martinez Ortiz
la source