Fusionner des blocs par entrelacement de lignes

15

Existe-t-il un moyen dédié de fusionner deux blocs de texte en entrelacant des lignes, comme en passant de ceci:

a1
a2
a3
a4
  b1
  b2
  b3
  b4

pour que:

a1
  b1
a2
  b2
a3
  b3
a4
  b4

en quelques commandes?

EDIT : J'aime vraiment la solution de Sato Katsura , voici comment je l'ai mise en œuvre:

function! Interleave()
    " retrieve last selected area position and size
    let start = line(".")
    execute "normal! gvo\<esc>"
    let end = line(".")
    let [start, end] = sort([start, end], "n")
    let size = (end - start + 1) / 2
    " and interleave!
    for i in range(size - 1)
        execute (start + size + i). 'm' .(start + 2 * i)
    endfor
endfunction

" Select your two contiguous, same-sized blocks, and use it to Interleave ;)
vnoremap <pickYourMap> <esc>:call Interleave()<CR>
iago-lito
la source
Maintenant, je suis curieux - quel est votre cas d'utilisation? Êtes-vous en train de renommer des sous-titres en bloc pour une saison télévisée?
VanLaser
@VanLaser Haha, je ne le suis pas. La plupart du temps, je suis en train d'analyser la sortie d'un programme, dont je dois vérifier la cohérence concernant l'ordre de création / puis la lecture retardée des objets. L'entrelacement des blocs facilite la mise en correspondance des lignes correspondantes dans les blocs de sortie retardés. J'ai également parfois besoin d'entrelacer des lignes de codes avec des instructions répétées et similaires pour la journalisation ou l'analyse comparative. Générer ces instructions est facile avec les macros, puis les entrelacer avec le code réel n'est plus qu'à quelques touches de cette fonction, ce qui est génial :)
iago-lito
1
@ lago-lito - merci pour la réponse! Oui, Vim est assez polyvalent :) Votre expression "eye-parsing" m'a fait penser aussi à scroll-bindingdeux fenêtres Vim.
VanLaser
J'ai du mal à utiliser cela, comment sélectionnez-vous les deux blocs consécutifs? Doivent-ils être adjacents?
cbcoutinho
@cbcoutinho Oui, ils l'ont :) Je ne suis pas sûr que vous puissiez les sélectionner tous les deux autrement. Dans l'exemple que j'ai montré, je mets mon curseur sur (disons) b1, puis je tape vippour sélectionner le morceau entier, puis ,itqui est le <map-I've-Picked>. Ça ne marche pas de votre côté?
iago-lito

Réponses:

8

Il n'y a pas de moyen dédié pour le faire (pour autant que je sache), mais oui, cela peut être fait avec quelques commandes:

function! Interleave(start, end, where)
    if a:start < a:where
        for i in range(0, a:end - a:start)
            execute a:start . 'm' . (a:where + i)
        endfor
    else
        for i in range(a:end - a:start, 0, -1)
            execute a:end . 'm' . (a:where + i)
        endfor
    endif
endfunction

Vous pouvez l'exécuter avec :call Interleave(5, 8, 1). Le premier paramètre est la première ligne à déplacer, le second la dernière ligne et le troisième où les déplacer. Vous voulez probablement activer les numéros de ligne pour voir ce que vous faites ( :set number).

Cela suppose que les blocs ne se chevauchent pas. Voir :help :moveet :help range()comprendre comment fonctionne la fonction.

Il existe probablement de meilleures façons de récupérer les deux blocs. Il y a un plugin flottant qui est censé vous permettre d'échanger deux blocs. Je ne me souviens pas du nom du plugin, mais l'auteur (peut-être le célèbre Dr. Chip?) A réfléchi plus que moi à la recherche d'une interface. :)

Sato Katsura
la source
Sucré! Je n'ai besoin que de deux arguments car les deux blocs sont contigus et ont la même taille: startet size. Avec une fonction homebrew qui récupère ces valeurs dans une sélection, ce sera parfait. J'y travaille. :)
iago-lito
Réticulation intéressante ? ;)
iago-lito
13

Voici une autre alternative:

:g/^a/+4t .
:+,+5d 

