mirror of
https://github.com/neovim/neovim.git
synced 2024-12-24 13:15:09 -07:00
Merge #10161 from equalsraf/tb-clipboard-reload
Support "reload" of providers
This commit is contained in:
commit
96be8a2c4d
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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')
|
||||
|
@ -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')
|
||||
|
@ -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
|
||||
|
||||
|
@ -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*
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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"); \
|
||||
} \
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
char buf[256];
|
||||
int len;
|
||||
typval_T tv;
|
||||
|
||||
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);
|
||||
// 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]`.
|
||||
|
@ -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
|
||||
|
6
test/functional/fixtures/autoload/provider/python.vim
Normal file
6
test/functional/fixtures/autoload/provider/python.vim
Normal file
@ -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
|
2
test/functional/fixtures/autoload/provider/ruby.vim
Normal file
2
test/functional/fixtures/autoload/provider/ruby.vim
Normal file
@ -0,0 +1,2 @@
|
||||
" A dummy test provider
|
||||
let g:loaded_ruby_provider = 2
|
26
test/functional/provider/provider_spec.lua
Normal file
26
test/functional/provider/provider_spec.lua
Normal file
@ -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)
|
Loading…
Reference in New Issue
Block a user