如何在Vim中快速更改变量名称?

119

我正在使用 Vim 阅读大量包含许多单字母变量名的 C 和 Perl 代码。

当我在阅读代码过程中,如果有一些命令可以快速更改变量名为更有意义的名称,那就太好了,这样我就可以更快地阅读剩余的代码。

Vim 中是否有一些命令可以让我快速完成这个操作?

我认为正则表达式不适用于此原因如下:

  1. 相同的单字母名称在不同的作用域块可能会有不同的用途;并且

  2. 相同的字母组合可能是另一个更长的变量名、字符串文本或注释的一部分。

是否有已知的解决方案?

7个回答

252
以下是如何重命名当前作用域中定义的变量的步骤:
1. 将光标移动到变量的使用处。 2. 按下 `gd`。这意味着将光标移动到定义处。 3. 然后按下 `[{` - 这将使光标跳转到作用域的起始位置。 4. 按下 `V` - 将启用可视行选择。 5. 按下 `%` - 将跳转到相对应的 `}`,从而选择整个作用域。 6. 按下 `:s/` - 开始替换命令。 7. `/` - 将插入与变量名匹配的模式(即在按下 `gd` 之前所在的变量名)。 8. `/newname/gc` - 将启动搜索和替换,并在每次匹配时进行确认。
现在您需要录制一个宏,或者更好地,映射一个按键。
以下是最终的映射:
" For local replace
nnoremap gr gd[{V%::s/<C-R>///gc<left><left><left>" For global replace
nnoremap gR gD:%s/<C-R>///gc<left><left><left>

将以下内容添加到你的`.vimrc`文件中或直接执行。 在本地变量上按下`gr`将会跳转到`:s`命令,你需要输入`new_variable_name`并按下Enter

55
学习超过5个我以前应该知道的Vim技巧,点赞!谢谢。 - Kenny Meyer
6
注意:在MacVim中的JavaScript模式下,gd无法定位变量定义的当前作用域中,它会跳转到文件中某个函数中第一次使用该变量名称的位置 :( 如果您在某个作用域内并想选择该作用域,我认为使用“va {” - “可视化选择 {” - 是正确的方法。 - Srikumar
5
稍微改进的版本:https://gist.github.com/048616a2e3f5d1b5a9ad 提示用户,显示旧名称,在替换后恢复光标位置 - Andy Ray
8
+1非常好的vimgineering(我认为我刚刚创造了一个新词)。注意:我实际上需要按两次冒号(“:”)键。第一次按下它时,我看到“:'<,'>”。如果我从那里只是输入“s/”,它不起作用;我必须在“s/”之前再输入一个冒号。 - Kelvin
10
请注意,在替换命令中您不需要使用 <C-R>。简单地省略搜索部分将导致 Vim 使用上次搜索的模式,而在这种情况下该模式是由 gd 命令创建的。因此,只需执行 :s//<newname>/gc 即可。 - shangxiao
显示剩余9条评论

44

我知道这是一个老问题,而且@mykola-golubyev的方法显然是OP问题中最好的答案(我假设你正在处理混淆的代码,其中可能会有多个带有相同变量名的块); 但是,由于问题名称,许多从Google搜索进入此处的人可能会寻找在VIM中重新命名变量的更通用方法-并且这些方法可能更简洁。

我很惊讶没有人提出这种方法:

* :s// NEWNAME /gc

*gn相同-搜索光标下单词的下一个出现并将其作为上次搜索的模式; 然后您可以在替换命令中省略搜索模式,VIM将假定最后一个模式是要搜索的模式。

对于较少量的var副本,以下是更快的一种方法:

* cw NEWNAME <esc> 然后重复使用n.进行其他出现的操作

*搜索出现次数cw更改单词n转到上次搜索的术语的下一个出现,并且.重复最后一条命令(现在为将单词更改为NEWNAME)

这些技巧的知识来源于Reddit上的@doomedbunnies

另一个酷炫的技巧:(@nobe4提供)

* cgn NEWNAME <esc>,然后重复使用.查找其他出现的情况

cgn代表"更改下一个出现的结果"。由于这是最后一个命令,所以您不需要n去到下一个出现的位置,因此击键次数减少了,更重要的是,不需要交替使用n.。但是,显然,这种方法的缺点是没有跳过出现的方式。

以下是与其他类似方法或具有重构支持的特定语言插件相比的一些优点:

  • 无需命令映射,不需要调整.vimrc(或init.vim),因此您可以在任何VIM副本中使用它(例如,在某个VPS上快速执行任务或朋友的机器上使用它,您按自己的方式配置VIM将会违背“快速”的目的)
  • 使用*gn进行单词选择非常快--只需一个按键(好吧,假设是1.5个按键)
  • 使用*gn可以确保您不会在其他单词内部获得任何匹配项,就像:%s /<C-R>//gc一样。比手动输入:%s / \<OLDNAME\> / NEWNAME / gc 更好:我个人倾向于忘记使用\<来仅限于匹配整个单词。
  • 不使用范围只会导致多击几次n以跳过不想要的匹配项--这可能甚至比需要将范围限制到某个代码块所需的额外击键还少。在正常情况下,您的变量很可能已经局限于某个代码块。

10
据我所知,VIM 实际上没有重构支持。在进行重构时,我通常会采取以下预防措施:
  1. 使用标记限制更改范围。
  2. 在输入正则表达式时,用\<和>括起名称。这将使其匹配整个单词,从而减少不正确的重命名类型。
  3. 不要进行多行替换以减少出现错误替换的机会。
  4. 仔细查看代码差异,如果不是一个小的更改话。
我的最终更改看起来像这样
:'a,'bs/\<foo\>/bar

我很希望我错了,即VIM没有重构工具,但是我还没有看到它。


在Perl中,您还可以将类型添加到搜索模式中。即$表示标量,@表示数组,%表示哈希。 - Nathan Fellman
@Nathan:不幸的是,在 Perl(5.X)中,数组和哈希的符号随用途而改变:%hash -> 整个哈希,$hash {key} -> 单个值,@hash {qw / a b /} 哈希切片。 - user55400

9
把这段代码放入你的 .vimrc 文件中。
" Function to rename the variable under the cursor
function! Rnvar()
  let word_to_replace = expand("<cword>")
  let replacement = input("new name: ")
  execute '%s/\(\W\)' . word_to_replace . '\(\W\)/\1' . replacement . '\2/gc'
endfunction

使用:call Rnvar()进行调用。

expand("<cword>")获取光标下的单词。搜索字符串使用%作为文件范围,\(\W\)模式用于查找要替换的单词边界处的非单词字符,并将它们保存在变量\1\2中,以便在替换模式中重新插入。


4

您可以在全局搜索和替换中使用 'c' 修饰符,它会要求您确认每个替换。这可能需要更长的时间,但对于非巨大的代码文件可能有效:

%s/\$var/\$foo/gc

c代表确认。

i代表忽略大小写。你需要c,确认每个更改。 - Michael Kristofik
抱歉..对此感到抱歉..已修正答案。 - Adnan
答案仍未完全修复 ;) - user55400
你是指转义的 $ 吗?这其实是我的个人偏好。$ 在正则表达式和 vi 中都有特殊含义,虽然不转义也可以使用,但为了更加清晰明了,我还是喜欢将其转义。 - Adnan

0
在C语言中,您可以尝试使用cscope来提高编程效率。它会尝试理解语法,因此有可能知道哪些字母是变量。

0
如果这是跨多个文件的,您可以考虑查看sed。使用find来获取您的文件,然后使用xargs加上sed进行替换。假设您想要在所有匹配*.c的文件中将a替换为a_better_name,您可以执行以下操作:
find . -name "*.c" | xargs sed -i -e 's/a/a_better_name/g'
请注意,这将替换所有出现的a,因此您可能需要更强大的正则表达式。

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