aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJustin M. Keyes <justinkz@gmail.com>2019-08-04 15:52:56 +0200
committerGitHub <noreply@github.com>2019-08-04 15:52:56 +0200
commit96be8a2c4d63faadb97c346abb336511a60ac89a (patch)
tree71167260e69f87566391af2b1a8924987e6fe1d7
parent2860453c4f9ad6abd7a967f9278401ae84a927e2 (diff)
parent2141dc22625f73f3ce73460e581934b94f141cf9 (diff)
downloadrneovim-96be8a2c4d63faadb97c346abb336511a60ac89a.tar.gz
rneovim-96be8a2c4d63faadb97c346abb336511a60ac89a.tar.bz2
rneovim-96be8a2c4d63faadb97c346abb336511a60ac89a.zip
Merge #10161 from equalsraf/tb-clipboard-reload
Support "reload" of providers
-rw-r--r--runtime/autoload/health/nvim.vim2
-rw-r--r--runtime/autoload/provider/clipboard.vim20
-rw-r--r--runtime/autoload/provider/node.vim3
-rw-r--r--runtime/autoload/provider/python.vim8
-rw-r--r--runtime/autoload/provider/python3.vim8
-rw-r--r--runtime/autoload/provider/ruby.vim3
-rw-r--r--runtime/doc/develop.txt46
-rw-r--r--runtime/doc/provider.txt8
-rw-r--r--src/nvim/eval.c85
-rw-r--r--test/functional/fixtures/autoload/provider/clipboard.vim3
-rw-r--r--test/functional/fixtures/autoload/provider/python.vim6
-rw-r--r--test/functional/fixtures/autoload/provider/ruby.vim2
-rw-r--r--test/functional/provider/provider_spec.lua26
13 files changed, 127 insertions, 93 deletions
diff --git a/runtime/autoload/health/nvim.vim b/runtime/autoload/health/nvim.vim
index a2227e3b29..c25f5ee64f 100644
--- a/runtime/autoload/health/nvim.vim
+++ b/runtime/autoload/health/nvim.vim
@@ -123,7 +123,7 @@ function! s:check_performance() abort
else
call health#report_info(buildtype)
call health#report_warn(
- \ 'Non-optimized build-type. Nvim will be slower.',
+ \ 'Non-optimized '.(has('debug')?'(DEBUG) ':'').'build. Nvim will be slower.',
\ ['Install a different Nvim package, or rebuild with `CMAKE_BUILD_TYPE=RelWithDebInfo`.',
\ s:suggest_faq])
endif
diff --git a/runtime/autoload/provider/clipboard.vim b/runtime/autoload/provider/clipboard.vim
index 2b06ee8c48..ce140b0948 100644
--- a/runtime/autoload/provider/clipboard.vim
+++ b/runtime/autoload/provider/clipboard.vim
@@ -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 1. provider#clipboard#Executable() may set 2.
+" To force a reload:
+" :unlet g:loaded_clipboard_provider
+" :runtime autoload/provider/clipboard.vim
+let g:loaded_clipboard_provider = 1
+
let s:copy = {}
let s:paste = {}
let s:clipboard = {}
@@ -120,13 +130,6 @@ 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
-
function! s:clipboard.get(reg) abort
if type(s:paste[a:reg]) == v:t_func
return s:paste[a:reg]()
@@ -192,3 +195,6 @@ function! provider#clipboard#Call(method, args) abort
let s:here = v:false
endtry
endfunction
+
+" eval_has_provider() decides based on this variable.
+let g:loaded_clipboard_provider = empty(provider#clipboard#Executable()) ? 1 : 2
diff --git a/runtime/autoload/provider/node.vim b/runtime/autoload/provider/node.vim
index 35882849bd..b2a3b3ee08 100644
--- a/runtime/autoload/provider/node.vim
+++ b/runtime/autoload/provider/node.vim
@@ -140,8 +140,9 @@ endfunction
let s:err = ''
let s:prog = provider#node#Detect()
+let g:loaded_node_provider = empty(s:prog) ? 1 : 2
-if empty(s:prog)
+if g:loaded_node_provider != 2
let s:err = 'Cannot find the "neovim" node package. Try :checkhealth'
endif
diff --git a/runtime/autoload/provider/python.vim b/runtime/autoload/provider/python.vim
index a06cbe4814..8a1d162784 100644
--- a/runtime/autoload/provider/python.vim
+++ b/runtime/autoload/provider/python.vim
@@ -7,9 +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:loaded_python_provider = empty(s:prog) ? 1 : 2
function! provider#python#Prog() abort
return s:prog
@@ -19,11 +18,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..38ef0cccfc 100644
--- a/runtime/autoload/provider/python3.vim
+++ b/runtime/autoload/provider/python3.vim
@@ -7,9 +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:loaded_python3_provider = empty(s:prog) ? 1 : 2
function! provider#python3#Prog() abort
return s:prog
@@ -19,11 +18,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..f843050df9 100644
--- a/runtime/autoload/provider/ruby.vim
+++ b/runtime/autoload/provider/ruby.vim
@@ -62,8 +62,9 @@ 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) ? 1 : 2
-if empty(s:prog)
+if g:loaded_ruby_provider != 2
let s:err = 'Cannot find the neovim RubyGem. Try :checkhealth'
endif
diff --git a/runtime/doc/develop.txt b/runtime/doc/develop.txt
index 843e23ee54..3262d9dec7 100644
--- a/runtime/doc/develop.txt
+++ b/runtime/doc/develop.txt
@@ -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)#Call function is implemented. 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 to 2 by the provider script to indicate that 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 provider#python#Call function is only
-defined 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 2 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*
diff --git a/runtime/doc/provider.txt b/runtime/doc/provider.txt
index 594c9602f4..dc045c360a 100644
--- a/runtime/doc/provider.txt
+++ b/runtime/doc/provider.txt
@@ -68,11 +68,11 @@ startup faster. Useful for working with virtualenvs. >
<
*g:loaded_python_provider*
To disable Python 2 support: >
- let g:loaded_python_provider = 1
+ let g:loaded_python_provider = 0
<
*g:loaded_python3_provider*
To disable Python 3 support: >
- let g:loaded_python3_provider = 1
+ let g:loaded_python3_provider = 0
PYTHON VIRTUALENVS ~
@@ -111,7 +111,7 @@ Run |:checkhealth| to see if your system is up-to-date.
RUBY PROVIDER CONFIGURATION ~
*g:loaded_ruby_provider*
To disable Ruby support: >
- let g:loaded_ruby_provider = 1
+ let g:loaded_ruby_provider = 0
<
*g:ruby_host_prog*
Command to start the Ruby host. By default this is "neovim-ruby-host". With
@@ -142,7 +142,7 @@ Run |:checkhealth| to see if your system is up-to-date.
NODEJS PROVIDER CONFIGURATION~
*g:loaded_node_provider*
To disable Node.js support: >
- :let g:loaded_node_provider = 1
+ :let g:loaded_node_provider = 0
<
*g:node_host_prog*
Command to start the Node.js host. Setting this makes startup faster.
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index cefd351dd7..d82a081c27 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -23968,52 +23968,57 @@ typval_T eval_call_provider(char *provider, char *method, list_T *arguments)
return rettv;
}
+/// Checks if a named provider is enabled.
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);
+ if (!strequal(name, "clipboard")
+ && !strequal(name, "python")
+ && !strequal(name, "python3")
+ && !strequal(name, "ruby")
+ && !strequal(name, "node")) {
+ // Avoid autoload for non-provider has() features.
+ return false;
+ }
+
+ char buf[256];
+ int len;
+ typval_T tv;
+
+ // Get the g:loaded_xx_provider variable.
+ len = snprintf(buf, sizeof(buf), "g:loaded_%s_provider", name);
+ if (get_var_tv(buf, len, &tv, NULL, false, true) == FAIL) {
+ // Trigger autoload once.
+ len = snprintf(buf, sizeof(buf), "provider#%s#bogus", name);
+ script_autoload(buf, len, false);
+
+ // Retry the (non-autoload-style) variable.
+ len = snprintf(buf, sizeof(buf), "g:loaded_%s_provider", name);
+ 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", name);
+ if (!!find_func((char_u *)buf) && p_lpl) {
+ emsgf("provider: %s: missing required variable g:loaded_%s_provider",
+ name, name);
}
+ return false;
}
- return has_ruby;
}
- return false;
+ bool ok = (tv.v_type == VAR_NUMBER)
+ ? 2 == tv.vval.v_number // Value of 2 means "loaded and working".
+ : false;
+
+ if (ok) {
+ // Call() must be defined if provider claims to be working.
+ snprintf(buf, sizeof(buf), "provider#%s#Call", name);
+ if (!find_func((char_u *)buf)) {
+ emsgf("provider: %s: g:loaded_%s_provider=2 but %s is not defined",
+ name, name, buf);
+ ok = false;
+ }
+ }
+
+ return ok;
}
/// Writes "<sourcing_name>:<sourcing_lnum>" to `buf[bufsize]`.
diff --git a/test/functional/fixtures/autoload/provider/clipboard.vim b/test/functional/fixtures/autoload/provider/clipboard.vim
index 6d777255c8..41e486b745 100644
--- a/test/functional/fixtures/autoload/provider/clipboard.vim
+++ b/test/functional/fixtures/autoload/provider/clipboard.vim
@@ -1,3 +1,5 @@
+let g:loaded_clipboard_provider = 2
+
let g:test_clip = { '+': [''], '*': [''], }
let s:methods = {}
@@ -35,7 +37,6 @@ function! s:methods.set(lines, regtype, reg)
let g:test_clip[a:reg] = [a:lines, a:regtype]
endfunction
-
function! provider#clipboard#Call(method, args)
return call(s:methods[a:method],a:args,s:methods)
endfunction
diff --git a/test/functional/fixtures/autoload/provider/python.vim b/test/functional/fixtures/autoload/provider/python.vim
new file mode 100644
index 0000000000..d68360ac30
--- /dev/null
+++ b/test/functional/fixtures/autoload/provider/python.vim
@@ -0,0 +1,6 @@
+" Dummy test provider, missing this required variable:
+" let g:loaded_brokenenabled_provider = 0
+
+function! provider#python#Call(method, args)
+ return 42
+endfunction
diff --git a/test/functional/fixtures/autoload/provider/ruby.vim b/test/functional/fixtures/autoload/provider/ruby.vim
new file mode 100644
index 0000000000..35becc27ff
--- /dev/null
+++ b/test/functional/fixtures/autoload/provider/ruby.vim
@@ -0,0 +1,2 @@
+" A dummy test provider
+let g:loaded_ruby_provider = 2
diff --git a/test/functional/provider/provider_spec.lua b/test/functional/provider/provider_spec.lua
new file mode 100644
index 0000000000..bfb0bbc3a3
--- /dev/null
+++ b/test/functional/provider/provider_spec.lua
@@ -0,0 +1,26 @@
+
+local helpers = require('test.functional.helpers')(after_each)
+local clear, eval = helpers.clear, helpers.eval
+local command = helpers.command
+local expect_err = helpers.expect_err
+
+describe('providers', function()
+ before_each(function()
+ clear('--cmd', 'let &rtp = "test/functional/fixtures,".&rtp')
+ end)
+
+ it('with #Call(), missing g:loaded_xx_provider', function()
+ command('set loadplugins')
+ -- Using test-fixture with broken impl:
+ -- test/functional/fixtures/autoload/provider/python.vim
+ expect_err('Vim:provider: python: missing required variable g:loaded_python_provider',
+ eval, "has('python')")
+ end)
+
+ it('with g:loaded_xx_provider, missing #Call()', function()
+ -- Using test-fixture with broken impl:
+ -- test/functional/fixtures/autoload/provider/ruby.vim
+ expect_err('Vim:provider: ruby: g:loaded_ruby_provider=2 but provider#ruby#Call is not defined',
+ eval, "has('ruby')")
+ end)
+end)