Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 58 additions & 4 deletions autoload/maktaba/plugin.vim
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,28 @@ function! s:FileHandleToFlagHandle(handle) abort
endfunction


" Ensures {plugin}'s plugin/flags.vim file has been sourced (if it has one and
" has not already had an instant/flags.vim file sourced).
function! s:EnsureFlagsLoaded(plugin) abort
if a:plugin._loaded_flags
return
endif
try
call a:plugin.Load('flags')
" If file was loaded and called maktaba#plugin#Enter, it should now have
" PLUGIN._loaded_flags set as a side effect.
if !a:plugin._loaded_flags
Comment thread
dbarnett marked this conversation as resolved.
call a:plugin.logger.Warn(
\ 'plugin/flags.vim seems to be missing maktaba#plugin#Enter call')
endif
catch /ERROR(NotFound):/
" Plugin has no plugin/flags.vim file. That's totally fine if it only uses
" built-in flags like plugin[commands].
endtry
let a:plugin._loaded_flags = 1
endfunction


""
" @private
" Used by maktaba#library to help throw good error messages about non-library
Expand Down Expand Up @@ -225,6 +247,10 @@ function! maktaba#plugin#Enter(file) abort
let [l:plugindir, l:filedir, l:handle] = s:SplitEnteredFile(a:file)
let l:plugin = maktaba#plugin#GetOrInstall(l:plugindir)
let l:controller = l:plugin._entered[l:filedir]
" Check for special path plugin/flags.vim or instant/flags.vim (which gets a
" handle 'flags^' in s:SplitEnteredFile's handle syntax).
let l:is_flags_file = (l:filedir ==# 'plugin' || l:filedir ==# 'instant')
\ && l:handle ==# 'flags^'
Comment thread
dbarnett marked this conversation as resolved.

if l:filedir ==# 'ftplugin'
call extend(l:controller, {l:handle : []}, 'keep')
Expand All @@ -240,7 +266,12 @@ function! maktaba#plugin#Enter(file) abort
return [l:plugin, 0]
endif

if l:filedir !=# 'autoload'
" Only load this full file if its corresponding flag is enabled.
" For example, plugin/autocmds.vim should exit early if the user configured
" !plugin[autocmds] in their flags.
" This override mechanism has no effect for autoload files (which should be
" reloadable) or for flags files (which should never need to be disabled).
if l:filedir !=# 'autoload' && !l:is_flags_file
Comment thread
dbarnett marked this conversation as resolved.
" Check the 'plugin' or 'instant' flag dictionaries for word on the this
" file, using the defaults specified in the s:defaultoff variable.
let l:flag = l:plugin.Flag(l:filedir)
Expand All @@ -255,6 +286,9 @@ function! maktaba#plugin#Enter(file) abort
" Note that a file isn't entered when it is sourced: it is entered when this
" function OKs an enter. (In other words, don't move this line up.)
call add(l:controller, l:handle)
if l:is_flags_file
let l:plugin._loaded_flags = 1
endif
return [l:plugin, 1]
endfunction

Expand Down Expand Up @@ -460,6 +494,7 @@ function! s:CreatePluginObject(name, location, settings) abort
\ 'Source': function('maktaba#plugin#Source'),
\ 'Load': function('maktaba#plugin#Load'),
\ 'AddonInfo': function('maktaba#plugin#AddonInfo'),
\ 'AllFlags': function('maktaba#plugin#AllFlags'),
\ 'Flag': function('maktaba#plugin#Flag'),
\ 'HasFlag': function('maktaba#plugin#HasFlag'),
\ 'HasDir': function('maktaba#plugin#HasDir'),
Expand All @@ -469,6 +504,7 @@ function! s:CreatePluginObject(name, location, settings) abort
\ 'IsLibrary': function('maktaba#plugin#IsLibrary'),
\ 'GetExtensionRegistry': function('maktaba#plugin#GetExtensionRegistry'),
\ '_entered': l:entrycontroller,
\ '_loaded_flags': 0,
\ }
" If plugin has an addon-info.json file with a "name" declared, overwrite the
" default name with the custom one.
Expand Down Expand Up @@ -506,10 +542,10 @@ function! s:CreatePluginObject(name, location, settings) abort

