mirror of
				https://github.com/neovim/neovim.git
				synced 2025-11-03 17:24:29 +00:00 
			
		
		
		
	Merge pull request #15516 from bfredl/keyset
refactor(api): Represent option dicts as a structs in C and reduce conversion overhead from lua
This commit is contained in:
		@@ -2544,6 +2544,7 @@ def CheckSpacing(filename, clean_lines, linenum, nesting_state, error):
 | 
			
		||||
                   r'(?<!\bPMap)'
 | 
			
		||||
                   r'(?<!\bArrayOf)'
 | 
			
		||||
                   r'(?<!\bDictionaryOf)'
 | 
			
		||||
                   r'(?<!\bDict)'
 | 
			
		||||
                   r'\((?:const )?(?:struct )?[a-zA-Z_]\w*(?: *\*(?:const)?)*\)'
 | 
			
		||||
                   r' +'
 | 
			
		||||
                   r'-?(?:\*+|&)?(?:\w+|\+\+|--|\()', cast_line)
 | 
			
		||||
 
 | 
			
		||||
@@ -27,6 +27,7 @@ set(BINARY_LIB_DIR ${PROJECT_BINARY_DIR}/lib/nvim/)
 | 
			
		||||
set(API_DISPATCH_GENERATOR ${GENERATOR_DIR}/gen_api_dispatch.lua)
 | 
			
		||||
set(API_UI_EVENTS_GENERATOR ${GENERATOR_DIR}/gen_api_ui_events.lua)
 | 
			
		||||
set(GENERATOR_C_GRAMMAR ${GENERATOR_DIR}/c_grammar.lua)
 | 
			
		||||
set(GENERATOR_HASHY ${GENERATOR_DIR}/hashy.lua)
 | 
			
		||||
set(API_METADATA ${PROJECT_BINARY_DIR}/api_metadata.mpack)
 | 
			
		||||
set(FUNCS_DATA ${PROJECT_BINARY_DIR}/funcs_data.mpack)
 | 
			
		||||
set(LUA_API_C_BINDINGS ${GENERATED_DIR}/lua_api_c_bindings.generated.c)
 | 
			
		||||
