Vim: s remplace les premières occurrences N <g sur une ligne

13

Dans vim, j'ai parfois l'occasion de remplacer les premières occurrences d'un match sur une ligne, mais pas tout le monde le gvoudrait. par exemple:

a a a a a

à

b b b a a

Je sais que je pourrais utiliser :s/a/b/[enter]:[up][enter]:[up][enter], mais c'est assez fastidieux à trois répétitions, j'ai des lignes avec potentiellement plus de 10 substitutions.
J'ai essayé:

  • :s/a/b/3g: vim s'est plaint de caractères de fin.
  • :s/a/b/3: modifie la première occurrence sur cette ligne et les deux lignes suivantes.
  • 3:s/a/b: identique à la précédente.
  • :s/a/b/g3: change toutes les occurrences sur cette ligne et les deux suivantes.
  • :3s/a/b: modifie la première occurrence sur la ligne 3.
  • :/a/,3/a/s/a/b: change la première occurrence sur chaque ligne entre la suivante aet la troisième ligne contenant adans le fichier (invite à inverser si nécessaire).
  • :/a/,/\([^a]*a\)\{3\}/s/a/b/: modifie la première occurrence sur chaque ligne entre celle-ci et la suivante avec 3 as dessus (et cela n'aurait pas été facilement extensible à une recherche à plusieurs caractères).

Et divers autres modèles d'adressage, dont aucun n'a fonctionné. Je dois dire que j'ai beaucoup appris sur la :scommande essayant de trouver une réponse à ce problème, mais je ne l'ai toujours pas résolu.

Quelqu'un sait-il comment faire ça?

(points bonus pour une plage spécifique, par exemple de la deuxième à la quatrième occurrence)

Kevin
la source
2
Je suis sûr que vous ne pouvez pas faire ça dans vim, mais pour le rendre moins fastidieux, connaissez-vous "n" et "." en mode visuel? Autrement dit, vous utilisez / pattern / pour trouver la chose à changer, changez-la en utilisant "cw" ou autre chose, puis utilisez "n" (juste n, pas de guillemets) pour trouver le modèle suivant et appuyez sur "." (juste période, pas de guillemets) pour effectuer la dernière modification.
Bruce Ediger
@BruceEdiger que je connaissais net ., même si je ne pensais pas les utiliser ici. Certainement une amélioration, merci.
Kevin
s/a/=something/devrait faire l'affaire ( :help sub-replace-=). Je ne parle pas assez bien à Vim pour écrire somethingdès le départ.
Gilles 'SO- arrête d'être méchant'
Remarque: :[up][enter]peut être remplacé par &, ce qui n'est toujours pas idéal mais au moins est moins douloureux.
Kowh

Réponses:

10

En s'appuyant sur l' :s/pattern/replacement/gc idée de Samus_ (qui semble être le moyen le plus simple d'assurer un fonctionnement correct lorsque le modèle est contenu dans la chaîne de remplacement), pour remplacer les 2e à 4e occurrences sur une seule ligne:

:call feedkeys("nyyyq") | s/pat/string/gc

feedkeys()est une fonction qui place la chaîne d'entrée dans la file d'attente d'entrée du clavier. Le but est de faire le décompte à l'avance afin de ne pas avoir à vous soucier de perdre le décompte ou d'être interrompu.

Pour un cas plus général, pour remplacer les Mth à Nth occurrences sur une seule ligne pour N supérieur ou égal à un très grand M :

:call feedkeys(repeat("n", M-1) . repeat("y", N-M+1) . "q") | s/pat/string/gc

Remplacez M et N par les valeurs que vous voulez (vous pouvez même laisser vimfaire l'arithmétique mentale triviale si vous ne voulez pas le faire vous-même). Notez qu'il .s'agit de l'opérateur de concaténation de chaînes de VimL. Évidemment , cela ne sauve que les frappes de grande M . Si vous utilisez fréquemment cette fonctionnalité, cela peut vous faire gagner du temps pour mettre ce qui précède dans une commande ou une fonction personnalisée, car elle est assez longue à taper.

jw013
la source
Hmm. J'aime celui la. Je devrais aussi pouvoir écrire une fonction.
Kevin
8

Pour la première question que je ferais:

:s/a/b
&&

La seconde est plus délicate, je ne sais pas comment le faire automatiquement mais vous pouvez faire en sorte que vim vous invite à chaque match comme ceci:

:s/a/b/gc

Ensuite, vous répondez «non» aux n premières correspondances et «oui» aux autres.

Samus_
la source
Hmm, j'ai oublié c, cela pourrait être la meilleure solution proposée à ce jour. Je devrais toujours compter, mais je pense que c'est la première option qui fonctionnerait avec les remplacements qui contiennent la chaîne de recherche.
Kevin
6
a a a a a
a a a a a
a a a a a
a a a a a
a a a a a
a a a a a
a a a a a

:3,6g/^/let i=0 | while i<3 | s/a/b/ | let i+=1 | endwhile

a a a a a
a a a a a
b b b a a
b b b a a
b b b a a
b b b a a
a a a a a
kev
la source
1
C'est bien, mais cela souffre toujours du problème que Gilles a signalé (sur un autre post, qui semble avoir été supprimé depuis), que cela ne fonctionne que si le remplacement ne contient pas le motif. Bien que j'aime l'idée de script.
Kevin
Une chose de beauté!
évêque le
1

Je pense que cela pourrait fonctionner, remplacez d'abord, puis répétez 2 fois:

:s/a/b/
2@:
ddsun
la source