通过交织线合并块


15

有没有一种专门的方法可以通过插入行来合并两个文本块,就像从下面这样传递:

a1
a2
a3
a4
  b1
  b2
  b3
  b4

对此:

a1
  b1
a2
  b2
a3
  b3
a4
  b4

在几个命令?

编辑:我真的很喜欢佐藤桂的解决方案,这是我如何实现它:

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>

现在我很好奇-您的用例是什么?您是否在电视季中对字幕进行重命名?
VanLaser

@VanLaser哈哈,我不是。通常,我在分析程序的输出,我需要检查有关创建顺序/然后延迟读取对象的一致性。交错块使匹配延迟输出块中的相应行变得更加容易。有时我还需要将代码行与重复的类似指令进行交错,以进行日志记录或基准测试。使用宏可以很容易地生成这些指令,然后将它们与实际代码进行交错仅需使用几次此功能即可,这真是

1
@ lago-lito-感谢您的回答!是的,Vim用途广泛:)您的表情“眼神分析”使我也想到了scroll-binding两个Vim窗口。
VanLaser 2015年

我在使用它时遇到麻烦,如何选择两个连续的块?他们需要相邻吗?
cbcoutinho

@cbcoutinho是的,他们有:)我不确定您是否可以同时选择它们。在我展示的示例中,我将光标放在(例如)上b1,然后单击vip以选择整个块,然后,it<map-I've-Picked>。是不是在你这边工作?
iago-lito

Answers:


8

没有专门的方法可以做到这一点(据我所知),但是是的,可以使用一些命令来完成:

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

您可以使用运行它:call Interleave(5, 8, 1)。第一个参数是要移动的第一行,第二个参数是最后一行,第三个参数是要移动它们的位置。您可能希望打开行号以查看自己在做什么(:set number)。

这假定块不重叠。查看:help :move:help range()了解该功能的工作原理。

可能有更好的方法来拾取两个块。有一个左右浮动的插件,可以让您交换两个块。我不记得插件的名称,但是作者(也许是著名的Dr. Chip?)比我投入了更多的心思来寻找接口。:)


甜!我只需要两个参数,因为两个块是连续的并且具有相同的大小:startsize。借助自酿功能可以从选择中检索这些值,这将是完美的。我在做这个工作。:)
iago-lito

有趣的交叉链接?;)
iago-

13

这是另一种选择:

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

首先将下面4行以下的行复制到当前行(:h :t)之后,然后删除连续的b行(:h :d

更好的命令是:

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

这意味着,对于以查找开头的每一行,请查找以“ b”开头的下一行并将其移至当前行下方。


1
我在第二个命令中得到了“ E16:无效范围”。我尝试了一下.+,$d,并且工作了(和一样.+,.+4d)。
彼得·勒维林

不知道为什么会这样
Christian Brabandt

1
不,不是。阅读:h:range,您始终可以使用直接编号代替正则表达式搜索
Christian Brabandt

2
@ iago-lito第二个技巧始终有效,但是您需要更改/^\s*b为另一个 :range。例如:选择第一个程序块,执行'<,'>g/^/'>+1m.
dedowsdi '19

1
@ iago-lito本质上与Christian的答案相同。如果您在视觉上选择第一个块,'>+1即第二个块的开头,则不会硬编码任何内容。
dedowsdi

3

如果您想对宏和标记有一点乐趣,可以尝试如下操作:

  • 首先做了个记号(这里a)对包含该行a1ma

  • 转到包含的行b1并标记为mb

  • 开始使用以下命令在所需的寄存器(此处为寄存器q)中记录宏qq

  • 在您的宏中插入以下内容: ddmb'apjma'b

  • 停止记录宏 q

  • 根据需要播放多次,X@q其中X播放的时间是多少。

详细说明宏:

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

编辑正如lago-lito在评论中提到的那样,此方法将覆盖标记和缓冲区。

  • 对于商标,我认为这不是一个真正的问题:我很少在缓冲区中使用所有26个商标,而且我认为大多数时候都会找到2个免费商标。

  • 对于缓冲区,可以将其保存在一个临时变量中:在记录宏之前,先使用它:let saveReg=getreg('"')来保存寄存器,并在完成操作后使用:call setreg('"', saveReg)来使寄存器回到其先前状态。

无论如何,我必须承认该解决方案只是一种快速的解决方法,并且不是最佳解决方案:我认为Christan的答案是最好的,应该接受,因为它不会弄乱缓冲区和标记,不会迫使用户创建功能,并显示全局命令的功能。


有趣。不幸的是,这覆盖了我可能正在使用的标记和寄存器的内容;)
iago-lito

@ lago-lito:确实覆盖标记和缓冲区。对于标记,我从未在缓冲区中使用所有26个标记,因此我认为这不是真正的问题。对于缓冲区,这可能是一个更大的问题,我认为您经常可以找到未使用的缓冲区,或者,如果确实找不到,请使用临时变量和函数getreg()setreg()保存缓冲区。但我同意这不是最佳解决方案:-)
statox

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.