mirror of
				https://github.com/neovim/neovim.git
				synced 2025-10-26 12:27:24 +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'(?<!\bPMap)' | ||||||
|                    r'(?<!\bArrayOf)' |                    r'(?<!\bArrayOf)' | ||||||
|                    r'(?<!\bDictionaryOf)' |                    r'(?<!\bDictionaryOf)' | ||||||
|  |                    r'(?<!\bDict)' | ||||||
|                    r'\((?:const )?(?:struct )?[a-zA-Z_]\w*(?: *\*(?:const)?)*\)' |                    r'\((?:const )?(?:struct )?[a-zA-Z_]\w*(?: *\*(?:const)?)*\)' | ||||||
|                    r' +' |                    r' +' | ||||||
|                    r'-?(?:\*+|&)?(?:\w+|\+\+|--|\()', cast_line) |                    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_DISPATCH_GENERATOR ${GENERATOR_DIR}/gen_api_dispatch.lua) | ||||||
| set(API_UI_EVENTS_GENERATOR ${GENERATOR_DIR}/gen_api_ui_events.lua) | set(API_UI_EVENTS_GENERATOR ${GENERATOR_DIR}/gen_api_ui_events.lua) | ||||||
| set(GENERATOR_C_GRAMMAR ${GENERATOR_DIR}/c_grammar.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(API_METADATA ${PROJECT_BINARY_DIR}/api_metadata.mpack) | ||||||
| set(FUNCS_DATA ${PROJECT_BINARY_DIR}/funcs_data.mpack) | set(FUNCS_DATA ${PROJECT_BINARY_DIR}/funcs_data.mpack) | ||||||
| set(LUA_API_C_BINDINGS ${GENERATED_DIR}/lua_api_c_bindings.generated.c) | 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_ENUM ${GENERATED_INCLUDES_DIR}/ex_cmds_enum.generated.h) | ||||||
| set(GENERATED_EX_CMDS_DEFS ${GENERATED_DIR}/ex_cmds_defs.generated.h) | set(GENERATED_EX_CMDS_DEFS ${GENERATED_DIR}/ex_cmds_defs.generated.h) | ||||||
| set(GENERATED_FUNCS ${GENERATED_DIR}/funcs.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_ENUM ${GENERATED_INCLUDES_DIR}/auevents_enum.generated.h) | ||||||
| set(GENERATED_EVENTS_NAMES_MAP ${GENERATED_DIR}/auevents_name_map.generated.h) | set(GENERATED_EVENTS_NAMES_MAP ${GENERATED_DIR}/auevents_name_map.generated.h) | ||||||
| set(GENERATED_OPTIONS ${GENERATED_DIR}/options.generated.h) | set(GENERATED_OPTIONS ${GENERATED_DIR}/options.generated.h) | ||||||
| set(EX_CMDS_GENERATOR ${GENERATOR_DIR}/gen_ex_cmds.lua) | set(EX_CMDS_GENERATOR ${GENERATOR_DIR}/gen_ex_cmds.lua) | ||||||
| set(FUNCS_GENERATOR ${GENERATOR_DIR}/gen_eval.lua) | set(FUNCS_GENERATOR ${GENERATOR_DIR}/gen_eval.lua) | ||||||
| set(EVENTS_GENERATOR ${GENERATOR_DIR}/gen_events.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(OPTIONS_GENERATOR ${GENERATOR_DIR}/gen_options.lua) | ||||||
| set(UNICODE_TABLES_GENERATOR ${GENERATOR_DIR}/gen_unicode_tables.lua) | set(UNICODE_TABLES_GENERATOR ${GENERATOR_DIR}/gen_unicode_tables.lua) | ||||||
| set(UNICODE_DIR ${PROJECT_SOURCE_DIR}/unicode) | set(UNICODE_DIR ${PROJECT_SOURCE_DIR}/unicode) | ||||||
| @@ -261,6 +265,7 @@ foreach(sfile ${NVIM_SOURCES} | |||||||
|               "${GENERATED_UI_EVENTS_CALL}" |               "${GENERATED_UI_EVENTS_CALL}" | ||||||
|               "${GENERATED_UI_EVENTS_REMOTE}" |               "${GENERATED_UI_EVENTS_REMOTE}" | ||||||
|               "${GENERATED_UI_EVENTS_BRIDGE}" |               "${GENERATED_UI_EVENTS_BRIDGE}" | ||||||
|  |               "${GENERATED_KEYSETS}" | ||||||
|               ) |               ) | ||||||
|   get_filename_component(full_d ${sfile} PATH) |   get_filename_component(full_d ${sfile} PATH) | ||||||
|   file(RELATIVE_PATH d "${CMAKE_CURRENT_LIST_DIR}" "${full_d}") |   file(RELATIVE_PATH d "${CMAKE_CURRENT_LIST_DIR}" "${full_d}") | ||||||
| @@ -364,12 +369,14 @@ add_custom_command( | |||||||
| list(APPEND NVIM_GENERATED_FOR_HEADERS | list(APPEND NVIM_GENERATED_FOR_HEADERS | ||||||
|   "${GENERATED_EX_CMDS_ENUM}" |   "${GENERATED_EX_CMDS_ENUM}" | ||||||
|   "${GENERATED_EVENTS_ENUM}" |   "${GENERATED_EVENTS_ENUM}" | ||||||
|  |   "${GENERATED_KEYSETS_DEFS}" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| list(APPEND NVIM_GENERATED_FOR_SOURCES | list(APPEND NVIM_GENERATED_FOR_SOURCES | ||||||
|   "${GENERATED_API_DISPATCH}" |   "${GENERATED_API_DISPATCH}" | ||||||
|   "${GENERATED_EX_CMDS_DEFS}" |   "${GENERATED_EX_CMDS_DEFS}" | ||||||
|   "${GENERATED_EVENTS_NAMES_MAP}" |   "${GENERATED_EVENTS_NAMES_MAP}" | ||||||
|  |   "${GENERATED_KEYSETS}" | ||||||
|   "${GENERATED_OPTIONS}" |   "${GENERATED_OPTIONS}" | ||||||
|   "${GENERATED_UNICODE_TABLES}" |   "${GENERATED_UNICODE_TABLES}" | ||||||
|   "${VIM_MODULE_FILE}" |   "${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 |   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} | add_custom_command(OUTPUT ${GENERATED_OPTIONS} | ||||||
|   COMMAND ${LUA_PRG} ${OPTIONS_GENERATOR} |   COMMAND ${LUA_PRG} ${OPTIONS_GENERATOR} | ||||||
|                      ${CMAKE_CURRENT_LIST_DIR} ${GENERATED_OPTIONS} |                      ${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()| | /// @see |nvim_set_keymap()| | ||||||
| /// | /// | ||||||
| /// @param  buffer  Buffer handle, or 0 for current buffer | /// @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) |                          Error *err) | ||||||
|   FUNC_API_SINCE(6) |   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) |   FUNC_API_SINCE(6) | ||||||
| { | { | ||||||
|   String rhs = { .data = "", .size = 0 }; |   String rhs = { .data = "", .size = 0 }; | ||||||
|   Dictionary opts = ARRAY_DICT_INIT; |   modify_keymap(buffer, true, mode, lhs, rhs, NULL, err); | ||||||
|   modify_keymap(buffer, true, mode, lhs, rhs, opts, err); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| /// Gets a map of buffer-local |user-commands|. | /// 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. | /// @param[out]  err   Error details, if any. | ||||||
| /// | /// | ||||||
| /// @returns Map of maps describing commands. | /// @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) |   FUNC_API_SINCE(4) | ||||||
| { | { | ||||||
|   bool global = (buffer == -1); |   bool global = (buffer == -1); | ||||||
|   bool builtin = false; |   bool builtin = api_object_to_bool(opts->builtin, "builtin", false, err); | ||||||
|  |   if (ERROR_SET(err)) { | ||||||
|   for (size_t i = 0; i < opts.size; i++) { |     return (Dictionary)ARRAY_DICT_INIT; | ||||||
|     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; |  | ||||||
|     } |  | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   if (global) { |   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 | /// @param[out]  err   Error details, if any | ||||||
| /// @return Id of the created/updated extmark | /// @return Id of the created/updated extmark | ||||||
| Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer col, | 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) |   FUNC_API_SINCE(7) | ||||||
| { | { | ||||||
|   buf_T *buf = find_buffer_by_handle(buffer, err); |   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; |     return 0; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   bool ephemeral = false; |  | ||||||
|  |  | ||||||
|   uint64_t id = 0; |   uint64_t id = 0; | ||||||
|   int line2 = -1; |   if (opts->id.type == kObjectTypeInteger && opts->id.data.integer > 0) { | ||||||
|   Decoration decor = DECORATION_INIT; |     id = (uint64_t)opts->id.data.integer; | ||||||
|   colnr_T col2 = -1; |   } else if (HAS_KEY(opts->id)) { | ||||||
|  |       api_set_error(err, kErrorTypeValidation, "id is not a positive integer"); | ||||||
|  |       goto error; | ||||||
|  |   } | ||||||
|  |  | ||||||
|   bool right_gravity = true; |   int line2 = -1; | ||||||
|   bool end_right_gravity = false; |   if (opts->end_line.type == kObjectTypeInteger) { | ||||||
|   bool end_gravity_set = false; |     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; |   VirtLines virt_lines = KV_INITIAL_VALUE; | ||||||
|   bool virt_lines_above = false; |   bool virt_lines_above = false; | ||||||
|   bool virt_lines_leftcol = false; |   bool virt_lines_leftcol = false; | ||||||
|  |  | ||||||
|   for (size_t i = 0; i < opts.size; i++) { |   if (opts->virt_lines.type == kObjectTypeArray) { | ||||||
|     String k = opts.items[i].key; |     Array a = opts->virt_lines.data.array; | ||||||
|     Object *v = &opts.items[i].value; |     for (size_t j = 0; j < a.size; j++) { | ||||||
|     if (strequal("id", k.data)) { |       if (a.items[j].type != kObjectTypeArray) { | ||||||
|       if (v->type != kObjectTypeInteger || v->data.integer <= 0) { |         api_set_error(err, kErrorTypeValidation, "virt_text_line item is not an Array"); | ||||||
|         api_set_error(err, kErrorTypeValidation, |  | ||||||
|                       "id is not a positive integer"); |  | ||||||
|         goto error; |         goto error; | ||||||
|       } |       } | ||||||
|  |       int dummig; | ||||||
|       id = (uint64_t)v->data.integer; |       VirtText jtem = parse_virt_text(a.items[j].data.array, err, &dummig); | ||||||
|     } else if (strequal("end_line", k.data)) { |       kv_push(virt_lines, jtem); | ||||||
|       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); |  | ||||||
|       if (ERROR_SET(err)) { |       if (ERROR_SET(err)) { | ||||||
|         goto error; |         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; |   size_t len = 0; | ||||||
|  |  | ||||||
|  |   bool ephemeral = false; | ||||||
|  |   OPTION_TO_BOOL(ephemeral, ephemeral, false); | ||||||
|  |  | ||||||
|   if (line < 0 || line > buf->b_ml.ml_line_count) { |   if (line < 0 || line > buf->b_ml.ml_line_count) { | ||||||
|     api_set_error(err, kErrorTypeValidation, "line value outside range"); |     api_set_error(err, kErrorTypeValidation, "line value outside range"); | ||||||
|     return 0; |     return 0; | ||||||
| @@ -1717,15 +1670,6 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer | |||||||
|     return 0; |     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 (col2 >= 0) { | ||||||
|     if (line2 >= 0 && line2 < buf->b_ml.ml_line_count) { |     if (line2 >= 0 && line2 < buf->b_ml.ml_line_count) { | ||||||
|       len = ephemeral ? MAXCOL : STRLEN(ml_get_buf(buf, (linenr_T)line2 + 1, false)); |       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; |     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; |   Decoration *d = NULL; | ||||||
|  |  | ||||||
|   if (ephemeral) { |   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 | #ifdef INCLUDE_GENERATED_DECLARATIONS | ||||||
| # define ArrayOf(...) Array | # define ArrayOf(...) Array | ||||||
| # define DictionaryOf(...) Dictionary | # define DictionaryOf(...) Dictionary | ||||||
|  | # define Dict(name) KeyDict_##name | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| // Basic types | // Basic types | ||||||
| @@ -129,5 +130,14 @@ struct key_value_pair { | |||||||
|   Object value; |   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 | #endif  // NVIM_API_PRIVATE_DEFS_H | ||||||
|   | |||||||
| @@ -6,6 +6,7 @@ | |||||||
| #include <stdbool.h> | #include <stdbool.h> | ||||||
| #include <stdlib.h> | #include <stdlib.h> | ||||||
| #include <string.h> | #include <string.h> | ||||||
|  | #include <stddef.h> | ||||||
|  |  | ||||||
| #include "nvim/api/private/defs.h" | #include "nvim/api/private/defs.h" | ||||||
| #include "nvim/api/private/helpers.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") | ///                   buffer, or -1 to signify global behavior ("all buffers") | ||||||
| /// @param  is_unmap  When true, removes the mapping that matches {lhs}. | /// @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, | 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_msg = NULL;  // the error message to report, if any | ||||||
|   char *err_arg = NULL;  // argument for the error message format string |   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; |     return; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   MapArguments parsed_args; |   MapArguments parsed_args = MAP_ARGUMENTS_INIT; | ||||||
|   memset(&parsed_args, 0, sizeof(parsed_args)); |   if (opts) { | ||||||
|   if (parse_keymap_opts(opts, &parsed_args, err)) { | #define KEY_TO_BOOL(name) \ | ||||||
|     goto fail_and_free; |     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; |   parsed_args.buffer = !global; | ||||||
|  |  | ||||||
| @@ -947,95 +959,6 @@ fail_and_free: | |||||||
|   return; |   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 | /// Collects `n` buffer lines into array `l`, optionally replacing newlines | ||||||
| /// with NUL. | /// 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) |                         Error *err) | ||||||
| { | { | ||||||
|   // TODO(bfredl): use a get/has_key interface instead and get rid of extra |   bool has_relative = false, relative_is_win = false; | ||||||
|   // flags |   if (config->relative.type == kObjectTypeString) { | ||||||
|   bool has_row = false, has_col = false, has_relative = false; |     // ignore empty string, to match nvim_win_get_config | ||||||
|   bool has_external = false, has_window = false; |     if (config->relative.data.string.size > 0) { | ||||||
|   bool has_width = false, has_height = false; |       if (!parse_float_relative(config->relative.data.string, &fconfig->relative)) { | ||||||
|   bool has_bufpos = false; |         api_set_error(err, kErrorTypeValidation, "Invalid value of 'relative' key"); | ||||||
|  |         return false; | ||||||
|  |       } | ||||||
|  |  | ||||||
|   for (size_t i = 0; i < config.size; i++) { |       if (!(HAS_KEY(config->row) && HAS_KEY(config->col)) && !HAS_KEY(config->bufpos)) { | ||||||
|     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, |         api_set_error(err, kErrorTypeValidation, | ||||||
|                       "'row' key must be Integer or Float"); |                       "'relative' requires 'row'/'col' or 'bufpos'"); | ||||||
|         return false; |         return false; | ||||||
|       } |       } | ||||||
|     } else if (!strcmp(key, "col")) { |  | ||||||
|       has_col = true; |       has_relative = true; | ||||||
|       if (val.type == kObjectTypeInteger) { |       fconfig->external = false; | ||||||
|         fconfig->col = (double)val.data.integer; |       if (fconfig->relative == kFloatRelativeWindow) { | ||||||
|       } else if (val.type == kObjectTypeFloat) { |         relative_is_win = true; | ||||||
|         fconfig->col = val.data.floating; |         fconfig->bufpos.lnum = -1; | ||||||
|       } 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; |  | ||||||
|       } |       } | ||||||
|  |     } | ||||||
|  |   } 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 { |     } else { | ||||||
|       api_set_error(err, kErrorTypeValidation, |       api_set_error(err, kErrorTypeValidation, | ||||||
|                     "Invalid key '%s'", key); |                     "'row' key must be Integer or Float"); | ||||||
|       return false; |       return false; | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   if (has_window && !(has_relative |   if (HAS_KEY(config->col)) { | ||||||
|                       && fconfig->relative == kFloatRelativeWindow)) { |     if (!has_relative) { | ||||||
|     api_set_error(err, kErrorTypeValidation, |       api_set_error(err, kErrorTypeValidation, "non-float cannot have 'col'"); | ||||||
|                   "'win' key is only valid with relative='win'"); |       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; |     return false; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   if ((has_relative && fconfig->relative == kFloatRelativeWindow) |   if (config->height.type == kObjectTypeInteger && config->height.data.integer > 0) { | ||||||
|       && (!has_window || fconfig->window == 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; |     fconfig->window = curwin->handle; | ||||||
|   } |     if (config->win.type == kObjectTypeInteger || config->win.type == kObjectTypeWindow) { | ||||||
|  |       if (config->win.data.integer > 0) { | ||||||
|   if (has_window && !has_bufpos) { |         fconfig->window = (Window)config->win.data.integer; | ||||||
|     fconfig->bufpos.lnum = -1; |       } | ||||||
|   } |     } else if (HAS_KEY(config->win)) { | ||||||
|  |       api_set_error(err, kErrorTypeValidation, "'win' key must be Integer or Window"); | ||||||
|   if (has_bufpos) { |       return false; | ||||||
|     if (!has_row) { |  | ||||||
|       fconfig->row = (fconfig->anchor & kFloatAnchorSouth) ? 0 : 1; |  | ||||||
|       has_row = true; |  | ||||||
|     } |     } | ||||||
|     if (!has_col) { |   } else { | ||||||
|       fconfig->col = 0; |     if (HAS_KEY(config->win)) { | ||||||
|       has_col = true; |       api_set_error(err, kErrorTypeValidation, "'win' key is only valid with relative='win'"); | ||||||
|  |       return false; | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   if (has_relative && has_external) { |   if (HAS_KEY(config->external)) { | ||||||
|     api_set_error(err, kErrorTypeValidation, |     fconfig->external = api_object_to_bool(config->external, "'external' key", false, err); | ||||||
|                   "Only one of 'relative' and 'external' must be used"); |     if (ERROR_SET(err)) { | ||||||
|     return false; |       return false; | ||||||
|   } else if (!reconf && !has_relative && !has_external) { |     } | ||||||
|  |     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, |     api_set_error(err, kErrorTypeValidation, | ||||||
|                   "One of 'relative' and 'external' must be used"); |                   "One of 'relative' and 'external' must be used"); | ||||||
|     return false; |     return false; | ||||||
|   } else if (has_relative) { |  | ||||||
|     fconfig->external = false; |  | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   if (!reconf && !(has_height && has_width)) { |  | ||||||
|     api_set_error(err, kErrorTypeValidation, |   if (HAS_KEY(config->focusable)) { | ||||||
|                   "Must specify 'width' and 'height'"); |     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; |     return false; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   if (fconfig->external && !ui_has(kUIMultigrid)) { |   if (HAS_KEY(config->border)) { | ||||||
|     api_set_error(err, kErrorTypeValidation, |     parse_border_style(config->border, fconfig, err); | ||||||
|                   "UI doesn't support external windows"); |     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; |     return false; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   if (has_relative != has_row || has_row != has_col) { |   if (HAS_KEY(config->noautocmd)) { | ||||||
|     api_set_error(err, kErrorTypeValidation, |     if (!new_win) { | ||||||
|                   "'relative' requires 'row'/'col' or 'bufpos'"); |       api_set_error(err, kErrorTypeValidation, "Invalid key: 'noautocmd'"); | ||||||
|     return false; |       return false; | ||||||
|  |     } | ||||||
|  |     fconfig->noautocmd = api_object_to_bool(config->noautocmd, "'noautocmd' key", false, err); | ||||||
|  |     if (ERROR_SET(err)) { | ||||||
|  |       return false; | ||||||
|  |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   return true; |   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 NIL ((Object)OBJECT_INIT) | ||||||
| #define NULL_STRING ((String)STRING_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) \ | #define PUT(dict, k, v) \ | ||||||
|   kv_push(dict, ((KeyValuePair) { .key = cstr_to_string(k), .value = v })) |   kv_push(dict, ((KeyValuePair) { .key = cstr_to_string(k), .value = v })) | ||||||
|  |  | ||||||
| @@ -138,6 +141,9 @@ typedef struct { | |||||||
|   } while (0) |   } while (0) | ||||||
|  |  | ||||||
| #ifdef INCLUDE_GENERATED_DECLARATIONS | #ifdef INCLUDE_GENERATED_DECLARATIONS | ||||||
|  | # include "keysets.h.generated.h" | ||||||
| # include "api/private/helpers.h.generated.h" | # include "api/private/helpers.h.generated.h" | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|  |  | ||||||
| #endif  // NVIM_API_PRIVATE_HELPERS_H | #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 | /// @param[out] err Error details, if any | ||||||
| /// | /// | ||||||
| /// @return Window handle, or 0 on error | /// @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_SINCE(6) | ||||||
|   FUNC_API_CHECK_TEXTLOCK |   FUNC_API_CHECK_TEXTLOCK | ||||||
| { | { | ||||||
| @@ -1761,24 +1761,15 @@ Dictionary nvim_get_color_map(void) | |||||||
| /// @param[out]  err  Error details, if any | /// @param[out]  err  Error details, if any | ||||||
| /// | /// | ||||||
| /// @return map of global |context|. | /// @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) |   FUNC_API_SINCE(6) | ||||||
| { | { | ||||||
|   Array types = ARRAY_DICT_INIT; |   Array types = ARRAY_DICT_INIT; | ||||||
|   for (size_t i = 0; i < opts.size; i++) { |   if (opts->types.type == kObjectTypeArray) { | ||||||
|     String k = opts.items[i].key; |     types = opts->types.data.array; | ||||||
|     Object v = opts.items[i].value; |   } else if (opts->types.type != kObjectTypeNil) { | ||||||
|     if (strequal("types", k.data)) { |     api_set_error(err, kErrorTypeValidation, "invalid value for key: types"); | ||||||
|       if (v.type != kObjectTypeArray) { |     return (Dictionary)ARRAY_DICT_INIT; | ||||||
|         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; |  | ||||||
|     } |  | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   int int_types = types.size > 0 ? 0 : kCtxAll; |   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|. | ///               as keys excluding |<buffer>| but including |noremap|. | ||||||
| ///               Values are Booleans. Unknown key is an error. | ///               Values are Booleans. Unknown key is an error. | ||||||
| /// @param[out]   err   Error details, if any. | /// @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) |   FUNC_API_SINCE(6) | ||||||
| { | { | ||||||
|   modify_keymap(-1, false, mode, lhs, rhs, opts, err); |   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. | /// @param[out]  err   Error details, if any. | ||||||
| /// | /// | ||||||
| /// @returns Map of maps describing commands. | /// @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) |   FUNC_API_SINCE(4) | ||||||
| { | { | ||||||
|   return nvim_buf_get_commands(-1, opts, err); |   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, | /// @param      config  Map defining the window configuration, | ||||||
| ///                     see |nvim_open_win()| | ///                     see |nvim_open_win()| | ||||||
| /// @param[out] err     Error details, if any | /// @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) |   FUNC_API_SINCE(6) | ||||||
| { | { | ||||||
|   win_T *win = find_window_by_handle(window, err); |   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_comment = P('//') * (not_nl ^ 0) | ||||||
| local c_preproc = P('#') * (not_nl ^ 0) | local c_preproc = P('#') * (not_nl ^ 0) | ||||||
| local typed_container = | local typed_container = | ||||||
|   (P('ArrayOf(') + P('DictionaryOf(')) * ((any - P(')')) ^ 1) * P(')') |   (P('ArrayOf(') + P('DictionaryOf(') + P('Dict(')) * ((any - P(')')) ^ 1) * P(')') | ||||||
| local c_id = ( | local c_id = ( | ||||||
|   typed_container + |   typed_container + | ||||||
|   (letter * (alpha ^ 0)) |   (letter * (alpha ^ 0)) | ||||||
|   | |||||||
| @@ -152,6 +152,8 @@ for _,f in ipairs(functions) do | |||||||
|     for i,param in ipairs(f.parameters) do |     for i,param in ipairs(f.parameters) do | ||||||
|       if param[1] == "DictionaryOf(LuaRef)" then |       if param[1] == "DictionaryOf(LuaRef)" then | ||||||
|         param = {"Dictionary", param[2]} |         param = {"Dictionary", param[2]} | ||||||
|  |       elseif startswith(param[1], "Dict(") then | ||||||
|  |         param = {"Dictionary", param[2]} | ||||||
|       end |       end | ||||||
|       f_exported.parameters[i] = param |       f_exported.parameters[i] = param | ||||||
|     end |     end | ||||||
| @@ -173,7 +175,10 @@ local output = io.open(dispatch_outputf, 'wb') | |||||||
|  |  | ||||||
| local function real_type(type) | local function real_type(type) | ||||||
|   local rv = 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 |     if rv:match('Array') then | ||||||
|       rv = 'Array' |       rv = 'Array' | ||||||
|     else |     else | ||||||
| @@ -209,8 +214,9 @@ for i = 1, #functions do | |||||||
|     -- Declare/initialize variables that will hold converted arguments |     -- Declare/initialize variables that will hold converted arguments | ||||||
|     for j = 1, #fn.parameters do |     for j = 1, #fn.parameters do | ||||||
|       local param = fn.parameters[j] |       local param = fn.parameters[j] | ||||||
|  |       local rt = real_type(param[1]) | ||||||
|       local converted = 'arg_'..j |       local converted = 'arg_'..j | ||||||
|       output:write('\n  '..param[1]..' '..converted..';') |       output:write('\n  '..rt..' '..converted..';') | ||||||
|     end |     end | ||||||
|     output:write('\n') |     output:write('\n') | ||||||
|     output:write('\n  if (args.size != '..#fn.parameters..') {') |     output:write('\n  if (args.size != '..#fn.parameters..') {') | ||||||
| @@ -225,7 +231,24 @@ for i = 1, #functions do | |||||||
|       param = fn.parameters[j] |       param = fn.parameters[j] | ||||||
|       converted = 'arg_'..j |       converted = 'arg_'..j | ||||||
|       local rt = real_type(param[1]) |       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 |         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 |           -- Buffer, Window, and Tabpage have a specific type, but are stored in integer | ||||||
|           output:write('\n  if (args.items['.. |           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]..'");') |           "Wrong type for argument '..j..' when calling '..fn.name..', expecting '..param[1]..'");') | ||||||
|         output:write('\n    goto cleanup;') |         output:write('\n    goto cleanup;') | ||||||
|         output:write('\n  }\n') |         output:write('\n  }\n') | ||||||
|       else |  | ||||||
|         output:write('\n  '..converted..' = args.items['..(j - 1)..'];\n') |  | ||||||
|       end |       end | ||||||
|  |  | ||||||
|       args[#args + 1] = converted |       args[#args + 1] = converted | ||||||
|     end |     end | ||||||
|  |  | ||||||
| @@ -423,13 +443,24 @@ local function process_function(fn) | |||||||
|     if param[1] == "DictionaryOf(LuaRef)" then |     if param[1] == "DictionaryOf(LuaRef)" then | ||||||
|       extra = "true, " |       extra = "true, " | ||||||
|     end |     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([[ |     write_shifted_output(output, string.format([[ | ||||||
|     const %s %s = nlua_pop_%s(lstate, %s&err); |  | ||||||
|  |  | ||||||
|     if (ERROR_SET(&err)) { |     if (ERROR_SET(&err)) { | ||||||
|       goto exit_%u; |       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( |     free_code[#free_code + 1] = ('api_free_%s(%s);'):format( | ||||||
|       lc_param_type, cparam) |       lc_param_type, cparam) | ||||||
|     cparams = cparam .. ', ' .. cparams |     cparams = cparam .. ', ' .. cparams | ||||||
| @@ -446,7 +477,7 @@ local function process_function(fn) | |||||||
|   for i = 1, #free_code do |   for i = 1, #free_code do | ||||||
|     local rev_i = #free_code - i + 1 |     local rev_i = #free_code - i + 1 | ||||||
|     local code = free_code[rev_i] |     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) |       free_at_exit_code = free_at_exit_code .. ('\n    %s'):format(code) | ||||||
|     else |     else | ||||||
|       free_at_exit_code = free_at_exit_code .. ('\n  exit_%u:\n    %s'):format( |       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( | local word = branch( | ||||||
|   concat( |   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(')')), |     one_or_more(any_character - lit(')')), | ||||||
|     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; |   size_t orig_rhs_len; | ||||||
| }; | }; | ||||||
| typedef struct map_arguments MapArguments; | 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_KEY -1  // keylen value for incomplete key-code | ||||||
| #define KEYLEN_PART_MAP -2  // keylen value for incomplete mapping | #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); |   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" | #include "nvim/vim.h" | ||||||
|  |  | ||||||
| #ifdef INCLUDE_GENERATED_DECLARATIONS | #ifdef INCLUDE_GENERATED_DECLARATIONS | ||||||
|  | # include "keysets.generated.h" | ||||||
| # include "msgpack_rpc/helpers.c.generated.h" | # include "msgpack_rpc/helpers.c.generated.h" | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|   | |||||||
| @@ -19,5 +19,6 @@ if module.test_luajit_prg == '' then | |||||||
|   end |   end | ||||||
| end | end | ||||||
| table.insert(module.include_paths, "${CMAKE_BINARY_DIR}/include") | table.insert(module.include_paths, "${CMAKE_BINARY_DIR}/include") | ||||||
|  | table.insert(module.include_paths, "${CMAKE_BINARY_DIR}/src/nvim/auto") | ||||||
|  |  | ||||||
| return module | return module | ||||||
|   | |||||||
| @@ -21,7 +21,7 @@ describe('nvim_get_commands', function() | |||||||
|   it('validates input', function() |   it('validates input', function() | ||||||
|     eq('builtin=true not implemented', pcall_err(meths.get_commands, |     eq('builtin=true not implemented', pcall_err(meths.get_commands, | ||||||
|       {builtin=true})) |       {builtin=true})) | ||||||
|     eq('unexpected key: foo', pcall_err(meths.get_commands, |     eq("Invalid key: 'foo'", pcall_err(meths.get_commands, | ||||||
|       {foo='blah'})) |       {foo='blah'})) | ||||||
|   end) |   end) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -436,16 +436,16 @@ describe('nvim_set_keymap, nvim_del_keymap', function() | |||||||
|   end) |   end) | ||||||
|  |  | ||||||
|   it('error on invalid optnames', function() |   it('error on invalid optnames', function() | ||||||
|     eq('Invalid key: silentt', |     eq("Invalid key: 'silentt'", | ||||||
|       pcall_err(meths.set_keymap, 'n', 'lhs', 'rhs', {silentt = true})) |       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})) |       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})) |       pcall_err(meths.set_keymap, 'n', 'lhs', 'rhs', {nowaiT = false})) | ||||||
|   end) |   end) | ||||||
|  |  | ||||||
|   it('error on <buffer> option key', function() |   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})) |       pcall_err(meths.set_keymap, 'n', 'lhs', 'rhs', {buffer = true})) | ||||||
|   end) |   end) | ||||||
|  |  | ||||||
| @@ -454,8 +454,8 @@ describe('nvim_set_keymap, nvim_del_keymap', function() | |||||||
|     -- note: need '%' to escape hyphens, which have special meaning in lua |     -- note: need '%' to escape hyphens, which have special meaning in lua | ||||||
|     it('throws an error when given non-boolean value for '..opt, function() |     it('throws an error when given non-boolean value for '..opt, function() | ||||||
|       local opts = {} |       local opts = {} | ||||||
|       opts[opt] = 2 |       opts[opt] = 'fooo' | ||||||
|       eq('Gave non-boolean value for an opt: '..opt, |       eq(opt..' is not a boolean', | ||||||
|         pcall_err(meths.set_keymap, 'n', 'lhs', 'rhs', opts)) |         pcall_err(meths.set_keymap, 'n', 'lhs', 'rhs', opts)) | ||||||
|     end) |     end) | ||||||
|   end |   end | ||||||
|   | |||||||
| @@ -1119,7 +1119,7 @@ describe('API', function() | |||||||
|  |  | ||||||
|   describe('nvim_get_context', function() |   describe('nvim_get_context', function() | ||||||
|     it('validates args', function() |     it('validates args', function() | ||||||
|       eq('unexpected key: blah', |       eq("Invalid key: 'blah'", | ||||||
|         pcall_err(nvim, 'get_context', {blah={}})) |         pcall_err(nvim, 'get_context', {blah={}})) | ||||||
|       eq('invalid value for key: types', |       eq('invalid value for key: types', | ||||||
|         pcall_err(nvim, 'get_context', {types=42})) |         pcall_err(nvim, 'get_context', {types=42})) | ||||||
|   | |||||||
| @@ -1529,7 +1529,7 @@ describe('float window', function() | |||||||
|  |  | ||||||
|     it('API has proper error messages', function() |     it('API has proper error messages', function() | ||||||
|       local buf = meths.create_buf(false,false) |       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})) |          pcall_err(meths.open_win,buf, false, {width=20,height=2,bork=true})) | ||||||
|       eq("'win' key is only valid with relative='win'", |       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})) |          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'", |       eq("'relative' requires 'row'/'col' or 'bufpos'", | ||||||
|          pcall_err(meths.open_win,buf, false, {width=20,height=2,relative='editor'})) |          pcall_err(meths.open_win,buf, false, {width=20,height=2,relative='editor'})) | ||||||
|       eq("'width' key must be a positive Integer", |       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", |       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", |       eq("'height' key must be a positive Integer", | ||||||
|          pcall_err(meths.open_win,buf, false, {width=20,height=0,relative='editor'})) |          pcall_err(meths.open_win,buf, false, {width=20,height=0,relative='editor', row=0, col=0})) | ||||||
|       eq("Must specify 'width' and 'height'", |       eq("Must specify 'width'", | ||||||
|          pcall_err(meths.open_win,buf, false, {relative='editor'})) |          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) |     end) | ||||||
|  |  | ||||||
|     it('can be placed relative window or cursor', function() |     it('can be placed relative window or cursor', function() | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Björn Linse
					Björn Linse