aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorThiago de Arruda <tpadilha84@gmail.com>2014-09-12 14:01:35 -0300
committerThiago de Arruda <tpadilha84@gmail.com>2014-09-12 14:01:35 -0300
commit6a8932aa588b8631e66255fb24e2776acdd46c8e (patch)
tree2f8b63891e478fdc40fc37cf03b029ebbb7f51a5 /src
parent042aca6eb4d0fe9e6ddf3bcfb96a39838b2633d1 (diff)
parent2a67b847aa2074f688fce9f96e060eeb5ba29435 (diff)
downloadrneovim-6a8932aa588b8631e66255fb24e2776acdd46c8e.tar.gz
rneovim-6a8932aa588b8631e66255fb24e2776acdd46c8e.tar.bz2
rneovim-6a8932aa588b8631e66255fb24e2776acdd46c8e.zip
Merge PR #1130 'Update to the experimental msgpack v5 branch'
Diffstat (limited to 'src')
-rw-r--r--src/nvim/api/buffer.c58
-rw-r--r--src/nvim/api/private/defs.h43
-rw-r--r--src/nvim/api/private/helpers.c125
-rw-r--r--src/nvim/api/private/helpers.h44
-rw-r--r--src/nvim/api/tabpage.c8
-rw-r--r--src/nvim/api/vim.c70
-rw-r--r--src/nvim/api/window.c38
-rw-r--r--src/nvim/eval.c96
-rw-r--r--src/nvim/ex_cmds2.c7
-rw-r--r--src/nvim/globals.h1
-rw-r--r--src/nvim/main.c26
-rw-r--r--src/nvim/map.c18
-rw-r--r--src/nvim/map.h3
-rw-r--r--src/nvim/memory.c14
-rw-r--r--src/nvim/ops.c5
-rw-r--r--src/nvim/option.c6
-rw-r--r--src/nvim/option_defs.h2
-rw-r--r--src/nvim/os/channel.c48
-rw-r--r--src/nvim/os/job.c12
-rw-r--r--src/nvim/os/msgpack_rpc.c101
-rw-r--r--src/nvim/os/msgpack_rpc.h1
-rw-r--r--src/nvim/os/msgpack_rpc_helpers.c229
-rw-r--r--src/nvim/os/msgpack_rpc_helpers.h114
-rw-r--r--src/nvim/os/provider.c173
-rw-r--r--src/nvim/os/wstream.c13
-rw-r--r--src/nvim/os_unix.c2
-rw-r--r--src/nvim/vim.h2
27 files changed, 602 insertions, 657 deletions
diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c
index a268e04559..8355bfe868 100644
--- a/src/nvim/api/buffer.c
+++ b/src/nvim/api/buffer.c
@@ -51,10 +51,10 @@ Integer buffer_get_length(Buffer buffer, Error *err)
String buffer_get_line(Buffer buffer, Integer index, Error *err)
{
String rv = {.size = 0};
- StringArray slice = buffer_get_slice(buffer, index, index, true, true, err);
+ Array slice = buffer_get_slice(buffer, index, index, true, true, err);
if (!err->set && slice.size) {
- rv = slice.items[0];
+ rv = slice.items[0].data.string;
}
free(slice.items);
@@ -70,7 +70,8 @@ String buffer_get_line(Buffer buffer, Integer index, Error *err)
/// @param[out] err Details of an error that may have occurred
void buffer_set_line(Buffer buffer, Integer index, String line, Error *err)
{
- StringArray array = {.items = &line, .size = 1};
+ Object l = STRING_OBJ(line);
+ Array array = {.items = &l, .size = 1};
buffer_set_slice(buffer, index, index, true, true, array, err);
}
@@ -81,7 +82,7 @@ void buffer_set_line(Buffer buffer, Integer index, String line, Error *err)
/// @param[out] err Details of an error that may have occurred
void buffer_del_line(Buffer buffer, Integer index, Error *err)
{
- StringArray array = ARRAY_DICT_INIT;
+ Array array = ARRAY_DICT_INIT;
buffer_set_slice(buffer, index, index, true, true, array, err);
}
@@ -94,14 +95,14 @@ void buffer_del_line(Buffer buffer, Integer index, Error *err)
/// @param include_end True if the slice includes the `end` parameter
/// @param[out] err Details of an error that may have occurred
/// @return An array of lines
-StringArray buffer_get_slice(Buffer buffer,
- Integer start,
- Integer end,
- Boolean include_start,
- Boolean include_end,
- Error *err)
+ArrayOf(String) buffer_get_slice(Buffer buffer,
+ Integer start,
+ Integer end,
+ Boolean include_start,
+ Boolean include_end,
+ Error *err)
{
- StringArray rv = ARRAY_DICT_INIT;
+ Array rv = ARRAY_DICT_INIT;
buf_T *buf = find_buffer_by_handle(buffer, err);
if (!buf) {
@@ -118,7 +119,7 @@ StringArray buffer_get_slice(Buffer buffer,
}
rv.size = (size_t)(end - start);
- rv.items = xcalloc(sizeof(String), rv.size);
+ rv.items = xcalloc(sizeof(Object), rv.size);
for (size_t i = 0; i < rv.size; i++) {
int64_t lnum = start + (int64_t)i;
@@ -129,13 +130,13 @@ StringArray buffer_get_slice(Buffer buffer,
}
const char *bufstr = (char *) ml_get_buf(buf, (linenr_T) lnum, false);
- rv.items[i] = cstr_to_string(bufstr);
+ rv.items[i] = STRING_OBJ(cstr_to_string(bufstr));
}
end:
if (err->set) {
for (size_t i = 0; i < rv.size; i++) {
- free(rv.items[i].data);
+ free(rv.items[i].data.string.data);
}
free(rv.items);
@@ -152,15 +153,15 @@ end:
/// @param end The last line index
/// @param include_start True if the slice includes the `start` parameter
/// @param include_end True if the slice includes the `end` parameter
-/// @param lines An array of lines to use as replacement(A 0-length array
-/// will simply delete the line range)
+/// @param replacement An array of lines to use as replacement(A 0-length
+// array will simply delete the line range)
/// @param[out] err Details of an error that may have occurred
void buffer_set_slice(Buffer buffer,
Integer start,
Integer end,
Boolean include_start,
Boolean include_end,
- StringArray replacement,
+ ArrayOf(String) replacement,
Error *err)
{
buf_T *buf = find_buffer_by_handle(buffer, err);
@@ -184,10 +185,15 @@ void buffer_set_slice(Buffer buffer,
size_t new_len = replacement.size;
size_t old_len = (size_t)(end - start);
ssize_t extra = 0; // lines added to text, can be negative
- char **lines = (new_len != 0) ? xmalloc(new_len * sizeof(char *)) : NULL;
+ char **lines = (new_len != 0) ? xcalloc(new_len, sizeof(char *)) : NULL;
for (size_t i = 0; i < new_len; i++) {
- String l = replacement.items[i];
+ if (replacement.items[i].type != kObjectTypeString) {
+ set_api_error("all items in the replacement array must be strings", err);
+ goto end;
+ }
+
+ String l = replacement.items[i].data.string;
lines[i] = xmemdupz(l.data, l.size);
}
@@ -430,7 +436,10 @@ Boolean buffer_is_valid(Buffer buffer)
/// to the end of the buffer.
/// @param lines An array of lines
/// @param[out] err Details of an error that may have occurred
-void buffer_insert(Buffer buffer, Integer lnum, StringArray lines, Error *err)
+void buffer_insert(Buffer buffer,
+ Integer lnum,
+ ArrayOf(String) lines,
+ Error *err)
{
buffer_set_slice(buffer, lnum, lnum, false, true, lines, err);
}
@@ -441,9 +450,9 @@ void buffer_insert(Buffer buffer, Integer lnum, StringArray lines, Error *err)
/// @param name The mark's name
/// @param[out] err Details of an error that may have occurred
/// @return The (row, col) tuple
-Position buffer_get_mark(Buffer buffer, String name, Error *err)
+ArrayOf(Integer, 2) buffer_get_mark(Buffer buffer, String name, Error *err)
{
- Position rv = POSITION_INIT;
+ Array rv = ARRAY_DICT_INIT;
buf_T *buf = find_buffer_by_handle(buffer, err);
if (!buf) {
@@ -473,8 +482,9 @@ Position buffer_get_mark(Buffer buffer, String name, Error *err)
return rv;
}
- rv.row = posp->lnum;
- rv.col = posp->col;
+ ADD(rv, INTEGER_OBJ(posp->lnum));
+ ADD(rv, INTEGER_OBJ(posp->col));
+
return rv;
}
diff --git a/src/nvim/api/private/defs.h b/src/nvim/api/private/defs.h
index b049412014..2dd229ec5f 100644
--- a/src/nvim/api/private/defs.h
+++ b/src/nvim/api/private/defs.h
@@ -5,17 +5,15 @@
#include <stdbool.h>
#include <string.h>
-#define ARRAY_DICT_INIT {.size = 0, .items = NULL}
+#define ARRAY_DICT_INIT {.size = 0, .capacity = 0, .items = NULL}
#define STRING_INIT {.data = NULL, .size = 0}
#define OBJECT_INIT { .type = kObjectTypeNil }
-#define POSITION_INIT { .row = 0, .col = 0 }
#define REMOTE_TYPE(type) typedef uint64_t type
-#define TYPED_ARRAY_OF(type) \
- typedef struct { \
- type *items; \
- size_t size; \
- } type##Array
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+ #define ArrayOf(...) Array
+ #define DictionaryOf(...) Dictionary
+#endif
// Basic types
typedef struct {
@@ -38,15 +36,6 @@ REMOTE_TYPE(Tabpage);
typedef struct object Object;
-TYPED_ARRAY_OF(Buffer);
-TYPED_ARRAY_OF(Window);
-TYPED_ARRAY_OF(Tabpage);
-TYPED_ARRAY_OF(String);
-
-typedef struct {
- Integer row, col;
-} Position;
-
typedef struct {
Object *items;
size_t size, capacity;
@@ -60,40 +49,30 @@ typedef struct {
} Dictionary;
typedef enum {
+ kObjectTypeBuffer,
+ kObjectTypeWindow,
+ kObjectTypeTabpage,
kObjectTypeNil,
kObjectTypeBoolean,
kObjectTypeInteger,
kObjectTypeFloat,
kObjectTypeString,
- kObjectTypeBuffer,
- kObjectTypeWindow,
- kObjectTypeTabpage,
kObjectTypeArray,
kObjectTypeDictionary,
- kObjectTypePosition,
- kObjectTypeStringArray,
- kObjectTypeBufferArray,
- kObjectTypeWindowArray,
- kObjectTypeTabpageArray,
} ObjectType;
struct object {
ObjectType type;
union {
+ Buffer buffer;
+ Window window;
+ Tabpage tabpage;
Boolean boolean;
Integer integer;
Float floating;
String string;
- Buffer buffer;
- Window window;
- Tabpage tabpage;
Array array;
Dictionary dictionary;
- Position position;
- StringArray stringarray;
- BufferArray bufferarray;
- WindowArray windowarray;
- TabpageArray tabpagearray;
} data;
};
diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c
index f6fb46e1d1..14a820aa1b 100644
--- a/src/nvim/api/private/helpers.c
+++ b/src/nvim/api/private/helpers.c
@@ -6,6 +6,7 @@
#include "nvim/api/private/helpers.h"
#include "nvim/api/private/defs.h"
#include "nvim/api/private/handle.h"
+#include "nvim/os/provider.h"
#include "nvim/ascii.h"
#include "nvim/vim.h"
#include "nvim/buffer.h"
@@ -449,6 +450,130 @@ bool object_to_vim(Object obj, typval_T *tv, Error *err)
return true;
}
+void api_free_string(String value)
+{
+ if (!value.data) {
+ return;
+ }
+
+ free(value.data);
+}
+
+void api_free_object(Object value)
+{
+ switch (value.type) {
+ case kObjectTypeNil:
+ case kObjectTypeBoolean:
+ case kObjectTypeInteger:
+ case kObjectTypeFloat:
+ case kObjectTypeBuffer:
+ case kObjectTypeWindow:
+ case kObjectTypeTabpage:
+ break;
+
+ case kObjectTypeString:
+ api_free_string(value.data.string);
+ break;
+
+ case kObjectTypeArray:
+ api_free_array(value.data.array);
+ break;
+
+ case kObjectTypeDictionary:
+ api_free_dictionary(value.data.dictionary);
+ break;
+
+ default:
+ abort();
+ }
+}
+
+void api_free_array(Array value)
+{
+ for (size_t i = 0; i < value.size; i++) {
+ api_free_object(value.items[i]);
+ }
+
+ free(value.items);
+}
+
+void api_free_dictionary(Dictionary value)
+{
+ for (size_t i = 0; i < value.size; i++) {
+ api_free_string(value.items[i].key);
+ api_free_object(value.items[i].value);
+ }
+
+ free(value.items);
+}
+
+Dictionary api_metadata(void)
+{
+ static Dictionary metadata = ARRAY_DICT_INIT;
+
+ if (!metadata.size) {
+ msgpack_rpc_init_function_metadata(&metadata);
+ init_type_metadata(&metadata);
+ provider_init_feature_metadata(&metadata);
+ }
+
+ return copy_object(DICTIONARY_OBJ(metadata)).data.dictionary;
+}
+
+static void init_type_metadata(Dictionary *metadata)
+{
+ Dictionary types = ARRAY_DICT_INIT;
+
+ Dictionary buffer_metadata = ARRAY_DICT_INIT;
+ PUT(buffer_metadata, "id", INTEGER_OBJ(kObjectTypeBuffer));
+
+ Dictionary window_metadata = ARRAY_DICT_INIT;
+ PUT(window_metadata, "id", INTEGER_OBJ(kObjectTypeWindow));
+
+ Dictionary tabpage_metadata = ARRAY_DICT_INIT;
+ PUT(tabpage_metadata, "id", INTEGER_OBJ(kObjectTypeTabpage));
+
+ PUT(types, "Buffer", DICTIONARY_OBJ(buffer_metadata));
+ PUT(types, "Window", DICTIONARY_OBJ(window_metadata));
+ PUT(types, "Tabpage", DICTIONARY_OBJ(tabpage_metadata));
+
+ PUT(*metadata, "types", DICTIONARY_OBJ(types));
+}
+
+/// Creates a deep clone of an object
+static Object copy_object(Object obj)
+{
+ switch (obj.type) {
+ case kObjectTypeNil:
+ case kObjectTypeBoolean:
+ case kObjectTypeInteger:
+ case kObjectTypeFloat:
+ return obj;
+
+ case kObjectTypeString:
+ return STRING_OBJ(cstr_to_string(obj.data.string.data));
+
+ case kObjectTypeArray: {
+ Array rv = ARRAY_DICT_INIT;
+ for (size_t i = 0; i < obj.data.array.size; i++) {
+ ADD(rv, copy_object(obj.data.array.items[i]));
+ }
+ return ARRAY_OBJ(rv);
+ }
+
+ case kObjectTypeDictionary: {
+ Dictionary rv = ARRAY_DICT_INIT;
+ for (size_t i = 0; i < obj.data.dictionary.size; i++) {
+ KeyValuePair item = obj.data.dictionary.items[i];
+ PUT(rv, item.key.data, copy_object(item.value));
+ }
+ return DICTIONARY_OBJ(rv);
+ }
+ default:
+ abort();
+ }
+}
+
/// Recursion helper for the `vim_to_object`. This uses a pointer table
/// to avoid infinite recursion due to cyclic references
///
diff --git a/src/nvim/api/private/helpers.h b/src/nvim/api/private/helpers.h
index f1b9dc3bc8..f3ecdaacc4 100644
--- a/src/nvim/api/private/helpers.h
+++ b/src/nvim/api/private/helpers.h
@@ -51,36 +51,11 @@
.data.array = a \
})
-#define STRINGARRAY_OBJ(a) ((Object) { \
- .type = kObjectTypeStringArray, \
- .data.stringarray = a \
- })
-
-#define BUFFERARRAY_OBJ(a) ((Object) { \
- .type = kObjectTypeBufferArray, \
- .data.bufferarray = a \
- })
-
-#define WINDOWARRAY_OBJ(a) ((Object) { \
- .type = kObjectTypeWindowArray, \
- .data.windowarray = a \
- })
-
-#define TABPAGEARRAY_OBJ(a) ((Object) { \
- .type = kObjectTypeTabpageArray, \
- .data.tabpagearray = a \
- })
-
#define DICTIONARY_OBJ(d) ((Object) { \
.type = kObjectTypeDictionary, \
.data.dictionary = d \
})
-#define POSITION_OBJ(p) ((Object) { \
- .type = kObjectTypePosition, \
- .data.position = p \
- })
-
#define NIL ((Object) {.type = kObjectTypeNil})
#define PUT(dict, k, v) \
@@ -91,6 +66,25 @@
#define ADD(array, item) \
kv_push(Object, array, item)
+// Helpers used by the generated msgpack-rpc api wrappers
+#define api_init_boolean
+#define api_init_integer
+#define api_init_float
+#define api_init_string = STRING_INIT
+#define api_init_buffer
+#define api_init_window
+#define api_init_tabpage
+#define api_init_object = NIL
+#define api_init_array = ARRAY_DICT_INIT
+#define api_init_dictionary = ARRAY_DICT_INIT
+
+#define api_free_boolean(value)
+#define api_free_integer(value)
+#define api_free_float(value)
+#define api_free_buffer(value)
+#define api_free_window(value)
+#define api_free_tabpage(value)
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "api/private/helpers.h.generated.h"
#endif
diff --git a/src/nvim/api/tabpage.c b/src/nvim/api/tabpage.c
index 535722c087..91020d6850 100644
--- a/src/nvim/api/tabpage.c
+++ b/src/nvim/api/tabpage.c
@@ -13,9 +13,9 @@
/// @param tabpage The tabpage
/// @param[out] err Details of an error that may have occurred
/// @return The number of windows in `tabpage`
-WindowArray tabpage_get_windows(Tabpage tabpage, Error *err)
+ArrayOf(Window) tabpage_get_windows(Tabpage tabpage, Error *err)
{
- WindowArray rv = ARRAY_DICT_INIT;
+ Array rv = ARRAY_DICT_INIT;
tabpage_T *tab = find_tab_by_handle(tabpage, err);
if (!tab) {
@@ -32,14 +32,14 @@ WindowArray tabpage_get_windows(Tabpage tabpage, Error *err)
rv.size++;
}
- rv.items = xmalloc(sizeof(Window) * rv.size);
+ rv.items = xmalloc(sizeof(Object) * rv.size);
size_t i = 0;
FOR_ALL_TAB_WINDOWS(tp, wp) {
if (tp != tab) {
break;
}
- rv.items[i++] = wp->handle;
+ rv.items[i++] = WINDOW_OBJ(wp->handle);
}
return rv;
diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c
index a2c50b4c81..43f2aafdc8 100644
--- a/src/nvim/api/vim.c
+++ b/src/nvim/api/vim.c
@@ -149,9 +149,9 @@ Integer vim_strwidth(String str, Error *err)
/// Returns a list of paths contained in 'runtimepath'
///
/// @return The list of paths
-StringArray vim_list_runtime_paths(void)
+ArrayOf(String) vim_list_runtime_paths(void)
{
- StringArray rv = ARRAY_DICT_INIT;
+ Array rv = ARRAY_DICT_INIT;
uint8_t *rtp = p_rtp;
if (*rtp == NUL) {
@@ -168,19 +168,20 @@ StringArray vim_list_runtime_paths(void)
}
// Allocate memory for the copies
- rv.items = xmalloc(sizeof(String) * rv.size);
+ rv.items = xmalloc(sizeof(Object) * rv.size);
// reset the position
rtp = p_rtp;
// Start copying
for (size_t i = 0; i < rv.size && *rtp != NUL; i++) {
- rv.items[i].data = xmalloc(MAXPATHL);
+ rv.items[i].type = kObjectTypeString;
+ rv.items[i].data.string.data = xmalloc(MAXPATHL);
// Copy the path from 'runtimepath' to rv.items[i]
int length = copy_option_part(&rtp,
- (char_u *)rv.items[i].data,
+ (char_u *)rv.items[i].data.string.data,
MAXPATHL,
",");
assert(length >= 0);
- rv.items[i].size = (size_t)length;
+ rv.items[i].data.string.size = (size_t)length;
}
return rv;
@@ -307,12 +308,22 @@ void vim_err_write(String str)
write_msg(str, true);
}
+/// Higher level error reporting function that ensures all str contents
+/// are written by sending a trailing linefeed to `vim_wrr_write`
+///
+/// @param str The message
+void vim_report_error(String str)
+{
+ vim_err_write(str);
+ vim_err_write((String) {.data = "\n", .size = 1});
+}
+
/// Gets the current list of buffer handles
///
/// @return The number of buffers
-BufferArray vim_get_buffers(void)
+ArrayOf(Buffer) vim_get_buffers(void)
{
- BufferArray rv = ARRAY_DICT_INIT;
+ Array rv = ARRAY_DICT_INIT;
buf_T *b = firstbuf;
while (b) {
@@ -320,12 +331,12 @@ BufferArray vim_get_buffers(void)
b = b->b_next;
}
- rv.items = xmalloc(sizeof(Buffer) * rv.size);
+ rv.items = xmalloc(sizeof(Object) * rv.size);
size_t i = 0;
b = firstbuf;
while (b) {
- rv.items[i++] = b->handle;
+ rv.items[i++] = BUFFER_OBJ(b->handle);
b = b->b_next;
}
@@ -370,9 +381,9 @@ void vim_set_current_buffer(Buffer buffer, Error *err)
/// Gets the current list of window handles
///
/// @return The number of windows
-WindowArray vim_get_windows(void)
+ArrayOf(Window) vim_get_windows(void)
{
- WindowArray rv = ARRAY_DICT_INIT;
+ Array rv = ARRAY_DICT_INIT;
tabpage_T *tp;
win_T *wp;
@@ -380,11 +391,11 @@ WindowArray vim_get_windows(void)
rv.size++;
}
- rv.items = xmalloc(sizeof(Window) * rv.size);
+ rv.items = xmalloc(sizeof(Object) * rv.size);
size_t i = 0;
FOR_ALL_TAB_WINDOWS(tp, wp) {
- rv.items[i++] = wp->handle;
+ rv.items[i++] = WINDOW_OBJ(wp->handle);
}
return rv;
@@ -426,9 +437,9 @@ void vim_set_current_window(Window window, Error *err)
/// Gets the current list of tabpage handles
///
/// @return The number of tab pages
-TabpageArray vim_get_tabpages(void)
+ArrayOf(Tabpage) vim_get_tabpages(void)
{
- TabpageArray rv = ARRAY_DICT_INIT;
+ Array rv = ARRAY_DICT_INIT;
tabpage_T *tp = first_tabpage;
while (tp) {
@@ -436,12 +447,12 @@ TabpageArray vim_get_tabpages(void)
tp = tp->tp_next;
}
- rv.items = xmalloc(sizeof(Tabpage) * rv.size);
+ rv.items = xmalloc(sizeof(Object) * rv.size);
size_t i = 0;
tp = first_tabpage;
while (tp) {
- rv.items[i++] = tp->handle;
+ rv.items[i++] = TABPAGE_OBJ(tp->handle);
tp = tp->tp_next;
}
@@ -501,22 +512,33 @@ 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.
+/// Registers the channel as the provider for `feature`. This fails if
+/// a provider for `feature` is already provided by another channel.
///
/// @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
-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];
- xstrlcpy(buf, method.data, sizeof(buf));
+ xstrlcpy(buf, feature.data, sizeof(buf));
if (!provider_register(buf, channel_id)) {
- set_api_error("Provider already registered", err);
+ set_api_error("Feature doesn't exist", err);
}
}
+Array vim_get_api_info(uint64_t channel_id)
+{
+ Array rv = ARRAY_DICT_INIT;
+
+ assert(channel_id <= INT64_MAX);
+ ADD(rv, INTEGER_OBJ((int64_t)channel_id));
+ ADD(rv, DICTIONARY_OBJ(api_metadata()));
+
+ return rv;
+}
+
/// 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/api/window.c b/src/nvim/api/window.c
index 1ab441bed3..dd256f2b6d 100644
--- a/src/nvim/api/window.c
+++ b/src/nvim/api/window.c
@@ -33,14 +33,14 @@ Buffer window_get_buffer(Window window, Error *err)
/// @param window The window handle
/// @param[out] err Details of an error that may have occurred
/// @return the (row, col) tuple
-Position window_get_cursor(Window window, Error *err)
+ArrayOf(Integer, 2) window_get_cursor(Window window, Error *err)
{
- Position rv = POSITION_INIT;
+ Array rv = ARRAY_DICT_INIT;
win_T *win = find_window_by_handle(window, err);
if (win) {
- rv.row = win->w_cursor.lnum;
- rv.col = win->w_cursor.col;
+ ADD(rv, INTEGER_OBJ(win->w_cursor.lnum));
+ ADD(rv, INTEGER_OBJ(win->w_cursor.col));
}
return rv;
@@ -51,31 +51,35 @@ Position window_get_cursor(Window window, Error *err)
/// @param window The window handle
/// @param pos the (row, col) tuple representing the new position
/// @param[out] err Details of an error that may have occurred
-void window_set_cursor(Window window, Position pos, Error *err)
+void window_set_cursor(Window window, ArrayOf(Integer, 2) pos, Error *err)
{
win_T *win = find_window_by_handle(window, err);
- if (!win) {
+ if (pos.size != 2 || pos.items[0].type != kObjectTypeInteger ||
+ pos.items[1].type != kObjectTypeInteger) {
+ set_api_error("\"pos\" argument must be a [row, col] array", err);
return;
}
- if (pos.row <= 0 || pos.row > win->w_buffer->b_ml.ml_line_count) {
- set_api_error("cursor position outside buffer", err);
+ if (!win) {
return;
}
- if (pos.row > LONG_MAX || pos.row < LONG_MIN) {
- set_api_error("Row value outside range", err);
+ int64_t row = pos.items[0].data.integer;
+ int64_t col = pos.items[1].data.integer;
+
+ if (row <= 0 || row > win->w_buffer->b_ml.ml_line_count) {
+ set_api_error("cursor position outside buffer", err);
return;
}
- if (pos.col > INT_MAX || pos.col < INT_MIN) {
+ if (col > MAXCOL || col < 0) {
set_api_error("Column value outside range", err);
return;
}
- win->w_cursor.lnum = (linenr_T)pos.row;
- win->w_cursor.col = (colnr_T)pos.col;
+ win->w_cursor.lnum = (linenr_T)row;
+ win->w_cursor.col = (colnr_T)col;
win->w_cursor.coladd = 0;
// When column is out of range silently correct it.
check_cursor_col_win(win);
@@ -243,14 +247,14 @@ void window_set_option(Window window, String name, Object value, Error *err)
/// @param window The window handle
/// @param[out] err Details of an error that may have occurred
/// @return The (row, col) tuple with the window position
-Position window_get_position(Window window, Error *err)
+ArrayOf(Integer, 2) window_get_position(Window window, Error *err)
{
- Position rv = POSITION_INIT;
+ Array rv = ARRAY_DICT_INIT;
win_T *win = find_window_by_handle(window, err);
if (win) {
- rv.col = win->w_wincol;
- rv.row = win->w_winrow;
+ ADD(rv, INTEGER_OBJ(win->w_winrow));
+ ADD(rv, INTEGER_OBJ(win->w_wincol));
}
return rv;
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 7c0d1b3d32..d8c2e73150 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -84,7 +84,6 @@
#include "nvim/os/channel.h"
#include "nvim/api/private/helpers.h"
#include "nvim/api/vim.h"
-#include "nvim/os/msgpack_rpc_helpers.h"
#include "nvim/os/dl.h"
#include "nvim/os/provider.h"
@@ -6307,6 +6306,8 @@ static struct fst {
{"acos", 1, 1, f_acos}, /* WJMc */
{"add", 2, 2, f_add},
{"and", 2, 2, f_and},
+ {"api_close", 1, 1, f_api_close},
+ {"api_spawn", 1, 2, f_api_spawn},
{"append", 2, 2, f_append},
{"argc", 0, 0, f_argc},
{"argidx", 0, 0, f_argidx},
@@ -7054,6 +7055,83 @@ static void f_and(typval_T *argvars, typval_T *rettv)
& get_tv_number_chk(&argvars[1], NULL);
}
+// "api_close(prog, argv)" function
+static void f_api_close(typval_T *argvars, typval_T *rettv)
+{
+ rettv->v_type = VAR_NUMBER;
+ rettv->vval.v_number = 0;
+
+ if (check_restricted() || check_secure()) {
+ return;
+ }
+
+ if (argvars[0].v_type != VAR_NUMBER) {
+ // Wrong argument types
+ EMSG(_(e_invarg));
+ return;
+ }
+
+ rettv->vval.v_number = channel_close(argvars[0].vval.v_number);
+}
+
+
+// "api_spawn(prog, argv)" function
+static void f_api_spawn(typval_T *argvars, typval_T *rettv)
+{
+ rettv->v_type = VAR_NUMBER;
+ rettv->vval.v_number = 0;
+
+ if (check_restricted() || check_secure()) {
+ return;
+ }
+
+ if (argvars[0].v_type != VAR_STRING
+ || (argvars[1].v_type != VAR_LIST && argvars[1].v_type != VAR_UNKNOWN)) {
+ // Wrong argument types
+ EMSG(_(e_invarg));
+ return;
+ }
+
+ list_T *args = NULL;
+ int argsl = 0;
+ if (argvars[1].v_type == VAR_LIST) {
+ args = argvars[1].vval.v_list;
+ argsl = args->lv_len;
+ // Assert that all list items are strings
+ for (listitem_T *arg = args->lv_first; arg != NULL; arg = arg->li_next) {
+ if (arg->li_tv.v_type != VAR_STRING) {
+ EMSG(_(e_invarg));
+ return;
+ }
+ }
+ }
+
+ // Allocate extra memory for the argument vector and the NULL pointer
+ int argvl = argsl + 2;
+ char **argv = xmalloc(sizeof(char_u *) * argvl);
+
+ // Copy program name
+ argv[0] = xstrdup((char *)argvars[0].vval.v_string);
+
+ int i = 1;
+ // Copy arguments to the vector
+ if (argsl > 0) {
+ for (listitem_T *arg = args->lv_first; arg != NULL; arg = arg->li_next) {
+ argv[i++] = xstrdup((char *)arg->li_tv.vval.v_string);
+ }
+ }
+
+ // The last item of argv must be NULL
+ argv[i] = NULL;
+ uint64_t channel_id = channel_from_job(argv);
+
+ if (!channel_id) {
+ EMSG(_(e_api_spawn_failed));
+ }
+
+ rettv->vval.v_number = (varnumber_T)channel_id;
+}
+
/*
* "append(lnum, string/list)" function
*/
@@ -12718,14 +12796,18 @@ static void f_send_call(typval_T *argvars, typval_T *rettv)
return;
}
+ if (errored) {
+ vim_report_error(result.data.string);
+ goto end;
+ }
+
Error conversion_error = {.set = false};
- if (errored || !object_to_vim(result, rettv, &conversion_error)) {
- EMSG(errored ?
- result.data.string.data :
- _("Error converting the call result"));
+ if (!object_to_vim(result, rettv, &conversion_error)) {
+ EMSG(_("Error converting the call result"));
}
- msgpack_rpc_free_object(result);
+end:
+ api_free_object(result);
}
// "send_event()" function
@@ -19239,7 +19321,7 @@ static void script_host_eval(char *method, typval_T *argvars, typval_T *rettv)
Error err = {.set = false};
object_to_vim(result, rettv, &err);
- msgpack_rpc_free_object(result);
+ api_free_object(result);
if (err.set) {
EMSG("Error converting value back to vim");
diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c
index f1524ffce9..17905c3046 100644
--- a/src/nvim/ex_cmds2.c
+++ b/src/nvim/ex_cmds2.c
@@ -55,7 +55,6 @@
#include "nvim/os/shell.h"
#include "nvim/os/fs_defs.h"
#include "nvim/os/provider.h"
-#include "nvim/os/msgpack_rpc_helpers.h"
#include "nvim/api/private/helpers.h"
#include "nvim/api/private/defs.h"
@@ -3264,7 +3263,7 @@ static void script_host_execute(char *method, exarg_T *eap)
Object result = provider_call(method, args);
// We don't care about the result, so free it just in case a bad provider
// returned something
- msgpack_rpc_free_object(result);
+ api_free_object(result);
}
free(script);
@@ -3278,7 +3277,7 @@ static void script_host_execute_file(char *method, exarg_T *eap)
Array args = ARRAY_DICT_INIT;
ADD(args, STRING_OBJ(cstr_to_string(buffer)));
Object result = provider_call(method, args);
- msgpack_rpc_free_object(result);
+ api_free_object(result);
}
static void script_host_do_range(char *method, exarg_T *eap)
@@ -3288,6 +3287,6 @@ static void script_host_do_range(char *method, exarg_T *eap)
ADD(args, INTEGER_OBJ(eap->line2));
ADD(args, STRING_OBJ(cstr_to_string((char *)eap->arg)));
Object result = provider_call(method, args);
- msgpack_rpc_free_object(result);
+ api_free_object(result);
}
diff --git a/src/nvim/globals.h b/src/nvim/globals.h
index 3b9a9ae64b..49a4a2f604 100644
--- a/src/nvim/globals.h
+++ b/src/nvim/globals.h
@@ -1098,6 +1098,7 @@ EXTERN garray_T error_ga
* Excluded are errors that are only used once and debugging messages.
*/
EXTERN char_u e_abort[] INIT(= N_("E470: Command aborted"));
+EXTERN char_u e_api_spawn_failed[] INIT(= N_("E903: Could not spawn API job"));
EXTERN char_u e_argreq[] INIT(= N_("E471: Argument required"));
EXTERN char_u e_backslash[] INIT(= N_("E10: \\ should be followed by /, ? or &"));
EXTERN char_u e_cmdwin[] INIT(= N_(
diff --git a/src/nvim/main.c b/src/nvim/main.c
index ebd9f7f3ff..fc1826975a 100644
--- a/src/nvim/main.c
+++ b/src/nvim/main.c
@@ -12,6 +12,8 @@
#include <string.h>
#include <stdbool.h>
+#include <msgpack.h>
+
#include "nvim/ascii.h"
#include "nvim/vim.h"
#include "nvim/main.h"
@@ -57,6 +59,9 @@
#include "nvim/os/input.h"
#include "nvim/os/os.h"
#include "nvim/os/signal.h"
+#include "nvim/os/msgpack_rpc_helpers.h"
+#include "nvim/api/private/defs.h"
+#include "nvim/api/private/helpers.h"
/* Maximum number of commands from + or -c arguments. */
#define MAX_ARG_CMDS 10
@@ -116,9 +121,6 @@ static void init_locale(void);
# endif
#endif /* NO_VIM_MAIN */
-extern const uint8_t msgpack_metadata[];
-extern const unsigned int msgpack_metadata_size;
-
/*
* Different types of error messages.
*/
@@ -1026,12 +1028,18 @@ static void command_line_scan(mparm_T *parmp)
msg_putchar('\n');
msg_didout = FALSE;
mch_exit(0);
- } else if (STRICMP(argv[0] + argv_idx, "api-msgpack-metadata") == 0) {
- for (unsigned int i = 0; i<msgpack_metadata_size; i++) {
- putchar(msgpack_metadata[i]);
+ } else if (STRICMP(argv[0] + argv_idx, "api-info") == 0) {
+ msgpack_sbuffer* b = msgpack_sbuffer_new();
+ msgpack_packer* p = msgpack_packer_new(b, msgpack_sbuffer_write);
+ Object md = DICTIONARY_OBJ(api_metadata());
+ msgpack_rpc_from_object(md, p);
+
+ for (size_t i = 0; i < b->size; i++) {
+ putchar(b->data[i]);
}
+
mch_exit(0);
- } else if (STRICMP(argv[0] + argv_idx, "embedded-mode") == 0) {
+ } else if (STRICMP(argv[0] + argv_idx, "embed") == 0) {
embedded_mode = true;
} else if (STRNICMP(argv[0] + argv_idx, "literal", 7) == 0) {
#if !defined(UNIX)
@@ -2212,8 +2220,8 @@ static void usage(void)
main_msg(_("-W <scriptout>\tWrite all typed commands to file <scriptout>"));
main_msg(_("--startuptime <file>\tWrite startup timing messages to <file>"));
main_msg(_("-i <viminfo>\t\tUse <viminfo> instead of .viminfo"));
- main_msg(_("--api-msgpack-metadata\tDump API metadata information and exit"));
- main_msg(_("--embedded-mode\tUse stdin/stdout as a msgpack-rpc channel. "
+ main_msg(_("--api-info\t\tDump API metadata serialized to msgpack and exit"));
+ main_msg(_("--embed\t\tUse stdin/stdout as a msgpack-rpc channel. "
"This can be used for embedding Neovim into other programs"));
main_msg(_("-h or --help\tPrint Help (this message) and exit"));
main_msg(_("--version\t\tPrint version information and exit"));
diff --git a/src/nvim/map.c b/src/nvim/map.c
index 2e47e8b249..24aa38d67d 100644
--- a/src/nvim/map.c
+++ b/src/nvim/map.c
@@ -1,10 +1,12 @@
#include <stdlib.h>
#include <stdbool.h>
+#include <string.h>
#include "nvim/map.h"
#include "nvim/map_defs.h"
#include "nvim/vim.h"
#include "nvim/memory.h"
+#include "nvim/os/msgpack_rpc.h"
#include "nvim/lib/khash.h"
@@ -87,7 +89,23 @@
return rv; \
}
+static inline khint_t String_hash(String s)
+{
+ khint_t h = 0;
+ for (size_t i = 0; i < s.size && s.data[i]; i++) {
+ h = (h << 5) - h + (uint8_t)s.data[i];
+ }
+ return h;
+}
+
+static inline bool String_eq(String a, String b)
+{
+ return strncmp(a.data, b.data, min(a.size, b.size)) == 0;
+}
+
+
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)
+MAP_IMPL(String, rpc_method_handler_fn, DEFAULT_INITIALIZER)
diff --git a/src/nvim/map.h b/src/nvim/map.h
index 73698cba22..616516c3e1 100644
--- a/src/nvim/map.h
+++ b/src/nvim/map.h
@@ -4,6 +4,8 @@
#include <stdbool.h>
#include "nvim/map_defs.h"
+#include "nvim/api/private/defs.h"
+#include "nvim/os/msgpack_rpc.h"
#define MAP_DECLS(T, U) \
KHASH_DECLARE(T##_##U##_map, T, U) \
@@ -23,6 +25,7 @@ MAP_DECLS(cstr_t, uint64_t)
MAP_DECLS(cstr_t, ptr_t)
MAP_DECLS(ptr_t, ptr_t)
MAP_DECLS(uint64_t, ptr_t)
+MAP_DECLS(String, rpc_method_handler_fn)
#define map_new(T, U) map_##T##_##U##_new
#define map_free(T, U) map_##T##_##U##_free
diff --git a/src/nvim/memory.c b/src/nvim/memory.c
index 50dcca3c84..1c3d6e372c 100644
--- a/src/nvim/memory.c
+++ b/src/nvim/memory.c
@@ -121,7 +121,8 @@ void *xmalloc(size_t size)
void *ret = try_malloc(size);
if (!ret) {
- OUT_STR("Vim: Error: Out of memory.\n");
+ OUT_STR(e_outofmem);
+ out_char('\n');
preserve_exit();
}
return ret;
@@ -147,7 +148,8 @@ void *xcalloc(size_t count, size_t size)
if (!ret && (!count || !size))
ret = calloc(1, 1);
if (!ret) {
- OUT_STR("Vim: Error: Out of memory.\n");
+ OUT_STR(e_outofmem);
+ out_char('\n');
preserve_exit();
}
}
@@ -174,7 +176,8 @@ void *xrealloc(void *ptr, size_t size)
if (!ret && !size)
ret = realloc(ptr, 1);
if (!ret) {
- OUT_STR("Vim: Error: Out of memory.\n");
+ OUT_STR(e_outofmem);
+ out_char('\n');
preserve_exit();
}
}
@@ -194,7 +197,7 @@ void *xmallocz(size_t size)
void *ret;
if (total_size < size) {
- OUT_STR("Vim: Data too large to fit into virtual memory space\n");
+ OUT_STR(_("Vim: Data too large to fit into virtual memory space\n"));
preserve_exit();
}
@@ -316,7 +319,8 @@ char *xstrdup(const char *str)
try_to_free_memory();
ret = strdup(str);
if (!ret) {
- OUT_STR("Vim: Error: Out of memory.\n");
+ OUT_STR(e_outofmem);
+ out_char('\n');
preserve_exit();
}
}
diff --git a/src/nvim/ops.c b/src/nvim/ops.c
index 317d773748..9b98c84be4 100644
--- a/src/nvim/ops.c
+++ b/src/nvim/ops.c
@@ -48,7 +48,6 @@
#include "nvim/undo.h"
#include "nvim/window.h"
#include "nvim/os/provider.h"
-#include "nvim/os/msgpack_rpc_helpers.h"
#include "nvim/api/private/helpers.h"
/*
@@ -5256,7 +5255,7 @@ static void get_clipboard(int name)
return;
err:
- msgpack_rpc_free_object(result);
+ api_free_object(result);
free(reg->y_array);
reg->y_array = NULL;
reg->y_size = 0;
@@ -5287,5 +5286,5 @@ static void set_clipboard(int name)
ADD(args, ARRAY_OBJ(lines));
Object result = provider_call("clipboard_set", args);
- msgpack_rpc_free_object(result);
+ api_free_object(result);
}
diff --git a/src/nvim/option.c b/src/nvim/option.c
index b9becebbf4..b26b6ed4cc 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -974,12 +974,6 @@ static struct vimoption
{"infercase", "inf", P_BOOL|P_VI_DEF,
(char_u *)&p_inf, PV_INF,
{(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,
(char_u *)&p_im, PV_NONE,
{(char_u *)FALSE, (char_u *)0L} SCRIPTID_INIT},
diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h
index 555e9166d6..cd61b6427c 100644
--- a/src/nvim/option_defs.h
+++ b/src/nvim/option_defs.h
@@ -631,8 +631,6 @@ EXTERN int p_write; /* 'write' */
EXTERN int p_wa; /* 'writeany' */
EXTERN int p_wb; /* 'writebackup' */
EXTERN long p_wd; /* 'writedelay' */
-EXTERN char *p_ipy; // 'initpython'
-EXTERN char *p_icpb; // 'initclipboard'
/*
* "indir" values for buffer-local opions.
diff --git a/src/nvim/os/channel.c b/src/nvim/os/channel.c
index bad82bc272..ad8f378dfc 100644
--- a/src/nvim/os/channel.c
+++ b/src/nvim/os/channel.c
@@ -22,8 +22,10 @@
#include "nvim/memory.h"
#include "nvim/os_unix.h"
#include "nvim/message.h"
+#include "nvim/term.h"
#include "nvim/map.h"
#include "nvim/log.h"
+#include "nvim/misc1.h"
#include "nvim/lib/kvec.h"
#define CHANNEL_BUFFER_SIZE 0xffff
@@ -156,7 +158,7 @@ bool channel_send_event(uint64_t id, char *name, Array args)
if (id > 0) {
if (!(channel = pmap_get(uint64_t)(channels, id)) || !channel->enabled) {
- msgpack_rpc_free_array(args);
+ api_free_array(args);
return false;
}
send_event(channel, name, args);
@@ -184,7 +186,7 @@ bool channel_send_call(uint64_t id,
Channel *channel = NULL;
if (!(channel = pmap_get(uint64_t)(channels, id)) || !channel->enabled) {
- msgpack_rpc_free_array(args);
+ api_free_array(args);
return false;
}
@@ -197,7 +199,7 @@ bool channel_send_call(uint64_t id,
"Channel %" PRIu64 " crossed maximum stack depth",
channel->id);
*result = STRING_OBJ(cstr_to_string(buf));
- msgpack_rpc_free_array(args);
+ api_free_array(args);
return false;
}
@@ -265,6 +267,23 @@ void channel_unsubscribe(uint64_t id, char *event)
unsubscribe(channel, event);
}
+/// Closes a channel
+///
+/// @param id The channel id
+/// @return true if successful, false otherwise
+bool channel_close(uint64_t id)
+{
+ Channel *channel;
+
+ if (!(channel = pmap_get(uint64_t)(channels, id)) || !channel->enabled) {
+ return false;
+ }
+
+ channel_kill(channel);
+ channel->enabled = false;
+ return true;
+}
+
/// Creates an API channel from stdin/stdout. This is used when embedding
/// Neovim
static void channel_from_stdio(void)
@@ -331,11 +350,10 @@ static void parse_msgpack(RStream *rstream, void *data, bool eof)
msgpack_unpacked unpacked;
msgpack_unpacked_init(&unpacked);
- UnpackResult result;
+ msgpack_unpack_return result;
// Deserialize everything we can.
- while ((result = msgpack_rpc_unpack(channel->unpacker, &unpacked))
- == kUnpackResultOk) {
+ while ((result = msgpack_unpacker_next(channel->unpacker, &unpacked))) {
if (kv_size(channel->call_stack) && is_rpc_response(&unpacked.data)) {
if (is_valid_rpc_response(&unpacked.data, channel)) {
call_stack_pop(&unpacked.data, channel);
@@ -362,7 +380,13 @@ static void parse_msgpack(RStream *rstream, void *data, bool eof)
}
}
- if (result == kUnpackResultFail) {
+ if (result == MSGPACK_UNPACK_NOMEM_ERROR) {
+ OUT_STR(e_outofmem);
+ out_char('\n');
+ preserve_exit();
+ }
+
+ if (result == MSGPACK_UNPACK_PARSE_ERROR) {
// See src/msgpack/unpack_template.h in msgpack source tree for
// causes for this error(search for 'goto _failed')
//
@@ -441,7 +465,7 @@ static void broadcast_event(char *name, Array args)
});
if (!kv_size(subscribed)) {
- msgpack_rpc_free_array(args);
+ api_free_array(args);
goto end;
}
@@ -489,7 +513,13 @@ static void close_channel(Channel *channel)
pmap_free(cstr_t)(channel->subscribed_events);
kv_destroy(channel->call_stack);
+ channel_kill(channel);
+
+ free(channel);
+}
+static void channel_kill(Channel *channel)
+{
if (channel->is_job) {
if (channel->data.job) {
job_stop(channel->data.job);
@@ -504,8 +534,6 @@ static void close_channel(Channel *channel)
mch_exit(0);
}
}
-
- free(channel);
}
static void close_cb(uv_handle_t *handle)
diff --git a/src/nvim/os/job.c b/src/nvim/os/job.c
index 9deca9de74..9fb2a49e50 100644
--- a/src/nvim/os/job.c
+++ b/src/nvim/os/job.c
@@ -197,6 +197,12 @@ Job *job_start(char **argv,
job->stdio[2].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE;
job->stdio[2].data.stream = (uv_stream_t *)&job->proc_stderr;
+ // Give all handles a reference to the job
+ handle_set_job((uv_handle_t *)&job->proc, job);
+ handle_set_job((uv_handle_t *)&job->proc_stdin, job);
+ handle_set_job((uv_handle_t *)&job->proc_stdout, job);
+ handle_set_job((uv_handle_t *)&job->proc_stderr, job);
+
// Spawn the job
if (uv_spawn(uv_default_loop(), &job->proc, &job->proc_opts) != 0) {
free_job(job);
@@ -204,12 +210,6 @@ Job *job_start(char **argv,
return NULL;
}
- // Give all handles a reference to the job
- handle_set_job((uv_handle_t *)&job->proc, job);
- handle_set_job((uv_handle_t *)&job->proc_stdin, job);
- handle_set_job((uv_handle_t *)&job->proc_stdout, job);
- handle_set_job((uv_handle_t *)&job->proc_stderr, job);
-
job->in = wstream_new(maxmem);
wstream_set_stream(job->in, (uv_stream_t *)&job->proc_stdin);
// Start the readable streams
diff --git a/src/nvim/os/msgpack_rpc.c b/src/nvim/os/msgpack_rpc.c
index c6e2af2f1c..d7e3d33c4b 100644
--- a/src/nvim/os/msgpack_rpc.c
+++ b/src/nvim/os/msgpack_rpc.c
@@ -17,9 +17,6 @@
# include "os/msgpack_rpc.c.generated.h"
#endif
-extern const uint8_t msgpack_metadata[];
-extern const unsigned int msgpack_metadata_size;
-
/// Validates the basic structure of the msgpack-rpc call and fills `res`
/// with the basic response structure.
///
@@ -39,11 +36,6 @@ WBuffer *msgpack_rpc_call(uint64_t channel_id,
return serialize_response(response_id, err, NIL, sbuffer);
}
- if (req->via.array.ptr[2].type == MSGPACK_OBJECT_POSITIVE_INTEGER
- && req->via.array.ptr[2].via.u64 == 0) {
- return serialize_metadata(response_id, channel_id, sbuffer);
- }
-
// dispatch the call
Error error = { .set = false };
Object rv = msgpack_rpc_dispatch(channel_id, req, &error);
@@ -63,42 +55,6 @@ WBuffer *msgpack_rpc_call(uint64_t channel_id,
return serialize_response(response_id, NULL, rv, sbuffer);
}
-/// Try to unpack a msgpack document from the data in the unpacker buffer. This
-/// function is a replacement to msgpack.h `msgpack_unpack_next` that lets
-/// the called know if the unpacking failed due to bad input or due to missing
-/// data.
-///
-/// @param unpacker The unpacker containing the parse buffer
-/// @param result The result which will contain the parsed object
-/// @return kUnpackResultOk : An object was parsed
-/// kUnpackResultFail : Got bad input
-/// kUnpackResultNeedMore: Need more data
-UnpackResult msgpack_rpc_unpack(msgpack_unpacker* unpacker,
- msgpack_unpacked* result)
- FUNC_ATTR_NONNULL_ALL
-{
- if (result->zone != NULL) {
- msgpack_zone_free(result->zone);
- }
-
- int res = msgpack_unpacker_execute(unpacker);
-
- if (res > 0) {
- result->zone = msgpack_unpacker_release_zone(unpacker);
- result->data = msgpack_unpacker_data(unpacker);
- msgpack_unpacker_reset(unpacker);
- return kUnpackResultOk;
- }
-
- if (res < 0) {
- // Since we couldn't parse it, destroy the data consumed so far
- msgpack_unpacker_reset(unpacker);
- return kUnpackResultFail;
- }
-
- return kUnpackResultNeedMore;
-}
-
/// Finishes the msgpack-rpc call with an error message.
///
/// @param msg The error message
@@ -109,12 +65,22 @@ void msgpack_rpc_error(char *msg, msgpack_packer *res)
size_t len = strlen(msg);
// error message
- msgpack_pack_raw(res, len);
- msgpack_pack_raw_body(res, msg, len);
+ msgpack_pack_bin(res, len);
+ msgpack_pack_bin_body(res, msg, len);
// Nil result
msgpack_pack_nil(res);
}
+/// Handler executed when an invalid method name is passed
+Object msgpack_rpc_handle_missing_method(uint64_t channel_id,
+ msgpack_object *req,
+ Error *error)
+{
+ snprintf(error->msg, sizeof(error->msg), "Invalid method name");
+ error->set = true;
+ return NIL;
+}
+
/// Serializes a msgpack-rpc request or notification(id == 0)
WBuffer *serialize_request(uint64_t request_id,
String method,
@@ -132,14 +98,14 @@ WBuffer *serialize_request(uint64_t request_id,
msgpack_pack_uint64(&pac, request_id);
}
- msgpack_pack_raw(&pac, method.size);
- msgpack_pack_raw_body(&pac, method.data, method.size);
+ msgpack_pack_bin(&pac, method.size);
+ msgpack_pack_bin_body(&pac, method.data, method.size);
msgpack_rpc_from_array(args, &pac);
WBuffer *rv = wstream_new_buffer(xmemdup(sbuffer->data, sbuffer->size),
sbuffer->size,
refcount,
free);
- msgpack_rpc_free_array(args);
+ api_free_array(args);
msgpack_sbuffer_clear(sbuffer);
return rv;
}
@@ -160,8 +126,8 @@ WBuffer *serialize_response(uint64_t response_id,
if (err_msg) {
String err = {.size = strlen(err_msg), .data = err_msg};
// error message
- msgpack_pack_raw(&pac, err.size);
- msgpack_pack_raw_body(&pac, err.data, err.size);
+ msgpack_pack_bin(&pac, err.size);
+ msgpack_pack_bin_body(&pac, err.data, err.size);
// Nil result
msgpack_pack_nil(&pac);
} else {
@@ -175,32 +141,7 @@ WBuffer *serialize_response(uint64_t response_id,
sbuffer->size,
1, // responses only go though 1 channel
free);
- msgpack_rpc_free_object(arg);
- msgpack_sbuffer_clear(sbuffer);
- return rv;
-}
-
-WBuffer *serialize_metadata(uint64_t id,
- uint64_t channel_id,
- msgpack_sbuffer *sbuffer)
- FUNC_ATTR_NONNULL_ALL
-{
- msgpack_packer pac;
- msgpack_packer_init(&pac, sbuffer, msgpack_sbuffer_write);
- msgpack_pack_array(&pac, 4);
- msgpack_pack_int(&pac, 1);
- msgpack_pack_uint64(&pac, id);
- // Nil error
- msgpack_pack_nil(&pac);
- // The result is the [channel_id, metadata] array
- msgpack_pack_array(&pac, 2);
- msgpack_pack_uint64(&pac, channel_id);
- msgpack_pack_raw(&pac, msgpack_metadata_size);
- msgpack_pack_raw_body(&pac, msgpack_metadata, msgpack_metadata_size);
- WBuffer *rv = wstream_new_buffer(xmemdup(sbuffer->data, sbuffer->size),
- sbuffer->size,
- 1,
- free);
+ api_free_object(arg);
msgpack_sbuffer_clear(sbuffer);
return rv;
}
@@ -234,9 +175,9 @@ static char *msgpack_rpc_validate(uint64_t *response_id, msgpack_object *req)
return "Message type must be 0";
}
- if (req->via.array.ptr[2].type != MSGPACK_OBJECT_POSITIVE_INTEGER
- && req->via.array.ptr[2].type != MSGPACK_OBJECT_RAW) {
- return "Method must be a positive integer or a string";
+ if (req->via.array.ptr[2].type != MSGPACK_OBJECT_BIN
+ && req->via.array.ptr[2].type != MSGPACK_OBJECT_STR) {
+ return "Method must be a string";
}
if (req->via.array.ptr[3].type != MSGPACK_OBJECT_ARRAY) {
diff --git a/src/nvim/os/msgpack_rpc.h b/src/nvim/os/msgpack_rpc.h
index 35f175d2a0..3476d791ea 100644
--- a/src/nvim/os/msgpack_rpc.h
+++ b/src/nvim/os/msgpack_rpc.h
@@ -25,6 +25,7 @@ typedef Object (*rpc_method_handler_fn)(uint64_t channel_id,
/// Initializes the msgpack-rpc method table
void msgpack_rpc_init(void);
+void msgpack_rpc_init_function_metadata(Dictionary *metadata);
/// Dispatches to the actual API function after basic payload validation by
/// `msgpack_rpc_call`. It is responsible for validating/converting arguments
diff --git a/src/nvim/os/msgpack_rpc_helpers.c b/src/nvim/os/msgpack_rpc_helpers.c
index e2c277abe4..b14de8245c 100644
--- a/src/nvim/os/msgpack_rpc_helpers.c
+++ b/src/nvim/os/msgpack_rpc_helpers.c
@@ -7,61 +7,67 @@
#include "nvim/vim.h"
#include "nvim/memory.h"
-#define REMOTE_FUNCS_IMPL(t, lt) \
- bool msgpack_rpc_to_##lt(msgpack_object *obj, t *arg) \
- { \
- *arg = obj->via.u64; \
- return obj->type == MSGPACK_OBJECT_POSITIVE_INTEGER; \
- } \
- \
- void msgpack_rpc_from_##lt(t result, msgpack_packer *res) \
- { \
- msgpack_pack_uint64(res, result); \
- }
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "os/msgpack_rpc_helpers.c.generated.h"
+#endif
-#define TYPED_ARRAY_IMPL(t, lt) \
- bool msgpack_rpc_to_##lt##array(msgpack_object *obj, t##Array *arg) \
+static msgpack_zone zone;
+static msgpack_sbuffer sbuffer;
+
+#define HANDLE_TYPE_CONVERSION_IMPL(t, lt) \
+ bool msgpack_rpc_to_##lt(msgpack_object *obj, t *arg) \
+ FUNC_ATTR_NONNULL_ALL \
{ \
- if (obj->type != MSGPACK_OBJECT_ARRAY) { \
+ if (obj->type != MSGPACK_OBJECT_EXT \
+ || obj->via.ext.type != kObjectType##t) { \
return false; \
} \
\
- arg->size = obj->via.array.size; \
- arg->items = xcalloc(obj->via.array.size, sizeof(t)); \
+ msgpack_object data; \
+ msgpack_unpack_return ret = msgpack_unpack(obj->via.ext.ptr, \
+ obj->via.ext.size, \
+ NULL, \
+ &zone, \
+ &data); \
\
- for (size_t i = 0; i < obj->via.array.size; i++) { \
- if (!msgpack_rpc_to_##lt(obj->via.array.ptr + i, &arg->items[i])) { \
- return false; \
- } \
+ if (ret != MSGPACK_UNPACK_SUCCESS) { \
+ return false; \
} \
\
+ *arg = data.via.u64; \
return true; \
} \
\
- void msgpack_rpc_from_##lt##array(t##Array result, msgpack_packer *res) \
+ void msgpack_rpc_from_##lt(t o, msgpack_packer *res) \
+ FUNC_ATTR_NONNULL_ARG(2) \
{ \
- msgpack_pack_array(res, result.size); \
- \
- for (size_t i = 0; i < result.size; i++) { \
- msgpack_rpc_from_##lt(result.items[i], res); \
- } \
- } \
- \
- void msgpack_rpc_free_##lt##array(t##Array value) { \
- for (size_t i = 0; i < value.size; i++) { \
- msgpack_rpc_free_##lt(value.items[i]); \
- } \
- \
- free(value.items); \
+ msgpack_packer pac; \
+ msgpack_packer_init(&pac, &sbuffer, msgpack_sbuffer_write); \
+ msgpack_pack_uint64(&pac, o); \
+ msgpack_pack_ext(res, sbuffer.size, kObjectType##t); \
+ msgpack_pack_ext_body(res, sbuffer.data, sbuffer.size); \
+ msgpack_sbuffer_clear(&sbuffer); \
}
+void msgpack_rpc_helpers_init(void)
+{
+ msgpack_zone_init(&zone, 0xfff);
+ msgpack_sbuffer_init(&sbuffer);
+}
+
+HANDLE_TYPE_CONVERSION_IMPL(Buffer, buffer)
+HANDLE_TYPE_CONVERSION_IMPL(Window, window)
+HANDLE_TYPE_CONVERSION_IMPL(Tabpage, tabpage)
+
bool msgpack_rpc_to_boolean(msgpack_object *obj, Boolean *arg)
+ FUNC_ATTR_NONNULL_ALL
{
*arg = obj->via.boolean;
return obj->type == MSGPACK_OBJECT_BOOLEAN;
}
bool msgpack_rpc_to_integer(msgpack_object *obj, Integer *arg)
+ FUNC_ATTR_NONNULL_ALL
{
if (obj->type == MSGPACK_OBJECT_POSITIVE_INTEGER
&& obj->via.u64 <= INT64_MAX) {
@@ -74,23 +80,27 @@ bool msgpack_rpc_to_integer(msgpack_object *obj, Integer *arg)
}
bool msgpack_rpc_to_float(msgpack_object *obj, Float *arg)
+ FUNC_ATTR_NONNULL_ALL
{
*arg = obj->via.dec;
return obj->type == MSGPACK_OBJECT_DOUBLE;
}
bool msgpack_rpc_to_string(msgpack_object *obj, String *arg)
+ FUNC_ATTR_NONNULL_ALL
{
- if (obj->type != MSGPACK_OBJECT_RAW) {
+ if (obj->type == MSGPACK_OBJECT_BIN || obj->type == MSGPACK_OBJECT_STR) {
+ arg->data = xmemdupz(obj->via.bin.ptr, obj->via.bin.size);
+ arg->size = obj->via.bin.size;
+ } else {
return false;
}
- arg->data = xmemdupz(obj->via.raw.ptr, obj->via.raw.size);
- arg->size = obj->via.raw.size;
return true;
}
bool msgpack_rpc_to_object(msgpack_object *obj, Object *arg)
+ FUNC_ATTR_NONNULL_ALL
{
switch (obj->type) {
case MSGPACK_OBJECT_NIL:
@@ -110,7 +120,8 @@ bool msgpack_rpc_to_object(msgpack_object *obj, Object *arg)
arg->type = kObjectTypeFloat;
return msgpack_rpc_to_float(obj, &arg->data.floating);
- case MSGPACK_OBJECT_RAW:
+ case MSGPACK_OBJECT_BIN:
+ case MSGPACK_OBJECT_STR:
arg->type = kObjectTypeString;
return msgpack_rpc_to_string(obj, &arg->data.string);
@@ -122,21 +133,22 @@ bool msgpack_rpc_to_object(msgpack_object *obj, Object *arg)
arg->type = kObjectTypeDictionary;
return msgpack_rpc_to_dictionary(obj, &arg->data.dictionary);
+ case MSGPACK_OBJECT_EXT:
+ switch (obj->via.ext.type) {
+ case kObjectTypeBuffer:
+ return msgpack_rpc_to_buffer(obj, &arg->data.buffer);
+ case kObjectTypeWindow:
+ return msgpack_rpc_to_window(obj, &arg->data.window);
+ case kObjectTypeTabpage:
+ return msgpack_rpc_to_tabpage(obj, &arg->data.tabpage);
+ }
default:
return false;
}
}
-bool msgpack_rpc_to_position(msgpack_object *obj, Position *arg)
-{
- return obj->type == MSGPACK_OBJECT_ARRAY
- && obj->via.array.size == 2
- && msgpack_rpc_to_integer(obj->via.array.ptr, &arg->row)
- && msgpack_rpc_to_integer(obj->via.array.ptr + 1, &arg->col);
-}
-
-
bool msgpack_rpc_to_array(msgpack_object *obj, Array *arg)
+ FUNC_ATTR_NONNULL_ALL
{
if (obj->type != MSGPACK_OBJECT_ARRAY) {
return false;
@@ -155,6 +167,7 @@ bool msgpack_rpc_to_array(msgpack_object *obj, Array *arg)
}
bool msgpack_rpc_to_dictionary(msgpack_object *obj, Dictionary *arg)
+ FUNC_ATTR_NONNULL_ALL
{
if (obj->type != MSGPACK_OBJECT_MAP) {
return false;
@@ -180,6 +193,7 @@ bool msgpack_rpc_to_dictionary(msgpack_object *obj, Dictionary *arg)
}
void msgpack_rpc_from_boolean(Boolean result, msgpack_packer *res)
+ FUNC_ATTR_NONNULL_ARG(2)
{
if (result) {
msgpack_pack_true(res);
@@ -189,22 +203,26 @@ void msgpack_rpc_from_boolean(Boolean result, msgpack_packer *res)
}
void msgpack_rpc_from_integer(Integer result, msgpack_packer *res)
+ FUNC_ATTR_NONNULL_ARG(2)
{
msgpack_pack_int64(res, result);
}
void msgpack_rpc_from_float(Float result, msgpack_packer *res)
+ FUNC_ATTR_NONNULL_ARG(2)
{
msgpack_pack_double(res, result);
}
void msgpack_rpc_from_string(String result, msgpack_packer *res)
+ FUNC_ATTR_NONNULL_ARG(2)
{
- msgpack_pack_raw(res, result.size);
- msgpack_pack_raw_body(res, result.data, result.size);
+ msgpack_pack_bin(res, result.size);
+ msgpack_pack_bin_body(res, result.data, result.size);
}
void msgpack_rpc_from_object(Object result, msgpack_packer *res)
+ FUNC_ATTR_NONNULL_ARG(2)
{
switch (result.type) {
case kObjectTypeNil:
@@ -231,10 +249,6 @@ void msgpack_rpc_from_object(Object result, msgpack_packer *res)
msgpack_rpc_from_array(result.data.array, res);
break;
- case kObjectTypePosition:
- msgpack_rpc_from_position(result.data.position, res);
- break;
-
case kObjectTypeBuffer:
msgpack_rpc_from_buffer(result.data.buffer, res);
break;
@@ -247,36 +261,14 @@ void msgpack_rpc_from_object(Object result, msgpack_packer *res)
msgpack_rpc_from_tabpage(result.data.tabpage, res);
break;
- case kObjectTypeStringArray:
- msgpack_rpc_from_stringarray(result.data.stringarray, res);
- break;
-
- case kObjectTypeBufferArray:
- msgpack_rpc_from_bufferarray(result.data.bufferarray, res);
- break;
-
- case kObjectTypeWindowArray:
- msgpack_rpc_from_windowarray(result.data.windowarray, res);
- break;
-
- case kObjectTypeTabpageArray:
- msgpack_rpc_from_tabpagearray(result.data.tabpagearray, res);
- break;
-
case kObjectTypeDictionary:
msgpack_rpc_from_dictionary(result.data.dictionary, res);
break;
}
}
-void msgpack_rpc_from_position(Position result, msgpack_packer *res)
-{
- msgpack_pack_array(res, 2);;
- msgpack_pack_int64(res, result.row);
- msgpack_pack_int64(res, result.col);
-}
-
void msgpack_rpc_from_array(Array result, msgpack_packer *res)
+ FUNC_ATTR_NONNULL_ARG(2)
{
msgpack_pack_array(res, result.size);
@@ -286,6 +278,7 @@ void msgpack_rpc_from_array(Array result, msgpack_packer *res)
}
void msgpack_rpc_from_dictionary(Dictionary result, msgpack_packer *res)
+ FUNC_ATTR_NONNULL_ARG(2)
{
msgpack_pack_map(res, result.size);
@@ -294,87 +287,3 @@ void msgpack_rpc_from_dictionary(Dictionary result, msgpack_packer *res)
msgpack_rpc_from_object(result.items[i].value, res);
}
}
-
-void msgpack_rpc_free_string(String value)
-{
- if (!value.data) {
- return;
- }
-
- free(value.data);
-}
-
-void msgpack_rpc_free_object(Object value)
-{
- switch (value.type) {
- case kObjectTypeNil:
- case kObjectTypeBoolean:
- case kObjectTypeInteger:
- case kObjectTypeFloat:
- case kObjectTypePosition:
- case kObjectTypeBuffer:
- case kObjectTypeWindow:
- case kObjectTypeTabpage:
- break;
-
- case kObjectTypeString:
- msgpack_rpc_free_string(value.data.string);
- break;
-
- case kObjectTypeArray:
- msgpack_rpc_free_array(value.data.array);
- break;
-
- case kObjectTypeStringArray:
- msgpack_rpc_free_stringarray(value.data.stringarray);
- break;
-
- case kObjectTypeBufferArray:
- msgpack_rpc_free_bufferarray(value.data.bufferarray);
- break;
-
- case kObjectTypeWindowArray:
- msgpack_rpc_free_windowarray(value.data.windowarray);
- break;
-
- case kObjectTypeTabpageArray:
- msgpack_rpc_free_tabpagearray(value.data.tabpagearray);
- break;
-
- case kObjectTypeDictionary:
- msgpack_rpc_free_dictionary(value.data.dictionary);
- break;
-
- default:
- abort();
- }
-}
-
-void msgpack_rpc_free_array(Array value)
-{
- for (uint32_t i = 0; i < value.size; i++) {
- msgpack_rpc_free_object(value.items[i]);
- }
-
- free(value.items);
-}
-
-void msgpack_rpc_free_dictionary(Dictionary value)
-{
- for (uint32_t i = 0; i < value.size; i++) {
- msgpack_rpc_free_string(value.items[i].key);
- msgpack_rpc_free_object(value.items[i].value);
- }
-
- free(value.items);
-}
-
-REMOTE_FUNCS_IMPL(Buffer, buffer)
-REMOTE_FUNCS_IMPL(Window, window)
-REMOTE_FUNCS_IMPL(Tabpage, tabpage)
-
-TYPED_ARRAY_IMPL(Buffer, buffer)
-TYPED_ARRAY_IMPL(Window, window)
-TYPED_ARRAY_IMPL(Tabpage, tabpage)
-TYPED_ARRAY_IMPL(String, string)
-
diff --git a/src/nvim/os/msgpack_rpc_helpers.h b/src/nvim/os/msgpack_rpc_helpers.h
index e3d1e756ef..aede6b1587 100644
--- a/src/nvim/os/msgpack_rpc_helpers.h
+++ b/src/nvim/os/msgpack_rpc_helpers.h
@@ -6,119 +6,11 @@
#include <msgpack.h>
-#include "nvim/func_attr.h"
#include "nvim/api/private/defs.h"
-/// Functions for validating and converting from msgpack types to C types.
-/// These are used by `msgpack_rpc_dispatch` to validate and convert each
-/// argument.
-///
-/// @param obj The object to convert
-/// @param[out] arg A pointer to the avalue
-/// @return true if the conversion succeeded, false otherwise
-bool msgpack_rpc_to_boolean(msgpack_object *obj, Boolean *arg)
- FUNC_ATTR_NONNULL_ALL;
-bool msgpack_rpc_to_integer(msgpack_object *obj, Integer *arg)
- FUNC_ATTR_NONNULL_ALL;
-bool msgpack_rpc_to_float(msgpack_object *obj, Float *arg)
- FUNC_ATTR_NONNULL_ALL;
-bool msgpack_rpc_to_position(msgpack_object *obj, Position *arg)
- FUNC_ATTR_NONNULL_ALL;
-bool msgpack_rpc_to_string(msgpack_object *obj, String *arg)
- FUNC_ATTR_NONNULL_ALL;
-bool msgpack_rpc_to_buffer(msgpack_object *obj, Buffer *arg)
- FUNC_ATTR_NONNULL_ALL;
-bool msgpack_rpc_to_window(msgpack_object *obj, Window *arg)
- FUNC_ATTR_NONNULL_ALL;
-bool msgpack_rpc_to_tabpage(msgpack_object *obj, Tabpage *arg)
- FUNC_ATTR_NONNULL_ALL;
-bool msgpack_rpc_to_object(msgpack_object *obj, Object *arg)
- FUNC_ATTR_NONNULL_ALL;
-bool msgpack_rpc_to_stringarray(msgpack_object *obj, StringArray *arg)
- FUNC_ATTR_NONNULL_ALL;
-bool msgpack_rpc_to_bufferarray(msgpack_object *obj, BufferArray *arg)
- FUNC_ATTR_NONNULL_ALL;
-bool msgpack_rpc_to_windowarray(msgpack_object *obj, WindowArray *arg)
- FUNC_ATTR_NONNULL_ALL;
-bool msgpack_rpc_to_tabpagearray(msgpack_object *obj, TabpageArray *arg)
- FUNC_ATTR_NONNULL_ALL;
-bool msgpack_rpc_to_array(msgpack_object *obj, Array *arg)
- FUNC_ATTR_NONNULL_ALL;
-bool msgpack_rpc_to_dictionary(msgpack_object *obj, Dictionary *arg)
- FUNC_ATTR_NONNULL_ALL;
-
-/// Functions for converting from C types to msgpack types.
-/// These are used by `msgpack_rpc_dispatch` to convert return values
-/// from the API
-///
-/// @param result A pointer to the result
-/// @param res A packer that contains the response
-void msgpack_rpc_from_boolean(Boolean result, msgpack_packer *res)
- FUNC_ATTR_NONNULL_ARG(2);
-void msgpack_rpc_from_integer(Integer result, msgpack_packer *res)
- FUNC_ATTR_NONNULL_ARG(2);
-void msgpack_rpc_from_float(Float result, msgpack_packer *res)
- FUNC_ATTR_NONNULL_ARG(2);
-void msgpack_rpc_from_position(Position result, msgpack_packer *res)
- FUNC_ATTR_NONNULL_ARG(2);
-void msgpack_rpc_from_string(String result, msgpack_packer *res)
- FUNC_ATTR_NONNULL_ARG(2);
-void msgpack_rpc_from_buffer(Buffer result, msgpack_packer *res)
- FUNC_ATTR_NONNULL_ARG(2);
-void msgpack_rpc_from_window(Window result, msgpack_packer *res)
- FUNC_ATTR_NONNULL_ARG(2);
-void msgpack_rpc_from_tabpage(Tabpage result, msgpack_packer *res)
- FUNC_ATTR_NONNULL_ARG(2);
-void msgpack_rpc_from_object(Object result, msgpack_packer *res)
- FUNC_ATTR_NONNULL_ARG(2);
-void msgpack_rpc_from_stringarray(StringArray result, msgpack_packer *res)
- FUNC_ATTR_NONNULL_ARG(2);
-void msgpack_rpc_from_bufferarray(BufferArray result, msgpack_packer *res)
- FUNC_ATTR_NONNULL_ARG(2);
-void msgpack_rpc_from_windowarray(WindowArray result, msgpack_packer *res)
- FUNC_ATTR_NONNULL_ARG(2);
-void msgpack_rpc_from_tabpagearray(TabpageArray result, msgpack_packer *res)
- FUNC_ATTR_NONNULL_ARG(2);
-void msgpack_rpc_from_array(Array result, msgpack_packer *res)
- FUNC_ATTR_NONNULL_ARG(2);
-void msgpack_rpc_from_dictionary(Dictionary result, msgpack_packer *res)
- FUNC_ATTR_NONNULL_ARG(2);
-
-/// Helpers for initializing types that may be freed later
-#define msgpack_rpc_init_boolean
-#define msgpack_rpc_init_integer
-#define msgpack_rpc_init_float
-#define msgpack_rpc_init_position
-#define msgpack_rpc_init_string = STRING_INIT
-#define msgpack_rpc_init_buffer
-#define msgpack_rpc_init_window
-#define msgpack_rpc_init_tabpage
-#define msgpack_rpc_init_object = {.type = kObjectTypeNil}
-#define msgpack_rpc_init_stringarray = ARRAY_DICT_INIT
-#define msgpack_rpc_init_bufferarray = ARRAY_DICT_INIT
-#define msgpack_rpc_init_windowarray = ARRAY_DICT_INIT
-#define msgpack_rpc_init_tabpagearray = ARRAY_DICT_INIT
-#define msgpack_rpc_init_array = ARRAY_DICT_INIT
-#define msgpack_rpc_init_dictionary = ARRAY_DICT_INIT
-
-/// Helpers for freeing arguments/return value
-///
-/// @param value The value to be freed
-#define msgpack_rpc_free_boolean(value)
-#define msgpack_rpc_free_integer(value)
-#define msgpack_rpc_free_float(value)
-#define msgpack_rpc_free_position(value)
-void msgpack_rpc_free_string(String value);
-#define msgpack_rpc_free_buffer(value)
-#define msgpack_rpc_free_window(value)
-#define msgpack_rpc_free_tabpage(value)
-void msgpack_rpc_free_object(Object value);
-void msgpack_rpc_free_stringarray(StringArray value);
-void msgpack_rpc_free_bufferarray(BufferArray value);
-void msgpack_rpc_free_windowarray(WindowArray value);
-void msgpack_rpc_free_tabpagearray(TabpageArray value);
-void msgpack_rpc_free_array(Array value);
-void msgpack_rpc_free_dictionary(Dictionary value);
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "os/msgpack_rpc_helpers.h.generated.h"
+#endif
#endif // NVIM_OS_MSGPACK_RPC_HELPERS_H
diff --git a/src/nvim/os/provider.c b/src/nvim/os/provider.c
index 07e757fe0e..2e7a677793 100644
--- a/src/nvim/os/provider.c
+++ b/src/nvim/os/provider.c
@@ -14,37 +14,34 @@
#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, ...) { \
+#define FEATURE(feature_name, ...) { \
.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;
+typedef struct {
+ char *name, **methods;
size_t name_length;
uint64_t channel_id;
-} features[] = {
+} Feature;
+
+static Feature features[] = {
FEATURE("python",
- &p_ipy,
"python_execute",
"python_execute_file",
"python_do_range",
"python_eval"),
FEATURE("clipboard",
- &p_icpb,
"clipboard_get",
"clipboard_set")
};
-static Map(cstr_t, uint64_t) *registered_providers = NULL;
+static PMap(cstr_t) *registered_providers = NULL;
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "os/provider.c.generated.h"
@@ -53,163 +50,101 @@ static Map(cstr_t, uint64_t) *registered_providers = NULL;
void provider_init(void)
{
- registered_providers = map_new(cstr_t, uint64_t)();
+ registered_providers = pmap_new(cstr_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;
+ Feature *f = find_feature(name);
+ return f != NULL && channel_exists(f->channel_id);
}
-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 (map_has(cstr_t, uint64_t)(registered_providers, method)) {
+ if (!f) {
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;
+ 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);
}
- map_put(cstr_t, uint64_t)(registered_providers, xstrdup(method), channel_id);
- ILOG("Registered channel %" PRIu64 " as the provider for \"%s\"",
+ ILOG("Registered channel %" PRIu64 " as the provider for the \"%s\" feature",
channel_id,
- method);
+ name);
return true;
}
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];
snprintf(buf,
sizeof(buf),
- "Provider for \"%s\" is not available",
+ "Provider for method \"%s\" is not available",
method);
- report_error(buf);
- msgpack_rpc_free_array(args);
+ vim_report_error(cstr_as_string(buf));
+ api_free_array(args);
return NIL;
}
bool error = false;
Object result = NIL;
- channel_send_call(channel_id, method, args, &result, &error);
+ channel_send_call(f->channel_id, method, args, &result, &error);
if (error) {
- report_error(result.data.string.data);
- msgpack_rpc_free_object(result);
+ vim_report_error(result.data.string);
+ api_free_object(result);
return NIL;
}
return result;
}
-static uint64_t get_provider_for(char *method)
+void provider_init_feature_metadata(Dictionary *metadata)
{
- uint64_t channel_id = map_get(cstr_t, uint64_t)(registered_providers, method);
+ Dictionary md = ARRAY_DICT_INIT;
- 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
- if (f) {
- 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 (size_t i = 0; i < FEATURE_COUNT; i++) {
+ Array methods = ARRAY_DICT_INIT;
+ Feature *f = &features[i];
- for (m = f->methods[i = 0]; m; m = f->methods[++i]) {
- if (!STRCMP(method, m)) {
- return true;
+ 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));
}
- return false;
+ PUT(*metadata, "features", DICTIONARY_OBJ(md));
}
-
-static struct feature *get_feature_for(char *method)
+static Feature * find_feature(char *name)
{
for (size_t i = 0; i < FEATURE_COUNT; i++) {
- struct feature *f = &features[i];
- if (feature_has_method(f, method)) {
+ Feature *f = &features[i];
+ if (!STRICMP(name, f->name)) {
return f;
}
}
diff --git a/src/nvim/os/wstream.c b/src/nvim/os/wstream.c
index 44463c7c88..00a53d1628 100644
--- a/src/nvim/os/wstream.c
+++ b/src/nvim/os/wstream.c
@@ -72,12 +72,12 @@ WStream * wstream_new(size_t maxmem)
/// @param wstream The `WStream` instance
void wstream_free(WStream *wstream) {
if (!wstream->pending_reqs) {
- handle_set_wstream((uv_handle_t *)wstream->stream, NULL);
if (wstream->free_handle) {
uv_close((uv_handle_t *)wstream->stream, close_cb);
+ } else {
+ handle_set_wstream((uv_handle_t *)wstream->stream, NULL);
+ free(wstream);
}
-
- free(wstream);
} else {
wstream->freed = true;
}
@@ -238,12 +238,7 @@ static void release_wbuffer(WBuffer *buffer)
static void close_cb(uv_handle_t *handle)
{
- WStream *wstream = handle_get_wstream(handle);
-
- if (wstream) {
- free(wstream);
- }
-
+ free(handle_get_wstream(handle));
free(handle->data);
free(handle);
}
diff --git a/src/nvim/os_unix.c b/src/nvim/os_unix.c
index 3fa8b803b2..6c79fbd479 100644
--- a/src/nvim/os_unix.c
+++ b/src/nvim/os_unix.c
@@ -55,6 +55,7 @@
#include "nvim/os/signal.h"
#include "nvim/os/job.h"
#include "nvim/os/msgpack_rpc.h"
+#include "nvim/os/msgpack_rpc_helpers.h"
#if defined(HAVE_SYS_IOCTL_H)
# include <sys/ioctl.h>
@@ -166,6 +167,7 @@ void mch_init(void)
#endif
msgpack_rpc_init();
+ msgpack_rpc_helpers_init();
event_init();
}
diff --git a/src/nvim/vim.h b/src/nvim/vim.h
index 6479aeaafb..3ef291ef7c 100644
--- a/src/nvim/vim.h
+++ b/src/nvim/vim.h
@@ -8,6 +8,8 @@
#ifndef NVIM_VIM_H
# define NVIM_VIM_H
+#define min(X, Y) (X < Y ? X : Y)
+
#include "nvim/types.h"
#include "nvim/pos.h" // for linenr_T, MAXCOL, etc...