" These special flags let the user control the loading of parts of the plugin.
if isdirectory(maktaba#path#Join([l:plugin.location, 'plugin']))
call l:plugin.Flag('plugin', {})
let l:plugin.flags.plugin = maktaba#flags#Create('plugin', {})
endif
if isdirectory(maktaba#path#Join([l:plugin.location, 'instant']))
call l:plugin.Flag('instant', {})
let l:plugin.flags.instant = maktaba#flags#Create('instant', {})
endif

" Load flags file first.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually I noticed this ApplySettings case probably also needs to EnsureFlagsLoaded first. I'm going to try to get some vroom coverage to repro an issue there along with adding the call to fix it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alright, pushed 0382ab2 to add the missing corner case handling (in Setting.Apply) and test coverage.

Expand Down Expand Up @@ -747,6 +783,17 @@ function! maktaba#plugin#AddonInfo() dict abort
endfunction



""
" @dict Plugin
" Returns a dictionary of @dict(Flag) handles defined on the plugin, with flag
" names as keys.
function! maktaba#plugin#AllFlags() dict abort
call s:EnsureFlagsLoaded(self)
return self.flags
endfunction


""
" @usage {flag}
" @dict Plugin
Expand All @@ -757,14 +804,16 @@ endfunction
" maktaba#plugin#Get('myplugin').Flag('foo')
" maktaba#plugin#Get('myplugin').flags.foo.Get()
" <
" except that the second version will not trigger flag loading if they haven't
" been loaded already.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If flags haven't been loaded yet, does that mean that flags.foo.Get() will throw some error (which one?) if the foo flag hasn't been set explicitly by the user (but wouldn't if it has?).

What's the right pattern for a library to use if it wants to access flags before flag loading? (Presumably this should also be an uncommon case outside of Maktaba proper(?); should we say that?).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, I want to go a little further to discourage accessing .flags, possibly rename it to ._flags and provide a .Flags() lookup method instead, but I don't want to get too verbose with usage contracts just yet in all the files I'd need to touch for that. The helpers ought to fully encapsulate having the flags resolved before letting you Set/Get them and I can't think of any reason to support poking at them partially initialized.

"
" You may access a portion of a flag (a specific value in a dict flag, or
" a specific item in a list flag) using a fairly natural square bracket
" syntax: >
" maktaba#plugin#Get('myplugin').Flag('plugin[autocmds]')
" <
" This is equivalent to: >
" maktaba#plugin#Get('myplugin').flags.plugin.Get()['autocmds']
" maktaba#plugin#Get('myplugin').Flag('plugin')['autocmds']
" <
" This syntax can be chained: >
" maktaba#plugin#Get('myplugin').Flag('complex[key][0]')
Expand All @@ -785,13 +834,16 @@ endfunction
" maktaba#plugin#Get('myplugin').Flag('foo', 'bar')
" maktaba#plugin#Get('myplugin').flags.foo.Set('bar')
" <
" except that the second version will not trigger flag loading if they haven't
" been loaded already.
"
" Also supports dict flag syntax: >
" maktaba#plugin#Get('myplugin').Flag('plugin[autocmds]', 1)
" <
"
" @throws BadValue if {flag} is an invalid flag name.
function! maktaba#plugin#Flag(flag, ...) dict abort
call s:EnsureFlagsLoaded(self)
let [l:flag, l:foci] = maktaba#setting#Handle(a:flag)
if !has_key(self.flags, l:flag)
if a:0 == 0 || !empty(l:foci)
Expand All @@ -808,8 +860,10 @@ endfunction


""
" @dict Plugin
" Whether or not the plugin has a flag named {flag}.
function! maktaba#plugin#HasFlag(flag) dict abort
call s:EnsureFlagsLoaded(self)
return has_key(self.flags, a:flag)
endfunction

Expand Down
5 changes: 3 additions & 2 deletions autoload/maktaba/setting.vim
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,9 @@ endfunction
" Gets the flag in {plugin} relevant to the setting.
" @throws NotFound if {plugin} does not define the appropriate flag.
function! maktaba#setting#GetFlag(plugin) dict abort
if has_key(a:plugin.flags, self._flagname)
return a:plugin.flags[self._flagname]
let l:plugin_flags = a:plugin.AllFlags()
if has_key(l:plugin_flags, self._flagname)
return l:plugin_flags[self._flagname]
endif
let l:msg = 'Flag "%s" not defined in %s.'
throw maktaba#error#NotFound(l:msg, self._flagname, a:plugin.name)
Expand Down
16 changes: 12 additions & 4 deletions doc/maktaba.txt
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,10 @@ Plugin.AddonInfo() *Plugin.AddonInfo()*
Otherwise, returns an empty dict.
Throws ERROR(BadValue) if addon-info.json isn't valid JSON.

Plugin.AllFlags() *Plugin.AllFlags()*
Returns a dictionary of |maktaba.Flag| handles defined on the plugin, with
flag names as keys.

Plugin.Flag({flag}) *Plugin.Flag()*
Returns the value of {flag}. See |maktaba#setting#Handle()| for {flag}
syntax.
Expand All @@ -301,6 +305,8 @@ Plugin.Flag({flag}) *Plugin.Flag()*
maktaba#plugin#Get('myplugin').Flag('foo')
maktaba#plugin#Get('myplugin').flags.foo.Get()
<
except that the second version will not trigger flag loading if they haven't
been loaded already.

You may access a portion of a flag (a specific value in a dict flag, or a
specific item in a list flag) using a fairly natural square bracket syntax:
Expand All @@ -309,7 +315,7 @@ Plugin.Flag({flag}) *Plugin.Flag()*
<
This is equivalent to:
>
maktaba#plugin#Get('myplugin').flags.plugin.Get()['autocmds']
maktaba#plugin#Get('myplugin').Flag('plugin')['autocmds']
<
This syntax can be chained:
>
Expand All @@ -330,6 +336,8 @@ Plugin.Flag({flag}, {value})
maktaba#plugin#Get('myplugin').Flag('foo', 'bar')
maktaba#plugin#Get('myplugin').flags.foo.Set('bar')
<
except that the second version will not trigger flag loading if they haven't
been loaded already.

Also supports dict flag syntax:
>
Expand All @@ -338,6 +346,9 @@ Plugin.Flag({flag}, {value})

Throws ERROR(BadValue) if {flag} is an invalid flag name.

Plugin.HasFlag({flag}) *Plugin.HasFlag()*
Whether or not the plugin has a flag named {flag}.

Plugin.MapPrefix({letter}, [throw]) *Plugin.MapPrefix()*
Returns the user's desired map prefix for the plugin. If the user has not
specified a map prefix, <leader>{letter} will be returned.
Expand Down Expand Up @@ -1550,9 +1561,6 @@ maktaba#plugin#Source({file}, [optional]) *maktaba#plugin#Source()*
Throws ERROR(NotAuthorized) if {file} cannot be read.
Throws ERROR(NotFound) if {file} does not describe a plugin file.

maktaba#plugin#HasFlag({flag}) *maktaba#plugin#HasFlag()*
Whether or not the plugin has a flag named {flag}.

maktaba#python#ImportModule({plugin}, {name}) *maktaba#python#ImportModule()*
Imports python module {name} into vim from {plugin}. Checks for {name} in
the plugin's python/ subdirectory for the named module.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@ if !s:enter
finish
endif

echomsg 'The flags file is sourced immediately.'
echomsg 'The flags file is sourced on first flag access if not already loaded.'

call s:plugin.Flag('plugin[optin]', 0)
6 changes: 6 additions & 0 deletions vroom/fakeplugins/myplugin/plugin/flags.vim
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
let [s:plugin, s:enter] = maktaba#plugin#Enter(expand('<sfile>', ':p'))
if !s:enter
finish
endif

echomsg 'Flags file sourced.'
1 change: 1 addition & 0 deletions vroom/mappings.vroom
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ instance, if you try to load the 'mappings' file, nothing happens:

@messages (STRICT)
:call g:plugin.Load('mappings')
~ The flags file is sourced on first flag access if not already loaded.
@messages

Unless you have EXPLICITLY enabled mappings:
Expand Down
34 changes: 12 additions & 22 deletions vroom/plugin.vroom
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,12 @@ ftplugin.vroom). Roughly speaking:
- syntax/ files are used to highlight new filetypes
- indent/ files are used to define indent rules for new filetypes

The interesting stuff usually happens in the instant/ and plugin/ directories.
- plugin/ files are sourced by vim after the vimrc file finishes
- instant/ files are sourced by maktaba IMMEDIATELY upon installation.
The interesting stuff usually happens in the plugin/ directory. plugin/ files
are sourced by vim after the vimrc file finishes.
In a standard maktaba plugin, they will look something like this:

| instant/
| flags.vim
| plugin/
| flags.vim
| settings.vim
| commands.vim
| autocmds.vim
Expand Down Expand Up @@ -80,12 +78,12 @@ sourced automatically EXCEPT files named mappings.vim. Maktaba policy is that
users must opt-in to key mappings. (If you use custom mapping files, you should
disable them by default as explained below).

You'll notice that some of these files go in the instant/ directory and others
go in the plugin/ directory. As a rule of thumb, only place files in instant/
if you ABSOLUTELY MUST. instant/ files are loaded IMMEDIATELY when the plugin is
installed. This is usually unnecessary. Every file in plugin/ will be sourced by
vim in the usual load order. You need only use instant/ files when you need work
to be done BEFORE the .vimrc file exists.
Maktaba also currently supports an instant/ directory with files that are
sourced by maktaba IMMEDIATELY upon installation. As a rule of thumb, only place
files in instant/ if you ABSOLUTELY MUST. instant/ files are loaded IMMEDIATELY
when the plugin is installed. This is usually unnecessary. Every file in plugin/
will be sourced by vim in the usual load order. You need only use instant/ files
when you need work to be done BEFORE the .vimrc file exists.

For a vast majority of plugins, the only thing you need to do at install time is
define your plugin flags. These are what users use to configure your plugin
Expand All @@ -99,12 +97,7 @@ Note the order of events:

The time to configure a plugin is BETWEEN installation and loading.

Notice: ANYTHING THAT YOU PUT IN THE instant/ DIRECTORY CANNOT BE CONFIGURED.

There are some important uses for instantly loaded files, but you should use
this feature with caution.

Almost every plugin will want an instant/flags.vim file: it's the place where
Almost every plugin will want a plugin/flags.vim file: it's the place where
you define flags used to configure your plugin. This allows you to avoid subtle
issues when users attempt to update complicated configurations that do not
become defined until load time. If you make use of the maktaba flags framework,
Expand Down Expand Up @@ -142,15 +135,11 @@ Now, we install the plugin by giving maktaba the full plugin path:

@messages (STRICT)
:let g:plugin = maktaba#plugin#Install(g:pluginpath)
~ Flags file sourced.
@messages

This should generally be done by a plugin manager.

A few things have happened. First of all, notice that the flags file was sourced
immediately. (Take a look at fakeplugins/myplugin/plugin/flags.vim to see where
the message came from.) Secondly and most importantly, our plugin is now on the
runtimepath and registered with maktaba:
Our plugin is now on the runtimepath and registered with maktaba:

:call maktaba#ensure#IsTrue(has_key(maktaba#rtp#LeafDirs(), 'myplugin'))
:call maktaba#ensure#IsTrue(maktaba#plugin#IsRegistered('myplugin'))
Expand All @@ -177,6 +166,7 @@ startup. However, if your plugin was installed after startup then it won't be
loaded automatically. You can load it with the Load function:

:call g:plugin.Load()
~ Flags file sourced.
~ I shall have things my way!

This will source all of the relevant plugin files.
Expand Down
5 changes: 4 additions & 1 deletion vroom/plugincontrol.vroom
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,10 @@ There are several other types of plugin files besides those in plugin/. Maktaba
provides a concept of instant/ files, which will be activated as soon as
possible after the plugin is activated.

:let g:instant_a = PluginPath(['instant', 'a.vim'])
:let g:legacyflagspath = maktaba#path#Join([g:repo, 'legacyflagsplugin'])
:let g:legacyflagsplugin = maktaba#plugin#Install(g:legacyflagspath)
:let g:instant_a = maktaba#path#Join([
|g:legacyflagsplugin.location, 'instant', 'a.vim'])
:call maktaba#ensure#IsTrue(maktaba#plugin#Enter(g:instant_a)[1])
:call maktaba#ensure#IsFalse(maktaba#plugin#Enter(g:instant_a)[1])

Expand Down
Loading