diff options
author | Rui Abreu Ferreira <raf-ep@gmx.com> | 2019-06-09 18:22:10 +0100 |
---|---|---|
committer | Justin M. Keyes <justinkz@gmail.com> | 2019-08-04 13:23:46 +0200 |
commit | 2cfe4748e57bd510b98ca81bd915f801f5a50bb5 (patch) | |
tree | 7fb2e4ce22b5c800a84c1408c5357e2035c4f296 | |
parent | 2860453c4f9ad6abd7a967f9278401ae84a927e2 (diff) | |
download | rneovim-2cfe4748e57bd510b98ca81bd915f801f5a50bb5.tar.gz rneovim-2cfe4748e57bd510b98ca81bd915f801f5a50bb5.tar.bz2 rneovim-2cfe4748e57bd510b98ca81bd915f801f5a50bb5.zip |
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.
-rw-r--r-- | runtime/autoload/provider/clipboard.vim | 16 | ||||
-rw-r--r-- | runtime/autoload/provider/node.vim | 4 | ||||
-rw-r--r-- | runtime/autoload/provider/python.vim | 6 | ||||
-rw-r--r-- | runtime/autoload/provider/python3.vim | 6 | ||||
-rw-r--r-- | runtime/autoload/provider/ruby.vim | 3 | ||||
-rw-r--r-- | runtime/doc/develop.txt | 6 | ||||
-rw-r--r-- | src/nvim/eval.c | 61 | ||||
-rw-r--r-- | test/functional/fixtures/autoload/provider/brokencall.vim | 2 | ||||
-rw-r--r-- | test/functional/fixtures/autoload/provider/brokenenabled.vim | 6 | ||||
-rw-r--r-- | test/functional/fixtures/autoload/provider/clipboard.vim | 1 | ||||
-rw-r--r-- | test/functional/provider/provider_spec.lua | 19 |
11 files changed, 68 insertions, 62 deletions
diff --git a/runtime/autoload/provider/clipboard.vim b/runtime/autoload/provider/clipboard.vim index 2b06ee8c48..e43f8fbb7a 100644 --- a/runtime/autoload/provider/clipboard.vim +++ b/runtime/autoload/provider/clipboard.vim @@ -48,6 +48,9 @@ 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 @@ -120,12 +123,11 @@ function! provider#clipboard#Executable() abort return '' endfunction -if empty(provider#clipboard#Executable()) - " provider#clipboard#Call() *must not* be defined if the provider is broken. - " Otherwise eval_has_provider() thinks the clipboard provider is - " functioning, and eval_call_provider() will happily call it. - finish -endif +" 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 @@ -192,3 +194,5 @@ function! provider#clipboard#Call(method, args) abort let s:here = v:false endtry endfunction + +call provider#clipboard#Reload() diff --git a/runtime/autoload/provider/node.vim b/runtime/autoload/provider/node.vim index 35882849bd..6413720605 100644 --- a/runtime/autoload/provider/node.vim +++ b/runtime/autoload/provider/node.vim @@ -2,6 +2,7 @@ if exists('g:loaded_node_provider') finish endif let g:loaded_node_provider = 1 +let g:provider#node#enabled = 0 function! s:is_minimum_version(version, min_major, min_minor) abort if empty(a:version) @@ -143,6 +144,9 @@ let s:prog = provider#node#Detect() if empty(s:prog) 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', []) + diff --git a/runtime/autoload/provider/python.vim b/runtime/autoload/provider/python.vim index a06cbe4814..d65506a55a 100644 --- a/runtime/autoload/provider/python.vim +++ b/runtime/autoload/provider/python.vim @@ -10,6 +10,7 @@ endif let g:loaded_python_provider = 1 let [s:prog, s:err] = provider#pythonx#Detect(2) +let g:provider#python#enabled = !empty(s:prog) function! provider#python#Prog() abort return s:prog @@ -19,11 +20,6 @@ function! provider#python#Error() abort return s:err endfunction -if s:prog == '' - " Detection failed - finish -endif - " The Python provider plugin will run in a separate instance of the Python " host. call remote#host#RegisterClone('legacy-python-provider', 'python') diff --git a/runtime/autoload/provider/python3.vim b/runtime/autoload/provider/python3.vim index 242a224cb3..469611c7ce 100644 --- a/runtime/autoload/provider/python3.vim +++ b/runtime/autoload/provider/python3.vim @@ -10,6 +10,7 @@ endif let g:loaded_python3_provider = 1 let [s:prog, s:err] = provider#pythonx#Detect(3) +let g:provider#python3#enabled = !empty(s:prog) function! provider#python3#Prog() abort return s:prog @@ -19,11 +20,6 @@ function! provider#python3#Error() abort return s:err endfunction -if s:prog == '' - " Detection failed - finish -endif - " The Python3 provider plugin will run in a separate instance of the Python3 " host. call remote#host#RegisterClone('legacy-python3-provider', 'python3') diff --git a/runtime/autoload/provider/ruby.vim b/runtime/autoload/provider/ruby.vim index 3b4c6c4839..df43dffa40 100644 --- a/runtime/autoload/provider/ruby.vim +++ b/runtime/autoload/provider/ruby.vim @@ -7,6 +7,7 @@ let g:loaded_ruby_provider = 1 function! provider#ruby#Detect() abort return s:prog endfunction +let g:provider#ruby#enabled = 0 function! provider#ruby#Prog() abort return s:prog @@ -65,6 +66,8 @@ let s:plugin_path = expand('<sfile>:p:h') . '/script_host.rb' if empty(s:prog) 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') diff --git a/runtime/doc/develop.txt b/runtime/doc/develop.txt index 843e23ee54..0f9e17e697 100644 --- a/runtime/doc/develop.txt +++ b/runtime/doc/develop.txt @@ -111,7 +111,7 @@ functions in eval.c: - 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)#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. 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. For example, the Python provider is implemented by the -autoload/provider/python.vim script; the provider#python#Call function is only -defined if a valid external Python host is found. That works well with 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. diff --git a/src/nvim/eval.c b/src/nvim/eval.c index cefd351dd7..ed01ca9f99 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -23968,52 +23968,27 @@ typval_T eval_call_provider(char *provider, char *method, list_T *arguments) return rettv; } -bool eval_has_provider(const char *name) -{ -#define CHECK_PROVIDER(name) \ - if (has_##name == -1) { \ - has_##name = !!find_func((char_u *)"provider#" #name "#Call"); \ - 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; - static int has_python = -1; - static int has_python3 = -1; - static int has_ruby = -1; - typval_T args[1]; - args[0].v_type = VAR_UNKNOWN; - - if (strequal(name, "clipboard")) { - CHECK_PROVIDER(clipboard); - 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); - } +/// Check 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); + + 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); } - 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]`. diff --git a/test/functional/fixtures/autoload/provider/brokencall.vim b/test/functional/fixtures/autoload/provider/brokencall.vim new file mode 100644 index 0000000000..2c83dd2b4a --- /dev/null +++ b/test/functional/fixtures/autoload/provider/brokencall.vim @@ -0,0 +1,2 @@ +" A dummy test provider +let g:provider#brokencall#enabled = 1 diff --git a/test/functional/fixtures/autoload/provider/brokenenabled.vim b/test/functional/fixtures/autoload/provider/brokenenabled.vim new file mode 100644 index 0000000000..54ed11cedc --- /dev/null +++ b/test/functional/fixtures/autoload/provider/brokenenabled.vim @@ -0,0 +1,6 @@ +" Dummy test provider, missing +" let g:provider#brokenenabled#enabled = 0 + +function! provider#brokenenabled#Call(method, args) + return 42 +endfunction diff --git a/test/functional/fixtures/autoload/provider/clipboard.vim b/test/functional/fixtures/autoload/provider/clipboard.vim index 6d777255c8..d1ddd81b12 100644 --- a/test/functional/fixtures/autoload/provider/clipboard.vim +++ b/test/functional/fixtures/autoload/provider/clipboard.vim @@ -35,6 +35,7 @@ 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) diff --git a/test/functional/provider/provider_spec.lua b/test/functional/provider/provider_spec.lua new file mode 100644 index 0000000000..6f414f36f4 --- /dev/null +++ b/test/functional/provider/provider_spec.lua @@ -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) |