Yes, this should be an exception (in my case for 18% of the plugins,
mostly for code snippet plugins and their libraries), and yes, we should
definitely encourage plugins to get their feature and version checks
right.
In my suggestion to load plugins according to a dependency graph, the
graph will mostly be empty, and Vim will load the plugins just as it
used to do it, letting the plugins decide if they can load successfully.
For the cases where fallbacks need to be specified, Vim will still
attempt to load plugin A, and only if that fails it will attempt to load
plugin B. So decisions are still based on the plugin's go/no go
assessment.
Yes, this is part of a plugin manager.
Plugin managers currently use &runtimepath to implement conditional
loading of plugins, based on conditions specified by the user.
If we want to clean up &runtimepath, and take fallback actions (that is,
load other plugins) based on the failure or success of loading plugins,
we need some other way to specify the fallbacks to Vim.
If we don't offer some other way to specify fallbacks, and to
disable/enable plugins, plugin managers will continue to resort to using
&runtimepath.
Using &runtimepath means the decision to load A or B and C has to be
made when &runtimpath is set up, which is before plugin loading is
attempted. So in fallback cases the user has to figure out and specify
the conditions when to load each plugin.
Which is something I would like to change. Let Vim load A, and only if
that fails load B and C.
Admittedly, fallback handling is not needed in many cases. But what can
plugin managers do except for using &runtimepath to cover such cases?
Please see below for a suggestion.
Hm, I was also thinking that it may be too much. Maybe it is.
Here is an idea of what it would look like:
Vim can remember the success of loading a plugin in
v:scripts[<scriptname>]. This stores the value the plugin has given
with :finish <value>.
Typically 0 or 1, -1 if no value was given, and -2 if the plugin did
not execute :finish. (A plugin can only specify values >= 0.)
This should cover most current uses of :finish correctly, and gives
plugins a way to be exact about loading successfully in the future.
Special (legacy) cases can be dealt with by plugin managers, please
see below.
<scriptname> is the full path name of the plugin.
Additionally, there are indices for the plugin base names, that is
the names without path and without trailing .vim:
v:scripts[<basename>] = ['<scriptname1>', '<scriptname2>', ...]
(There can be no collisions, <basename> cannot contain path
separators, <scriptname> must contain at least a leading path
separator.)
In .vimrc, the user can say
:plugdep "<basename>" "<condition>" or
:plugdep "<scriptname>" "<condition>"
and the condition can use the v:scripts dictionary, or just 0.
The "<condition>" is eval()'ed by Vim before Vim tries to load the
plugin <scriptname>. If the result is false, Vim skips the plugin.
Plugins with dependencies are considered last, and in the order of the
:plugdep commands.
Example:
:function PluginLoaded(name)
: " ':finish' -> not loaded, no ':finish' -> loaded
: " (and below: ':finish <value>' -> loaded if <value> > 0)
: let loaded = {-1: 0, -2: 1}
: if !has_key(v:scripts, a:name)
: return 0 " unknown -> not loaded
: endif
: let val = v:scripts[a:name]
: if type(val) == type(1)
: " a:name is full plugin path
: return get(loaded, val, val) > 0
: endif
: " plugin basename: has loaded if loaded from any path
: return max(
: \ map(val, 'get(loaded, v:scripts[v:val], v:scripts[v:val])')
: \ ) > 0
:endfunction
:
:plugdep 'vim-addon-mw-utils' '!PluginLoaded("ultisnips")'
:plugdep 'tlib_vim' '!PluginLoaded("ultisnips")'
:plugdep 'vim-snipmate' '!PluginLoaded("ultisnips")'
:
:plugdep 'test_me' '0' " disabled
Example for netrw (Gary):
:plugdep $HOME.'/.vim/plugin/netrwPlugin.vim'
: \ 'v:version > 703 || (v:version == 703 && has("patch465"))'
:plugdep 'netrwPlugin' '!PluginLoaded("netrwPlugin")'
netrw always sets its g:loaded_netrwPlugin variable, even if it does
not load successfully. If it would set g:loaded_netrwPlugin only
when loading successfully, then several attempts could be made to
try loading different versions of it, and we would only need to
specify order of precedence here (= order of :plugdep commands):
:plugdep $HOME.'/.vim/netrw/netrwPlugin.vim' '1'
:plugdep 'netrwPlugin' '1' " or '!PluginLoaded("netrwPlugin")'
And maybe there should be a way to suppress netrw's error message
for unsuccessful loads.
Too much, maybe. But maybe not?
There still is the assumption that ':finish' (without value) means 'not
loaded'. But that's usually true, and where it is not, the plugin
manager can make a list of such plugins and use special checks on the
plugin's g:..._loaded variables (in the PluginLoaded() function).
With this in place, I believe &runtimepath does not need to be changed
by plugin managers.
And in all cases the decision of whether a plugin loads successfully is
left to the plugin to decide. Unless the user *really* wants to
override this.