327

我想在 Vim 中合并两行代码块,即,将行 k到 l并将它们附加到行m到 n。如果您更喜欢伪代码解释:[line[k+i] + line[m+i] for i in range(min(l-k, n-m)+1)].

例如,

abc
def
...

123
45
...

应该成为

abc123
def45

有没有一种很好的方法可以做到这一点,而无需逐行手动复制和粘贴?

4

9 回答 9

885

您当然可以通过一次复制/粘贴(使用块模式选择)来完成所有这些,但我猜这不是您想要的。

如果您只想使用Ex命令执行此操作

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

会变身

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

进入

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

更新:有这么多赞成的答案值得更彻底的解释。

在 Vim 中,你可以使用竖线 ( |) 来链接多个 Ex 命令,所以上面的等价于

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

许多 Ex 命令接受一系列行作为前缀参数 - 在上述情况下,5,8before thedel1,4before thes///指定命令操作的行。

del删除给定的行。它可以接受一个寄存器参数,但是当没有给出一个参数时,它会将行转储到未命名的寄存器中@",就像在正常模式下删除一样。 let l=split(@")然后使用默认分隔符将删除的行拆分为一个列表:空格。要在已删除行中有空格的输入上正常工作,例如:

more than 
hour 
our 
never 
ever
after
work is
over
~

我们需要指定一个不同的分隔符,以防止“工作是”被分成两个列表元素:let l=split(@","\n").

最后,在替换中s/$/\=remove(l,0)/,我们将每一行的末尾 ( $) 替换为表达式的值remove(l,0)remove(l,0)改变 list l,删除并返回它的第一个元素。这让我们可以按照阅读的顺序替换已删除的行。我们可以使用相反的顺序替换已删除的行remove(l,-1)

于 2012-05-25T19:44:06.780 回答
58

:global结合, :move, 和命令可以得到一个优雅简洁的 Ex 命令来解决这个问题:join。假设第一个行块从缓冲区的第一行开始,并且光标位于第二个块的第一行之前的行,命令如下。

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

有关此技术的详细说明,请参阅对类似问题的回答“<a href="https://stackoverflow.com/q/9658612/254635">如何实现“paste -d ''”开箱即用的行为在 Vim 中?”。

于 2012-05-26T07:09:30.270 回答
45

要加入线块,您必须执行以下步骤:

  1. 转到第三行:jj
  2. 进入视觉块模式:CTRL-v
  3. 将光标锚定到行尾(对于不同长度的行很重要):$
  4. 走到最后:CTRL-END
  5. 切块:x
  6. 转到第一行的末尾:kk$
  7. 在此处粘贴块:p

该运动不是最好的(我不是专家),但它可以像您想要的那样工作。希望会有一个更短的版本。

这是先决条件,因此此技术效果很好:

  • 起始块的所有行(在问题的示例中abcdef)具有相同的长度XOR
  • 起始块的第一行是最长的,你不需要关心中间的额外空格)XOR
  • 起始块的第一行不是最长的,并且您在末尾添加空格。
于 2012-05-25T19:41:02.217 回答
19

这是我的做法(光标在第一行):

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

你需要知道两件事:

  • 第二组的第一行开始的行号(在我的情况下为 5),以及
  • 每组中的行数(在我的示例中为 3)。

这是发生了什么:

  • qa将直到下q一个的所有内容记录到a.
  • ma在当前行上创建一个标记。
  • :5<CR>进入下一组。
  • y$猛拉其余的行。
  • 'a返回标记,设置较早。
  • $p粘贴在行尾。
  • :5<CR>返回到第二组的第一行。
  • dd删除它。
  • 'a返回标记。
  • jq下降一行,并停止录制。
  • 3@a重复每一行的动作(在我的例子中是 3 个)
于 2012-05-26T00:54:59.103 回答
8

如其他地方所述,块选择是要走的路。但您也可以使用以下任何变体:

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

此方法依赖于 UNIX 命令行。创建该paste实用程序是为了处理这种行合并。

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.
于 2012-05-26T04:18:02.667 回答
3

样本数据与 rampion 的相同。

:1,4s/$/\=getline(line('.')+4)/ | 5,8d
于 2012-07-20T06:11:41.963 回答
3

我不认为让它太复杂。我只需在
( :set virtualedit=all)
选择块 123 及以下所有内容上设置 virtualedit。
把它放在第一列之后:

abc    123
def    45
...    ...

并删除到 1 个空格之间的多个空格:

:%s/\s\{2,}/ /g
于 2013-06-07T09:01:53.543 回答
2

我会使用复杂的重复:)

鉴于这种:

aaa
bbb
ccc

AAA
BBB
CCC

将光标放在第一行,按以下命令:

qa}jdd''pkJxjq

然后根据需要多次按@a(您可以随后使用)。@@

你最终应该得到:

aaaAAA
bbbBBB
cccCCC

(加上换行符。)

解释:

  • qa开始录制复杂的重复a

  • }跳到下一个空行

  • jdd删除下一行

  • ''回到最后一次跳跃前的位置

  • p将删除的行粘贴到当前行下

  • kJ将当前行附加到上一行的末尾

  • x删除J合并行之间添加的空格;如果你想要空间,你可以省略它

  • j转到下一行

  • q结束复杂的重复录音

之后,您将@a用于运行存储在 中的复杂重复a,然后您可以使用@@重新运行上次运行的复杂重复。

于 2012-07-14T15:39:10.483 回答
1

可以有很多方法来实现这一点。我将使用以下两种方法中的任何一种来合并两个文本块。

假设第一个块在第 1 行,第二个块从第 10 行开始,光标的初始位置在第 1 行。

(\n 表示按回车键。)

1. abc
   def
   ghi        

10. 123
    456
    789

使用以下命令使用宏:复制、粘贴和加入。

qaqqa:+9y\npkJjq2@a10G3dd

使用命令使用宏在第 n 行号处移动一行并加入。

qcqqc:10m .\nkJjq2@c

于 2014-02-15T16:20:38.470 回答