sbdchd / neoformat

:sparkles: A (Neo)vim plugin for formatting code.
BSD 2-Clause "Simplified" License
1.99k stars 189 forks source link

Do not format files/lines which weren't formatter-compliant before edits #197

Open clbarnes opened 5 years ago

clbarnes commented 5 years ago

Some maintainers get upset if you make a small change and the entire file gets reformatted, clobbering their diffs.

One way around this is to check if a file is compliant before you open it, and then skip reformatting if it wasn't. However, as neoformat works with lots of different formatters with possibly varying configurations, you'd need to remember which one etc. You'd also need to read the file multiple times, and you wouldn't get the benefit of reformatting your own code if you added, e.g. a function to a much larger (non-compliant) module.

The other way around it is to get the diff between the original file and the buffer, and only format the lines which have been changed (using the same functions as the visual selection stuff). It would obviously fail if that set of lines did not produce valid AST, but in that case, a conservative reformatter probably wouldn't want to reformat them anyway.

clbarnes commented 5 years ago

I approximated some of this functionality in my config; here's an example with black: it checks whether a newly-opened buffer is pretty, and disables neoformat if it's not.

let black_root = $HOME . "/.pyenv/versions/nvim3/bin/black"

" neoformat config needs to be set up for each tool
let g:neoformat_python_black = {
    \ 'exe': black_root,
    \ 'args': ['-'],
    \ 'stdin': 1
\}
let g:neoformat_enabled_python = ['black']

augroup fmt
    autocmd!

    " the below line needs to be replicated for every format-checking tool
    autocmd BufReadPost *.py exe "w !" . black_root . " --check -" | let b:neoformat_disable = v:shell_error
    autocmd BufWritePre * if (!exists("b:neoformat_disable") || !b:neoformat_disable) | undojoin | Neoformat | endif
augroup END

This lacks a few possible advantages:

Rather than having a separate variable, I guess I could set b:neoformat_enabled_python = []

Could use the FileType event instead of read, as well, because then you'd have access to much more sensible variables.

clbarnes commented 5 years ago

Here's a second pass, which actually cuts neoformat out of the equation (and obviously loses all the benefits of the error checking and so on).

https://gist.github.com/clbarnes/2555fd49034b1361318b3e4ace0ca8ae