dense-analysis / ale

Check syntax in Vim/Neovim asynchronously and fix files, with Language Server Protocol (LSP) support
BSD 2-Clause "Simplified" License
13.48k stars 1.43k forks source link

Support fixers which must look at files on disk #611

Open w0rp opened 7 years ago

w0rp commented 7 years ago

Some tools for fixing files might have to only work with files on disk in exactly the right path. ALE should support these tools. Here is what needs to be done.

  1. Add a new option fix_file which when set to 1 will tell ALE that the file will be modified.
  2. Before jobs for fixers with fix_file are run, write changes to the file so far with writefile.
  3. After job for fixers with fix_file are finished, load the lines of input for the next fixer back from the file.
  4. Fixers with fix_file set to 1 should be skipped when fixing is done from an event for fixing files while typing.
JeanMertz commented 5 years ago

I know this is quite an old issue, so perhaps this has long been resolved.

However, I ran into a situation like this (?) today, when I wanted to use rustfix as a fixer for Rust in Ale.

Here's what I had to do to make this somewhat work:

call ale#Set('rust_rustfix_executable', 'cargo fix')
call ale#Set('rust_rustfix_options', '--allow-dirty --allow-staged')

function! RustFix(buffer) abort
    let l:executable = ale#Var(a:buffer, 'rust_rustfix_executable')
    let l:options = ale#Var(a:buffer, 'rust_rustfix_options')

    return {
    \   'command': l:executable
    \       . (empty(l:options) ? '' : ' ' . l:options),
    \   'read_buffer': 0,
    \   'process_with': 'RustFixCallback',
    \}
endfunction

function! RustFixCallback(buffer, output) abort
    " edit
    return []
endfunction
let g:ale_fixers = {
    \ '*': ['remove_trailing_lines', 'trim_whitespace'],
    \ 'rust': ['rustfmt', 'rustfix'],
    \ }
augroup ALEPostFix
    autocmd!
    au User ALEFixPost edit
    " au User ALEFixPost checktime %
augroup END

The reason I needed this hack is that:

  1. cargo fix (which is the command to run rustfix) works on an entire workspace, not just a single file
  2. it also does not accept any input on stdin, nor does it produce any (usable) output on stdout.
  3. I managed to make it apply the changes, and could see them on my filesystem, but Vim didn't reload the file, so the changes weren't visible after running ALEFix
  4. If I used the (commented out) edit inside RustFixCallback, it would work, but if there was another fixer that still had to run, it would error with "The file was changed before fixing finished"
  5. I say that it somewhat works, because if multiple fixers are executed (such as rustfmt, together with my rustfix) after saving, I have to save a second time to reflect the actual rustfix changes in my buffer.

I figured I'd give you this example. Perhaps there's already a way to handle this now? I realise that a fixer that works on an entire workspace/directory isn't really something that ALE supports, but I wanted to leverage the tooling of ALE, without having to use my own shenanigans to start some kind of background process and handle reloading, etc.

Any thoughts on if there is a better way to tackle this? If not, any thoughts on if ALE should handle this and how that should work?

w0rp commented 5 years ago

Fixing an entire workspace sounds like something out of scope for ALE. ALE is designed to lint or fix one file at a time, and in some cases point out where problems originated in other files.

JeanMertz commented 5 years ago

Fixing an entire workspace sounds like something out of scope for ALE.

Yeah, I figured. I guess the reason I posted it here is not so much for the workspace-wide support, but more the question on how to handle fixers that save their changes to the (not a temporary) file, and that file has to then be reloaded in Vim.

The fact that this specific linter is workspace-wide isn't that important to me, it's more that ALE currently doesn't have a way (if I'm correct in my research) to fetch the changes from the changed file on disk.

w0rp commented 5 years ago

It works for temporary files, but not for the file you're currently fixing yet.

ariciputi commented 5 months ago

Hi, I found this issue after digging around for a while. What I'm trying to do is to add support for F# linter/fixer/LSP into ALE, and I would be happy to submit a PR once done. However I got into an issue quite similar to the one described here.

In my case Fantomas (which is the F# code formatter) only supports write on disk mode. Working with temporary files is not viable since Fantomas supports .editorconfig files (which I use myself and I would like to support too), and moving the to-be-fixed file to a temporary directory breaks EditorConfig completely (which I think will become an issue more and more widely spread as EditorConfig will be adopted, by the way).

So as described above I managed to get the fixer to work correctly, but the buffer is not refreshed unless I do some autocommand magic which I would like to avoid if possible.

So all in all, is there any stantard/official way of reloading buffer content when write-on-disk mode is the only way to go?