英文:
(Neo)vim substitute over block/regular visual range, and not line visual range
问题
如果我在Vim的可视模式下突出显示一块文本,然后按下 :
键,我可以在它上面运行一个命令。例如:
:'<,'>s/foo/bar/g
用于替换,- 或
:'<,'> !bash
将这些行发送到bash
shell 并获取输出, - 或
:'<,'>sort
对这些行进行排序, - 或
:'<,'> !some_cli_util
将这些行发送到some_cli_util
。
如果我要执行的文本恰好位于单独的一行上,这是可以的,但通常情况下并非如此。例如,如果我正在编写文档并想要示例CLI命令及其输出,那么只需编写:
$ head requirements.txt
然后选择文本 head requirements.txt
,输入 : !sh<CR>
,就可以得到实际命令的输出。但实际上,当我选择 head requirements.txt
并按下 :
键时,我的选择会扩展为完整行选择,这会导致前导的 $
符号与实际命令一起被覆盖,因此由于前导的 $
符号,命令会失败。
一个不太琐碎的例子是,如果我有一个LaTeX表格,如下所示:
\begin{table}
\begin{tabular}{lllll}
col1 & col2 & col3 & col4 & col5 \\
foo19s & fook2e & foojd9 & alfooc & 9dfooj \\
9foo1s & ekfoo2 & lqfoo & alfoo & 9efoo9s \\
1sfoo9 & d9fooe & fooj9d & alsdfo & 9hfooje \\
19sfoo & 9dfooe & lqfoo & dfoo9e & ajfoo
\end{tabular}
\end{table}
我想将所有 foo
替换为 bar
,但只在标题为 col2
的列中。如果我只能块选择 col2
,然后执行替代命令 :
<,>s/foo/bar/g
就可以了。但当前,如果我块选择了 col2
,然后按 :
键,Vim会自动选择我的块选择范围内的所有行。
理想情况下,我希望Vim足够智能,如果我在块可视模式下按下 :
键,它将使用 :
<,>
范围选择器,但如果我在行可视模式下按下 :
键,则它将使用 :'<,'>
范围选择器。
:h
<告诉我
<
和
>` 标记确实存在,但我无法看到如何将它们应用于命令。
我可以做什么来启用Vim(或Neovim)以在块选择上执行命令?或者是否有关于这个问题的开放问题?
英文:
If I highlight a block of text in visual mode in Vim and then press :
, I can run a command on it. For example
:'<,'>s/foo/bar/g
to substitute,- or
:'<,'> !bash
to send the lines to thebash
shell and get the output, - or
:'<,'>sort
to sort those lines, - or
:'<,'> !some_cli_util
to send those lines tosome_cli_util
This is fine if the text I want to execute is conveniently on it's own line, but often that isn't the case. For example, if I'm writing docs and want to give and example of a cli command and it's output, then it would be nice to just write:
$ head requirements.txt
and then to select the text head requirements.txt
, type : !sh<CR>
, and be given the actual output of the command. But in reality, when I select head requirements.txt
and type :
, then my selection gets expanded to a full-line selection, which causes the leading $
to be clobbered in with the actual command, and so because of the leading $
, the command fails.
A less trivial example of where this would be useful, is if I had a LaTeX table like:
\begin{table}
\begin{tabular}{lllll}
col1 & col2 & col3 & col4 & col5 \\
foo19s & fook2e & foojd9 & alfooc & 9dfooj \\
9foo1s & ekfoo2 & lqfoo & alfoo & 9efoo9s \\
1sfoo9 & d9fooe & fooj9d & alsdfo & 9hfooje \\
19sfoo & 9dfooe & lqfoo & dfoo9e & ajfoo
\end{tabular}
\end{table}
and I wanted to replace all the instances of foo
with bar
, but only in the columns titled col2
. If I could block-select only col2
, and then execute a substitute command like :`<,`>s/foo/bar/g
then this would work. But currently, if I block select only col2
and then press :
, Vim will auto-select the entire range of lines that were in my block selection.
Ideally, I'd like vim to be smart enough such that if am in block-visual mode and press :
, then it'll use the :`<,`>
range selector, but if I'm in line-visual mode and press :
then it'll use the :'<,'>
range selector.
:h `<
tells me that the `<
and `>
marks do exist, but I can't see how to apply them to commands.
What can I do to enable vim (or neovim) to execute commands on block-selections? or is there an issue open for this problem?
答案1
得分: 2
> 我不能只使用常规的可视行选择器 :'<,'>s/ba/__/g
,因为那会不正确地将所有的 baz
替换为 __z
。
是的,你绝对可以,但要使用更精细的搜索模式:
:[range]s/ba\zer/__/g
请参考 :help \ze
。
--- 编辑 ---
正如另一个答案中所解释的,Ex 命令只适用于行,范围是行的范围。你无法将除了一行或多行之外的其他内容传递给 :help :s
、:help :range!
或 :help :sort
。
没有绕过这一点的方法。
虽然Ex 命令的范围始终是逐行的,但其中一些命令可能以某种方式遵守可视选择。
在:help :s
的情况下,你可以再次使用更精细的搜索模式:
<C-v><motions>
:[range]s/\%V.*\zsfoo\ze.*%V/bar
请参考 :help \%V
。
英文:
> I can't just use the regular visual-line selector :'<,'>s/ba/__/g
because that will incorrectly replace all the baz
s with __z
s.
Yes, you definitely can, but with a more refined search pattern:
:[range]s/ba\zer/__/g
See :help \ze
.
--- EDIT ---
As explained in the other answer, Ex commands only work on lines and ranges are ranges of lines. You wont be able to pass something else than one or several lines to :help :s
, :help :range!
or :help :sort
.
There is no way around that.
While the range of Ex commands is always linewise, some of them may honor the visual selection in some way.
In the case of :help :s
, what you can do is, again, use a more refined search pattern:
<C-v><motions>
:[range]s/\%V.*\zsfoo\ze.*%V/bar
See :help \%V
.
答案2
得分: 1
如何完成这个任务在romainl的回答中已经完美描述了。然而,我想回答一下“为什么要这样做?”。
你写道:
> 理想情况下,我希望Vim足够智能,以至于如果我在块可视模式下按:,那么它将使用:<,
>范围选择器,但如果我在行可视模式下按:,它将使用:'<,>'范围选择器。
这种行为目前是不可能的,至少在现在(Vim 9.0)是不可能的。引用自:h :visual_example
:
> 目前,“:”命令只对整行起作用。当你选择一行的一部分,执行像“:!date”这样的操作将替换整行。如果你只想替换一行的一部分,你将不得不为它创建一个映射。在未来的版本中,“:”可能会在部分行上起作用。
同样,:h :s
中也有(重点是):
> 对于[range]中的每一行,替换{pattern}的匹配项
人们普遍误解Vim只操作(块)可视选择。并不是所有命令都是如此。相反,它们操作被选中的行。
所以,你将不得不使用类似于(正如romainl所指出的,为了完整起见):s/ba\zer/__/g
这样的行替换。
英文:
How to get it done is described perfectly in romainl's answer. I'd like to answer the "why?", though.
You wrote:
> Ideally, I'd like vim to be smart enough such that if am in block-visual mode and press :, then it'll use the :`<,`> range selector, but if I'm in line-visual mode and press : then it'll use the :'<,'> range selector.
This behavior is explicitly impossible, at least for now (Vim 9.0). Quoting from :h :visual_example
:
> Currently the ":" command works on whole lines only. When you select part of
> a line, doing something like ":!date" will replace the whole line. If you
> want only part of the line to be replaced you will have to make a mapping for
> it. In a future release ":" may work on partial lines.
Likewise, :h :s
says (emphasis mine):
> For each line in [range] replace a match of {pattern}
It is a common misconception that Vim would operate on the (block) visual selection only. Not all commands do. Instead, they operate on lines that are selected.
So you'll have to use a line-wise substitution like (as pointed out by romainl and included for the sake of completeness) :s/ba\zer/__/g
.
答案3
得分: 1
这是一个合理的工具查找,但正如其他人指出的,范围仅适用于逐行。对于那些希望将选择内容用作外部程序参数的情况,我建议将其复制并粘贴到命令行中。
一个可能为您提供通用解决方案的插件是 vis:https://github.com/vim-scripts/vis。而不是:'<,'>s/foo/bar/g
,您可以执行:'<,'>B s/foo/bar/g
,这不太方便,但可以完成任务。
英文:
It's a reasonable tool to look for, but as others have pointed out, ranges are only line-wise. For the cases where you'd like to use a selection as an argument to an external program, I'd recommend yanking and pasting in the command-line.
A plugin that might give you a general-purpose solution is vis: https://github.com/vim-scripts/vis. Instead of :'<,'>s/foo/bar/g
, you'd execute :'<,'>B s/foo/bar/g
, which is less convenient, but will do the trick.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论