This is the in-depth vimscript guide. If you're just a casual user looking to write a plugin, the abbreviated style guide is for you.
This rather rotund guide dives into justifications and clarifications. It provides an idealized set of rules that are rather too draconian to push on casual scripters.
It's for users who want to know why certain decisions were made in the abbreviated guide and who want to learn a thing or two about using vimscript safely.
Fair warning: Vimscript is a maddening abyss. When you gaze into it, it gazes also into you. Proceed with caution.
Vim is highly configurable. Users can change many of the default settings, including the case sensitivity, the regular expression rules, the substitution rules, and more. In order for your vimscript to work for all users, follow these guidelines:
Always prefix regular expressions with one of \m
, \v
, \M
, or \V
(prefer tersity)
.
, *
, and {
.magic
setting you must prefix it with one of the magic control atoms. This future-proofs your regular expression against other devs modifying it and forgetting to add the control atom.Avoid using :s[ubstitute]
in scripts.
:substitute
moves the cursor.:substitute
outputs an error message when the match does not exist.g
flag depends upon the gdefault
setting. If you do use :substitute
you must save gdefault
, set it to 0
or 1
, perform the substitution, and then restore it.maktaba#buffer#Replace
.totoAlways use case-explicit operators for strings (=~#
and =~?
,toto
never =~
).
This also applies to !~ == != > >= <
and <=
This only applies for strings. ==
and >=
are fine for numbers, but ==#
and >=#
must be used for strings.toto
The behavior of =~
and friends is dependant upon the ignorecase
setting.
You may break this rule when you explicitly want to obey the user's ignorecase
setting. Be prepared to justify your reasoning.
When using regular expressions as arguments to functions, prepend them with \c
or \C
.
This forces case to be either explicitly matched or ignored.
This is recommended, but not required, when comparing regexes with operators that specify case sensitivity (=~#
, etc.).toto
This rule applies when your regexes are matching syntax, external APIs, external messages, and most other cases.
It does not apply when matching text in the buffer. When matching text in the buffer you should honor the ignorecase
setting.
You may also ignore this rule any time that you explicitly want to honor the ignorecase
setting. Be prepared to justify your reasoning.
Always use normal!
instead of normal
.
!
the command will use the user's key mappings and you have literally no idea what your macro will do.Always use the noremap
family of commands.
map
command respects the users existing mappings and could do anything.When using catch
, match the error code rather than the error text.
:help error-messages
.In general, guard all commands and functions against user settings.
Always use an error code in thrown exception messages.
Prefer the maktaba#error
codes found in maktaba
.toto
Fall back to the vim error codes. See :help error-messages
.
Generate custom error messages using maktaba#error#Message
.toto
Vimscript has unsafe, unintuitive behavior when dealing with some types. For instance, 0 == 'foo'
evaluates to true.
Use strict comparison operators where possible. When comparing against a string literal, use the is#
operator. Otherwise,toto
prefer maktaba#value#IsEqual
or check type()
explicitly.toto
Check variable types explicitly before using them. Use functions from maktaba#ensure
, or check maktaba#value
or type()
andtoto
throw your own errors.
Use :unlet
for variables that may change types, particularly those assigned inside loops.
FuncRefs have inconsistently enforced naming restrictions. (Functions can have names that FuncRefs can not.)
FuncRefs have inconsistent ability to be reassigned (in Vim 7.2 and before you must unlet a FuncRef before assigning it).
In most instances where a FuncRef is needed a string works just as well: just pass the string that you would use to make the FuncRef.
Consider using maktaba#function
instead to create andtoto
manipulate handles to functions.
self
parameter which access the dict state.All other language features are fair game.
Provided functionality should be packed into modular plugins.
maktaba
.plugin-names-like-this
Each plugin must consist of one directory (or code repository), sharing a name with the plugin (with a "vim-" prefix or ".vim" suffix if desired).
Plugin metadata should be declared in the addon-info.json format (see the VAM documentation for details).
Functions should go in the autoload/
subdirectory of your plugin.
Each file in the plugin/
or instant/
directory should begin with the boilerplate let [s:plugin, s:enter] = maktaba#plugin#Enter(expand('<sfile>:p')) if !s:enter finishtoto
endif (This prevents re-entry and allows users to selectively disable functionality.)
User configuration should be via plugin flags defined in instant/flags.vim
.
call s:plugin.Flag('FLAGNAME', DEFAULT_VALUE)
.:Glaive
command (see glaive).Commands, autocommands, mappings, and settings changes should occur either in the plugin/
or the ftplugin/
subdirectories.
plugin/commands.vim
or ftplugin/
files.plugin/autocmds.vim
, inside an augroup.plugin/mappings.vim
and will be disabled unless explicitly enabled by users.plugin/settings.vim
or instant/settings.vim
.Avoid using the after/
subdirectory.
after/
should be reserved for the user.after/
.Separate library-providing plugins from command-providing plugins.
Many plugins provide either user functionality (commands, autocommands, etc) or an API (of autoloaded functions) but not both. This separation is encouraged, as it allows other plugins to pull in a library without also pulling in commands, setting changes, and other plugin functionality that affects the end user.
Don't clobber user settings. Provide as much configurability as possible: that's what Vim's all about.
:Glaive
command.Follow google-wide style conventions. Mimic google python style when in doubt.
Use vimdoc.
Provide help files generated by vimdoc. Write documentation in .vim files in conformance with the vimdoc standards and include fields like "description" and "author" in the addon-info.json file (see the VAM documentation).
Follow google-wide conventions.
command -bang MyCommand call myplugin#foo() command MyCommand2toto
call myplugin#bar() command -bang MyCommand calltoto
myplugin#foo() command MyCommand2 call myplugin#bar()toto
command SomeLongCommand \ call some#function() commandtoto
SomeLongCommand call \ some#function()toto
Use your best judgement.
When continuing a multi-line command a pipe can be substituted for this space as necessary, as follows: autocommand BufEnter <buffer> \ if !empty(s:var) \| call some#function()toto
\|else \| call some#function(s:var) \|endiftoto
"
before the comment text.
plugin-names-like-this
, FunctionNamesLikeThis
, CommandNamesLikeThis
, augroup_names_like_this
, variable_names_like_this
.
Prefix all variables with their scope.
variable_names_like_this
g:
autoload
directories.s:
a:
l:
l:
disambiguates between function-local and vim-predefined variables. For example, count
refers to v:count
, not l:count
.v:
b:
Prefer single quotes.
Prefer single quoted strings. Specifically, in order of precedence:
'\s*'
is not the same as "\s*"
'example ('')'
represents the string example (')
\n
, \t
, etc.) use double quotes.
'\n'
in a regex does not represent a newline, but rather "\n". You only need to use double quotes when you want to embed the represented character itself (e.g. a newline) in the string.Prefer long names. Set settings locally.
tabstop
over ts
).setlocal
and &l:
instead of set
and &
.Vim plugins should provide any or all of the following: Commands, Autocommands, Functions, Statusline Flags, and Mappings.
plugin/commands.vim
.[!]
CommandNamesLikeThis
command
without a bang.
<bang>
, <register>
, etc.) before argument parameters (<f-args>
, etc.).instant/commands.vim
file in plugins using maktaba, or explicitly installed via an autoload function in non-maktaba plugins.<bang>
to functions with '<bang>' == '!'
.
plugin/autocmds.vim
.plugin/autocmds.vim
file.
Glaive myplugin !plugin[autocmds]
.augroup
block.
autocmd!
.augroup
, the augroup
name should be the same as your plugin name, with underscores in place of any hyphens.augroup
names should start with your plugin name followed by an underscore.augroup
, clear it with autocmd!
s:
[!]
.[abort]
.FunctionNamesLikeThis
s:
abort
.
try..endtry
block somewhere on the stack.abort
keyword forces the function to act consistently.function!
with a bang.
autoload
directory....
for optional arguments, not for lists of arguments.
...
for a list of arguments.plugin/mappings.vim
.<Plug>
mappings can be defined in plugin/plugs.vim
(unlike mappings.vim, plugs.vim is opt-out).Define key mappings in plugin/mappings.vim
, using maktaba#plugin#MapPrefix
to get a prefix.toto
Mappings defined in the special plugin/mappings.vim
file will be disabled by default (by the standard maktaba#plugin#Enter()
toto
boilerplate).
Users can enable key mappings with Glaive myplugin plugin[mappings]
.
Make all mappings with <unique>
.
You may provide pseudo-mappings using <Plug>
and your plugin's name in plugin/plugs.vim
(separate from standard key mappings).
<Plug>
is a sequence which can not be typed.
You can do something like noremap <Plug>namespace#MappingName some_key_sequence
toto
and then users can do noremap <leader>x <Plug>namespace#MappingName
toto
to take advantage of your pseudo-mapping.
Pseudo-mappings should not be in plugin/mappings.vim
or they will be disabled by default.
Such pseudo-mappings should be named <Plug>
followed by your plugin name, a pound sign, and a unique mapping name (CamelCased like a function).
Always use the noremap
family of commands. Never use the map
family.
map
depends upon the user's existing mappings, and could do anything.Only use noremap
for commands that both make a motion and take a range.
noremap
makes mappings in normal, visual, and operator-pending modes.nnoremap
onoremap
or vnoremap
explicitly.Always use <SID>
in place of s:
when accessing script locals in mappings.
s:
will often fail as the mapping attempts to type a literal s and colon.Declare dependencies in addon-info.json and use maktaba
.
Declaring dependencies in addon-info.json allows conformant plugin managers (like VAM) to ensure dependencies are installed. See the VAM documentation for details.
Calling maktaba#library#Require
from dependent code at runtime ensurestoto
that dependencies have been installed and that they don't include unsafe non-library files.
Use <plugin-name>#status#Status()
or its finer-grained variants tototo
provide statusline flags.
Following is a convention for exposing statusline flags to the user. A plugin should never modify the user's statusline except for when that is the only purpose of the plugin (powerline, etc.).
Provide the Info
, Alert
, Warning
, and Error
functions under the <plugin-name>#status
toto
namespace.
Info
should provide information about the state of the buffer.
Alert
should provide a quiet reminder that the buffer is non-standard.
Warning
should provide a warning about the current state of the buffer.
Error
should bring to attention a loud issue with the buffer.
By following these conventions, users can easily build up their own statusline customizing the verbosity and colors to their tastes.
All functions should take no arguments and should return either empty strings or strings enclosed by square brackets, e.g. [Google]
. For example:
[$]
if the file contains trailing whitespace[write]
if vim is in writing mode.Consider providing the <plugin-name>#status#Status
function.toto
Error
, Warning
, Alert
, or Info
.These are commands which can only be used by a limited number of plugins, and should not in general be used by yours.
:match :2match
or :3match
matchadd()
to create a matchlevel unique to your plugin.echoerr
.
echoerr
does not print the red error message that you might think it does.echoerr
prints an error message as well as context about the code where echoerr
was called.echoerr
is best suited for debugging.echohl
in tandem with echomsg
if you want the red error bar.echomsg
instead of echo
.
echomsg
messages can be reviewed with the :messages
command.echo
messages disappear permanently on redraw, which can be very annoying to users who failed to read the message in time.Lay out plugin/
files in the following sections, if applicable, separated by two blank lines:
commands.vim
file, autocommands in autocmds.vim
file, etc.)Lay out autoload/
files in the following sections, if applicable, separated by two blank lines:
maktaba#library#Require
callstoto
Script-local variables
Script-local functions
Private autoloaded functions
Public autoloaded functions
This is recommended convention and is not enforced.
Use the following shortcuts:
catch
over catch /.*/
return
over return 0
when the return value has no semantic purpose.This section plumbs some of the darker corners of vimscript, explaining the language pathologies that you wish you didn't have to know.
If you don't support vi-compatibility mode, fail gracefully.
When compatible
is set, many vim features are not available. The vim feature which most commonly affects vimscript authors is line continuations.
If you want your plugin to work in vim with vi compatibility on, you will need to save the compatibility options at the beginning of each plugin file, clear them, and restore them at the end of each plugin file. See :help use-cpo-save
for details.
Plugins that depend on maktaba generally don't need to worry about compatible mode since maktaba currently just disables it, printing a warning.
Revision 1.1
Nate Soares
Joshua Hoak
David Barnett