mirror of
https://github.com/neovim/neovim.git
synced 2024-09-17 20:58:20 -04:00
provider: decide status by g:loaded_xx_provider
This commit is contained in:
parent
2cfe4748e5
commit
66938b928c
@ -1,6 +1,16 @@
|
||||
" The clipboard provider uses shell commands to communicate with the clipboard.
|
||||
" The provider function will only be registered if a supported command is
|
||||
" available.
|
||||
|
||||
if exists('g:loaded_clipboard_provider')
|
||||
finish
|
||||
endif
|
||||
" Default to FALSE. Set by provider#clipboard#Executable() later.
|
||||
" To force a reload:
|
||||
" :unlet g:loaded_clipboard_provider
|
||||
" :runtime autoload/provider/clipboard.vim
|
||||
let g:loaded_clipboard_provider = 0
|
||||
|
||||
let s:copy = {}
|
||||
let s:paste = {}
|
||||
let s:clipboard = {}
|
||||
@ -48,9 +58,6 @@ endfunction
|
||||
let s:cache_enabled = 1
|
||||
let s:err = ''
|
||||
|
||||
" eval_has_provider checks the variable to verify provider status
|
||||
let g:provider#clipboard#enabled = 0
|
||||
|
||||
function! provider#clipboard#Error() abort
|
||||
return s:err
|
||||
endfunction
|
||||
@ -123,12 +130,6 @@ function! provider#clipboard#Executable() abort
|
||||
return ''
|
||||
endfunction
|
||||
|
||||
" Call this to setup/reload the provider
|
||||
function! provider#clipboard#Reload()
|
||||
" #enabled is used by eval_has_provider()
|
||||
let g:provider#clipboard#enabled = !empty(provider#clipboard#Executable())
|
||||
endfunction
|
||||
|
||||
function! s:clipboard.get(reg) abort
|
||||
if type(s:paste[a:reg]) == v:t_func
|
||||
return s:paste[a:reg]()
|
||||
@ -195,4 +196,5 @@ function! provider#clipboard#Call(method, args) abort
|
||||
endtry
|
||||
endfunction
|
||||
|
||||
call provider#clipboard#Reload()
|
||||
" eval_has_provider() decides based on this variable.
|
||||
let g:loaded_clipboard_provider = !empty(provider#clipboard#Executable())
|
||||
|
@ -1,8 +1,7 @@
|
||||
if exists('g:loaded_node_provider')
|
||||
finish
|
||||
endif
|
||||
let g:loaded_node_provider = 1
|
||||
let g:provider#node#enabled = 0
|
||||
let g:loaded_node_provider = 0
|
||||
|
||||
function! s:is_minimum_version(version, min_major, min_minor) abort
|
||||
if empty(a:version)
|
||||
@ -141,12 +140,10 @@ endfunction
|
||||
|
||||
let s:err = ''
|
||||
let s:prog = provider#node#Detect()
|
||||
let g:loaded_node_provider = !empty(s:prog)
|
||||
|
||||
if empty(s:prog)
|
||||
if !g:loaded_node_provider
|
||||
let s:err = 'Cannot find the "neovim" node package. Try :checkhealth'
|
||||
else
|
||||
let g:provider#node#enabled = 1
|
||||
endif
|
||||
|
||||
call remote#host#RegisterPlugin('node-provider', 'node', [])
|
||||
|
||||
|
@ -7,10 +7,8 @@
|
||||
if exists('g:loaded_python_provider')
|
||||
finish
|
||||
endif
|
||||
let g:loaded_python_provider = 1
|
||||
|
||||
let [s:prog, s:err] = provider#pythonx#Detect(2)
|
||||
let g:provider#python#enabled = !empty(s:prog)
|
||||
let g:loaded_python_provider = !empty(s:prog)
|
||||
|
||||
function! provider#python#Prog() abort
|
||||
return s:prog
|
||||
|
@ -7,10 +7,8 @@
|
||||
if exists('g:loaded_python3_provider')
|
||||
finish
|
||||
endif
|
||||
let g:loaded_python3_provider = 1
|
||||
|
||||
let [s:prog, s:err] = provider#pythonx#Detect(3)
|
||||
let g:provider#python3#enabled = !empty(s:prog)
|
||||
let g:loaded_python3_provider = !empty(s:prog)
|
||||
|
||||
function! provider#python3#Prog() abort
|
||||
return s:prog
|
||||
|
@ -2,12 +2,11 @@
|
||||
if exists('g:loaded_ruby_provider')
|
||||
finish
|
||||
endif
|
||||
let g:loaded_ruby_provider = 1
|
||||
let g:loaded_ruby_provider = 0
|
||||
|
||||
function! provider#ruby#Detect() abort
|
||||
return s:prog
|
||||
endfunction
|
||||
let g:provider#ruby#enabled = 0
|
||||
|
||||
function! provider#ruby#Prog() abort
|
||||
return s:prog
|
||||
@ -63,11 +62,10 @@ endfunction
|
||||
let s:err = ''
|
||||
let s:prog = s:detect()
|
||||
let s:plugin_path = expand('<sfile>:p:h') . '/script_host.rb'
|
||||
let g:loaded_ruby_provider = !empty(s:prog)
|
||||
|
||||
if empty(s:prog)
|
||||
if !g:loaded_ruby_provider
|
||||
let s:err = 'Cannot find the neovim RubyGem. Try :checkhealth'
|
||||
else
|
||||
let g:provider#ruby#enabled = 1
|
||||
endif
|
||||
|
||||
call remote#host#RegisterClone('legacy-ruby-provider', 'ruby')
|
||||
|
@ -84,12 +84,11 @@ Developer guidelines *dev-guidelines*
|
||||
|
||||
PROVIDERS *dev-provider*
|
||||
|
||||
A goal of Nvim is to allow extension of the editor without special knowledge
|
||||
in the core. But some Vim components are too tightly coupled; in those cases
|
||||
a "provider" hook is exposed.
|
||||
A primary goal of Nvim is to allow extension of the editor without special
|
||||
knowledge in the core. Some core functions are delegated to "providers"
|
||||
implemented as external scripts.
|
||||
|
||||
Consider two examples of integration with external systems that are
|
||||
implemented in Vim and are now decoupled from Nvim core as providers:
|
||||
Examples:
|
||||
|
||||
1. In the Vim source code, clipboard logic accounts for more than 1k lines of
|
||||
C source code (ui.c), to perform two tasks that are now accomplished with
|
||||
@ -101,29 +100,28 @@ implemented in Vim and are now decoupled from Nvim core as providers:
|
||||
scripting is performed by an external host process implemented in ~2k lines
|
||||
of Python.
|
||||
|
||||
Ideally we could implement Python and clipboard integration in pure vimscript
|
||||
and without touching the C code. But this is infeasible without compromising
|
||||
backwards compatibility with Vim; that's where providers help.
|
||||
The provider framework invokes VimL from C. It is composed of two functions
|
||||
in eval.c:
|
||||
|
||||
The provider framework helps call vimscript from C. It is composed of two
|
||||
functions in eval.c:
|
||||
|
||||
- eval_call_provider(name, method, arguments): calls provider#(name)#Call
|
||||
- eval_call_provider(name, method, arguments): calls provider#{name}#Call
|
||||
with the method and arguments.
|
||||
- eval_has_provider(name): Checks if a provider is implemented. Returns true
|
||||
if the provider#(name)#enabled variable is not 0. Called by |has()|
|
||||
(vimscript) to check if features are available.
|
||||
|
||||
The provider#(name)#Call function implements integration with an external
|
||||
system, because shell commands and |RPC| clients are easier to work with in
|
||||
vimscript.
|
||||
- eval_has_provider(name): Checks the `g:loaded_{name}_provider` variable
|
||||
which must be set by the provider script to indicate whether it is enabled
|
||||
and working. Called by |has()| to check if features are available.
|
||||
|
||||
For example, the Python provider is implemented by the
|
||||
autoload/provider/python.vim script; the variable provider#python#enabled is only
|
||||
1 if a valid external Python host is found. That works well with the
|
||||
`has('python')` expression (normally used by Python plugins) because if the
|
||||
Python host isn't installed then the plugin will "think" it is running in
|
||||
a Vim compiled without the "+python" feature.
|
||||
"autoload/provider/python.vim" script, which sets `g:loaded_python_provider`
|
||||
to TRUE only if a valid external Python host is found. Then `has("python")`
|
||||
reflects whether Python support is working.
|
||||
|
||||
*provider-reload*
|
||||
Sometimes a GUI or other application may want to force a provider to
|
||||
"reload". To reload a provider, undefine its "loaded" flag, then use
|
||||
|:runtime| to reload it: >
|
||||
|
||||
:unlet g:loaded_clipboard_provider
|
||||
:runtime autoload/provider/clipboard.vim
|
||||
|
||||
|
||||
DOCUMENTATION *dev-doc*
|
||||
|
||||
|
@ -23968,27 +23968,35 @@ typval_T eval_call_provider(char *provider, char *method, list_T *arguments)
|
||||
return rettv;
|
||||
}
|
||||
|
||||
/// Check if a named provider is enabled
|
||||
/// Checks if a named provider is enabled.
|
||||
bool eval_has_provider(const char *provider)
|
||||
{
|
||||
char enabled_varname[256];
|
||||
int enabled_varname_len = snprintf(enabled_varname, sizeof(enabled_varname),
|
||||
"provider#%s#enabled", provider);
|
||||
|
||||
char buf[256];
|
||||
int len;
|
||||
typval_T tv;
|
||||
if (get_var_tv(enabled_varname, enabled_varname_len, &tv,
|
||||
NULL, false, false) == FAIL) {
|
||||
char call_varname[256];
|
||||
snprintf(call_varname, sizeof(call_varname), "provider#%s#Call", provider);
|
||||
int has_call = !!find_func((char_u *)call_varname);
|
||||
|
||||
if (has_call && p_lpl) {
|
||||
emsgf("Provider '%s' failed to set %s", provider, enabled_varname);
|
||||
// Get the g:loaded_xx_provider variable.
|
||||
len = snprintf(buf, sizeof(buf), "g:loaded_%s_provider", provider);
|
||||
if (get_var_tv(buf, len, &tv, NULL, false, true) == FAIL) {
|
||||
// Trigger autoload once.
|
||||
len = snprintf(buf, sizeof(buf), "provider#%s#bogus", provider);
|
||||
script_autoload(buf, len, false);
|
||||
|
||||
// Retry the (non-autoload-style) variable.
|
||||
len = snprintf(buf, sizeof(buf), "g:loaded_%s_provider", provider);
|
||||
if (get_var_tv(buf, len, &tv, NULL, false, true) == FAIL) {
|
||||
// Show a hint if Call() is defined but g:loaded_xx_provider is missing.
|
||||
snprintf(buf, sizeof(buf), "provider#%s#Call", provider);
|
||||
bool has_call = !!find_func((char_u *)buf);
|
||||
if (has_call && p_lpl) {
|
||||
emsgf("provider: %s: missing required variable g:loaded_%s_provider",
|
||||
provider, provider);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return (tv.v_type == VAR_NUMBER) ? tv.vval.v_number != 0: true;
|
||||
return (tv.v_type == VAR_NUMBER) ? !!tv.vval.v_number : false;
|
||||
}
|
||||
|
||||
/// Writes "<sourcing_name>:<sourcing_lnum>" to `buf[bufsize]`.
|
||||
|
@ -1,2 +1,2 @@
|
||||
" A dummy test provider
|
||||
let g:provider#brokencall#enabled = 1
|
||||
let g:loaded_brokencall_provider = 1
|
||||
|
@ -1,5 +1,5 @@
|
||||
" Dummy test provider, missing
|
||||
" let g:provider#brokenenabled#enabled = 0
|
||||
" Dummy test provider, missing this required variable:
|
||||
" let g:loaded_brokenenabled_provider = 0
|
||||
|
||||
function! provider#brokenenabled#Call(method, args)
|
||||
return 42
|
||||
|
@ -1,3 +1,5 @@
|
||||
let g:loaded_clipboard_provider = 1
|
||||
|
||||
let g:test_clip = { '+': [''], '*': [''], }
|
||||
|
||||
let s:methods = {}
|
||||
@ -35,8 +37,6 @@ function! s:methods.set(lines, regtype, reg)
|
||||
let g:test_clip[a:reg] = [a:lines, a:regtype]
|
||||
endfunction
|
||||
|
||||
let provider#clipboard#enabled = 1
|
||||
|
||||
function! provider#clipboard#Call(method, args)
|
||||
return call(s:methods[a:method],a:args,s:methods)
|
||||
endfunction
|
||||
|
@ -1,19 +1,21 @@
|
||||
|
||||
local helpers = require('test.functional.helpers')(after_each)
|
||||
local clear, eq, feed_command, eval = helpers.clear, helpers.eq, helpers.feed_command, helpers.eval
|
||||
local clear, eq, eval = helpers.clear, helpers.eq, helpers.eval
|
||||
local command = helpers.command
|
||||
local expect_err = helpers.expect_err
|
||||
|
||||
describe('Providers', function()
|
||||
describe('providers', function()
|
||||
before_each(function()
|
||||
clear('--cmd', 'let &rtp = "test/functional/fixtures,".&rtp')
|
||||
end)
|
||||
|
||||
it('must set the enabled variable or fail', function()
|
||||
eq(42, eval("provider#brokenenabled#Call('dosomething', [])"))
|
||||
feed_command("call has('brokenenabled')")
|
||||
eq(0, eval("has('brokenenabled')"))
|
||||
it('must define g:loaded_xx_provider', function()
|
||||
command('set loadplugins')
|
||||
expect_err('Vim:provider: brokenenabled: missing required variable g:loaded_brokenenabled_provider',
|
||||
eval, "has('brokenenabled')")
|
||||
end)
|
||||
|
||||
it('without Call() are enabled', function()
|
||||
it('without Call() but with g:loaded_xx_provider', function()
|
||||
eq(1, eval("has('brokencall')"))
|
||||
end)
|
||||
end)
|
||||
|
Loading…
Reference in New Issue
Block a user