Vim宏接受用户输入

3

假设我有以下文本文件:

name: John Doe
description: My name is John Doe and I'm really good at vim!
name: John Doe
description: My name is John Doe and I'm really good at vim!
name: John Doe
description: My name is John Doe and I'm really good at vim!
name: John Doe
description: My name is John Doe and I'm really good at vim!


有没有一种方法可以记录一个宏,执行以下操作:
从第一个“John”开始:
  1. cw,但允许运行宏的用户在此处输入

  2. js<is>w偷偷摸摸地获得“is”后面的单词

  3. 用给定的输入替换John
  4. 重复
理想情况下,我想在第一行输入以下按键:

@qJane

然后拥有:
name: Jane Doe
description: My name is Jane Doe and I'm really good at vim!

@qJim

name: Jim Doe
description: My name is Jim Doe and I'm really good at vim!
2个回答

3
最简单的获取用户输入的方法是使用 input() 函数,该函数允许您提示输入名称并在表达式中使用它。
不幸的是,您不能真正从宏中使用它,因为宏会记录您对提示的答案(重放您构建的宏也会有类似的问题)。
但是,您可以从函数中使用它并将该函数绑定到键映射中:
function! ChangeName()
  let newname = input('Name? ')
  return '0/\<John\>'."\<cr>cw".newname."\<esc>n."
endfunction
nnoremap <expr> <leader>cn ChangeName()

您可以键入\cn,然后提示您输入新名称。一旦您输入了新名称并按下ENTER键,它将用新名称替换前两个出现的John
该函数返回映射的扩展,使用<expr>在map命令中,使其使用函数的返回值。(让函数发出一个带有:execute:normal!命令也是一种可能的方法。)
你提到了按下@qJim@qJane,在没有提示的情况下新名称“感觉”像命令的一部分。通过从映射背后的函数中使用getchar()来循环,可以更接近这样的东西。您仍然需要决定如何终止名称,您会在末尾输入ENTER吗?您将使用时间来决定何时结束命令(需要快速输入吗?)如果使用getchar(),您可能还需要处理退格和取消命令。使用input()肯定更容易,根据您特定的用例可能足够。

我喜欢这个,特别是 getchar() 部分。 - Daniel Cooke
1
getchar() 可能会有些棘手... 但如果你对此感兴趣,可以看一下 vim-surround 的前几行,里面有一些关于此的想法。vim-surround 插件执行类似于获取环绕字符和 HTML 标签的操作,这或许与你所需的接近。 - filbranden
function 后面的感叹号是用来干什么的? - Martín Nieva
1
@MartínNieva 请查看:help :function。(一般来说,Vim拥有非常丰富的帮助系统,请首先使用它,因为在大多数情况下它可以直接回答您的问题。) - filbranden

2
尽管q经常与术语一起使用,但它的目的只是记录和重复一系列字符。重复是关键 — 如果您的意图不是重复完全相同的序列,则q可能不是理想的解决方案。
您应该可以通过使用映射轻松实现此操作(请注意,:h macro将指向:map)。您可以使用:execute构建一个命令,该命令将在当前行和下一行中替换第一次出现的Johninput()返回的任何内容。后者负责提示用户。
这里有一个小例子,将其映射到,n
:nnoremap ,n :exe ',+s/John/' . input('Name: ')<CR>

如果您不知道要替换的名称,也可以询问:

:nnoremap ,n :exe ',+s/' . input('Old name: ') . '/' . input('New name: ')<CR>

或者使用一个正则表达式来捕获“name”之后的第一个单词,忽略“:”或“is”。
:nnoremap ,n :exe ',+s/\vname(: \| is )+\zs\w+/' . input('New name: ')<CR>

虽然这肯定是一个解决方案,但它并不像我想要的那样通用。不幸的是,似乎没有办法从宏中接受输入。使用这种解决方案(除非我弄错了),我将不得不映射一个新命令,并将 ,+s/John/ 替换为我想要替换的名称。 - Daniel Cooke
@DanielCooke,宏中有一种接受输入的方法,如上所示。请注意宏和使用“q”记录的重复按键之间的区别。至于用你想要的名字替换John,你是指手动吗?当然不是。你试过执行我发布的解决方案了吗?输入解决方案,然后你可以用,nJoe,nJane等来替换你的名字。 - sidyll
是的,我的意思是 - 这个命令对于不是John的名字是无效的。 如果我想用'Dan'替换'Chris' - 你已经在命令中硬编码了字符串',+s/John/'。所以正如我之前所说的 - 每次我想更改一个新的名字,我都必须更改这个硬编码的字符串。 - Daniel Cooke
@DanielCooke 好的,你在最初的问题中没有提到这一点,但我会更新我的答案并提供几个解决方案。 - sidyll
只是为了进一步澄清,当你使用:map时,你并不是在创建一个命令,而是在创建一个宏。命令是用:command!创建的。重复按键序列可以用q记录。 - sidyll
@siidyll 谢谢你的更新!在这个宏中使用 cw 是可能的吗?如果可以的话,那就太完美了。 - Daniel Cooke

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接