If you're like me, you have a lot of autocommands for specific file types in your .vimrc. Specific
setlocals, some nice mappings, etc. After a while those
autocmds start adding up and you soon have hundreds of lines of
autocmd FileType file setlocal foo=bar
You might want to put those commands in a function and call it once, or maybe put them in a separate file that you source with a single autocmd. At this poing you'll start wondering if there's a better way. Of course there is. We're using Vim after all, there is almost always a better way. Enter
What are ftplugins?
Filetype plugins, or
ftplugins for short, are
.vim files that run specific commands depending on what file is opened. Vim has a lot (and I mean a lot) ftplugins built in and plugins can add additional ones.
How to use them
ftplugins are sourced automatically (assuming you have
filetype plugin on in your .vimrc), as long as they're in a specific folder and have a specific file name. You can write either a complete replacement to an existing
ftplugin, or just add extra stuff to whatever Vim does by default. You control that with the directory you put it in.
The file has to be named in a specific way:
[file type].vim. You can also name it
[file type]_whatever.vim, though I personally don't see any reason for that. The directory where it should live depends on what you want to do
This is what I recommend for most users. You can add key bindings, extra
setlocals, override a single default setting, maybe a few new commands, or whatever else you have in your
In order to use your new settings, NeoVim users put the file in
~/.config/nvim/after/ftplugin, while for Vim users it's
Completely override default settings
A word of warning before you do that: You'll have to set up everything Vim does by default for that specific file type. This includes omni function, comment string, format options etc. It's almost always overkill and you lose the ability to receive upstream updates for the file type.
If you still want to go for that, put your file in
~/.local/share/nvim/site/ftplugin/ for NeoVim, or
~/.vim/ftplugin/ for Vim
ftplugin file structure
While the file structure isn't forced upon you, there are some things the plugin file should have: a header, a check whether it needs to load or not, and an undo
A header is just a comment describing the file. It's useful when sharing it on the Internet, so the other person knows what it is, when was it last updated, or who made it. I usually use a header that looks like this:
Feel free to use it and modify as needed
" *.[file] filetype config " Created: [date] " Last Modified: [date] " Creator: Przemek Dragańczuk
" Licence: WTFPL
Loading a file multiple times can cause some problems, like running a command multiple times. You can fix it in a simple way: check if a specific variable is defined, if it is, then there is no need to load the file, otherwise set the variable and run the rest of the file. The code for that looks like this:
I prefer naming the variable depending on the filetype, for example
if exists("b:did_ftplugin") finish endif let b:did_ftplugin = 1
b:did_html_ftplugin, that way I have a way to disable this specific file from loading by just putting
b:did_[filetype]_ftplugin = 1in my .vimrc
What would happen if Vim guessed the filetype wrong and then you set it manually with
:setfiletype? Vim would source the
ftplugin for what it guessed, then it would source the correct one. This means that you still have all the settings from the incorrect
ftplugin. You can get around that by setting a variable
b:undo_ftplugin to contain all the commands that will revert whatever the
I personally put them all in a function, like this:
function! s:undo() setl formatoptions< setl tabstop< setl textwidth< endfunction let b:undo_ftplugin = "call s:undo()"
setlocal [setting]<(notice the "<" at the end) resets the setting to it's global value.
After doing everything above you should have a .vim file that contains the following:
" *.[file] filetype config " Created: [date] " Last Modified: [date] " Creator: [creator] " Licence: [licence] if exists("b:did_[file]_ftplugin") finish endif let b:did_[file]_ftplugin=1 let b:undo_ftplugin = "call s:undo()" function s:undo() endfunction
It was brought to my attention that
b:undo_ftplugin overwrites the undo created by the builtin
ftplugin. In order to fix it, we need to change one line:
Notice the red dot, which means Vim will append to, instead of overwriting, the variable
let b:undo_ftplugin .= "call s:undo()"
Writing the actual plugin
Now that we have the boilerplate out of the way, we can get to setting some stuff. Using
ftplugins gives us some extra modifiers to make sure we only affect the files we want.
setlocal works how it used to, just remember to revert it in
You can create maps that don't affect other buffers by adding
<buffer> to your
map commands. For example:
will cause pressing <Leader>e to display "Hello world", but only for specific filetypes
nnoremap <buffer> <Leader>e :echo "Hello world"
If you want/need to use some variables, you can prepend them with
b: to make them visible only in this script (the
ftplugin you're writing) or current buffer respectively
You can now move all the stuff you set in auto commands or auto groups to their own
ftplugins, cleaning up your .vimrc of all those
autocmd FileType... calls.
:h write-plugin- Overview on writing Vim plugins
:h ftplugin- Information specific to