A simple, cross-language Vim code formatter plugin supporting both range and full-file formatting.
It uses code formatters; 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. This plugin supports compatible Vim commands, or any command line code formatter 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.
- 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')}
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')}
Finally, custom Vim commands may be used instead of shell commands by ensuring that your final string is prefixed with a :
. For an example implementation, see here:
" Use vim's built-in commands.
" 1. = (the vimscript_builtin)
" 2. Replace all instances of multiple blank lines, shortening to a single
function! s:vimscript_builtin(startline, endline)
return printf(
\ ':silent! execute "normal! %igg=%igg" | silent! %i,%iglobal/^\_$\n\_^$/de',
\ a:startline, a:endline,
\ a:startline, a:endline
\ )
endfunction
let g:vim_filetype_formatter_commands = {'vim': function('s:vimscript_builtin')}
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.
From within Vim, type:
:help filetype_formatter
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 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
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-placecat
: write the contents of the temporary file to stdoutrm
: 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
Language | Default Formatter | Other Formatters |
---|---|---|
bash/sh | shfmt | |
biblatex | bibtool | |
css | prettier | |
dockerfile | vim.lsp.buf.format | |
dosini | built-in | |
gitconfig | built-in | |
go | gofmt | |
graphql | prettier | |
html | prettier | |
htmldjango | prettier_jinja | |
javascript/jsx | prettier | |
jinja.html | prettier_jinja | |
json | prettier | |
jsonc | prettier | |
lua | stylua | |
make | built-in | |
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 | taplo | |
typescript/tsx | prettier | |
vimscript | built-in | |
yaml | prettier | |
zsh | built-in |
For example, if you have a different version of prettier installed in your project than you installed globally, you'll probably want vim-filetype-formatter to use your project's version of prettier. To achieve this:
- Place the following line in
init.vim
/.vimrc
:let $PATH = $PWD .. '/node_modules/.bin:' .. $PATH
- Open Neovim at the root of your project.
- You should now be referencing executable files within your project's
node_modules/
folder.
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']