mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06: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 clipboard provider uses shell commands to communicate with the clipboard.
|
||||||
" The provider function will only be registered if a supported command is
|
" The provider function will only be registered if a supported command is
|
||||||
" available.
|
" 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:copy = {}
|
||||||
let s:paste = {}
|
let s:paste = {}
|
||||||
let s:clipboard = {}
|
let s:clipboard = {}
|
||||||
@ -48,9 +58,6 @@ 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
|
||||||
@ -123,12 +130,6 @@ function! provider#clipboard#Executable() abort
|
|||||||
return ''
|
return ''
|
||||||
endfunction
|
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
|
function! s:clipboard.get(reg) abort
|
||||||
if type(s:paste[a:reg]) == v:t_func
|
if type(s:paste[a:reg]) == v:t_func
|
||||||
return s:paste[a:reg]()
|
return s:paste[a:reg]()
|
||||||
@ -195,4 +196,5 @@ function! provider#clipboard#Call(method, args) abort
|
|||||||
endtry
|
endtry
|
||||||
endfunction
|
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')
|
if exists('g:loaded_node_provider')
|
||||||
finish
|
finish
|
||||||
endif
|
endif
|
||||||
let g:loaded_node_provider = 1
|
let g:loaded_node_provider = 0
|
||||||
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)
|
||||||
@ -141,12 +140,10 @@ endfunction
|
|||||||
|
|
||||||
let s:err = ''
|
let s:err = ''
|
||||||
let s:prog = provider#node#Detect()
|
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'
|
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', [])
|
||||||
|
|
||||||
|
@ -7,10 +7,8 @@
|
|||||||
if exists('g:loaded_python_provider')
|
if exists('g:loaded_python_provider')
|
||||||
finish
|
finish
|
||||||
endif
|
endif
|
||||||
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)
|
let g:loaded_python_provider = !empty(s:prog)
|
||||||
|
|
||||||
function! provider#python#Prog() abort
|
function! provider#python#Prog() abort
|
||||||
return s:prog
|
return s:prog
|
||||||
|
@ -7,10 +7,8 @@
|
|||||||
if exists('g:loaded_python3_provider')
|
if exists('g:loaded_python3_provider')
|
||||||
finish
|
finish
|
||||||
endif
|
endif
|
||||||
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)
|
let g:loaded_python3_provider = !empty(s:prog)
|
||||||
|
|
||||||
function! provider#python3#Prog() abort
|
function! provider#python3#Prog() abort
|
||||||
return s:prog
|
return s:prog
|
||||||
|
@ -2,12 +2,11 @@
|
|||||||
if exists('g:loaded_ruby_provider')
|
if exists('g:loaded_ruby_provider')
|
||||||
finish
|
finish
|
||||||
endif
|
endif
|
||||||
let g:loaded_ruby_provider = 1
|
let g:loaded_ruby_provider = 0
|
||||||
|
|
||||||
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
|
||||||
@ -63,11 +62,10 @@ endfunction
|
|||||||
let s:err = ''
|
let s:err = ''
|
||||||
let s:prog = s:detect()
|
let s:prog = s:detect()
|
||||||
let s:plugin_path = expand('<sfile>:p:h') . '/script_host.rb'
|
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'
|
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')
|
||||||
|
@ -84,12 +84,11 @@ Developer guidelines *dev-guidelines*
|
|||||||
|
|
||||||
PROVIDERS *dev-provider*
|
PROVIDERS *dev-provider*
|
||||||
|
|
||||||
A goal of Nvim is to allow extension of the editor without special knowledge
|
A primary goal of Nvim is to allow extension of the editor without special
|
||||||
in the core. But some Vim components are too tightly coupled; in those cases
|
knowledge in the core. Some core functions are delegated to "providers"
|
||||||
a "provider" hook is exposed.
|
implemented as external scripts.
|
||||||
|
|
||||||
Consider two examples of integration with external systems that are
|
Examples:
|
||||||
implemented in Vim and are now decoupled from Nvim core as providers:
|
|
||||||
|
|
||||||
1. In the Vim source code, clipboard logic accounts for more than 1k lines of
|
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
|
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
|
scripting is performed by an external host process implemented in ~2k lines
|
||||||
of Python.
|
of Python.
|
||||||
|
|
||||||
Ideally we could implement Python and clipboard integration in pure vimscript
|
The provider framework invokes VimL from C. It is composed of two functions
|
||||||
and without touching the C code. But this is infeasible without compromising
|
in eval.c:
|
||||||
backwards compatibility with Vim; that's where providers help.
|
|
||||||
|
|
||||||
The provider framework helps call vimscript from C. It is composed of two
|
- eval_call_provider(name, method, arguments): calls provider#{name}#Call
|
||||||
functions in eval.c:
|
|
||||||
|
|
||||||
- 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 the `g:loaded_{name}_provider` variable
|
||||||
if the provider#(name)#enabled variable is not 0. Called by |has()|
|
which must be set by the provider script to indicate whether it is enabled
|
||||||
(vimscript) to check if features are available.
|
and working. Called by |has()| 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.
|
|
||||||
|
|
||||||
For example, the Python provider is implemented by the
|
For example, the Python provider is implemented by the
|
||||||
autoload/provider/python.vim script; the variable provider#python#enabled is only
|
"autoload/provider/python.vim" script, which sets `g:loaded_python_provider`
|
||||||
1 if a valid external Python host is found. That works well with the
|
to TRUE only if a valid external Python host is found. Then `has("python")`
|
||||||
`has('python')` expression (normally used by Python plugins) because if the
|
reflects whether Python support is working.
|
||||||
Python host isn't installed then the plugin will "think" it is running in
|
|
||||||
a Vim compiled without the "+python" feature.
|
*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*
|
DOCUMENTATION *dev-doc*
|
||||||
|
|
||||||
|
@ -23968,27 +23968,35 @@ typval_T eval_call_provider(char *provider, char *method, list_T *arguments)
|
|||||||
return rettv;
|
return rettv;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if a named provider is enabled
|
/// Checks if a named provider is enabled.
|
||||||
bool eval_has_provider(const char *provider)
|
bool eval_has_provider(const char *provider)
|
||||||
{
|
{
|
||||||
char enabled_varname[256];
|
char buf[256];
|
||||||
int enabled_varname_len = snprintf(enabled_varname, sizeof(enabled_varname),
|
int len;
|
||||||
"provider#%s#enabled", provider);
|
|
||||||
|
|
||||||
typval_T tv;
|
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);
|
|
||||||
|
|
||||||
|
// 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) {
|
if (has_call && p_lpl) {
|
||||||
emsgf("Provider '%s' failed to set %s", provider, enabled_varname);
|
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]`.
|
/// Writes "<sourcing_name>:<sourcing_lnum>" to `buf[bufsize]`.
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
" A dummy test provider
|
" A dummy test provider
|
||||||
let g:provider#brokencall#enabled = 1
|
let g:loaded_brokencall_provider = 1
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
" Dummy test provider, missing
|
" Dummy test provider, missing this required variable:
|
||||||
" let g:provider#brokenenabled#enabled = 0
|
" let g:loaded_brokenenabled_provider = 0
|
||||||
|
|
||||||
function! provider#brokenenabled#Call(method, args)
|
function! provider#brokenenabled#Call(method, args)
|
||||||
return 42
|
return 42
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
let g:loaded_clipboard_provider = 1
|
||||||
|
|
||||||
let g:test_clip = { '+': [''], '*': [''], }
|
let g:test_clip = { '+': [''], '*': [''], }
|
||||||
|
|
||||||
let s:methods = {}
|
let s:methods = {}
|
||||||
@ -35,8 +37,6 @@ 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)
|
||||||
endfunction
|
endfunction
|
||||||
|
@ -1,19 +1,21 @@
|
|||||||
|
|
||||||
local helpers = require('test.functional.helpers')(after_each)
|
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()
|
before_each(function()
|
||||||
clear('--cmd', 'let &rtp = "test/functional/fixtures,".&rtp')
|
clear('--cmd', 'let &rtp = "test/functional/fixtures,".&rtp')
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('must set the enabled variable or fail', function()
|
it('must define g:loaded_xx_provider', function()
|
||||||
eq(42, eval("provider#brokenenabled#Call('dosomething', [])"))
|
command('set loadplugins')
|
||||||
feed_command("call has('brokenenabled')")
|
expect_err('Vim:provider: brokenenabled: missing required variable g:loaded_brokenenabled_provider',
|
||||||
eq(0, eval("has('brokenenabled')"))
|
eval, "has('brokenenabled')")
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('without Call() are enabled', function()
|
it('without Call() but with g:loaded_xx_provider', function()
|
||||||
eq(1, eval("has('brokencall')"))
|
eq(1, eval("has('brokencall')"))
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
Loading…
Reference in New Issue
Block a user