6

I set up this function in my .vimrc file. It duplicates the current window, moves it to a new tab, reads the output of yapf (a code formatter) run on the current file, sets up a diff between the original file and the formatted file, and selects the original file window. Then I proceed to merge the format changes I like by using ]c and [c to navigate the changes and do/dp to accept the formatted change or keep the original.

Here's the function I use:

function! Yapf()                                                                
         exe "normal \<C-w>s\<C-w>T"                                                 
         vnew                                                                    
         setlocal buftype=nofile                                                 
         read !yapf #                                                            
         windo diffthis                                                          
         wincmd p                                                                
endfunction

(I came up with this with help from answers to a previous question)

But I tend to run this on scripts that start with a shebang line, and it always seemed to suggest a blank line at the start of the file (which would break the shebang behaviour). E.g. I created a new file with the following content:

#!/usr/bin/python2

print "hi"

Then calling call Yapf() in vim, the resulting diff shows a blank line at the top of the output of the yapf call.

Calling yapf from the command line directly on the file produces an output with no extra initial line.

Is this extra line coming from how I am using read? How can I fix my function to avoid this issue?

3 Answers 3

8

I don't have a good explanation for why Vim does this, it just does. :help :read says that the :read command inserts text below the cursor and Vim considers the cursor in an empty buffer to be on the first line, even though there is no line in the buffer at that point. So a :read into an empty buffer will always leave an empty first line.

To work around that, I always follow the :read command in such scripts with the command:

0d_

which deletes the first line into the "black hole register". See :help quote_. That way, no normal registers are affected by the deletion.

You can also work around this by replacing that first line with the results of some command, as with your command as an example,

.!yapf #
1
  • 0d_ is reliable enough that it's still used in the example command for diffing buffer contents against file contents (see :h :DiffOrig). Thanks for this reminder, though.
    – Walf
    Nov 15 at 7:26
4

You have options:

  • Filter the contents of your new split via %!yapf #
  • Delete the last line :1d_

Let's clean up that function a little:

  • stop messing with the window layout. aka no <c-w>s
  • wipe the buffer when we are done
  • stop diff'ing when close the buffer
  • set the filetype so we get some highlighting

All together now:

function! Yapf()                                                                
  let bufnr = bufnr('%')
  tabnew
  execute 'b' . bufnr
  vnew                                                                    
  setlocal buftype=nofile                                                 
  setlocal bufhidden=wipe                                                 
  setlocal noswapfile
  read !yapf #                                                            
  0delete_
  let &filetype=getbufvar('#', '&filetype')
  execute 'autocmd BufWipeout <buffer> diffoff!'
  diffthis
  wincmd p                                                                
  diffthis
endfunction

For more help see:

:h range!
:h :d
:h quote_
:h 'filetype'
:h getbufvar()
:h tabnew
:h :b
:h bufnr()
:h :diffoff
:h BufWipeout
:h 'bufhidden'
1
  • Very helpful! If you put this into your answer on the other question I'll accept your answer there? (Just since much of the answer is more relevant to the general problem of diffing with the output of an external command than the issue of the blank extra line)
    – LangeHaare
    Nov 29, 2017 at 14:58
4

Read interprets the address 0 to mean "before the first line", which effectively puts its output on line 1. This works for me interactively in Vim 8:

:0read name

:h {address} describes address 0 and even uses :read as an example :)

1
  • Hmm thanks! Replacing read !yapf # with 0read !yapf # and removing 0delete_ in Peter Rincker's function does not seem to work for me sadly (still have the issue with the first line) - would be nice if this worked though to reduce vimrc length by 1 line!
    – LangeHaare
    Dec 1, 2017 at 11:07

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.