Fusionar bloques entrelazando líneas

15

¿Hay una manera dedicada de fusionar dos bloques de texto entrelazando líneas, como pasar de esto:

a1
a2
a3
a4
  b1
  b2
  b3
  b4

a ese:

a1
  b1
a2
  b2
a3
  b3
a4
  b4

en unos pocos comandos?

EDITAR : Realmente me gusta la solución de Sato Katsura , así es como la he implementado:

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
fuente
Ahora tengo curiosidad: ¿cuál es su caso de uso? ¿Estás cambiando el nombre de los subtítulos para una temporada de televisión?
VanLaser
@ VanLaser Jaja, no lo soy. Principalmente, estoy analizando los resultados de un programa, que necesito verificar si hay coherencia con respecto al orden de creación / luego la lectura retrasada de objetos. La intercalación de bloques facilita la coincidencia de las líneas correspondientes en los bloques de salida retrasada. A veces también necesito intercalar líneas de códigos con instrucciones repetidas y similares para el registro o la evaluación comparativa. Generar esas instrucciones es fácil con macros, luego intercalarlas con el código real ahora está a solo un par de pulsaciones de teclas con esta función, que se siente genial :)
iago-lito
1
@ lago-lito - ¡gracias por la respuesta! Sí, Vim es bastante versátil :) Su expresión "analizar los ojos" también me hizo pensar en scroll-bindingdos ventanas de Vim.
VanLaser
Tengo problemas para usar esto, ¿cómo seleccionas los dos bloques consecutivos? ¿Necesitan ser adyacentes?
cbcoutinho
@cbcoutinho Sí, sí :) No estoy seguro de que pueda seleccionarlos a ambos de lo contrario. En el ejemplo que he mostrado, puse mi cursor en (digamos) b1, luego presioné vippara seleccionar todo el fragmento, luego ,itcuál es el <map-I've-Picked>. ¿No está funcionando de tu lado?
iago-lito

Respuestas:

8

No hay una forma dedicada de hacerlo (hasta donde yo sé), pero sí, se puede hacer con algunos comandos:

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

Puedes ejecutarlo con :call Interleave(5, 8, 1). El primer parámetro es la primera línea para mover, el segundo la última línea y el tercero donde moverlos. Probablemente desee activar los números de línea para ver lo que está haciendo ( :set number).

Esto supone que los bloques no se superponen. Ver :help :movey :help range()comprender cómo funciona la función.

Probablemente hay mejores formas de recoger los dos bloques. Hay un complemento flotando que se supone que te permite intercambiar dos bloques. No recuerdo el nombre del complemento, pero el autor (¿quizás el famoso Dr. Chip?) Ha pensado más en encontrar una interfaz que yo. :)

Sato Katsura
fuente
¡Dulce! Solo necesito dos argumentos ya que los dos bloques son contiguos y tienen el mismo tamaño: starty size. Con una función homebrew que recupera esos valores de una selección, será perfecto. Estoy trabajando en ello. :)
iago-lito
Crosslink interesante ? ;)
iago-lito
13

Aquí hay otra alternativa:

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

Primero copie las líneas que están debajo de las 4 líneas después de la línea actual ( :h :t) luego elimine las líneas b consecutivas ( :h :d)

Aún mejor es este comando:

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

Lo que significa que, para cada línea que comienza con a, encuentre la siguiente línea que comienza con 'b' y muévala a debajo de la línea actual.

Christian Brabandt
fuente
1
Obtuve "E16: Rango inválido" en el segundo comando. Lo intenté en su .+,$dlugar, y eso funcionó (como lo hizo .+,.+4d).
Peter Lewerin
No estoy seguro de por qué sucede esto
Christian Brabandt
1
No, no lo hace. Leer: h: rango, siempre puede usar la numeración directa en lugar de una búsqueda de
expresiones
2
@ iago-lito El segundo truco siempre funciona, pero debes cambiar /^\s*ba otro :range. por ejemplo: seleccione el primer bloque, ejecute'<,'>g/^/'>+1m.
dedowsdi
1
@ iago-lito Es esencialmente lo mismo que la respuesta de Christian. Nada está codificado si selecciona visualmente el primer bloque, '>+1marca el comienzo del segundo bloque.
dedowsdi
3

Si quieres divertirte un poco con macros y marcas, puedes probar algo como esto:

  • Primero ponga una marca (aquí a) en la línea que contiene a1conma

  • Vaya a la línea que contiene b1y márquela conmb

  • Comience a grabar una macro en el registro que desee (aquí el registro q) conqq

  • Inserte lo siguiente en su macro: ddmb'apjma'b

  • Deja de grabar la macro con q

  • Reproducirlo tantas veces como sea necesario con X@qdonde Xes el número de veces para reproducirlo.

Para detallar 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

Editar Como lo mencionó lago-lito en los comentarios, este método sobrescribirá las marcas y los tampones.

  • Para las marcas, no creo que sea un problema real: rara vez uso las 26 marcas en un búfer y creo que la mayoría de las veces encontraré 2 marcas libres.

  • Para el búfer, es posible guardarlo en una variable temporal: antes de grabar la macro, use :let saveReg=getreg('"')para guardar el registro y, una vez que se realiza la acción, use :call setreg('"', saveReg)para regresar el registro a su estado anterior.

De todos modos, debo admitir que esta solución es solo una solución rápida y no es óptima: en mi opinión, la respuesta de Christan es la mejor y debe aceptarse porque no interfiere con los buffers y las marcas, no obliga al usuario a crear una función y muestra el poder del comando global.

statox
fuente
Interesante. Desafortunadamente, esto sobrescribe el contenido de marcas y registros, que puedo estar usando;)
iago-lito
@ lago-lito: de hecho sobrescribe marcas y amortiguadores. Para las marcas, nunca uso las 26 marcas en mis buffers, así que no creo que sea realmente un problema. Para los tampones que podría ser más de un problema, creo que a menudo se puede encontrar un buffer no utilizado y de si realmente no puede, use una variable temporal y las funciones getreg()y setreg()para guardar la memoria intermedia. Pero estoy de acuerdo en que no es una solución óptima :-)
statox
1

Acabo de ver otra pregunta similar y la solución consiste en:

Salta al medio más uno:

Mj

Y correr:

:,$g/./exe 'm' 2*line('.')-line('$')-1
SergioAraujo
fuente
Interesante :) ¡Tenga cuidado con que esto intercala todo su archivo, sin embargo, y no solo el párrafo seleccionado!
iago-lito