We've written a fair amount of functionality for our Potion plugin, and that's all we're going to do in this book. Before we finish we'll talk about a few more important ways to polish it up and really make it shine.
First on the list is making our plugin more efficient with autoloading.
Currently when a user loads our plugin (by opening a Potion file) all of its functionality is loaded. Our plugin is still small so this probably isn't a big deal, but for larger plugins loading all of their code can take a noticeable amount of time.
Vim's solution to this is something called "autoload". Autoload lets you delay loading code until it's actually needed. You'll take a slight performance hit overall, but if your users don't always use every single bit of code in your plugin autoloading can be a huge speedup.
Here's how it works. Look at the following command:
:call somefile#Hello()
When you run this command, Vim will behave a bit differently than a normal function call.
If this function has already been loaded, Vim will simply call it normally.
Otherwise Vim will look for a file called autoload/somefile.vim
in your
~/.vim
directory (and any Pathogen bundles).
If this file exists, Vim will load/source the file. It will then try to call the function normally.
Inside this file, the function should be defined like this:
function somefile#Hello()
" ...
endfunction
You can use multiple #
characters in the function name to represent
subdirectories. For example:
:call myplugin#somefile#Hello()
This will look for the autoloaded file at autoload/myplugin/somefile.vim
. The
function inside it needs to be defined with the full autoload path:
function myplugin#somefile#Hello()
" ...
endfunction
To get a feel for how this works, let's give it a try. Create
a ~/.vim/autoload/example.vim
file and add the following to it:
echom "Loading..."
function! example#Hello()
echom "Hello, world!"
endfunction
echom "Done loading."
Save the file and run :call example#Hello()
. Vim will output the following:
Loading...
Done loading.
Hello, world!
This little demonstration proves a few things:
example.vim
file on the fly. It didn't even exist
when we opened Vim, so it couldn't have been loaded on startup!Without closing Vim, change the definition of the function to look like this:
echom "Loading..."
function! example#Hello()
echom "Hello AGAIN, world!"
endfunction
echom "Done loading."
Save the file and without closing Vim run :call example#Hello()
. Vim will
simply output:
Hello, world!
Vim already has a definition for example#Hello
, so it doesn't need to reload
the file, which means:
Now run :call example#BadFunction()
. You'll see the loading messages again,
as well as an error about a nonexistent function. But now try running :call
example#Hello()
again. This time you'll see the updated message!
By now you should have a pretty clear grip on what happens when Vim encounters a call to a function with an autoload-style name:
If that's not completely solid in your mind yet, go back and work through this demonstration again and try to see where each rule takes effect.
Autoloading isn't free. There's some (small) overhead involved with setting it up, not to mention the ugly function names you need to sprinkle through your code.
With that said, if you're creating a plugin that won't be used every time a user opens a Vim session it's probably a good idea to move as much functionality into autoloaded files as possible. This will reduce the impact your plugin has on your users' startup times, which is important as people install more and more Vim plugins.
So what kind of things can be safely autoloaded? The answer is basically anything that's not directly called by your users. Mappings and custom commands can't be autoloaded (because they wouldn't be available for the users to call), but many other things can be.
Let's look at our Potion plugin and see what we can autoload.
We'll start with the compile and run functionality. Remember that our
ftplugin/potion/running.vim
file looked like this at the end of the previous
chapter:
if !exists("g:potion_command")
let g:potion_command = "potion"
endif
function! PotionCompileAndRunFile()
silent !clear
execute "!" . g:potion_command . " " . bufname("%")
endfunction
function! PotionShowBytecode()
" Get the bytecode.
let bytecode = system(g:potion_command . " -c -V " . bufname("%"))
" Open a new split and set it up.
vsplit __Potion_Bytecode__
normal! ggdG
setlocal filetype=potionbytecode
setlocal buftype=nofile
" Insert the bytecode.
call append(0, split(bytecode, '\v\n'))
endfunction
nnoremap <buffer> <localleader>r :call PotionCompileAndRunFile()<cr>
nnoremap <buffer> <localleader>b :call PotionShowBytecode()<cr>
This file is already only called when a Potion file is loaded, so it doesn't add to the overhead of Vim's startup in general. But there may be some users who simply don't need this functionality, so if we can autoload some of it we'll save them a few milliseconds every time they open a Potion file.
Yes, in this case the savings won't be huge. But I'm sure you can imagine a plugin with many thousands of lines of functions where the time required to load them would be more significant.
Let's get started. Create an autoload/potion/running.vim
file in your plugin
repo. Then move the two functions into it and adjust their names, so they look
like this:
echom "Autoloading..."
function! potion#running#PotionCompileAndRunFile()
silent !clear
execute "!" . g:potion_command . " " . bufname("%")
endfunction
function! potion#running#PotionShowBytecode()
" Get the bytecode.
let bytecode = system(g:potion_command . " -c -V " . bufname("%"))
" Open a new split and set it up.
vsplit __Potion_Bytecode__
normal! ggdG
setlocal filetype=potionbytecode
setlocal buftype=nofile
" Insert the bytecode.
call append(0, split(bytecode, '\v\n'))
endfunction
Notice how the potion#running
portion of the function names matches the
directory and file name where they live. Now change the
ftplugin/potion/running.vim
file to look like this:
if !exists("g:potion_command")
let g:potion_command = "potion"
endif
nnoremap <buffer> <localleader>r
\ :call potion#running#PotionCompileAndRunFile()<cr>
nnoremap <buffer> <localleader>b
\ :call potion#running#PotionShowBytecode()<cr>
Save the files, close Vim, and open up your factorial.pn
file. Try using the
mappings to make sure they still work properly.
Make sure that you see the diagnostic Autoloading...
message only the first
time you run one of the mappings (you may need to use :messages
to see it).
Once you confirm that autoloading is working properly you can remove that
message.
As you can see, we've left the nnoremap
calls that map the keys. We can't
autoload these because the user would have no way to initiate the autoloading if
we did!
This is a common pattern you'll see in Vim plugins: most of their functionality
will be held in autoloaded functions, with just nnoremap
and command
commands in the files that Vim loads every time. Keep it in mind whenever
you're writing a non-trivial Vim plugin.
Read :help autoload
.
Experiment a bit and find out how autoloading variables behaves.
Suppose you wanted to programatically force a reload of an autoload file Vim has
already loaded, without bothering the user. How might you do this? You may
want to read :help :silent
. Please don't ever do this in real life.