在(NeoVim)中复制多行并替换匹配项
我想在nvim中选取多行,然后把我复制的内容粘贴到 :%s,\V...,以替换文件中所有出现的文本为一个新的字符串。举例来说,
foo([
"var1",
"var2",
"var3",
])
bar([
"var1",
"var2",
"var3",
])
理想情况下会变成
my_vars = [
"var1",
"var2",
"var3",
]
foo(my_vars)
bar(my_vars)
我知道在这种情况下,我可以只用对 :%s 编写某些表达式来实现,但我更倾向于避免这种做法,因为对于复杂的多行表达式,事情会变得非常乏味。
我知道可以使用类似 refactoring.nvim 的插件来提取变量,但这在这种场景下仅对单个可视区段有效,不能解决此处的问题。
解决方案
简单的部分
- 将光标放在
foo上,执行ci),剪下圆括号中的内容并将你带入插入模式。 - 输入
my_vars,退出插入模式。缓冲区应当看起来像这样:
``` foo(my_vars)
bar([
"var1",
"var2",
"var3",
])
``
3. 使用O在当前行之上打开一行。
4. 将刚才输入的名字按顺序插入、=、
``` my_vars = [ "var1", "var2", "var3", ] foo(my_vars)
bar([ "var1", "var2", "var3", ]) ```
路上的障碍
此时,你的列表仍然保存在未命名寄存器中,在命令行中很容易被插入:
:%s/<C-r>"
但很明显,这种方法行不通:
将多行文本复制并放入命令行其实是可行的,但它不会给你一个用于替换的合适正则表达式模式。为此,你必须辛苦地转义各种特殊字符,直到得到一个有效的模式。并不有趣。
真正的启示
我经常要做类似的事情,因此在我的Vim之旅早期就把以下内容整理了出来。
- 一个函数,用以从任意字符串中得到合适的模式的函数:
" return a representation of the selected text
" suitable for use as a search pattern
function! GetSelection(escaped)
let old_reg = getreg("v")
normal! gv"vy
let raw_search = getreg("v")
call setreg("v", old_reg)
if a:escaped
return substitute(escape(raw_search, '\/.*$^~[]'), "\n", '\\n', "g")
endif
return raw_search
endfunction
* 一个映射,用来精简上面的“路上的障碍”:
xnoremap <key> :<C-u>%s/<C-r>=GetSelection(1)<CR>/
因此,回到之前还没变复杂的地方:
my_vars = [
"var1",
"var2",
"var3",
]
foo(my_vars)
bar([
"var1",
"var2",
"var3",
])
我会…
- 使用可视模式选中列表,按
va[。 - 按
<key>进入命令行模式,得到一个干净的占位符:
:%s/\[\n "var1",\n "var2",\n "var3",\n\]/
3.输入替换文本:
:%s/\[\n "var1",\n "var2",\n "var3",\n\]/my_vars
4.再添加 "confirm" 标志,以跳过第一次匹配:
:%s/\[\n "var1",\n "var2",\n "var3",\n\]/my_vars/c
总的来看,看起来是这样的:
ci)
my_vars<Esc>
O<C-a> = <Cr>"<Esc>
va]<key>
my_vars/c<CR>
n
y
