Vim:在寄存器中存储多个命令时如何处理换行符?

Posted

技术标签:

【中文标题】Vim:在寄存器中存储多个命令时如何处理换行符?【英文标题】:Vim: How to handle newlines when storing multiple commands in registers? 【发布时间】:2011-02-13 10:15:10 【问题描述】:

我有一个存储 vim 命令的 sn-ps 的文件。当我需要一个 sn-p 时,我将其拉出,然后使用 @" 执行它。 sn-ps 存储为脚本,每个命令一行,如下所示:

:s/foo/bar/g
:echo "hello"
:s/1/2/g

编辑:我从示例中删除了正常模式命令,因为它们不是问题的一部分。

现在这个过程不再起作用了:当执行 sn-p 时,它只是停在第一行,就好像在等待换行一样。

是否有某个选项会影响@ 的执行方式?我很确定它在很久以前就可以工作了...

用 ^M 字符替换换行符可以,但会使文件更难处理。


附加信息:

这是另一个症状:当我拉出一个 sn-p 时,如果我使用 @" 执行它,它会停止在第一行,正如我刚刚解释的那样。但如果我用:@ 执行它,它就可以工作。但是帮助文件似乎并没有暗示这两个命令如何处理寄存器内容的任何区别......

【问题讨论】:

【参考方案1】:

我认为问题不在于^M^J。 Vim 宏会将其中任何一个视为记录的宏的有效行尾字符。我认为问题在于额外的换行符。

在您的示例中,2j 之后至少有一个虚假的换行符,除非您在复制 sn-p 时特别小心,否则10k 之后可能还有另一个。这些额外的换行符就像在普通模式下按<Enter>——它们将光标向下移动一行。

这就是我认为您希望 sn-p 的样子:

:s/foo/bar/g
2j:s/1/2/g
10k

(即使这有点误导 - 您仍然需要注意不要在 10k 之后复制换行符。)

为什么这些额外的换行符会产生如此大的影响?好吧,一方面,它们会导致您与预期的位置相距至少一行,这会导致您想要在特定行上执行的任何操作(例如执行 :s// 命令)。

然而,更重要的是——这就是我认为在你的例子中发生的事情——如果宏试图在缓冲区的最后一行使用<Enter>,Vim 会停止宏播放。 (我猜 Vim 认为这是一个错误,任何错误都会导致宏停止运行。)

这是一个例子。假设你有这个 sn-p 存储在寄存器 x 中:

4j
:echo "Done"

(注意4j之后的换行符。)

此外,假设您在缓冲区中有以下五行(并且只有这五行):

line 1
line 2
line 3
line 4
line 5

如果您现在在line 1 上按@x,则:echo "Done" 永远不会执行。 Vim 将光标向下移动 4 行到line 5,然后由于额外的换行符而尝试再向下移动一行,但它不能。在:echo 命令有机会运行之前,该宏将停止执行。

但是,如果您将 x 寄存器更改为以下内容,它会起作用:

4j:echo "Done"

所以回到你原来的例子,我敢打赌,2j 之后的额外换行符试图将你的光标移动到它不能去的地方,这会导致宏停止。屏幕的最后一行包含最后执行的命令 (:s/foo/bar/g),这看起来像是 Vim 正在等待您按 Return。

最后,我强烈建议使用另一种方法来存储和执行 Vim 命令序列。您使用的技术对于简单的情况是可以容忍的,但它很脆弱并且不能很好地扩展。 Vim 有一个完整的脚本语言,包括函数和自定义命令,它可以用来做你现在正在做的所有事情,但是以更健壮的方式。 Vim 脚本是一个很大的话题,但我会从这里开始:

:help script

请务必阅读:normal 命令,该命令可让您在脚本中执行普通模式命令(如2j10k)。

祝你好运!

【讨论】:

您好,比尔,感谢您提供详细而翔实的回复。我完全同意我应该使用正确的 vim 脚本而不是我的这个“vim sn-ping” :) 我确实在朝着那个方向前进。但我仍然对手头的问题感到好奇。我将为您的努力投票,但您的解释虽然正确,但并没有解决它:即使我完全删除了正常模式命令,问题仍然存在。例如,如果我执行两个 ":echo" 命令,只有第一个被执行。如果我运行 vim -u NONE 我确实会执行它们,所以它一定是某个地方的一些设置...... 抱歉,我的意思是“只有第一行被显示”,它就在那里等待<CR>...我用更多信息更新了这个问题。跨度> 嗯。我试图复制你所描述的内容,虽然我在此过程中学到了很多关于宏、寄存器、Vim 的换行符处理以及其他几十个主题的知识,但我没有太多运气弄清楚你到底怎么了。所以这里有一些问题: Vim 的版本? GUI 还是终端?什么操作系统? .vimrc 的内容?有什么插件吗? sn-ps 文件的fileformatencodingfileencoding 的值? compatiblecpoptions 设置的值?寄存器的确切值? (对简洁性感到抱歉;否则无法将它们放入字符限制中。) 哦,还有一个问题:如何将 sn-p 复制到寄存器中?我知道你说你在拉线,但你一开始是如何选择线的? (即视觉模式?视觉线模式?countyy?鼠标?)你选择最后一行的尾随换行符吗? 感谢您的承诺,非常感谢。折腾了一阵子,终于找到了问题所在……好奇的看我的回答!【参考方案2】:

我终于找到了罪魁祸首。不知何故,我的 .vimrc 文件中的 <C-J> 有一个命令映射。当使用默认的cpoptions 读取时,这变成了<NL> 上的映射。

我是如何发现的:我注意到当用-u ~/.vimrc 启动 vim 时,它确实会执行 yanked sn-ps。我生成了一个带有和不带有该命令行选项的会话文件,并对它们进行了比较。这样我发现一组不同的cpoptions 用于读取相同的.vimrc 文件,因此在一种情况下映射确实在<C-J> 上,在另一种情况下它被转换为<NL> 上的映射!

如果有人有类似的问题,我建议仔细查看当前设置的命令映射,:cmap

【讨论】:

这也发生在我身上,但并非偶然。我有 C-h,j,k,l 映射到切换窗口。这会产生一个令人讨厌的错误,其中录制的宏可以工作(因为它们得到 ^M),但是在粘贴和编辑时它们会失败(因为它们得到 ^J)。

以上是关于Vim:在寄存器中存储多个命令时如何处理换行符?的主要内容,如果未能解决你的问题,请参考以下文章

IOS,应用启动时如何处理多个本地通知?

合并发布者时如何处理错误?

使用组件依赖时如何处理多个 Dagger 组件

在电源查询中加载 CSV 时如何处理多个引号?

从 API 接收时如何处理 blob 存储 url

在 monorepo 中的多个应用程序之间共享组件时如何处理共享依赖项