vim-filetype-formatter
vim-filetype-formatter copied to clipboard
Format program files in vim using your favorite command line formatter
Vim Filetype Formatter
A simple, cross-language Vim code formatter plugin supporting both range and full-file formatting. It uses code formatters available in your $PATH; it does not install them.
See our pre-configured languages and formatters. Don't like the defaults? Writing your own is easy! Each Vim filetype maps to one command-line command. This plugin supports any code formatter command as long as it:
- Reads from standard input.
- Writes to standard output.
- Is in your
$PATH
.
Requires Bash and a recent version of Vim or Neovim.
Differentiating Features
- Respects configuration files (
pyproject.toml
,.rustfmt.toml
,.prettierrc.toml
, etc.) - Accepts visually-selected ranges for any formatter
- Preserves Vim cursor location after the formatter has run
- Clear logging, so you can see why a formatter is or isn't working (
:LogFiletypeFormat
) - Easy debugging of user configuration (
:DebugFiletypeFormat
) - Chain formatters together with Unix pipes
- Configurable, with sane defaults
- Simple, extendable codebase
- Modular: does not pollute your Vim environment with custom key mappings / poor Vim plugin practices
The following screencast demonstrates :FiletypeFormat
, :LogFiletypeFormat
, and :DebugFiletypeFormat
.
Although black works out of the box for Python, the above example overrides the default and combines black with isort and docformatter using Unix pipes. This specific example can be achieved with the following configuration in your vimrc
or init.vim
:
let g:vim_filetype_formatter_commands = {
\ 'python': 'black -q - | isort -q - | docformatter -',
\ }
For further customization (e.g., where you need anything dynamic), you can pass either a Funcref
or a lambda expression
. For example, you might want to pass the current filename as an argument to your command line program. Here is an example for Python using a lambda expression:
let g:vim_filetype_formatter_commands = {
\ 'python': {-> printf('black -q --stdin-filename="%1$s" - | isort -q --filename="%1$s" - | docformatter -', expand('%:p'))},
\ }
Here's another Python example involving ruff.
function s:formatter_python()
return printf(
\ 'ruff check --unsafe-fixes -q --fix-only --stdin-filename="%1$s" - | ' ..
\ 'ruff format -q --stdin-filename="%1$s" -',
\ expand('%:p'))
endfunction
let g:vim_filetype_formatter_commands = {'python': function('s:formatter_python')}
Finally, here's an example of how we can support prettier's built-in range functionality:
function! s:prettier(startline, endline)
return printf(
\ 'npx --no-update-notifier --silent --no-install prettier --range-start=%i --range-end=%i --stdin-filepath="%s"',
\ line2byte(a:startline) - 1,
\ line2byte(a:endline + 1) - 1,
\ expand('%:p')
\ )
endfunction
let g:vim_filetype_formatter_commands = {'javascript': function('s:prettier')}
Installation
If using vim-plug, place the following line in the Plugin section of your init.vim
/ vimrc
:
Plug 'pappasam/vim-filetype-formatter'
Then run the Ex command:
:PlugInstall
I personally use vim-packager, so if you'd like to go down the "package" rabbit hole, I suggest giving that a try.
Full Documentation
From within Vim, type:
:help filetype_formatter
Key mappings
This plugin provides no default key mappings. I recommend setting a key mapping for normal mode and visual mode like this:
nnoremap <silent> <leader>f <Cmd>FiletypeFormat<CR>
xnoremap <silent> <leader>f :FiletypeFormat<CR>
Default configurations
Default configurations may be overridden by creating our own g:vim_filetype_formatter_commands
dictionary. If you would like to map one filetype to another, see g:vim_filetype_formatter_ft_maps
. See here for specifics on how to do this.
If you would like to use a formatter listed above in "Other Formatters", you'll first need to packadd vim-filetype-formatter
and then add it to g:vim_filetype_formatter
commands. Here is an example of how to override Python's formatter with the built-in configuration for ruff
:
packadd vim-filetype-formatter
let g:vim_filetype_formatter_commands.python = g:vim_filetype_formatter_builtins.ruff
Non-standard code formatters
In the rare case where a required code formatter does not read from standard input and/or write to standard output, don't panic. With some effort, you can probably still create a working command by chaining the code formatter with standard Unix programs. See the following example, using nginxbeautifier
:
\ 'nginx':
\ 'dd status=none of=/tmp/nginx.conf >& /dev/null && '
\ .. 'nginxbeautifier --space 4 /tmp/nginx.conf >& /dev/null && '
\ .. 'cat /tmp/nginx.conf && '
\ .. 'rm /tmp/nginx.conf',
-
dd
: readvim-filetype-formatter
's standard output as standard input, writing to a temporary file named/tmp/nginx.conf
-
nginxbeautifier
: read from the temporary file and modify that file in-place -
cat
: write the contents of the temporary file to stdout -
rm
: remove the temporary file to keep things tidy
It's not exactly pretty, but:
- Reality isn't always pretty
- We can use the command because it reads from standard input and writes to standard output
Batteries Included
Language | Default Formatter | Other Formatters |
---|---|---|
bash/sh | shfmt | |
biblatex | bibtool | |
css | prettier | |
go | gofmt | |
graphql | prettier | |
html | prettier | |
javascript/jsx | prettier | |
json | prettier | |
jsonc | prettier | |
lua | stylua | |
markdown | prettier | |
mdx | prettier | |
nginx | nginxfmt | |
ocaml | ocamlformat | |
prisma | prettier_prisma | |
python | black | ruff |
r | styler | |
rust | rustfmt | leptosfmt |
scss | prettier | |
svelte | prettier_svelte | |
terraform | terraform_fmt | |
toml | toml_sort | |
typescript/tsx | prettier | |
yaml | prettier |
FAQ
How can I have per-project settings?
If using a recent version of Neovim, see :help 'exrc'
.
" $XDG_CONFIG_HOME/init.vim
set exrc
" $PROJECT_PATH/.nvimrc
packadd vim-filetype-formatter
let g:vim_filetype_formatter_commands['python'] = g:vim_filetype_formatter_builtins['ruff']
let g:vim_filetype_formatter_commands['rust'] = g:vim_filetype_formatter_builtins['leptosfmt']