diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/nvim/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | src/nvim/api/vim.c | 17 | ||||
| -rw-r--r-- | src/nvim/eval.c | 5 | ||||
| -rw-r--r-- | src/nvim/map.c | 1 | ||||
| -rw-r--r-- | src/nvim/map.h | 1 | ||||
| -rw-r--r-- | src/nvim/os/channel.c | 13 | ||||
| -rw-r--r-- | src/nvim/os/event.c | 3 | ||||
| -rw-r--r-- | src/nvim/os/provider.c | 204 | ||||
| -rw-r--r-- | src/nvim/os/provider.h | 11 | 
9 files changed, 255 insertions, 1 deletions
| diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index b3d11eeba0..951cd9ded1 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -51,6 +51,7 @@ set(CONV_SRCS    os/rstream.c    os/signal.c    os/users.c +  os/provider.c    os/uv_helpers.c    os/wstream.c    os/msgpack_rpc.c diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 9834633813..6c793cbc54 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -10,6 +10,7 @@  #include "nvim/api/private/defs.h"  #include "nvim/api/buffer.h"  #include "nvim/os/channel.h" +#include "nvim/os/provider.h"  #include "nvim/vim.h"  #include "nvim/buffer.h"  #include "nvim/window.h" @@ -503,6 +504,22 @@ void vim_unsubscribe(uint64_t channel_id, String event)    channel_unsubscribe(channel_id, e);  } +/// Registers the channel as the provider for `method`. This fails if +/// a provider for `method` is already registered. +/// +/// @param channel_id The channel id +/// @param method The method name +/// @param[out] err Details of an error that may have occurred +void vim_register_provider(uint64_t channel_id, String method, Error *err) +{ +  char buf[METHOD_MAXLEN]; +  xstrlcpy(buf, method.data, sizeof(buf)); + +  if (!provider_register(buf, channel_id)) { +    set_api_error("Provider already registered", err); +  } +} +  /// Writes a message to vim output or error buffer. The string is split  /// and flushed after each newline. Incomplete lines are kept for writing  /// later. diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 428f1430b3..a3d07d8c4f 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -85,6 +85,7 @@  #include "nvim/api/private/helpers.h"  #include "nvim/os/msgpack_rpc_helpers.h"  #include "nvim/os/dl.h" +#include "nvim/os/provider.h"  #define DICT_MAXNEST 100        /* maximum nesting of lists and dicts */ @@ -9807,6 +9808,10 @@ static void f_has(typval_T *argvars, typval_T *rettv)      }    } +  if (n == FALSE && provider_has_feature((char *)name)) { +    n = TRUE; +  } +    rettv->vval.v_number = n;  } diff --git a/src/nvim/map.c b/src/nvim/map.c index 46eca8e6f5..2e47e8b249 100644 --- a/src/nvim/map.c +++ b/src/nvim/map.c @@ -87,6 +87,7 @@      return rv;                                                                \    } +MAP_IMPL(cstr_t, uint64_t, DEFAULT_INITIALIZER)  MAP_IMPL(cstr_t, ptr_t, DEFAULT_INITIALIZER)  MAP_IMPL(ptr_t, ptr_t, DEFAULT_INITIALIZER)  MAP_IMPL(uint64_t, ptr_t, DEFAULT_INITIALIZER) diff --git a/src/nvim/map.h b/src/nvim/map.h index 61f56821b8..73698cba22 100644 --- a/src/nvim/map.h +++ b/src/nvim/map.h @@ -19,6 +19,7 @@    U map_##T##_##U##_put(Map(T, U) *map, T key, U value);                      \    U map_##T##_##U##_del(Map(T, U) *map, T key); +MAP_DECLS(cstr_t, uint64_t)  MAP_DECLS(cstr_t, ptr_t)  MAP_DECLS(ptr_t, ptr_t)  MAP_DECLS(uint64_t, ptr_t) diff --git a/src/nvim/os/channel.c b/src/nvim/os/channel.c index efe628098c..c12779e794 100644 --- a/src/nvim/os/channel.c +++ b/src/nvim/os/channel.c @@ -6,6 +6,7 @@  #include <msgpack.h>  #include "nvim/api/private/helpers.h" +#include "nvim/api/vim.h"  #include "nvim/os/channel.h"  #include "nvim/os/event.h"  #include "nvim/os/rstream.h" @@ -17,9 +18,11 @@  #include "nvim/os/msgpack_rpc.h"  #include "nvim/os/msgpack_rpc_helpers.h"  #include "nvim/vim.h" +#include "nvim/ascii.h"  #include "nvim/memory.h"  #include "nvim/message.h"  #include "nvim/map.h" +#include "nvim/log.h"  #include "nvim/lib/kvec.h"  typedef struct { @@ -274,7 +277,15 @@ static void job_out(RStream *rstream, void *data, bool eof)  static void job_err(RStream *rstream, void *data, bool eof)  { -  // TODO(tarruda): plugin error messages should be sent to the error buffer +  size_t count; +  char buf[256]; +  Channel *channel = job_data(data); + +  while ((count = rstream_available(rstream))) { +    size_t read = rstream_read(rstream, buf, sizeof(buf) - 1); +    buf[read] = NUL; +    ELOG("Channel %" PRIu64 " stderr: %s", channel->id, buf); +  }  }  static void parse_msgpack(RStream *rstream, void *data, bool eof) diff --git a/src/nvim/os/event.c b/src/nvim/os/event.c index 4e091716b2..0528339865 100644 --- a/src/nvim/os/event.c +++ b/src/nvim/os/event.c @@ -9,6 +9,7 @@  #include "nvim/os/input.h"  #include "nvim/os/channel.h"  #include "nvim/os/server.h" +#include "nvim/os/provider.h"  #include "nvim/os/signal.h"  #include "nvim/os/rstream.h"  #include "nvim/os/job.h" @@ -50,6 +51,8 @@ void event_init(void)    channel_init();    // Servers    server_init(); +  // Providers +  provider_init();  }  void event_teardown(void) diff --git a/src/nvim/os/provider.c b/src/nvim/os/provider.c new file mode 100644 index 0000000000..967314eee4 --- /dev/null +++ b/src/nvim/os/provider.c @@ -0,0 +1,204 @@ +#include <stdint.h> +#include <inttypes.h> +#include <stdbool.h> +#include <assert.h> + +#include "nvim/os/provider.h" +#include "nvim/memory.h" +#include "nvim/api/vim.h" +#include "nvim/api/private/helpers.h" +#include "nvim/api/private/defs.h" +#include "nvim/os/channel.h" +#include "nvim/os/shell.h" +#include "nvim/os/os.h" +#include "nvim/log.h" +#include "nvim/map.h" +#include "nvim/message.h" +#include "nvim/os/msgpack_rpc_helpers.h" + +#define FEATURE_COUNT (sizeof(features) / sizeof(features[0])) + +#define FEATURE(feature_name, provider_bootstrap_command, ...) {            \ +  .name = feature_name,                                                     \ +  .bootstrap_command = provider_bootstrap_command,                          \ +  .argv = NULL,                                                             \ +  .channel_id = 0,                                                          \ +  .methods = (char *[]){__VA_ARGS__, NULL}                                  \ +} + +static struct feature { +  char *name, **bootstrap_command, **argv, **methods; +  size_t name_length; +  uint64_t channel_id; +} features[] = { +}; + +static Map(cstr_t, uint64_t) *registered_providers = NULL; + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "os/provider.c.generated.h" +#endif + + +void provider_init(void) +{ +  registered_providers = map_new(cstr_t, uint64_t)(); +} + +bool provider_has_feature(char *name) +{ +  for (size_t i = 0; i < FEATURE_COUNT; i++) { +    struct feature *f = &features[i]; +    if (!STRICMP(name, f->name)) { +      return f->channel_id || can_execute(f); +    } +  } + +  return false; +} + +bool provider_available(char *method) +{ +  return map_has(cstr_t, uint64_t)(registered_providers, method); +} + +bool provider_register(char *method, uint64_t channel_id) +{ +  if (map_has(cstr_t, uint64_t)(registered_providers, method)) { +    return false; +  } + +  // First check if this method is part of a feature, and if so, update +  // the feature structure with the channel id +  struct feature *f = get_feature_for(method); +  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); +  ILOG("Registered channel %" PRIu64 " as the provider for \"%s\"", +       channel_id, +       method); + +  return true; +} + +Object provider_call(char *method, Object arg) +{ +  uint64_t channel_id = get_provider_for(method); + +  if (!channel_id) { +    char buf[256]; +    snprintf(buf, +             sizeof(buf), +             "Provider for \"%s\" is not available", +             method); +    report_error(buf); +    return NIL; +  } + +  bool error = false; +  Object result = NIL; +  channel_send_call(channel_id, method, arg, &result, &error); + +  if (error) { +    report_error(result.data.string.data); +    msgpack_rpc_free_object(result); +    return NIL; +  } +   +  return result; +} + +static uint64_t get_provider_for(char *method) +{ +  uint64_t channel_id = map_get(cstr_t, uint64_t)(registered_providers, method); + +  if (channel_id) { +    return channel_id; +  } + +  // Try to bootstrap if the method is part of a feature +  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 +  f->bootstrap_command = NULL; +  f->channel_id = 0; +  return 0; +} + +static bool can_execute(struct feature *f) +{ +  if (!f->bootstrap_command) { +    return false; +  } + +  char *cmd = *f->bootstrap_command; + +  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) +{ +  vim_err_write((String) {.data = str, .size = strlen(str)}); +  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; +} diff --git a/src/nvim/os/provider.h b/src/nvim/os/provider.h new file mode 100644 index 0000000000..c6f12e02dd --- /dev/null +++ b/src/nvim/os/provider.h @@ -0,0 +1,11 @@ +#ifndef NVIM_OS_PROVIDER_H +#define NVIM_OS_PROVIDER_H + +#include "nvim/api/private/defs.h" + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "os/provider.h.generated.h" +#endif + +#endif  // NVIM_OS_PROVIDER_H + | 
