Mesclar blocos intercalando linhas

15

Existe uma maneira dedicada de mesclar dois blocos de texto intercalando linhas, como passar disso:

a1
a2
a3
a4
  b1
  b2
  b3
  b4

para isso:

a1
  b1
a2
  b2
a3
  b3
a4
  b4

em alguns comandos?

EDIT : Eu realmente gosto da solução de Sato Katsura , aqui está como eu a implementei:

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
fonte
Agora estou curioso - qual é o seu caso de uso? Você está renomeando as legendas para uma temporada de TV?
VanLaser
@VanLaser Haha, eu não sou. Principalmente, estou analisando os resultados de um programa, que preciso verificar a consistência em relação à ordem de criação / depois a leitura atrasada dos objetos. A intercalação de blocos facilita a correspondência das linhas correspondentes nos blocos de saída atrasada. Às vezes, também preciso intercalar linhas de códigos com instruções repetidas e semelhantes para registro ou comparação. Gerando essas instruções é fácil com macros, então intercalação-los com código real está agora a apenas um par de teclas de distância com esta função, que se sente muito bem :)
iago-lito
11
@ lago-lito - obrigado pela resposta! Sim, o Vim é bastante versátil :) Sua expressão "análise dos olhos" também me fez pensar em scroll-bindingduas janelas do Vim.
VanLaser 31/08/2015
Estou tendo problemas para usar isso, como você está selecionando os dois blocos consecutivos? Eles precisam ser adjacentes?
Cbcoutinho 16/0318
@cbcoutinho Sim, eles têm :) Não tenho certeza se você poderia selecioná-los. No exemplo que eu mostrei, eu coloco meu cursor (digamos) b1, depois pressiono vippara selecionar o pedaço inteiro, então ,itqual é o <map-I've-Picked>. Não está funcionando do seu lado?
Iago-lito

Respostas:

8

Não há uma maneira dedicada de fazer isso (tanto quanto eu sei), mas sim, isso pode ser feito com alguns 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

Você pode executá-lo com :call Interleave(5, 8, 1). O primeiro parâmetro é a primeira linha a ser movida, o segundo a última linha e o terceiro para onde movê-los. Você provavelmente deseja ativar os números de linha para ver o que está fazendo ( :set number).

Isso pressupõe que os blocos não se sobrepõem. Veja :help :movee :help range()entenda como a função funciona.

Provavelmente existem maneiras melhores de pegar os dois blocos. Existe um plugin flutuando que permite que você troque dois blocos. Não me lembro do nome do plugin, mas o autor (talvez o famoso Dr. Chip?) Pensou mais em encontrar uma interface do que eu. :)

Sato Katsura
fonte
Doce! Eu só preciso de dois argumentos, já que os dois blocos são contíguos e têm o mesmo tamanho: starte size. Com uma função de homebrew que recupera esses valores de uma seleção, ela será perfeita. Eu estou trabalhando nisso. :)
iago-lito
Crosslink interessante ? ;)
iago-lito
13

Aqui está outra alternativa:

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

Primeiro copie as linhas abaixo de 4 linhas para a linha atual ( :h :t) e exclua as b linhas consecutivas ( :h :d)

Ainda melhor é este comando:

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

O que significa que, para cada linha começando com encontre a próxima linha começando com 'b' e mova-a para abaixo da linha atual.

Christian Brabandt
fonte
11
Eu recebi "E16: intervalo inválido" no segundo comando. Eu tentei .+,$d, e funcionou (como fez .+,.+4d).
Peter Lewerin
Não sei por que isso acontece
Christian Brabandt
11
não, não faz. Leia: h: gama, você sempre pode usar numeração direta, em vez de uma busca regex
Christian Brabandt
2
@ iago-lito O segundo truque sempre funciona, mas você precisa mudar /^\s*bpara outro :range. por exemplo: selecione o 1º bloco, execute'<,'>g/^/'>+1m.
dedowsdi 13/05/19
11
@ iago-lito É essencialmente o mesmo que a resposta de Christian. Nada é codificado se você selecionar visualmente o 1º bloco, '>+1marca o início do 2º bloco.
dedowsdi
3

Se você quiser se divertir um pouco com macros e marcas, tente algo como:

  • Primeiro, coloque uma marca (aqui a) na linha que contém a1comma

  • Vá para a linha que contém b1e marque-a commb

  • Comece a gravar uma macro no registro desejado (aqui o registro q) comqq

  • Insira o seguinte na sua macro: ddmb'apjma'b

  • Pare de gravar a macro com q

  • Jogá-lo quantas vezes for necessário com X@qonde Xé o número de tempo para jogar.

Para detalhar a 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 o lago-lito mencionou nos comentários, esse método substituirá as marcas e os buffers.

  • Para as marcas que não considero um problema real: raramente uso todas as 26 marcas em um buffer e acho que na maioria das vezes encontraremos 2 marcas grátis.

  • Para o buffer, é possível salvá-lo em uma variável temporária: Antes de gravar a macro, use :let saveReg=getreg('"')para salvar o registro e, depois que a ação for concluída, use :call setreg('"', saveReg)-o para retornar o registro ao estado anterior.

De qualquer forma, devo admitir que esta solução é apenas uma solução rápida e não é ideal: na minha opinião, a resposta de Christan é a melhor e deve ser aceita porque não mexe com buffers e marcas, não força o usuário a criar uma função e mostra o poder do comando global.

statox
fonte
Interessante. Infelizmente, este substitui o conteúdo de marcas e registros, que eu possa estar a utilizar;)
iago-lito
@ lago-lito: na verdade, substitui marcas e buffers. Para notas, nunca uso todas as 26 marcas em meus buffers, portanto, não acho que isso seja realmente um problema. Para buffers pode ser mais de um problema, eu acho que muitas vezes você pode encontrar um buffer não utilizado ou se você realmente não puder, use uma variável temporária e as funções getreg()e setreg()para salvar seu buffer. Mas eu concordo que não é uma solução ideal :-)
statox
1

Acabei de ver outra pergunta semelhante e a solução consiste em:

Salte para o meio mais um:

Mj

E corra:

:,$g/./exe 'm' 2*line('.')-line('$')-1
SergioAraujo
fonte
Interessante :) Lembre-se de que isso intercala todo o arquivo e não apenas o parágrafo selecionado!
iago-lito 21/09