2

I know I can enter diff mode in vim by calling diffsplit <filename>.

I also know I can call an external command and replace the file contents with its output (e.g. in this answer someone uses a json formatting tool by calling :%!python -m json.tool).

Say I wanted to do something similar, but wanted to see the diff of the current file and the output of the json formatter, and choose which changes to keep. How can I sent the output of the tool to diffsplit?

Notes:

  1. I could send the output of the formatter to a file in /tmp or something and then do vimdiff on the file from there. But it would be nice to avoid having to read and write an extra file to/from disk.
  2. I mainly want to do this for the whole file. If there's a way to send a specific range to the formatter and then diff that range (or diff the result of the command glued into the original file) that would be a nice bonus.
  3. I am not actually using a json formatter but am looking for a generic solution that would work for this situation (and similar).
  • 1
    In answer to just your question about diffing just a range of lines, I use the linediff plugin for that: github.com/AndrewRadev/linediff.vim. You can diff two ranges of different files or of the same file, make changes to those diffs, and when you quit the diff, the changes will be applied to the original file(s). – garyjohn Nov 11 '17 at 20:54
1

I have this one-liner and I hope this helps:

:vnew | setlocal buftype=nofile | exec "read !ls -l" | windo diffthis

You may (and probably will) replace ls -l with any other commands, and you may need shellescape() and expand() to compose your shell command for exec.

Regarding sending selected lines to the command line, you may better do a function, as after vnew it may be hard to get the selected lines (unless you have yanked it).

Hope these helps: :h buftype :h exec :h read! :h windo

0

Here is what I came up with for:

augroup FormatDiff
  autocmd!
  autocmd TextChanged * if &diff && get(g:, 'diff_format_prg', '') != '' | silent execute '%!'.g:diff_format_prg | let g:ignore = 1 | diffupdate | unlet g:ignore | | endif
  autocmd FilterWritePre if &diff && get(g:, 'diff_format_prg', '') != '' && get(g:, 'ingore', 0) == 0 | silent execute '%!'.g:diff_format_prg | endif
augroup END

I then did let g:diff_format_prg = 'jq -S .'. Followed by by :diffsplit anotherfile.json

The overview:

Try and run the formatter on the very first diff e.g. :diffpslit. And then for subsequent file changes (TextChange) run formatter and then do :diffupdate without triggering an extra formatting.

Some thoughts:

  • I am not sure how I feel about auto-fromatting on TextChange
  • Probably need to check for buffer-local variables instead of just global, g:diff_format_prg, if this was to be generalized
  • Need a better way of displaying formatter errors or handling them at all
  • Will fire on all filter commands in a diff'ed buffer (unexpected)
  • I am not certain how this could be applied to just a specific buffer range

For more help see:

:h TextChange
:h FilterWritePre
:h get()
:h b:
:h g:
:h :range!
:h diff
:h diffupdate

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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