web-dev-qa-db-fra.com

Fusionner plusieurs lignes (deux blocs) dans Vim

J'aimerais fusionner deux blocs de lignes dans Vim, c'est-à-dire prendre les lignes n..m et les ajouter aux lignes a..b. Si vous préférez une explication pseudocode: [a[i] + b[i] for i in min(len(a), len(b))]

Exemple:

abc
def
...

123
45
...

devraient devenir

abc123
def45

Existe-t-il un bon moyen de faire cela sans copier/coller manuellement?

322
ThiefMaster

Vous pouvez certainement faire tout cela avec un seul copier/coller (en utilisant la sélection en mode bloc), mais je suppose que ce n'est pas ce que vous voulez.

Si vous voulez faire cela avec seulement Ex commandes

:5,8del | let l=split(@") | 1,4s/$/\=remove(l,0)/

va transformer

work it 
make it 
do it 
makes us 
harder
better
faster
stronger
~

dans

work it harder
make it better
do it faster
makes us stronger
~

UPDATE: Une réponse avec autant de votes positifs mérite une explication plus approfondie.

Dans Vim, vous pouvez utiliser le caractère de pipe (|) pour chaîner plusieurs commandes Ex, ainsi ce qui précède équivaut à

:5,8del
:let l=split(@")
:1,4s/$/\=remove(l,0)/

De nombreuses commandes Ex acceptent une plage de lignes comme argument de préfixe - dans le cas ci-dessus, le 5,8 avant le del et le 1,4 avant le s/// spécifient les lignes utilisées par les commandes. sur.

del supprime les lignes données. Cela peut prendre un argument de registre, mais quand aucun n'est spécifié, il vide les lignes dans le registre sans nom, @", comme le fait la suppression en mode normal. let l=split(@") divise ensuite les lignes supprimées en une liste, en utilisant le délimiteur par défaut: espace. Pour fonctionner correctement sur une entrée comportant des espaces dans les lignes supprimées, comme:

more than 
hour 
our 
never 
ever
after
work is
over
~

nous aurions besoin de spécifier un délimiteur différent, pour éviter que "work is" ne soit divisé en deux éléments de liste: let l=split(@","\n").

Enfin, dans la substitution s/$/\=remove(l,0)/, nous remplaçons la fin de chaque ligne ($) par la valeur de l'expression remove(l,0). remove(l,0) modifie la liste l, en supprimant et en renvoyant son premier élément. Cela nous permet de remplacer les lignes supprimées dans l'ordre dans lequel nous les avons lues. Nous pourrions remplacer les lignes supprimées dans l'ordre inverse en utilisant remove(l,-1).

881
rampion

Une commande Ex élégante et concise permettant de résoudre le problème peut être obtenue en combinant les commandes :global, :move et :join. En supposant que le premier bloc de lignes commence sur la première ligne du tampon et que le curseur se trouve sur la ligne précédant immédiatement la première ligne du deuxième bloc, la commande est la suivante.

:1,g/^/''+m.|-j!

Pour une explication détaillée de la technique utilisée, voir le réponse J'ai donné à la question " le comportement de Vim-d '' sorti de la boîte? ".

57
ib.

Pour joindre des blocs de ligne, vous devez suivre les étapes suivantes:

  1. Aller à la troisième ligne: jj
  2. Entrer en mode bloc visuel: CTRL-v
  3. Ancrez le curseur à la fin de la ligne (important pour les lignes de longueurs différentes): $
  4. Allez à la fin: CTRL-END
  5. Couper le bloc: x
  6. Aller à la fin de la première ligne: kk$
  7. Collez le bloc ici: p

Le mouvement n'est pas le meilleur (je ne suis pas un expert), mais il fonctionne comme vous le souhaitiez. J'espère qu'il y en aura une version plus courte.

Voici les prérequis pour que cette technique fonctionne bien:

  • Toutes les lignes du bloc de départ (dans l'exemple de la question abc et def) ont la même longueur XOR
  • la première ligne du bloc de départ est la plus longue et vous ne vous souciez pas des espaces supplémentaires entre) XOR
  • La première ligne du bloc de départ n'est pas la plus longue et vous permet d'ajouter des espaces à la fin.
44
mliebelt

Voici comment je le ferais (avec le curseur sur la première ligne):

qama:5<CR>y$'a$p:5<CR>dd'ajq3@a

Vous devez savoir deux choses:

  • Le numéro de ligne sur lequel commence la première ligne du deuxième groupe (5 dans mon cas), et
  • le nombre de lignes dans chaque groupe (3 dans mon exemple).

Voici ce qui se passe:

  • qa enregistre tout jusqu'au q suivant dans un "tampon" dans a.
  • ma crée un repère sur la ligne en cours.
  • :5<CR> passe au groupe suivant.
  • y$ tire le reste de la ligne.
  • 'a retourne à la marque définie précédemment.
  • $p colle en fin de ligne.
  • :5<CR> retourne à la première ligne du deuxième groupe.
  • dd le supprime.
  • 'a retourne à la marque.
  • jq descend d'une ligne et arrête l'enregistrement.
  • 3@a répète l'action pour chaque ligne (3 dans mon cas)
19

Comme mentionné ailleurs, la sélection de blocs est la voie à suivre. Mais vous pouvez aussi utiliser n’importe quelle variante de:

:!tail -n -6 % | paste -d '\0' % - | head -n 5

Cette méthode repose sur la ligne de commande UNIX. L'utilitaire paste a été créé pour gérer ce type de fusion de lignes.

PASTE(1)                  BSD General Commands Manual                 PASTE(1)

NAME
     paste -- merge corresponding or subsequent lines of files

SYNOPSIS
     paste [-s] [-d list] file ...

DESCRIPTION
     The paste utility concatenates the corresponding lines of the given input files, replacing all but the last file's newline characters with a single tab character,
     and writes the resulting lines to standard output.  If end-of-file is reached on an input file while other input files still contain data, the file is treated as if
     it were an endless source of empty lines.
8
kevinlawler

Les exemples de données sont les mêmes que ceux de Rampion.

:1,4s/$/\=getline(line('.')+4)/ | 5,8d
3
kev

Je ne pense pas que cela soit trop compliqué. Je voudrais juste mettre virtualedit sur
(:set virtualedit=all)
Sélectionnez le bloc 123 et tous ci-dessous.
Mettez-le après la première colonne:

abc    123
def    45
...    ...

et supprimez les espaces multiples entre 1 espace:

:%s/\s\{2,}/ /g
3
Reman

Je voudrais utiliser des répétitions complexes :)

Compte tenu de ceci:

aaa
bbb
ccc

AAA
BBB
CCC

Avec le curseur sur le premier "a" de la première ligne, appuyez sur ce qui suit:

qq}jdd''$pkJj0q

puis appuyez sur @q (vous pourrez ensuite utiliser @@) autant de fois que nécessaire.

Vous devriez vous retrouver avec:

aaaAAA
bbbBBB
cccCCC

(Plus une nouvelle ligne.)

2
Gerardo Marset

Il y a beaucoup de façons d'accomplir cela. Je vais fusionner deux blocs de texte en utilisant l'une des deux méthodes suivantes.

supposons que le premier bloc est à la ligne 1 et que le deuxième bloc commence à la ligne 10, la position initiale du curseur étant à la ligne 1.

(\ n signifie appuyer sur la touche Entrée.)

1. abc
   def
   ghi        

10. 123
    456
    789

avec une macro en utilisant les commandes: copier, coller et rejoindre.

qaqqa: + 9y\npkJjq2 @ a10G3dd

avec une macro en utilisant les commandes déplacer une ligne au numéro de ligne nième et rejoindre.

qcqqc: 10m.\nkJjq2 @ c

1
dvk317960