如何在Vim中对比打开文件中的两行?

39

我偶尔会在我的代码中看到非常长的行,我需要检查它们是否相同。在vim中是否有一种方式可以选择两行并将它们进行比较以显示这两行之间的任何差异?

例如,给定vim中的两行:

AVeryLongReturnType* MyLongClassName:hasAnEvenLongerFunction(That *is, Overloaded *with, Multiple *different, Parameter *lists);
AVeryLongReturnType* MyLongClassName:hasAnEvenLongerFunction(That *is, Overloaded *with, Multiple *different, Parameter *1ists);

我想让vim告诉我这两行实际上不同,因为它们拼写"lists"的方式不同。这种操作可能吗?如果是,怎么做?


如果在两个缓冲区中使用 :diffthis (:difft) 命令,它们将被进行差异比较。 - Evgeni Sergeev
刚刚注意到这个代码被复制到这里,没有任何提及原作者的地方。不知道是否涉及侵犯版权/开放版权,但这并不公平。 - Paul Evans
5个回答

50

一种快速而不太规范的解决方案是选择这两行并在去重的同时进行排序:

  • 选择要处理的行
  • 输入命令":sort u"
  • 如果只剩下一行,那么这两行相等;
  • 如果两行都保留下来了,那么它们之间必定存在差异。

如果需要,可以通过撤销操作恢复所有更改。


2
太聪明了。我一直在想一个好的干净的解决方案! - Cory Klein
你会如何做到这一点,并在不同的分割窗口中呈现结果? - serup
@serup,“结果”指您想在一个新的分割窗口中看到“different”或“equal”这两个单词吗?实话实说,我不认为提出的黑客方法可以在没有再次引入一些辅助脚本的情况下完成这项任务,而这些辅助脚本将会计算上面“第二个点”的前后行数……如果这个数字发生了变化,那么这些行就是相等的,反之则不同。 - jelsayeh
@CoryKlein 我不会称这为“干净”的解决方案,但它非常高效地完成了任务 :-) - jelsayeh
我喜欢它!谢谢。 - netskink

9
一种替代@sehe方法的方式不需要使用临时文件:
funct! DiffTwoTexts(text1, text2)
  new
  put =a:text1
  normal ggdd
  diffthis
  new
  put =a:text2
  normal ggdd
  diffthis
endfunct

funct! DiffTwoLines(line1, line2)
  let text1 = getline(a:line1)
  let text2 = getline(a:line2)
  call DiffTwoTexts(text1, text2)
endfunct

comma! DiffWithNext call DiffTwoLines('.', line('.') + 1)

由于所有内容都在同一行上,因此这篇文章仍然很难阅读,所以我进行了修改:

funct! EvalTextPreprocessor(expr, text)
  let text = a:text
  return eval(a:expr)
endfunct

comma! -nargs=1 DiffWithNextPre call DiffTwoTexts(
      \ EvalTextPreprocessor(<q-args>, getline('.')),
      \ EvalTextPreprocessor(<q-args>, getline(line('.') + 1)))

这个新命令接受一个vimscript表达式作为参数,在表达式中,变量text指的是正在被预处理的行。因此,您可以调用例如:

DiffWithNextPre split(text, '[(,)]\zs')

对于您的样本数据,这将提供两个缓冲区。
AVeryLongReturnType* MyLongClassName:hasAnEvenLongerFunction(
That *is,
 Overloaded *with,
 Multiple *different,
 Parameter *lists)
;

并且

AVeryLongReturnType* MyLongClassName:hasAnEvenLongerFunction(
That *is,
 Overloaded *with,
 Multiple *different,
 Parameter *1ists)
;

只有以Parameter开头的行才会被突出显示。

您甚至可以从那里开始构建,创建一个命令。

comma! DiffTwoCFunctionSigs DiffWithNextPre split(text, '[(,)]\s*\zs')

请注意,我稍微修改了正则表达式,以便它将在行尾保留尾随空格。您可以通过将\s*移动到\zs之后来忽略它们。如果您不熟悉vim特定的RE原子,请参阅:help /\zs
另一个好处是使该命令接受一个范围(请参阅:help command-range),您可以通过将范围的第一行与最后一行进行比较来使用它。因此,您只需要从第一行到第二行进行可视选择并调用命令即可。

感谢您展示了创建带参数命令的示例。我有点不知所措(我的答案是我第一次编写的 Vim 脚本,超出了在 vimrc 中设置选项的范围...) - sehe
@sehe:可能需要花费很多时间来理解,是的 :) 如果你创建一个文件~/.vim/plugins/diff_two_lines.vim并将代码放在那里,每当你使用vim时就会有这些命令可用。或者实际上,你可以将它们放在你的.vimrc中;将其作为插件只是更加有组织。记住,你必须重新启动vim(或:source你的脚本)才能使命令生效。 - intuited

9

我使用了linediff.vim.

这个插件提供了一个简单命令,":Linediff",用于比较两块不同的文本。


这个插件的URL应该是这个,如果我没记错的话: https://www.vim.org/scripts/script.php?script_id=3745 - undefined

7

这不是一个功能,但可以轻松地编写脚本,例如在您的vimrc中:

function! DiffLineWithNext()
    let f1=tempname()
    let f2=tempname()

    exec ".write " . f1
    exec ".+1write " . f2

    exec "tabedit " . f1
    exec "vert diffsplit " . f2
endfunction

这将在另一个标签页中以垂直分割的方式打开当前行和下一行。 请注意,此代码仅为示例。

  • 它不检查下一行是否存在(是否有后续行)
  • 它不清理创建的临时文件
  • 一个很好的改进是选择范围或使用''标记来选择其他

如果要水平分割,则可以省略'vert'。

将其映射到一些花哨的东西上,这样您就不必手动:call它:

:nnoremap <F10> :call DiffLineWithNext()^M

非常好的答案!在变量名前面的“&”真的有必要吗? - Matthias Braun
@MatthiasBraun 哎呀,看来我在2011年对Vim脚本有一些误解!已纠正。 - sehe
我明白了。你现在为什么把所有命令都包裹在exec中?我只是好奇,想更多地了解 Vim 脚本。 - Matthias Braun
@MatthiasBraun 是的。只需从这里开始,例如 http://learnvimscriptthehardway.stevelosh.com/ (我的原始代码实际上错误地创建了名为“&f1”和“&f2”的文件,而没有使用tempname())。 - sehe

4
你也可以创建一个新的空窗口缓冲区并复制行,然后执行命令。
:windo diffthis 

这应该打开一个新窗口,显示这两行之间的差异。


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