Insérez un nombre incrémentiel sur chaque ligne dans une sélection ou une correspondance


10

J'ai un problème que je peux penser à deux approches générales pour résoudre, mais je ne connais pas les détails de l'une ou l'autre approche.

...
Level 1:    cũng    also
Level 1:    và      and
Level 1:    như     like; such as
Level 2:    các     plural marker
Level 2:    của     belonging to
...

Pour chaque ligne commençant par "Niveau n", je veux insérer un numéro commençant par "01". Pour plus de simplicité, ajoutons le numéro.

Approche 1: sélectionnez manuellement toutes les lignes de même niveau. Invoquez la magie que je vais bientôt apprendre.

Approche 2: Écrivez une recherche et remplacez qui correspond à toutes les lignes avec un niveau donné qui, à chaque correspondance, inclut un numéro dans le texte de remplacement, qui augmente d'une unité à chaque correspondance.

J'ai trouvé des questions similaires sur StackOverflow ou sur d'autres sites Vim, mais chacun semble avoir un ou plusieurs des problèmes suivants:

  1. Il s'agit d'insérer le numéro de ligne actuel plutôt qu'un nombre arbitraire mais incrémenté.
  2. Ne met pas à zéro le nombre.
  3. Ne fonctionne pas réellement pour les sélections sur mon Vim 7.4 fonctionnant sous Windows 7. (Celles-ci entraînent l'erreur E481: No range allowed.)

J'utilise gVim dans Windows avec mswin.vim mais une solution qui fonctionne sur toutes les installations Vim vanilla sans avoir à personnaliser la configuration pourrait être la meilleure.


Les meilleures balises auxquelles je pouvais penser pour la question n'existent pas: sélection et plage, alors n'hésitez pas à ré-étiqueter ou à créer l'une de ces balises.
hippietrail

1
Ce plugin n'est pas une solution complète à votre problème, mais il est extrêmement utile pour ajouter des colonnes de nombres: VisIncr . Documents ici . FWIW.
lcd047

Réponses:


17

Semblable à la réponse sur https://vi.stackexchange.com/a/818/227 , vous pouvez utiliser la commande globale.

Avec lui, vous pouvez demander à vim de rechercher des lignes correspondant à un modèle, puis d'exécuter des commandes dessus.

Dans votre cas, vous souhaitez ajouter du texte aux lignes commençant par "Niveau N:", afin que notre commande globale puisse être

:g/^Level \d:/{COMMANDS}

Utilisation de la commande de substitution (remplacement d'expressions régulières) pour la commande

Les commandes sont plus amusantes. J'aime généralement faire un remplacement d'expression régulière pour des trucs comme ça, car il est facile d'utiliser des variables.

Exemple pour votre question

:let i = 1 | g/^Level \d:/s/^/\=printf("%02d ", i)/ | let i = i+1

Comment ça fonctionne

Dans la section de remplacement d'une commande de substitution peut être une expression.

La première chose que nous ferons est de définir une variable icomme étant le numéro de départ. J'ai choisi 1, mais n'importe quel nombre fera l'affaire.let i = 1

Ensuite, nous exécutons notre commande globale, qui nous configure pour effectuer une action sur les lignes correspondantes. g/^Level \d:/

Nous allons demander à notre commande globale d'insérer la valeur et d'incrémenter notre compteur à l'aide de la commande de substitution et de la commande let. s/^/\=printf("%02d ", i)/ | let i = i+1

L'expression régulière de la commande de substitution trouve le début de la ligne ^et la remplace par une expression, et notre expression sera le résultat d'une impression formatée. Comme dans le langage C, printf de vim prend les paramètres de formatage. %02dsignifie convertir un argument comme s'il s'agissait d'un nombre décimal d, occupant au moins 2 espaces 2et remplir avec 0 0. Pour plus de détails et d'autres options de conversion (y compris la mise en forme à virgule flottante), voir :help printf. Nous donnons à printf notre variable de comptage iet cela nous donne 01la première fois, 02la deuxième fois, etc. Ceci est utilisé par la commande de substitution pour remplacer le début de la ligne, insérant efficacement le résultat de printf au début.

Notez que je mets un espace après le d: "%02d ". Vous ne l'avez pas demandé dans la question (et je n'ai pas vu d'exemple de sortie), mais je soupçonnais que vous vouliez séparer le nombre du mot "Niveau". Supprimez l'espace de la chaîne donnée à printf pour avoir le numéro inséré juste à côté du L dans Level.

Enfin, cela let i = i + 1incrémente notre compteur après chaque substitution.

Cela peut être appliqué généralement pour remplacer des parties de lignes qui sont appariées par d'autres critères par des données fonctionnelles arbitraires.

Utilisation de commandes normales combinées

C'est bon pour les insertions simples ou l'édition complexe. Comme avec substitute, nous utiliserons global pour faire correspondre, mais au lieu de substitution d'expression régulière, nous exécuterons une série d'opérations comme si elles étaient tapées par l'utilisateur.

Exemple pour votre question

:let i = 1 | g/^Level \d:/execute "normal! I" . printf("%02d ", i) | let i = i+1

Comment ça fonctionne

Les valeurs utilisées sont très similaires à celles du substitut (nous utilisons toujours printf pour formater notre nombre pour qu'il soit 0 rembourré avec 2 chiffres), mais l'opération est différente.

Ici, nous utilisons la commande d'exécution, qui prend une chaîne et exécute la chaîne comme une commande ex ( :help :exe). Nous construisons une chaîne qui combine "normal! I" avec nos données, qui seront "normales! I01" la première fois et "normales! I02" la deuxième fois, etc.

La normalcommande effectue des opérations comme en mode normal. Dans cet exemple, notre commande normale est I, qui insère au début de la ligne. Si nous l'avions utilisé, ddcela supprimerait la ligne, oouvrirait une nouvelle ligne après la ligne correspondante. C'est comme si vous vous tapiez I(ou toute autre opération) vous-même en mode normal. nous utilisons l' !after normalpour nous assurer qu'aucun mappage ne nous gêne. Tu vois :help :normal.

Ce qui est alors inséré est la valeur de notre printf, comme dans le premier exemple d'utilisation de substitut.

Cette méthode peut être plus sophistiquée que l'expression régulière, car vous pouvez faire des choses comme execute "normal! ^2wy" . i . "th$p", qui iront au début du texte ^, avancer de 2 mots 2w, tirer jusqu'à ce que le ième caractère 'h' y" . i . "th, aller à la fin de la ligne $et coller p.

C'est presque comme exécuter une macro, mais n'utilise pas réellement un registre et peut combiner des chaînes de toutes les expressions. Je trouve que c'est très puissant.

Approche où chaque niveau a son propre compteur

Vous voudrez peut-être que chaque niveau ait son propre compteur. Si vous connaissez le nombre maximal de niveaux à l'avance, vous pouvez effectuer les opérations suivantes (ajouter du code supplémentaire pour trouver le plus grand niveau n'est peut-être pas trop difficile, mais rendrait cette réponse trop longue. Cela devient aussi long que cela).

Tout d'abord, libérons i, au cas où nous l'aurions déjà utilisé comme entier. Nous ne pouvons pas convertir i en liste, nous devons le créer de cette façon.

:unlet! i

Ensuite, permet de définir i pour être une liste contenant le nombre de niveaux. Vous avez montré 2 dans votre question, mais supposons 10 pour le plaisir. Étant donné que l'indexation de liste est basée sur 0, et je ne veux pas déranger la correction pour 1 basée comme votre liste, nous allons simplement créer suffisamment d'éléments (11) et ne jamais utiliser l'index 0.

:let j = 0
:let i = []
:while j < 11 | let i += [1] | let j += 1 | endwhile

Ensuite, nous avons besoin d'un moyen d'obtenir le numéro de niveau. Heureusement, le substitut est également disponible en fonction, nous allons donc lui donner notre ligne et extraire le numéro de niveausubstitute(getline("."), "^Level \\(\\d\\):.*", "\\=submatch(1)", "")

Puisque i est maintenant une liste de 11 1s (chaque indice est le compteur de notre niveau), nous pouvons maintenant ajuster l'un des exemples ci-dessus pour utiliser le résultat de cette substitution:

Via une commande de substitution:

:unlet! i | unlet! j | let j = 0 | let i = [] | while j < 11 | let i += [1] | let j += 1 | endwhile
:g/^Level \d:/let ind=str2nr(substitute(getline("."), "^Level \\(\\d\\):.*", "\\=submatch(1)", "")) | s/^/\=printf("%02d ", i[ind])/ | let i[ind] += 1

Via la commande normale:

:unlet! i | unlet! j | let j = 0 | let i = [] | while j < 11 | let i += [1] | let j += 1 | endwhile
:g/^Level \d:/let ind=str2nr(substitute(getline("."), "^Level \\(\\d\\):.*", "\\=submatch(1)", "")) | execute "normal! I" . printf("%02d ", i[ind]) | let i[ind] += 1

Exemple d'entrée:

Level 1: stuff

Level 1: Stuff

Some text
Level 3: Other

Level 1: Meh

Level 2: More

Exemple de sortie:

01 Level 1: stuff

02 Level 1: Stuff

Some text
01 Level 3: Other

03 Level 1: Meh

01 Level 2: More

Wow, très encyclopédique. Je n'ai lu que la première partie et j'ai l'impression d'en avoir appris plus sur Vim que je ne l'ai fait ces dernières années. C'est le genre de réponse qui rend Stack Exchange meilleur que les sites Q&R moyens et montre également les avantages d'avoir une SE spécifique à Vim.
hippietrail

3

Vous pouvez créer à partir de https://stackoverflow.com/a/4224454/15934 pour mettre à zéro vos numéros.

" A reminder: how to start numbers at the first line
:'<,'>s/^\s*\zs/\=(line('.') - line("'<")+1).'. '

Mais afin de simplifier l'action de remplissage des nombres, j'opterais pour une paire de fonctions et une commande:

command!  -range=% -nargs=? PrependNumbers <line1>,<line2>call s:Prepend(<args>)

function! s:ReplExpr(nb_digits, number)
  return repeat('0', a:nb_digits - strlen(a:number)).a:number
endfunction

function! s:Prepend(...) range
  let pattern = a:0 > 0 ? '\ze'. a:1 : '^'
  let nb_values = (a:lastline - a:firstline) + 1
  let nb_digits = strlen(nb_values)
  exe ':'.a:firstline.','a:lastline.'s#'.pattern.'#\=s:ReplExpr(nb_digits, 1+ line(".")-'.a:firstline.')." "#'
endfunction

Ensuite, sélectionnez vos lignes et tapez :PrependNumber(vous verrez :'<,'>PrependNumber. [Remarque: La commande prend un paramètre facultatif: le motif avant lequel le numéro sera inséré]


Mais cela ne line(".")signifie pas «utiliser le numéro de ligne actuel»? C'était mon problème 1. avec les réponses précédentes que j'ai pu trouver.
hippietrail

1
Oui, ça l'est. C'est pourquoi je soustrais le numéro de ligne de la première ligne de la plage.
Luc Hermitte

Ah OK, je ne l'ai pas encore testé parce que je ne me souviens même pas comment entrer un script dans Vim ... dois le lire en premier (-:
hippietrail

1
Pendant que vous êtes sous Windows, copiez collez le code dans $HOME/vimfiles/plugin/whatevernameyouwish.vim. Ou même votre $HOME/_vimrc(nom de fichier Windows) si vous préférez (dans un premier temps). Si vous n'êtes pas sûr de ce que $ HOME est sur votre machine, demandez à vim ce qu'il pense que c'est ->:echo $HOME
Luc Hermitte

1
Vous n'avez même pas besoin :sourcesi le script vim est correctement installé dans le bon répertoire ({rtp} / plugin, ou {rtp} / ftplugin / {filetype} / ftplugin pour les plugins spécifiques au type de fichier). :sourcec'est ce avec quoi nous avons dû jouer il y a plus d'une décennie et demie.
Luc Hermitte

2

Vous pouvez l'approcher en enregistrant une macro. En commençant par votre fichier d'origine, ajoutez la première instance au numéro.

01 Level 1:    cũng    also
Level 1:    và      and
Level 1:    như     like; such as
Level 2:    các     plural marker
Level 2:    của     belonging to

Déplacez votre curseur sur 01, sélectionnez et tirez dessus avec yiw. Vous voulez maintenant enregistrer vos actions à partir de ce moment.

qq/^Level 1<CR>P<C-A>A<space><esc>0yiwq

  • qq Démarrer une macro dans le registre q
  • /^Level 1<CR> Rechercher une ligne commençant par "Niveau 1"
  • P coller avant le curseur (contient votre numéro)
  • <C-A> incrémente le nombre
  • A<space><esc> Insérez un espace après le numéro
  • 0 Aller au début
  • yiw arracher le numéro actuel
  • q Mettre fin à la macro

Répétez ensuite cette macro en utilisant @q.


1
Le <C-A>contrôle + a? Cela sélectionne tout dans Vim sur Windows.
hippietrail

C'est Control + a. Dans vim, il devrait incrémenter un nombre. Si vous avez modifié vos raccourcis clavier pour effectuer des actions Windows au lieu des actions Vim, vous ne pourrez pas faire la plupart des choses que les gens publient ici.
jecxjo

Non, la version Vim pour Windows est livrée avec différentes liaisons en raison de la sagesse infinie de quelqu'un. J'utilise simplement les fixations vanille prêtes à l'emploi. Je n'ai jamais changé de reliure à clé Vim depuis plus de vingt ans, que je me souvienne (-:
hippietrail

Cela ne devrait pas être le cas, mon installation Windows a des liaisons vim normales. Serait-il possible que vous ayez installé la version de quelqu'un d'autre de vim? Ou pourriez-vous exécuter vim en mode "Facile"? Je crois que Windows installe des icônes de bureau avec des options de mode normal et facile.
jecxjo

3
Non, c'est parce que l'installation par défaut dans Windows charge mswin.vim. Si vous créez votre propre vimrc et ne chargez pas mswin.vim, vous obtenez les liaisons vim normales. Je garde un seul vimrc pour toutes mes installations (Linux, Mac et Windows) et ne traite jamais avec mswin.vim. Pour plus d'informations sur ce problème, voir stackoverflow.com/questions/289681/…
jecxjo
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.