Have you ever wonder what was The Beginning of All Things™?
In other words: have you ever wonder what Vim is doing when you start it? What files are sourced, why, and in what order? If you don’t really care, I encourage you to reconsider your position. Knowing what Vim is doing at startup lets you speed it up, overwrites some defaults from the plugins you’ve installed, or even creates your own filetypes. In short, it gives you even more power to customize Vim to your precise needs.
In this article, we’ll look at two main ideas:
- What happens during Vim’s startup.
- What are the runtime paths, what’s sourced from there, and when.
This article assumes that you have some advanced knowledge about Vim. If you don’t, you can read my series of article to learn Vim from the ground up. More specifically, a lot of knowledge in this article is built on Vim for Expert Users.
Take your headlamp and a good rope, we’ll go exploring today many paths in our filesystems.
Vim’s Startup
When you start Vim, many things happen behind the curtain before your favorite cursor is displayed on the screen. I describe here a simplified version, trying to underline the most important steps.
Startup’s Order
Here’s what Vim will do when you run it:
- Set the shell options (useful for the command
:!
, and for Vim’s terminal). - Process the arguments given to the CLI. Buffers are created for all files given as arguments.
- The value of the environment variable
$VIMINIT
(an Ex command) is executed. - Source the user’s vimrc file.
- The value of the environment variable
$EXINIT
(an Ex command) is executed. - If the option “exrc” is set, Vim will try to find and load a vimrc in the working directory. Keep in mind however that setting “exrc” is not secure.
- Source filetypes, filetype plugins, and indent plugins. We’ll see them below.
- Source syntax highligting scripts.
- Source plugin’s scripts.
- Load runtime’s and plugin’s scripts from the “after” folder.
- Source the shada file (for Neovim) or the viminfo file (for Vim).
- Execute the options given to Vim affecting the startup.
Let’s add more precisions:
- If you want to run more than one command using
$VIMINIT
, you can separate them with the symbol|
. - Your vimrc file can be:
- In Vimscript for Vim (located at
$HOME/.vimrc
). - In Lua or Vimscript for Neovim (located in
$HOME/.config/nvim/init.vim
, or$XDG_CONFIG_HOME/nvim/init.vim
if$XDG_CONFIG_HOME
is set).
- In Vimscript for Vim (located at
- Setting the option
exrc
can be dangerous: Vim can load potentially unsecure vimrc files you might have downloaded with other files. - You can use the option
-u <another_vimrc>
when you run Vim in your terminal, to load another vimrc file instead of the default one. You can also use of of these value instead of a vimrc for debug purposes:NORC
- Don’t load any vimrc but load your plugins.NONE
- Don’t load any vimrc nor plugins.
- You can run any command using the options
-c
or+
. For example:vim +"set shiftwidth=4|echo 'hello'"
Profiling Vim’s Startup
If Vim does terrible things at startup and you don’t understand how to fix it, you can use the option -V
to have more details about the startup process.
If you’re startup is too slow and you want to find the bottleneck, you can profile it with vim --startuptime <file>
. It will write every files loaded (with timestamps) in the file <file>
.
Special Environment Variables
Two environment variables are only defined when Vim starts:
$VIM
- Used to locate various user files.$VIMRUNTIME
- Used to locate various support files for Vim. We’ll speak more about the runtime below.
You can look at the value of these variable with the command :echo $VIM
for example. If you define these environment variables in your shell, Vim won’t overwrite them.
:help startup
:help slow-start
:help 'shell'
:help $VIM
:help $VIMRUNTIME
The Runtime Path
The runtime path is similar to the environment variable PATH in Unix-based systems. Vim will search in these paths to locate and source many different files during startup. To see all of these paths, you can look at the value of the option runtimepath
, by running the command :set runtimepath?
.
If you use Vim, it’s likely that you’ll see $HOME/.vim
as the first path of the list. If you’re a Neovim adept, it will be $HOME/.config/nvim
.
The subdirectories of the runtime paths have different meanings according to their names. The Vimscript files inside these directories will be sourced at different times during startup, sometimes in different ways.
Important Runtime Paths
Here are some important runtime paths. Vim will parse them in this order:
For Vim:
$HOME/.vim
$VIM/vimfiles
$VIM/vimfiles/after
$HOME/.vim/after
For Neovim:
$XDG_CONFIG_HOME/nvim
(or~/.config/nvim
if$XDG_CONFIG_HOME
is not set)$VIMRUNTIME
$XDG_CONFIG_HOME/nvim/after
It’s important to notice that every runtime file in your user directory will be sourced before the default ones. It means that Vim’s defaults can overwrite your own runtime files. That’s why it’s often better to put all your custom runtime files in the directory after
, sourced after everything. More on that below.
Subdirectories of the Runtime Paths
As I was writing above, the configuration files in the runtime paths contains different things depending of the subdirectory they’re in. Here’s an overview of the most useful of these subdirectories, and how you can use them to customize Vim even further.
I encourage you to look at the default configuration files to understand the meaning of these subdirectories. They are in the directory set in the variable $VIMRUNTIME
. Don’t forget that you can only read the value of $VIMRUNTIME
after launching Vim.
You can also find examples in my own dotfiles.
Keep in mind that any subdirectory you’ll create in your user runtime directory will be sourced before the gl
ftplugin
The subdirectory ftplugin (for f
ilet
ype plugin
) allow you to load pieces of configuration each time you open a buffer with a specific filetype.
For example, let’s say that you need a couple of mappings only for markdown files. You can:
- Open a markdown file and verify that the filetype is indeed markdown, by running the command
:set filetype?
. - Create the file
markdown.vim
in$HOME/.vim/ftplugin
for Vim, or$XDG_CONFIG_HOME/nvim/ftplugin
for Neovim. - Add options and mappings only for markdown files in there. For example:
setlocal spell
will enable spelling each time you open a markdown file.
You need to keep in mind, however, that each time a buffer with the filetype markdown
is created, the file ftplugin/markdown.vim
will be sourced. As a result, you need to make sure that:
- You reset any autocommand each time the file is sourced (using an autocommand group and
:au!
). - Every mapping you’re creating have the special argument
<buffer>
; for example,map <buffer> <leader>h echo "hello"
. - You’re using
setlocal
instead ofset
to set options. - You’re using the
-buffer
argument for any user command.
If you don’t respect these rules, your pieces of configuration won’t only be loaded for the buffers with a precise filetype, but for every buffer, globally.
autoload
The subdirectory autoload is also very useful. It lets you load your custom functions when you call them, instead of loading them when Vim starts. As a result, it can significantly speed up Vim’s startup. These functions need to begin with the path of the file they’re written in, for Vim to know where to find them when you call them.
For example, you can create a new file autoload/general.vim
and use it to declare a new custom function MyFunction
. This function needs to be called general#MyFunction
, because it’s declared in the file general.vim
. To call it, you can run :call general#MyFunction
.
You can also create subdirectories in the directory autoload
. For example, you can create the file autoload/path/to/general.vim
. In that case, you need to call your function path#to#general#MyFunction
, describing the location of the file itself.
I encourage you to put all your custom functions in the “autoload” subdirectory.
syntax
The subdirectory syntax lets you create your own syntax files. They’re used for code highlighting for example. You can first create a syntax group matching some regex, and then you link your syntax group to highlight groups. These highlight groups will decide of the text’s color displayed.
Vim supports many syntaxes by default (you can see them in the directory $VIMRUNTIME/syntax
), I never had to create my own syntax groups. It’s however good to know that you can do it in case you need it.
colors
You can create highlight groups in the subdirectory colors. Each file represents a different color scheme. Again, you’ll find different examples in the directory $VIMRUNTIME/colors
. To change the color scheme of your highlighting, you can use the command :color <my_color_scheme>
.
ftdetect
Have you ever dreamed to create your own filetypes? That’s great, because Vim is a dream machine. You can create your filetypes in the subdirectory “ftdetect”.
For example, if you have a bunch of file with the extension “new” and you want to create a filetype for them, you can:
- Create the file
ftdetect/new.vim
- Write
au BufRead,BufNewFile *.new set filetype=new
in it.
You can also link some filetypes with other ones. For example, I wanted the files with the extension .yaml.dist
to have the yaml
filetype. So I’ve:
- Created the file
ftdetect/yaml.dist.vim
- Written
autocmd BufNewFile,BufRead *.yml.dist set filetype=yaml
in it.
compiler
In Vim, you can use the command :make
to run a specific compiler, depending of the filetype of your current buffer.
Under the hood, Vim will search a file named after the filetype of your current buffer, in the “compiler” directory. For example, if if you want to run the Golang compiler with precise options, you can create the file compiler/go.vim
and add whatever you need.
The Directory after
The subdirectory “after” is a special one. You can think of it as another runtime path in the runtime path, where you can add the same subdirectories we saw above.
Everything in the after directory will be loaded after everything else. You can also see it when you look at the order of the most important runtime path at the beginning of this section.
From there, you can override anything you want: indentation, filetype plugins, or even the external plugins you’ve installed. For example, I can override Lisp indentation by creating the file after/indent/lisp.vim
and setting whatever options or variables I want to modify.
I didn’t cover the subdirectory indent
by the way, but I’m sure you can figure out its purpose.
The Runtime Command
If you want to source manually some files from your runtime path, you can use the command :runtime <file>
. For example, if you want to load every files from the colors
subdirectory, you can run :runtime colors/**/*.vim
.
You can also use :runtime!
(with a bang !
) to source everything.
:help 'runtimepath'
:help autoload
:help ftplugin
:help syntax
:help new-filetype
:help after-directory
Disabling Runtime Files
Finally, if you want to debug one of your runtime files, you can disable some of them by sourcing these specific files:
:source $VIMRUNTIME/ftoff.vim
- Disable the detection of filetypes:source $VIMRUNTIME/ftplugin.vim
- Enable ftplugin:source $VIMRUNTIME/ftplugof.vim
- Disable ftplugin:source $VIMRUNTIME/indent.vim
- Enable indentation:source $VIMRUNTIME/indoff.vim
- Disable indentation
The Startup Has Been Revealed
Now that you know what Vim’s doing when it’s starting, you can add your pieces of configuration in the good runtime subdirectory for more flexibility. You can also try to speed up your startup if you find it too slow.
What did we see in this article?
- Vim load specific configuration files and environment variables at startup
- You can use the options
-V
or--startuptime
to get more details regarding Vim’s startup. - The environment variable
$VIM
and$VIMRUNTIME
are set during startup. - The subdirectories in the runtime path have a meaning, sometimes affecting directly how they’re loaded at startup, and when.
- The runtime path subdirectory “after” is a special one: it’s a runtime path too (accepting the same subdirectories), and you can overwrite every other loaded config files in there.
- The command
:runtime
allows you to manually load config files from your runtime paths.
I encourage you to experiment with all these configuration files to improve your config. Personally, I find the subdirectories “autoload” and “ftplugin” especially handy.