Copiez d'abord les lignes qui se trouvent 4 lignes en dessous de la ligne actuelle ( :h :t) puis supprimez les lignes b consécutives ( :h :d)

Encore mieux est cette commande:

 :g/^a//^\s*b/m .

Cela signifie que, pour chaque ligne commençant par a, recherchez la ligne suivante commençant par «b» et déplacez-la en dessous de la ligne actuelle.

Christian Brabandt
la source
1
J'ai obtenu "E16: Plage non valide" sur la deuxième commande. J'ai essayé à la .+,$dplace, et cela a fonctionné (comme l'a fait .+,.+4d).
Peter Lewerin
Je ne sais pas pourquoi cela se produit
Christian Brabandt
1
non ça ne marche pas. Lire: h: plage, vous pouvez toujours utiliser la numérotation directe au lieu d'une recherche d'expression régulière
Christian Brabandt
2
@ iago-lito La deuxième astuce fonctionne toujours, mais vous devez passer /^\s*bà une autre :range. par exemple: sélectionnez le 1er bloc, exécutez'<,'>g/^/'>+1m.
dedowsdi
1
@ iago-lito C'est essentiellement la même chose que la réponse de Christian. Rien n'est codé en dur si vous sélectionnez visuellement le 1er bloc, '>+1marque le début du 2e bloc.
dedowsdi
3

Si vous voulez vous amuser un peu avec les macros et les marques, vous pouvez essayer quelque chose comme ceci:

  • Mettez d'abord une marque (ici a) sur la ligne contenant a1avecma

  • Accédez à la ligne contenant b1et marquez-la avecmb

  • Commencez à enregistrer une macro dans le registre que vous souhaitez (ici le registre q) avecqq

  • Insérez le suivant dans votre macro: ddmb'apjma'b

  • Arrêtez l'enregistrement de la macro avec q

  • Jouez-le autant de fois que nécessaire avec X@qXest le nombre de fois pour le jouer.

Pour détailler la macro:

dd mb 'a p j ma 'b
 |  |  | | |    |
 |  |  | | |    go back to line marked `b`
 |  |  | | |
 |  |  | | move of one line and replace the mark `a`
 |  |  | insert the deleted line under the line marked `a`
 |  |  go to line marked `a`
 |  mark the future line to move with `b`
 delete the line to move

Modifier Comme lago-lito l'a mentionné dans les commentaires, cette méthode écrasera les marques et les tampons.

  • Pour les marques, je ne pense pas que ce soit un vrai problème: j'utilise rarement les 26 marques dans un tampon et je pense que la plupart du temps, on trouvera 2 marques libres.

  • Pour le tampon, il est possible de l'enregistrer dans une variable temporaire: avant d'enregistrer la macro, utilisez :let saveReg=getreg('"')pour enregistrer le registre et une fois l'action terminée, utilisez :call setreg('"', saveReg)pour ramener le registre à son état précédent.

Quoi qu'il en soit, je dois admettre que cette solution n'est qu'une solution rapide et n'est pas optimale: à mon avis, la réponse de Christan est la meilleure et doit être acceptée car elle ne dérange pas les tampons et les marques, ne force pas l'utilisateur à créer une fonction et montre la puissance de la commande globale.

statox
la source
Intéressant. Malheureusement, cela écrase le contenu des marques et des registres, que j'utilise peut-être;)
iago-lito
@ lago-lito: en effet, il écrase les marques et les tampons. Pour les notes, je n'utilise jamais les 26 marques dans mes tampons, donc je ne pense pas que ce soit vraiment un problème. Pour les tampons, cela pourrait être plus un problème, je pense que vous pouvez souvent trouver un tampon inutilisé ou si vous ne le pouvez vraiment pas, utilisez une variable temporaire et les fonctions getreg()et setreg()pour enregistrer votre tampon. Mais je suis d'accord que ce n'est pas une solution optimale :-)
statox
1

Je viens de voir une autre question similaire et la solution consiste à:

Aller au milieu plus un:

Mj

Et courir:

:,$g/./exe 'm' 2*line('.')-line('$')-1
SergioAraujo
la source
Intéressant :) Attention, cela entrelace tout votre fichier, et pas seulement le paragraphe sélectionné!
iago-lito