From 2cfe4748e57bd510b98ca81bd915f801f5a50bb5 Mon Sep 17 00:00:00 2001 From: Rui Abreu Ferreira Date: Sun, 9 Jun 2019 18:22:10 +0100 Subject: 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. --- src/nvim/eval.c | 61 +++++++++++++++++---------------------------------------- 1 file changed, 18 insertions(+), 43 deletions(-) (limited to 'src') 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 ":" to `buf[bufsize]`. -- cgit From 66938b928c05b913f3a11e520d13ca854621799d Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sun, 4 Aug 2019 03:54:06 +0200 Subject: provider: decide status by g:loaded_xx_provider --- src/nvim/eval.c | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index ed01ca9f99..6b7a359508 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -23968,27 +23968,35 @@ typval_T eval_call_provider(char *provider, char *method, list_T *arguments) return rettv; } -/// Check if a named provider is enabled +/// Checks 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); - + char buf[256]; + int len; 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); + // 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) { + 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 ":" to `buf[bufsize]`. -- cgit From 241956720d02d933b0b27097a3b0a1966f138d0b Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sun, 4 Aug 2019 12:06:24 +0200 Subject: provider: g:loaded_xx_provider=2 means "enabled and working" Value of 1 cannot be used, because users might set that in their vimrc to _disable_ a provider, which would confuse :checkhealth and has(). --- src/nvim/eval.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 6b7a359508..3bcec56b06 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -23996,7 +23996,9 @@ bool eval_has_provider(const char *provider) } } - return (tv.v_type == VAR_NUMBER) ? !!tv.vval.v_number : false; + return (tv.v_type == VAR_NUMBER) + ? 2 == tv.vval.v_number // Value of 2 means "loaded and working". + : false; } /// Writes ":" to `buf[bufsize]`. -- cgit From 5e6a08f2e6b21e83c9fb381042f0aed89de4598d Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sun, 4 Aug 2019 12:20:30 +0200 Subject: provider: skip non-provider has() feature-names We don't want to retry autoload sourcing (slow) for every random has() query that finds it way to eval_call_provider(). --- src/nvim/eval.c | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 3bcec56b06..dc18532b40 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -23969,28 +23969,37 @@ typval_T eval_call_provider(char *provider, char *method, list_T *arguments) } /// Checks if a named provider is enabled. -bool eval_has_provider(const char *provider) -{ +bool eval_has_provider(const char *name) +{ + 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", provider); + 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", provider); + 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", provider); + 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", provider); + snprintf(buf, sizeof(buf), "provider#%s#Call", name); bool has_call = !!find_func((char_u *)buf); if (has_call && p_lpl) { emsgf("provider: %s: missing required variable g:loaded_%s_provider", - provider, provider); + name, name); } return false; } -- cgit From 2141dc22625f73f3ce73460e581934b94f141cf9 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sun, 4 Aug 2019 12:37:05 +0200 Subject: provider: check #Call() if g:loaded_xx_provider=2 --- src/nvim/eval.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index dc18532b40..d82a081c27 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -23996,8 +23996,7 @@ bool eval_has_provider(const char *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); - bool has_call = !!find_func((char_u *)buf); - if (has_call && p_lpl) { + if (!!find_func((char_u *)buf) && p_lpl) { emsgf("provider: %s: missing required variable g:loaded_%s_provider", name, name); } @@ -24005,9 +24004,21 @@ bool eval_has_provider(const char *name) } } - return (tv.v_type == VAR_NUMBER) + 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 ":" to `buf[bufsize]`. -- cgit