provider: Major refactor

- Providers for features are now registered as a unit. For example, instead of
  calling `register_provider("clipboard_get")` and
  `register_provider("clipboard_set")`, clients call
  `register_provider("clipboard")` and nvim will assume it implements all
  methods of the "clipboard" feature
- Bootstrapping code was removed. With the `api_spawn` function exposed to
  vimscript, it's no longer necessary and will be handled by plugins
  distributed with nvim.
- Now the `has` function will return true if there's a live channel that
  has registered as a provider for the feature.
- 'initpython'/'initclipboard' options were removed
- A new API function was exposed: `vim_discover_features` which returns an
  object with information about pluggable features such as 'python' or
  'clipboard'
This commit is contained in:
Thiago de Arruda 2014-09-10 10:11:45 -03:00
parent 5060902930
commit a1ce3a3acc
4 changed files with 68 additions and 128 deletions

View File

@ -502,22 +502,28 @@ void vim_unsubscribe(uint64_t channel_id, String event)
channel_unsubscribe(channel_id, e); channel_unsubscribe(channel_id, e);
} }
/// Registers the channel as the provider for `method`. This fails if /// Registers the channel as the provider for `feature`. This fails if
/// a provider for `method` is already registered. /// a provider for `feature` is already provided by another channel.
/// ///
/// @param channel_id The channel id /// @param channel_id The channel id
/// @param method The method name /// @param feature The feature name
/// @param[out] err Details of an error that may have occurred /// @param[out] err Details of an error that may have occurred
void vim_register_provider(uint64_t channel_id, String method, Error *err) void vim_register_provider(uint64_t channel_id, String feature, Error *err)
{ {
char buf[METHOD_MAXLEN]; char buf[METHOD_MAXLEN];
xstrlcpy(buf, method.data, sizeof(buf)); xstrlcpy(buf, feature.data, sizeof(buf));
if (!provider_register(buf, channel_id)) { if (!provider_register(buf, channel_id)) {
set_api_error("Provider already registered", err); set_api_error("Feature doesn't exist", err);
} }
} }
/// Returns a feature->method list dictionary for all pluggable features
Dictionary vim_discover_features(void)
{
return provider_get_all();
}
/// Writes a message to vim output or error buffer. The string is split /// Writes a message to vim output or error buffer. The string is split
/// and flushed after each newline. Incomplete lines are kept for writing /// and flushed after each newline. Incomplete lines are kept for writing
/// later. /// later.

View File

@ -974,12 +974,6 @@ static struct vimoption
{"infercase", "inf", P_BOOL|P_VI_DEF, {"infercase", "inf", P_BOOL|P_VI_DEF,
(char_u *)&p_inf, PV_INF, (char_u *)&p_inf, PV_INF,
{(char_u *)FALSE, (char_u *)0L} SCRIPTID_INIT}, {(char_u *)FALSE, (char_u *)0L} SCRIPTID_INIT},
{"initclipboard","icpb",P_STRING|P_VI_DEF|P_SECURE,
(char_u *)&p_icpb, PV_NONE,
{(char_u *)"", (char_u *)0L} SCRIPTID_INIT},
{"initpython","ipy",P_STRING|P_VI_DEF|P_SECURE,
(char_u *)&p_ipy, PV_NONE,
{(char_u *)"", (char_u *)0L} SCRIPTID_INIT},
{"insertmode", "im", P_BOOL|P_VI_DEF|P_VIM, {"insertmode", "im", P_BOOL|P_VI_DEF|P_VIM,
(char_u *)&p_im, PV_NONE, (char_u *)&p_im, PV_NONE,
{(char_u *)FALSE, (char_u *)0L} SCRIPTID_INIT}, {(char_u *)FALSE, (char_u *)0L} SCRIPTID_INIT},

View File

