aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/os/provider.c
blob: 2e7a67779300b197b595443f5edf04f058dc7115 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
#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"

#define FEATURE_COUNT (sizeof(features) / sizeof(features[0]))

#define FEATURE(feature_name, ...) {            \
  .name = feature_name,                                                     \
  .channel_id = 0,                                                          \
  .methods = (char *[]){__VA_ARGS__, NULL}                                  \
}

typedef struct {
  char *name, **methods;
  size_t name_length;
  uint64_t channel_id;
} Feature;

static Feature features[] = {
  FEATURE("python",
          "python_execute",
          "python_execute_file",
          "python_do_range",
          "python_eval"),

  FEATURE("clipboard",
          "clipboard_get",
          "clipboard_set")
};

static PMap(cstr_t) *registered_providers = NULL;

#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "os/provider.c.generated.h"
#endif


void provider_init(void)
{
  registered_providers = pmap_new(cstr_t)();
}

bool provider_has_feature(char *name)
{
  Feature *f = find_feature(name);
  return f != NULL && channel_exists(f->channel_id);
}

bool provider_register(char *name, uint64_t channel_id)
{
  Feature *f = find_feature(name);

  if (!f) {
    return false;
  }

  if (f->channel_id && channel_exists(f->channel_id)) {
    ILOG("Feature \"%s\" is already provided by another channel"
         "(will be replaced)", name);
  }

  DLOG("Registering provider for \"%s\"", name);
  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,
       name);

  return true;
}

Object provider_call(char *method, Array args)
{
  Feature *f = pmap_get(cstr_t)(registered_providers, method);

  if (!f || !channel_exists(f->channel_id)) {
    char buf[256];
    snprintf(buf,
             sizeof(buf),
             "Provider for method \"%s\" is not available",
             method);
    vim_report_error(cstr_as_string(buf));
    api_free_array(args);
    return NIL;
  }

  bool error = false;
  Object result = NIL;
  channel_send_call(f->channel_id, method, args, &result, &error);

  if (error) {
    vim_report_error(result.data.string);
    api_free_object(result);
    return NIL;
  }
  
  return result;
}

void provider_init_feature_metadata(Dictionary *metadata)
{
  Dictionary md = ARRAY_DICT_INIT;

  for (size_t i = 0; i < FEATURE_COUNT; i++) {
    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(md, f->name, ARRAY_OBJ(methods));
  }

  PUT(*metadata, "features", DICTIONARY_OBJ(md));
}

static Feature * find_feature(char *name)
{
  for (size_t i = 0; i < FEATURE_COUNT; i++) {
    Feature *f = &features[i];
    if (!STRICMP(name, f->name)) {
      return f;
    }
  }

  return NULL;
}