Changer les noms de variables dans Vim

96

J'essaie de lire beaucoup de code C / Perl via Vim qui contient de nombreux noms de variables à une seule lettre.

Ce serait bien d'avoir une commande qui pourrait m'aider à changer le nom d'une variable en quelque chose de plus significatif pendant que je suis en train de lire le code afin que je puisse lire le reste plus rapidement.

Y a-t-il une commande dans Vim qui pourrait me permettre de le faire rapidement?

Je ne pense pas que les expressions régulières fonctionneraient parce que:

  1. le même nom de lettre unique peut avoir des objectifs différents dans différents blocs de portée

  2. la même combinaison de lettres pourrait faire partie d'un autre nom de var plus grand, ou dans une chaîne ou un commentaire ... ne voudrait pas les modifier

Des solutions connues?

Aditya Mukherji
la source

Réponses:

198

Voici comment renommer une variable qui est définie dans la portée actuelle {}.

Déplacez votre curseur sur l'utilisation variable. Appuyez sur gd. Ce qui signifie - déplacez le curseur vers la définition. Maintenant, appuyez sur [{- cela vous amènera au début de la portée. Appuyez sur V- pour activer la sélection de Visual Line. Appuyez sur %- pour sauter à l'opposé }et sélectionnera donc toute la portée. Appuyez sur :s/- démarrage de la commande de remplacement. <C-R>/- insérera un motif correspondant au nom de la variable (ce nom sur lequel vous étiez avant d'appuyer sur gd). /newname/gc<CR>- lancera la recherche et remplacera avec confirmation à chaque match.

Vous devez maintenant enregistrer une macros ou, mieux encore, mapper une clé.

Voici les mappages finaux:

" For local replace
nnoremap gr gd[{V%::s/<C-R>///gc<left><left><left>

" For global replace
nnoremap gR gD:%s/<C-R>///gc<left><left><left>

Mettez ceci à votre .vimrcou exécutez simplement. Après cette pression grsur la variable locale vous amènera à la :scommande où vous devez simplement entrer new_variable_nameet appuyer sur Enter.

Mykola Golubyev
la source
40
+1 pour avoir appris plus de 5 nouvelles astuces Vim que j'aurais dû connaître auparavant. Merci
Kenny Meyer
6
Remarque: gd ne fonctionne pas pour localiser la variable dans la portée immédiate de la définition en mode Javascript dans macvim. Cela m'amène à la première utilisation du nom de variable dans une fonction du fichier: (Si vous êtes dans une certaine portée et que vous voulez sélectionner cette portée, "va {" - "visual select around {" - est la voie à suivre Je pense.
Srikumar
3
Une version légèrement améliorée: gist.github.com/048616a2e3f5d1b5a9ad invite l'utilisateur, affiche l'ancien nom, restaure la position du curseur après le remplacement
Andy Ray
7
+1 très bon vimgineering (je pense que je viens d'inventer un nouveau terme). Remarque: en fait, j'avais besoin d'appuyer deux :fois sur deux points ( ). La première fois que j'ai appuyé dessus, j'ai vu :'<,'>. Si je tapais juste à s/partir de là, cela ne fonctionnait pas; J'ai dû taper un autre deux-points avant le s/.
Kelvin
6
Notez que vous n'avez pas besoin <C-R>de la commande de substitution, simplement en omettant la recherche, vim utilisera le dernier modèle recherché, qui dans ce cas a été créé à partir de la gdcommande. Il :s//<newname>/gcsuffira donc simplement de faire .
shangxiao
10

AFAIK, il n'y a pas de support de refactoring réel dans VIM. Lorsque vous effectuez un changement de nom avec l'intention d'un refactor, je prends généralement les précautions suivantes:

  1. Limitez la portée du changement mes marques d'utilisation.
  2. Lors de la saisie de l'expression régulière, placez le nom entre \ <et>. Cela le fera correspondre à un mot entier, ce qui réduit les types de changements de noms incorrects qui se produiront.
  3. Ne faites pas de remplacement multiligne pour réduire les chances d'un mauvais remplacement
  4. Regardez attentivement le code diff si c'est autre chose qu'un petit changement.

Mon changement de fin ressemble à quelque chose comme ça

:'a,'bs/\<foo\>/bar

J'adorerais me tromper sur le fait qu'il n'y a pas d'outil de refactoring pour VIM mais je ne l'ai pas vu.

JaredPar
la source
En Perl, vous pouvez également ajouter le type au modèle de recherche. ie $ pour les scalaires, @ pour les tableaux,% pour les hachages
Nathan Fellman
@Nathan: malheureusement, c'est un peu plus difficile en Perl (5.X), car le sigil des tableaux et des hachages change avec l'usage:% hash -> hash entier, $ hash {key} -> single value, @hash { tranche de hachage qw / ab /}.
user55400
10

Je sais que c'est une vieille question, et la manière de @ mykola-golubyev EST évidemment la meilleure réponse pour le cas particulier de la question OP (qui, je suppose, passe par un code obscurci où vous êtes susceptible d'avoir plusieurs blocs avec les mêmes noms de var) ; mais avec le nom de la question comme celui-là, de nombreuses personnes qui viennent ici à partir de recherches Google recherchent probablement des moyens moins spécifiques à la situation pour renommer les variables dans VIM - et ceux-ci peuvent être plus concis

Je suis surpris que personne n'ait suggéré de cette façon:

* :s// NOUVEAU NOM /gc

Le *est le même que gn- il recherche l'occurrence suivante du mot sous le curseur ET il devient le dernier modèle recherché, donc lorsque vous omettez le modèle de recherche dans la commande de remplacement, VIM suppose que c'est le modèle à rechercher.

Pour de petites quantités de copies var, une copie encore plus rapide:

* cw NEWNAME <esc> puis répétez n.pour d'autres occurrences

Recherche d'occurrence, cwcommande de changement de mot , npasse à l'occurrence suivante du dernier terme recherché et .répète la dernière commande (qui est de changer le mot en NEWNAME )

(Crédits pour moi sachant tout cela, allez à @doomedbunnies sur Reddit )

Une autre astuce intéressante est la suivante: ( crédits à @ nobe4 )

* cgn NEWNAME <esc> puis répétez .pour d'autres occurrences

cgnest "changer tout ce qui est le résultat de (trouver l'occurrence suivante)". Maintenant que c'est la dernière commande , vous n'avez pas besoin nde passer à l'occurrence suivante, donc moins de coups à nouveau, et, plus important encore, pas besoin d'alterner net. . Mais, évidemment, celui-ci présente l'inconvénient de ne pas avoir de moyen de sauter une occurrence.

Voici quelques avantages:

  • pas de mappage, pas de .vimrc (ou init.vim), vous pouvez donc l'utiliser dans n'importe quelle copie VIM que vous rencontrez (par exemple, une tâche rapide sur certains VPS ou la machine de votre ami où la configuration de VIM à votre manière irait à l'encontre de l'objectif de `` rapide '' )
  • en utilisant *ougn pour la sélection de mots est très rapide - une seule touche (enfin, disons 1,5)
  • en utilisant *ou gns'assure que vous n'obtenez aucune correspondance dans d'autres mots, tout comme le :%s/<C-R>//gcfait. Beats en tapant le :%s/\<OLDNAME\>/NEWNAME/gcà la main: personnellement, j'ai tendance à oublier d'utiliser le\< choses pour limiter les correspondances à des mots entiers uniquement.
  • Ne pas utiliser la lunette entraînera seulement quelques coups supplémentaires npour sauter les matchs indésirables - probablement encore moins que les coups supplémentaires nécessaires pour limiter la portée. Dans des circonstances normales, vos variables sont probablement quelque peu localisées dans un certain bloc de code de toute façon.
Ivan Bartsov
la source
8

Mettez ceci dans votre .vimrc

" Function to rename the variable under the cursor
function! Rnvar()
  let word_to_replace = expand("<cword>")
  let replacement = input("new name: ")
  execute '%s/\(\W\)' . word_to_replace . '\(\W\)/\1' . replacement . '\2/gc'
endfunction

Appelez-le avec :call Rnvar()

expand("<cword>")obtient le mot sous le curseur. Les utilisations de chaîne de recherche %pour le fichier entier , et les \(\W\)motifs recherchent des caractères non-mot à la limite du mot à remplacer, et de les enregistrer dans des variables \1et de \2manière à être réinséré dans le modèle de remplacement.

Mike
la source
3

Vous pouvez utiliser le modificateur «c» dans la recherche globale et remplacer qui vous demandera une confirmation pour chaque remplacement. Cela prendrait plus de temps, mais cela pourrait fonctionner pour un fichier de code non énorme:

%s/\$var/\$foo/gc

Le c signifie confirmer.

Adnan
la source
Le i signifie ignorer la casse. Vous voulez c, confirmez chaque changement.
Michael Kristofik
oups ... désolé pour ça ... Correction de la réponse
Adnan
Réponse toujours pas complètement corrigée;)
user55400
vouliez-vous dire les $ échappés? C'est quelque chose qui est une de mes préférences personnelles. $ fonctionne sans évasion et aussi avec et puisque dans le monde des regex et dans vi $ a une signification particulière, j'aime y échapper juste pour plus de clarté.
Adnan
0

En c, vous pourrez peut-être faire des progrès en utilisant cscope. Il tente de comprendre la syntaxe, donc aurait une chance de savoir quand la lettre était une variable.

Kim Reece
la source
0

S'il s'agit de plusieurs fichiers, vous pouvez envisager de jeter un œil à sed. Utilisez find pour récupérer vos fichiers et xargs plus sed pour un remplacement. Supposons que vous souhaitiez remplacer a par a_better_name dans tous les fichiers correspondant à * .c, vous pouvez faire

trouver . -nom "* .c" | xargs sed -i -e 's / a / a_better_name / g'

Gardez à l'esprit que cela remplacera TOUTES les occurrences de a, vous voudrez peut-être une regex plus robuste.

Drew Olson
la source