【问题标题】:Returning to Normal Mode from Insert Mode after "execute normal"“执行正常”后从插入模式返回正常模式
【发布时间】:2014-11-14 02:07:39
【问题描述】:

您可以在 Ex 模式下以编程方式执行正常模式命令,例如执行正常,例如

:execute "normal" "iNEWTEXT\<Esc>0"

这会切换到插入模式 (i),写入“NEWTEXT”,转义到正常模式 (\),然后移动到行首 (​​0)。

但是,使用非常量字符串(寄存器或变量)时,行为是不同的。例如,假设您在任何文件(不一定是 vimscript 文件)的一行中保存了上面相同的命令:

iNEWTEXT\<Esc>0

然后您可以通过"zy$ 将文本复制到任何寄存器(此处为z)并通过@z 执行该寄存器。不过,这一次的输出有所不同:

NEWTEXT\<Esc>0

进入插入模式后,转义不再被视为特殊字符,而是按字面意思理解。像 \e 这样的替代形式也不起作用。有没有解决的办法?

编辑:使用 Ingo 的回答,我创建了以下函数。基本上,用途是在文件文本中嵌入一组正常/插入命令,并能够执行它们。更常见的是,running Ex commands from a line of text 使用了类似的东西,但我找不到任何可以在正常和插入模式下执行此操作的东西。

因此,您的文件中将包含如下文本:

jy10j10jpO\<Esc>jEll

在该行上,您可以调用函数或重映射,然后命令将执行(在此示例中,复制和粘贴 10 行,并将 2 列移到第一个单词之后)。 Ingo 的替代方案更适合认真使用,即从另一个文件中获取命令,在 .vimrc 中包含命令,或文件类型特定的选项。由会话保存的宏也可以正常工作,并且比将命令分散在文件中更实用。就我而言,我正在跨多个设备进行同步,并且不想拥有另一个文件或使用这个非常特定的命令弄乱我的 vimrc,但不介意弄乱这个特定的文件本身。把它想象成一个可移植的宏。

" Execute current line as Vim normal mode commands.
nnoremap <A-y> :call EvaluateLineAsNormalModeCmd()<CR>

function! EvaluateLineAsNormalModeCmd()
    let g:getCurrentLine = getline(".")
    "have to :execute twice: once to get the contents of the 
    "register inserted into a double-quoted string, and then once for 
    "the :normal to evaluate the string.
    execute 'execute "normal" "' . g:getCurrentLine  . '"'
endfunction

EDIT2/3:这里有两个函数使用 Christian Brabandt 的答案。它们的工作原理大致相同,但可以在最后将用户置于插入模式(而根据我的最少信息,其他上下文中的 'i' 被认为是不完整的命令并且未执行,并且 :startinsert 不能用于那种情况)。 PS:请不要问我那些单引号和双引号在做什么,因为我无法理解它O_o

function! EvaluateLineAsNormalModeCmd()
    normal! 0y$
    execute ':call feedkeys("'.@".'", "t")'
endfunction

function! EvaluateLineAsNormalModeCmd()
    let g:getCurrentLine = getline(".")
    execute ':call feedkeys("'.g:getCurrentLine.'", "t")'
endfunction

【问题讨论】:

  • 您是否尝试过将\&lt;Esc&gt; 序列更改为^[? (即&lt;C-v&gt;&lt;Esc&gt;
  • 试过这个,但它遇到了同样的问题:一切都是字面意思。 Ingo 的双重执行功能解决了这个问题。

标签: vim


【解决方案1】:

如果你真的需要这个(用例是可疑的),你必须:execute 两次:一次将寄存器的内容插入双引号字符串,然后一次用于:normal 评估字符串。

:execute 'execute "normal" "' . @z . '"'

PS:请提供更多背景信息;你的最终目标是什么?当一个问题只是一个很小的技术步骤时,很难提供一个好的答案。如果你不告诉我们为什么你想要这个,很容易屈服于XY problem

【讨论】:

  • 不是不透明的;很难知道包含太多信息的线路在哪里。无论如何,我有一个在多台计算机之间共享的文件(通过同步应用程序,即 BTSync)。我定期对其执行一系列命令,例如复制和粘贴特定部分的文本。将命令保存在宏中(并在会话中恢复)有效,但需要在每台不同的计算机上完成。我想我会将命令保存为文件中的文本,然后从那里简单地执行它。我的用例的可疑性是否意味着有更好的方法来解决这个问题?我对想法持开放态度。
  • 感谢您的解释。您可以将命令同步为真实文件旁边的command.vim Vimscript 文件和:source 它们。或者,如果您同步您的.vimrc(可能通过其他方式),您可以在那里定义一个命令,甚至可以定义一个自定义文件类型(+ 带有缓冲区本地命令的插件)。
  • 谢谢。这很好用。不确定我是否会想到这个解决方案。需要了解有关双引号等的更多信息。:source 文件的选项有效,但它只有一个命令,所以看起来有点沉重。该命令本身有点不稳定(容易进行小调整)并且是特定的,所以没有想到将它放在 .vimrc 中。也很完美。现在我的文件中有类似jv10jy10jpO\&lt;Esc&gt;jEll 的行,我从那里运行它。在所有情况下,拥有这样一条线并不是很实用或不可能,但在这里对我很有用,因为我不关心文本并且可以在 vimrc 中同步重映射。
【解决方案2】:

我宁愿使用feedkeys() 函数。例如。对于您的示例,这应该可以:

exe ':call feedkeys("'.@".'", "t")'

(如果您将您的线路拉入未命名的寄存器,则相应地调整寄存器名称)。请注意,引用可能会变得丑陋。

要了解发生了什么,这就是所做的:

exe ':call feedkeys('           - First part of the feedkeys() function call
    "                           - Start of Quote for the first argument
    .                           - String concatenation
    @"                          - content of the unnamed register
    .                           - String concatenation
    '                           - Start of second part of the feedkeys function call
    "                           - End of Quote for the first argument
    , "t")'                     - Second argument of feedkeys() function call

您也可以像这样分两步完成:

   exe ':let a="'. @". '"'       - Also needs to quote @" correctly.
   call feedkeys(a, 't')

应该更容易理解。 exe 调用只是将规范化的键符号转换为文字键。

【讨论】:

  • 丑吗?我相信这个词是“令人难以置信的”。我想我可以通过阅读:h expr-quote 来弄清楚双引号和单引号,但是包围未命名寄存器(@“)的句点是做什么的?它们似乎不是字符串连接运算符,因为我不确定连接的是什么。
  • 它基本上是一个 feedkeys 调用,需要由 exe 评估,以便正确翻译关键符号。 (我会相应地更新答案,这样更容易阅读)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-06-26
  • 1970-01-01
  • 2013-07-03
  • 2022-01-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多