On 10-Feb-16, Bram Moolenaar wrote:
> 
> Olaf Dabrunz wrote:
> 
> > > > On 09-Feb-16, Bram Moolenaar wrote:
> > > > > 
> > > > > I had another idea.  Currently when installing a plugin or support for
> > > > > a language, the files are scattered over different directories under
> > > > > $VIMRUNTIME.  That makes it hard to update them.
> > > > > 
> > > > > How about this: use $VIMRUNTIME/bundles.  Below that will be the
> > > > > directories that are usually directly under $VIMRUNTIME.  For example,
> > > > > netrw would be installed in the directories:
> > > > >       $VIMRUNTIME/bundles/netrw/plugin
> > > > >       $VIMRUNTIME/bundles/netrw/autoload
> > > > >       $VIMRUNTIME/bundles/netrw/syntax
> > > > > It doesn't need an "indent" directory.
> > > > 
> > > > I like the idea.
> > > > 
> > > > But it should be easily possible to enable or disable plugins.
> > > > 
> > > > E.g., I enable some of my Vundle plugins conditionally depending on the
> > > > features in the vim version that I start:
> > > > 
> > > >     if has('iconv')
> > > >       Plugin 'mbbill/fencview'
> > > >     endif
> > > > 
> > > >     if has("python") || has("python3")
> > > >       Plugin 'SirVer/ultisnips'
> > > >     else
> > > >       Plugin 'MarcWeber/vim-addon-mw-utils'
> > > >       Plugin 'tomtom/tlib_vim'
> > > >       Plugin 'garbas/vim-snipmate'
> > > >     endif
> > > > 
> > > > This is esp. relevant on Fedora, where "vi" is a tiny build of vim (I
> > > > believe), and "vim" is a huge build of vim (I believe).
> > > 
> > > Why does every user need to take care of this?  The plugin should check
> > > for features and skip if it is missing something.
> > 
> > Because I may prefer using plugin A, but when plugin A does not work, I
> > may want to use plugin B instead.
> > 
> > A plugin cannot know my preferences about this.
> > 
> > Also, when A and B are alternatives that cannot both be loaded (one
> > excludes the other), as in Gary's 'netrw' example, the user may need to
> > choose between A and B, depending on Vim's version or features.
> 
> OK, but this should be an exception and not happen very often.  We
> should not say "it happens anyway so let's just let plugins mess about".
> In other words: plugins should be encourage to make it easier for the
> user.

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.

> > But I agree that plugins generally should refuse to load when they
> > cannot work with the version of Vim that tries to load them.
> > 
> > Still, an experienced user or a plugin packager should be able to make
> > better decisions than the plugin writer, because getting all the
> > conditions right under which a plugin works can be a challenge, and some
> > conditions may only become apparent in some specific setup.
> > 
> >     And when more conditions are figured out by users or plugin
> >     packagers, tried and tested, these could be forwarded to the plugin
> >     author.  (Unless they are very specific to some distribution or
> >     setup.)
> 
> Anything we can do to make it easier for plugin authors?  A big
> improvement would be if plugins would write tests that we can run
> against different Vim configurations.  I haven't seen plugins with tests
> though...
> 
> > It may be possible to specify plugin dependencies to Vim, so Vim can try
> > to load plugins in the order dictated by a dependency graph, so that if
> > plugin A does not load or is disabled, it attempts to load plugin B.
> > And some plugin C is only loaded when plugin B has been loaded.
> > 
> > To make this work, Vim also needs a reliable way to find out if a plugin
> > has loaded correctly.
> > 
> >     Maybe like this: when during plugin loading the plugin does *not*
> >     use 'finish', then Vim can consider the plugin loaded.
> > 
> >     But some plugins load in stages, e.g. the taglist.vim plugin, so
> >     they use 'finish' even when loading successfully...
> 
> This is for a large part the work of a plugin manager.  If you have
> dependencies you also need a way to download and install plugins
> (otherwise you can only give error messages).
> 
> And if plugin writers are so bad at bailing out when it won't work on
> the current Vim, you can't really expect them to define dependencies
> properly.
> 
> > > I would also prefer Plugins run by default, and have some way to disable
> > > them when you don't want them.  Most plugins should just be used, only
> > > in some cases would you want to skip one.
> > 
> > If we find a way to specify plugin disable/enable information and plugin
> > dependencies to Vim, and a way to check if a plugin loaded successfully,
> > then this and the issues above could all be covered.
> 
> I think you are describing a plugin manager.

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.

> > > > Also, the compatibility issues need to be addressed.
> > > > 
> > > > Some of them may go away simply by using a different subdirectory name,
> > > > such as "plugpacks" instead of "bundles".  Old plugins and plugin
> > > > managers can use the old and known subdirectories, together with the
> > > > known &runtimepath methods of including them.
> > > > 
> > > > It should be possible to find out if a plugin is compatible with the new
> > > > "plugpacks" subdirectory scheme.  This is the difficult part, because
> > > > how do we do this without requiring a flag in the plugin, which must be
> > > > present in all future versions as well?
> > > > 
> > > > It may not be so bad to have a flag, it can just become part of the
> > > > boilerplate template for a plugin.
> > > > 
> > > > But a standard boilerplate template for a vim plugin does not exist yet,
> > > > to my knowledge.  Google tried to make one, but AFAIK it did not catch
> > > > on.
> > > 
> > > I'm afraid that if we want to be backwards compatible with plugins that
> > > depend on the runtime file layout we might be stuck in a sub-optimal
> > > situation.  And every plugin manager finds its own way, making it even
> > > worse in the long run.
> > > 
> > > I think it's not too bad to support the proposed directory layout.  In
> > > most cases everything will just keep working.  In case some plugin needs
> > > to find other plugins, and can only find other plugins from
> > > 'runtimepath', then we can make that plugin add itself to 'runtimepath'.
> > > Worst case all of them, using globpath(&runtimepath, 'bundles/*').
> > > 
> > > I do think we need to get rid of adding lots of directories to
> > > 'runtimepath'.  It's supposed to be an option set by the user.  The only
> > > reason plugin managers use it is because there is no alternative.
> > 
> > Cleaning up &runtimepath is one thing I like about this idea.  The other
> > thing is that each plugin lives in its own little directory subtree, so
> > it is easy to install, remove and update.
> > 
> > Also, in many cases we can try to load a plugin to see if the plugin can
> > work with the current Vim.
> 
> Right, that's what I was trying to make possible.  Sort of a manual
> plugin management.
> 
> > Now if we also can specify plugin disable/enable information and plugin
> > dependencies (between each other and on Vim features) to Vim, and find
> > out when a plugin loaded successfully, and if all this can be done in a
> > simple and reliable way, this could be an alternative to &runtimepath.
> 
> I think that's too much for the Vim distribution to support.  Unless we
> include a plugin manager.

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.


-- 
Olaf Dabrunz (oda <at> fctrace.org)

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
For more options, visit https://groups.google.com/d/optout.

Raspunde prin e-mail lui