551 lines
No EOL
32 KiB
HTML
551 lines
No EOL
32 KiB
HTML
<!DOCTYPE html>
|
|
<html lang='en'><head><meta charset='utf-8' /><meta name='pinterest' content='nopin' /><link href='../../../../static/css/style.css' rel='stylesheet' type='text/css' /><link href='../../../../static/css/print.css' rel='stylesheet' type='text/css' media='print' /><title>Writing Vim Plugins / Steve Losh</title></head><body><header><a id='logo' href='https://stevelosh.com/'>Steve Losh</a><nav><a href='../../../index.html'>Blog</a> - <a href='https://stevelosh.com/projects/'>Projects</a> - <a href='https://stevelosh.com/photography/'>Photography</a> - <a href='https://stevelosh.com/links/'>Links</a> - <a href='https://stevelosh.com/rss.xml'>Feed</a></nav></header><hr class='main-separator' /><main id='page-blog-entry'><article><h1><a href='index.html'>Writing Vim Plugins</a></h1><p class='date'>Posted on September 6th, 2011.</p><p>A while ago I wrote a <a href="../../../2010/09/coming-home-to-vim/index.html">post</a> about switching back to <a href="http://www.vim.org/">Vim</a>. Since then
|
|
I've written two plugins for Vim, one of which has been officially "released".</p>
|
|
|
|
<p>A couple of people have asked me if I'd write a guide to creating Vim plugins.
|
|
I don't feel confident enough to write an official "guide", but I do have some advice
|
|
for Vim plugin authors that might be useful.</p>
|
|
|
|
<ol class="table-of-contents"><li><a href="index.html#s1-other-people-who-know-more-than-i-do">Other People Who Know More Than I Do</a><ol><li><a href="index.html#s2-tim-pope">Tim Pope</a></li><li><a href="index.html#s3-scrooloose">Scrooloose</a></li></ol></li><li><a href="index.html#s4-be-pathogen-compatible">Be Pathogen-Compatible</a></li><li><a href="index.html#s5-please-for-the-love-of-god-use-normal">Please, For the Love of God, Use normal!</a></li><li><a href="index.html#s6-mapping-keys-the-right-way">Mapping Keys the Right Way</a><ol><li><a href="index.html#s7-when-to-map-keys">When to Map Keys</a></li><li><a href="index.html#s8-imap-and-nmap-are-pure-evil">imap and nmap are Pure Evil</a></li><li><a href="index.html#s9-let-me-configure-mappings">Let Me Configure Mappings</a></li></ol></li><li><a href="index.html#s10-localize-mappings-and-settings">Localize Mappings and Settings</a><ol><li><a href="index.html#s11-localizing-mappings">Localizing Mappings</a></li><li><a href="index.html#s12-localizing-settings">Localizing Settings</a></li></ol></li><li><a href="index.html#s13-autoload-is-your-friend">Autoload is Your Friend</a></li><li><a href="index.html#s14-backwards-compatibility-is-a-big-deal">Backwards Compatibility is a Big Deal</a><ol><li><a href="index.html#s15-what-matters-for-backards-compatibility">What Matters for Backards Compatibility?</a></li><li><a href="index.html#s16-use-semantic-versioning-so-i-can-stay-sane">Use Semantic Versioning So I Can Stay Sane</a></li></ol></li><li><a href="index.html#s17-document-everything">Document Everything</a><ol><li><a href="index.html#s18-pick-some-requirements-and-stick-to-them">Pick Some Requirements and Stick to Them</a></li><li><a href="index.html#s19-write-a-readme">Write a README</a></li><li><a href="index.html#s20-create-a-simple-website">Create a Simple Website</a></li><li><a href="index.html#s21-write-a-vim-help-document">Write a Vim Help Document</a></li><li><a href="index.html#s22-keep-a-changelog">Keep a Changelog</a></li></ol></li><li><a href="index.html#s23-making-vimscript-palatable">Making Vimscript Palatable</a><ol><li><a href="index.html#s24-wrap-everything">Wrap. Everything.</a></li><li><a href="index.html#s25-scripting-vim-with-other-languages">Scripting Vim with Other Languages</a></li><li><a href="index.html#s26-unit-testing-will-make-you-drink">Unit Testing Will Make You Drink</a></li></ol></li><li><a href="index.html#s27-tl-dr">TL;DR</a></li></ol>
|
|
|
|
<h2 id="s1-other-people-who-know-more-than-i-do"><a href="index.html#s1-other-people-who-know-more-than-i-do">Other People Who Know More Than I Do</a></h2>
|
|
|
|
<p>Writing two decently-sized Vim plugins has given me some experience, but there are
|
|
a lot of people that know far more than I do. There are two in particular that come
|
|
to mind. I'd love for them to write some guides (or even books) about modern-day Vim
|
|
scripting.</p>
|
|
|
|
<h3 id="s2-tim-pope"><a href="index.html#s2-tim-pope">Tim Pope</a></h3>
|
|
|
|
<p>The first is <a href="http://tpo.pe/">Tim Pope</a>. He's written a ton of Vim plugins like <a href="https://github.com/tpope/vim-pathogen">Pathogen</a>,
|
|
<a href="https://github.com/tpope/vim-surround">Surround</a>, <a href="https://github.com/tpope/vim-repeat">Repeat</a>, <a href="https://github.com/tpope/vim-speeddating">Speeddating</a> and <a href="https://github.com/tpope/vim-fugitive">Fugitive</a>. Each of those is clear,
|
|
focused and polished.</p>
|
|
|
|
<p>It would be awesome to read a guide on the ins and outs of Vim scripting by him.</p>
|
|
|
|
<h3 id="s3-scrooloose"><a href="index.html#s3-scrooloose">Scrooloose</a></h3>
|
|
|
|
<p>The other person that comes to mind is <a href="http://got-ravings.blogspot.com/">Scrooloose</a>, author of <a href="https://github.com/scrooloose/nerdtree">NERDTree</a>,
|
|
<a href="https://github.com/scrooloose/nerdcommenter">NERDCommenter</a> and <a href="https://github.com/scrooloose/syntastic">Syntastic</a>.</p>
|
|
|
|
<p>His plugins are large and full-featured but work incredibly well, considering how
|
|
tricky and painful Vimscript is to work with. I'd love to read a guide on writing
|
|
large-scale Vim plugins by him.</p>
|
|
|
|
<h2 id="s4-be-pathogen-compatible"><a href="index.html#s4-be-pathogen-compatible">Be Pathogen-Compatible</a></h2>
|
|
|
|
<p>It's 2011. When writing your plugin, <em>please</em> make its source compatible with
|
|
<a href="https://github.com/tpope/vim-pathogen">Pathogen</a>. It's very easy to do this — just set up your project's files like
|
|
this:</p>
|
|
|
|
<pre><code>yourplugin/
|
|
doc/
|
|
yourplugin.txt
|
|
plugin/
|
|
yourplugin.vim
|
|
...
|
|
README
|
|
LICENSE</code></pre>
|
|
|
|
<p>This will let users use Pathogen (or <a href="https://github.com/gmarik/vundle">Vundle</a>) to install and use your plugin.</p>
|
|
|
|
<p>The days of "unzip and drag the files into the right directories" and the horror of
|
|
Vimballs are over. Pathogen and Vundle are the right way to manage plugins, so let
|
|
your users use them.</p>
|
|
|
|
<h2 id="s5-please-for-the-love-of-god-use-normal"><a href="index.html#s5-please-for-the-love-of-god-use-normal">Please, For the Love of God, Use normal!</a></h2>
|
|
|
|
<p>My first piece of actual scripting advice is something simple but important. If
|
|
you're writing a Vim plugin and need to perform some actions, you might be tempted to
|
|
use <code>normal</code>. Don't. Instead, you need to use <code>normal!</code>.</p>
|
|
|
|
<p><code>normal!</code> is like <code>normal</code>, but ignores mappings the user has set up. If you use
|
|
plain old <code>normal dd</code> and I've remapped <code>dd</code> to do something else, the call will use
|
|
my mapping and probably not do what your plugin expects. Using <code>normal!</code> ensures
|
|
that the call will do what you expect no matter what the user has mapped.</p>
|
|
|
|
<p>This is a single instance of a more general theme. Vim is very customizable and users
|
|
will do lots of crazy things in their <code>.vimrc</code> files. If a key can be mapped or
|
|
a setting changed, you <em>have</em> to assume that some user of your plugin will have
|
|
mapped or changed it.</p>
|
|
|
|
<h2 id="s6-mapping-keys-the-right-way"><a href="index.html#s6-mapping-keys-the-right-way">Mapping Keys the Right Way</a></h2>
|
|
|
|
<p>Most plugins add key mappings to make them easier to use. Unfortunately this can be
|
|
tricky to get right. You can never tell what keys your users have already mapped
|
|
themselves, and shadowing someone's favorite key mapping will break their muscle
|
|
memory and annoy them to no end.</p>
|
|
|
|
<h3 id="s7-when-to-map-keys"><a href="index.html#s7-when-to-map-keys">When to Map Keys</a></h3>
|
|
|
|
<p>The first question to ask is whether your plugin needs to map keys itself at all.</p>
|
|
|
|
<p>My <a href="https://sjl.bitbucket.io/gundo.vim/">Gundo</a> plugin has only one feature that needs to be mapped to a key in order to
|
|
make it useful: the "toggle Gundo" action.</p>
|
|
|
|
<p>Gundo doesn't map this key itself, because no matter what "default" mapping I pick
|
|
someone will have already mapped it. Instead I added a section right in the README
|
|
file that shows how a user can map the key themselves:</p>
|
|
|
|
<pre><code>nnoremap <F5> :GundoToggle<CR></code></pre>
|
|
|
|
<p>By making users add this line to their <code>.vimrc</code> themselves it shows them which key is
|
|
used to toggle Gundo (which they would have to know anyway) and also makes it obvious
|
|
how to change it to suit their taste.</p>
|
|
|
|
<h3 id="s8-imap-and-nmap-are-pure-evil"><a href="index.html#s8-imap-and-nmap-are-pure-evil">imap and nmap are Pure Evil</a></h3>
|
|
|
|
<p>Sometimes forcing the user to map their own keys won't work. Perhaps your plugin has
|
|
many mappings that would be tedious for a user to set up manually (like my
|
|
<a href="https://sjl.bitbucket.io/threesome.vim">Threesome</a> plugin), or its mappings are mnemonic and wouldn't really make sense if
|
|
mapped to other keys.</p>
|
|
|
|
<p>I'll talk more about how to deal with this in a moment, but the most important thing
|
|
to remember when mapping your own keys is that you must always, <em>always</em>,
|
|
<strong><em>always</em></strong> use the <code>noremap</code> forms of the various <code>map</code> commands.</p>
|
|
|
|
<p>If you map a key with <code>nmap</code> and the user has remapped a key that your mapping uses,
|
|
your mapped key will almost certainly not do what you want. Using <code>nnoremap</code> will
|
|
ignore user mappings and do what you expect.</p>
|
|
|
|
<p>This is the same principle as <code>normal</code> and <code>normal!</code>: <em>never</em> trust your users'
|
|
configurations.</p>
|
|
|
|
<h3 id="s9-let-me-configure-mappings"><a href="index.html#s9-let-me-configure-mappings">Let Me Configure Mappings</a></h3>
|
|
|
|
<p>If you feel that your plugin must map some keys, please make those mappings
|
|
configurable in some way.</p>
|
|
|
|
<p>There are a number of ways to do this. The easiest way is to provide a configuration
|
|
option that disables all mappings. The user can them remap the keys as they see fit.
|
|
For example:</p>
|
|
|
|
<pre><code>if !exists('g:yourplugin_map_keys')
|
|
let g:yourplugin_map_keys = 1
|
|
endif
|
|
|
|
if g:yourplugin_map_keys
|
|
nnoremap <leader>d :call <sid>YourPluginDelete()<CR>
|
|
endif</code></pre>
|
|
|
|
<p>Normal users will get the mappings automatically set up for them, and power users can
|
|
remap the keys to whatever they wish to avoid shadowing their own mappings.</p>
|
|
|
|
<p>If your plugin's mappings all start with a common prefix (like <code><leader></code> or
|
|
<code><localleader></code>) you have another option: allow users to configure this prefix. This
|
|
is the approach I've used in <a href="https://sjl.bitbucket.io/threesome.vim">Threesome</a>. It works like this:</p>
|
|
|
|
<pre><code>if !exists('g:yourplugin_map_prefix')
|
|
let g:yourplugin_map_prefix = '<leader>'
|
|
endif
|
|
|
|
execute "nnoremap" g:yourplugin_map_prefix."d" ":call <sid>YourPluginDelete()<CR>"</code></pre>
|
|
|
|
<p>The <code>execute</code> command lets you build the mapping string dynamically so your users can
|
|
change the mapping prefix.</p>
|
|
|
|
<p>There is a third option for solving this problem: the <code>hasmapto()</code> Vim function.
|
|
Some plugins will use this to map a command to a key <em>unless</em> the user has already
|
|
mapped that command to something else. I don't personally like this option because
|
|
it feels less clear to me, but I know other people feel differently so I wanted to
|
|
mention it.</p>
|
|
|
|
<h2 id="s10-localize-mappings-and-settings"><a href="index.html#s10-localize-mappings-and-settings">Localize Mappings and Settings</a></h2>
|
|
|
|
<p>The next step in being a good Vim plugin author is to try to minimize the effects of
|
|
your key mappings and setting changes. Some plugins will need to have global
|
|
effects but others will not.</p>
|
|
|
|
<p>For example: if you're writing a plugin for working with Python files it should only
|
|
take effect for Python buffers, not all buffers.</p>
|
|
|
|
<h3 id="s11-localizing-mappings"><a href="index.html#s11-localizing-mappings">Localizing Mappings</a></h3>
|
|
|
|
<p>Key binding are easy to localize to single buffers. All of the <code>noremap</code> commands
|
|
can take an extra <code><buffer></code> argument that will localize the mapping to the current
|
|
buffer.</p>
|
|
|
|
<pre><code>" Remaps <leader>z globally
|
|
nnoremap <leader>z :YourPluginFoo<cr>
|
|
|
|
" Remaps <leader>z only in the current buffer
|
|
nnoremap <buffer> <leader>z :YourPluginFoo<cr></code></pre>
|
|
|
|
<p>However, the problem is that you need to run this command in every buffer you want
|
|
the mapping active. To do this your plugin can use an <code>autocommand</code>. Here's a full
|
|
example, using this concept plus the previously mentioned configuration options:</p>
|
|
|
|
<pre><code>if !exists('g:yourplugin_map_keys')
|
|
let g:yourplugin_map_keys = 1
|
|
endif
|
|
|
|
if !exists('g:yourplugin_map_prefix')
|
|
let g:yourplugin_map_prefix = '<leader>'
|
|
endif
|
|
|
|
if g:yourplugin_map_keys
|
|
execute "autocommand FileType python" "nnoremap <buffer>" g:yourplugin_map_prefix."d" ":call <sid>YourPluginDelete()<CR>"
|
|
endif
|
|
</code></pre>
|
|
|
|
<p>Now your plugin will define a key mapping only for Python buffers, and your users can
|
|
disable or customize this mapping as they see fit.</p>
|
|
|
|
<p>This mapping command is quite ugly. Unfortunately that's the price of using
|
|
Vimscript and trying to make a plugin that will work for many users. Later I'll talk
|
|
about one possible solution to this ugliness.</p>
|
|
|
|
<h3 id="s12-localizing-settings"><a href="index.html#s12-localizing-settings">Localizing Settings</a></h3>
|
|
|
|
<p>Just as you should make mappings local to buffers when appropriate, you should do the
|
|
same with settings like <code>foldmethod</code>, <code>foldmarker</code> and <code>shiftwidth</code>. Not all
|
|
settings can be set locally in a buffer. You can read <code>:help <settingname></code> to see
|
|
if it's possible.</p>
|
|
|
|
<p>You can use <code>setlocal</code> instead of <code>set</code> to localize settings to individual buffers.
|
|
Like with mappings you'll need to use an autocommand to run the <code>setlocal</code> command
|
|
every time the users opens a new buffer.</p>
|
|
|
|
<h2 id="s13-autoload-is-your-friend"><a href="index.html#s13-autoload-is-your-friend">Autoload is Your Friend</a></h2>
|
|
|
|
<p>If your plugin is something that users will be using all the time you can skip this
|
|
section.</p>
|
|
|
|
<p>If you're writing something that will only be used in specific cases, you can help
|
|
your users by using Vim's <code>autoload</code> functionality to delay loading its code until
|
|
the user actually tries to use it.</p>
|
|
|
|
<p>The way <code>autoload</code> works is fairly simple. Normally you would bind a key to call one of your
|
|
plugin's functions with something like this:</p>
|
|
|
|
<pre><code>nnoremap <leader>z :call YourPluginFunction()<CR></code></pre>
|
|
|
|
<p>You can use autoloading by prepending <code>yourplugin#</code> to the name of the function:</p>
|
|
|
|
<pre><code>nnoremap <leader>z :call yourplugin#YourPluginFunction()<CR></code></pre>
|
|
|
|
<p>When this mapping is run, Vim will do the following:</p>
|
|
|
|
<ol>
|
|
<li>Check to see if <code>YourPluginFunction</code> is already defined. If so, call it.</li>
|
|
<li>Otherwise, look in <code>~/.vim/autoload/</code> for a file named <code>yourplugin.vim</code>.</li>
|
|
<li>If it exists, parse and load the file (which presumably defines
|
|
<code>YourPluginFunction</code> somewhere inside of it).</li>
|
|
<li>Call the function.</li>
|
|
</ol>
|
|
|
|
<p>This means that instead of putting all of your plugin's code in
|
|
<code>plugin/yourplugin.vim</code> you can put just the key mapping code there and pull the rest
|
|
out into <code>autoload/yourplugin.vim</code>.</p>
|
|
|
|
<p>If your plugin has a decent amount of code this can reduce the startup time of Vim by
|
|
a significant amount.</p>
|
|
|
|
<p>Check out the full documentation of <code>autoload</code> by running <code>:help autoload</code> to learn
|
|
much more.</p>
|
|
|
|
<h2 id="s14-backwards-compatibility-is-a-big-deal"><a href="index.html#s14-backwards-compatibility-is-a-big-deal">Backwards Compatibility is a Big Deal</a></h2>
|
|
|
|
<p>Once you've written your Vim plugin and released it into the wild, you have to
|
|
maintain it. Users will find bugs and ask for new features.</p>
|
|
|
|
<p>Part of being a responsible developer of any kind, including a Vim plugin author, is
|
|
maintaining backwards compatibility, <em>especially</em> for tools that users will use every
|
|
day and burn into their muscle memory. Users rely on tools to work, and tools that
|
|
break backwards compatibility will quickly lose users' trust.</p>
|
|
|
|
<p>Maintaining backwards compatibility will cause your plugin's code to get crufty in
|
|
spots, but it's the price of maintaining your users' happiness.</p>
|
|
|
|
<h3 id="s15-what-matters-for-backards-compatibility"><a href="index.html#s15-what-matters-for-backards-compatibility">What Matters for Backards Compatibility?</a></h3>
|
|
|
|
<p>For a Vim plugin the most important part of staying backwards compatible is ensuring
|
|
that key mappings, customized or not, continue to do what users expect.</p>
|
|
|
|
<p>If your plugin maps key <code>X</code> to do <code>Y</code>, then pressing <code>X</code> should <em>always</em> do <code>Y</code>, even
|
|
if you change how <code>Y</code> is called by renaming <code>Y</code> to <code>Z</code>. This may mean changing <code>Y</code>
|
|
into a wrapper function which simply calls <code>Z</code>.</p>
|
|
|
|
<p>There are many other aspects of backwards compatibility that you will have to
|
|
consider, depending on the purpose of your plugin. The rule of thumb you should
|
|
follow is: if a user uses this plugin on a daily basis and has its usage burned into
|
|
their muscle memory, updating the plugin should not make them relearn anything.</p>
|
|
|
|
<h3 id="s16-use-semantic-versioning-so-i-can-stay-sane"><a href="index.html#s16-use-semantic-versioning-so-i-can-stay-sane">Use Semantic Versioning So I Can Stay Sane</a></h3>
|
|
|
|
<p>A fast, simple, easy way to document your plugin's state is to use <a href="http://semver.org/">semantic
|
|
versioning</a>.</p>
|
|
|
|
<p>Semantic versioning is simply the idea that instead of picking arbitrary version
|
|
numbers for releases of your project, you use version numbers that describe the
|
|
backwards-compatible state in a meaningful way.</p>
|
|
|
|
<p>In a nutshell, these rules describe how you should select version numbers for new
|
|
releases:</p>
|
|
|
|
<ul>
|
|
<li>Version numbers have three components: <code>major.minor.bugfix</code>. For example: <code>1.2.4</code>
|
|
or <code>2.13.0</code>.</li>
|
|
<li>Versions with a major version of 0 (e.g. <code>0.2.3</code>) make no guarantees about
|
|
backwards compatibility. You are free to break anything you want. It's only after
|
|
you release <code>1.0.0</code> that you begin making promises.</li>
|
|
<li>If a release introduces backwards-incompatible changes, increment the major version
|
|
number.</li>
|
|
<li>If a release is backwards-compatible, but adds <em>new</em> features, increment the minor
|
|
version number.</li>
|
|
<li>If a release simply fixes bugs, refactors code, or improves performance, increment
|
|
the bugfix version number.</li>
|
|
</ul>
|
|
|
|
<p>This simple scheme makes it easy for users to tell (in a broad sense) what has
|
|
changed when they update your project.</p>
|
|
|
|
<p>If only the bugfix number has changed they can update without fear and continue on
|
|
without worrying about changes unless they're curious.</p>
|
|
|
|
<p>If the minor version number has changed they might want to look at the changelog to
|
|
see what new features they may want to take advantage of, but if they're busy they
|
|
can simply update and move on.</p>
|
|
|
|
<p>If the major version number has changed it's a major red flag, and they'll want to
|
|
read the changelog carefully to see what is different.</p>
|
|
|
|
<p>Some people don't like semantic versioning for the following reason:</p>
|
|
|
|
<blockquote>
|
|
<p>If I have to increment the major version number every time I make
|
|
backwards-incompatible changes, I'll quickly be at ugly versions like 24.1.2!</p>
|
|
</blockquote>
|
|
|
|
<p>To this I say: "Yes, but if that happens you're doing things wrong in the first
|
|
place."</p>
|
|
|
|
<p>Keep your project in "beta" (i.e. version <code>0.*.*</code>) for as long as you need to
|
|
experiment freely. <em>Take your time</em> and make sure you've gotten things (mostly)
|
|
right. Once you release <code>1.0.0</code> it's time to start being responsible and caring
|
|
about backwards compatibility.</p>
|
|
|
|
<p>Breaking functionality all the time harms your users by reducing their productivity
|
|
and frustrating them. Yes, it means adding some cruft to your code over time, but
|
|
it's the price of not being evil.</p>
|
|
|
|
<h2 id="s17-document-everything"><a href="index.html#s17-document-everything">Document Everything</a></h2>
|
|
|
|
<p>A critical part of releasing a Vim plugin to the world is writing documentation for
|
|
it. Vim has fantastic documentation itself, so your plugins should follow in its
|
|
footsteps and provide thorough docs.</p>
|
|
|
|
<h3 id="s18-pick-some-requirements-and-stick-to-them"><a href="index.html#s18-pick-some-requirements-and-stick-to-them">Pick Some Requirements and Stick to Them</a></h3>
|
|
|
|
<p>The most important part of your documentation is telling users what they need to have
|
|
in order to use your plugin. Vim runs on nearly every system imaginable and can be
|
|
compiled in many different ways, so being specific about your plugin's requirements
|
|
will save users a lot of trial and error.</p>
|
|
|
|
<ul>
|
|
<li>Does your plugin only work with Vim version X.Y or later?</li>
|
|
<li>Does it require Python/Ruby/etc support compiled in? Which version?</li>
|
|
<li>Does it not work on Windows?</li>
|
|
<li>Does it rely on an external tool?</li>
|
|
</ul>
|
|
|
|
<p>If the answer to any of those questions is "yes", you <em>must</em> mention it in the
|
|
documentation.</p>
|
|
|
|
<h3 id="s19-write-a-readme"><a href="index.html#s19-write-a-readme">Write a README</a></h3>
|
|
|
|
<p>The first step to documenting your plugin is to write a README file for the
|
|
repository. You can also use the text of this file as the description if you upload
|
|
your plugin to the <a href="http://www.vim.org/">vim website</a>, or the content of your plugin's website if you
|
|
create one for it.</p>
|
|
|
|
<p>Some examples of things to include in your README are:</p>
|
|
|
|
<ul>
|
|
<li>An overview of what the plugin does.</li>
|
|
<li>Screenshots, if possible.</li>
|
|
<li>Requirements.</li>
|
|
<li>Installation instructions.</li>
|
|
<li>Common configuration options that many users will want to know.</li>
|
|
<li>Links to:
|
|
|
|
<ul>
|
|
<li>A canonical web address to find the plugin.</li>
|
|
<li>The bug tracker for the plugin.</li>
|
|
<li>The source code or repository of the plugin.</li>
|
|
</ul></li>
|
|
</ul>
|
|
|
|
<h3 id="s20-create-a-simple-website"><a href="index.html#s20-create-a-simple-website">Create a Simple Website</a></h3>
|
|
|
|
<p>This isn't strictly necessary, but having a simple website for your plugin is an
|
|
extra touch that makes it seem more polished.</p>
|
|
|
|
<p>It also gives you a canonical URL that people can visit to get the latest information
|
|
about your plugin.</p>
|
|
|
|
<p>I've made simple sites for both of my plugins: <a href="https://sjl.bitbucket.io/gundo.vim/">Gundo</a> and <a href="https://sjl.bitbucket.io/threesome.vim">Threesome</a>. Feel
|
|
free to use them as an example or even take their code and use it for your own plugin
|
|
sites if you like.</p>
|
|
|
|
<h3 id="s21-write-a-vim-help-document"><a href="index.html#s21-write-a-vim-help-document">Write a Vim Help Document</a></h3>
|
|
|
|
<p>The bulk of your plugin's documentation should be in the form of a Vim help document.
|
|
Users are used to using Vim's <code>:help</code> and they'll expect to be able to use it to
|
|
learn about your plugin.</p>
|
|
|
|
<p>Creating a help document is as easy as creating a <code>doc/yourplugin.txt</code> file in your
|
|
project. It will be indexed automatically by <code>pathogen#helptags()</code> so your users
|
|
will have the docs at their fingertips.</p>
|
|
|
|
<p>Two easy ways to learn the syntax of help files are by reading <code>:help help-writing</code>
|
|
and using an existing plugin's help file as an example.</p>
|
|
|
|
<p>Take your time and craft a beautiful help file you can be proud of. Don't be afraid
|
|
to add a bit of personality to your docs to break the dryness. The <a href="https://github.com/scrooloose/syntastic/blob/master/doc/syntastic.txt">syntastic help
|
|
file</a> is a great example (especially the <code>About</code> section).</p>
|
|
|
|
<p>Things to include in your documentation:</p>
|
|
|
|
<ul>
|
|
<li>A brief overview of the plugin.</li>
|
|
<li>A more in-depth description of how the plugin is used.</li>
|
|
<li>Every single key mapping the plugin creates.</li>
|
|
<li>Ways to extend the plugin, if applicable.</li>
|
|
<li>All configuration variables (including their default values!).</li>
|
|
<li>The plugin's changelog.</li>
|
|
<li>The plugin's license.</li>
|
|
<li>Links to the plugin's repository and bug tracker.</li>
|
|
</ul>
|
|
|
|
<p>In a nutshell: your help file should contain <em>anything</em> a user would ever need to know
|
|
about your plugin.</p>
|
|
|
|
<h3 id="s22-keep-a-changelog"><a href="index.html#s22-keep-a-changelog">Keep a Changelog</a></h3>
|
|
|
|
<p>The last part of documenting your project is keeping a changelog. You can skip this
|
|
while your project is still in "beta" (i.e. less than version <code>1.0.0</code>) but once you
|
|
officially release a real version you need to keep your users informed about what has
|
|
changed between releases.</p>
|
|
|
|
<p>I like to include this log in the README, the plugin's website, and the
|
|
documentation to make it as easy as possible for users to see what's changed.</p>
|
|
|
|
<p>Try to keep the language of the changelog at a high enough level for your users to
|
|
understand without knowing anything about the implementation of your plugin. Things
|
|
like "added feature X" and "fixed bug Y" are great, while things like "refactored the
|
|
inner workings of utility function Z" are best left in commit messages.</p>
|
|
|
|
<h2 id="s23-making-vimscript-palatable"><a href="index.html#s23-making-vimscript-palatable">Making Vimscript Palatable</a></h2>
|
|
|
|
<p>The worst part about writing Vim plugins is, without a doubt, dealing with Vimscript.
|
|
It's an esoteric language that's grown organically over the years seemingly without
|
|
any strong design direction.</p>
|
|
|
|
<p>Features are added to Vim, then Vimscript features are added to control those
|
|
features, then hacky workarounds are added for flexibility.</p>
|
|
|
|
<p>The syntax is terse, ugly and inconsistent. Is <code>" foo</code> a comment? Sometimes.</p>
|
|
|
|
<p>Much of the time you'll spend writing your first plugin will be learning how to do
|
|
things in Vimscript. The help documentation on all of its features is thorough, but
|
|
it can be hard to find what you're looking for if you don't know the exact name.
|
|
Looking through other plugins is often very helpful in pointing you toward what you
|
|
need.</p>
|
|
|
|
<p>There are a couple of ways to ease the pain of Vimscript, and I'll briefly talk about
|
|
two of them here.</p>
|
|
|
|
<h3 id="s24-wrap-everything"><a href="index.html#s24-wrap-everything">Wrap. Everything.</a></h3>
|
|
|
|
<p>The first piece of advice I have is this: if you want to make your plugins readable
|
|
and maintainable then you need to wrap up functionality even more than you would in
|
|
other languages.</p>
|
|
|
|
<p>For example, my <a href="https://sjl.bitbucket.io/gundo.vim/">Gundo</a> plugin has a few utility functions that look like this:</p>
|
|
|
|
<pre><code>function! s:GundoGoToWindowForBufferName(name)"{{{
|
|
if bufwinnr(bufnr(a:name)) != -1
|
|
exe bufwinnr(bufnr(a:name)) . "wincmd w"
|
|
return 1
|
|
else
|
|
return 0
|
|
endif
|
|
endfunction"}}}</code></pre>
|
|
|
|
<p>This function will go to the window for the given buffer name and gracefully handle
|
|
the case where the buffer/window does not exist. It's verbose but much more readable
|
|
than the alternative of using that <code>if</code> statement in every place I need to switch
|
|
windows.</p>
|
|
|
|
<p>As you write your plugin you'll "grow" a number of these utility functions. Any time
|
|
you duplicate code you should think about creating one, but you should also do so any
|
|
time you write a particularly hairy line of Vimscript. Pulling complex lines out
|
|
into named functions will save you a lot of reviewing and rethinking down the line.</p>
|
|
|
|
<h3 id="s25-scripting-vim-with-other-languages"><a href="index.html#s25-scripting-vim-with-other-languages">Scripting Vim with Other Languages</a></h3>
|
|
|
|
<p>Another option for making Vimscript less painful is to simply not use it much at all.
|
|
Vim includes support for creating plugins in a number of other languages like Python
|
|
and Ruby. Many plugin authors choose to move nearly all of their code into another
|
|
language, using a small Vimscript "wrapper" to expose it to the user.</p>
|
|
|
|
<p>I decided to try this approach with <a href="https://sjl.bitbucket.io/threesome.vim">Threesome</a> after seeing it used in the
|
|
<a href="https://github.com/jceb/vim-orgmode">vim-orgmode</a> plugin to great effect. Overall I consider it to be a good idea,
|
|
with a few caveats.</p>
|
|
|
|
<p>First, using another language will requires your plugin's users to use a version of
|
|
Vim compiled with support for that version. In this day and age it's usually not
|
|
a problem, but if you want your plugin to run everywhere then it's not an option.</p>
|
|
|
|
<p>Using another language adds overhead. You need to not only learn Vimscript but also
|
|
the interface between Vim and the language. For small plugins this can add more
|
|
complexity to the project than it saves, but for larger plugins it can pay for
|
|
itself. It's up to you to decide whether it's worth it.</p>
|
|
|
|
<p>Finally, using another language does not entirely insulate you from the
|
|
eccentricities of Vimscript. You still need to learn how to do most things in
|
|
Vimscript — using another language simply lets you wrap most of this up more neatly
|
|
than you otherwise could.</p>
|
|
|
|
<h3 id="s26-unit-testing-will-make-you-drink"><a href="index.html#s26-unit-testing-will-make-you-drink">Unit Testing Will Make You Drink</a></h3>
|
|
|
|
<p>Unit testing (and other types of testing) is becoming more and more popular today.
|
|
In particular the Python and Ruby communities seem to be getting more and more
|
|
excited about it as time goes on.</p>
|
|
|
|
<p>Unfortunately, unit testing Vim plugins lies somewhere between "painful" and
|
|
"<a href="http://www.amazon.com/Garden-Weasel-90206/dp/B002ECYRH4">garden-weasel</a>ing your face" on the difficulty scale.</p>
|
|
|
|
<p>I tried adding some unit tests to <a href="https://sjl.bitbucket.io/gundo.vim/">Gundo</a>, but even after looking at a number of
|
|
frameworks I was spending hours simply trying to get my tests to function.</p>
|
|
|
|
<p>I didn't even bother trying to add tests to <a href="https://sjl.bitbucket.io/threesome.vim">Threesome</a> because for every hour
|
|
I would have spent fighting Vim to create tests I could have cleaned up the code and
|
|
fixed bugs instead.</p>
|
|
|
|
<p>I'll gladly change my opinion on the subject if someone writes a unit testing
|
|
framework for Vim that's as easy to use as <a href="https://bitheap.org/cram/">Cram</a>. In fact, I'll even buy the
|
|
author a $100 bottle of scotch (or whatever they prefer).</p>
|
|
|
|
<p>Until that happens I personally don't think it's worth your time to unit test Vim
|
|
plugins. Spend your extra hours reading documentation, testing things manually with
|
|
a variety of settings, and thinking hard about your code instead.</p>
|
|
|
|
<h2 id="s27-tl-dr"><a href="index.html#s27-tl-dr">TL;DR</a></h2>
|
|
|
|
<p>Writing Vim plugins is tricky. Vimscript is a rabbit hole of sadness and despair,
|
|
and trying to please all your users while maintaining backwards compatibility is
|
|
a monumental task.</p>
|
|
|
|
<p>With that said, creating something that people use every day to help them make
|
|
beautiful software projects is extremely rewarding. Even if your plugin doesn't get
|
|
many users, being able to use a tool <em>you wrote</em> is very satisfying.</p>
|
|
|
|
<p>So if you've got an idea for a plugin that would make Vim better just sit down, learn
|
|
about Vimscript, create it, and release it so we can all benefit.</p>
|
|
|
|
<p>If you have any questions or comments feel free to hit me up <a href="http://twitter.com/stevelosh">on
|
|
Twitter</a>. You might also enjoy following <a href="http://twitter.com/dotvimrc">@dotvimrc</a> where I try to
|
|
tweet random, bite-sized lines you might like to put in your <code>.vimrc</code> file.</p>
|
|
</article></main><hr class='main-separator' /><footer><nav><a href='https://github.com/sjl/'>GitHub</a> ・ <a href='https://twitter.com/stevelosh/'>Twitter</a> ・ <a href='https://instagram.com/thirtytwobirds/'>Instagram</a> ・ <a href='https://hg.stevelosh.com/.plan/'>.plan</a></nav></footer></body></html> |