- Introduction
- Option API
- Note on how options could be set
There exist two kinds of options in Vim.
First there are vim options that are used to tune how Vim behaves in various
situations. They are set with
:set
. Some are global,
other are
local to buffers.
In this later case we usually choose their value either on a filetype basis, or
on a project basis.
Then there are plugin options.
Most plugins only care for global options. In that case they are plenty happy with:
" At the start of the plugin script, to make sure the option exists
let g:my_option = get(g:, 'my_option', somedefaultvalue)
...
" and then everywhere else, when the option is used
call s:use(g:my_option)
Alas sometimes, we need to use an option that can be specific to the current project (and/or to the current filetype). Typical examples are: "which naming convention should be applied?", "where the compilation shall happen for the current project?", "where project specific snippets are stored?", and so on.
This means that technically, again, options can be either g:
lobal, or b:
uffer-local,
or even w:
indow-local or t:
ab-local.
In those later cases, we have no choice but to fetch the option in the last moment. Indeed, we cannot store the value of the option when the plugin starts and expect it to remain valid in a buffer not yet opened. Instead, first we'd check if there is a window-local option, then a buffer-local option, then a tab-local option... and eventually a global option.
In order to simplify the process of fetching the value of such not-always-global (plugin) options, lh-vim-lib provides a few helper functions to plugin authors.
Sometimes, we need to distinguish when an option is not set, from the case it's voluntarily set to 0, 42, an empty list, etc.
In order to represent the state unset, a special option value can be obtained
(with lh#option#unset()
), and checked (with lh#option#is_set()
and
lh#option#is_unset()
).
Example:
let opt = lh#option#get('my.option')
if lh#option#is_unset(opt)
echoerr "Sorry, you need to set the option (bpg):my.option"
endif
All the following functions, that permit to fetch an option value, take an
optional parameter: the {scope}
list that specifies where to search for the
option.
By default, {scope}
is considered to be "bpg"
.
This means, that lh#option#get('my.option')
will search in order for an
option named either b:my.option
, or p:my.option
, or at the last resort
g:my_option
.
This means we can specify the scopes, and the order, in which a variable is
searched. For instance, If you wish to also check w:my.option
or
t:my.option
, then pass for instance "wbptg"
to the {scope}
parameter.
Notes:
- In Vim documentation, what I call scope is named name spaces.
- You need to be aware that lh-vim-lib functions cannot access script-variables.
- As a personal convention, when I document an option, I prefix its name with
the list of admissible scopes. IOW, you'll see
(bpg):my.option
, or(wbg):my.other_option
..., in the documentation of my plugins depending of the supported scopes.
At this point, you're probably wondering what are those p:variables
as there
is no such beast in Vim. Indeed. p:variables
are a personal extension meant
to hold project-variables. You'll find a more thorough description of this
feature in the related help page.
Some the options available though lh-vim-lib and its ft-option API
(lh#ft#option#*()
) can be specialized for each filetype. Doing so for every
filetype would quickly become cumbersome when these filetypes have a lot in
common like for instance C and C++. Instead of copying all
(bpg):c_some.options
into (bpg):cpp_some.options
, it's more interesting to
say that (bpg):c_some.options
are valid in C++ contexts.
As a consequence, to simplify options tuning, lh#ft#option#*()
functions support
filetype inheritance.
By default, C++ option settings inherits C option settings. In future versions, Java option settings may also inherit C or C++ option settings.
If you want to define new inheritance relations between filetypes, send me an
email so I add it to the default configuration, or do so in your .vimrc
with
:let g:{ft3}_inherits = 'ft1,ft2,...'
You may have noticed that I use indiscriminately (bpg):my_option
and
(bpg):my.options
in my examples. lh-vim-lib option-API makes no difference
between both.
Dictionary options
permit to limit scope pollution. For instance, instead of having
g:myplugin_option1
, g:myplugin_option2
..., with g:myplugin.option1
,
g:myplugin.option2
... we only see g:myplugin
in
g:
.
That's why a non negligible number of plugins use dictionary options.
Note: lh-vim-lib :LetIfUndef
and :LetTo
commands are able to assign
dict.key.subkey
is one step. (TODO: add link)
Adds new values to a vim list-option -- and prevents the values from being listed more than once.
Example:
call lh#option#add('l:tags', '.tags')
" or
call lh#option#add('l:tags', ['.tags'])
" which are equivalent to
setlocal tags+=.tags
This function becomes interesting to use variables and avoid more complex code like:
exe 'setlocal tags+='.fnameescape(some_path.'/tags')
Fetches the value of a user defined option, that may be empty.
Parameters:
{name}
option (root) name{default}
default value for the option if not found -- default:lh#option#unset()
{scope}
scopes in which the option name shall be searched -- default:"bpg"
See also:
lh#option#get_non_empty()
lh#option#get_from_buf()
lh#ft#option#get()
lh#ft#option#get_postfixed()
lh#ft#option#get_all()
Fetches the value of a user defined option, that is not empty.
IOW, returns of b:{name}
, g:{name}
..., or {default}
the first which exists and is not empty.
Parameters:
{name}
option (root) name{default}
default value for the option if not found -- default:lh#option#unset()
{scope}
scopes in which the option name shall be searched -- default:"bpg"
See also:
lh#option#get_non_empty()
lh#option#get_from_buf()
Same as lh#option#get()
except that it works from {bufid}
context.
Parameters:
{bufid}
buffer identifier to use to search forb:{name}
{name}
option (root) name{default}
default value for the option if not found -- default:lh#option#unset()
{scope}
scopes in which the option name shall be searched -- default:"bpg"
See also:
lh#option#get()
lh#option#get_non_empty()
Encapsulates getbufvar(buf, varname, lh#option#unset())
when {default}
is not passed. This provides getbufvar()
on older versions of vim.
Encapsulates getbufvar(buf, varname, get(g:, varname, lh#option#unset()))
.
These functions relate to options that can also be specialised on a filetype basis.
Filetype inheritance is supported in all these functions.
Fetches the value of a user defined option that can be specialized on a filetype basis
Returns which ever exists first among: b:{name}_{ft}
, or p:{name}_{ft}
, or
g:{name}_{ft}
, or b:{name}
, or p:{name}
, or g:{name}
. {default}
is
returned if none exists.
Parameters:
{name}
option (root) name{ft}
filetype for which the option name shall be searched -- usual value:&ft
{default}
default value for the option if not found -- default:lh#option#unset()
{scope}
scopes in which the option name shall be searched -- default:"bpg"
See also:
lh#option#get()
lh#ft#option#get_postfixed()
lh#ft#option#get_all()
Fetches the value of a user defined option that can be specialized on a filetype basis.
This function is similar to lh#ft#option#get()
. The difference relates to the
option names searched: returns which ever exists first among: b:{name}_{ft},
or g:{name}_{ft}
, or b:{name}
, or g:{name}
. {default}
is returned if
none exists.
Parameters:
{name}
option (root) name{ft}
filetype for which the option name shall be searched -- usual value:&ft
{default}
default value for the option if not found -- default:lh#option#unset()
{scope}
scopes in which the option name shall be searched -- default:"bpg"
See also:
lh#ft#option#get()
lh#ft#option#get_all()
Fetches the merged values of a dictionary that can be specialized on a filetype basis.
Unlike lh#ft#option#get()
, this time, we gather every possible value, but
we keep the most specialized value.
This only works to gather dictionaries scattered in many specialized variables.
Considering that the following variables will be
dictionaries --
expecting they exists --, this function will merge all their values into one,
keeping the most specialized value when there are.
Possible variable names: b:{ft}_{name}
, or p:{ft}_{name}
, or g:{ft}_{name}
, or
b:{name}
, or p:{name}
, or g:{name}
.
Parameters:
{name}
option (root) name{ft}
filetype for which the option name shall be searched -- default value:&ft
Example:
Unlet g:foo
Unlet b:foo
Unlet g:FT_foo
Unlet b:FT_foo
LetTo g:foo.glob = 'g'
LetTo g:foo.spe_buff = 'g'
LetTo g:foo.spe_gFT = 'g'
LetTo g:FT_foo.gFT = 'gft'
LetTo g:FT_foo.spe_gFT = 'gft'
LetTo g:FT_foo.spe_bFT = 'gft'
LetTo b:foo.buff = 'b'
LetTo b:foo.spe_buff = 'b'
LetTo b:foo.spe_bFT = 'b'
LetTo b:FT_foo.bFT = 'bft'
LetTo b:FT_foo.spe_bFT = 'bft'
let d = lh#ft#option#get_all('foo', 'FT')
AssertEquals(d.glob, 'g')
AssertEquals(d.buff, 'b')
AssertEquals(d.spe_buff, 'b')
AssertEquals(d.gFT, 'gft')
AssertEquals(d.spe_gFT, 'gft')
AssertEquals(d.bFT, 'bft')
AssertEquals(d.spe_bFT, 'bft')
See also:
lh#ft#option#get()
lh#ft#option#get_all()
Tells whether the expression is set (i.e. different from lh#option#unset()
).
Tells whether the expression is not set (i.e. identical to
lh#option#unset()
).
Returns an object that is interpreted as unset by
lh#option#is_set()
and lh#option#is_unset()
.
Parameter:
{textual context}
Optional message that can report more information about the nature of the unset option.
Example:
:echo lh#object#to_string(lh#option#unset())
{(unset)}
:echo lh#object#to_string(lh#option#unset('No known extension associated to xxx filetype'))
{(No known extension associated to xxx filetype)}
So far I haven't addressed the question regarding where options could be set depending on their scope.
The answer depends on the scope of the option we wish to define.
A global option is best initialized in a script that is loaded once. Afterward, it could be changed globally for every buffer on user actions.
The best place to initialize a variable once is quite certainly the
.vimrc
.
Sometimes, we use a plugin that uses global variables to tune a behaviour that
should have been project specific. That's for instance the case of
alternate.vim which uses g:alternateSearchPath
to indicate where to find a
header file given an implementation file and the other way around. That option
should be specific to each project, and yet, it's a global one. In those cases,
we could use the always loaded section of
local_vimrcs to change the value
of the option every time we enter a different buffer.
That's a classic workaround with plugins which aren't project-aware as they should have been.
When plugins do use options that can be specialized for each buffer (filetype and/or project specific options), we should not set those options in scripts which will initialize them in only one single buffer.
Indeed, if you set b:my.option
in your .vimrc
, it will be set only for the
buffer opened along with Vim. The option won't be known in buffers
opened/created later. It's the same with
plugin scripts.
At best a buffer-related
autocommand could be
defined in a .vimrc
or in a plugin script, and that autocommand will
eventually modify/set a buffer-local variable or a buffer-local vim-option.
Local options are thus best initialized from buffer-related autocommands. We could hard-code these autocommands manually, we could or rely on plugins or on core features that does this transparently. That is to say:
- From a filetype-plugin script, we can initialize local options based on the ... current filetype.
- From a local_vimrc, we can initialize local options based on the ... current directory. The semantics we associate to a directory is usually: "what's within belong to a same project".
- From a
.editorconfig
file given lh-vim-lib hook is used, we can initialize local options based on the current directory and on the current file extension. - From an extended let-modeline, we can define a local variable also -- but as modelines, this doesn't scale much.
- project and projectionist plugins provides other ways to achieve a similar result.
And of course, we can change local variables manually. But beware, changing a
buffer-local variable in a buffer won't change its value in other buffers, even
if they are meant to belong to a same project. However, when we change manually
a p:
roject variable in a buffer, the change will be replicated
to all other buffers belonging to the same project, whether they are already
opened or they'll be opened later on.