@ -631,8 +631,6 @@ EXTERN int p_write; /* 'write' */
EXTERN int p_wa; /* 'writeany' */ EXTERN int p_wa; /* 'writeany' */
EXTERN int p_wb; /* 'writebackup' */ EXTERN int p_wb; /* 'writebackup' */
EXTERN long p_wd; /* 'writedelay' */ EXTERN long p_wd; /* 'writedelay' */
EXTERN char *p_ipy; // 'initpython'
EXTERN char *p_icpb; // 'initclipboard'
/* /*
* "indir" values for buffer-local opions. * "indir" values for buffer-local opions.

View File

@ -17,33 +17,31 @@
#define FEATURE_COUNT (sizeof(features) / sizeof(features[0])) #define FEATURE_COUNT (sizeof(features) / sizeof(features[0]))
#define FEATURE(feature_name, provider_bootstrap_command, ...) { \ #define FEATURE(feature_name, ...) { \
.name = feature_name, \ .name = feature_name, \
.bootstrap_command = provider_bootstrap_command, \
.argv = NULL, \
.channel_id = 0, \ .channel_id = 0, \
.methods = (char *[]){__VA_ARGS__, NULL} \ .methods = (char *[]){__VA_ARGS__, NULL} \
} }
static struct feature { typedef struct {
char *name, **bootstrap_command, **argv, **methods; char *name, **methods;
size_t name_length; size_t name_length;
uint64_t channel_id; uint64_t channel_id;
} features[] = { } Feature;
static Feature features[] = {
FEATURE("python", FEATURE("python",
&p_ipy,
"python_execute", "python_execute",
"python_execute_file", "python_execute_file",
"python_do_range", "python_do_range",
"python_eval"), "python_eval"),
FEATURE("clipboard", FEATURE("clipboard",
&p_icpb,
"clipboard_get", "clipboard_get",
"clipboard_set") "clipboard_set")
}; };
static Map(cstr_t, uint64_t) *registered_providers = NULL; static PMap(cstr_t) *registered_providers = NULL;
#ifdef INCLUDE_GENERATED_DECLARATIONS #ifdef INCLUDE_GENERATED_DECLARATIONS
# include "os/provider.c.generated.h" # include "os/provider.c.generated.h"
@ -52,60 +50,57 @@ static Map(cstr_t, uint64_t) *registered_providers = NULL;
void provider_init(void) void provider_init(void)
{ {
registered_providers = map_new(cstr_t, uint64_t)(); registered_providers = pmap_new(cstr_t)();
} }
bool provider_has_feature(char *name) bool provider_has_feature(char *name)
{ {
for (size_t i = 0; i < FEATURE_COUNT; i++) { Feature *f = find_feature(name);
struct feature *f = &features[i]; return f != NULL && channel_exists(f->channel_id);
if (!STRICMP(name, f->name)) {
return f->channel_id || can_execute(f);
}
}
return false;
} }
bool provider_available(char *method) bool provider_register(char *name, uint64_t channel_id)
{ {
return map_has(cstr_t, uint64_t)(registered_providers, method); Feature *f = find_feature(name);
}
bool provider_register(char *method, uint64_t channel_id) if (!f) {
{
if (map_has(cstr_t, uint64_t)(registered_providers, method)) {
return false; return false;
} }
// First check if this method is part of a feature, and if so, update if (f->channel_id && channel_exists(f->channel_id)) {
// the feature structure with the channel id ILOG("Feature \"%s\" is already provided by another channel"
struct feature *f = get_feature_for(method); "(will be replaced)", name);
if (f) {
DLOG("Registering provider for \"%s\" "
"which is part of the \"%s\" feature",
method,
f->name);
f->channel_id = channel_id;
} }
map_put(cstr_t, uint64_t)(registered_providers, xstrdup(method), channel_id); DLOG("Registering provider for \"%s\"", name);
ILOG("Registered channel %" PRIu64 " as the provider for \"%s\"", f->channel_id = channel_id;
// Associate all method names with the feature struct
size_t i;
char *method;
for (method = f->methods[i = 0]; method; method = f->methods[++i]) {
pmap_put(cstr_t)(registered_providers, method, f);
DLOG("Channel \"%" PRIu64 "\" will be sent requests for \"%s\"",
channel_id,
method);
}
ILOG("Registered channel %" PRIu64 " as the provider for the \"%s\" feature",
channel_id, channel_id,
method); name);
return true; return true;
} }
Object provider_call(char *method, Array args) Object provider_call(char *method, Array args)
{ {
uint64_t channel_id = get_provider_for(method); Feature *f = pmap_get(cstr_t)(registered_providers, method);
if (!channel_id) { if (!f || !channel_exists(f->channel_id)) {
char buf[256]; char buf[256];
snprintf(buf, snprintf(buf,
sizeof(buf), sizeof(buf),
"Provider for \"%s\" is not available", "Provider for method \"%s\" is not available",
method); method);
report_error(buf); report_error(buf);
api_free_array(args); api_free_array(args);
@ -114,7 +109,7 @@ Object provider_call(char *method, Array args)
bool error = false; bool error = false;
Object result = NIL; Object result = NIL;
channel_send_call(channel_id, method, args, &result, &error); channel_send_call(f->channel_id, method, args, &result, &error);
if (error) { if (error) {
report_error(result.data.string.data); report_error(result.data.string.data);
@ -125,62 +120,36 @@ Object provider_call(char *method, Array args)
return result; return result;
} }
static uint64_t get_provider_for(char *method) Dictionary provider_get_all(void)
{ {
uint64_t channel_id = map_get(cstr_t, uint64_t)(registered_providers, method); Dictionary rv = ARRAY_DICT_INIT;
if (channel_id) { for (size_t i = 0; i < FEATURE_COUNT; i++) {
return channel_id; Array methods = ARRAY_DICT_INIT;
Feature *f = &features[i];
size_t j;
char *method;
for (method = f->methods[j = 0]; method; method = f->methods[++j]) {
ADD(methods, STRING_OBJ(cstr_to_string(method)));
}
PUT(rv, f->name, ARRAY_OBJ(methods));
} }
// Try to bootstrap if the method is part of a feature return rv;
struct feature *f = get_feature_for(method);
if (!f || !can_execute(f)) {
ELOG("Cannot bootstrap provider for \"%s\"", method);
goto err;
}
if (f->channel_id) {
ELOG("Already bootstrapped provider for \"%s\"", f->name);
goto err;
}
f->channel_id = channel_from_job(f->argv);
if (!f->channel_id) {
ELOG("The provider for \"%s\" failed to bootstrap", f->name);
goto err;
}
return f->channel_id;
err:
// Ensure we won't try to restart the provider
if (f) {
f->bootstrap_command = NULL;
f->channel_id = 0;
}
return 0;
} }
static bool can_execute(struct feature *f) static Feature * find_feature(char *name)
{ {
if (!f->bootstrap_command) { for (size_t i = 0; i < FEATURE_COUNT; i++) {
return false; Feature *f = &features[i];
if (!STRICMP(name, f->name)) {
return f;
}
} }
char *cmd = *f->bootstrap_command; return NULL;
if (!cmd || !strlen(cmd)) {
return false;
}
if (!f->argv) {
f->argv = shell_build_argv((uint8_t *)cmd, NULL);
}
return os_can_exe((uint8_t *)f->argv[0]);
} }
static void report_error(char *str) static void report_error(char *str)
@ -188,30 +157,3 @@ static void report_error(char *str)
vim_err_write((String) {.data = str, .size = strlen(str)}); vim_err_write((String) {.data = str, .size = strlen(str)});
vim_err_write((String) {.data = "\n", .size = 1}); vim_err_write((String) {.data = "\n", .size = 1});
} }
static bool feature_has_method(struct feature *f, char *method)
{
size_t i;
char *m;
for (m = f->methods[i = 0]; m; m = f->methods[++i]) {
if (!STRCMP(method, m)) {
return true;
}
}
return false;
}
static struct feature *get_feature_for(char *method)
{
for (size_t i = 0; i < FEATURE_COUNT; i++) {
struct feature *f = &features[i];
if (feature_has_method(f, method)) {
return f;
}
}
return NULL;
}