mirror of
https://github.com/neovim/neovim.git
synced 2025-09-08 04:18:18 +00:00
provider: let providers decide their status
Instead of deciding provider status in eval_has_provider, move the decision to the provider Vim scripts. Previously, provider loading worked as follows: 1. eval_has_provider() verified provider availability by searching for the provider#providername#Call function and cached this verificaion as a static variable for some providers 2. providers short-circuited on loading to prevent the definition of the Call function (with the exception of the node provider that did not) This commit changes the expected interface between nvim and its providers to facilitate provider reloading, by splitting the verification of the provider from the availability of the Call function. eval_has_provider() now checks for a provider#providername#enabled variable. It is up to the provider script to set this to 0 or 1 accordingly. eval_call_provider() remains unchanged. All providers hosting a Call function were updated to respect this. The clipboard provider now has a Reload function to reload the provider.
This commit is contained in:

committed by
Justin M. Keyes

parent
2860453c4f
commit
2cfe4748e5
@@ -48,6 +48,9 @@ endfunction
|
|||||||
let s:cache_enabled = 1
|
let s:cache_enabled = 1
|
||||||
let s:err = ''
|
let s:err = ''
|
||||||
|
|
||||||
|
" eval_has_provider checks the variable to verify provider status
|
||||||
|
let g:provider#clipboard#enabled = 0
|
||||||
|
|
||||||
function! provider#clipboard#Error() abort
|
function! provider#clipboard#Error() abort
|
||||||
return s:err
|
return s:err
|
||||||
endfunction
|
endfunction
|
||||||
@@ -120,12 +123,11 @@ function! provider#clipboard#Executable() abort
|
|||||||
return ''
|
return ''
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
if empty(provider#clipboard#Executable())
|
" Call this to setup/reload the provider
|
||||||
" provider#clipboard#Call() *must not* be defined if the provider is broken.
|
function! provider#clipboard#Reload()
|
||||||
" Otherwise eval_has_provider() thinks the clipboard provider is
|
" #enabled is used by eval_has_provider()
|
||||||
" functioning, and eval_call_provider() will happily call it.
|
let g:provider#clipboard#enabled = !empty(provider#clipboard#Executable())
|
||||||
finish
|
endfunction
|
||||||
endif
|
|
||||||
|
|
||||||
function! s:clipboard.get(reg) abort
|
function! s:clipboard.get(reg) abort
|
||||||
if type(s:paste[a:reg]) == v:t_func
|
if type(s:paste[a:reg]) == v:t_func
|
||||||
@@ -192,3 +194,5 @@ function! provider#clipboard#Call(method, args) abort
|
|||||||
let s:here = v:false
|
let s:here = v:false
|
||||||
endtry
|
endtry
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
call provider#clipboard#Reload()
|
||||||
|
@@ -2,6 +2,7 @@ if exists('g:loaded_node_provider')
|
|||||||
finish
|
finish
|
||||||
endif
|
endif
|
||||||
let g:loaded_node_provider = 1
|
let g:loaded_node_provider = 1
|
||||||
|
let g:provider#node#enabled = 0
|
||||||
|
|
||||||
function! s:is_minimum_version(version, min_major, min_minor) abort
|
function! s:is_minimum_version(version, min_major, min_minor) abort
|
||||||
if empty(a:version)
|
if empty(a:version)
|
||||||
@@ -143,6 +144,9 @@ let s:prog = provider#node#Detect()
|
|||||||
|
|
||||||
if empty(s:prog)
|
if empty(s:prog)
|
||||||
let s:err = 'Cannot find the "neovim" node package. Try :checkhealth'
|
let s:err = 'Cannot find the "neovim" node package. Try :checkhealth'
|
||||||
|
else
|
||||||
|
let g:provider#node#enabled = 1
|
||||||
endif
|
endif
|
||||||
|
|
||||||
call remote#host#RegisterPlugin('node-provider', 'node', [])
|
call remote#host#RegisterPlugin('node-provider', 'node', [])
|
||||||
|
|
||||||
|
@@ -10,6 +10,7 @@ endif
|
|||||||
let g:loaded_python_provider = 1
|
let g:loaded_python_provider = 1
|
||||||
|
|
||||||
let [s:prog, s:err] = provider#pythonx#Detect(2)
|
let [s:prog, s:err] = provider#pythonx#Detect(2)
|
||||||
|
let g:provider#python#enabled = !empty(s:prog)
|
||||||
|
|
||||||
function! provider#python#Prog() abort
|
function! provider#python#Prog() abort
|
||||||
return s:prog
|
return s:prog
|
||||||
@@ -19,11 +20,6 @@ function! provider#python#Error() abort
|
|||||||
return s:err
|
return s:err
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
if s:prog == ''
|
|
||||||
" Detection failed
|
|
||||||
finish
|
|
||||||
endif
|
|
||||||
|
|
||||||
" The Python provider plugin will run in a separate instance of the Python
|
" The Python provider plugin will run in a separate instance of the Python
|
||||||
" host.
|
" host.
|
||||||
call remote#host#RegisterClone('legacy-python-provider', 'python')
|
call remote#host#RegisterClone('legacy-python-provider', 'python')
|
||||||
|
@@ -10,6 +10,7 @@ endif
|
|||||||
let g:loaded_python3_provider = 1
|
let g:loaded_python3_provider = 1
|
||||||
|
|
||||||
let [s:prog, s:err] = provider#pythonx#Detect(3)
|
let [s:prog, s:err] = provider#pythonx#Detect(3)
|
||||||
|
let g:provider#python3#enabled = !empty(s:prog)
|
||||||
|
|
||||||
function! provider#python3#Prog() abort
|
function! provider#python3#Prog() abort
|
||||||
return s:prog
|
return s:prog
|
||||||
@@ -19,11 +20,6 @@ function! provider#python3#Error() abort
|
|||||||
return s:err
|
return s:err
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
if s:prog == ''
|
|
||||||
" Detection failed
|
|
||||||
finish
|
|
||||||
endif
|
|
||||||
|
|
||||||
" The Python3 provider plugin will run in a separate instance of the Python3
|
" The Python3 provider plugin will run in a separate instance of the Python3
|
||||||
" host.
|
" host.
|
||||||
call remote#host#RegisterClone('legacy-python3-provider', 'python3')
|
call remote#host#RegisterClone('legacy-python3-provider', 'python3')
|
||||||
|
@@ -7,6 +7,7 @@ let g:loaded_ruby_provider = 1
|
|||||||
function! provider#ruby#Detect() abort
|
function! provider#ruby#Detect() abort
|
||||||
return s:prog
|
return s:prog
|
||||||
endfunction
|
endfunction
|
||||||
|
let g:provider#ruby#enabled = 0
|
||||||
|
|
||||||
function! provider#ruby#Prog() abort
|
function! provider#ruby#Prog() abort
|
||||||
return s:prog
|
return s:prog
|
||||||
@@ -65,6 +66,8 @@ let s:plugin_path = expand('<sfile>:p:h') . '/script_host.rb'
|
|||||||
|
|
||||||
if empty(s:prog)
|
if empty(s:prog)
|
||||||
let s:err = 'Cannot find the neovim RubyGem. Try :checkhealth'
|
let s:err = 'Cannot find the neovim RubyGem. Try :checkhealth'
|
||||||
|
else
|
||||||
|
let g:provider#ruby#enabled = 1
|
||||||
endif
|
endif
|
||||||
|
|
||||||
call remote#host#RegisterClone('legacy-ruby-provider', 'ruby')
|
call remote#host#RegisterClone('legacy-ruby-provider', 'ruby')
|
||||||
|
@@ -111,7 +111,7 @@ 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.
|
with the method and arguments.
|
||||||
- eval_has_provider(name): Checks if a provider is implemented. Returns true
|
- eval_has_provider(name): Checks if a provider is implemented. Returns true
|
||||||
if the provider#(name)#Call function is implemented. Called by |has()|
|
if the provider#(name)#enabled variable is not 0. Called by |has()|
|
||||||
(vimscript) to check if features are available.
|
(vimscript) to check if features are available.
|
||||||
|
|
||||||
The provider#(name)#Call function implements integration with an external
|
The provider#(name)#Call function implements integration with an external
|
||||||
@@ -119,8 +119,8 @@ system, because shell commands and |RPC| clients are easier to work with in
|
|||||||
vimscript.
|
vimscript.
|
||||||
|
|
||||||
For example, the Python provider is implemented by the
|
For example, the Python provider is implemented by the
|
||||||
autoload/provider/python.vim script; the provider#python#Call function is only
|
autoload/provider/python.vim script; the variable provider#python#enabled is only
|
||||||
defined if a valid external Python host is found. That works well with the
|
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
|
`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
|
Python host isn't installed then the plugin will "think" it is running in
|
||||||
a Vim compiled without the "+python" feature.
|
a Vim compiled without the "+python" feature.
|
||||||
|
@@ -23968,52 +23968,27 @@ typval_T eval_call_provider(char *provider, char *method, list_T *arguments)
|
|||||||
return rettv;
|
return rettv;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool eval_has_provider(const char *name)
|
/// Check if a named provider is enabled
|
||||||
|
bool eval_has_provider(const char *provider)
|
||||||
{
|
{
|
||||||
#define CHECK_PROVIDER(name) \
|
char enabled_varname[256];
|
||||||
if (has_##name == -1) { \
|
int enabled_varname_len = snprintf(enabled_varname, sizeof(enabled_varname),
|
||||||
has_##name = !!find_func((char_u *)"provider#" #name "#Call"); \
|
"provider#%s#enabled", provider);
|
||||||
if (!has_##name) { \
|
|
||||||
script_autoload("provider#" #name "#Call", \
|
|
||||||
sizeof("provider#" #name "#Call") - 1, \
|
|
||||||
false); \
|
|
||||||
has_##name = !!find_func((char_u *)"provider#" #name "#Call"); \
|
|
||||||
} \
|
|
||||||
}
|
|
||||||
|
|
||||||
static int has_clipboard = -1;
|
typval_T tv;
|
||||||
static int has_python = -1;
|
if (get_var_tv(enabled_varname, enabled_varname_len, &tv,
|
||||||
static int has_python3 = -1;
|
NULL, false, false) == FAIL) {
|
||||||
static int has_ruby = -1;
|
char call_varname[256];
|
||||||
typval_T args[1];
|
snprintf(call_varname, sizeof(call_varname), "provider#%s#Call", provider);
|
||||||
args[0].v_type = VAR_UNKNOWN;
|
int has_call = !!find_func((char_u *)call_varname);
|
||||||
|
|
||||||
if (strequal(name, "clipboard")) {
|
if (has_call && p_lpl) {
|
||||||
CHECK_PROVIDER(clipboard);
|
emsgf("Provider '%s' failed to set %s", provider, enabled_varname);
|
||||||
return has_clipboard;
|
|
||||||
} else if (strequal(name, "python3")) {
|
|
||||||
CHECK_PROVIDER(python3);
|
|
||||||
return has_python3;
|
|
||||||
} else if (strequal(name, "python")) {
|
|
||||||
CHECK_PROVIDER(python);
|
|
||||||
return has_python;
|
|
||||||
} else if (strequal(name, "ruby")) {
|
|
||||||
bool need_check_ruby = (has_ruby == -1);
|
|
||||||
CHECK_PROVIDER(ruby);
|
|
||||||
if (need_check_ruby && has_ruby == 1) {
|
|
||||||
char *rubyhost = call_func_retstr("provider#ruby#Detect", 0, args, true);
|
|
||||||
if (rubyhost) {
|
|
||||||
if (*rubyhost == NUL) {
|
|
||||||
// Invalid rubyhost executable. Gem is probably not installed.
|
|
||||||
has_ruby = 0;
|
|
||||||
}
|
|
||||||
xfree(rubyhost);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return has_ruby;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return (tv.v_type == VAR_NUMBER) ? tv.vval.v_number != 0: true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Writes "<sourcing_name>:<sourcing_lnum>" to `buf[bufsize]`.
|
/// Writes "<sourcing_name>:<sourcing_lnum>" to `buf[bufsize]`.
|
||||||
|
@@ -0,0 +1,2 @@
|
|||||||
|
" A dummy test provider
|
||||||
|
let g:provider#brokencall#enabled = 1
|
@@ -0,0 +1,6 @@
|
|||||||
|
" Dummy test provider, missing
|
||||||
|
" let g:provider#brokenenabled#enabled = 0
|
||||||
|
|
||||||
|
function! provider#brokenenabled#Call(method, args)
|
||||||
|
return 42
|
||||||
|
endfunction
|
@@ -35,6 +35,7 @@ function! s:methods.set(lines, regtype, reg)
|
|||||||
let g:test_clip[a:reg] = [a:lines, a:regtype]
|
let g:test_clip[a:reg] = [a:lines, a:regtype]
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
let provider#clipboard#enabled = 1
|
||||||
|
|
||||||
function! provider#clipboard#Call(method, args)
|
function! provider#clipboard#Call(method, args)
|
||||||
return call(s:methods[a:method],a:args,s:methods)
|
return call(s:methods[a:method],a:args,s:methods)
|
||||||
|
19
test/functional/provider/provider_spec.lua
Normal file
19
test/functional/provider/provider_spec.lua
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
|
||||||
|
local helpers = require('test.functional.helpers')(after_each)
|
||||||
|
local clear, eq, feed_command, eval = helpers.clear, helpers.eq, helpers.feed_command, helpers.eval
|
||||||
|
|
||||||
|
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')"))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('without Call() are enabled', function()
|
||||||
|
eq(1, eval("has('brokencall')"))
|
||||||
|
end)
|
||||||
|
end)
|
Reference in New Issue
Block a user