Skip to content

Instantly share code, notes, and snippets.

@denvaar
Last active June 22, 2022 05:55
Show Gist options
  • Save denvaar/675bab58237cf31ec8a851e4bdc93955 to your computer and use it in GitHub Desktop.
Save denvaar/675bab58237cf31ec8a851e4bdc93955 to your computer and use it in GitHub Desktop.
`mix format` on save in vim

There Are a few challenges when it comes to automatically mix format-ing your files on save in Vim.

  • Needs to happen async, so it doesn't block you from typing while it's working.
  • You want to be able to keep your cursor position, not be reset to the top of the file after formatting completes.
  • Formatting should happen smoothly whether doing :w, or :wq. Formatting after :wq gave me trouble in the past and would fail formatting. I think this is because mix format was called via the editor, but then the editor process exited, so it was getting terminated (I think).

This simple config seems to do a pretty good job at solving these challenges:

First, in $nvim_config_dir/ftplugin/elixir.vim (and eelixir.vim) I have an autocommand set up. It uses BufWritePost because the work is going to happen async. I think BufWritePre should be OK too.

augroup formatting
  autocmd! * <buffer>
  autocmd BufWritePost <buffer> :call elixir#formatting#mix_format()
augroup END

Then in $nvim_config_dir/autoload/elixir/formatting.vim I have these two functions.

function elixir#formatting#on_mix_format_exit(id, data, event)
  if a:data > 0
    echom "⚠ mix format "
  else
    execute 'edit'
    echom "✓ mix format "
  endif
endfunction

function elixir#formatting#mix_format()
  let l:filepath = expand('%:p')
  let l:mixformatcmd = 'nohup mix format '
        \. l:filepath
        \. ' 2>&1 > /dev/null'

  call jobstart(
        \['bash', '-c', mixformatcmd],
        \{'detach': 1,
        \ 'on_exit': function(
        \ 'elixir#formatting#on_mix_format_exit')})
endfunction

Since the mix_format function spawns a job to run the actual mix format program, it happens in the background so you can continue typing.

Instead of doing just mix format path/to/file, I am using nohup, which allows mix format to keep going even if the parent process dies.

I also set the detach option on the job so that it will not be killed when nvim exits.

When the job finishes, on_mix_format_exit is called and checks if mix format ran with errors or not. execute edit reloads the buffer contents, which has the formatted version at this point, and it does it without changing where the cursor is at.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment