如何在Vim中对同一文件中的两个子程序进行“diff”比较?

44

在同一个文件中,是否可以对两个非常相似的子程序进行diff或甚至是vimdiff比较?如果可以,该如何做呢?

我能想到的方法是将这两个子程序分别复制到两个单独的文件中,然后再进行diff比较,但是有没有在原始文件中直接进行比较的方法呢?

6个回答

53
插件linediff.vim:对两个文本块执行交互式差异比较与Vincent指出的那个类似,但还有一些额外的功能:
  • 有一个关闭打开缓冲区的命令
  • 似乎可以在没有图形界面的情况下工作
  • 在被比较的原始文件上插入一些视觉指示。

要使用它,您需要对第一个要进行差异比较的文本块进行可视选择,然后输入命令:Linediff,然后对第二个文本块重复此操作。要退出,请使用:LineDiffReset

我发现以下映射很有帮助:

noremap <leader>ldt :Linediff<CR>
noremap <leader>ldo :LinediffReset<CR>

9
这个插件非常好用。它有git的文档和维护记录。它可以在原始文件中突出显示所选块。它不需要gvim。我无法想象如何改进它,而且在我看来,这是最好的解决方案。请尝试并点赞。 - Sam Watkins
这个链接似乎在这个回答发布后的11年里失效了。但它可能是与AndrewRadev/linediff.vim相同的东西吗? - undefined

25

在原始文件中无法这样做,但是您可以通过仅使用单独的缓冲区而不是单独的文件来完成此操作。如果您将一个子例程复制到寄存器 a(例如,在可视模式下输入 "ay),并将另一个子例程复制到寄存器 b,则应该可以实现此操作:

enew | call setline(1, split(@a, "\n")) | diffthis | vnew | call setline(1, split(@b, "\n")) | diffthis

自动化:

let g:diffed_buffers = []

function DiffText(a, b, diffed_buffers)
    enew
    setlocal buftype=nowrite
    call add(a:diffed_buffers, bufnr('%'))
    call setline(1, split(a:a, "\n"))
    diffthis
    vnew
    setlocal buftype=nowrite
    call add(a:diffed_buffers, bufnr('%'))
    call setline(1, split(a:b, "\n"))
    diffthis
endfunction

function WipeOutDiffs(diffed_buffers)
    for buffer in a:diffed_buffers
        execute 'bwipeout! ' . buffer
    endfor
endfunction

nnoremap <special> <F7> :call DiffText(@a, @b, g:diffed_buffers)<CR>
nnoremap <special> <F8> :call WipeOutDiffs(g:diffed_buffers) | let g:diffed_buffers=[]<CR>
请注意,如果Vim拒绝放弃更改的文件(请参见:h abandon),则您可能希望设置hidden选项。

1
这个很好用,但是每次我通过f8调用WipeOutDiffs时都会收到“未清除缓冲区:bwitpeout!2”(数字会改变)的提示。这是什么原因?谢谢!! - Stop Slandering Monica Cellio
这是一个很棒的回答。非常感谢。 - netskink

7

您可以将这两个部分/子程序/部分写入两个文件中,然后使用vimdiff查看它们之间的差异。

    :1, 39 write part1          //any line range or marks can be used
    :40, 79 write part2
    :!vimdiff part1 part2

如果您不喜欢使用行号,您可以将光标放在该部分的开头,按v并选择到该部分的末尾,然后按:。它将显示:“'<,'>”。然后在命令行中输入write和文件名。按回车键。同样,对第二个也做同样的操作。然后,您可以执行上面提到的vimdiff命令。
(write命令将其保存为新文件。)编写一个新文件可能不是一个好主意,但这有助于我。特别是当我们需要多次进行比较时。
这是一种简单的方法,无需使用插件或关注内存。

3

我一直在使用这个命令:

vimdiff <(cat file.foo | sed -n 10,15p) <(cat file.foo | sed -n 20,25p)

这里需要将数字馈送给sed,这些数字是我想在文件中进行差异比较的行号。 <(*) 表示对其进行评估并将其重定向为输入。


阅读一段时间后,仍然在想为什么我使用了 cat... 如果省略它也应该可以工作:vimdiff <(sed -n 10,15p file.foo) <(sed -n 20,25p file.foo) - kuzyn

3

我非常喜欢ZyX的答案,但需要进行两个修改才能无缝运行:

  1. 按照实现方式,<F7> 用垂直分割的diff显示替换了活动缓冲区。然后,<F8> 关闭了diff,但没有重新加载原始缓冲区。为了解决这个问题,我将第三行的 enew 改为 execute 'tab split | enew'

  2. 为了最大程度地减少副作用,在 WipeOutDiffs() 结束前,我添加了 call remove(a:diffed_buffers, 0, -1)

希望有所帮助, - Stu


希望你能发布完整的文件。我没有任何错误,所以我不确定这些更改是否已经存在,或者是Vim在这些年里发生了变化,使它们变得无关紧要。 - netskink

1
你可以尝试使用Block diff vim plugin, 它会在新标签页中创建2个新缓冲区以显示差异之处。

我看过它的代码:它几乎完全做到了我上面解决方案所做的事情,但是它不能通过按一个键来关闭已打开的缓冲区。 - ZyX
非常好,但它只在有GUI时才有效,而当我在远程服务器上工作时并不总是这种情况。因此,我选择接受ZyX的答案。无论如何,谢谢! - Nigu
实际上它在非 GUI 版本的 vim 上运行正常(使用 vim 7.3)。它使用了非 GUI 标签页实现方式,也许当编写此插件时该方式还不存在,所以现在它可以正常工作? - lessthanideal

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