Cory Klein
Cory Klein

Reputation: 55720

How to diff two lines in an open file in vim?

I occasionally see very long lines in my code that I need to check if they are the same. Is there a way in vim to select two lines and diff them to show any differences between the two?

For example, given the two lines in vim:

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

I would like vim to tell me that the two lines are in fact different because each spells "lists" differently. Is this possible, and if so, how do I do it?

Upvotes: 42

Views: 15579

Answers (6)

chipfall
chipfall

Reputation: 360

I like jelsayeh's answer for speed but if you want the "vimdiff" version, I came up with:

command! DL :tabnew | put! | diffthis | 2d | vnew | put! | diffthis

for my .vimrc. Then, select two lines into the unnamed buffer either with 2yy in normal mode or highlight and yank two lines in visual mode, then run :DL from command mode and see it in "vimdiff".

There's a superfluous blank line in each split but it doesn't affect the display. You need to :q! out of each split.

make "tabnew" just "new" if you would prefer in a split below instead.

Upvotes: 0

jelsayeh
jelsayeh

Reputation: 756

A quick and dirty solution is to just select both lines and sort them while removing duplicates:

  • select lines
  • ":sort u"
  • if only one line remains, both were equal
  • if both remain, there most be some difference

An undo recovers everything again.

Upvotes: 58

Sonia Hamilton
Sonia Hamilton

Reputation: 4419

I used linediff.vim.

This plugin provides a simple command, ":Linediff", which is used to diff two separate blocks of text.

Upvotes: 10

sehe
sehe

Reputation: 393457

That is not a feature, however it is easily scripted, e.g. in your vimrc:

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

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

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

This will open the current and next lines in vertical split in another tab. Note that this code is a sample

  • it doesn't check whether next line exists (there are any following lines)
  • it doesn't cleanup the tempfiles created
  • a nice improvement would be to take a range, or use the '' mark to select the other line

You can leave off the 'vert' in order to have a horizontal split

Map it to something fancy so you don't have to :call it manually:

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

Upvotes: 7

serup
serup

Reputation: 547

you could also just create a new empty window buffer and copy line, then make command:

:windo diffthis 

this should open a new window showing the differences of those 2 lines

Upvotes: 4

intuited
intuited

Reputation: 24044

An alternative to @sehe's approach would not require the use of temp files:

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)

This will still be pretty hard to read, since it keeps everything on a single line, so I came up with this modification:

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)))

This new command takes a vimscript expression as its argument, wherein the variable text refers to whichever line is being preprocessed. So you can call, e.g.

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

For your sample data, this gives the two buffers

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

and

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

Only the lines that start with Parameter are highlighted.

You can even build up from there, creating a command

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

Notice that I modified the regexp a bit so that it will keep trailing spaces at the end of lines. You could get it to ignore them entirely by moving the \s* to after the \zs. See :help /\zs if you're unfamiliar with what that vim-specific RE atom does.

A nicety would be to make the command take a range (see :help command-range), which you could use by diffing the first line of the range with the last line. So then you just visual-select from the first line to the second and call the command.

Upvotes: 9

Related Questions