mirror of
https://github.com/neovim/neovim.git
synced 2025-10-01 07:28:34 +00:00
eval: Add eval_call_provider/eval_has_provider functions
These use autoloaded vimscript to replace the provider_call/provider_has functions, moving the implementation of providers to pure vimscript(we lose nothing since vimscript can also call msgpack-rpc functions). When calling the rpcrequest function from a provider, temporarily switch to the caller scope. This is required for compatibility with legacy plugins, because they may depend on scope information that changes when "leaving" the C stack to enter the vimscript stack.
This commit is contained in:
119
src/nvim/eval.c
119
src/nvim/eval.c
@@ -457,7 +457,7 @@ typedef struct {
|
|||||||
} JobEvent;
|
} JobEvent;
|
||||||
#define JobEventFreer(x)
|
#define JobEventFreer(x)
|
||||||
KMEMPOOL_INIT(JobEventPool, JobEvent, JobEventFreer)
|
KMEMPOOL_INIT(JobEventPool, JobEvent, JobEventFreer)
|
||||||
kmempool_t(JobEventPool) *job_event_pool = NULL;
|
static kmempool_t(JobEventPool) *job_event_pool = NULL;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Initialize the global and v: variables.
|
* Initialize the global and v: variables.
|
||||||
@@ -12540,11 +12540,52 @@ static void f_rpcrequest(typval_T *argvars, typval_T *rettv)
|
|||||||
ADD(args, vim_to_object(tv));
|
ADD(args, vim_to_object(tv));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
scid_T save_current_SID;
|
||||||
|
uint8_t *save_sourcing_name, *save_autocmd_fname, *save_autocmd_match;
|
||||||
|
linenr_T save_sourcing_lnum;
|
||||||
|
int save_autocmd_fname_full, save_autocmd_bufnr;
|
||||||
|
void *save_funccalp;
|
||||||
|
|
||||||
|
if (provider_call_nesting) {
|
||||||
|
// If this is called from a provider function, restore the scope
|
||||||
|
// information of the caller.
|
||||||
|
save_current_SID = current_SID;
|
||||||
|
save_sourcing_name = sourcing_name;
|
||||||
|
save_sourcing_lnum = sourcing_lnum;
|
||||||
|
save_autocmd_fname = autocmd_fname;
|
||||||
|
save_autocmd_match = autocmd_match;
|
||||||
|
save_autocmd_fname_full = autocmd_fname_full;
|
||||||
|
save_autocmd_bufnr = autocmd_bufnr;
|
||||||
|
save_funccalp = save_funccal();
|
||||||
|
//
|
||||||
|
current_SID = provider_caller_scope.SID;
|
||||||
|
sourcing_name = provider_caller_scope.sourcing_name;
|
||||||
|
sourcing_lnum = provider_caller_scope.sourcing_lnum;
|
||||||
|
autocmd_fname = provider_caller_scope.autocmd_fname;
|
||||||
|
autocmd_match = provider_caller_scope.autocmd_match;
|
||||||
|
autocmd_fname_full = provider_caller_scope.autocmd_fname_full;
|
||||||
|
autocmd_bufnr = provider_caller_scope.autocmd_bufnr;
|
||||||
|
restore_funccal(provider_caller_scope.funccalp);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Error err = ERROR_INIT;
|
Error err = ERROR_INIT;
|
||||||
Object result = channel_send_call((uint64_t)argvars[0].vval.v_number,
|
Object result = channel_send_call((uint64_t)argvars[0].vval.v_number,
|
||||||
(char *)argvars[1].vval.v_string,
|
(char *)argvars[1].vval.v_string,
|
||||||
args,
|
args,
|
||||||
&err);
|
&err);
|
||||||
|
|
||||||
|
if (provider_call_nesting) {
|
||||||
|
current_SID = save_current_SID;
|
||||||
|
sourcing_name = save_sourcing_name;
|
||||||
|
sourcing_lnum = save_sourcing_lnum;
|
||||||
|
autocmd_fname = save_autocmd_fname;
|
||||||
|
autocmd_match = save_autocmd_match;
|
||||||
|
autocmd_fname_full = save_autocmd_fname_full;
|
||||||
|
autocmd_bufnr = save_autocmd_bufnr;
|
||||||
|
restore_funccal(save_funccalp);
|
||||||
|
}
|
||||||
|
|
||||||
if (err.set) {
|
if (err.set) {
|
||||||
vim_report_error(cstr_as_string(err.msg));
|
vim_report_error(cstr_as_string(err.msg));
|
||||||
goto end;
|
goto end;
|
||||||
@@ -19690,3 +19731,79 @@ static void script_host_eval(char *method, typval_T *argvars, typval_T *rettv)
|
|||||||
api_free_object(result);
|
api_free_object(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typval_T eval_call_provider(char *provider, char *method, list_T *arguments)
|
||||||
|
{
|
||||||
|
char func[256];
|
||||||
|
int name_len = snprintf(func, sizeof(func), "provider#%s#Call", provider);
|
||||||
|
|
||||||
|
// Save caller scope information
|
||||||
|
struct caller_scope saved_provider_caller_scope = provider_caller_scope;
|
||||||
|
provider_caller_scope = (struct caller_scope) {
|
||||||
|
.SID = current_SID,
|
||||||
|
.sourcing_name = sourcing_name,
|
||||||
|
.sourcing_lnum = sourcing_lnum,
|
||||||
|
.autocmd_fname = autocmd_fname,
|
||||||
|
.autocmd_match = autocmd_match,
|
||||||
|
.autocmd_fname_full = autocmd_fname_full,
|
||||||
|
.autocmd_bufnr = autocmd_bufnr,
|
||||||
|
.funccalp = save_funccal()
|
||||||
|
};
|
||||||
|
provider_call_nesting++;
|
||||||
|
|
||||||
|
typval_T argvars[3] = {
|
||||||
|
{.v_type = VAR_STRING, .vval.v_string = (uint8_t *)method, .v_lock = 0},
|
||||||
|
{.v_type = VAR_LIST, .vval.v_list = arguments, .v_lock = 0},
|
||||||
|
{.v_type = VAR_UNKNOWN}
|
||||||
|
};
|
||||||
|
typval_T rettv = {.v_type = VAR_UNKNOWN, .v_lock = 0};
|
||||||
|
arguments->lv_refcount++;
|
||||||
|
|
||||||
|
int dummy;
|
||||||
|
(void)call_func((uint8_t *)func,
|
||||||
|
name_len,
|
||||||
|
&rettv,
|
||||||
|
2,
|
||||||
|
argvars,
|
||||||
|
curwin->w_cursor.lnum,
|
||||||
|
curwin->w_cursor.lnum,
|
||||||
|
&dummy,
|
||||||
|
true,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
arguments->lv_refcount--;
|
||||||
|
// Restore caller scope information
|
||||||
|
restore_funccal(provider_caller_scope.funccalp);
|
||||||
|
provider_caller_scope = saved_provider_caller_scope;
|
||||||
|
provider_call_nesting--;
|
||||||
|
|
||||||
|
return rettv;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool eval_has_provider(char *name)
|
||||||
|
{
|
||||||
|
#define source_provider(name) \
|
||||||
|
do_source((uint8_t *)"$VIMRUNTIME/autoload/provider/" name ".vim", \
|
||||||
|
false, \
|
||||||
|
false)
|
||||||
|
|
||||||
|
#define check_provider(name) \
|
||||||
|
if (has_##name == -1) { \
|
||||||
|
has_##name = !!find_func((uint8_t *)"provider#" #name "#Call"); \
|
||||||
|
if (!has_##name) { \
|
||||||
|
source_provider(#name); \
|
||||||
|
has_##name = !!find_func((uint8_t *)"provider#" #name "#Call"); \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
static int has_clipboard = -1, has_python = -1;
|
||||||
|
|
||||||
|
if (!strcmp(name, "clipboard")) {
|
||||||
|
check_provider(clipboard);
|
||||||
|
return has_clipboard;
|
||||||
|
} else if (!strcmp(name, "python")) {
|
||||||
|
check_provider(python);
|
||||||
|
return has_python;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
@@ -360,6 +360,16 @@ EXTERN int garbage_collect_at_exit INIT(= FALSE);
|
|||||||
|
|
||||||
/* ID of script being sourced or was sourced to define the current function. */
|
/* ID of script being sourced or was sourced to define the current function. */
|
||||||
EXTERN scid_T current_SID INIT(= 0);
|
EXTERN scid_T current_SID INIT(= 0);
|
||||||
|
// Scope information for the code that indirectly triggered the current
|
||||||
|
// provider function call
|
||||||
|
EXTERN struct caller_scope {
|
||||||
|
scid_T SID;
|
||||||
|
uint8_t *sourcing_name, *autocmd_fname, *autocmd_match;
|
||||||
|
linenr_T sourcing_lnum;
|
||||||
|
int autocmd_fname_full, autocmd_bufnr;
|
||||||
|
void *funccalp;
|
||||||
|
} provider_caller_scope;
|
||||||
|
EXTERN int provider_call_nesting INIT(= 0);
|
||||||
|
|
||||||
/* Magic number used for hashitem "hi_key" value indicating a deleted item.
|
/* Magic number used for hashitem "hi_key" value indicating a deleted item.
|
||||||
* Only the address is used. */
|
* Only the address is used. */
|
||||||
|
Reference in New Issue
Block a user