aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/api
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim/api')
-rw-r--r--src/nvim/api/buffer.c371
-rw-r--r--src/nvim/api/keysets.lua52
-rw-r--r--src/nvim/api/private/defs.h10
-rw-r--r--src/nvim/api/private/helpers.c461
-rw-r--r--src/nvim/api/private/helpers.h6
-rw-r--r--src/nvim/api/vim.c27
-rw-r--r--src/nvim/api/window.c2
7 files changed, 430 insertions, 499 deletions
diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c
index 0ef2776263..34d7f6e32d 100644
--- a/src/nvim/api/buffer.c
+++ b/src/nvim/api/buffer.c
@@ -858,7 +858,7 @@ ArrayOf(Dictionary) nvim_buf_get_keymap(Buffer buffer, String mode, Error *err)
/// @see |nvim_set_keymap()|
///
/// @param buffer Buffer handle, or 0 for current buffer
-void nvim_buf_set_keymap(Buffer buffer, String mode, String lhs, String rhs, Dictionary opts,
+void nvim_buf_set_keymap(Buffer buffer, String mode, String lhs, String rhs, Dict(keymap) *opts,
Error *err)
FUNC_API_SINCE(6)
{
@@ -874,8 +874,7 @@ void nvim_buf_del_keymap(Buffer buffer, String mode, String lhs, Error *err)
FUNC_API_SINCE(6)
{
String rhs = { .data = "", .size = 0 };
- Dictionary opts = ARRAY_DICT_INIT;
- modify_keymap(buffer, true, mode, lhs, rhs, opts, err);
+ modify_keymap(buffer, true, mode, lhs, rhs, NULL, err);
}
/// Gets a map of buffer-local |user-commands|.
@@ -885,22 +884,13 @@ void nvim_buf_del_keymap(Buffer buffer, String mode, String lhs, Error *err)
/// @param[out] err Error details, if any.
///
/// @returns Map of maps describing commands.
-Dictionary nvim_buf_get_commands(Buffer buffer, Dictionary opts, Error *err)
+Dictionary nvim_buf_get_commands(Buffer buffer, Dict(get_commands) *opts, Error *err)
FUNC_API_SINCE(4)
{
bool global = (buffer == -1);
- bool builtin = false;
-
- for (size_t i = 0; i < opts.size; i++) {
- String k = opts.items[i].key;
- Object v = opts.items[i].value;
- if (!strequal("builtin", k.data)) {
- api_set_error(err, kErrorTypeValidation, "unexpected key: %s", k.data);
- return (Dictionary)ARRAY_DICT_INIT;
- }
- if (strequal("builtin", k.data)) {
- builtin = v.data.boolean;
- }
+ bool builtin = api_object_to_bool(opts->builtin, "builtin", false, err);
+ if (ERROR_SET(err)) {
+ return (Dictionary)ARRAY_DICT_INIT;
}
if (global) {
@@ -1485,7 +1475,7 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e
/// @param[out] err Error details, if any
/// @return Id of the created/updated extmark
Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer col,
- Dictionary opts, Error *err)
+ Dict(set_extmark) *opts, Error *err)
FUNC_API_SINCE(7)
{
buf_T *buf = find_buffer_by_handle(buffer, err);
@@ -1498,211 +1488,174 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
return 0;
}
- bool ephemeral = false;
uint64_t id = 0;
+ if (opts->id.type == kObjectTypeInteger && opts->id.data.integer > 0) {
+ id = (uint64_t)opts->id.data.integer;
+ } else if (HAS_KEY(opts->id)) {
+ api_set_error(err, kErrorTypeValidation, "id is not a positive integer");
+ goto error;
+ }
+
int line2 = -1;
- Decoration decor = DECORATION_INIT;
+ if (opts->end_line.type == kObjectTypeInteger) {
+ Integer val = opts->end_line.data.integer;
+ if (val < 0 || val > buf->b_ml.ml_line_count) {
+ api_set_error(err, kErrorTypeValidation, "end_line value outside range");
+ goto error;
+ } else {
+ line2 = (int)val;
+ }
+ } else if (HAS_KEY(opts->end_line)) {
+ api_set_error(err, kErrorTypeValidation, "end_line is not an integer");
+ goto error;
+ }
+
colnr_T col2 = -1;
+ if (opts->end_col.type == kObjectTypeInteger) {
+ Integer val = opts->end_col.data.integer;
+ if (val < 0 || val > MAXCOL) {
+ api_set_error(err, kErrorTypeValidation, "end_col value outside range");
+ goto error;
+ } else {
+ col2 = (int)val;
+ }
+ } else if (HAS_KEY(opts->end_col)) {
+ api_set_error(err, kErrorTypeValidation, "end_col is not an integer");
+ goto error;
+ }
- bool right_gravity = true;
- bool end_right_gravity = false;
- bool end_gravity_set = false;
+ Decoration decor = DECORATION_INIT;
- VirtLines virt_lines = KV_INITIAL_VALUE;
- bool virt_lines_above = false;
- bool virt_lines_leftcol = false;
+ if (HAS_KEY(opts->hl_group)) {
+ decor.hl_id = object_to_hl_id(opts->hl_group, "hl_group", err);
+ if (ERROR_SET(err)) {
+ goto error;
+ }
+ }
- for (size_t i = 0; i < opts.size; i++) {
- String k = opts.items[i].key;
- Object *v = &opts.items[i].value;
- if (strequal("id", k.data)) {
- if (v->type != kObjectTypeInteger || v->data.integer <= 0) {
- api_set_error(err, kErrorTypeValidation,
- "id is not a positive integer");
- goto error;
- }
+ if (opts->virt_text.type == kObjectTypeArray) {
+ decor.virt_text = parse_virt_text(opts->virt_text.data.array, err,
+ &decor.virt_text_width);
+ if (ERROR_SET(err)) {
+ goto error;
+ }
+ } else if (HAS_KEY(opts->virt_text)) {
+ api_set_error(err, kErrorTypeValidation, "virt_text is not an Array");
+ goto error;
+ }
+
+ if (opts->virt_text_pos.type == kObjectTypeString) {
+ String str = opts->virt_text_pos.data.string;
+ if (strequal("eol", str.data)) {
+ decor.virt_text_pos = kVTEndOfLine;
+ } else if (strequal("overlay", str.data)) {
+ decor.virt_text_pos = kVTOverlay;
+ } else if (strequal("right_align", str.data)) {
+ decor.virt_text_pos = kVTRightAlign;
+ } else {
+ api_set_error(err, kErrorTypeValidation, "virt_text_pos: invalid value");
+ goto error;
+ }
+ } else if (HAS_KEY(opts->virt_text_pos)) {
+ api_set_error(err, kErrorTypeValidation, "virt_text_pos is not a String");
+ goto error;
+ }
- id = (uint64_t)v->data.integer;
- } else if (strequal("end_line", k.data)) {
- if (v->type != kObjectTypeInteger) {
- api_set_error(err, kErrorTypeValidation,
- "end_line is not an integer");
- goto error;
- }
- if (v->data.integer < 0 || v->data.integer > buf->b_ml.ml_line_count) {
- api_set_error(err, kErrorTypeValidation,
- "end_line value outside range");
- goto error;
- }
+ if (opts->virt_text_win_col.type == kObjectTypeInteger) {
+ decor.col = (int)opts->virt_text_win_col.data.integer;
+ decor.virt_text_pos = kVTWinCol;
+ } else if (HAS_KEY(opts->virt_text_win_col)) {
+ api_set_error(err, kErrorTypeValidation,
+ "virt_text_win_col is not a Number of the correct size");
+ goto error;
+ }
- line2 = (int)v->data.integer;
- } else if (strequal("end_col", k.data)) {
- if (v->type != kObjectTypeInteger) {
- api_set_error(err, kErrorTypeValidation,
- "end_col is not an integer");
- goto error;
- }
- if (v->data.integer < 0 || v->data.integer > MAXCOL) {
- api_set_error(err, kErrorTypeValidation,
- "end_col value outside range");
- goto error;
- }
+#define OPTION_TO_BOOL(target, name, val) \
+ target = api_object_to_bool(opts-> name, #name, val, err); \
+ if (ERROR_SET(err)) { \
+ goto error; \
+ }
- col2 = (colnr_T)v->data.integer;
- } else if (strequal("hl_group", k.data)) {
- String hl_group;
- switch (v->type) {
- case kObjectTypeString:
- hl_group = v->data.string;
- decor.hl_id = syn_check_group((char_u *)(hl_group.data),
- (int)hl_group.size);
- break;
- case kObjectTypeInteger:
- decor.hl_id = (int)v->data.integer;
- break;
- default:
- api_set_error(err, kErrorTypeValidation,
- "hl_group is not valid.");
- goto error;
- }
- } else if (strequal("virt_text", k.data)) {
- if (v->type != kObjectTypeArray) {
- api_set_error(err, kErrorTypeValidation,
- "virt_text is not an Array");
- goto error;
- }
- decor.virt_text = parse_virt_text(v->data.array, err,
- &decor.virt_text_width);
- if (ERROR_SET(err)) {
- goto error;
- }
- } else if (strequal("virt_text_pos", k.data)) {
- if (v->type != kObjectTypeString) {
- api_set_error(err, kErrorTypeValidation,
- "virt_text_pos is not a String");
- goto error;
- }
- String str = v->data.string;
- if (strequal("eol", str.data)) {
- decor.virt_text_pos = kVTEndOfLine;
- } else if (strequal("overlay", str.data)) {
- decor.virt_text_pos = kVTOverlay;
- } else if (strequal("right_align", str.data)) {
- decor.virt_text_pos = kVTRightAlign;
- } else {
- api_set_error(err, kErrorTypeValidation,
- "virt_text_pos: invalid value");
- goto error;
- }
- } else if (strequal("virt_text_win_col", k.data)) {
- if (v->type != kObjectTypeInteger) {
- api_set_error(err, kErrorTypeValidation,
- "virt_text_win_col is not a Number of the correct size");
- goto error;
- }
+ OPTION_TO_BOOL(decor.virt_text_hide, virt_text_hide, false);
+ OPTION_TO_BOOL(decor.hl_eol, hl_eol, false);
+
+ if (opts->hl_mode.type == kObjectTypeString) {
+ String str = opts->hl_mode.data.string;
+ if (strequal("replace", str.data)) {
+ decor.hl_mode = kHlModeReplace;
+ } else if (strequal("combine", str.data)) {
+ decor.hl_mode = kHlModeCombine;
+ } else if (strequal("blend", str.data)) {
+ decor.hl_mode = kHlModeBlend;
+ } else {
+ api_set_error(err, kErrorTypeValidation,
+ "virt_text_pos: invalid value");
+ goto error;
+ }
+ } else if (HAS_KEY(opts->hl_mode)) {
+ api_set_error(err, kErrorTypeValidation, "hl_mode is not a String");
+ goto error;
+ }
- decor.col = (int)v->data.integer;
- decor.virt_text_pos = kVTWinCol;
- } else if (strequal("virt_text_hide", k.data)) {
- decor.virt_text_hide = api_object_to_bool(*v,
- "virt_text_hide", false, err);
- if (ERROR_SET(err)) {
- goto error;
- }
- } else if (strequal("virt_lines", k.data)) {
- if (v->type != kObjectTypeArray) {
- api_set_error(err, kErrorTypeValidation,
- "virt_lines is not an Array");
- goto error;
- }
- Array a = v->data.array;
- for (size_t j = 0; j < a.size; j++) {
- if (a.items[j].type != kObjectTypeArray) {
- api_set_error(err, kErrorTypeValidation,
- "virt_text_line item is not an Array");
- goto error;
- }
- int dummig;
- VirtText jtem = parse_virt_text(a.items[j].data.array, err, &dummig);
- kv_push(virt_lines, jtem);
- if (ERROR_SET(err)) {
- goto error;
- }
- }
- } else if (strequal("virt_lines_above", k.data)) {
- virt_lines_above = api_object_to_bool(*v, "virt_lines_above", false, err);
- if (ERROR_SET(err)) {
- goto error;
- }
- } else if (strequal("virt_lines_leftcol", k.data)) {
- virt_lines_leftcol = api_object_to_bool(*v, "virt_lines_leftcol", false, err);
- if (ERROR_SET(err)) {
- goto error;
- }
- } else if (strequal("hl_eol", k.data)) {
- decor.hl_eol = api_object_to_bool(*v, "hl_eol", false, err);
- if (ERROR_SET(err)) {
- goto error;
- }
- } else if (strequal("hl_mode", k.data)) {
- if (v->type != kObjectTypeString) {
- api_set_error(err, kErrorTypeValidation,
- "hl_mode is not a String");
- goto error;
- }
- String str = v->data.string;
- if (strequal("replace", str.data)) {
- decor.hl_mode = kHlModeReplace;
- } else if (strequal("combine", str.data)) {
- decor.hl_mode = kHlModeCombine;
- } else if (strequal("blend", str.data)) {
- decor.hl_mode = kHlModeBlend;
- } else {
- api_set_error(err, kErrorTypeValidation,
- "virt_text_pos: invalid value");
+ VirtLines virt_lines = KV_INITIAL_VALUE;
+ bool virt_lines_above = false;
+ bool virt_lines_leftcol = false;
+
+ if (opts->virt_lines.type == kObjectTypeArray) {
+ Array a = opts->virt_lines.data.array;
+ for (size_t j = 0; j < a.size; j++) {
+ if (a.items[j].type != kObjectTypeArray) {
+ api_set_error(err, kErrorTypeValidation, "virt_text_line item is not an Array");
goto error;
}
- } else if (strequal("ephemeral", k.data)) {
- ephemeral = api_object_to_bool(*v, "ephemeral", false, err);
+ int dummig;
+ VirtText jtem = parse_virt_text(a.items[j].data.array, err, &dummig);
+ kv_push(virt_lines, jtem);
if (ERROR_SET(err)) {
goto error;
}
- } else if (strequal("priority", k.data)) {
- if (v->type != kObjectTypeInteger) {
- api_set_error(err, kErrorTypeValidation,
- "priority is not a Number of the correct size");
- goto error;
- }
+ }
+ } else if (HAS_KEY(opts->virt_lines)) {
+ api_set_error(err, kErrorTypeValidation, "virt_lines is not an Array");
+ goto error;
+ }
- if (v->data.integer < 0 || v->data.integer > UINT16_MAX) {
- api_set_error(err, kErrorTypeValidation,
- "priority is not a valid value");
- goto error;
- }
- decor.priority = (DecorPriority)v->data.integer;
- } else if (strequal("right_gravity", k.data)) {
- if (v->type != kObjectTypeBoolean) {
- api_set_error(err, kErrorTypeValidation,
- "right_gravity must be a boolean");
- goto error;
- }
- right_gravity = v->data.boolean;
- } else if (strequal("end_right_gravity", k.data)) {
- if (v->type != kObjectTypeBoolean) {
- api_set_error(err, kErrorTypeValidation,
- "end_right_gravity must be a boolean");
- goto error;
- }
- end_right_gravity = v->data.boolean;
- end_gravity_set = true;
- } else {
- api_set_error(err, kErrorTypeValidation, "unexpected key: %s", k.data);
+ OPTION_TO_BOOL(virt_lines_above, virt_lines_above, false);
+ OPTION_TO_BOOL(virt_lines_leftcol, virt_lines_leftcol, false);
+
+ if (opts->priority.type == kObjectTypeInteger) {
+ Integer val = opts->priority.data.integer;
+
+ if (val < 0 || val > UINT16_MAX) {
+ api_set_error(err, kErrorTypeValidation, "priority is not a valid value");
goto error;
}
+ decor.priority = (DecorPriority)val;
+ } else if (HAS_KEY(opts->priority)) {
+ api_set_error(err, kErrorTypeValidation, "priority is not a Number of the correct size");
+ goto error;
}
+ bool right_gravity = true;
+ OPTION_TO_BOOL(right_gravity, right_gravity, true);
+
+ // Only error out if they try to set end_right_gravity without
+ // setting end_col or end_line
+ if (line2 == -1 && col2 == -1 && HAS_KEY(opts->end_right_gravity)) {
+ api_set_error(err, kErrorTypeValidation,
+ "cannot set end_right_gravity without setting end_line or end_col");
+ goto error;
+ }
+
+ bool end_right_gravity = false;
+ OPTION_TO_BOOL(end_right_gravity, end_right_gravity, false);
+
size_t len = 0;
+
+ bool ephemeral = false;
+ OPTION_TO_BOOL(ephemeral, ephemeral, false);
+
if (line < 0 || line > buf->b_ml.ml_line_count) {
api_set_error(err, kErrorTypeValidation, "line value outside range");
return 0;
@@ -1717,15 +1670,6 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
return 0;
}
-
- // Only error out if they try to set end_right_gravity without
- // setting end_col or end_line
- if (line2 == -1 && col2 == -1 && end_gravity_set) {
- api_set_error(err, kErrorTypeValidation,
- "cannot set end_right_gravity "
- "without setting end_line or end_col");
- }
-
if (col2 >= 0) {
if (line2 >= 0 && line2 < buf->b_ml.ml_line_count) {
len = ephemeral ? MAXCOL : STRLEN(ml_get_buf(buf, (linenr_T)line2 + 1, false));
@@ -1744,15 +1688,6 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
col2 = 0;
}
- if (decor.virt_text_pos == kVTRightAlign) {
- decor.col = 0;
- for (size_t i = 0; i < kv_size(decor.virt_text); i++) {
- decor.col
- += (int)mb_string2cells((char_u *)kv_A(decor.virt_text, i).text);
- }
- }
-
-
Decoration *d = NULL;
if (ephemeral) {
diff --git a/src/nvim/api/keysets.lua b/src/nvim/api/keysets.lua
new file mode 100644
index 0000000000..76ce9e15ea
--- /dev/null
+++ b/src/nvim/api/keysets.lua
@@ -0,0 +1,52 @@
+return {
+ context = {
+ "types";
+ };
+ set_extmark = {
+ "id";
+ "end_line";
+ "end_col";
+ "hl_group";
+ "virt_text";
+ "virt_text_pos";
+ "virt_text_win_col";
+ "virt_text_hide";
+ "hl_eol";
+ "hl_mode";
+ "ephemeral";
+ "priority";
+ "right_gravity";
+ "end_right_gravity";
+ "virt_lines";
+ "virt_lines_above";
+ "virt_lines_leftcol";
+ };
+ keymap = {
+ "noremap";
+ "nowait";
+ "silent";
+ "script";
+ "expr";
+ "unique";
+ };
+ get_commands = {
+ "builtin";
+ };
+ float_config = {
+ "row";
+ "col";
+ "width";
+ "height";
+ "anchor";
+ "relative";
+ "win";
+ "bufpos";
+ "external";
+ "focusable";
+ "zindex";
+ "border";
+ "style";
+ "noautocmd";
+ };
+}
+
diff --git a/src/nvim/api/private/defs.h b/src/nvim/api/private/defs.h
index f0d48bf145..8346e01558 100644
--- a/src/nvim/api/private/defs.h
+++ b/src/nvim/api/private/defs.h
@@ -19,6 +19,7 @@
#ifdef INCLUDE_GENERATED_DECLARATIONS
# define ArrayOf(...) Array
# define DictionaryOf(...) Dictionary
+# define Dict(name) KeyDict_##name
#endif
// Basic types
@@ -129,5 +130,14 @@ struct key_value_pair {
Object value;
};
+typedef Object *(*field_hash)(void *retval, const char *str, size_t len);
+typedef struct {
+ char *str;
+ size_t ptr_off;
+} KeySetLink;
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "keysets_defs.generated.h"
+#endif
#endif // NVIM_API_PRIVATE_DEFS_H
diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c
index 193f1dd572..24fac0a916 100644
--- a/src/nvim/api/private/helpers.c
+++ b/src/nvim/api/private/helpers.c
@@ -6,6 +6,7 @@
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
+#include <stddef.h>
#include "nvim/api/private/defs.h"
#include "nvim/api/private/helpers.h"
@@ -814,7 +815,7 @@ Array string_to_array(const String input, bool crlf)
/// buffer, or -1 to signify global behavior ("all buffers")
/// @param is_unmap When true, removes the mapping that matches {lhs}.
void modify_keymap(Buffer buffer, bool is_unmap, String mode, String lhs, String rhs,
- Dictionary opts, Error *err)
+ Dict(keymap) *opts, Error *err)
{
char *err_msg = NULL; // the error message to report, if any
char *err_arg = NULL; // argument for the error message format string
@@ -833,10 +834,21 @@ void modify_keymap(Buffer buffer, bool is_unmap, String mode, String lhs, String
return;
}
- MapArguments parsed_args;
- memset(&parsed_args, 0, sizeof(parsed_args));
- if (parse_keymap_opts(opts, &parsed_args, err)) {
- goto fail_and_free;
+ MapArguments parsed_args = MAP_ARGUMENTS_INIT;
+ if (opts) {
+#define KEY_TO_BOOL(name) \
+ parsed_args. name = api_object_to_bool(opts-> name, #name, false, err); \
+ if (ERROR_SET(err)) { \
+ goto fail_and_free; \
+ }
+
+ KEY_TO_BOOL(nowait);
+ KEY_TO_BOOL(noremap);
+ KEY_TO_BOOL(silent);
+ KEY_TO_BOOL(script);
+ KEY_TO_BOOL(expr);
+ KEY_TO_BOOL(unique);
+#undef KEY_TO_BOOL
}
parsed_args.buffer = !global;
@@ -947,95 +959,6 @@ fail_and_free:
return;
}
-/// Read in the given opts, setting corresponding flags in `out`.
-///
-/// @param opts A dictionary passed to @ref nvim_set_keymap or
-/// @ref nvim_buf_set_keymap.
-/// @param[out] out MapArguments object in which to set parsed
-/// |:map-arguments| flags.
-/// @param[out] err Error details, if any.
-///
-/// @returns Zero on success, nonzero on failure.
-Integer parse_keymap_opts(Dictionary opts, MapArguments *out, Error *err)
-{
- char *err_msg = NULL; // the error message to report, if any
- char *err_arg = NULL; // argument for the error message format string
- ErrorType err_type = kErrorTypeNone;
-
- out->buffer = false;
- out->nowait = false;
- out->silent = false;
- out->script = false;
- out->expr = false;
- out->unique = false;
-
- for (size_t i = 0; i < opts.size; i++) {
- KeyValuePair *key_and_val = &opts.items[i];
- char *optname = key_and_val->key.data;
-
- if (key_and_val->value.type != kObjectTypeBoolean) {
- err_msg = "Gave non-boolean value for an opt: %s";
- err_arg = optname;
- err_type = kErrorTypeValidation;
- goto fail_with_message;
- }
-
- bool was_valid_opt = false;
- switch (optname[0]) {
- // note: strncmp up to and including the null terminator, so that
- // "nowaitFoobar" won't match against "nowait"
-
- // don't recognize 'buffer' as a key; user shouldn't provide <buffer>
- // when calling nvim_set_keymap or nvim_buf_set_keymap, since it can be
- // inferred from which function they called
- case 'n':
- if (STRNCMP(optname, "noremap", 8) == 0) {
- was_valid_opt = true;
- out->noremap = key_and_val->value.data.boolean;
- } else if (STRNCMP(optname, "nowait", 7) == 0) {
- was_valid_opt = true;
- out->nowait = key_and_val->value.data.boolean;
- }
- break;
- case 's':
- if (STRNCMP(optname, "silent", 7) == 0) {
- was_valid_opt = true;
- out->silent = key_and_val->value.data.boolean;
- } else if (STRNCMP(optname, "script", 7) == 0) {
- was_valid_opt = true;
- out->script = key_and_val->value.data.boolean;
- }
- break;
- case 'e':
- if (STRNCMP(optname, "expr", 5) == 0) {
- was_valid_opt = true;
- out->expr = key_and_val->value.data.boolean;
- }
- break;
- case 'u':
- if (STRNCMP(optname, "unique", 7) == 0) {
- was_valid_opt = true;
- out->unique = key_and_val->value.data.boolean;
- }
- break;
- default:
- break;
- } // switch
- if (!was_valid_opt) {
- err_msg = "Invalid key: %s";
- err_arg = optname;
- err_type = kErrorTypeValidation;
- goto fail_with_message;
- }
- } // for
-
- return 0;
-
-fail_with_message:
- api_set_error(err, err_type, err_msg, err_arg);
- return 1;
-}
-
/// Collects `n` buffer lines into array `l`, optionally replacing newlines
/// with NUL.
///
@@ -1879,213 +1802,227 @@ static void parse_border_style(Object style, FloatConfig *fconfig, Error *err)
}
}
-bool parse_float_config(Dictionary config, FloatConfig *fconfig, bool reconf, bool new_win,
+bool parse_float_config(Dict(float_config) *config, FloatConfig *fconfig, bool reconf, bool new_win,
Error *err)
{
- // TODO(bfredl): use a get/has_key interface instead and get rid of extra
- // flags
- bool has_row = false, has_col = false, has_relative = false;
- bool has_external = false, has_window = false;
- bool has_width = false, has_height = false;
- bool has_bufpos = false;
-
- for (size_t i = 0; i < config.size; i++) {
- char *key = config.items[i].key.data;
- Object val = config.items[i].value;
- if (!strcmp(key, "row")) {
- has_row = true;
- if (val.type == kObjectTypeInteger) {
- fconfig->row = (double)val.data.integer;
- } else if (val.type == kObjectTypeFloat) {
- fconfig->row = val.data.floating;
- } else {
- api_set_error(err, kErrorTypeValidation,
- "'row' key must be Integer or Float");
- return false;
- }
- } else if (!strcmp(key, "col")) {
- has_col = true;
- if (val.type == kObjectTypeInteger) {
- fconfig->col = (double)val.data.integer;
- } else if (val.type == kObjectTypeFloat) {
- fconfig->col = val.data.floating;
- } else {
- api_set_error(err, kErrorTypeValidation,
- "'col' key must be Integer or Float");
- return false;
- }
- } else if (strequal(key, "width")) {
- has_width = true;
- if (val.type == kObjectTypeInteger && val.data.integer > 0) {
- fconfig->width = (int)val.data.integer;
- } else {
- api_set_error(err, kErrorTypeValidation,
- "'width' key must be a positive Integer");
+ bool has_relative = false, relative_is_win = false;
+ if (config->relative.type == kObjectTypeString) {
+ // ignore empty string, to match nvim_win_get_config
+ if (config->relative.data.string.size > 0) {
+ if (!parse_float_relative(config->relative.data.string, &fconfig->relative)) {
+ api_set_error(err, kErrorTypeValidation, "Invalid value of 'relative' key");
return false;
}
- } else if (strequal(key, "height")) {
- has_height = true;
- if (val.type == kObjectTypeInteger && val.data.integer > 0) {
- fconfig->height = (int)val.data.integer;
- } else {
- api_set_error(err, kErrorTypeValidation,
- "'height' key must be a positive Integer");
- return false;
- }
- } else if (!strcmp(key, "anchor")) {
- if (val.type != kObjectTypeString) {
- api_set_error(err, kErrorTypeValidation,
- "'anchor' key must be String");
- return false;
- }
- if (!parse_float_anchor(val.data.string, &fconfig->anchor)) {
- api_set_error(err, kErrorTypeValidation,
- "Invalid value of 'anchor' key");
- return false;
- }
- } else if (!strcmp(key, "relative")) {
- if (val.type != kObjectTypeString) {
- api_set_error(err, kErrorTypeValidation,
- "'relative' key must be String");
- return false;
- }
- // ignore empty string, to match nvim_win_get_config
- if (val.data.string.size > 0) {
- has_relative = true;
- if (!parse_float_relative(val.data.string, &fconfig->relative)) {
- api_set_error(err, kErrorTypeValidation,
- "Invalid value of 'relative' key");
- return false;
- }
- }
- } else if (!strcmp(key, "win")) {
- has_window = true;
- if (val.type != kObjectTypeInteger
- && val.type != kObjectTypeWindow) {
- api_set_error(err, kErrorTypeValidation,
- "'win' key must be Integer or Window");
- return false;
- }
- fconfig->window = (Window)val.data.integer;
- } else if (!strcmp(key, "bufpos")) {
- if (val.type != kObjectTypeArray) {
- api_set_error(err, kErrorTypeValidation,
- "'bufpos' key must be Array");
- return false;
- }
- if (!parse_float_bufpos(val.data.array, &fconfig->bufpos)) {
- api_set_error(err, kErrorTypeValidation,
- "Invalid value of 'bufpos' key");
- return false;
- }
- has_bufpos = true;
- } else if (!strcmp(key, "external")) {
- has_external = fconfig->external
- = api_object_to_bool(val, "'external' key", false, err);
- if (ERROR_SET(err)) {
- return false;
- }
- } else if (!strcmp(key, "focusable")) {
- fconfig->focusable
- = api_object_to_bool(val, "'focusable' key", true, err);
- if (ERROR_SET(err)) {
- return false;
- }
- } else if (strequal(key, "zindex")) {
- if (val.type == kObjectTypeInteger && val.data.integer > 0) {
- fconfig->zindex = (int)val.data.integer;
- } else {
+
+ if (!(HAS_KEY(config->row) && HAS_KEY(config->col)) && !HAS_KEY(config->bufpos)) {
api_set_error(err, kErrorTypeValidation,
- "'zindex' key must be a positive Integer");
+ "'relative' requires 'row'/'col' or 'bufpos'");
return false;
}
- } else if (!strcmp(key, "border")) {
- parse_border_style(val, fconfig, err);
- if (ERROR_SET(err)) {
- return false;
+
+ has_relative = true;
+ fconfig->external = false;
+ if (fconfig->relative == kFloatRelativeWindow) {
+ relative_is_win = true;
+ fconfig->bufpos.lnum = -1;
}
- } else if (!strcmp(key, "style")) {
- if (val.type != kObjectTypeString) {
- api_set_error(err, kErrorTypeValidation,
- "'style' key must be String");
+ }
+ } else if (HAS_KEY(config->relative)) {
+ api_set_error(err, kErrorTypeValidation, "'relative' key must be String");
+ return false;
+ }
+
+ if (config->anchor.type == kObjectTypeString) {
+ if (!parse_float_anchor(config->anchor.data.string, &fconfig->anchor)) {
+ api_set_error(err, kErrorTypeValidation, "Invalid value of 'anchor' key");
+ return false;
+ }
+ } else if (HAS_KEY(config->anchor)) {
+ api_set_error(err, kErrorTypeValidation, "'anchor' key must be String");
+ return false;
+ }
+
+ if (HAS_KEY(config->row)) {
+ if (!has_relative) {
+ api_set_error(err, kErrorTypeValidation, "non-float cannot have 'row'");
+ return false;
+ } else if (config->row.type == kObjectTypeInteger) {
+ fconfig->row = (double)config->row.data.integer;
+ } else if (config->row.type == kObjectTypeFloat) {
+ fconfig->row = config->row.data.floating;
+ } else {
+ api_set_error(err, kErrorTypeValidation,
+ "'row' key must be Integer or Float");
+ return false;
+ }
+ }
+
+ if (HAS_KEY(config->col)) {
+ if (!has_relative) {
+ api_set_error(err, kErrorTypeValidation, "non-float cannot have 'col'");
+ return false;
+ } else if (config->col.type == kObjectTypeInteger) {
+ fconfig->col = (double)config->col.data.integer;
+ } else if (config->col.type == kObjectTypeFloat) {
+ fconfig->col = config->col.data.floating;
+ } else {
+ api_set_error(err, kErrorTypeValidation,
+ "'col' key must be Integer or Float");
+ return false;
+ }
+ }
+
+ if (HAS_KEY(config->bufpos)) {
+ if (!has_relative) {
+ api_set_error(err, kErrorTypeValidation, "non-float cannot have 'bufpos'");
+ return false;
+ } else if (config->bufpos.type == kObjectTypeArray) {
+ if (!parse_float_bufpos(config->bufpos.data.array, &fconfig->bufpos)) {
+ api_set_error(err, kErrorTypeValidation, "Invalid value of 'bufpos' key");
return false;
}
- if (val.data.string.data[0] == NUL) {
- fconfig->style = kWinStyleUnused;
- } else if (striequal(val.data.string.data, "minimal")) {
- fconfig->style = kWinStyleMinimal;
- } else {
- api_set_error(err, kErrorTypeValidation,
- "Invalid value of 'style' key");
+
+ if (!HAS_KEY(config->row)) {
+ fconfig->row = (fconfig->anchor & kFloatAnchorSouth) ? 0 : 1;
}
- } else if (strequal(key, "noautocmd") && new_win) {
- fconfig->noautocmd
- = api_object_to_bool(val, "'noautocmd' key", false, err);
- if (ERROR_SET(err)) {
- return false;
+ if (!HAS_KEY(config->col)) {
+ fconfig->col = 0;
}
} else {
- api_set_error(err, kErrorTypeValidation,
- "Invalid key '%s'", key);
+ api_set_error(err, kErrorTypeValidation, "'bufpos' key must be Array");
return false;
}
}
- if (has_window && !(has_relative
- && fconfig->relative == kFloatRelativeWindow)) {
- api_set_error(err, kErrorTypeValidation,
- "'win' key is only valid with relative='win'");
+ if (config->width.type == kObjectTypeInteger && config->width.data.integer > 0) {
+ fconfig->width = (int)config->width.data.integer;
+ } else if (HAS_KEY(config->width)) {
+ api_set_error(err, kErrorTypeValidation, "'width' key must be a positive Integer");
+ return false;
+ } else if (!reconf) {
+ api_set_error(err, kErrorTypeValidation, "Must specify 'width'");
return false;
}
- if ((has_relative && fconfig->relative == kFloatRelativeWindow)
- && (!has_window || fconfig->window == 0)) {
- fconfig->window = curwin->handle;
+ if (config->height.type == kObjectTypeInteger && config->height.data.integer > 0) {
+ fconfig->height = (int)config->height.data.integer;
+ } else if (HAS_KEY(config->height)) {
+ api_set_error(err, kErrorTypeValidation, "'height' key must be a positive Integer");
+ return false;
+ } else if (!reconf) {
+ api_set_error(err, kErrorTypeValidation, "Must specify 'height'");
+ return false;
}
- if (has_window && !has_bufpos) {
- fconfig->bufpos.lnum = -1;
+ if (relative_is_win) {
+ fconfig->window = curwin->handle;
+ if (config->win.type == kObjectTypeInteger || config->win.type == kObjectTypeWindow) {
+ if (config->win.data.integer > 0) {
+ fconfig->window = (Window)config->win.data.integer;
+ }
+ } else if (HAS_KEY(config->win)) {
+ api_set_error(err, kErrorTypeValidation, "'win' key must be Integer or Window");
+ return false;
+ }
+ } else {
+ if (HAS_KEY(config->win)) {
+ api_set_error(err, kErrorTypeValidation, "'win' key is only valid with relative='win'");
+ return false;
+ }
}
- if (has_bufpos) {
- if (!has_row) {
- fconfig->row = (fconfig->anchor & kFloatAnchorSouth) ? 0 : 1;
- has_row = true;
+ if (HAS_KEY(config->external)) {
+ fconfig->external = api_object_to_bool(config->external, "'external' key", false, err);
+ if (ERROR_SET(err)) {
+ return false;
}
- if (!has_col) {
- fconfig->col = 0;
- has_col = true;
+ if (has_relative && fconfig->external) {
+ api_set_error(err, kErrorTypeValidation,
+ "Only one of 'relative' and 'external' must be used");
+ return false;
+ }
+ if (fconfig->external && !ui_has(kUIMultigrid)) {
+ api_set_error(err, kErrorTypeValidation,
+ "UI doesn't support external windows");
+ return false;
}
}
- if (has_relative && has_external) {
- api_set_error(err, kErrorTypeValidation,
- "Only one of 'relative' and 'external' must be used");
- return false;
- } else if (!reconf && !has_relative && !has_external) {
+ if (!reconf && (!has_relative && !fconfig->external)) {
api_set_error(err, kErrorTypeValidation,
"One of 'relative' and 'external' must be used");
return false;
- } else if (has_relative) {
- fconfig->external = false;
}
- if (!reconf && !(has_height && has_width)) {
- api_set_error(err, kErrorTypeValidation,
- "Must specify 'width' and 'height'");
- return false;
+
+ if (HAS_KEY(config->focusable)) {
+ fconfig->focusable = api_object_to_bool(config->focusable, "'focusable' key", false, err);
+ if (ERROR_SET(err)) {
+ return false;
+ }
}
- if (fconfig->external && !ui_has(kUIMultigrid)) {
- api_set_error(err, kErrorTypeValidation,
- "UI doesn't support external windows");
+ if (config->zindex.type == kObjectTypeInteger && config->zindex.data.integer > 0) {
+ fconfig->zindex = (int)config->zindex.data.integer;
+ } else if (HAS_KEY(config->zindex)) {
+ api_set_error(err, kErrorTypeValidation, "'zindex' key must be a positive Integer");
return false;
}
- if (has_relative != has_row || has_row != has_col) {
- api_set_error(err, kErrorTypeValidation,
- "'relative' requires 'row'/'col' or 'bufpos'");
+ if (HAS_KEY(config->border)) {
+ parse_border_style(config->border, fconfig, err);
+ if (ERROR_SET(err)) {
+ return false;
+ }
+ }
+
+ if (config->style.type == kObjectTypeString) {
+ if (config->style.data.string.data[0] == NUL) {
+ fconfig->style = kWinStyleUnused;
+ } else if (striequal(config->style.data.string.data, "minimal")) {
+ fconfig->style = kWinStyleMinimal;
+ } else {
+ api_set_error(err, kErrorTypeValidation, "Invalid value of 'style' key");
+ }
+ } else if (HAS_KEY(config->style)) {
+ api_set_error(err, kErrorTypeValidation, "'style' key must be String");
return false;
}
+
+ if (HAS_KEY(config->noautocmd)) {
+ if (!new_win) {
+ api_set_error(err, kErrorTypeValidation, "Invalid key: 'noautocmd'");
+ return false;
+ }
+ fconfig->noautocmd = api_object_to_bool(config->noautocmd, "'noautocmd' key", false, err);
+ if (ERROR_SET(err)) {
+ return false;
+ }
+ }
+
return true;
}
+
+bool api_dict_to_keydict(void *rv, field_hash hashy, Dictionary dict, Error *err)
+{
+ for (size_t i = 0; i < dict.size; i++) {
+ String k = dict.items[i].key;
+ Object *field = hashy(rv, k.data, k.size);
+ if (!field) {
+ api_set_error(err, kErrorTypeValidation, "Invalid key: '%.*s'", (int)k.size, k.data);
+ return false;
+ }
+
+ *field = dict.items[i].value;
+ }
+
+ return true;
+}
+
+void api_free_keydict(void *dict, KeySetLink *table)
+{
+ for (size_t i = 0; table[i].str; i++) {
+ api_free_object(*(Object *)((char *)dict + table[i].ptr_off));
+ }
+}
+
diff --git a/src/nvim/api/private/helpers.h b/src/nvim/api/private/helpers.h
index ecce6afa26..2cdd80bffe 100644
--- a/src/nvim/api/private/helpers.h
+++ b/src/nvim/api/private/helpers.h
@@ -59,6 +59,9 @@
#define NIL ((Object)OBJECT_INIT)
#define NULL_STRING ((String)STRING_INIT)
+// currently treat key=vim.NIL as if the key was missing
+#define HAS_KEY(o) ((o).type != kObjectTypeNil)
+
#define PUT(dict, k, v) \
kv_push(dict, ((KeyValuePair) { .key = cstr_to_string(k), .value = v }))
@@ -138,6 +141,9 @@ typedef struct {
} while (0)
#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "keysets.h.generated.h"
# include "api/private/helpers.h.generated.h"
#endif
+
+
#endif // NVIM_API_PRIVATE_HELPERS_H
diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c
index e2d7c03307..f80d605d9a 100644
--- a/src/nvim/api/vim.c
+++ b/src/nvim/api/vim.c
@@ -1424,7 +1424,7 @@ void nvim_chan_send(Integer chan, String data, Error *err)
/// @param[out] err Error details, if any
///
/// @return Window handle, or 0 on error
-Window nvim_open_win(Buffer buffer, Boolean enter, Dictionary config, Error *err)
+Window nvim_open_win(Buffer buffer, Boolean enter, Dict(float_config) *config, Error *err)
FUNC_API_SINCE(6)
FUNC_API_CHECK_TEXTLOCK
{
@@ -1761,24 +1761,15 @@ Dictionary nvim_get_color_map(void)
/// @param[out] err Error details, if any
///
/// @return map of global |context|.
-Dictionary nvim_get_context(Dictionary opts, Error *err)
+Dictionary nvim_get_context(Dict(context) *opts, Error *err)
FUNC_API_SINCE(6)
{
Array types = ARRAY_DICT_INIT;
- for (size_t i = 0; i < opts.size; i++) {
- String k = opts.items[i].key;
- Object v = opts.items[i].value;
- if (strequal("types", k.data)) {
- if (v.type != kObjectTypeArray) {
- api_set_error(err, kErrorTypeValidation, "invalid value for key: %s",
- k.data);
- return (Dictionary)ARRAY_DICT_INIT;
- }
- types = v.data.array;
- } else {
- api_set_error(err, kErrorTypeValidation, "unexpected key: %s", k.data);
- return (Dictionary)ARRAY_DICT_INIT;
- }
+ if (opts->types.type == kObjectTypeArray) {
+ types = opts->types.data.array;
+ } else if (opts->types.type != kObjectTypeNil) {
+ api_set_error(err, kErrorTypeValidation, "invalid value for key: types");
+ return (Dictionary)ARRAY_DICT_INIT;
}
int int_types = types.size > 0 ? 0 : kCtxAll;
@@ -1888,7 +1879,7 @@ ArrayOf(Dictionary) nvim_get_keymap(String mode)
/// as keys excluding |<buffer>| but including |noremap|.
/// Values are Booleans. Unknown key is an error.
/// @param[out] err Error details, if any.
-void nvim_set_keymap(String mode, String lhs, String rhs, Dictionary opts, Error *err)
+void nvim_set_keymap(String mode, String lhs, String rhs, Dict(keymap) *opts, Error *err)
FUNC_API_SINCE(6)
{
modify_keymap(-1, false, mode, lhs, rhs, opts, err);
@@ -1914,7 +1905,7 @@ void nvim_del_keymap(String mode, String lhs, Error *err)
/// @param[out] err Error details, if any.
///
/// @returns Map of maps describing commands.
-Dictionary nvim_get_commands(Dictionary opts, Error *err)
+Dictionary nvim_get_commands(Dict(get_commands) *opts, Error *err)
FUNC_API_SINCE(4)
{
return nvim_buf_get_commands(-1, opts, err);
diff --git a/src/nvim/api/window.c b/src/nvim/api/window.c
index 99ba297111..95eae1af2d 100644
--- a/src/nvim/api/window.c
+++ b/src/nvim/api/window.c
@@ -385,7 +385,7 @@ Boolean nvim_win_is_valid(Window window)
/// @param config Map defining the window configuration,
/// see |nvim_open_win()|
/// @param[out] err Error details, if any
-void nvim_win_set_config(Window window, Dictionary config, Error *err)
+void nvim_win_set_config(Window window, Dict(float_config) *config, Error *err)
FUNC_API_SINCE(6)
{
win_T *win = find_window_by_handle(window, err);