@@ -42,12 +43,15 @@ set(GENERATED_UI_EVENTS_METADATA ${GENERATED_DIR}/api/private/ui_events_metadata
 | 
			
		||||
set(GENERATED_EX_CMDS_ENUM ${GENERATED_INCLUDES_DIR}/ex_cmds_enum.generated.h)
 | 
			
		||||
set(GENERATED_EX_CMDS_DEFS ${GENERATED_DIR}/ex_cmds_defs.generated.h)
 | 
			
		||||
set(GENERATED_FUNCS ${GENERATED_DIR}/funcs.generated.h)
 | 
			
		||||
set(GENERATED_KEYSETS ${GENERATED_DIR}/keysets.generated.h)
 | 
			
		||||
set(GENERATED_KEYSETS_DEFS ${GENERATED_DIR}/keysets_defs.generated.h)
 | 
			
		||||
set(GENERATED_EVENTS_ENUM ${GENERATED_INCLUDES_DIR}/auevents_enum.generated.h)
 | 
			
		||||
set(GENERATED_EVENTS_NAMES_MAP ${GENERATED_DIR}/auevents_name_map.generated.h)
 | 
			
		||||
set(GENERATED_OPTIONS ${GENERATED_DIR}/options.generated.h)
 | 
			
		||||
set(EX_CMDS_GENERATOR ${GENERATOR_DIR}/gen_ex_cmds.lua)
 | 
			
		||||
set(FUNCS_GENERATOR ${GENERATOR_DIR}/gen_eval.lua)
 | 
			
		||||
set(EVENTS_GENERATOR ${GENERATOR_DIR}/gen_events.lua)
 | 
			
		||||
set(KEYSETS_GENERATOR ${GENERATOR_DIR}/gen_keysets.lua)
 | 
			
		||||
set(OPTIONS_GENERATOR ${GENERATOR_DIR}/gen_options.lua)
 | 
			
		||||
set(UNICODE_TABLES_GENERATOR ${GENERATOR_DIR}/gen_unicode_tables.lua)
 | 
			
		||||
set(UNICODE_DIR ${PROJECT_SOURCE_DIR}/unicode)
 | 
			
		||||
@@ -261,6 +265,7 @@ foreach(sfile ${NVIM_SOURCES}
 | 
			
		||||
              "${GENERATED_UI_EVENTS_CALL}"
 | 
			
		||||
              "${GENERATED_UI_EVENTS_REMOTE}"
 | 
			
		||||
              "${GENERATED_UI_EVENTS_BRIDGE}"
 | 
			
		||||
              "${GENERATED_KEYSETS}"
 | 
			
		||||
              )
 | 
			
		||||
  get_filename_component(full_d ${sfile} PATH)
 | 
			
		||||
  file(RELATIVE_PATH d "${CMAKE_CURRENT_LIST_DIR}" "${full_d}")
 | 
			
		||||
@@ -364,12 +369,14 @@ add_custom_command(
 | 
			
		||||
list(APPEND NVIM_GENERATED_FOR_HEADERS
 | 
			
		||||
  "${GENERATED_EX_CMDS_ENUM}"
 | 
			
		||||
  "${GENERATED_EVENTS_ENUM}"
 | 
			
		||||
  "${GENERATED_KEYSETS_DEFS}"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
list(APPEND NVIM_GENERATED_FOR_SOURCES
 | 
			
		||||
  "${GENERATED_API_DISPATCH}"
 | 
			
		||||
  "${GENERATED_EX_CMDS_DEFS}"
 | 
			
		||||
  "${GENERATED_EVENTS_NAMES_MAP}"
 | 
			
		||||
  "${GENERATED_KEYSETS}"
 | 
			
		||||
  "${GENERATED_OPTIONS}"
 | 
			
		||||
  "${GENERATED_UNICODE_TABLES}"
 | 
			
		||||
  "${VIM_MODULE_FILE}"
 | 
			
		||||
@@ -404,6 +411,12 @@ add_custom_command(OUTPUT ${GENERATED_EVENTS_ENUM} ${GENERATED_EVENTS_NAMES_MAP}
 | 
			
		||||
  DEPENDS ${EVENTS_GENERATOR} ${CMAKE_CURRENT_LIST_DIR}/auevents.lua
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
add_custom_command(OUTPUT ${GENERATED_KEYSETS} ${GENERATED_KEYSETS_DEFS}
 | 
			
		||||
  COMMAND ${LUA_PRG} ${KEYSETS_GENERATOR}
 | 
			
		||||
      ${CMAKE_CURRENT_LIST_DIR} ${LUA_SHARED_MODULE_SOURCE} ${GENERATED_KEYSETS} ${GENERATED_KEYSETS_DEFS}
 | 
			
		||||
  DEPENDS ${KEYSETS_GENERATOR} ${CMAKE_CURRENT_LIST_DIR}/api/keysets.lua ${GENERATOR_HASHY}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
add_custom_command(OUTPUT ${GENERATED_OPTIONS}
 | 
			
		||||
  COMMAND ${LUA_PRG} ${OPTIONS_GENERATOR}
 | 
			
		||||
                     ${CMAKE_CURRENT_LIST_DIR} ${GENERATED_OPTIONS}
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
  int line2 = -1;
 | 
			
		||||
  Decoration decor = DECORATION_INIT;
 | 
			
		||||
  colnr_T col2 = -1;
 | 
			
		||||
  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;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  bool right_gravity = true;
 | 
			
		||||
  bool end_right_gravity = false;
 | 
			
		||||
  bool end_gravity_set = false;
 | 
			
		||||
  int line2 = -1;
 | 
			
		||||
  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;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Decoration decor = DECORATION_INIT;
 | 
			
		||||
 | 
			
		||||
  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;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  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;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  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;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
#define OPTION_TO_BOOL(target, name, val) \
 | 
			
		||||
    target = api_object_to_bool(opts-> name, #name, val, err); \
 | 
			
		||||
    if (ERROR_SET(err)) { \
 | 
			
		||||
      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;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  VirtLines virt_lines = KV_INITIAL_VALUE;
 | 
			
		||||
  bool virt_lines_above = false;
 | 
			
		||||
  bool virt_lines_leftcol = false;
 | 
			
		||||
 | 
			
		||||
  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");
 | 
			
		||||
  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;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      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;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      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;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      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);
 | 
			
		||||
      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_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;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      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");
 | 
			
		||||
        goto error;
 | 
			
		||||
      }
 | 
			
		||||
    } else if (strequal("ephemeral", k.data)) {
 | 
			
		||||
      ephemeral = api_object_to_bool(*v, "ephemeral", false, err);
 | 
			
		||||
      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;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      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);
 | 
			
		||||
      goto error;
 | 
			
		||||
    }
 | 
			
		||||
  } else if (HAS_KEY(opts->virt_lines)) {
 | 
			
		||||
    api_set_error(err, kErrorTypeValidation, "virt_lines is not an Array");
 | 
			
		||||
    goto error;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  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) {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										52
									
								
								src/nvim/api/keysets.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								src/nvim/api/keysets.lua
									
									
									
									
									
										Normal file
									
								
							@@ -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";
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
  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;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
  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 {
 | 
			
		||||
      if (!(HAS_KEY(config->row) && HAS_KEY(config->col)) && !HAS_KEY(config->bufpos)) {
 | 
			
		||||
        api_set_error(err, kErrorTypeValidation,
 | 
			
		||||
                      "'row' key must be Integer or Float");
 | 
			
		||||
                      "'relative' requires 'row'/'col' or 'bufpos'");
 | 
			
		||||
        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");
 | 
			
		||||
        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 {
 | 
			
		||||
        api_set_error(err, kErrorTypeValidation,
 | 
			
		||||
                      "'zindex' key must be a positive Integer");
 | 
			
		||||
        return false;
 | 
			
		||||
      }
 | 
			
		||||
    } else if (!strcmp(key, "border")) {
 | 
			
		||||
      parse_border_style(val, fconfig, err);
 | 
			
		||||
      if (ERROR_SET(err)) {
 | 
			
		||||
        return false;
 | 
			
		||||
      }
 | 
			
		||||
    } else if (!strcmp(key, "style")) {
 | 
			
		||||
      if (val.type != kObjectTypeString) {
 | 
			
		||||
        api_set_error(err, kErrorTypeValidation,
 | 
			
		||||
                      "'style' key must be String");
 | 
			
		||||
        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");
 | 
			
		||||
      }
 | 
			
		||||
    } else if (strequal(key, "noautocmd") && new_win) {
 | 
			
		||||
      fconfig->noautocmd
 | 
			
		||||
        = api_object_to_bool(val, "'noautocmd' key", false, 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 (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,
 | 
			
		||||
                    "Invalid key '%s'", key);
 | 
			
		||||
                    "'row' key must be Integer or Float");
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (has_window && !(has_relative
 | 
			
		||||
                      && fconfig->relative == kFloatRelativeWindow)) {
 | 
			
		||||
    api_set_error(err, kErrorTypeValidation,
 | 
			
		||||
                  "'win' key is only valid with relative='win'");
 | 
			
		||||
  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 (!HAS_KEY(config->row)) {
 | 
			
		||||
        fconfig->row = (fconfig->anchor & kFloatAnchorSouth) ? 0 : 1;
 | 
			
		||||
      }
 | 
			
		||||
      if (!HAS_KEY(config->col)) {
 | 
			
		||||
        fconfig->col = 0;
 | 
			
		||||
      }
 | 
			
		||||
    } else {
 | 
			
		||||
      api_set_error(err, kErrorTypeValidation, "'bufpos' key must be Array");
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  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)) {
 | 
			
		||||
  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 (relative_is_win) {
 | 
			
		||||
    fconfig->window = curwin->handle;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (has_window && !has_bufpos) {
 | 
			
		||||
    fconfig->bufpos.lnum = -1;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (has_bufpos) {
 | 
			
		||||
    if (!has_row) {
 | 
			
		||||
      fconfig->row = (fconfig->anchor & kFloatAnchorSouth) ? 0 : 1;
 | 
			
		||||
      has_row = true;
 | 
			
		||||
    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;
 | 
			
		||||
    }
 | 
			
		||||
    if (!has_col) {
 | 
			
		||||
      fconfig->col = 0;
 | 
			
		||||
      has_col = true;
 | 
			
		||||
  } else {
 | 
			
		||||
    if (HAS_KEY(config->win)) {
 | 
			
		||||
      api_set_error(err, kErrorTypeValidation, "'win' key is only valid with relative='win'");
 | 
			
		||||
      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 (HAS_KEY(config->external)) {
 | 
			
		||||
    fconfig->external = api_object_to_bool(config->external, "'external' key", false, err);
 | 
			
		||||
    if (ERROR_SET(err)) {
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
    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 (!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'");
 | 
			
		||||
 | 
			
		||||
  if (HAS_KEY(config->focusable)) {
 | 
			
		||||
    fconfig->focusable = api_object_to_bool(config->focusable, "'focusable' key", false, err);
 | 
			
		||||
    if (ERROR_SET(err)) {
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  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 (fconfig->external && !ui_has(kUIMultigrid)) {
 | 
			
		||||
    api_set_error(err, kErrorTypeValidation,
 | 
			
		||||
                  "UI doesn't support external windows");
 | 
			
		||||
  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_relative != has_row || has_row != has_col) {
 | 
			
		||||
    api_set_error(err, kErrorTypeValidation,
 | 
			
		||||
                  "'relative' requires 'row'/'col' or 'bufpos'");
 | 
			
		||||
    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));
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
 
 | 
			
		||||
@@ -17,7 +17,7 @@ local fill = ws ^ 0
 | 
			
		||||
local c_comment = P('//') * (not_nl ^ 0)
 | 
			
		||||
local c_preproc = P('#') * (not_nl ^ 0)
 | 
			
		||||
local typed_container =
 | 
			
		||||
  (P('ArrayOf(') + P('DictionaryOf(')) * ((any - P(')')) ^ 1) * P(')')
 | 
			
		||||
  (P('ArrayOf(') + P('DictionaryOf(') + P('Dict(')) * ((any - P(')')) ^ 1) * P(')')
 | 
			
		||||
local c_id = (
 | 
			
		||||
  typed_container +
 | 
			
		||||
  (letter * (alpha ^ 0))
 | 
			
		||||
 
 | 
			
		||||
@@ -152,6 +152,8 @@ for _,f in ipairs(functions) do
 | 
			
		||||
    for i,param in ipairs(f.parameters) do
 | 
			
		||||
      if param[1] == "DictionaryOf(LuaRef)" then
 | 
			
		||||
        param = {"Dictionary", param[2]}
 | 
			
		||||
      elseif startswith(param[1], "Dict(") then
 | 
			
		||||
        param = {"Dictionary", param[2]}
 | 
			
		||||
      end
 | 
			
		||||
      f_exported.parameters[i] = param
 | 
			
		||||
    end
 | 
			
		||||
@@ -173,7 +175,10 @@ local output = io.open(dispatch_outputf, 'wb')
 | 
			
		||||
 | 
			
		||||
local function real_type(type)
 | 
			
		||||
  local rv = type
 | 
			
		||||
  if c_grammar.typed_container:match(rv) then
 | 
			
		||||
  local rmatch = string.match(type, "Dict%(([_%w]+)%)")
 | 
			
		||||
  if rmatch then
 | 
			
		||||
    return "KeyDict_"..rmatch
 | 
			
		||||
  elseif c_grammar.typed_container:match(rv) then
 | 
			
		||||
    if rv:match('Array') then
 | 
			
		||||
      rv = 'Array'
 | 
			
		||||
    else
 | 
			
		||||
@@ -209,8 +214,9 @@ for i = 1, #functions do
 | 
			
		||||
    -- Declare/initialize variables that will hold converted arguments
 | 
			
		||||
    for j = 1, #fn.parameters do
 | 
			
		||||
      local param = fn.parameters[j]
 | 
			
		||||
      local rt = real_type(param[1])
 | 
			
		||||
      local converted = 'arg_'..j
 | 
			
		||||
      output:write('\n  '..param[1]..' '..converted..';')
 | 
			
		||||
      output:write('\n  '..rt..' '..converted..';')
 | 
			
		||||
    end
 | 
			
		||||
    output:write('\n')
 | 
			
		||||
    output:write('\n  if (args.size != '..#fn.parameters..') {')
 | 
			
		||||
@@ -225,7 +231,24 @@ for i = 1, #functions do
 | 
			
		||||
      param = fn.parameters[j]
 | 
			
		||||
      converted = 'arg_'..j
 | 
			
		||||
      local rt = real_type(param[1])
 | 
			
		||||
      if rt ~= 'Object' then
 | 
			
		||||
      if rt == 'Object' then
 | 
			
		||||
        output:write('\n  '..converted..' = args.items['..(j - 1)..'];\n')
 | 
			
		||||
      elseif rt:match('^KeyDict_') then
 | 
			
		||||
        converted = '&' .. converted
 | 
			
		||||
        output:write('\n  if (args.items['..(j - 1)..'].type == kObjectTypeDictionary) {') --luacheck: ignore 631
 | 
			
		||||
        output:write('\n    memset('..converted..', 0, sizeof(*'..converted..'));') -- TODO: neeeee
 | 
			
		||||
        output:write('\n    if (!api_dict_to_keydict('..converted..', '..rt..'_get_field, args.items['..(j - 1)..'].data.dictionary, error)) {')
 | 
			
		||||
        output:write('\n      goto cleanup;')
 | 
			
		||||
        output:write('\n    }')
 | 
			
		||||
          output:write('\n  } else if (args.items['..(j - 1)..'].type == kObjectTypeArray && args.items['..(j - 1)..'].data.array.size == 0) {') --luacheck: ignore 631
 | 
			
		||||
        output:write('\n    memset('..converted..', 0, sizeof(*'..converted..'));')
 | 
			
		||||
 | 
			
		||||
        output:write('\n  } else {')
 | 
			
		||||
        output:write('\n    api_set_error(error, kErrorTypeException, \
 | 
			
		||||
          "Wrong type for argument '..j..' when calling '..fn.name..', expecting '..param[1]..'");')
 | 
			
		||||
        output:write('\n    goto cleanup;')
 | 
			
		||||
        output:write('\n  }\n')
 | 
			
		||||
      else
 | 
			
		||||
        if rt:match('^Buffer$') or rt:match('^Window$') or rt:match('^Tabpage$') then
 | 
			
		||||
          -- Buffer, Window, and Tabpage have a specific type, but are stored in integer
 | 
			
		||||
          output:write('\n  if (args.items['..
 | 
			
		||||
@@ -257,10 +280,7 @@ for i = 1, #functions do
 | 
			
		||||
          "Wrong type for argument '..j..' when calling '..fn.name..', expecting '..param[1]..'");')
 | 
			
		||||
        output:write('\n    goto cleanup;')
 | 
			
		||||
        output:write('\n  }\n')
 | 
			
		||||
      else
 | 
			
		||||
        output:write('\n  '..converted..' = args.items['..(j - 1)..'];\n')
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      args[#args + 1] = converted
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
@@ -423,13 +443,24 @@ local function process_function(fn)
 | 
			
		||||
    if param[1] == "DictionaryOf(LuaRef)" then
 | 
			
		||||
      extra = "true, "
 | 
			
		||||
    end
 | 
			
		||||
    local errshift = 0
 | 
			
		||||
    if string.match(param_type, '^KeyDict_') then
 | 
			
		||||
      write_shifted_output(output, string.format([[
 | 
			
		||||
      %s %s = { 0 }; nlua_pop_keydict(lstate, &%s, %s_get_field, %s&err);]], param_type, cparam, cparam, param_type, extra))
 | 
			
		||||
      cparam = '&'..cparam
 | 
			
		||||
      errshift = 1 -- free incomplete dict on error
 | 
			
		||||
    else
 | 
			
		||||
      write_shifted_output(output, string.format([[
 | 
			
		||||
      const %s %s = nlua_pop_%s(lstate, %s&err);]], param[1], cparam, param_type, extra))
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    write_shifted_output(output, string.format([[
 | 
			
		||||
    const %s %s = nlua_pop_%s(lstate, %s&err);
 | 
			
		||||
 | 
			
		||||
    if (ERROR_SET(&err)) {
 | 
			
		||||
      goto exit_%u;
 | 
			
		||||
    }
 | 
			
		||||
    ]], param[1], cparam, param_type, extra, #fn.parameters - j))
 | 
			
		||||
 | 
			
		||||
    ]], #fn.parameters - j + errshift))
 | 
			
		||||
    free_code[#free_code + 1] = ('api_free_%s(%s);'):format(
 | 
			
		||||
      lc_param_type, cparam)
 | 
			
		||||
    cparams = cparam .. ', ' .. cparams
 | 
			
		||||
@@ -446,7 +477,7 @@ local function process_function(fn)
 | 
			
		||||
  for i = 1, #free_code do
 | 
			
		||||
    local rev_i = #free_code - i + 1
 | 
			
		||||
    local code = free_code[rev_i]
 | 
			
		||||
    if i == 1 then
 | 
			
		||||
    if i == 1 and not string.match(real_type(fn.parameters[1][1]), '^KeyDict_') then
 | 
			
		||||
      free_at_exit_code = free_at_exit_code .. ('\n    %s'):format(code)
 | 
			
		||||
    else
 | 
			
		||||
      free_at_exit_code = free_at_exit_code .. ('\n  exit_%u:\n    %s'):format(
 | 
			
		||||
 
 | 
			
		||||
@@ -60,7 +60,7 @@ local right_word = concat(
 | 
			
		||||
)
 | 
			
		||||
local word = branch(
 | 
			
		||||
  concat(
 | 
			
		||||
    branch(lit('ArrayOf('), lit('DictionaryOf(')), -- typed container macro
 | 
			
		||||
    branch(lit('ArrayOf('), lit('DictionaryOf('), lit('Dict(')), -- typed container macro
 | 
			
		||||
    one_or_more(any_character - lit(')')),
 | 
			
		||||
    lit(')')
 | 
			
		||||
  ),
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										67
									
								
								src/nvim/generators/gen_keysets.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								src/nvim/generators/gen_keysets.lua
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,67 @@
 | 
			
		||||
 | 
			
		||||
local nvimsrcdir = arg[1]
 | 
			
		||||
local shared_file = arg[2]
 | 
			
		||||
local funcs_file = arg[3]
 | 
			
		||||
local defs_file = arg[4]
 | 
			
		||||
 | 
			
		||||
_G.vim = loadfile(shared_file)()
 | 
			
		||||
 | 
			
		||||
if nvimsrcdir == '--help' then
 | 
			
		||||
  print([[
 | 
			
		||||
Usage:
 | 
			
		||||
  lua gen_keyset.lua TODOFIXUPDATETHIS
 | 
			
		||||
 | 
			
		||||
Will generate build/src/nvim/auto/keyset.generated.h with definition of functions
 | 
			
		||||
static const array.
 | 
			
		||||
]])
 | 
			
		||||
  os.exit(0)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
package.path = nvimsrcdir .. '/?.lua;' .. package.path
 | 
			
		||||
local hashy = require'generators.hashy'
 | 
			
		||||
 | 
			
		||||
local funcspipe = io.open(funcs_file, 'wb')
 | 
			
		||||
local defspipe = io.open(defs_file, 'wb')
 | 
			
		||||
 | 
			
		||||
local keysets = require'api.keysets'
 | 
			
		||||
 | 
			
		||||
for name, keys in pairs(keysets) do
 | 
			
		||||
  local neworder, hashfun = hashy.hashy_hash(name, keys, function (idx)
 | 
			
		||||
    return name.."_table["..idx.."].str"
 | 
			
		||||
  end)
 | 
			
		||||
 | 
			
		||||
  defspipe:write("typedef struct {\n")
 | 
			
		||||
  for _, key in ipairs(neworder) do
 | 
			
		||||
    defspipe:write("  Object "..key..";\n")
 | 
			
		||||
  end
 | 
			
		||||
  defspipe:write("} KeyDict_"..name..";\n\n")
 | 
			
		||||
 | 
			
		||||
  defspipe:write("extern KeySetLink "..name.."_table[];\n")
 | 
			
		||||
 | 
			
		||||
  funcspipe:write("KeySetLink "..name.."_table[] = {\n")
 | 
			
		||||
  for _, key in ipairs(neworder) do
 | 
			
		||||
    funcspipe:write('  {"'..key..'", offsetof(KeyDict_'..name..", "..key..")},\n")
 | 
			
		||||
  end
 | 
			
		||||
    funcspipe:write('  {NULL, 0},\n')
 | 
			
		||||
  funcspipe:write("};\n\n")
 | 
			
		||||
 | 
			
		||||
  funcspipe:write(hashfun)
 | 
			
		||||
 | 
			
		||||
  funcspipe:write([[
 | 
			
		||||
Object *KeyDict_]]..name..[[_get_field(void *retval, const char *str, size_t len)
 | 
			
		||||
{
 | 
			
		||||
  int hash = ]]..name..[[_hash(str, len);
 | 
			
		||||
  if (hash == -1) {
 | 
			
		||||
    return NULL;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return (Object *)((char *)retval + ]]..name..[[_table[hash].ptr_off);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
]])
 | 
			
		||||
  defspipe:write("#define api_free_keydict_"..name.."(x) api_free_keydict(x, "..name.."_table)\n")
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
funcspipe:close()
 | 
			
		||||
defspipe:close()
 | 
			
		||||
							
								
								
									
										122
									
								
								src/nvim/generators/hashy.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										122
									
								
								src/nvim/generators/hashy.lua
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,122 @@
 | 
			
		||||
-- HASHY McHASHFACE
 | 
			
		||||
 | 
			
		||||
local M = {}
 | 
			
		||||
_G.d = M
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
local function setdefault(table, key)
 | 
			
		||||
  local val = table[key]
 | 
			
		||||
  if val == nil then
 | 
			
		||||
    val = {}
 | 
			
		||||
    table[key] = val
 | 
			
		||||
  end
 | 
			
		||||
  return val
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function M.build_pos_hash(strings)
 | 
			
		||||
  local len_buckets = {}
 | 
			
		||||
  local maxlen = 0
 | 
			
		||||
  for _,s in ipairs(strings) do
 | 
			
		||||
    table.insert(setdefault(len_buckets, #s),s)
 | 
			
		||||
    if #s > maxlen then maxlen = #s end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  local len_pos_buckets = {}
 | 
			
		||||
  local worst_buck_size = 0
 | 
			
		||||
 | 
			
		||||
  for len = 1,maxlen do
 | 
			
		||||
    local strs = len_buckets[len]
 | 
			
		||||
    if strs then
 | 
			
		||||
      -- the best position so far generates `best_bucket`
 | 
			
		||||
      -- with `minsize` worst case collisions
 | 
			
		||||
      local bestpos, minsize, best_bucket = nil, #strs*2, nil
 | 
			
		||||
      for pos = 1,len do
 | 
			
		||||
        local try_bucket = {}
 | 
			
		||||
        for _,str in ipairs(strs) do
 | 
			
		||||
          local poschar = string.sub(str, pos, pos)
 | 
			
		||||
          table.insert(setdefault(try_bucket, poschar), str)
 | 
			
		||||
        end
 | 
			
		||||
        local maxsize = 1
 | 
			
		||||
        for _,pos_strs in pairs(try_bucket) do
 | 
			
		||||
          maxsize = math.max(maxsize, #pos_strs)
 | 
			
		||||
        end
 | 
			
		||||
        if maxsize < minsize then
 | 
			
		||||
          bestpos = pos
 | 
			
		||||
          minsize = maxsize
 | 
			
		||||
          best_bucket = try_bucket
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
      len_pos_buckets[len] = {bestpos, best_bucket}
 | 
			
		||||
      worst_buck_size = math.max(worst_buck_size, minsize)
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
  return len_pos_buckets, maxlen, worst_buck_size
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function M.switcher(put, tab, maxlen, worst_buck_size)
 | 
			
		||||
  local neworder = {}
 | 
			
		||||
  put "  switch (len) {\n"
 | 
			
		||||
  local bucky = worst_buck_size > 1
 | 
			
		||||
  for len = 1,maxlen do
 | 
			
		||||
    local vals = tab[len]
 | 
			
		||||
    if vals then
 | 
			
		||||
      put("    case "..len..": ")
 | 
			
		||||
      local pos, posbuck = unpack(vals)
 | 
			
		||||
      local keys = vim.tbl_keys(posbuck)
 | 
			
		||||
      if #keys > 1 then
 | 
			
		||||
        table.sort(keys)
 | 
			
		||||
        put("switch (str["..(pos-1).."]) {\n")
 | 
			
		||||
        for _,c in ipairs(keys) do
 | 
			
		||||
          local buck = posbuck[c]
 | 
			
		||||
          local startidx = #neworder
 | 
			
		||||
          vim.list_extend(neworder, buck)
 | 
			
		||||
          local endidx = #neworder
 | 
			
		||||
          put("      case '"..c.."': ")
 | 
			
		||||
          put("low = "..startidx.."; ")
 | 
			
		||||
          if bucky then put("high = "..endidx.."; ") end
 | 
			
		||||
          put "break;\n"
 | 
			
		||||
        end
 | 
			
		||||
        put "      default: break;\n"
 | 
			
		||||
        put "    }\n  "
 | 
			
		||||
      else
 | 
			
		||||
          local startidx = #neworder
 | 
			
		||||
          table.insert(neworder, posbuck[keys[1]][1])
 | 
			
		||||
          local endidx = #neworder
 | 
			
		||||
          put("low = "..startidx.."; ")
 | 
			
		||||
          if bucky then put("high = "..endidx.."; ") end
 | 
			
		||||
      end
 | 
			
		||||
      put "  break;\n"
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
  put "    default: break;\n"
 | 
			
		||||
  put "  }\n"
 | 
			
		||||
  return neworder
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function M.hashy_hash(name, strings, access)
 | 
			
		||||
  local stats = {}
 | 
			
		||||
  local put = function(str) table.insert(stats, str) end
 | 
			
		||||
  local len_pos_buckets, maxlen, worst_buck_size = M.build_pos_hash(strings)
 | 
			
		||||
  put("int "..name.."_hash(const char *str, size_t len)\n{\n")
 | 
			
		||||
  if worst_buck_size > 1 then
 | 
			
		||||
    put("  int low = 0, high = 0;\n")
 | 
			
		||||
  else
 | 
			
		||||
    put("  int low = -1;\n")
 | 
			
		||||
  end
 | 
			
		||||
  local neworder = M.switcher(put, len_pos_buckets, maxlen, worst_buck_size)
 | 
			
		||||
  if worst_buck_size > 1 then
 | 
			
		||||
    error [[ not implemented yet ]] -- TODO(bfredl)
 | 
			
		||||
  else
 | 
			
		||||
    put [[
 | 
			
		||||
  if (low < 0) {
 | 
			
		||||
    return -1;
 | 
			
		||||
  }
 | 
			
		||||
  ]]
 | 
			
		||||
    put("if(memcmp(str, "..access("low")..", len)) {\n    return -1;\n  }\n")
 | 
			
		||||
    put "  return low;\n"
 | 
			
		||||
    put "}\n\n"
 | 
			
		||||
  end
 | 
			
		||||
  return neworder, table.concat(stats)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
return M
 | 
			
		||||
@@ -56,6 +56,8 @@ struct map_arguments {
 | 
			
		||||
  size_t orig_rhs_len;
 | 
			
		||||
};
 | 
			
		||||
typedef struct map_arguments MapArguments;
 | 
			
		||||
#define MAP_ARGUMENTS_INIT { false, false, false, false, false, false, false, \
 | 
			
		||||
                             { 0 }, 0, NULL, 0, false, NULL, 0 }
 | 
			
		||||
 | 
			
		||||
#define KEYLEN_PART_KEY -1  // keylen value for incomplete key-code
 | 
			
		||||
#define KEYLEN_PART_MAP -2  // keylen value for incomplete mapping
 | 
			
		||||
 
 | 
			
		||||
@@ -1299,3 +1299,25 @@ void nlua_init_types(lua_State *const lstate)
 | 
			
		||||
 | 
			
		||||
  lua_rawset(lstate, -3);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void nlua_pop_keydict(lua_State *L, void *retval, field_hash hashy, Error *err)
 | 
			
		||||
{
 | 
			
		||||
  lua_pushnil(L);  // [dict, nil]
 | 
			
		||||
  while (lua_next(L, -2)) {
 | 
			
		||||
    // [dict, key, value]
 | 
			
		||||
    size_t len;
 | 
			
		||||
    const char *s = lua_tolstring(L, -2, &len);
 | 
			
		||||
    Object *field = hashy(retval, s, len);
 | 
			
		||||
    if (!field) {
 | 
			
		||||
      api_set_error(err, kErrorTypeValidation, "invalid key: %.*s", (int)len, s);
 | 
			
		||||
      lua_pop(L, 3);  // []
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    *field = nlua_pop_Object(L, true, err);
 | 
			
		||||
  }
 | 
			
		||||
  // [dict]
 | 
			
		||||
  lua_pop(L, 1);
 | 
			
		||||
  // []
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -15,6 +15,7 @@
 | 
			
		||||
#include "nvim/vim.h"
 | 
			
		||||
 | 
			
		||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
 | 
			
		||||
# include "keysets.generated.h"
 | 
			
		||||
# include "msgpack_rpc/helpers.c.generated.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -19,5 +19,6 @@ if module.test_luajit_prg == '' then
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
table.insert(module.include_paths, "${CMAKE_BINARY_DIR}/include")
 | 
			
		||||
table.insert(module.include_paths, "${CMAKE_BINARY_DIR}/src/nvim/auto")
 | 
			
		||||
 | 
			
		||||
return module
 | 
			
		||||
 
 | 
			
		||||
@@ -21,7 +21,7 @@ describe('nvim_get_commands', function()
 | 
			
		||||
  it('validates input', function()
 | 
			
		||||
    eq('builtin=true not implemented', pcall_err(meths.get_commands,
 | 
			
		||||
      {builtin=true}))
 | 
			
		||||
    eq('unexpected key: foo', pcall_err(meths.get_commands,
 | 
			
		||||
    eq("Invalid key: 'foo'", pcall_err(meths.get_commands,
 | 
			
		||||
      {foo='blah'}))
 | 
			
		||||
  end)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -436,16 +436,16 @@ describe('nvim_set_keymap, nvim_del_keymap', function()
 | 
			
		||||
  end)
 | 
			
		||||
 | 
			
		||||
  it('error on invalid optnames', function()
 | 
			
		||||
    eq('Invalid key: silentt',
 | 
			
		||||
    eq("Invalid key: 'silentt'",
 | 
			
		||||
      pcall_err(meths.set_keymap, 'n', 'lhs', 'rhs', {silentt = true}))
 | 
			
		||||
    eq('Invalid key: sidd',
 | 
			
		||||
    eq("Invalid key: 'sidd'",
 | 
			
		||||
      pcall_err(meths.set_keymap, 'n', 'lhs', 'rhs', {sidd = false}))
 | 
			
		||||
    eq('Invalid key: nowaiT',
 | 
			
		||||
    eq("Invalid key: 'nowaiT'",
 | 
			
		||||
      pcall_err(meths.set_keymap, 'n', 'lhs', 'rhs', {nowaiT = false}))
 | 
			
		||||
  end)
 | 
			
		||||
 | 
			
		||||
  it('error on <buffer> option key', function()
 | 
			
		||||
    eq('Invalid key: buffer',
 | 
			
		||||
    eq("Invalid key: 'buffer'",
 | 
			
		||||
      pcall_err(meths.set_keymap, 'n', 'lhs', 'rhs', {buffer = true}))
 | 
			
		||||
  end)
 | 
			
		||||
 | 
			
		||||
@@ -454,8 +454,8 @@ describe('nvim_set_keymap, nvim_del_keymap', function()
 | 
			
		||||
    -- note: need '%' to escape hyphens, which have special meaning in lua
 | 
			
		||||
    it('throws an error when given non-boolean value for '..opt, function()
 | 
			
		||||
      local opts = {}
 | 
			
		||||
      opts[opt] = 2
 | 
			
		||||
      eq('Gave non-boolean value for an opt: '..opt,
 | 
			
		||||
      opts[opt] = 'fooo'
 | 
			
		||||
      eq(opt..' is not a boolean',
 | 
			
		||||
        pcall_err(meths.set_keymap, 'n', 'lhs', 'rhs', opts))
 | 
			
		||||
    end)
 | 
			
		||||
  end
 | 
			
		||||
 
 | 
			
		||||
@@ -1119,7 +1119,7 @@ describe('API', function()
 | 
			
		||||
 | 
			
		||||
  describe('nvim_get_context', function()
 | 
			
		||||
    it('validates args', function()
 | 
			
		||||
      eq('unexpected key: blah',
 | 
			
		||||
      eq("Invalid key: 'blah'",
 | 
			
		||||
        pcall_err(nvim, 'get_context', {blah={}}))
 | 
			
		||||
      eq('invalid value for key: types',
 | 
			
		||||
        pcall_err(nvim, 'get_context', {types=42}))
 | 
			
		||||
 
 | 
			
		||||
@@ -1529,7 +1529,7 @@ describe('float window', function()
 | 
			
		||||
 | 
			
		||||
    it('API has proper error messages', function()
 | 
			
		||||
      local buf = meths.create_buf(false,false)
 | 
			
		||||
      eq("Invalid key 'bork'",
 | 
			
		||||
      eq("Invalid key: 'bork'",
 | 
			
		||||
         pcall_err(meths.open_win,buf, false, {width=20,height=2,bork=true}))
 | 
			
		||||
      eq("'win' key is only valid with relative='win'",
 | 
			
		||||
         pcall_err(meths.open_win,buf, false, {width=20,height=2,relative='editor',row=0,col=0,win=0}))
 | 
			
		||||
@@ -1542,13 +1542,15 @@ describe('float window', function()
 | 
			
		||||
      eq("'relative' requires 'row'/'col' or 'bufpos'",
 | 
			
		||||
         pcall_err(meths.open_win,buf, false, {width=20,height=2,relative='editor'}))
 | 
			
		||||
      eq("'width' key must be a positive Integer",
 | 
			
		||||
         pcall_err(meths.open_win,buf, false, {width=-1,height=2,relative='editor'}))
 | 
			
		||||
         pcall_err(meths.open_win,buf, false, {width=-1,height=2,relative='editor', row=0, col=0}))
 | 
			
		||||
      eq("'height' key must be a positive Integer",
 | 
			
		||||
         pcall_err(meths.open_win,buf, false, {width=20,height=-1,relative='editor'}))
 | 
			
		||||
         pcall_err(meths.open_win,buf, false, {width=20,height=-1,relative='editor', row=0, col=0}))
 | 
			
		||||
      eq("'height' key must be a positive Integer",
 | 
			
		||||
         pcall_err(meths.open_win,buf, false, {width=20,height=0,relative='editor'}))
 | 
			
		||||
      eq("Must specify 'width' and 'height'",
 | 
			
		||||
         pcall_err(meths.open_win,buf, false, {relative='editor'}))
 | 
			
		||||
         pcall_err(meths.open_win,buf, false, {width=20,height=0,relative='editor', row=0, col=0}))
 | 
			
		||||
      eq("Must specify 'width'",
 | 
			
		||||
         pcall_err(meths.open_win,buf, false, {relative='editor', row=0, col=0}))
 | 
			
		||||
      eq("Must specify 'height'",
 | 
			
		||||
         pcall_err(meths.open_win,buf, false, {relative='editor', row=0, col=0, width=2}))
 | 
			
		||||
    end)
 | 
			
		||||
 | 
			
		||||
    it('can be placed relative window or cursor', function()
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user