diff options
Diffstat (limited to 'src')
147 files changed, 23717 insertions, 15564 deletions
diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index 360993de68..017883a913 100755 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -156,34 +156,10 @@ endforeach() list(REMOVE_ITEM NVIM_SOURCES ${to_remove}) -# Legacy files that do not yet pass -Wconversion. -set(CONV_SOURCES - lua/treesitter.c - mbyte.c - memline.c - regexp.c - screen.c - search.c - spell.c - spellfile.c - syntax.c - window.c) -foreach(sfile ${CONV_SOURCES}) - if(NOT EXISTS "${CMAKE_CURRENT_LIST_DIR}/${sfile}") - message(FATAL_ERROR "${sfile} doesn't exist (it was added to CONV_SOURCES)") - endif() -endforeach() - if(NOT MSVC) - set_source_files_properties( - ${CONV_SOURCES} PROPERTIES COMPILE_FLAGS "${COMPILE_FLAGS} -Wno-conversion") - # xdiff, mpack, lua-cjson: inlined external project, we don't maintain it. #9306 set_source_files_properties( ${EXTERNAL_SOURCES} PROPERTIES COMPILE_FLAGS "${COMPILE_FLAGS} -Wno-conversion -Wno-missing-noreturn -Wno-missing-format-attribute -Wno-double-promotion") - - set_source_files_properties( - eval/funcs.c PROPERTIES COMPILE_FLAGS "${COMPILE_FLAGS} -Wno-conversion") endif() if(NOT "${MIN_LOG_LEVEL}" MATCHES "^$") @@ -230,6 +206,7 @@ add_custom_target(update_version_stamp -DNVIM_VERSION_PATCH=${NVIM_VERSION_PATCH} -DNVIM_VERSION_PRERELEASE=${NVIM_VERSION_PRERELEASE} -DOUTPUT=${NVIM_VERSION_GIT_H} + -DCMAKE_MESSAGE_LOG_LEVEL=${CMAKE_MESSAGE_LOG_LEVEL} -P ${PROJECT_SOURCE_DIR}/cmake/GenerateVersion.cmake WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} BYPRODUCTS ${NVIM_VERSION_GIT_H}) @@ -268,7 +245,7 @@ foreach(sfile ${NVIM_SOURCES} set(depends "${HEADER_GENERATOR}" "${sfile}") if("${f}" STREQUAL "version.c") # Ensure auto/versiondef_git.h exists after "make clean". - list(APPEND depends "${NVIM_VERSION_GIT_H}") + list(APPEND depends update_version_stamp "${NVIM_VERSION_GIT_H}") endif() add_custom_command( OUTPUT "${gf_c_h}" "${gf_h_h}" @@ -789,12 +766,27 @@ foreach(sfile ${LINT_NVIM_SOURCES}) endforeach() add_custom_target(lintc DEPENDS ${LINT_TARGETS}) +add_custom_target(uncrustify-version + COMMAND ${CMAKE_COMMAND} + -D UNCRUSTIFY_PRG=${UNCRUSTIFY_PRG} + -D CONFIG_FILE=${PROJECT_SOURCE_DIR}/src/uncrustify.cfg + -P ${PROJECT_SOURCE_DIR}/cmake/CheckUncrustifyVersion.cmake) + add_glob_targets( TARGET lintuncrustify COMMAND ${UNCRUSTIFY_PRG} FLAGS -c "${PROJECT_SOURCE_DIR}/src/uncrustify.cfg" -q --check FILES ${LINT_NVIM_SOURCES} ) +add_dependencies(lintuncrustify uncrustify-version) + +add_custom_target(formatc + COMMAND ${CMAKE_COMMAND} + -D FORMAT_PRG=${UNCRUSTIFY_PRG} + -D LANG=c + -P ${PROJECT_SOURCE_DIR}/cmake/Format.cmake + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}) +add_dependencies(formatc uncrustify-version) add_custom_target( lintcfull diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c index 806b649ce6..1b1a161226 100644 --- a/src/nvim/api/buffer.c +++ b/src/nvim/api/buffer.c @@ -538,9 +538,9 @@ void nvim_buf_set_text(uint64_t channel_id, Buffer buffer, Integer start_row, In Integer end_row, Integer end_col, ArrayOf(String) replacement, Error *err) FUNC_API_SINCE(7) { - FIXED_TEMP_ARRAY(scratch, 1); + MAXSIZE_TEMP_ARRAY(scratch, 1); if (replacement.size == 0) { - scratch.items[0] = STRING_OBJ(STATIC_CSTR_AS_STRING("")); + ADD_C(scratch, STRING_OBJ(STATIC_CSTR_AS_STRING(""))); replacement = scratch; } @@ -932,7 +932,7 @@ Integer nvim_buf_get_changedtick(Buffer buffer, Error *err) /// @param mode Mode short-name ("n", "i", "v", ...) /// @param buffer Buffer handle, or 0 for current buffer /// @param[out] err Error details, if any -/// @returns Array of maparg()-like dictionaries describing mappings. +/// @returns Array of |maparg()|-like dictionaries describing mappings. /// The "buffer" key holds the associated buffer handle. ArrayOf(Dictionary) nvim_buf_get_keymap(uint64_t channel_id, Buffer buffer, String mode, Error *err) FUNC_API_SINCE(3) diff --git a/src/nvim/api/command.c b/src/nvim/api/command.c index 4c2404a0d8..33efa6b326 100644 --- a/src/nvim/api/command.c +++ b/src/nvim/api/command.c @@ -306,7 +306,7 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error char *cmdline = NULL; char *cmdname = NULL; - char **args = NULL; + ArrayOf(String) args; size_t argc = 0; String retv = (String)STRING_INIT; @@ -412,22 +412,15 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error } if (!argc_valid) { - argc = 0; // Ensure that args array isn't erroneously freed at the end. VALIDATION_ERROR("Incorrect number of arguments supplied"); } - if (argc != 0) { - args = xcalloc(argc, sizeof(char *)); - - for (size_t i = 0; i < argc; i++) { - args[i] = string_to_cstr(cmd->args.data.array.items[i].data.string); - } - } + args = cmd->args.data.array; } // Simply pass the first argument (if it exists) as the arg pointer to `set_cmd_addr_type()` // since it only ever checks the first argument. - set_cmd_addr_type(&ea, argc > 0 ? args[0] : NULL); + set_cmd_addr_type(&ea, argc > 0 ? args.items[0].data.string.data : NULL); if (HAS_KEY(cmd->range)) { if (!(ea.argt & EX_RANGE)) { @@ -600,7 +593,7 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error OBJ_TO_CMOD_FLAG(CMOD_SILENT, mods.silent, false, "'mods.silent'"); OBJ_TO_CMOD_FLAG(CMOD_ERRSILENT, mods.emsg_silent, false, "'mods.emsg_silent'"); - OBJ_TO_CMOD_FLAG(CMOD_UNSILENT, mods.silent, false, "'mods.unsilent'"); + OBJ_TO_CMOD_FLAG(CMOD_UNSILENT, mods.unsilent, false, "'mods.unsilent'"); OBJ_TO_CMOD_FLAG(CMOD_SANDBOX, mods.sandbox, false, "'mods.sandbox'"); OBJ_TO_CMOD_FLAG(CMOD_NOAUTOCMD, mods.noautocmd, false, "'mods.noautocmd'"); OBJ_TO_CMOD_FLAG(CMOD_BROWSE, mods.browse, false, "'mods.browse'"); @@ -676,10 +669,6 @@ end: xfree(cmdname); xfree(ea.args); xfree(ea.arglens); - for (size_t i = 0; i < argc; i++) { - xfree(args[i]); - } - xfree(args); return retv; @@ -704,12 +693,11 @@ static bool string_iswhite(String str) } /// Build cmdline string for command, used by `nvim_cmd()`. -/// -/// @return OK or FAIL. -static void build_cmdline_str(char **cmdlinep, exarg_T *eap, CmdParseInfo *cmdinfo, char **args, - size_t argc) +static void build_cmdline_str(char **cmdlinep, exarg_T *eap, CmdParseInfo *cmdinfo, + ArrayOf(String) args, size_t argc) { StringBuilder cmdline = KV_INITIAL_VALUE; + kv_resize(cmdline, 32); // Make it big enough to handle most typical commands // Add command modifiers if (cmdinfo->cmdmod.cmod_tab != 0) { @@ -779,11 +767,11 @@ static void build_cmdline_str(char **cmdlinep, exarg_T *eap, CmdParseInfo *cmdin // Keep the index of the position where command name starts, so eap->cmd can point to it. size_t cmdname_idx = cmdline.size; - kv_printf(cmdline, "%s", eap->cmd); + kv_concat(cmdline, eap->cmd); // Command bang. if (eap->argt & EX_BANG && eap->forceit) { - kv_printf(cmdline, "!"); + kv_concat(cmdline, "!"); } // Command register. @@ -791,29 +779,35 @@ static void build_cmdline_str(char **cmdlinep, exarg_T *eap, CmdParseInfo *cmdin kv_printf(cmdline, " %c", eap->regname); } - // Iterate through each argument and store the starting index and length of each argument - size_t *argidx = xcalloc(argc, sizeof(size_t)); eap->argc = argc; eap->arglens = xcalloc(argc, sizeof(size_t)); + size_t argstart_idx = cmdline.size; for (size_t i = 0; i < argc; i++) { - argidx[i] = cmdline.size + 1; // add 1 to account for the space. - eap->arglens[i] = STRLEN(args[i]); - kv_printf(cmdline, " %s", args[i]); + String s = args.items[i].data.string; + eap->arglens[i] = s.size; + kv_concat(cmdline, " "); + kv_concat_len(cmdline, s.data, s.size); } + // Done appending to cmdline, ensure it is NUL terminated + kv_push(cmdline, NUL); + // Now that all the arguments are appended, use the command index and argument indices to set the // values of eap->cmd, eap->arg and eap->args. eap->cmd = cmdline.items + cmdname_idx; eap->args = xcalloc(argc, sizeof(char *)); + size_t offset = argstart_idx; for (size_t i = 0; i < argc; i++) { - eap->args[i] = cmdline.items + argidx[i]; + offset++; // Account for space + eap->args[i] = cmdline.items + offset; + offset += eap->arglens[i]; } // If there isn't an argument, make eap->arg point to end of cmdline. - eap->arg = argc > 0 ? eap->args[0] : cmdline.items + cmdline.size; + eap->arg = argc > 0 ? eap->args[0] : + cmdline.items + cmdline.size - 1; // Subtract 1 to account for NUL // Finally, make cmdlinep point to the cmdline string. *cmdlinep = cmdline.items; - xfree(argidx); // Replace, :make and :grep with 'makeprg' and 'grepprg'. char *p = replace_makeprg(eap, eap->arg, cmdlinep); diff --git a/src/nvim/api/keysets.lua b/src/nvim/api/keysets.lua index 1f1fa1e63a..a764fb069b 100644 --- a/src/nvim/api/keysets.lua +++ b/src/nvim/api/keysets.lua @@ -39,6 +39,7 @@ return { "unique"; "callback"; "desc"; + "replace_keycodes"; }; get_commands = { "builtin"; diff --git a/src/nvim/api/options.c b/src/nvim/api/options.c index 4ed676e613..867584dd71 100644 --- a/src/nvim/api/options.c +++ b/src/nvim/api/options.c @@ -104,20 +104,20 @@ Object nvim_get_option_value(String name, Dict(option) *opts, Error *err) long numval = 0; char *stringval = NULL; - int result = access_option_value_for(name.data, &numval, &stringval, scope, opt_type, from, - true, err); + getoption_T result = access_option_value_for(name.data, &numval, &stringval, scope, opt_type, + from, true, err); if (ERROR_SET(err)) { return rv; } switch (result) { - case 0: + case gov_string: rv = STRING_OBJ(cstr_as_string(stringval)); break; - case 1: + case gov_number: rv = INTEGER_OBJ(numval); break; - case 2: + case gov_bool: switch (numval) { case 0: case 1: @@ -280,7 +280,7 @@ Object nvim_buf_get_option(Buffer buffer, String name, Error *err) return get_option_from(buf, SREQ_BUF, name, err); } -/// Sets a buffer option value. Passing 'nil' as value deletes the option (only +/// Sets a buffer option value. Passing `nil` as value deletes the option (only /// works if there's a global fallback) /// /// @param channel_id @@ -318,7 +318,7 @@ Object nvim_win_get_option(Window window, String name, Error *err) return get_option_from(win, SREQ_WIN, name, err); } -/// Sets a window option value. Passing 'nil' as value deletes the option(only +/// Sets a window option value. Passing `nil` as value deletes the option (only /// works if there's a global fallback) /// /// @param channel_id @@ -338,7 +338,7 @@ void nvim_win_set_option(uint64_t channel_id, Window window, String name, Object set_option_to(channel_id, win, SREQ_WIN, name, value, err); } -/// Gets the value of a global or local(buffer, window) option. +/// Gets the value of a global or local (buffer, window) option. /// /// @param from If `type` is `SREQ_WIN` or `SREQ_BUF`, this must be a pointer /// to the window or buffer. @@ -393,7 +393,7 @@ Object get_option_from(void *from, int type, String name, Error *err) return rv; } -/// Sets the value of a global or local(buffer, window) option. +/// Sets the value of a global or local (buffer, window) option. /// /// @param to If `type` is `SREQ_WIN` or `SREQ_BUF`, this must be a pointer /// to the window or buffer. @@ -483,8 +483,8 @@ void set_option_to(uint64_t channel_id, void *to, int type, String name, Object }); } -static int access_option_value(char *key, long *numval, char **stringval, int opt_flags, bool get, - Error *err) +static getoption_T access_option_value(char *key, long *numval, char **stringval, int opt_flags, + bool get, Error *err) { if (get) { return get_option_value(key, numval, stringval, opt_flags); @@ -501,13 +501,13 @@ static int access_option_value(char *key, long *numval, char **stringval, int op } } -static int access_option_value_for(char *key, long *numval, char **stringval, int opt_flags, - int opt_type, void *from, bool get, Error *err) +static getoption_T access_option_value_for(char *key, long *numval, char **stringval, int opt_flags, + int opt_type, void *from, bool get, Error *err) { bool need_switch = false; switchwin_T switchwin; aco_save_T aco; - int result = 0; + getoption_T result = 0; try_start(); switch (opt_type) { diff --git a/src/nvim/api/private/defs.h b/src/nvim/api/private/defs.h index b1e0dd364c..9c7e59e4b3 100644 --- a/src/nvim/api/private/defs.h +++ b/src/nvim/api/private/defs.h @@ -36,6 +36,7 @@ typedef enum { kMessageTypeRequest = 0, kMessageTypeResponse = 1, kMessageTypeNotification = 2, + kMessageTypeRedrawEvent = 3, } MessageType; /// Mask for all internal calls diff --git a/src/nvim/api/private/helpers.h b/src/nvim/api/private/helpers.h index a4348d8b44..1441da853c 100644 --- a/src/nvim/api/private/helpers.h +++ b/src/nvim/api/private/helpers.h @@ -74,18 +74,18 @@ #define ADD_C(array, item) \ kv_push_c(array, item) -#define FIXED_TEMP_ARRAY(name, fixsize) \ - Array name = ARRAY_DICT_INIT; \ - Object name##__items[fixsize]; \ - name.size = fixsize; \ - name.items = name##__items; \ - #define MAXSIZE_TEMP_ARRAY(name, maxsize) \ Array name = ARRAY_DICT_INIT; \ Object name##__items[maxsize]; \ name.capacity = maxsize; \ name.items = name##__items; \ +#define MAXSIZE_TEMP_DICT(name, maxsize) \ + Dictionary name = ARRAY_DICT_INIT; \ + KeyValuePair name##__items[maxsize]; \ + name.capacity = maxsize; \ + name.items = name##__items; \ + #define cbuf_as_string(d, s) ((String) { .data = d, .size = s }) #define STATIC_CSTR_AS_STRING(s) ((String) { .data = s, .size = sizeof(s) - 1 }) diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c index 54ce838b9b..6239e414a7 100644 --- a/src/nvim/api/ui.c +++ b/src/nvim/api/ui.c @@ -27,7 +27,7 @@ typedef struct { uint64_t channel_id; #define UI_BUF_SIZE 4096 ///< total buffer size for pending msgpack data. - /// guranteed size available for each new event (so packing of simple events + /// guaranteed size available for each new event (so packing of simple events /// and the header of grid_line will never fail) #define EVENT_BUF_SIZE 256 char buf[UI_BUF_SIZE]; ///< buffer of packed but not yet sent msgpack data @@ -43,7 +43,7 @@ typedef struct { // We start packing the two outermost msgpack arrays before knowing the total // number of elements. Thus track the location where array size will need - // to be written in the msgpack buffer, once the specifc array is finished. + // to be written in the msgpack buffer, once the specific array is finished. char *nevents_pos; char *ncalls_pos; uint32_t nevents; ///< number of distinct events (top-level args to "redraw" @@ -748,8 +748,10 @@ static void remote_ui_hl_attr_define(UI *ui, Integer id, HlAttrs rgb_attrs, HlAt UIData *data = ui->data; Array args = data->call_buf; ADD_C(args, INTEGER_OBJ(id)); - ADD_C(args, DICTIONARY_OBJ(hlattrs2dict(rgb_attrs, true))); - ADD_C(args, DICTIONARY_OBJ(hlattrs2dict(cterm_attrs, false))); + MAXSIZE_TEMP_DICT(rgb, 16); + MAXSIZE_TEMP_DICT(cterm, 16); + ADD_C(args, DICTIONARY_OBJ(hlattrs2dict(&rgb, rgb_attrs, true))); + ADD_C(args, DICTIONARY_OBJ(hlattrs2dict(&cterm, cterm_attrs, false))); if (ui->ui_ext[kUIHlState]) { ADD_C(args, ARRAY_OBJ(info)); @@ -758,9 +760,6 @@ static void remote_ui_hl_attr_define(UI *ui, Integer id, HlAttrs rgb_attrs, HlAt } push_call(ui, "hl_attr_define", args); - // TODO(bfredl): could be elided - api_free_dictionary(kv_A(args, 1).data.dictionary); - api_free_dictionary(kv_A(args, 2).data.dictionary); } static void remote_ui_highlight_set(UI *ui, int id) @@ -772,11 +771,9 @@ static void remote_ui_highlight_set(UI *ui, int id) return; } data->hl_id = id; - Dictionary hl = hlattrs2dict(syn_attr2entry(id), ui->rgb); - - ADD_C(args, DICTIONARY_OBJ(hl)); + MAXSIZE_TEMP_DICT(dict, 16); + ADD_C(args, DICTIONARY_OBJ(hlattrs2dict(&dict, syn_attr2entry(id), ui->rgb))); push_call(ui, "highlight_set", args); - api_free_dictionary(kv_A(args, 0).data.dictionary); } /// "true" cursor used only for input focus @@ -963,7 +960,7 @@ static Array translate_contents(UI *ui, Array contents) Array new_item = ARRAY_DICT_INIT; int attr = (int)item.items[0].data.integer; if (attr) { - Dictionary rgb_attrs = hlattrs2dict(syn_attr2entry(attr), ui->rgb); + Dictionary rgb_attrs = hlattrs2dict(NULL, syn_attr2entry(attr), ui->rgb); ADD(new_item, DICTIONARY_OBJ(rgb_attrs)); } else { ADD(new_item, DICTIONARY_OBJ((Dictionary)ARRAY_DICT_INIT)); diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 56516b2ac7..e2f58dba62 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -37,6 +37,7 @@ #include "nvim/highlight.h" #include "nvim/highlight_defs.h" #include "nvim/highlight_group.h" +#include "nvim/insexpand.h" #include "nvim/lua/executor.h" #include "nvim/mapping.h" #include "nvim/mark.h" @@ -472,10 +473,10 @@ Object nvim_exec_lua(String code, Array args, Error *err) Object nvim_notify(String msg, Integer log_level, Dictionary opts, Error *err) FUNC_API_SINCE(7) { - FIXED_TEMP_ARRAY(args, 3); - args.items[0] = STRING_OBJ(msg); - args.items[1] = INTEGER_OBJ(log_level); - args.items[2] = DICTIONARY_OBJ(opts); + MAXSIZE_TEMP_ARRAY(args, 3); + ADD_C(args, STRING_OBJ(msg)); + ADD_C(args, INTEGER_OBJ(log_level)); + ADD_C(args, DICTIONARY_OBJ(opts)); return nlua_exec(STATIC_CSTR_AS_STRING("return vim.notify(...)"), args, err); } @@ -1009,10 +1010,10 @@ static void term_write(char *buf, size_t size, void *data) if (cb == LUA_NOREF) { return; } - FIXED_TEMP_ARRAY(args, 3); - args.items[0] = INTEGER_OBJ((Integer)chan->id); - args.items[1] = BUFFER_OBJ(terminal_buf(chan->term)); - args.items[2] = STRING_OBJ(((String){ .data = buf, .size = size })); + MAXSIZE_TEMP_ARRAY(args, 3); + ADD_C(args, INTEGER_OBJ((Integer)chan->id)); + ADD_C(args, BUFFER_OBJ(terminal_buf(chan->term))); + ADD_C(args, STRING_OBJ(((String){ .data = buf, .size = size }))); textlock++; nlua_call_ref(cb, "input", args, false, NULL); textlock--; @@ -1410,7 +1411,7 @@ Dictionary nvim_get_mode(void) /// Gets a list of global (non-buffer-local) |mapping| definitions. /// /// @param mode Mode short-name ("n", "i", "v", ...) -/// @returns Array of maparg()-like dictionaries describing mappings. +/// @returns Array of |maparg()|-like dictionaries describing mappings. /// The "buffer" key is always zero. ArrayOf(Dictionary) nvim_get_keymap(uint64_t channel_id, String mode) FUNC_API_SINCE(3) @@ -1422,8 +1423,8 @@ ArrayOf(Dictionary) nvim_get_keymap(uint64_t channel_id, String mode) /// /// To set a buffer-local mapping, use |nvim_buf_set_keymap()|. /// -/// Unlike |:map|, leading/trailing whitespace is accepted as part of the {lhs} -/// or {rhs}. Empty {rhs} is |<Nop>|. |keycodes| are replaced as usual. +/// Unlike |:map|, leading/trailing whitespace is accepted as part of the {lhs} or {rhs}. +/// Empty {rhs} is |<Nop>|. |keycodes| are replaced as usual. /// /// Example: /// <pre> @@ -1440,13 +1441,15 @@ ArrayOf(Dictionary) nvim_get_keymap(uint64_t channel_id, String mode) /// or "!" for |:map!|, or empty string for |:map|. /// @param lhs Left-hand-side |{lhs}| of the mapping. /// @param rhs Right-hand-side |{rhs}| of the mapping. -/// @param opts Optional parameters map: keys are |:map-arguments|, values -/// are booleans (default false). Accepts all |:map-arguments| as -/// keys excluding |<buffer>| but including |noremap| and "desc". -/// Unknown key is an error. "desc" can be used to give a -/// description to the mapping. When called from Lua, also accepts a -/// "callback" key that takes a Lua function to call when the -/// mapping is executed. +/// @param opts Optional parameters map: keys are |:map-arguments|, values are booleans (default +/// false). Accepts all |:map-arguments| as keys excluding |<buffer>| but including +/// |noremap| and "desc". Unknown key is an error. +/// "desc" can be used to give a description to the mapping. +/// When called from Lua, also accepts a "callback" key that takes a Lua function to +/// call when the mapping is executed. +/// When "expr" is true, "replace_keycodes" (boolean) can be used to replace keycodes +/// in the resulting string (see |nvim_replace_termcodes()|), and a Lua callback +/// returning `nil` is equivalent to returning an empty string. /// @param[out] err Error details, if any. void nvim_set_keymap(uint64_t channel_id, String mode, String lhs, String rhs, Dict(keymap) *opts, Error *err) @@ -2256,3 +2259,11 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error * return result; } + +void nvim_error_event(uint64_t channel_id, Integer lvl, String data) + FUNC_API_REMOTE_ONLY +{ + // TODO(bfredl): consider printing message to user, as will be relevant + // if we fork nvim processes as async workers + ELOG("async error on channel %" PRId64 ": %s", channel_id, data.size ? data.data : ""); +} diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index d51079b515..2b4c9c5b9c 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -14,10 +14,12 @@ #include "nvim/edit.h" #include "nvim/eval.h" #include "nvim/eval/userfunc.h" +#include "nvim/eval/vars.h" #include "nvim/ex_docmd.h" #include "nvim/ex_getln.h" #include "nvim/fileio.h" #include "nvim/getchar.h" +#include "nvim/insexpand.h" #include "nvim/lua/executor.h" #include "nvim/map.h" #include "nvim/option.h" @@ -1835,9 +1837,13 @@ bool apply_autocmds_group(event_T event, char *fname, char *fname_io, bool force } ap->last = true; + // Make sure cursor and topline are valid. The first time the current + // values are saved, restored by reset_lnums(). When nested only the + // values are corrected when needed. if (nesting == 1) { - // make sure cursor and topline are valid check_lnums(true); + } else { + check_lnums_nested(true); } // Execute the autocmd. The `getnextac` callback handles iteration. @@ -2051,8 +2057,8 @@ static bool call_autocmd_callback(const AutoCmd *ac, const AutoPatCmd *apc) break; } - FIXED_TEMP_ARRAY(args, 1); - args.items[0] = DICTIONARY_OBJ(data); + MAXSIZE_TEMP_ARRAY(args, 1); + ADD_C(args, DICTIONARY_OBJ(data)); Object result = nlua_call_ref(callback.data.luaref, NULL, args, true, NULL); if (result.type == kObjectTypeBoolean) { diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index f937450107..6dd71e92a6 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -37,6 +37,7 @@ #include "nvim/diff.h" #include "nvim/digraph.h" #include "nvim/eval.h" +#include "nvim/eval/vars.h" #include "nvim/ex_cmds.h" #include "nvim/ex_cmds2.h" #include "nvim/ex_docmd.h" @@ -2482,6 +2483,9 @@ void buflist_setfpos(buf_T *const buf, win_T *const win, linenr_T lnum, colnr_T wip->wi_mark.view = mark_view_make(win->w_topline, wip->wi_mark.mark); } } + if (win != NULL) { + wip->wi_changelistidx = win->w_changelistidx; + } if (copy_options && win != NULL) { // Save the window-specific option values. copy_winopt(&win->w_onebuf_opt, &wip->wi_opt); @@ -2585,6 +2589,9 @@ void get_winopts(buf_T *buf) } else { copy_winopt(&curwin->w_allbuf_opt, &curwin->w_onebuf_opt); } + if (wip != NULL) { + curwin->w_changelistidx = wip->wi_changelistidx; + } if (curwin->w_float_config.style == kWinStyleMinimal) { didset_window_options(curwin); @@ -3281,7 +3288,7 @@ void maketitle(void) len = (int)STRLEN(buf_p); if (len > 100) { len -= 100; - len += mb_tail_off(buf_p, buf_p + len) + 1; + len += utf_cp_tail_off(buf_p, buf_p + len) + 1; buf_p += len; } STRCPY(icon_str, buf_p); @@ -3463,7 +3470,7 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, int use_san // Proceed character by character through the statusline format string // fmt_p is the current position in the input buffer - for (char *fmt_p = usefmt; *fmt_p;) { + for (char *fmt_p = usefmt; *fmt_p != NUL;) { if (curitem == (int)stl_items_len) { size_t new_len = stl_items_len * 3 / 2; @@ -3477,7 +3484,7 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, int use_san stl_items_len = new_len; } - if (*fmt_p != NUL && *fmt_p != '%') { + if (*fmt_p != '%') { prevchar_isflag = prevchar_isitem = false; } diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index 4e890f7d10..b7f66e6dba 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -289,6 +289,7 @@ struct wininfo_S { winopt_T wi_opt; // local window options bool wi_fold_manual; // copy of w_fold_manual garray_T wi_folds; // clone of w_folds + int wi_changelistidx; // copy of w_changelistidx }; /* @@ -368,6 +369,7 @@ struct mapblock { char m_expr; // <expr> used, m_str is an expression sctx_T m_script_ctx; // SCTX where map was defined char *m_desc; // description of keymap + bool m_replace_keycodes; // replace termcodes in lua function }; /// Used for highlighting in the status line. @@ -444,11 +446,11 @@ typedef struct { // "containedin" argument int b_syn_sync_flags; // flags about how to sync int16_t b_syn_sync_id; // group to sync on - long b_syn_sync_minlines; // minimal sync lines offset - long b_syn_sync_maxlines; // maximal sync lines offset - long b_syn_sync_linebreaks; // offset for multi-line pattern - char_u *b_syn_linecont_pat; // line continuation pattern - regprog_T *b_syn_linecont_prog; // line continuation program + linenr_T b_syn_sync_minlines; // minimal sync lines offset + linenr_T b_syn_sync_maxlines; // maximal sync lines offset + linenr_T b_syn_sync_linebreaks; // offset for multi-line pattern + char_u *b_syn_linecont_pat; // line continuation pattern + regprog_T *b_syn_linecont_prog; // line continuation program syn_time_T b_syn_linecont_time; int b_syn_linecont_ic; // ignore-case flag for above int b_syn_topgrp; // for ":syntax include" @@ -582,9 +584,9 @@ struct file_buffer { linenr_T b_mod_top; // topmost lnum that was changed linenr_T b_mod_bot; // lnum below last changed line, AFTER the // change - long b_mod_xlines; // number of extra buffer lines inserted; + linenr_T b_mod_xlines; // number of extra buffer lines inserted; // negative when lines were deleted - wininfo_T *b_wininfo; // list of last used info for each window + wininfo_T *b_wininfo; // list of last used info for each window disptick_T b_mod_tick_syn; // last display tick syntax was updated disptick_T b_mod_tick_decor; // last display tick decoration providers // where invoked @@ -946,16 +948,15 @@ struct diffblock_S { typedef struct tabpage_S tabpage_T; struct tabpage_S { handle_T handle; - tabpage_T *tp_next; ///< next tabpage or NULL - frame_T *tp_topframe; ///< topframe for the windows - win_T *tp_curwin; ///< current window in this Tab page - win_T *tp_prevwin; ///< previous window in this Tab page - win_T *tp_firstwin; ///< first window in this Tab page - win_T *tp_lastwin; ///< last window in this Tab page - long tp_old_Rows; ///< Rows when Tab page was left - long tp_old_Columns; ///< Columns when Tab page was left - long tp_ch_used; ///< value of 'cmdheight' when frame size - ///< was set + tabpage_T *tp_next; ///< next tabpage or NULL + frame_T *tp_topframe; ///< topframe for the windows + win_T *tp_curwin; ///< current window in this Tab page + win_T *tp_prevwin; ///< previous window in this Tab page + win_T *tp_firstwin; ///< first window in this Tab page + win_T *tp_lastwin; ///< last window in this Tab page + long tp_old_Rows_avail; ///< ROWS_AVAIL when Tab page was left + long tp_old_Columns; ///< Columns when Tab page was left + long tp_ch_used; ///< value of 'cmdheight' when frame size was set diff_T *tp_first_diff; buf_T *(tp_diffbuf[DB_COUNT]); diff --git a/src/nvim/buffer_updates.c b/src/nvim/buffer_updates.c index 47b88945c7..14973502ab 100644 --- a/src/nvim/buffer_updates.c +++ b/src/nvim/buffer_updates.c @@ -316,23 +316,23 @@ void buf_updates_send_splice(buf_T *buf, int start_row, colnr_T start_col, bcoun BufUpdateCallbacks cb = kv_A(buf->update_callbacks, i); bool keep = true; if (cb.on_bytes != LUA_NOREF && (cb.preview || !cmdpreview)) { - FIXED_TEMP_ARRAY(args, 11); + MAXSIZE_TEMP_ARRAY(args, 11); // the first argument is always the buffer handle - args.items[0] = BUFFER_OBJ(buf->handle); + ADD_C(args, BUFFER_OBJ(buf->handle)); // next argument is b:changedtick - args.items[1] = INTEGER_OBJ(buf_get_changedtick(buf)); - - args.items[2] = INTEGER_OBJ(start_row); - args.items[3] = INTEGER_OBJ(start_col); - args.items[4] = INTEGER_OBJ(start_byte); - args.items[5] = INTEGER_OBJ(old_row); - args.items[6] = INTEGER_OBJ(old_col); - args.items[7] = INTEGER_OBJ(old_byte); - args.items[8] = INTEGER_OBJ(new_row); - args.items[9] = INTEGER_OBJ(new_col); - args.items[10] = INTEGER_OBJ(new_byte); + ADD_C(args, INTEGER_OBJ(buf_get_changedtick(buf))); + + ADD_C(args, INTEGER_OBJ(start_row)); + ADD_C(args, INTEGER_OBJ(start_col)); + ADD_C(args, INTEGER_OBJ(start_byte)); + ADD_C(args, INTEGER_OBJ(old_row)); + ADD_C(args, INTEGER_OBJ(old_col)); + ADD_C(args, INTEGER_OBJ(old_byte)); + ADD_C(args, INTEGER_OBJ(new_row)); + ADD_C(args, INTEGER_OBJ(new_col)); + ADD_C(args, INTEGER_OBJ(new_byte)); textlock++; Object res = nlua_call_ref(cb.on_bytes, "bytes", args, true, NULL); @@ -361,13 +361,13 @@ void buf_updates_changedtick(buf_T *buf) BufUpdateCallbacks cb = kv_A(buf->update_callbacks, i); bool keep = true; if (cb.on_changedtick != LUA_NOREF) { - FIXED_TEMP_ARRAY(args, 2); + MAXSIZE_TEMP_ARRAY(args, 2); // the first argument is always the buffer handle - args.items[0] = BUFFER_OBJ(buf->handle); + ADD_C(args, BUFFER_OBJ(buf->handle)); // next argument is b:changedtick - args.items[1] = INTEGER_OBJ(buf_get_changedtick(buf)); + ADD_C(args, INTEGER_OBJ(buf_get_changedtick(buf))); textlock++; Object res = nlua_call_ref(cb.on_changedtick, "changedtick", diff --git a/src/nvim/change.c b/src/nvim/change.c index 4568b71fd9..c063ece907 100644 --- a/src/nvim/change.c +++ b/src/nvim/change.c @@ -17,6 +17,7 @@ #include "nvim/fold.h" #include "nvim/indent.h" #include "nvim/indent_c.h" +#include "nvim/insexpand.h" #include "nvim/mark.h" #include "nvim/memline.h" #include "nvim/move.h" @@ -966,10 +967,10 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) int extra_len = 0; // length of p_extra string int lead_len; // length of comment leader int comment_start = 0; // start index of the comment leader - char_u *lead_flags; // position in 'comments' for comment leader - char_u *leader = NULL; // copy of comment leader + char *lead_flags; // position in 'comments' for comment leader + char *leader = NULL; // copy of comment leader char_u *allocated = NULL; // allocated memory - char_u *p; + char *p; char_u saved_char = NUL; // init for GCC pos_T *pos; bool do_si = may_do_si(); @@ -1007,9 +1008,9 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) // autoindent etc) a bit later. replace_push(NUL); // Call twice because BS over NL expects it replace_push(NUL); - p = saved_line + curwin->w_cursor.col; + p = (char *)saved_line + curwin->w_cursor.col; while (*p != NUL) { - p += replace_push_mb(p); + p += replace_push_mb((char_u *)p); } saved_line[curwin->w_cursor.col] = NUL; } @@ -1017,8 +1018,8 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) if ((State & MODE_INSERT) && (State & VREPLACE_FLAG) == 0) { p_extra = saved_line + curwin->w_cursor.col; if (do_si) { // need first char after new line break - p = (char_u *)skipwhite((char *)p_extra); - first_char = *p; + p = skipwhite((char *)p_extra); + first_char = (unsigned char)(*p); } extra_len = (int)STRLEN(p_extra); saved_char = *p_extra; @@ -1054,12 +1055,12 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) // "if (condition) {" if (!trunc_line && do_si && *saved_line != NUL && (p_extra == NULL || first_char != '{')) { - char_u *ptr; + char *ptr; old_cursor = curwin->w_cursor; - ptr = saved_line; + ptr = (char *)saved_line; if (flags & OPENLINE_DO_COM) { - lead_len = get_leader_len((char *)ptr, NULL, false, true); + lead_len = get_leader_len(ptr, NULL, false, true); } else { lead_len = 0; } @@ -1067,12 +1068,12 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) // Skip preprocessor directives, unless they are recognised as comments. if (lead_len == 0 && ptr[0] == '#') { while (ptr[0] == '#' && curwin->w_cursor.lnum > 1) { - ptr = ml_get(--curwin->w_cursor.lnum); + ptr = (char *)ml_get(--curwin->w_cursor.lnum); } newindent = get_indent(); } if (flags & OPENLINE_DO_COM) { - lead_len = get_leader_len((char *)ptr, NULL, false, true); + lead_len = get_leader_len(ptr, NULL, false, true); } else { lead_len = 0; } @@ -1083,7 +1084,7 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) // */ // #define IN_THE_WAY // This should line up here; - p = (char_u *)skipwhite((char *)ptr); + p = skipwhite(ptr); if (p[0] == '/' && p[1] == '*') { p++; } @@ -1107,7 +1108,7 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) while (p > ptr && ascii_iswhite(*p)) { p--; } - char_u last_char = *p; + char last_char = *p; // find the character just before the '{' or ';' if (last_char == '{' || last_char == ';') { @@ -1129,7 +1130,7 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) if ((pos = findmatch(NULL, '(')) != NULL) { curwin->w_cursor.lnum = pos->lnum; newindent = get_indent(); - ptr = get_cursor_line_ptr(); + ptr = (char *)get_cursor_line_ptr(); } } // If last character is '{' do indent, without @@ -1141,7 +1142,7 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) // Don't do this if the previous line ended in ';' or // '}'. } else if (last_char != ';' && last_char != '}' - && cin_is_cinword(ptr)) { + && cin_is_cinword((char_u *)ptr)) { did_si = true; } } @@ -1158,7 +1159,7 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) } else { was_backslashed = false; } - ptr = ml_get(++curwin->w_cursor.lnum); + ptr = (char *)ml_get(++curwin->w_cursor.lnum); } if (was_backslashed) { newindent = 0; // Got to end of file @@ -1166,7 +1167,7 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) newindent = get_indent(); } } - p = (char_u *)skipwhite((char *)ptr); + p = skipwhite(ptr); if (*p == '}') { // if line starts with '}': do indent did_si = true; } else { // can delete indent when '{' typed @@ -1191,14 +1192,13 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) // This may then be inserted in front of the new line. end_comment_pending = NUL; if (flags & OPENLINE_DO_COM) { - lead_len = get_leader_len((char *)saved_line, (char **)&lead_flags, dir == BACKWARD, true); + lead_len = get_leader_len((char *)saved_line, &lead_flags, dir == BACKWARD, true); if (lead_len == 0 && curbuf->b_p_cin && do_cindent && dir == FORWARD && (!has_format_option(FO_NO_OPEN_COMS) || (flags & OPENLINE_FORMAT))) { // Check for a line comment after code. comment_start = check_linecomment(saved_line); if (comment_start != MAXCOL) { - lead_len = get_leader_len((char *)saved_line + comment_start, - (char **)&lead_flags, false, true); + lead_len = get_leader_len((char *)saved_line + comment_start, &lead_flags, false, true); if (lead_len != 0) { lead_len += comment_start; if (did_do_comment != NULL) { @@ -1211,11 +1211,11 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) lead_len = 0; } if (lead_len > 0) { - char_u *lead_repl = NULL; // replaces comment leader + char *lead_repl = NULL; // replaces comment leader int lead_repl_len = 0; // length of *lead_repl char_u lead_middle[COM_MAX_LEN]; // middle-comment string char_u lead_end[COM_MAX_LEN]; // end-comment string - char_u *comment_end = NULL; // where lead_end has been found + char_u *comment_end = NULL; // where lead_end has been found int extra_space = false; // append extra space int current_flag; int require_blank = false; // requires blank after middle @@ -1229,7 +1229,7 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) continue; } if (*p == COM_START || *p == COM_MIDDLE) { - current_flag = *p; + current_flag = (unsigned char)(*p); if (*p == COM_START) { // Doing "O" on a start of comment does not insert leader. if (dir == BACKWARD) { @@ -1238,7 +1238,7 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) } // find start of middle part - (void)copy_option_part((char **)&p, (char *)lead_middle, COM_MAX_LEN, ","); + (void)copy_option_part(&p, (char *)lead_middle, COM_MAX_LEN, ","); require_blank = false; } @@ -1249,7 +1249,7 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) } p++; } - (void)copy_option_part((char **)&p, (char *)lead_middle, COM_MAX_LEN, ","); + (void)copy_option_part(&p, (char *)lead_middle, COM_MAX_LEN, ","); while (*p && p[-1] != ':') { // find end of end flags // Check whether we allow automatic ending of comments @@ -1258,7 +1258,7 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) } p++; } - size_t n = copy_option_part((char **)&p, (char *)lead_end, COM_MAX_LEN, ","); + size_t n = copy_option_part(&p, (char *)lead_end, COM_MAX_LEN, ","); if (end_comment_pending == -1) { // we can set it now end_comment_pending = lead_end[n - 1]; @@ -1267,9 +1267,9 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) // If the end of the comment is in the same line, don't use // the comment leader. if (dir == FORWARD) { - for (p = saved_line + lead_len; *p; p++) { + for (p = (char *)saved_line + lead_len; *p; p++) { if (STRNCMP(p, lead_end, n) == 0) { - comment_end = p; + comment_end = (char_u *)p; lead_len = 0; break; } @@ -1279,7 +1279,7 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) // Doing "o" on a start of comment inserts the middle leader. if (lead_len > 0) { if (current_flag == COM_START) { - lead_repl = lead_middle; + lead_repl = (char *)lead_middle; lead_repl_len = (int)STRLEN(lead_middle); } @@ -1309,10 +1309,10 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) // Doing "O" on the end of a comment inserts the middle leader. // Find the string for the middle leader, searching backwards. - while (p > curbuf->b_p_com && *p != ',') { + while (p > (char *)curbuf->b_p_com && *p != ',') { p--; } - for (lead_repl = p; lead_repl > curbuf->b_p_com + for (lead_repl = p; lead_repl > (char *)curbuf->b_p_com && lead_repl[-1] != ':'; lead_repl--) {} lead_repl_len = (int)(p - lead_repl); @@ -1321,7 +1321,7 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) extra_space = true; // Check whether we allow automatic ending of comments - for (p2 = p; *p2 && *p2 != ':'; p2++) { + for (p2 = (char_u *)p; *p2 && *p2 != ':'; p2++) { if (*p2 == COM_AUTO_END) { end_comment_pending = -1; // means we want to set it } @@ -1341,7 +1341,7 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) if (dir == BACKWARD) { lead_len = 0; } else { - lead_repl = (char_u *)""; + lead_repl = ""; lead_repl_len = 0; } break; @@ -1357,7 +1357,7 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) + 1; assert(bytes >= 0); leader = xmalloc((size_t)bytes); - allocated = leader; // remember to free it later + allocated = (char_u *)leader; // remember to free it later STRLCPY(leader, saved_line, lead_len + 1); @@ -1375,9 +1375,9 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) for (p = lead_flags; *p != NUL && *p != ':';) { if (*p == COM_RIGHT || *p == COM_LEFT) { - c = *p++; + c = (unsigned char)(*p++); } else if (ascii_isdigit(*p) || *p == '-') { - off = getdigits_int((char **)&p, true, 0); + off = getdigits_int(&p, true, 0); } else { p++; } @@ -1391,15 +1391,14 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) // Compute the length of the replaced characters in // screen characters, not bytes. { - int repl_size = vim_strnsize(lead_repl, - lead_repl_len); + int repl_size = vim_strnsize((char_u *)lead_repl, lead_repl_len); int old_size = 0; - char_u *endp = p; + char *endp = p; int l; while (old_size < repl_size && p > leader) { MB_PTR_BACK(leader, p); - old_size += ptr2cells((char *)p); + old_size += ptr2cells(p); } l = lead_repl_len - (int)(endp - p); if (l != 0) { @@ -1415,11 +1414,11 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) // blank-out any other chars from the old leader. while (--p >= leader) { - int l = utf_head_off(leader, p); + int l = utf_head_off((char_u *)leader, (char_u *)p); if (l > 1) { p -= l; - if (ptr2cells((char *)p) > 1) { + if (ptr2cells(p) > 1) { p[1] = ' '; l--; } @@ -1432,19 +1431,18 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) } } } else { // left adjusted leader - p = (char_u *)skipwhite((char *)leader); + p = skipwhite(leader); // Compute the length of the replaced characters in // screen characters, not bytes. Move the part that is // not to be overwritten. { - int repl_size = vim_strnsize(lead_repl, - lead_repl_len); + int repl_size = vim_strnsize((char_u *)lead_repl, lead_repl_len); int i; int l; for (i = 0; i < lead_len && p[i] != NUL; i += l) { - l = utfc_ptr2len((char *)p + i); - if (vim_strnsize(p, i + l) > repl_size) { + l = utfc_ptr2len(p + i); + if (vim_strnsize((char_u *)p, i + l) > repl_size) { break; } } @@ -1466,10 +1464,10 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) lead_len--; memmove(p, p + 1, (size_t)(leader + lead_len - p)); } else { - int l = utfc_ptr2len((char *)p); + int l = utfc_ptr2len(p); if (l > 1) { - if (ptr2cells((char *)p) > 1) { + if (ptr2cells(p) > 1) { // Replace a double-wide char with // two spaces l--; @@ -1487,7 +1485,7 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) // Recompute the indent, it may have changed. if (curbuf->b_p_ai || do_si) { - newindent = get_indent_str_vtab(leader, + newindent = get_indent_str_vtab((char_u *)leader, curbuf->b_p_ts, curbuf->b_p_vts_array, false); } @@ -1505,7 +1503,7 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) while (off > 0 && lead_len > 0 && leader[lead_len - 1] == ' ') { // Don't do it when there is a tab before the space - if (vim_strchr(skipwhite((char *)leader), '\t') != NULL) { + if (vim_strchr(skipwhite(leader), '\t') != NULL) { break; } lead_len--; @@ -1604,7 +1602,7 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) } } STRCAT(leader, p_extra); - p_extra = leader; + p_extra = (char_u *)leader; did_ai = true; // So truncating blanks works with comments less_cols -= lead_len; } else { diff --git a/src/nvim/charset.c b/src/nvim/charset.c index 028dd70eb2..a26a7f6aaf 100644 --- a/src/nvim/charset.c +++ b/src/nvim/charset.c @@ -1351,10 +1351,10 @@ bool try_getdigits(char **pp, intmax_t *nr) /// @param def Default value, if parsing fails or overflow occurs. /// /// @return Number read from the string, or `def` on parse failure or overflow. -intmax_t getdigits(char_u **pp, bool strict, intmax_t def) +intmax_t getdigits(char **pp, bool strict, intmax_t def) { intmax_t number; - int ok = try_getdigits((char **)pp, &number); + int ok = try_getdigits(pp, &number); if (strict && !ok) { abort(); } @@ -1366,7 +1366,7 @@ intmax_t getdigits(char_u **pp, bool strict, intmax_t def) /// @see getdigits int getdigits_int(char **pp, bool strict, int def) { - intmax_t number = getdigits((char_u **)pp, strict, def); + intmax_t number = getdigits(pp, strict, def); #if SIZEOF_INTMAX_T > SIZEOF_INT if (strict) { assert(number >= INT_MIN && number <= INT_MAX); @@ -1380,7 +1380,7 @@ int getdigits_int(char **pp, bool strict, int def) /// Gets a long number from a string. /// /// @see getdigits -long getdigits_long(char_u **pp, bool strict, long def) +long getdigits_long(char **pp, bool strict, long def) { intmax_t number = getdigits(pp, strict, def); #if SIZEOF_INTMAX_T > SIZEOF_LONG @@ -1398,7 +1398,7 @@ long getdigits_long(char_u **pp, bool strict, long def) /// @see getdigits int32_t getdigits_int32(char **pp, bool strict, long def) { - intmax_t number = getdigits((char_u **)pp, strict, def); + intmax_t number = getdigits(pp, strict, def); #if SIZEOF_INTMAX_T > SIZEOF_INT32_T if (strict) { assert(number >= INT32_MIN && number <= INT32_MAX); diff --git a/src/nvim/cursor_shape.c b/src/nvim/cursor_shape.c index 62cf60e03b..9c33b1a806 100644 --- a/src/nvim/cursor_shape.c +++ b/src/nvim/cursor_shape.c @@ -43,7 +43,7 @@ cursorentry_T shape_table[SHAPE_IDX_COUNT] = }; /// Converts cursor_shapes into an Array of Dictionaries -/// @param arena initialized arena where memory will be alocated +/// @param arena initialized arena where memory will be allocated /// /// @return Array of the form {[ "cursor_shape": ... ], ...} Array mode_style_array(Arena *arena) diff --git a/src/nvim/decoration_provider.c b/src/nvim/decoration_provider.c index 0f6a260247..04d875c4e3 100644 --- a/src/nvim/decoration_provider.c +++ b/src/nvim/decoration_provider.c @@ -63,9 +63,9 @@ void decor_providers_start(DecorProviders *providers, int type, char **err) bool active; if (p->redraw_start != LUA_NOREF) { - FIXED_TEMP_ARRAY(args, 2); - args.items[0] = INTEGER_OBJ((int)display_tick); - args.items[1] = INTEGER_OBJ(type); + MAXSIZE_TEMP_ARRAY(args, 2); + ADD_C(args, INTEGER_OBJ((int)display_tick)); + ADD_C(args, INTEGER_OBJ(type)); active = decor_provider_invoke(p->ns_id, "start", p->redraw_start, args, true, err); } else { active = true; @@ -96,12 +96,12 @@ void decor_providers_invoke_win(win_T *wp, DecorProviders *providers, for (size_t k = 0; k < kv_size(*providers); k++) { DecorProvider *p = kv_A(*providers, k); if (p && p->redraw_win != LUA_NOREF) { - FIXED_TEMP_ARRAY(args, 4); - args.items[0] = WINDOW_OBJ(wp->handle); - args.items[1] = BUFFER_OBJ(wp->w_buffer->handle); + MAXSIZE_TEMP_ARRAY(args, 4); + ADD_C(args, WINDOW_OBJ(wp->handle)); + ADD_C(args, BUFFER_OBJ(wp->w_buffer->handle)); // TODO(bfredl): we are not using this, but should be first drawn line? - args.items[2] = INTEGER_OBJ(wp->w_topline - 1); - args.items[3] = INTEGER_OBJ(knownmax); + ADD_C(args, INTEGER_OBJ(wp->w_topline - 1)); + ADD_C(args, INTEGER_OBJ(knownmax)); if (decor_provider_invoke(p->ns_id, "win", p->redraw_win, args, true, err)) { kvi_push(*line_providers, p); } @@ -124,10 +124,10 @@ void providers_invoke_line(win_T *wp, DecorProviders *providers, int row, bool * for (size_t k = 0; k < kv_size(*providers); k++) { DecorProvider *p = kv_A(*providers, k); if (p && p->redraw_line != LUA_NOREF) { - FIXED_TEMP_ARRAY(args, 3); - args.items[0] = WINDOW_OBJ(wp->handle); - args.items[1] = BUFFER_OBJ(wp->w_buffer->handle); - args.items[2] = INTEGER_OBJ(row); + MAXSIZE_TEMP_ARRAY(args, 3); + ADD_C(args, WINDOW_OBJ(wp->handle)); + ADD_C(args, BUFFER_OBJ(wp->w_buffer->handle)); + ADD_C(args, INTEGER_OBJ(row)); if (decor_provider_invoke(p->ns_id, "line", p->redraw_line, args, true, err)) { *has_decor = true; } else { @@ -150,8 +150,8 @@ void decor_providers_invoke_buf(buf_T *buf, DecorProviders *providers, char **er for (size_t i = 0; i < kv_size(*providers); i++) { DecorProvider *p = kv_A(*providers, i); if (p && p->redraw_buf != LUA_NOREF) { - FIXED_TEMP_ARRAY(args, 1); - args.items[0] = BUFFER_OBJ(buf->handle); + MAXSIZE_TEMP_ARRAY(args, 1); + ADD_C(args, BUFFER_OBJ(buf->handle)); decor_provider_invoke(p->ns_id, "buf", p->redraw_buf, args, true, err); } } @@ -167,8 +167,8 @@ void decor_providers_invoke_end(DecorProviders *providers, char **err) for (size_t i = 0; i < kv_size(*providers); i++) { DecorProvider *p = kv_A(*providers, i); if (p && p->active && p->redraw_end != LUA_NOREF) { - FIXED_TEMP_ARRAY(args, 1); - args.items[0] = INTEGER_OBJ((int)display_tick); + MAXSIZE_TEMP_ARRAY(args, 1); + ADD_C(args, INTEGER_OBJ((int)display_tick)); decor_provider_invoke(p->ns_id, "end", p->redraw_end, args, true, err); } } diff --git a/src/nvim/diff.c b/src/nvim/diff.c index 75021e90d6..849204f789 100644 --- a/src/nvim/diff.c +++ b/src/nvim/diff.c @@ -115,7 +115,10 @@ void diff_buf_delete(buf_T *buf) tp->tp_diff_invalid = true; if (tp == curtab) { - diff_redraw(true); + // don't redraw right away, more might change or buffer state + // is invalid right now + need_diff_redraw = true; + redraw_later(curwin, VALID); } } } @@ -369,9 +372,8 @@ static void diff_mark_adjust_tp(tabpage_T *tp, int idx, linenr_T line1, linenr_T // 2. 3. 4. 5.: inserted/deleted lines touching this diff. if (deleted > 0) { + off = 0; if (dp->df_lnum[idx] >= line1) { - off = dp->df_lnum[idx] - lnum_deleted; - if (last <= line2) { // 4. delete all lines of diff if ((dp->df_next != NULL) @@ -388,14 +390,13 @@ static void diff_mark_adjust_tp(tabpage_T *tp, int idx, linenr_T line1, linenr_T dp->df_count[idx] = 0; } else { // 5. delete lines at or just before top of diff + off = dp->df_lnum[idx] - lnum_deleted; n = off; dp->df_count[idx] -= line2 - dp->df_lnum[idx] + 1; check_unchanged = true; } dp->df_lnum[idx] = line1; } else { - off = 0; - if (last < line2) { // 2. delete at end of diff dp->df_count[idx] -= last - lnum_deleted + 1; @@ -418,10 +419,13 @@ static void diff_mark_adjust_tp(tabpage_T *tp, int idx, linenr_T line1, linenr_T } } - int i; - for (i = 0; i < DB_COUNT; i++) { + for (int i = 0; i < DB_COUNT; i++) { if ((tp->tp_diffbuf[i] != NULL) && (i != idx)) { - dp->df_lnum[i] -= off; + if (dp->df_lnum[i] > off) { + dp->df_lnum[i] -= off; + } else { + dp->df_lnum[i] = 1; + } dp->df_count[i] += n; } } @@ -648,9 +652,11 @@ void diff_redraw(bool dofold) need_diff_redraw = false; FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { - if (!wp->w_p_diff) { + // when closing windows or wiping buffers skip invalid window + if (!wp->w_p_diff || !buf_valid(wp->w_buffer)) { continue; } + redraw_later(wp, SOME_VALID); if (wp != curwin) { wp_other = wp; @@ -659,8 +665,8 @@ void diff_redraw(bool dofold) foldUpdateAll(wp); } - // A change may have made filler lines invalid, need to take care - // of that for other windows. + // A change may have made filler lines invalid, need to take care of + // that for other windows. int n = diff_check(wp, wp->w_topline); if (((wp != curwin) && (wp->w_topfill > 0)) || (n > 0)) { @@ -677,6 +683,7 @@ void diff_redraw(bool dofold) check_topfill(wp, false); } } + if (wp_other != NULL && curwin->w_p_scb) { if (used_max_fill_curwin) { // The current window was set to use the maximum number of filler @@ -1421,7 +1428,7 @@ void diff_win_options(win_T *wp, int addbuf) } wp->w_p_fdc_save = vim_strsave(wp->w_p_fdc); } - xfree(wp->w_p_fdc); + free_string_option(wp->w_p_fdc); wp->w_p_fdc = (char_u *)xstrdup("2"); assert(diff_foldcolumn >= 0 && diff_foldcolumn <= 9); snprintf((char *)wp->w_p_fdc, STRLEN(wp->w_p_fdc) + 1, "%d", diff_foldcolumn); @@ -2485,6 +2492,17 @@ void nv_diffgetput(bool put, size_t count) ex_diffgetput(&ea); } +/// Return true if "diff" appears in the list of diff blocks of the current tab. +static bool valid_diff(diff_T *diff) +{ + for (diff_T *dp = curtab->tp_first_diff; dp != NULL; dp = dp->df_next) { + if (dp == diff) { + return true; + } + } + return false; +} + /// ":diffget" and ":diffput" /// /// @param eap @@ -2694,8 +2712,9 @@ void ex_diffgetput(exarg_T *eap) for (i = 0; i < count; i++) { // remember deleting the last line of the buffer buf_empty = curbuf->b_ml.ml_line_count == 1; - ml_delete(lnum, false); - added--; + if (ml_delete(lnum, false) == OK) { + added--; + } } for (i = 0; i < dp->df_count[idx_from] - start_skip - end_skip; i++) { @@ -2742,10 +2761,9 @@ void ex_diffgetput(exarg_T *eap) } } - // Adjust marks. This will change the following entries! if (added != 0) { - mark_adjust(lnum, lnum + count - 1, (long)MAXLNUM, added, - kExtmarkUndo); + // Adjust marks. This will change the following entries! + mark_adjust(lnum, lnum + count - 1, (long)MAXLNUM, added, kExtmarkUndo); if (curwin->w_cursor.lnum >= lnum) { // Adjust the cursor position if it's in/after the changed // lines. @@ -2762,7 +2780,15 @@ void ex_diffgetput(exarg_T *eap) // Diff is deleted, update folds in other windows. diff_fold_update(dfree, idx_to); xfree(dfree); - } else { + } + + // mark_adjust() may have made "dp" invalid. We don't know where + // to continue then, bail out. + if (added != 0 && !valid_diff(dp)) { + break; + } + + if (dfree == NULL) { // mark_adjust() may have changed the count in a wrong way dp->df_count[idx_to] = new_count; } @@ -3042,8 +3068,8 @@ static int parse_diff_ed(char_u *line, diffhunk_T *hunk) // change: {first}[,{last}]c{first}[,{last}] // append: {first}a{first}[,{last}] // delete: {first}[,{last}]d{first} - char_u *p = line; - linenr_T f1 = getdigits_int32((char **)&p, true, 0); + char *p = (char *)line; + linenr_T f1 = getdigits_int32(&p, true, 0); if (*p == ',') { p++; l1 = getdigits(&p, true, 0); @@ -3053,7 +3079,7 @@ static int parse_diff_ed(char_u *line, diffhunk_T *hunk) if (*p != 'a' && *p != 'c' && *p != 'd') { return FAIL; // invalid diff format } - int difftype = *p++; + int difftype = (uint8_t)(*p++); long f2 = getdigits(&p, true, 0); if (*p == ',') { p++; @@ -3090,7 +3116,7 @@ static int parse_diff_unified(char_u *line, diffhunk_T *hunk) { // Parse unified diff hunk header: // @@ -oldline,oldcount +newline,newcount @@ - char_u *p = line; + char *p = (char *)line; if (*p++ == '@' && *p++ == '@' && *p++ == ' ' && *p++ == '-') { long oldcount; long newline; diff --git a/src/nvim/digraph.c b/src/nvim/digraph.c index 355900c93f..733b3d3d5d 100644 --- a/src/nvim/digraph.c +++ b/src/nvim/digraph.c @@ -1662,28 +1662,28 @@ bool check_digraph_chars_valid(int char1, int char2) /// format: {c1}{c2} char {c1}{c2} char ... /// /// @param str -void putdigraph(char_u *str) +void putdigraph(char *str) { while (*str != NUL) { - str = (char_u *)skipwhite((char *)str); + str = skipwhite(str); if (*str == NUL) { return; } - char_u char1 = *str++; - char_u char2 = *str++; + uint8_t char1 = (uint8_t)(*str++); + uint8_t char2 = (uint8_t)(*str++); if (!check_digraph_chars_valid(char1, char2)) { return; } - str = (char_u *)skipwhite((char *)str); + str = skipwhite(str); if (!ascii_isdigit(*str)) { emsg(_(e_number_exp)); return; } - int n = getdigits_int((char **)&str, true, 0); + int n = getdigits_int(&str, true, 0); registerdigraph(char1, char2, n); } @@ -2121,7 +2121,7 @@ void ex_loadkeymap(exarg_T *eap) vim_snprintf((char *)buf, sizeof(buf), "<buffer> %s %s", ((kmap_T *)curbuf->b_kmap_ga.ga_data)[i].from, ((kmap_T *)curbuf->b_kmap_ga.ga_data)[i].to); - (void)do_map(0, buf, MODE_LANGMAP, false); + (void)do_map(MAPTYPE_MAP, buf, MODE_LANGMAP, false); } p_cpo = save_cpo; @@ -2158,7 +2158,7 @@ static void keymap_unload(void) for (int i = 0; i < curbuf->b_kmap_ga.ga_len; i++) { vim_snprintf(buf, sizeof(buf), "<buffer> %s", kp[i].from); - (void)do_map(1, (char_u *)buf, MODE_LANGMAP, false); + (void)do_map(MAPTYPE_UNMAP, (char_u *)buf, MODE_LANGMAP, false); } keymap_ga_clear(&curbuf->b_kmap_ga); diff --git a/src/nvim/digraph.h b/src/nvim/digraph.h index 039fc3370d..71330ae9b1 100644 --- a/src/nvim/digraph.h +++ b/src/nvim/digraph.h @@ -1,7 +1,6 @@ #ifndef NVIM_DIGRAPH_H #define NVIM_DIGRAPH_H -#include "nvim/eval/funcs.h" #include "nvim/ex_cmds_defs.h" #include "nvim/types.h" diff --git a/src/nvim/edit.c b/src/nvim/edit.c index 0571e71cb5..5861e4c52b 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -18,7 +18,6 @@ #include "nvim/digraph.h" #include "nvim/edit.h" #include "nvim/eval.h" -#include "nvim/eval/typval.h" #include "nvim/event/loop.h" #include "nvim/ex_docmd.h" #include "nvim/ex_getln.h" @@ -29,6 +28,7 @@ #include "nvim/highlight_group.h" #include "nvim/indent.h" #include "nvim/indent_c.h" +#include "nvim/insexpand.h" #include "nvim/keycodes.h" #include "nvim/main.h" #include "nvim/mapping.h" @@ -48,180 +48,18 @@ #include "nvim/plines.h" #include "nvim/popupmnu.h" #include "nvim/quickfix.h" -#include "nvim/regexp.h" #include "nvim/screen.h" #include "nvim/search.h" #include "nvim/spell.h" #include "nvim/state.h" #include "nvim/strings.h" #include "nvim/syntax.h" -#include "nvim/tag.h" #include "nvim/terminal.h" #include "nvim/ui.h" #include "nvim/undo.h" #include "nvim/vim.h" #include "nvim/window.h" -// Definitions used for CTRL-X submode. -// Note: If you change CTRL-X submode, you must also maintain ctrl_x_msgs[] -// and ctrl_x_mode_names[]. - -#define CTRL_X_WANT_IDENT 0x100 - -#define CTRL_X_NORMAL 0 ///< CTRL-N CTRL-P completion, default -#define CTRL_X_NOT_DEFINED_YET 1 -#define CTRL_X_SCROLL 2 -#define CTRL_X_WHOLE_LINE 3 -#define CTRL_X_FILES 4 -#define CTRL_X_TAGS (5 + CTRL_X_WANT_IDENT) -#define CTRL_X_PATH_PATTERNS (6 + CTRL_X_WANT_IDENT) -#define CTRL_X_PATH_DEFINES (7 + CTRL_X_WANT_IDENT) -#define CTRL_X_FINISHED 8 -#define CTRL_X_DICTIONARY (9 + CTRL_X_WANT_IDENT) -#define CTRL_X_THESAURUS (10 + CTRL_X_WANT_IDENT) -#define CTRL_X_CMDLINE 11 -#define CTRL_X_FUNCTION 12 -#define CTRL_X_OMNI 13 -#define CTRL_X_SPELL 14 -#define CTRL_X_LOCAL_MSG 15 ///< only used in "ctrl_x_msgs" -#define CTRL_X_EVAL 16 ///< for builtin function complete() -#define CTRL_X_CMDLINE_CTRL_X 17 ///< CTRL-X typed in CTRL_X_CMDLINE - -#define CTRL_X_MSG(i) ctrl_x_msgs[(i) & ~CTRL_X_WANT_IDENT] -#define CTRL_X_MODE_LINE_OR_EVAL(m) \ - ((m) == CTRL_X_WHOLE_LINE || (m) == CTRL_X_EVAL) - -// Message for CTRL-X mode, index is ctrl_x_mode. -static char *ctrl_x_msgs[] = -{ - N_(" Keyword completion (^N^P)"), // CTRL_X_NORMAL, ^P/^N compl. - N_(" ^X mode (^]^D^E^F^I^K^L^N^O^Ps^U^V^Y)"), - NULL, // CTRL_X_SCROLL: depends on state - N_(" Whole line completion (^L^N^P)"), - N_(" File name completion (^F^N^P)"), - N_(" Tag completion (^]^N^P)"), - N_(" Path pattern completion (^N^P)"), - N_(" Definition completion (^D^N^P)"), - NULL, // CTRL_X_FINISHED - N_(" Dictionary completion (^K^N^P)"), - N_(" Thesaurus completion (^T^N^P)"), - N_(" Command-line completion (^V^N^P)"), - N_(" User defined completion (^U^N^P)"), - N_(" Omni completion (^O^N^P)"), - N_(" Spelling suggestion (s^N^P)"), - N_(" Keyword Local completion (^N^P)"), - NULL, // CTRL_X_EVAL doesn't use msg. - N_(" Command-line completion (^V^N^P)"), -}; - -static char *ctrl_x_mode_names[] = { - "keyword", - "ctrl_x", - "scroll", - "whole_line", - "files", - "tags", - "path_patterns", - "path_defines", - "unknown", // CTRL_X_FINISHED - "dictionary", - "thesaurus", - "cmdline", - "function", - "omni", - "spell", - NULL, // CTRL_X_LOCAL_MSG only used in "ctrl_x_msgs" - "eval", - "cmdline", -}; - -static char e_hitend[] = N_("Hit end of paragraph"); -static char e_compldel[] = N_("E840: Completion function deleted text"); - -/* - * Structure used to store one match for insert completion. - */ -typedef struct compl_S compl_T; -struct compl_S { - compl_T *cp_next; - compl_T *cp_prev; - char_u *cp_str; // matched text - char_u *(cp_text[CPT_COUNT]); // text for the menu - typval_T cp_user_data; - char_u *cp_fname; // file containing the match, allocated when - // cp_flags has CP_FREE_FNAME - int cp_flags; // CP_ values - int cp_number; // sequence number -}; - -/* - * All the current matches are stored in a list. - * "compl_first_match" points to the start of the list. - * "compl_curr_match" points to the currently selected entry. - * "compl_shown_match" is different from compl_curr_match during - * ins_compl_get_exp(). - */ -static compl_T *compl_first_match = NULL; -static compl_T *compl_curr_match = NULL; -static compl_T *compl_shown_match = NULL; -static compl_T *compl_old_match = NULL; - -/* After using a cursor key <Enter> selects a match in the popup menu, - * otherwise it inserts a line break. */ -static int compl_enter_selects = FALSE; - -/* When "compl_leader" is not NULL only matches that start with this string - * are used. */ -static char_u *compl_leader = NULL; - -static bool compl_get_longest = false; // put longest common string in compl_leader - -static int compl_no_insert = FALSE; /* FALSE: select & insert - TRUE: noinsert */ -static int compl_no_select = FALSE; /* FALSE: select & insert - TRUE: noselect */ - -static bool compl_used_match; // Selected one of the matches. - // When false the match was edited or using - // the longest common string. - -static int compl_was_interrupted = FALSE; /* didn't finish finding - completions. */ - -static bool compl_restarting = false; // don't insert match - -// When the first completion is done "compl_started" is set. When it's -// false the word to be completed must be located. -static bool compl_started = false; - -// Which Ctrl-X mode are we in? -static int ctrl_x_mode = CTRL_X_NORMAL; - -static int compl_matches = 0; -static char_u *compl_pattern = NULL; -static Direction compl_direction = FORWARD; -static Direction compl_shows_dir = FORWARD; -static int compl_pending = 0; // > 1 for postponed CTRL-N -static pos_T compl_startpos; -static colnr_T compl_col = 0; /* column where the text starts - * that is being completed */ -static char_u *compl_orig_text = NULL; /* text as it was before - * completion started */ -static int compl_cont_mode = 0; -static expand_T compl_xp; - -static bool compl_opt_refresh_always = false; - -static int pum_selected_item = -1; - -/// state for pum_ext_select_item. -struct { - bool active; - int item; - bool insert; - bool finish; -} pum_want; - typedef struct insert_state { VimState state; cmdarg_T *ca; @@ -253,8 +91,6 @@ typedef struct insert_state { #define BACKSPACE_WORD_NOT_SPACE 3 #define BACKSPACE_LINE 4 -static size_t spell_bad_len = 0; // length of located bad word - static colnr_T Insstart_textlen; // length of line when insert started static colnr_T Insstart_blank_vcol; // vcol for first inserted blank static bool update_Insstart_orig = true; // set Insstart_orig to Insstart @@ -521,7 +357,7 @@ static int insert_check(VimState *state) // If typed something may trigger CursorHoldI again. if (s->c != K_EVENT // but not in CTRL-X mode, a script can't restore the state - && ctrl_x_mode == CTRL_X_NORMAL) { + && ctrl_x_mode_normal()) { did_cursorhold = false; } @@ -532,8 +368,8 @@ static int insert_check(VimState *state) if (can_cindent && cindent_on() - && ctrl_x_mode == CTRL_X_NORMAL - && !compl_started) { + && ctrl_x_mode_normal() + && !ins_compl_active()) { insert_do_cindent(s); } @@ -551,7 +387,7 @@ static int insert_check(VimState *state) Insstart_orig = Insstart; } - if (stop_insert_mode && !compl_started) { + if (stop_insert_mode && !ins_compl_active()) { // ":stopinsert" used s->count = 0; return 0; // exit insert mode @@ -688,29 +524,25 @@ static int insert_execute(VimState *state, int key) // Special handling of keys while the popup menu is visible or wanted // and the cursor is still in the completed word. Only when there is // a match, skip this when no matches were found. - if (compl_started + if (ins_compl_active() && pum_wanted() - && curwin->w_cursor.col >= compl_col - && (compl_shown_match == NULL - || compl_shown_match != compl_shown_match->cp_next)) { + && curwin->w_cursor.col >= ins_compl_col() + && ins_compl_has_shown_match()) { // BS: Delete one character from "compl_leader". if ((s->c == K_BS || s->c == Ctrl_H) - && curwin->w_cursor.col > compl_col + && curwin->w_cursor.col > ins_compl_col() && (s->c = ins_compl_bs()) == NUL) { return 1; // continue } // When no match was selected or it was edited. - if (!compl_used_match) { + if (!ins_compl_used_match()) { // CTRL-L: Add one character from the current match to // "compl_leader". Except when at the original match and // there is nothing to add, CTRL-L works like CTRL-P then. if (s->c == Ctrl_L - && (!CTRL_X_MODE_LINE_OR_EVAL(ctrl_x_mode) - || (compl_shown_match != NULL - && compl_shown_match->cp_str != NULL - && (int)STRLEN(compl_shown_match->cp_str) - > curwin->w_cursor.col - compl_col))) { + && (!ctrl_x_mode_line_or_eval() + || ins_compl_long_shown_match())) { ins_compl_addfrommatch(); return 1; // continue } @@ -736,7 +568,7 @@ static int insert_execute(VimState *state, int key) // Pressing CTRL-Y selects the current match. When // compl_enter_selects is set the Enter key does the same. if ((s->c == Ctrl_Y - || (compl_enter_selects + || (ins_compl_enter_selects() && (s->c == CAR || s->c == K_KENTER || s->c == NL))) && stop_arrow() == OK) { ins_compl_delete(); @@ -747,7 +579,7 @@ static int insert_execute(VimState *state, int key) // Prepare for or stop CTRL-X mode. This doesn't do completion, but it does // fix up the text when finishing completion. - compl_get_longest = false; + ins_compl_init_get_longest(); if (ins_compl_prep(s->c)) { return 1; // continue } @@ -779,8 +611,7 @@ static int insert_execute(VimState *state, int key) s->c = do_digraph(s->c); - if ((s->c == Ctrl_V || s->c == Ctrl_Q) - && (ctrl_x_mode == CTRL_X_CMDLINE || ctrl_x_mode == CTRL_X_CMDLINE_CTRL_X)) { + if ((s->c == Ctrl_V || s->c == Ctrl_Q) && ctrl_x_mode_cmdline()) { insert_do_complete(s); return 1; } @@ -791,8 +622,7 @@ static int insert_execute(VimState *state, int key) return 1; // continue } - if (cindent_on() - && ctrl_x_mode == 0) { + if (cindent_on() && ctrl_x_mode_none()) { // A key name preceded by a bang means this key is not to be // inserted. Skip ahead to the re-indenting below. // A key name preceded by a star means that indenting has to be @@ -873,7 +703,7 @@ static int insert_handle_key(InsertState *s) goto normalchar; // insert CTRL-Z as normal char case Ctrl_O: // execute one command - if (ctrl_x_mode == CTRL_X_OMNI) { + if (ctrl_x_mode_omni()) { insert_do_complete(s); break; } @@ -946,14 +776,14 @@ static int insert_handle_key(InsertState *s) break; case Ctrl_D: // Make indent one shiftwidth smaller. - if (ctrl_x_mode == CTRL_X_PATH_DEFINES) { + if (ctrl_x_mode_path_defines()) { insert_do_complete(s); break; } FALLTHROUGH; case Ctrl_T: // Make indent one shiftwidth greater. - if (s->c == Ctrl_T && ctrl_x_mode == CTRL_X_THESAURUS) { + if (s->c == Ctrl_T && ctrl_x_mode_thesaurus()) { if (check_compl_option(false)) { insert_do_complete(s); } @@ -992,7 +822,7 @@ static int insert_handle_key(InsertState *s) case Ctrl_U: // delete all inserted text in current line // CTRL-X CTRL-U completes with 'completefunc'. - if (ctrl_x_mode == CTRL_X_FUNCTION) { + if (ctrl_x_mode_function()) { insert_do_complete(s); } else { s->did_backspace = ins_bs(s->c, BACKSPACE_LINE, &s->inserted_space); @@ -1157,7 +987,7 @@ check_pum: FALLTHROUGH; case TAB: // TAB or Complete patterns along path - if (ctrl_x_mode == CTRL_X_PATH_PATTERNS) { + if (ctrl_x_mode_path_patterns()) { insert_do_complete(s); break; } @@ -1205,7 +1035,7 @@ check_pum: break; case Ctrl_K: // digraph or keyword completion - if (ctrl_x_mode == CTRL_X_DICTIONARY) { + if (ctrl_x_mode_dictionary()) { if (check_compl_option(true)) { insert_do_complete(s); } @@ -1223,7 +1053,7 @@ check_pum: break; case Ctrl_RSB: // Tag name completion after ^X - if (ctrl_x_mode != CTRL_X_TAGS) { + if (!ctrl_x_mode_tags()) { goto normalchar; } else { insert_do_complete(s); @@ -1231,7 +1061,7 @@ check_pum: break; case Ctrl_F: // File name completion after ^X - if (ctrl_x_mode != CTRL_X_FILES) { + if (!ctrl_x_mode_files()) { goto normalchar; } else { insert_do_complete(s); @@ -1240,7 +1070,7 @@ check_pum: case 's': // Spelling completion after ^X case Ctrl_S: - if (ctrl_x_mode != CTRL_X_SPELL) { + if (!ctrl_x_mode_spell()) { goto normalchar; } else { insert_do_complete(s); @@ -1248,7 +1078,7 @@ check_pum: break; case Ctrl_L: // Whole line completion after ^X - if (ctrl_x_mode != CTRL_X_WHOLE_LINE) { + if (!ctrl_x_mode_whole_line()) { goto normalchar; } FALLTHROUGH; @@ -1258,8 +1088,7 @@ check_pum: // if 'complete' is empty then plain ^P is no longer special, // but it is under other ^X modes if (*curbuf->b_p_cpt == NUL - && (ctrl_x_mode == CTRL_X_NORMAL - || ctrl_x_mode == CTRL_X_WHOLE_LINE) + && (ctrl_x_mode_normal() || ctrl_x_mode_whole_line()) && !(compl_cont_status & CONT_LOCAL)) { goto normalchar; } @@ -1394,10 +1223,10 @@ bool edit(int cmdchar, bool startln, long count) // the value of `restart_edit` before `ex_normal` returns. restart_edit = 'i'; force_restart_edit = true; + return false; } else { - terminal_enter(); + return terminal_enter(); } - return false; } // Don't allow inserting in the sandbox. @@ -1409,7 +1238,7 @@ bool edit(int cmdchar, bool startln, long count) // Don't allow changes in the buffer while editing the cmdline. The // caller of getcmdline() may get confused. // Don't allow recursive insert mode when busy with completion. - if (textlock != 0 || compl_started || compl_busy || pum_visible()) { + if (textlock != 0 || ins_compl_active() || compl_busy || pum_visible()) { emsg(_(e_textlock)); return false; } @@ -1425,6 +1254,11 @@ bool edit(int cmdchar, bool startln, long count) return s->c == Ctrl_O; } +bool ins_need_undo_get(void) +{ + return ins_need_undo; +} + /// Redraw for Insert mode. /// This is postponed until getting the next character to make '$' in the 'cpo' /// option work correctly. @@ -1432,7 +1266,7 @@ bool edit(int cmdchar, bool startln, long count) /// inserting sequences of characters (e.g., for CTRL-R). /// /// @param ready not busy with something -static void ins_redraw(bool ready) +void ins_redraw(bool ready) { if (char_avail()) { return; @@ -2019,3523 +1853,6 @@ static bool del_char_after_col(int limit_col) return true; } -/* - * CTRL-X pressed in Insert mode. - */ -static void ins_ctrl_x(void) -{ - if (ctrl_x_mode != CTRL_X_CMDLINE && ctrl_x_mode != CTRL_X_CMDLINE_CTRL_X) { - // if the next ^X<> won't ADD nothing, then reset compl_cont_status - if (compl_cont_status & CONT_N_ADDS) { - compl_cont_status |= CONT_INTRPT; - } else { - compl_cont_status = 0; - } - // We're not sure which CTRL-X mode it will be yet - ctrl_x_mode = CTRL_X_NOT_DEFINED_YET; - edit_submode = (char_u *)_(CTRL_X_MSG(ctrl_x_mode)); - edit_submode_pre = NULL; - showmode(); - } else { - // CTRL-X in CTRL-X CTRL-V mode behaves differently to make CTRL-X - // CTRL-V look like CTRL-N - ctrl_x_mode = CTRL_X_CMDLINE_CTRL_X; - } - - may_trigger_modechanged(); -} - -// Whether other than default completion has been selected. -bool ctrl_x_mode_not_default(void) - FUNC_ATTR_PURE -{ - return ctrl_x_mode != CTRL_X_NORMAL; -} - -// Whether CTRL-X was typed without a following character, -// not including when in CTRL-X CTRL-V mode. -bool ctrl_x_mode_not_defined_yet(void) - FUNC_ATTR_PURE -{ - return ctrl_x_mode == CTRL_X_NOT_DEFINED_YET; -} - -/// Check that the "dict" or "tsr" option can be used. -/// -/// @param dict_opt check "dict" when true, "tsr" when false. -static bool check_compl_option(bool dict_opt) -{ - if (dict_opt - ? (*curbuf->b_p_dict == NUL && *p_dict == NUL && !curwin->w_p_spell) - : (*curbuf->b_p_tsr == NUL && *p_tsr == NUL - && *curbuf->b_p_tsrfu == NUL && *p_tsrfu == NUL)) { - ctrl_x_mode = CTRL_X_NORMAL; - edit_submode = NULL; - msg_attr((dict_opt - ? _("'dictionary' option is empty") - : _("'thesaurus' option is empty")), HL_ATTR(HLF_E)); - if (emsg_silent == 0) { - vim_beep(BO_COMPL); - setcursor(); - ui_flush(); - os_delay(2004L, false); - } - return false; - } - return true; -} - -/// Check that the character "c" a valid key to go to or keep us in CTRL-X mode? -/// This depends on the current mode. -/// -/// @param c character to check -bool vim_is_ctrl_x_key(int c) - FUNC_ATTR_WARN_UNUSED_RESULT -{ - // Always allow ^R - let its results then be checked - if (c == Ctrl_R) { - return true; - } - - // Accept <PageUp> and <PageDown> if the popup menu is visible. - if (ins_compl_pum_key(c)) { - return true; - } - - switch (ctrl_x_mode) { - case 0: // Not in any CTRL-X mode - return c == Ctrl_N || c == Ctrl_P || c == Ctrl_X; - case CTRL_X_NOT_DEFINED_YET: - case CTRL_X_CMDLINE_CTRL_X: - return c == Ctrl_X || c == Ctrl_Y || c == Ctrl_E - || c == Ctrl_L || c == Ctrl_F || c == Ctrl_RSB - || c == Ctrl_I || c == Ctrl_D || c == Ctrl_P - || c == Ctrl_N || c == Ctrl_T || c == Ctrl_V - || c == Ctrl_Q || c == Ctrl_U || c == Ctrl_O - || c == Ctrl_S || c == Ctrl_K || c == 's' - || c == Ctrl_Z; - case CTRL_X_SCROLL: - return c == Ctrl_Y || c == Ctrl_E; - case CTRL_X_WHOLE_LINE: - return c == Ctrl_L || c == Ctrl_P || c == Ctrl_N; - case CTRL_X_FILES: - return c == Ctrl_F || c == Ctrl_P || c == Ctrl_N; - case CTRL_X_DICTIONARY: - return c == Ctrl_K || c == Ctrl_P || c == Ctrl_N; - case CTRL_X_THESAURUS: - return c == Ctrl_T || c == Ctrl_P || c == Ctrl_N; - case CTRL_X_TAGS: - return c == Ctrl_RSB || c == Ctrl_P || c == Ctrl_N; - case CTRL_X_PATH_PATTERNS: - return c == Ctrl_P || c == Ctrl_N; - case CTRL_X_PATH_DEFINES: - return c == Ctrl_D || c == Ctrl_P || c == Ctrl_N; - case CTRL_X_CMDLINE: - return c == Ctrl_V || c == Ctrl_Q || c == Ctrl_P || c == Ctrl_N - || c == Ctrl_X; - case CTRL_X_FUNCTION: - return c == Ctrl_U || c == Ctrl_P || c == Ctrl_N; - case CTRL_X_OMNI: - return c == Ctrl_O || c == Ctrl_P || c == Ctrl_N; - case CTRL_X_SPELL: - return c == Ctrl_S || c == Ctrl_P || c == Ctrl_N; - case CTRL_X_EVAL: - return (c == Ctrl_P || c == Ctrl_N); - } - internal_error("vim_is_ctrl_x_key()"); - return false; -} - -/// Check that character "c" is part of the item currently being -/// completed. Used to decide whether to abandon complete mode when the menu -/// is visible. -/// -/// @param c character to check -static bool ins_compl_accept_char(int c) - FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT -{ - if (ctrl_x_mode & CTRL_X_WANT_IDENT) { - // When expanding an identifier only accept identifier chars. - return vim_isIDc(c); - } - - switch (ctrl_x_mode) { - case CTRL_X_FILES: - // When expanding file name only accept file name chars. But not - // path separators, so that "proto/<Tab>" expands files in - // "proto", not "proto/" as a whole - return vim_isfilec(c) && !vim_ispathsep(c); - - case CTRL_X_CMDLINE: - case CTRL_X_CMDLINE_CTRL_X: - case CTRL_X_OMNI: - // Command line and Omni completion can work with just about any - // printable character, but do stop at white space. - return vim_isprintc(c) && !ascii_iswhite(c); - - case CTRL_X_WHOLE_LINE: - // For while line completion a space can be part of the line. - return vim_isprintc(c); - } - return vim_iswordc(c); -} - -/// This is like ins_compl_add(), but if 'ic' and 'inf' are set, then the -/// case of the originally typed text is used, and the case of the completed -/// text is inferred, ie this tries to work out what case you probably wanted -/// the rest of the word to be in -- webb -/// -/// @param[in] cont_s_ipos next ^X<> will set initial_pos -int ins_compl_add_infercase(char_u *str_arg, int len, bool icase, char_u *fname, Direction dir, - bool cont_s_ipos) - FUNC_ATTR_NONNULL_ARG(1) -{ - char_u *str = str_arg; - int i, c; - int actual_len; // Take multi-byte characters - int actual_compl_length; // into account. - int min_len; - bool has_lower = false; - bool was_letter = false; - int flags = 0; - - if (p_ic && curbuf->b_p_inf && len > 0) { - // Infer case of completed part. - - // Find actual length of completion. - { - const char_u *p = str; - actual_len = 0; - while (*p != NUL) { - MB_PTR_ADV(p); - actual_len++; - } - } - - // Find actual length of original text. - { - const char_u *p = compl_orig_text; - actual_compl_length = 0; - while (*p != NUL) { - MB_PTR_ADV(p); - actual_compl_length++; - } - } - - /* "actual_len" may be smaller than "actual_compl_length" when using - * thesaurus, only use the minimum when comparing. */ - min_len = actual_len < actual_compl_length - ? actual_len : actual_compl_length; - - // Allocate wide character array for the completion and fill it. - int *const wca = xmalloc((size_t)actual_len * sizeof(*wca)); - { - const char_u *p = str; - for (i = 0; i < actual_len; i++) { - wca[i] = mb_ptr2char_adv(&p); - } - } - - // Rule 1: Were any chars converted to lower? - { - const char_u *p = compl_orig_text; - for (i = 0; i < min_len; i++) { - c = mb_ptr2char_adv(&p); - if (mb_islower(c)) { - has_lower = true; - if (mb_isupper(wca[i])) { - // Rule 1 is satisfied. - for (i = actual_compl_length; i < actual_len; i++) { - wca[i] = mb_tolower(wca[i]); - } - break; - } - } - } - } - - /* - * Rule 2: No lower case, 2nd consecutive letter converted to - * upper case. - */ - if (!has_lower) { - const char_u *p = compl_orig_text; - for (i = 0; i < min_len; i++) { - c = mb_ptr2char_adv(&p); - if (was_letter && mb_isupper(c) && mb_islower(wca[i])) { - // Rule 2 is satisfied. - for (i = actual_compl_length; i < actual_len; i++) { - wca[i] = mb_toupper(wca[i]); - } - break; - } - was_letter = mb_islower(c) || mb_isupper(c); - } - } - - // Copy the original case of the part we typed. - { - const char_u *p = compl_orig_text; - for (i = 0; i < min_len; i++) { - c = mb_ptr2char_adv(&p); - if (mb_islower(c)) { - wca[i] = mb_tolower(wca[i]); - } else if (mb_isupper(c)) { - wca[i] = mb_toupper(wca[i]); - } - } - } - - // Generate encoding specific output from wide character array. - // Multi-byte characters can occupy up to five bytes more than - // ASCII characters, and we also need one byte for NUL, so stay - // six bytes away from the edge of IObuff. - { - char_u *p = IObuff; - i = 0; - while (i < actual_len && (p - IObuff + 6) < IOSIZE) { - p += utf_char2bytes(wca[i++], (char *)p); - } - *p = NUL; - } - - xfree(wca); - - str = IObuff; - } - if (cont_s_ipos) { - flags |= CP_CONT_S_IPOS; - } - if (icase) { - flags |= CP_ICASE; - } - - return ins_compl_add(str, len, fname, NULL, false, NULL, dir, flags, false); -} - -/// Add a match to the list of matches -/// -/// @param[in] str Match to add. -/// @param[in] len Match length, -1 to use #STRLEN. -/// @param[in] fname File name match comes from. May be NULL. -/// @param[in] cptext Extra text for popup menu. May be NULL. If not NULL, -/// must have exactly #CPT_COUNT items. -/// @param[in] cptext_allocated If true, will not copy cptext strings. -/// -/// @note Will free strings in case of error. -/// cptext itself will not be freed. -/// @param[in] cdir Completion direction. -/// @param[in] adup True if duplicate matches are to be accepted. -/// -/// @return NOTDONE if the given string is already in the list of completions, -/// otherwise it is added to the list and OK is returned. FAIL will be -/// returned in case of error. -static int ins_compl_add(char_u *const str, int len, char_u *const fname, - char_u *const *const cptext, const bool cptext_allocated, - typval_T *user_data, const Direction cdir, int flags_arg, const bool adup) - FUNC_ATTR_NONNULL_ARG(1) -{ - compl_T *match; - const Direction dir = (cdir == kDirectionNotSet ? compl_direction : cdir); - int flags = flags_arg; - - if (flags & CP_FAST) { - fast_breakcheck(); - } else { - os_breakcheck(); - } -#define FREE_CPTEXT(cptext, cptext_allocated) \ - do { \ - if ((cptext) != NULL && (cptext_allocated)) { \ - for (size_t i = 0; i < CPT_COUNT; i++) { \ - xfree((cptext)[i]); \ - } \ - } \ - } while (0) - if (got_int) { - FREE_CPTEXT(cptext, cptext_allocated); - return FAIL; - } - if (len < 0) { - len = (int)STRLEN(str); - } - - /* - * If the same match is already present, don't add it. - */ - if (compl_first_match != NULL && !adup) { - match = compl_first_match; - do { - if (!(match->cp_flags & CP_ORIGINAL_TEXT) - && STRNCMP(match->cp_str, str, len) == 0 - && match->cp_str[len] == NUL) { - FREE_CPTEXT(cptext, cptext_allocated); - return NOTDONE; - } - match = match->cp_next; - } while (match != NULL && match != compl_first_match); - } - - // Remove any popup menu before changing the list of matches. - ins_compl_del_pum(); - - /* - * Allocate a new match structure. - * Copy the values to the new match structure. - */ - match = xcalloc(1, sizeof(compl_T)); - match->cp_number = -1; - if (flags & CP_ORIGINAL_TEXT) { - match->cp_number = 0; - } - match->cp_str = vim_strnsave(str, (size_t)len); - - // match-fname is: - // - compl_curr_match->cp_fname if it is a string equal to fname. - // - a copy of fname, CP_FREE_FNAME is set to free later THE allocated mem. - // - NULL otherwise. --Acevedo - if (fname != NULL - && compl_curr_match != NULL - && compl_curr_match->cp_fname != NULL - && STRCMP(fname, compl_curr_match->cp_fname) == 0) { - match->cp_fname = compl_curr_match->cp_fname; - } else if (fname != NULL) { - match->cp_fname = vim_strsave(fname); - flags |= CP_FREE_FNAME; - } else { - match->cp_fname = NULL; - } - match->cp_flags = flags; - - if (cptext != NULL) { - int i; - - for (i = 0; i < CPT_COUNT; i++) { - if (cptext[i] == NULL) { - continue; - } - if (*cptext[i] != NUL) { - match->cp_text[i] = (cptext_allocated - ? cptext[i] - : (char_u *)xstrdup((char *)cptext[i])); - } else if (cptext_allocated) { - xfree(cptext[i]); - } - } - } - - if (user_data != NULL) { - match->cp_user_data = *user_data; - } - - /* - * Link the new match structure in the list of matches. - */ - if (compl_first_match == NULL) { - match->cp_next = match->cp_prev = NULL; - } else if (dir == FORWARD) { - match->cp_next = compl_curr_match->cp_next; - match->cp_prev = compl_curr_match; - } else { // BACKWARD - match->cp_next = compl_curr_match; - match->cp_prev = compl_curr_match->cp_prev; - } - if (match->cp_next) { - match->cp_next->cp_prev = match; - } - if (match->cp_prev) { - match->cp_prev->cp_next = match; - } else { // if there's nothing before, it is the first match - compl_first_match = match; - } - compl_curr_match = match; - - /* - * Find the longest common string if still doing that. - */ - if (compl_get_longest && (flags & CP_ORIGINAL_TEXT) == 0) { - ins_compl_longest_match(match); - } - - return OK; -} - -/// Check that "str[len]" matches with "match->cp_str", considering -/// "match->cp_flags". -/// -/// @param match completion match -/// @param str character string to check -/// @param len length of "str" -static bool ins_compl_equal(compl_T *match, char_u *str, size_t len) - FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL -{ - if (match->cp_flags & CP_EQUAL) { - return true; - } - if (match->cp_flags & CP_ICASE) { - return STRNICMP(match->cp_str, str, len) == 0; - } - return STRNCMP(match->cp_str, str, len) == 0; -} - -/* - * Reduce the longest common string for match "match". - */ -static void ins_compl_longest_match(compl_T *match) -{ - char_u *p, *s; - int c1, c2; - int had_match; - - if (compl_leader == NULL) { - // First match, use it as a whole. - compl_leader = vim_strsave(match->cp_str); - had_match = (curwin->w_cursor.col > compl_col); - ins_compl_delete(); - ins_bytes(compl_leader + ins_compl_len()); - ins_redraw(FALSE); - - /* When the match isn't there (to avoid matching itself) remove it - * again after redrawing. */ - if (!had_match) { - ins_compl_delete(); - } - compl_used_match = false; - } else { - // Reduce the text if this match differs from compl_leader. - p = compl_leader; - s = match->cp_str; - while (*p != NUL) { - c1 = utf_ptr2char((char *)p); - c2 = utf_ptr2char((char *)s); - - if ((match->cp_flags & CP_ICASE) - ? (mb_tolower(c1) != mb_tolower(c2)) - : (c1 != c2)) { - break; - } - MB_PTR_ADV(p); - MB_PTR_ADV(s); - } - - if (*p != NUL) { - // Leader was shortened, need to change the inserted text. - *p = NUL; - had_match = (curwin->w_cursor.col > compl_col); - ins_compl_delete(); - ins_bytes(compl_leader + ins_compl_len()); - ins_redraw(FALSE); - - /* When the match isn't there (to avoid matching itself) remove it - * again after redrawing. */ - if (!had_match) { - ins_compl_delete(); - } - } - - compl_used_match = false; - } -} - -/* - * Add an array of matches to the list of matches. - * Frees matches[]. - */ -static void ins_compl_add_matches(int num_matches, char_u **matches, int icase) - FUNC_ATTR_NONNULL_ALL -{ - int add_r = OK; - Direction dir = compl_direction; - - for (int i = 0; i < num_matches && add_r != FAIL; i++) { - if ((add_r = ins_compl_add(matches[i], -1, NULL, NULL, false, NULL, dir, - CP_FAST | (icase ? CP_ICASE : 0), - false)) == OK) { - // If dir was BACKWARD then honor it just once. - dir = FORWARD; - } - } - FreeWild(num_matches, matches); -} - -/* Make the completion list cyclic. - * Return the number of matches (excluding the original). - */ -static int ins_compl_make_cyclic(void) -{ - compl_T *match; - int count = 0; - - if (compl_first_match != NULL) { - /* - * Find the end of the list. - */ - match = compl_first_match; - // there's always an entry for the compl_orig_text, it doesn't count. - while (match->cp_next != NULL && match->cp_next != compl_first_match) { - match = match->cp_next; - ++count; - } - match->cp_next = compl_first_match; - compl_first_match->cp_prev = match; - } - return count; -} - -// Set variables that store noselect and noinsert behavior from the -// 'completeopt' value. -void completeopt_was_set(void) -{ - compl_no_insert = false; - compl_no_select = false; - if (strstr((char *)p_cot, "noselect") != NULL) { - compl_no_select = true; - } - if (strstr((char *)p_cot, "noinsert") != NULL) { - compl_no_insert = true; - } -} - -/* - * Start completion for the complete() function. - * "startcol" is where the matched text starts (1 is first column). - * "list" is the list of matches. - */ -void set_completion(colnr_T startcol, list_T *list) -{ - int flags = CP_ORIGINAL_TEXT; - - // If already doing completions stop it. - if (ctrl_x_mode != CTRL_X_NORMAL) { - ins_compl_prep(' '); - } - ins_compl_clear(); - ins_compl_free(); - - compl_direction = FORWARD; - if (startcol > curwin->w_cursor.col) { - startcol = curwin->w_cursor.col; - } - compl_col = startcol; - compl_length = (int)curwin->w_cursor.col - (int)startcol; - // compl_pattern doesn't need to be set - compl_orig_text = vim_strnsave(get_cursor_line_ptr() + compl_col, - (size_t)compl_length); - if (p_ic) { - flags |= CP_ICASE; - } - if (ins_compl_add(compl_orig_text, -1, NULL, NULL, false, NULL, 0, - flags | CP_FAST, false) != OK) { - return; - } - - ctrl_x_mode = CTRL_X_EVAL; - - ins_compl_add_list(list); - compl_matches = ins_compl_make_cyclic(); - compl_started = true; - compl_used_match = true; - compl_cont_status = 0; - int save_w_wrow = curwin->w_wrow; - int save_w_leftcol = curwin->w_leftcol; - - compl_curr_match = compl_first_match; - if (compl_no_insert || compl_no_select) { - ins_complete(K_DOWN, false); - if (compl_no_select) { - ins_complete(K_UP, false); - } - } else { - ins_complete(Ctrl_N, false); - } - compl_enter_selects = compl_no_insert; - - // Lazily show the popup menu, unless we got interrupted. - if (!compl_interrupted) { - show_pum(save_w_wrow, save_w_leftcol); - } - - may_trigger_modechanged(); - ui_flush(); -} - -/* "compl_match_array" points the currently displayed list of entries in the - * popup menu. It is NULL when there is no popup menu. */ -static pumitem_T *compl_match_array = NULL; -static int compl_match_arraysize; - -/* - * Remove any popup menu. - */ -static void ins_compl_del_pum(void) -{ - if (compl_match_array != NULL) { - pum_undisplay(false); - XFREE_CLEAR(compl_match_array); - } -} - -/// Check if the popup menu should be displayed. -static bool pum_wanted(void) - FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT -{ - // "completeopt" must contain "menu" or "menuone" - return vim_strchr((char *)p_cot, 'm') != NULL; -} - -/// Check that there are two or more matches to be shown in the popup menu. -/// One if "completopt" contains "menuone". -static bool pum_enough_matches(void) - FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT -{ - // Don't display the popup menu if there are no matches or there is only - // one (ignoring the original text). - compl_T *comp = compl_first_match; - int i = 0; - do { - if (comp == NULL - || ((comp->cp_flags & CP_ORIGINAL_TEXT) == 0 && ++i == 2)) { - break; - } - comp = comp->cp_next; - } while (comp != compl_first_match); - - if (strstr((char *)p_cot, "menuone") != NULL) { - return i >= 1; - } - return i >= 2; -} - -static void trigger_complete_changed_event(int cur) -{ - static bool recursive = false; - save_v_event_T save_v_event; - - if (recursive) { - return; - } - - dict_T *v_event = get_v_event(&save_v_event); - if (cur < 0) { - tv_dict_add_dict(v_event, S_LEN("completed_item"), tv_dict_alloc()); - } else { - dict_T *item = ins_compl_dict_alloc(compl_curr_match); - tv_dict_add_dict(v_event, S_LEN("completed_item"), item); - } - pum_set_event_info(v_event); - tv_dict_set_keys_readonly(v_event); - - recursive = true; - textlock++; - apply_autocmds(EVENT_COMPLETECHANGED, NULL, NULL, false, curbuf); - textlock--; - recursive = false; - - restore_v_event(v_event, &save_v_event); -} - -/// Show the popup menu for the list of matches. -/// Also adjusts "compl_shown_match" to an entry that is actually displayed. -void ins_compl_show_pum(void) -{ - compl_T *compl; - compl_T *shown_compl = NULL; - bool did_find_shown_match = false; - bool shown_match_ok = false; - int i; - int cur = -1; - colnr_T col; - int lead_len = 0; - bool array_changed = false; - - if (!pum_wanted() || !pum_enough_matches()) { - return; - } - - // Dirty hard-coded hack: remove any matchparen highlighting. - do_cmdline_cmd("if exists('g:loaded_matchparen')|3match none|endif"); - - // Update the screen before drawing the popup menu over it. - update_screen(0); - - if (compl_match_array == NULL) { - array_changed = true; - // Need to build the popup menu list. - compl_match_arraysize = 0; - compl = compl_first_match; - // - // If it's user complete function and refresh_always, - // do not use "compl_leader" as prefix filter. - // - if (ins_compl_need_restart()) { - XFREE_CLEAR(compl_leader); - } - if (compl_leader != NULL) { - lead_len = (int)STRLEN(compl_leader); - } - do { - if ((compl->cp_flags & CP_ORIGINAL_TEXT) == 0 - && (compl_leader == NULL - || ins_compl_equal(compl, compl_leader, (size_t)lead_len))) { - compl_match_arraysize++; - } - compl = compl->cp_next; - } while (compl != NULL && compl != compl_first_match); - if (compl_match_arraysize == 0) { - return; - } - - assert(compl_match_arraysize >= 0); - compl_match_array = xcalloc((size_t)compl_match_arraysize, sizeof(pumitem_T)); - // If the current match is the original text don't find the first - // match after it, don't highlight anything. - if (compl_shown_match->cp_flags & CP_ORIGINAL_TEXT) { - shown_match_ok = true; - } - - i = 0; - compl = compl_first_match; - do { - if ((compl->cp_flags & CP_ORIGINAL_TEXT) == 0 - && (compl_leader == NULL - || ins_compl_equal(compl, compl_leader, (size_t)lead_len))) { - if (!shown_match_ok) { - if (compl == compl_shown_match || did_find_shown_match) { - /* This item is the shown match or this is the - * first displayed item after the shown match. */ - compl_shown_match = compl; - did_find_shown_match = true; - shown_match_ok = true; - } else { - // Remember this displayed match for when the - // shown match is just below it. - shown_compl = compl; - } - cur = i; - } - - if (compl->cp_text[CPT_ABBR] != NULL) { - compl_match_array[i].pum_text = - compl->cp_text[CPT_ABBR]; - } else { - compl_match_array[i].pum_text = compl->cp_str; - } - compl_match_array[i].pum_kind = compl->cp_text[CPT_KIND]; - compl_match_array[i].pum_info = compl->cp_text[CPT_INFO]; - if (compl->cp_text[CPT_MENU] != NULL) { - compl_match_array[i++].pum_extra = - compl->cp_text[CPT_MENU]; - } else { - compl_match_array[i++].pum_extra = compl->cp_fname; - } - } - - if (compl == compl_shown_match) { - did_find_shown_match = true; - - /* When the original text is the shown match don't set - * compl_shown_match. */ - if (compl->cp_flags & CP_ORIGINAL_TEXT) { - shown_match_ok = true; - } - - if (!shown_match_ok && shown_compl != NULL) { - /* The shown match isn't displayed, set it to the - * previously displayed match. */ - compl_shown_match = shown_compl; - shown_match_ok = true; - } - } - compl = compl->cp_next; - } while (compl != NULL && compl != compl_first_match); - - if (!shown_match_ok) { // no displayed match at all - cur = -1; - } - } else { - // popup menu already exists, only need to find the current item. - for (i = 0; i < compl_match_arraysize; i++) { - if (compl_match_array[i].pum_text == compl_shown_match->cp_str - || compl_match_array[i].pum_text - == compl_shown_match->cp_text[CPT_ABBR]) { - cur = i; - break; - } - } - } - - // In Replace mode when a $ is displayed at the end of the line only - // part of the screen would be updated. We do need to redraw here. - dollar_vcol = -1; - - // Compute the screen column of the start of the completed text. - // Use the cursor to get all wrapping and other settings right. - col = curwin->w_cursor.col; - curwin->w_cursor.col = compl_col; - pum_selected_item = cur; - pum_display(compl_match_array, compl_match_arraysize, cur, array_changed, 0); - curwin->w_cursor.col = col; - - if (has_event(EVENT_COMPLETECHANGED)) { - trigger_complete_changed_event(cur); - } -} - -#define DICT_FIRST (1) // use just first element in "dict" -#define DICT_EXACT (2) // "dict" is the exact name of a file - -/// Add any identifiers that match the given pattern in the list of dictionary -/// files "dict_start" to the list of completions. -/// -/// @param flags DICT_FIRST and/or DICT_EXACT -/// @param thesaurus Thesaurus completion -static void ins_compl_dictionaries(char_u *dict_start, char_u *pat, int flags, int thesaurus) -{ - char_u *dict = dict_start; - char_u *ptr; - char_u *buf; - regmatch_T regmatch; - char_u **files; - int count; - int save_p_scs; - Direction dir = compl_direction; - - if (*dict == NUL) { - /* When 'dictionary' is empty and spell checking is enabled use - * "spell". */ - if (!thesaurus && curwin->w_p_spell) { - dict = (char_u *)"spell"; - } else { - return; - } - } - - buf = xmalloc(LSIZE); - regmatch.regprog = NULL; // so that we can goto theend - - // If 'infercase' is set, don't use 'smartcase' here - save_p_scs = p_scs; - if (curbuf->b_p_inf) { - p_scs = FALSE; - } - - /* When invoked to match whole lines for CTRL-X CTRL-L adjust the pattern - * to only match at the start of a line. Otherwise just match the - * pattern. Also need to double backslashes. */ - if (CTRL_X_MODE_LINE_OR_EVAL(ctrl_x_mode)) { - char_u *pat_esc = vim_strsave_escaped(pat, (char_u *)"\\"); - - size_t len = STRLEN(pat_esc) + 10; - ptr = xmalloc(len); - vim_snprintf((char *)ptr, len, "^\\s*\\zs\\V%s", pat_esc); - regmatch.regprog = vim_regcomp((char *)ptr, RE_MAGIC); - xfree(pat_esc); - xfree(ptr); - } else { - regmatch.regprog = vim_regcomp((char *)pat, p_magic ? RE_MAGIC : 0); - if (regmatch.regprog == NULL) { - goto theend; - } - } - - // ignore case depends on 'ignorecase', 'smartcase' and "pat" - regmatch.rm_ic = ignorecase(pat); - while (*dict != NUL && !got_int && !compl_interrupted) { - // copy one dictionary file name into buf - if (flags == DICT_EXACT) { - count = 1; - files = &dict; - } else { - /* Expand wildcards in the dictionary name, but do not allow - * backticks (for security, the 'dict' option may have been set in - * a modeline). */ - copy_option_part((char **)&dict, (char *)buf, LSIZE, ","); - if (!thesaurus && STRCMP(buf, "spell") == 0) { - count = -1; - } else if (vim_strchr((char *)buf, '`') != NULL - || expand_wildcards(1, &buf, &count, &files, - EW_FILE|EW_SILENT) != OK) { - count = 0; - } - } - - if (count == -1) { - /* Complete from active spelling. Skip "\<" in the pattern, we - * don't use it as a RE. */ - if (pat[0] == '\\' && pat[1] == '<') { - ptr = pat + 2; - } else { - ptr = pat; - } - spell_dump_compl(ptr, regmatch.rm_ic, &dir, 0); - } else if (count > 0) { // avoid warning for using "files" uninit - ins_compl_files(count, files, thesaurus, flags, - ®match, buf, &dir); - if (flags != DICT_EXACT) { - FreeWild(count, files); - } - } - if (flags != 0) { - break; - } - } - -theend: - p_scs = save_p_scs; - vim_regfree(regmatch.regprog); - xfree(buf); -} - -static void ins_compl_files(int count, char_u **files, int thesaurus, int flags, - regmatch_T *regmatch, char_u *buf, Direction *dir) - FUNC_ATTR_NONNULL_ARG(2, 7) -{ - char_u *ptr; - int i; - FILE *fp; - int add_r; - - for (i = 0; i < count && !got_int && !compl_interrupted; i++) { - fp = os_fopen((char *)files[i], "r"); // open dictionary file - if (flags != DICT_EXACT) { - msg_hist_off = true; // reset in msg_trunc_attr() - vim_snprintf((char *)IObuff, IOSIZE, - _("Scanning dictionary: %s"), (char *)files[i]); - (void)msg_trunc_attr((char *)IObuff, true, HL_ATTR(HLF_R)); - } - - if (fp == NULL) { - continue; - } - /* - * Read dictionary file line by line. - * Check each line for a match. - */ - while (!got_int && !compl_interrupted - && !vim_fgets(buf, LSIZE, fp)) { - ptr = buf; - while (vim_regexec(regmatch, (char *)buf, (colnr_T)(ptr - buf))) { - ptr = regmatch->startp[0]; - if (CTRL_X_MODE_LINE_OR_EVAL(ctrl_x_mode)) { - ptr = find_line_end(ptr); - } else { - ptr = find_word_end(ptr); - } - add_r = ins_compl_add_infercase(regmatch->startp[0], - (int)(ptr - regmatch->startp[0]), - p_ic, files[i], *dir, false); - if (thesaurus) { - char_u *wstart; - - /* - * Add the other matches on the line - */ - ptr = buf; - while (!got_int) { - /* Find start of the next word. Skip white - * space and punctuation. */ - ptr = find_word_start(ptr); - if (*ptr == NUL || *ptr == NL) { - break; - } - wstart = ptr; - - // Find end of the word. - // Japanese words may have characters in - // different classes, only separate words - // with single-byte non-word characters. - while (*ptr != NUL) { - const int l = utfc_ptr2len((char *)ptr); - - if (l < 2 && !vim_iswordc(*ptr)) { - break; - } - ptr += l; - } - - // Add the word. Skip the regexp match. - if (wstart != regmatch->startp[0]) { - add_r = ins_compl_add_infercase(wstart, (int)(ptr - wstart), - p_ic, files[i], *dir, false); - } - } - } - if (add_r == OK) { - // if dir was BACKWARD then honor it just once - *dir = FORWARD; - } else if (add_r == FAIL) { - break; - } - // avoid expensive call to vim_regexec() when at end - // of line - if (*ptr == '\n' || got_int) { - break; - } - } - line_breakcheck(); - ins_compl_check_keys(50, false); - } - fclose(fp); - } -} - -/* - * Find the start of the next word. - * Returns a pointer to the first char of the word. Also stops at a NUL. - */ -char_u *find_word_start(char_u *ptr) - FUNC_ATTR_PURE -{ - while (*ptr != NUL && *ptr != '\n' && mb_get_class(ptr) <= 1) { - ptr += utfc_ptr2len((char *)ptr); - } - return ptr; -} - -/* - * Find the end of the word. Assumes it starts inside a word. - * Returns a pointer to just after the word. - */ -char_u *find_word_end(char_u *ptr) - FUNC_ATTR_PURE -{ - const int start_class = mb_get_class(ptr); - if (start_class > 1) { - while (*ptr != NUL) { - ptr += utfc_ptr2len((char *)ptr); - if (mb_get_class(ptr) != start_class) { - break; - } - } - } - return ptr; -} - -/* - * Find the end of the line, omitting CR and NL at the end. - * Returns a pointer to just after the line. - */ -static char_u *find_line_end(char_u *ptr) -{ - char_u *s; - - s = ptr + STRLEN(ptr); - while (s > ptr && (s[-1] == CAR || s[-1] == NL)) { - --s; - } - return s; -} - -/* - * Free the list of completions - */ -static void ins_compl_free(void) -{ - compl_T *match; - - XFREE_CLEAR(compl_pattern); - XFREE_CLEAR(compl_leader); - - if (compl_first_match == NULL) { - return; - } - - ins_compl_del_pum(); - pum_clear(); - - compl_curr_match = compl_first_match; - do { - match = compl_curr_match; - compl_curr_match = compl_curr_match->cp_next; - xfree(match->cp_str); - // several entries may use the same fname, free it just once. - if (match->cp_flags & CP_FREE_FNAME) { - xfree(match->cp_fname); - } - for (int i = 0; i < CPT_COUNT; i++) { - xfree(match->cp_text[i]); - } - tv_clear(&match->cp_user_data); - xfree(match); - } while (compl_curr_match != NULL && compl_curr_match != compl_first_match); - compl_first_match = compl_curr_match = NULL; - compl_shown_match = NULL; - compl_old_match = NULL; -} - -static void ins_compl_clear(void) -{ - compl_cont_status = 0; - compl_started = false; - compl_matches = 0; - XFREE_CLEAR(compl_pattern); - XFREE_CLEAR(compl_leader); - edit_submode_extra = NULL; - XFREE_CLEAR(compl_orig_text); - compl_enter_selects = false; - // clear v:completed_item - set_vim_var_dict(VV_COMPLETED_ITEM, tv_dict_alloc_lock(VAR_FIXED)); -} - -/// Check that Insert completion is active. -bool ins_compl_active(void) - FUNC_ATTR_PURE -{ - return compl_started; -} - -static void ins_compl_update_sequence_numbers(void) -{ - int number = 0; - compl_T *match; - - if (compl_direction == FORWARD) { - // search backwards for the first valid (!= -1) number. - // This should normally succeed already at the first loop - // cycle, so it's fast! - for (match = compl_curr_match->cp_prev; - match != NULL && match != compl_first_match; - match = match->cp_prev) { - if (match->cp_number != -1) { - number = match->cp_number; - break; - } - } - if (match != NULL) { - // go up and assign all numbers which are not assigned yet - for (match = match->cp_next; - match != NULL && match->cp_number == -1; - match = match->cp_next) { - match->cp_number = ++number; - } - } - } else { // BACKWARD - assert(compl_direction == BACKWARD); - // search forwards (upwards) for the first valid (!= -1) - // number. This should normally succeed already at the - // first loop cycle, so it's fast! - for (match = compl_curr_match->cp_next; - match != NULL && match != compl_first_match; - match = match->cp_next) { - if (match->cp_number != -1) { - number = match->cp_number; - break; - } - } - if (match != NULL) { - // go down and assign all numbers which are not - // assigned yet - for (match = match->cp_prev; - match && match->cp_number == -1; - match = match->cp_prev) { - match->cp_number = ++number; - } - } - } -} - -// Get complete information -void get_complete_info(list_T *what_list, dict_T *retdict) -{ -#define CI_WHAT_MODE 0x01 -#define CI_WHAT_PUM_VISIBLE 0x02 -#define CI_WHAT_ITEMS 0x04 -#define CI_WHAT_SELECTED 0x08 -#define CI_WHAT_INSERTED 0x10 -#define CI_WHAT_ALL 0xff - int what_flag; - - if (what_list == NULL) { - what_flag = CI_WHAT_ALL; - } else { - what_flag = 0; - for (listitem_T *item = tv_list_first(what_list) - ; item != NULL - ; item = TV_LIST_ITEM_NEXT(what_list, item)) { - const char *what = tv_get_string(TV_LIST_ITEM_TV(item)); - - if (STRCMP(what, "mode") == 0) { - what_flag |= CI_WHAT_MODE; - } else if (STRCMP(what, "pum_visible") == 0) { - what_flag |= CI_WHAT_PUM_VISIBLE; - } else if (STRCMP(what, "items") == 0) { - what_flag |= CI_WHAT_ITEMS; - } else if (STRCMP(what, "selected") == 0) { - what_flag |= CI_WHAT_SELECTED; - } else if (STRCMP(what, "inserted") == 0) { - what_flag |= CI_WHAT_INSERTED; - } - } - } - - int ret = OK; - if (what_flag & CI_WHAT_MODE) { - ret = tv_dict_add_str(retdict, S_LEN("mode"), - (char *)ins_compl_mode()); - } - - if (ret == OK && (what_flag & CI_WHAT_PUM_VISIBLE)) { - ret = tv_dict_add_nr(retdict, S_LEN("pum_visible"), pum_visible()); - } - - if (ret == OK && (what_flag & CI_WHAT_ITEMS)) { - list_T *li = tv_list_alloc(ins_compl_len()); - - ret = tv_dict_add_list(retdict, S_LEN("items"), li); - if (ret == OK && compl_first_match != NULL) { - compl_T *match = compl_first_match; - do { - if (!(match->cp_flags & CP_ORIGINAL_TEXT)) { - dict_T *di = tv_dict_alloc(); - - tv_list_append_dict(li, di); - tv_dict_add_str(di, S_LEN("word"), EMPTY_IF_NULL(match->cp_str)); - tv_dict_add_str(di, S_LEN("abbr"), EMPTY_IF_NULL(match->cp_text[CPT_ABBR])); - tv_dict_add_str(di, S_LEN("menu"), EMPTY_IF_NULL(match->cp_text[CPT_MENU])); - tv_dict_add_str(di, S_LEN("kind"), EMPTY_IF_NULL(match->cp_text[CPT_KIND])); - tv_dict_add_str(di, S_LEN("info"), EMPTY_IF_NULL(match->cp_text[CPT_INFO])); - if (match->cp_user_data.v_type == VAR_UNKNOWN) { - tv_dict_add_str(di, S_LEN("user_data"), ""); - } else { - tv_dict_add_tv(di, S_LEN("user_data"), &match->cp_user_data); - } - } - match = match->cp_next; - } while (match != NULL && match != compl_first_match); - } - } - - if (ret == OK && (what_flag & CI_WHAT_SELECTED)) { - if (compl_curr_match != NULL && compl_curr_match->cp_number == -1) { - ins_compl_update_sequence_numbers(); - } - ret = tv_dict_add_nr(retdict, S_LEN("selected"), - (compl_curr_match != NULL) - ? compl_curr_match->cp_number - 1 : -1); - } - - (void)ret; - // TODO(vim): - // if (ret == OK && (what_flag & CI_WHAT_INSERTED)) -} - -// Return Insert completion mode name string -static char_u *ins_compl_mode(void) -{ - if (ctrl_x_mode == CTRL_X_NOT_DEFINED_YET || ctrl_x_mode == CTRL_X_SCROLL || compl_started) { - return (char_u *)ctrl_x_mode_names[ctrl_x_mode & ~CTRL_X_WANT_IDENT]; - } - return (char_u *)""; -} - -/* - * Delete one character before the cursor and show the subset of the matches - * that match the word that is now before the cursor. - * Returns the character to be used, NUL if the work is done and another char - * to be got from the user. - */ -static int ins_compl_bs(void) -{ - char_u *line = get_cursor_line_ptr(); - char_u *p = line + curwin->w_cursor.col; - MB_PTR_BACK(line, p); - ptrdiff_t p_off = p - line; - - // Stop completion when the whole word was deleted. For Omni completion - // allow the word to be deleted, we won't match everything. - // Respect the 'backspace' option. - if ((int)(p - line) - (int)compl_col < 0 - || ((int)(p - line) - (int)compl_col == 0 && ctrl_x_mode != CTRL_X_OMNI) - || ctrl_x_mode == CTRL_X_EVAL - || (!can_bs(BS_START) && (int)(p - line) - (int)compl_col - - compl_length < 0)) { - return K_BS; - } - - /* Deleted more than what was used to find matches or didn't finish - * finding all matches: need to look for matches all over again. */ - if (curwin->w_cursor.col <= compl_col + compl_length - || ins_compl_need_restart()) { - ins_compl_restart(); - } - - // ins_compl_restart() calls update_screen(0) which may invalidate the pointer - // TODO(bfredl): get rid of random update_screen() calls deep inside completion logic - line = get_cursor_line_ptr(); - - xfree(compl_leader); - compl_leader = vim_strnsave(line + compl_col, (size_t)(p_off - (ptrdiff_t)compl_col)); - ins_compl_new_leader(); - if (compl_shown_match != NULL) { - // Make sure current match is not a hidden item. - compl_curr_match = compl_shown_match; - } - - return NUL; -} - -/// Check that we need to find matches again, ins_compl_restart() is to -/// be called. -static bool ins_compl_need_restart(void) - FUNC_ATTR_PURE -{ - // Return true if we didn't complete finding matches or when the - // "completefunc" returned "always" in the "refresh" dictionary item. - return compl_was_interrupted - || ((ctrl_x_mode == CTRL_X_FUNCTION || ctrl_x_mode == CTRL_X_OMNI) - && compl_opt_refresh_always); -} - -/* - * Called after changing "compl_leader". - * Show the popup menu with a different set of matches. - * May also search for matches again if the previous search was interrupted. - */ -static void ins_compl_new_leader(void) -{ - ins_compl_del_pum(); - ins_compl_delete(); - ins_bytes(compl_leader + ins_compl_len()); - compl_used_match = false; - - if (compl_started) { - ins_compl_set_original_text(compl_leader); - } else { - spell_bad_len = 0; // need to redetect bad word - // Matches were cleared, need to search for them now. - // Set "compl_restarting" to avoid that the first match is inserted. - compl_restarting = true; - if (ins_complete(Ctrl_N, true) == FAIL) { - compl_cont_status = 0; - } - compl_restarting = false; - } - - compl_enter_selects = !compl_used_match; - - // Show the popup menu with a different set of matches. - ins_compl_show_pum(); - - /* Don't let Enter select the original text when there is no popup menu. - * Don't let Enter select when use user function and refresh_always is set */ - if (compl_match_array == NULL || ins_compl_need_restart()) { - compl_enter_selects = FALSE; - } -} - -/* - * Return the length of the completion, from the completion start column to - * the cursor column. Making sure it never goes below zero. - */ -static int ins_compl_len(void) -{ - int off = (int)curwin->w_cursor.col - (int)compl_col; - - if (off < 0) { - return 0; - } - return off; -} - -/* - * Append one character to the match leader. May reduce the number of - * matches. - */ -static void ins_compl_addleader(int c) -{ - int cc; - - if (stop_arrow() == FAIL) { - return; - } - if ((cc = utf_char2len(c)) > 1) { - char buf[MB_MAXBYTES + 1]; - - utf_char2bytes(c, (char *)buf); - buf[cc] = NUL; - ins_char_bytes((char_u *)buf, (size_t)cc); - } else { - ins_char(c); - } - - // If we didn't complete finding matches we must search again. - if (ins_compl_need_restart()) { - ins_compl_restart(); - } - - xfree(compl_leader); - compl_leader = vim_strnsave(get_cursor_line_ptr() + compl_col, - (size_t)(curwin->w_cursor.col - compl_col)); - ins_compl_new_leader(); -} - -/* - * Setup for finding completions again without leaving CTRL-X mode. Used when - * BS or a key was typed while still searching for matches. - */ -static void ins_compl_restart(void) -{ - /* update screen before restart. - * so if complete is blocked, - * will stay to the last popup menu and reduce flicker */ - update_screen(0); - ins_compl_free(); - compl_started = false; - compl_matches = 0; - compl_cont_status = 0; - compl_cont_mode = 0; -} - -/* - * Set the first match, the original text. - */ -static void ins_compl_set_original_text(char_u *str) - FUNC_ATTR_NONNULL_ALL -{ - // Replace the original text entry. - // The CP_ORIGINAL_TEXT flag is either at the first item or might possibly be - // at the last item for backward completion - if (compl_first_match->cp_flags & CP_ORIGINAL_TEXT) { // safety check - xfree(compl_first_match->cp_str); - compl_first_match->cp_str = vim_strsave(str); - } else if (compl_first_match->cp_prev != NULL - && (compl_first_match->cp_prev->cp_flags & CP_ORIGINAL_TEXT)) { - xfree(compl_first_match->cp_prev->cp_str); - compl_first_match->cp_prev->cp_str = vim_strsave(str); - } -} - -/* - * Append one character to the match leader. May reduce the number of - * matches. - */ -static void ins_compl_addfrommatch(void) -{ - char_u *p; - int len = (int)curwin->w_cursor.col - (int)compl_col; - int c; - compl_T *cp; - assert(compl_shown_match != NULL); - p = compl_shown_match->cp_str; - if ((int)STRLEN(p) <= len) { // the match is too short - // When still at the original match use the first entry that matches - // the leader. - if (compl_shown_match->cp_flags & CP_ORIGINAL_TEXT) { - p = NULL; - for (cp = compl_shown_match->cp_next; cp != NULL - && cp != compl_first_match; cp = cp->cp_next) { - if (compl_leader == NULL - || ins_compl_equal(cp, compl_leader, STRLEN(compl_leader))) { - p = cp->cp_str; - break; - } - } - if (p == NULL || (int)STRLEN(p) <= len) { - return; - } - } else { - return; - } - } - p += len; - c = utf_ptr2char((char *)p); - ins_compl_addleader(c); -} - -/// Prepare for Insert mode completion, or stop it. -/// Called just after typing a character in Insert mode. -/// -/// @param c character that was typed -/// -/// @return true when the character is not to be inserted; -static bool ins_compl_prep(int c) -{ - char_u *ptr; - bool retval = false; - const int prev_mode = ctrl_x_mode; - - /* Forget any previous 'special' messages if this is actually - * a ^X mode key - bar ^R, in which case we wait to see what it gives us. - */ - if (c != Ctrl_R && vim_is_ctrl_x_key(c)) { - edit_submode_extra = NULL; - } - - // Ignore end of Select mode mapping and mouse scroll buttons. - if (c == K_SELECT || c == K_MOUSEDOWN || c == K_MOUSEUP - || c == K_MOUSELEFT || c == K_MOUSERIGHT || c == K_EVENT - || c == K_COMMAND || c == K_LUA) { - return retval; - } - - if (ctrl_x_mode == CTRL_X_CMDLINE_CTRL_X && c != Ctrl_X) { - if (c == Ctrl_V || c == Ctrl_Q || c == Ctrl_Z || ins_compl_pum_key(c) - || !vim_is_ctrl_x_key(c)) { - // Not starting another completion mode. - ctrl_x_mode = CTRL_X_CMDLINE; - - // CTRL-X CTRL-Z should stop completion without inserting anything - if (c == Ctrl_Z) { - retval = true; - } - } else { - ctrl_x_mode = CTRL_X_CMDLINE; - - // Other CTRL-X keys first stop completion, then start another - // completion mode. - ins_compl_prep(' '); - ctrl_x_mode = CTRL_X_NOT_DEFINED_YET; - } - } - - // Set "compl_get_longest" when finding the first matches. - if (ctrl_x_mode == CTRL_X_NOT_DEFINED_YET - || (ctrl_x_mode == CTRL_X_NORMAL && !compl_started)) { - compl_get_longest = (strstr((char *)p_cot, "longest") != NULL); - compl_used_match = true; - } - - if (ctrl_x_mode == CTRL_X_NOT_DEFINED_YET) { - /* - * We have just typed CTRL-X and aren't quite sure which CTRL-X mode - * it will be yet. Now we decide. - */ - switch (c) { - case Ctrl_E: - case Ctrl_Y: - ctrl_x_mode = CTRL_X_SCROLL; - if (!(State & REPLACE_FLAG)) { - edit_submode = (char_u *)_(" (insert) Scroll (^E/^Y)"); - } else { - edit_submode = (char_u *)_(" (replace) Scroll (^E/^Y)"); - } - edit_submode_pre = NULL; - showmode(); - break; - case Ctrl_L: - ctrl_x_mode = CTRL_X_WHOLE_LINE; - break; - case Ctrl_F: - ctrl_x_mode = CTRL_X_FILES; - break; - case Ctrl_K: - ctrl_x_mode = CTRL_X_DICTIONARY; - break; - case Ctrl_R: - // Simply allow ^R to happen without affecting ^X mode - break; - case Ctrl_T: - ctrl_x_mode = CTRL_X_THESAURUS; - break; - case Ctrl_U: - ctrl_x_mode = CTRL_X_FUNCTION; - break; - case Ctrl_O: - ctrl_x_mode = CTRL_X_OMNI; - break; - case 's': - case Ctrl_S: - ctrl_x_mode = CTRL_X_SPELL; - emsg_off++; // Avoid getting the E756 error twice. - spell_back_to_badword(); - emsg_off--; - break; - case Ctrl_RSB: - ctrl_x_mode = CTRL_X_TAGS; - break; - case Ctrl_I: - case K_S_TAB: - ctrl_x_mode = CTRL_X_PATH_PATTERNS; - break; - case Ctrl_D: - ctrl_x_mode = CTRL_X_PATH_DEFINES; - break; - case Ctrl_V: - case Ctrl_Q: - ctrl_x_mode = CTRL_X_CMDLINE; - break; - case Ctrl_Z: - ctrl_x_mode = CTRL_X_NORMAL; - edit_submode = NULL; - showmode(); - retval = true; - break; - case Ctrl_P: - case Ctrl_N: - /* ^X^P means LOCAL expansion if nothing interrupted (eg we - * just started ^X mode, or there were enough ^X's to cancel - * the previous mode, say ^X^F^X^X^P or ^P^X^X^X^P, see below) - * do normal expansion when interrupting a different mode (say - * ^X^F^X^P or ^P^X^X^P, see below) - * nothing changes if interrupting mode 0, (eg, the flag - * doesn't change when going to ADDING mode -- Acevedo */ - if (!(compl_cont_status & CONT_INTRPT)) { - compl_cont_status |= CONT_LOCAL; - } else if (compl_cont_mode != 0) { - compl_cont_status &= ~CONT_LOCAL; - } - FALLTHROUGH; - default: - /* If we have typed at least 2 ^X's... for modes != 0, we set - * compl_cont_status = 0 (eg, as if we had just started ^X - * mode). - * For mode 0, we set "compl_cont_mode" to an impossible - * value, in both cases ^X^X can be used to restart the same - * mode (avoiding ADDING mode). - * Undocumented feature: In a mode != 0 ^X^P and ^X^X^P start - * 'complete' and local ^P expansions respectively. - * In mode 0 an extra ^X is needed since ^X^P goes to ADDING - * mode -- Acevedo */ - if (c == Ctrl_X) { - if (compl_cont_mode != 0) { - compl_cont_status = 0; - } else { - compl_cont_mode = CTRL_X_NOT_DEFINED_YET; - } - } - ctrl_x_mode = CTRL_X_NORMAL; - edit_submode = NULL; - showmode(); - break; - } - } else if (ctrl_x_mode != CTRL_X_NORMAL) { - // We're already in CTRL-X mode, do we stay in it? - if (!vim_is_ctrl_x_key(c)) { - if (ctrl_x_mode == CTRL_X_SCROLL) { - ctrl_x_mode = CTRL_X_NORMAL; - } else { - ctrl_x_mode = CTRL_X_FINISHED; - } - edit_submode = NULL; - } - showmode(); - } - - if (compl_started || ctrl_x_mode == CTRL_X_FINISHED) { - /* Show error message from attempted keyword completion (probably - * 'Pattern not found') until another key is hit, then go back to - * showing what mode we are in. */ - showmode(); - if ((ctrl_x_mode == CTRL_X_NORMAL - && c != Ctrl_N - && c != Ctrl_P - && c != Ctrl_R - && !ins_compl_pum_key(c)) - || ctrl_x_mode == CTRL_X_FINISHED) { - /* Get here when we have finished typing a sequence of ^N and - * ^P or other completion characters in CTRL-X mode. Free up - * memory that was used, and make sure we can redo the insert. */ - if (compl_curr_match != NULL || compl_leader != NULL || c == Ctrl_E) { - /* - * If any of the original typed text has been changed, eg when - * ignorecase is set, we must add back-spaces to the redo - * buffer. We add as few as necessary to delete just the part - * of the original text that has changed. - * When using the longest match, edited the match or used - * CTRL-E then don't use the current match. - */ - if (compl_curr_match != NULL && compl_used_match && c != Ctrl_E) { - ptr = compl_curr_match->cp_str; - } else { - ptr = NULL; - } - ins_compl_fixRedoBufForLeader(ptr); - } - - bool want_cindent = (can_cindent && cindent_on()); - - // When completing whole lines: fix indent for 'cindent'. - // Otherwise, break line if it's too long. - if (compl_cont_mode == CTRL_X_WHOLE_LINE) { - // re-indent the current line - if (want_cindent) { - do_c_expr_indent(); - want_cindent = false; // don't do it again - } - } else { - int prev_col = curwin->w_cursor.col; - - // put the cursor on the last char, for 'tw' formatting - if (prev_col > 0) { - dec_cursor(); - } - - if (!arrow_used && !ins_need_undo && c != Ctrl_E) { - insertchar(NUL, 0, -1); - } - - if (prev_col > 0 - && get_cursor_line_ptr()[curwin->w_cursor.col] != NUL) { - inc_cursor(); - } - } - - // If the popup menu is displayed pressing CTRL-Y means accepting - // the selection without inserting anything. When - // compl_enter_selects is set the Enter key does the same. - if ((c == Ctrl_Y || (compl_enter_selects - && (c == CAR || c == K_KENTER || c == NL))) - && pum_visible()) { - retval = true; - } - - // CTRL-E means completion is Ended, go back to the typed text. - // but only do this, if the Popup is still visible - if (c == Ctrl_E) { - ins_compl_delete(); - if (compl_leader != NULL) { - ins_bytes(compl_leader + ins_compl_len()); - } else if (compl_first_match != NULL) { - ins_bytes(compl_orig_text + ins_compl_len()); - } - retval = true; - } - - auto_format(false, true); - - // Trigger the CompleteDonePre event to give scripts a chance to - // act upon the completion before clearing the info, and restore - // ctrl_x_mode, so that complete_info() can be used. - ctrl_x_mode = prev_mode; - ins_apply_autocmds(EVENT_COMPLETEDONEPRE); - - ins_compl_free(); - compl_started = false; - compl_matches = 0; - if (!shortmess(SHM_COMPLETIONMENU)) { - msg_clr_cmdline(); // necessary for "noshowmode" - } - ctrl_x_mode = CTRL_X_NORMAL; - compl_enter_selects = false; - if (edit_submode != NULL) { - edit_submode = NULL; - showmode(); - } - - // Avoid the popup menu remains displayed when leaving the - // command line window. - if (c == Ctrl_C && cmdwin_type != 0) { - update_screen(0); - } - - /* - * Indent now if a key was typed that is in 'cinkeys'. - */ - if (want_cindent && in_cinkeys(KEY_COMPLETE, ' ', inindent(0))) { - do_c_expr_indent(); - } - // Trigger the CompleteDone event to give scripts a chance to act - // upon the end of completion. - ins_apply_autocmds(EVENT_COMPLETEDONE); - } - } else if (ctrl_x_mode == CTRL_X_LOCAL_MSG) { - /* Trigger the CompleteDone event to give scripts a chance to act - * upon the (possibly failed) completion. */ - ins_apply_autocmds(EVENT_COMPLETEDONE); - } - - may_trigger_modechanged(); - - /* reset continue_* if we left expansion-mode, if we stay they'll be - * (re)set properly in ins_complete() */ - if (!vim_is_ctrl_x_key(c)) { - compl_cont_status = 0; - compl_cont_mode = 0; - } - - return retval; -} - -/* - * Fix the redo buffer for the completion leader replacing some of the typed - * text. This inserts backspaces and appends the changed text. - * "ptr" is the known leader text or NUL. - */ -static void ins_compl_fixRedoBufForLeader(char_u *ptr_arg) -{ - int len; - char_u *p; - char_u *ptr = ptr_arg; - - if (ptr == NULL) { - if (compl_leader != NULL) { - ptr = compl_leader; - } else { - return; // nothing to do - } - } - if (compl_orig_text != NULL) { - p = compl_orig_text; - for (len = 0; p[len] != NUL && p[len] == ptr[len]; len++) {} - if (len > 0) { - len -= utf_head_off(p, p + len); - } - for (p += len; *p != NUL; MB_PTR_ADV(p)) { - AppendCharToRedobuff(K_BS); - } - } else { - len = 0; - } - AppendToRedobuffLit((char *)ptr + len, -1); -} - -/* - * Loops through the list of windows, loaded-buffers or non-loaded-buffers - * (depending on flag) starting from buf and looking for a non-scanned - * buffer (other than curbuf). curbuf is special, if it is called with - * buf=curbuf then it has to be the first call for a given flag/expansion. - * - * Returns the buffer to scan, if any, otherwise returns curbuf -- Acevedo - */ -static buf_T *ins_compl_next_buf(buf_T *buf, int flag) -{ - static win_T *wp = NULL; - - if (flag == 'w') { // just windows - if (buf == curbuf || wp == NULL) { // first call for this flag/expansion - wp = curwin; - } - assert(wp); - while ((wp = (wp->w_next != NULL ? wp->w_next : firstwin)) != curwin - && wp->w_buffer->b_scanned) {} - buf = wp->w_buffer; - } else { - /* 'b' (just loaded buffers), 'u' (just non-loaded buffers) or 'U' - * (unlisted buffers) - * When completing whole lines skip unloaded buffers. */ - while ((buf = (buf->b_next != NULL ? buf->b_next : firstbuf)) != curbuf - && ((flag == 'U' - ? buf->b_p_bl - : (!buf->b_p_bl - || (buf->b_ml.ml_mfp == NULL) != (flag == 'u'))) - || buf->b_scanned)) {} - } - return buf; -} - -/// Get the user-defined completion function name for completion 'type' -static char_u *get_complete_funcname(int type) -{ - switch (type) { - case CTRL_X_FUNCTION: - return curbuf->b_p_cfu; - case CTRL_X_OMNI: - return curbuf->b_p_ofu; - case CTRL_X_THESAURUS: - return *curbuf->b_p_tsrfu == NUL ? p_tsrfu : curbuf->b_p_tsrfu; - default: - return (char_u *)""; - } -} - -/// Execute user defined complete function 'completefunc' or 'omnifunc', and -/// get matches in "matches". -/// -/// @param type CTRL_X_OMNI or CTRL_X_FUNCTION -static void expand_by_function(int type, char_u *base) -{ - list_T *matchlist = NULL; - dict_T *matchdict = NULL; - char_u *funcname; - pos_T pos; - typval_T rettv; - const int save_State = State; - - assert(curbuf != NULL); - funcname = get_complete_funcname(type); - if (*funcname == NUL) { - return; - } - - // Call 'completefunc' to obtain the list of matches. - typval_T args[3]; - args[0].v_type = VAR_NUMBER; - args[1].v_type = VAR_STRING; - args[2].v_type = VAR_UNKNOWN; - args[0].vval.v_number = 0; - args[1].vval.v_string = base != NULL ? (char *)base : ""; - - pos = curwin->w_cursor; - // Lock the text to avoid weird things from happening. Also disallow - // switching to another window, it should not be needed and may end up in - // Insert mode in another buffer. - textlock++; - - // Call a function, which returns a list or dict. - if (call_vim_function((char *)funcname, 2, args, &rettv) == OK) { - switch (rettv.v_type) { - case VAR_LIST: - matchlist = rettv.vval.v_list; - break; - case VAR_DICT: - matchdict = rettv.vval.v_dict; - break; - case VAR_SPECIAL: - FALLTHROUGH; - default: - // TODO(brammool): Give error message? - tv_clear(&rettv); - break; - } - } - textlock--; - - curwin->w_cursor = pos; // restore the cursor position - validate_cursor(); - if (!equalpos(curwin->w_cursor, pos)) { - emsg(_(e_compldel)); - goto theend; - } - - if (matchlist != NULL) { - ins_compl_add_list(matchlist); - } else if (matchdict != NULL) { - ins_compl_add_dict(matchdict); - } - -theend: - // Restore State, it might have been changed. - State = save_State; - - if (matchdict != NULL) { - tv_dict_unref(matchdict); - } - if (matchlist != NULL) { - tv_list_unref(matchlist); - } -} - -/* - * Add completions from a list. - */ -static void ins_compl_add_list(list_T *const list) -{ - Direction dir = compl_direction; - - // Go through the List with matches and add each of them. - TV_LIST_ITER(list, li, { - if (ins_compl_add_tv(TV_LIST_ITEM_TV(li), dir, true) == OK) { - // If dir was BACKWARD then honor it just once. - dir = FORWARD; - } else if (did_emsg) { - break; - } - }); -} - -/* - * Add completions from a dict. - */ -static void ins_compl_add_dict(dict_T *dict) -{ - dictitem_T *di_refresh; - dictitem_T *di_words; - - // Check for optional "refresh" item. - compl_opt_refresh_always = false; - di_refresh = tv_dict_find(dict, S_LEN("refresh")); - if (di_refresh != NULL && di_refresh->di_tv.v_type == VAR_STRING) { - const char *v = (const char *)di_refresh->di_tv.vval.v_string; - - if (v != NULL && strcmp(v, "always") == 0) { - compl_opt_refresh_always = true; - } - } - - // Add completions from a "words" list. - di_words = tv_dict_find(dict, S_LEN("words")); - if (di_words != NULL && di_words->di_tv.v_type == VAR_LIST) { - ins_compl_add_list(di_words->di_tv.vval.v_list); - } -} - -/// Add a match to the list of matches from VimL object -/// -/// @param[in] tv Object to get matches from. -/// @param[in] dir Completion direction. -/// @param[in] fast use fast_breakcheck() instead of os_breakcheck(). -/// -/// @return NOTDONE if the given string is already in the list of completions, -/// otherwise it is added to the list and OK is returned. FAIL will be -/// returned in case of error. -int ins_compl_add_tv(typval_T *const tv, const Direction dir, bool fast) - FUNC_ATTR_NONNULL_ALL -{ - const char *word; - bool dup = false; - bool empty = false; - int flags = fast ? CP_FAST : 0; - char *(cptext[CPT_COUNT]); - typval_T user_data; - - user_data.v_type = VAR_UNKNOWN; - if (tv->v_type == VAR_DICT && tv->vval.v_dict != NULL) { - word = tv_dict_get_string(tv->vval.v_dict, "word", false); - cptext[CPT_ABBR] = tv_dict_get_string(tv->vval.v_dict, "abbr", true); - cptext[CPT_MENU] = tv_dict_get_string(tv->vval.v_dict, "menu", true); - cptext[CPT_KIND] = tv_dict_get_string(tv->vval.v_dict, "kind", true); - cptext[CPT_INFO] = tv_dict_get_string(tv->vval.v_dict, "info", true); - tv_dict_get_tv(tv->vval.v_dict, "user_data", &user_data); - - if (tv_dict_get_number(tv->vval.v_dict, "icase")) { - flags |= CP_ICASE; - } - dup = (bool)tv_dict_get_number(tv->vval.v_dict, "dup"); - empty = (bool)tv_dict_get_number(tv->vval.v_dict, "empty"); - if (tv_dict_get_string(tv->vval.v_dict, "equal", false) != NULL - && tv_dict_get_number(tv->vval.v_dict, "equal")) { - flags |= CP_EQUAL; - } - } else { - word = tv_get_string_chk(tv); - memset(cptext, 0, sizeof(cptext)); - } - if (word == NULL || (!empty && *word == NUL)) { - for (size_t i = 0; i < CPT_COUNT; i++) { - xfree(cptext[i]); - } - return FAIL; - } - return ins_compl_add((char_u *)word, -1, NULL, - (char_u **)cptext, true, &user_data, dir, flags, dup); -} - -/// Returns true when using a user-defined function for thesaurus completion. -static bool thesaurus_func_complete(int type) -{ - return type == CTRL_X_THESAURUS - && (*curbuf->b_p_tsrfu != NUL || *p_tsrfu != NUL); -} - -// Get the next expansion(s), using "compl_pattern". -// The search starts at position "ini" in curbuf and in the direction -// compl_direction. -// When "compl_started" is false start at that position, otherwise continue -// where we stopped searching before. -// This may return before finding all the matches. -// Return the total number of matches or -1 if still unknown -- Acevedo -static int ins_compl_get_exp(pos_T *ini) -{ - static pos_T first_match_pos; - static pos_T last_match_pos; - static char_u *e_cpt = (char_u *)""; // curr. entry in 'complete' - static bool found_all = false; // Found all matches of a - // certain type. - static buf_T *ins_buf = NULL; // buffer being scanned - - pos_T *pos; - char_u **matches; - int save_p_scs; - bool save_p_ws; - int save_p_ic; - int i; - int num_matches; - int len; - int found_new_match; - int type = ctrl_x_mode; - char_u *ptr; - char_u *dict = NULL; - int dict_f = 0; - bool set_match_pos; - pos_T prev_pos = { 0, 0, 0 }; - int l_ctrl_x_mode = ctrl_x_mode; - - assert(curbuf != NULL); - - if (!compl_started) { - FOR_ALL_BUFFERS(buf) { - buf->b_scanned = false; - } - found_all = false; - ins_buf = curbuf; - e_cpt = (compl_cont_status & CONT_LOCAL) - ? (char_u *)"." : curbuf->b_p_cpt; - last_match_pos = first_match_pos = *ini; - } else if (ins_buf != curbuf && !buf_valid(ins_buf)) { - ins_buf = curbuf; // In case the buffer was wiped out. - } - - compl_old_match = compl_curr_match; // remember the last current match - pos = (compl_direction == FORWARD) ? &last_match_pos : &first_match_pos; - - // For ^N/^P loop over all the flags/windows/buffers in 'complete' - for (;;) { - found_new_match = FAIL; - set_match_pos = false; - - assert(l_ctrl_x_mode == ctrl_x_mode); - - // For ^N/^P pick a new entry from e_cpt if compl_started is off, - // or if found_all says this entry is done. For ^X^L only use the - // entries from 'complete' that look in loaded buffers. - if ((l_ctrl_x_mode == CTRL_X_NORMAL - || CTRL_X_MODE_LINE_OR_EVAL(l_ctrl_x_mode)) - && (!compl_started || found_all)) { - found_all = false; - while (*e_cpt == ',' || *e_cpt == ' ') { - e_cpt++; - } - if (*e_cpt == '.' && !curbuf->b_scanned) { - ins_buf = curbuf; - first_match_pos = *ini; - // Move the cursor back one character so that ^N can match the - // word immediately after the cursor. - if (ctrl_x_mode == CTRL_X_NORMAL && dec(&first_match_pos) < 0) { - // Move the cursor to after the last character in the - // buffer, so that word at start of buffer is found - // correctly. - first_match_pos.lnum = ins_buf->b_ml.ml_line_count; - first_match_pos.col = (colnr_T)STRLEN(ml_get(first_match_pos.lnum)); - } - last_match_pos = first_match_pos; - type = 0; - - // Remember the first match so that the loop stops when we - // wrap and come back there a second time. - set_match_pos = true; - } else if (vim_strchr("buwU", *e_cpt) != NULL - && (ins_buf = ins_compl_next_buf(ins_buf, *e_cpt)) != curbuf) { - // Scan a buffer, but not the current one. - if (ins_buf->b_ml.ml_mfp != NULL) { // loaded buffer - compl_started = true; - first_match_pos.col = last_match_pos.col = 0; - first_match_pos.lnum = ins_buf->b_ml.ml_line_count + 1; - last_match_pos.lnum = 0; - type = 0; - } else { // unloaded buffer, scan like dictionary - found_all = true; - if (ins_buf->b_fname == NULL) { - continue; - } - type = CTRL_X_DICTIONARY; - dict = (char_u *)ins_buf->b_fname; - dict_f = DICT_EXACT; - } - msg_hist_off = true; // reset in msg_trunc_attr() - vim_snprintf((char *)IObuff, IOSIZE, _("Scanning: %s"), - ins_buf->b_fname == NULL - ? buf_spname(ins_buf) - : ins_buf->b_sfname == NULL - ? ins_buf->b_fname - : ins_buf->b_sfname); - (void)msg_trunc_attr((char *)IObuff, true, HL_ATTR(HLF_R)); - } else if (*e_cpt == NUL) { - break; - } else { - if (CTRL_X_MODE_LINE_OR_EVAL(l_ctrl_x_mode)) { - type = -1; - } else if (*e_cpt == 'k' || *e_cpt == 's') { - if (*e_cpt == 'k') { - type = CTRL_X_DICTIONARY; - } else { - type = CTRL_X_THESAURUS; - } - if (*++e_cpt != ',' && *e_cpt != NUL) { - dict = e_cpt; - dict_f = DICT_FIRST; - } - } else if (*e_cpt == 'i') { - type = CTRL_X_PATH_PATTERNS; - } else if (*e_cpt == 'd') { - type = CTRL_X_PATH_DEFINES; - } else if (*e_cpt == ']' || *e_cpt == 't') { - msg_hist_off = true; // reset in msg_trunc_attr() - type = CTRL_X_TAGS; - vim_snprintf((char *)IObuff, IOSIZE, "%s", _("Scanning tags.")); - (void)msg_trunc_attr((char *)IObuff, true, HL_ATTR(HLF_R)); - } else { - type = -1; - } - - // in any case e_cpt is advanced to the next entry - (void)copy_option_part((char **)&e_cpt, (char *)IObuff, IOSIZE, ","); - - found_all = true; - if (type == -1) { - continue; - } - } - } - - // If complete() was called then compl_pattern has been reset. - // The following won't work then, bail out. - if (compl_pattern == NULL) { - break; - } - - switch (type) { - case -1: - break; - case CTRL_X_PATH_PATTERNS: - case CTRL_X_PATH_DEFINES: - find_pattern_in_path(compl_pattern, compl_direction, - STRLEN(compl_pattern), FALSE, FALSE, - ((type == CTRL_X_PATH_DEFINES - && !(compl_cont_status & CONT_SOL)) - ? FIND_DEFINE - : FIND_ANY), - 1L, ACTION_EXPAND, 1, MAXLNUM); - break; - - case CTRL_X_DICTIONARY: - case CTRL_X_THESAURUS: - if (thesaurus_func_complete(type)) { - expand_by_function(type, compl_pattern); - } else { - ins_compl_dictionaries(dict != NULL ? dict - : (type == CTRL_X_THESAURUS - ? (*curbuf->b_p_tsr == NUL ? p_tsr : curbuf->b_p_tsr) - : (*curbuf->b_p_dict == - NUL ? p_dict : curbuf->b_p_dict)), - compl_pattern, - dict != NULL ? dict_f : 0, type == CTRL_X_THESAURUS); - } - dict = NULL; - break; - - case CTRL_X_TAGS: - // set p_ic according to p_ic, p_scs and pat for find_tags(). - save_p_ic = p_ic; - p_ic = ignorecase(compl_pattern); - - // Find up to TAG_MANY matches. Avoids that an enormous number - // of matches is found when compl_pattern is empty - g_tag_at_cursor = true; - if (find_tags(compl_pattern, &num_matches, &matches, - TAG_REGEXP | TAG_NAMES | TAG_NOIC | TAG_INS_COMP - | (l_ctrl_x_mode != CTRL_X_NORMAL ? TAG_VERBOSE : 0), - TAG_MANY, (char_u *)curbuf->b_ffname) == OK && num_matches > 0) { - ins_compl_add_matches(num_matches, matches, p_ic); - } - g_tag_at_cursor = false; - p_ic = save_p_ic; - break; - - case CTRL_X_FILES: - if (expand_wildcards(1, &compl_pattern, &num_matches, &matches, - EW_FILE|EW_DIR|EW_ADDSLASH|EW_SILENT) == OK) { - // May change home directory back to "~". - tilde_replace(compl_pattern, num_matches, matches); -#ifdef BACKSLASH_IN_FILENAME - if (curbuf->b_p_csl[0] != NUL) { - for (int i = 0; i < num_matches; i++) { - char_u *ptr = matches[i]; - while (*ptr != NUL) { - if (curbuf->b_p_csl[0] == 's' && *ptr == '\\') { - *ptr = '/'; - } else if (curbuf->b_p_csl[0] == 'b' && *ptr == '/') { - *ptr = '\\'; - } - ptr += utfc_ptr2len(ptr); - } - } - } -#endif - ins_compl_add_matches(num_matches, matches, p_fic || p_wic); - } - break; - - case CTRL_X_CMDLINE: - case CTRL_X_CMDLINE_CTRL_X: - if (expand_cmdline(&compl_xp, compl_pattern, - (int)STRLEN(compl_pattern), - &num_matches, &matches) == EXPAND_OK) { - ins_compl_add_matches(num_matches, matches, false); - } - break; - - case CTRL_X_FUNCTION: - case CTRL_X_OMNI: - expand_by_function(type, compl_pattern); - break; - - case CTRL_X_SPELL: - num_matches = expand_spelling(first_match_pos.lnum, - compl_pattern, &matches); - if (num_matches > 0) { - ins_compl_add_matches(num_matches, matches, p_ic); - } - break; - - default: // normal ^P/^N and ^X^L - // If 'infercase' is set, don't use 'smartcase' here - save_p_scs = p_scs; - assert(ins_buf); - if (ins_buf->b_p_inf) { - p_scs = FALSE; - } - - // Buffers other than curbuf are scanned from the beginning or the - // end but never from the middle, thus setting nowrapscan in this - // buffers is a good idea, on the other hand, we always set - // wrapscan for curbuf to avoid missing matches -- Acevedo,Webb - save_p_ws = p_ws; - if (ins_buf != curbuf) { - p_ws = false; - } else if (*e_cpt == '.') { - p_ws = true; - } - bool looped_around = false; - for (;;) { - bool cont_s_ipos = false; - - msg_silent++; // Don't want messages for wrapscan. - // CTRL_X_MODE_LINE_OR_EVAL(l_ctrl_x_mode) || word-wise search that - // has added a word that was at the beginning of the line. - if (CTRL_X_MODE_LINE_OR_EVAL(l_ctrl_x_mode) - || (compl_cont_status & CONT_SOL)) { - found_new_match = search_for_exact_line(ins_buf, pos, - compl_direction, - compl_pattern); - } else { - found_new_match = searchit(NULL, ins_buf, pos, NULL, - compl_direction, - compl_pattern, 1L, - SEARCH_KEEP + SEARCH_NFMSG, - RE_LAST, NULL); - } - msg_silent--; - if (!compl_started || set_match_pos) { - // set "compl_started" even on fail - compl_started = true; - first_match_pos = *pos; - last_match_pos = *pos; - set_match_pos = false; - } else if (first_match_pos.lnum == last_match_pos.lnum - && first_match_pos.col == last_match_pos.col) { - found_new_match = FAIL; - } else if ((compl_direction == FORWARD) - && (prev_pos.lnum > pos->lnum - || (prev_pos.lnum == pos->lnum - && prev_pos.col >= pos->col))) { - if (looped_around) { - found_new_match = FAIL; - } else { - looped_around = true; - } - } else if ((compl_direction != FORWARD) - && (prev_pos.lnum < pos->lnum - || (prev_pos.lnum == pos->lnum - && prev_pos.col <= pos->col))) { - if (looped_around) { - found_new_match = FAIL; - } else { - looped_around = true; - } - } - prev_pos = *pos; - if (found_new_match == FAIL) { - if (ins_buf == curbuf) { - found_all = true; - } - break; - } - - // when ADDING, the text before the cursor matches, skip it - if ((compl_cont_status & CONT_ADDING) && ins_buf == curbuf - && ini->lnum == pos->lnum - && ini->col == pos->col) { - continue; - } - ptr = ml_get_buf(ins_buf, pos->lnum, false) + pos->col; - if (CTRL_X_MODE_LINE_OR_EVAL(l_ctrl_x_mode)) { - if (compl_cont_status & CONT_ADDING) { - if (pos->lnum >= ins_buf->b_ml.ml_line_count) { - continue; - } - ptr = ml_get_buf(ins_buf, pos->lnum + 1, false); - if (!p_paste) { - ptr = (char_u *)skipwhite((char *)ptr); - } - } - len = (int)STRLEN(ptr); - } else { - char_u *tmp_ptr = ptr; - - if (compl_cont_status & CONT_ADDING) { - tmp_ptr += compl_length; - // Skip if already inside a word. - if (vim_iswordp(tmp_ptr)) { - continue; - } - // Find start of next word. - tmp_ptr = find_word_start(tmp_ptr); - } - // Find end of this word. - tmp_ptr = find_word_end(tmp_ptr); - len = (int)(tmp_ptr - ptr); - - if ((compl_cont_status & CONT_ADDING) - && len == compl_length) { - if (pos->lnum < ins_buf->b_ml.ml_line_count) { - // Try next line, if any. the new word will be "join" as if the - // normal command "J" was used. IOSIZE is always greater than - // compl_length, so the next STRNCPY always works -- Acevedo - STRNCPY(IObuff, ptr, len); - ptr = ml_get_buf(ins_buf, pos->lnum + 1, false); - tmp_ptr = ptr = (char_u *)skipwhite((char *)ptr); - // Find start of next word. - tmp_ptr = find_word_start(tmp_ptr); - // Find end of next word. - tmp_ptr = find_word_end(tmp_ptr); - if (tmp_ptr > ptr) { - if (*ptr != ')' && IObuff[len - 1] != TAB) { - if (IObuff[len - 1] != ' ') { - IObuff[len++] = ' '; - } - // IObuf =~ "\k.* ", thus len >= 2 - if (p_js - && (IObuff[len - 2] == '.' - || IObuff[len - 2] == '?' - || IObuff[len - 2] == '!')) { - IObuff[len++] = ' '; - } - } - // copy as much as possible of the new word - if (tmp_ptr - ptr >= IOSIZE - len) { - tmp_ptr = ptr + IOSIZE - len - 1; - } - STRLCPY(IObuff + len, ptr, IOSIZE - len); - len += (int)(tmp_ptr - ptr); - cont_s_ipos = true; - } - IObuff[len] = NUL; - ptr = IObuff; - } - if (len == compl_length) { - continue; - } - } - } - if (ins_compl_add_infercase(ptr, len, p_ic, - ins_buf == curbuf ? NULL : (char_u *)ins_buf->b_sfname, - 0, cont_s_ipos) != NOTDONE) { - found_new_match = OK; - break; - } - } - p_scs = save_p_scs; - p_ws = save_p_ws; - } - - // check if compl_curr_match has changed, (e.g. other type of - // expansion added something) - if (type != 0 && compl_curr_match != compl_old_match) { - found_new_match = OK; - } - - // break the loop for specialized modes (use 'complete' just for the - // generic l_ctrl_x_mode == CTRL_X_NORMAL) or when we've found a new match - if ((l_ctrl_x_mode != CTRL_X_NORMAL - && !CTRL_X_MODE_LINE_OR_EVAL(l_ctrl_x_mode)) - || found_new_match != FAIL) { - if (got_int) { - break; - } - // Fill the popup menu as soon as possible. - if (type != -1) { - ins_compl_check_keys(0, false); - } - - if ((l_ctrl_x_mode != CTRL_X_NORMAL - && !CTRL_X_MODE_LINE_OR_EVAL(l_ctrl_x_mode)) - || compl_interrupted) { - break; - } - compl_started = true; - } else { - // Mark a buffer scanned when it has been scanned completely - if (type == 0 || type == CTRL_X_PATH_PATTERNS) { - assert(ins_buf); - ins_buf->b_scanned = true; - } - - compl_started = false; - } - } - compl_started = true; - - if ((l_ctrl_x_mode == CTRL_X_NORMAL - || CTRL_X_MODE_LINE_OR_EVAL(l_ctrl_x_mode)) - && *e_cpt == NUL) { // Got to end of 'complete' - found_new_match = FAIL; - } - - i = -1; // total of matches, unknown - if (found_new_match == FAIL - || (l_ctrl_x_mode != CTRL_X_NORMAL - && !CTRL_X_MODE_LINE_OR_EVAL(l_ctrl_x_mode))) { - i = ins_compl_make_cyclic(); - } - - if (compl_old_match != NULL) { - // If several matches were added (FORWARD) or the search failed and has - // just been made cyclic then we have to move compl_curr_match to the - // next or previous entry (if any) -- Acevedo - compl_curr_match = compl_direction == FORWARD - ? compl_old_match->cp_next - : compl_old_match->cp_prev; - if (compl_curr_match == NULL) { - compl_curr_match = compl_old_match; - } - } - may_trigger_modechanged(); - - return i; -} - -// Delete the old text being completed. -static void ins_compl_delete(void) -{ - int col; - - // In insert mode: Delete the typed part. - // In replace mode: Put the old characters back, if any. - col = compl_col + (compl_cont_status & CONT_ADDING ? compl_length : 0); - if ((int)curwin->w_cursor.col > col) { - if (stop_arrow() == FAIL) { - return; - } - backspace_until_column(col); - } - - // TODO(vim): is this sufficient for redrawing? Redrawing everything - // causes flicker, thus we can't do that. - changed_cline_bef_curs(); - // clear v:completed_item - set_vim_var_dict(VV_COMPLETED_ITEM, tv_dict_alloc_lock(VAR_FIXED)); -} - -// Insert the new text being completed. -// "in_compl_func" is TRUE when called from complete_check(). -static void ins_compl_insert(int in_compl_func) -{ - ins_bytes(compl_shown_match->cp_str + ins_compl_len()); - compl_used_match = !(compl_shown_match->cp_flags & CP_ORIGINAL_TEXT); - - dict_T *dict = ins_compl_dict_alloc(compl_shown_match); - set_vim_var_dict(VV_COMPLETED_ITEM, dict); - if (!in_compl_func) { - compl_curr_match = compl_shown_match; - } -} - -// Convert to complete item dict -static dict_T *ins_compl_dict_alloc(compl_T *match) -{ - // { word, abbr, menu, kind, info } - dict_T *dict = tv_dict_alloc_lock(VAR_FIXED); - tv_dict_add_str(dict, S_LEN("word"), EMPTY_IF_NULL(match->cp_str)); - tv_dict_add_str(dict, S_LEN("abbr"), EMPTY_IF_NULL(match->cp_text[CPT_ABBR])); - tv_dict_add_str(dict, S_LEN("menu"), EMPTY_IF_NULL(match->cp_text[CPT_MENU])); - tv_dict_add_str(dict, S_LEN("kind"), EMPTY_IF_NULL(match->cp_text[CPT_KIND])); - tv_dict_add_str(dict, S_LEN("info"), EMPTY_IF_NULL(match->cp_text[CPT_INFO])); - if (match->cp_user_data.v_type == VAR_UNKNOWN) { - tv_dict_add_str(dict, S_LEN("user_data"), ""); - } else { - tv_dict_add_tv(dict, S_LEN("user_data"), &match->cp_user_data); - } - return dict; -} - -/// Fill in the next completion in the current direction. -/// If "allow_get_expansion" is TRUE, then we may call ins_compl_get_exp() to -/// get more completions. If it is FALSE, then we just do nothing when there -/// are no more completions in a given direction. The latter case is used when -/// we are still in the middle of finding completions, to allow browsing -/// through the ones found so far. -/// @return the total number of matches, or -1 if still unknown -- webb. -/// -/// compl_curr_match is currently being used by ins_compl_get_exp(), so we use -/// compl_shown_match here. -/// -/// Note that this function may be called recursively once only. First with -/// "allow_get_expansion" TRUE, which calls ins_compl_get_exp(), which in turn -/// calls this function with "allow_get_expansion" FALSE. -/// -/// @param count Repeat completion this many times; should be at least 1 -/// @param insert_match Insert the newly selected match -/// @param in_compl_func Called from complete_check() -static int ins_compl_next(int allow_get_expansion, int count, int insert_match, int in_compl_func) -{ - int num_matches = -1; - int todo = count; - compl_T *found_compl = NULL; - bool found_end = false; - const bool started = compl_started; - - /* When user complete function return -1 for findstart which is next - * time of 'always', compl_shown_match become NULL. */ - if (compl_shown_match == NULL) { - return -1; - } - - if (compl_leader != NULL - && (compl_shown_match->cp_flags & CP_ORIGINAL_TEXT) == 0) { - // Set "compl_shown_match" to the actually shown match, it may differ - // when "compl_leader" is used to omit some of the matches. - while (!ins_compl_equal(compl_shown_match, - compl_leader, STRLEN(compl_leader)) - && compl_shown_match->cp_next != NULL - && compl_shown_match->cp_next != compl_first_match) { - compl_shown_match = compl_shown_match->cp_next; - } - - /* If we didn't find it searching forward, and compl_shows_dir is - * backward, find the last match. */ - if (compl_shows_dir == BACKWARD - && !ins_compl_equal(compl_shown_match, compl_leader, STRLEN(compl_leader)) - && (compl_shown_match->cp_next == NULL - || compl_shown_match->cp_next == compl_first_match)) { - while (!ins_compl_equal(compl_shown_match, compl_leader, STRLEN(compl_leader)) - && compl_shown_match->cp_prev != NULL - && compl_shown_match->cp_prev != compl_first_match) { - compl_shown_match = compl_shown_match->cp_prev; - } - } - } - - if (allow_get_expansion && insert_match - && (!(compl_get_longest || compl_restarting) || compl_used_match)) { - // Delete old text to be replaced - ins_compl_delete(); - } - - // When finding the longest common text we stick at the original text, - // don't let CTRL-N or CTRL-P move to the first match. - bool advance = count != 1 || !allow_get_expansion || !compl_get_longest; - - // When restarting the search don't insert the first match either. - if (compl_restarting) { - advance = false; - compl_restarting = false; - } - - /* Repeat this for when <PageUp> or <PageDown> is typed. But don't wrap - * around. */ - while (--todo >= 0) { - if (compl_shows_dir == FORWARD && compl_shown_match->cp_next != NULL) { - compl_shown_match = compl_shown_match->cp_next; - found_end = (compl_first_match != NULL - && (compl_shown_match->cp_next == compl_first_match - || compl_shown_match == compl_first_match)); - } else if (compl_shows_dir == BACKWARD - && compl_shown_match->cp_prev != NULL) { - found_end = (compl_shown_match == compl_first_match); - compl_shown_match = compl_shown_match->cp_prev; - found_end |= (compl_shown_match == compl_first_match); - } else { - if (!allow_get_expansion) { - if (advance) { - if (compl_shows_dir == BACKWARD) { - compl_pending -= todo + 1; - } else { - compl_pending += todo + 1; - } - } - return -1; - } - - if (!compl_no_select && advance) { - if (compl_shows_dir == BACKWARD) { - --compl_pending; - } else { - ++compl_pending; - } - } - - // Find matches. - num_matches = ins_compl_get_exp(&compl_startpos); - - // handle any pending completions - while (compl_pending != 0 && compl_direction == compl_shows_dir - && advance) { - if (compl_pending > 0 && compl_shown_match->cp_next != NULL) { - compl_shown_match = compl_shown_match->cp_next; - --compl_pending; - } - if (compl_pending < 0 && compl_shown_match->cp_prev != NULL) { - compl_shown_match = compl_shown_match->cp_prev; - ++compl_pending; - } else { - break; - } - } - found_end = false; - } - if ((compl_shown_match->cp_flags & CP_ORIGINAL_TEXT) == 0 - && compl_leader != NULL - && !ins_compl_equal(compl_shown_match, - compl_leader, STRLEN(compl_leader))) { - todo++; - } else { - // Remember a matching item. - found_compl = compl_shown_match; - } - - // Stop at the end of the list when we found a usable match. - if (found_end) { - if (found_compl != NULL) { - compl_shown_match = found_compl; - break; - } - todo = 1; // use first usable match after wrapping around - } - } - - // Insert the text of the new completion, or the compl_leader. - if (compl_no_insert && !started) { - ins_bytes(compl_orig_text + ins_compl_len()); - compl_used_match = false; - } else if (insert_match) { - if (!compl_get_longest || compl_used_match) { - ins_compl_insert(in_compl_func); - } else { - ins_bytes(compl_leader + ins_compl_len()); - } - } else { - compl_used_match = false; - } - - if (!allow_get_expansion) { - // redraw to show the user what was inserted - update_screen(0); - - // display the updated popup menu - ins_compl_show_pum(); - - // Delete old text to be replaced, since we're still searching and - // don't want to match ourselves! - ins_compl_delete(); - } - - /* Enter will select a match when the match wasn't inserted and the popup - * menu is visible. */ - if (compl_no_insert && !started) { - compl_enter_selects = TRUE; - } else { - compl_enter_selects = !insert_match && compl_match_array != NULL; - } - - /* - * Show the file name for the match (if any) - * Truncate the file name to avoid a wait for return. - */ - if (compl_shown_match->cp_fname != NULL) { - char *lead = _("match in file"); - int space = sc_col - vim_strsize(lead) - 2; - char *s; - char *e; - - if (space > 0) { - // We need the tail that fits. With double-byte encoding going - // back from the end is very slow, thus go from the start and keep - // the text that fits in "space" between "s" and "e". - for (s = e = (char *)compl_shown_match->cp_fname; *e != NUL; MB_PTR_ADV(e)) { - space -= ptr2cells(e); - while (space < 0) { - space += ptr2cells(s); - MB_PTR_ADV(s); - } - } - msg_hist_off = true; - vim_snprintf((char *)IObuff, IOSIZE, "%s %s%s", lead, - (char_u *)s > compl_shown_match->cp_fname ? "<" : "", s); - msg((char *)IObuff); - msg_hist_off = false; - redraw_cmdline = false; // don't overwrite! - } - } - - return num_matches; -} - -void pum_ext_select_item(int item, bool insert, bool finish) -{ - if (!pum_visible() || item < -1 || item >= compl_match_arraysize) { - return; - } - pum_want.active = true; - pum_want.item = item; - pum_want.insert = insert; - pum_want.finish = finish; -} - -// Call this while finding completions, to check whether the user has hit a key -// that should change the currently displayed completion, or exit completion -// mode. Also, when compl_pending is not zero, show a completion as soon as -// possible. -- webb -// "frequency" specifies out of how many calls we actually check. -// "in_compl_func" is TRUE when called from complete_check(), don't set -// compl_curr_match. -void ins_compl_check_keys(int frequency, int in_compl_func) -{ - static int count = 0; - - // Don't check when reading keys from a script, :normal or feedkeys(). - // That would break the test scripts. But do check for keys when called - // from complete_check(). - if (!in_compl_func && (using_script() || ex_normal_busy)) { - return; - } - - // Only do this at regular intervals - if (++count < frequency) { - return; - } - count = 0; - - /* Check for a typed key. Do use mappings, otherwise vim_is_ctrl_x_key() - * can't do its work correctly. */ - int c = vpeekc_any(); - if (c != NUL) { - if (vim_is_ctrl_x_key(c) && c != Ctrl_X && c != Ctrl_R) { - c = safe_vgetc(); // Eat the character - compl_shows_dir = ins_compl_key2dir(c); - (void)ins_compl_next(false, ins_compl_key2count(c), - c != K_UP && c != K_DOWN, in_compl_func); - } else { - /* Need to get the character to have KeyTyped set. We'll put it - * back with vungetc() below. But skip K_IGNORE. */ - c = safe_vgetc(); - if (c != K_IGNORE) { - /* Don't interrupt completion when the character wasn't typed, - * e.g., when doing @q to replay keys. */ - if (c != Ctrl_R && KeyTyped) { - compl_interrupted = TRUE; - } - - vungetc(c); - } - } - } - if (compl_pending != 0 && !got_int && !compl_no_insert) { - int todo = compl_pending > 0 ? compl_pending : -compl_pending; - - compl_pending = 0; - (void)ins_compl_next(false, todo, true, in_compl_func); - } -} - -/* - * Decide the direction of Insert mode complete from the key typed. - * Returns BACKWARD or FORWARD. - */ -static int ins_compl_key2dir(int c) -{ - if (c == K_EVENT || c == K_COMMAND || c == K_LUA) { - return pum_want.item < pum_selected_item ? BACKWARD : FORWARD; - } - if (c == Ctrl_P || c == Ctrl_L - || c == K_PAGEUP || c == K_KPAGEUP - || c == K_S_UP || c == K_UP) { - return BACKWARD; - } - return FORWARD; -} - -/// Check that "c" is a valid completion key only while the popup menu is shown -/// -/// @param c character to check -static bool ins_compl_pum_key(int c) - FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT -{ - return pum_visible() && (c == K_PAGEUP || c == K_KPAGEUP || c == K_S_UP - || c == K_PAGEDOWN || c == K_KPAGEDOWN - || c == K_S_DOWN || c == K_UP || c == K_DOWN); -} - -/* - * Decide the number of completions to move forward. - * Returns 1 for most keys, height of the popup menu for page-up/down keys. - */ -static int ins_compl_key2count(int c) -{ - int h; - - if (c == K_EVENT || c == K_COMMAND || c == K_LUA) { - int offset = pum_want.item - pum_selected_item; - return abs(offset); - } - - if (ins_compl_pum_key(c) && c != K_UP && c != K_DOWN) { - h = pum_get_height(); - if (h > 3) { - h -= 2; // keep some context - } - return h; - } - return 1; -} - -/// Check that completion with "c" should insert the match, false if only -/// to change the currently selected completion. -/// -/// @param c character to check -static bool ins_compl_use_match(int c) - FUNC_ATTR_CONST FUNC_ATTR_WARN_UNUSED_RESULT -{ - switch (c) { - case K_UP: - case K_DOWN: - case K_PAGEDOWN: - case K_KPAGEDOWN: - case K_S_DOWN: - case K_PAGEUP: - case K_KPAGEUP: - case K_S_UP: - return false; - case K_EVENT: - case K_COMMAND: - case K_LUA: - return pum_want.active && pum_want.insert; - } - return true; -} - -/* - * Do Insert mode completion. - * Called when character "c" was typed, which has a meaning for completion. - * Returns OK if completion was done, FAIL if something failed. - */ -static int ins_complete(int c, bool enable_pum) -{ - char_u *line; - int startcol = 0; // column where searched text starts - colnr_T curs_col; // cursor column - int n; - int save_w_wrow; - int save_w_leftcol; - int insert_match; - const bool save_did_ai = did_ai; - int flags = CP_ORIGINAL_TEXT; - - compl_direction = ins_compl_key2dir(c); - insert_match = ins_compl_use_match(c); - - if (!compl_started) { - // First time we hit ^N or ^P (in a row, I mean) - - did_ai = false; - did_si = false; - can_si = false; - can_si_back = false; - if (stop_arrow() == FAIL) { - return FAIL; - } - - line = ml_get(curwin->w_cursor.lnum); - curs_col = curwin->w_cursor.col; - compl_pending = 0; - - /* If this same ctrl_x_mode has been interrupted use the text from - * "compl_startpos" to the cursor as a pattern to add a new word - * instead of expand the one before the cursor, in word-wise if - * "compl_startpos" is not in the same line as the cursor then fix it - * (the line has been split because it was longer than 'tw'). if SOL - * is set then skip the previous pattern, a word at the beginning of - * the line has been inserted, we'll look for that -- Acevedo. */ - if ((compl_cont_status & CONT_INTRPT) == CONT_INTRPT - && compl_cont_mode == ctrl_x_mode) { - /* - * it is a continued search - */ - compl_cont_status &= ~CONT_INTRPT; // remove INTRPT - if (ctrl_x_mode == CTRL_X_NORMAL - || ctrl_x_mode == CTRL_X_PATH_PATTERNS - || ctrl_x_mode == CTRL_X_PATH_DEFINES) { - if (compl_startpos.lnum != curwin->w_cursor.lnum) { - // line (probably) wrapped, set compl_startpos to the - // first non_blank in the line, if it is not a wordchar - // include it to get a better pattern, but then we don't - // want the "\\<" prefix, check it below. - compl_col = (colnr_T)getwhitecols(line); - compl_startpos.col = compl_col; - compl_startpos.lnum = curwin->w_cursor.lnum; - compl_cont_status &= ~CONT_SOL; // clear SOL if present - } else { - /* S_IPOS was set when we inserted a word that was at the - * beginning of the line, which means that we'll go to SOL - * mode but first we need to redefine compl_startpos */ - if (compl_cont_status & CONT_S_IPOS) { - compl_cont_status |= CONT_SOL; - compl_startpos.col = (colnr_T)((char_u *)skipwhite((char *)line - + compl_length - + compl_startpos.col) - line); - } - compl_col = compl_startpos.col; - } - compl_length = curwin->w_cursor.col - (int)compl_col; - /* IObuff is used to add a "word from the next line" would we - * have enough space? just being paranoid */ -#define MIN_SPACE 75 - if (compl_length > (IOSIZE - MIN_SPACE)) { - compl_cont_status &= ~CONT_SOL; - compl_length = (IOSIZE - MIN_SPACE); - compl_col = curwin->w_cursor.col - compl_length; - } - compl_cont_status |= CONT_ADDING | CONT_N_ADDS; - if (compl_length < 1) { - compl_cont_status &= CONT_LOCAL; - } - } else if (CTRL_X_MODE_LINE_OR_EVAL(ctrl_x_mode)) { - compl_cont_status = CONT_ADDING | CONT_N_ADDS; - } else { - compl_cont_status = 0; - } - } else { - compl_cont_status &= CONT_LOCAL; - } - - if (!(compl_cont_status & CONT_ADDING)) { // normal expansion - compl_cont_mode = ctrl_x_mode; - if (ctrl_x_mode != CTRL_X_NORMAL) { - // Remove LOCAL if ctrl_x_mode != CTRL_X_NORMAL - compl_cont_status = 0; - } - compl_cont_status |= CONT_N_ADDS; - compl_startpos = curwin->w_cursor; - startcol = (int)curs_col; - compl_col = 0; - } - - // Work out completion pattern and original text -- webb - if (ctrl_x_mode == CTRL_X_NORMAL - || (ctrl_x_mode & CTRL_X_WANT_IDENT - && !thesaurus_func_complete(ctrl_x_mode))) { - if ((compl_cont_status & CONT_SOL) - || ctrl_x_mode == CTRL_X_PATH_DEFINES) { - if (!(compl_cont_status & CONT_ADDING)) { - while (--startcol >= 0 && vim_isIDc(line[startcol])) {} - compl_col += ++startcol; - compl_length = curs_col - startcol; - } - if (p_ic) { - compl_pattern = str_foldcase(line + compl_col, compl_length, NULL, 0); - } else { - compl_pattern = vim_strnsave(line + compl_col, (size_t)compl_length); - } - } else if (compl_cont_status & CONT_ADDING) { - char_u *prefix = (char_u *)"\\<"; - - // we need up to 2 extra chars for the prefix - compl_pattern = xmalloc(quote_meta(NULL, line + compl_col, - compl_length) + 2); - if (!vim_iswordp(line + compl_col) - || (compl_col > 0 - && ( - vim_iswordp(mb_prevptr(line, line + compl_col)) - ))) { - prefix = (char_u *)""; - } - STRCPY(compl_pattern, prefix); - (void)quote_meta(compl_pattern + STRLEN(prefix), - line + compl_col, compl_length); - } else if (--startcol < 0 - || !vim_iswordp(mb_prevptr(line, line + startcol + 1))) { - // Match any word of at least two chars - compl_pattern = vim_strsave((char_u *)"\\<\\k\\k"); - compl_col += curs_col; - compl_length = 0; - } else { - // Search the point of change class of multibyte character - // or not a word single byte character backward. - startcol -= utf_head_off(line, line + startcol); - int base_class = mb_get_class(line + startcol); - while (--startcol >= 0) { - int head_off = utf_head_off(line, line + startcol); - if (base_class != mb_get_class(line + startcol - head_off)) { - break; - } - startcol -= head_off; - } - compl_col += ++startcol; - compl_length = (int)curs_col - startcol; - if (compl_length == 1) { - /* Only match word with at least two chars -- webb - * there's no need to call quote_meta, - * xmalloc(7) is enough -- Acevedo - */ - compl_pattern = xmalloc(7); - STRCPY(compl_pattern, "\\<"); - (void)quote_meta(compl_pattern + 2, line + compl_col, 1); - STRCAT(compl_pattern, "\\k"); - } else { - compl_pattern = xmalloc(quote_meta(NULL, line + compl_col, - compl_length) + 2); - STRCPY(compl_pattern, "\\<"); - (void)quote_meta(compl_pattern + 2, line + compl_col, - compl_length); - } - } - } else if (CTRL_X_MODE_LINE_OR_EVAL(ctrl_x_mode)) { - compl_col = (colnr_T)getwhitecols(line); - compl_length = (int)curs_col - (int)compl_col; - if (compl_length < 0) { // cursor in indent: empty pattern - compl_length = 0; - } - if (p_ic) { - compl_pattern = str_foldcase(line + compl_col, compl_length, NULL, 0); - } else { - compl_pattern = vim_strnsave(line + compl_col, (size_t)compl_length); - } - } else if (ctrl_x_mode == CTRL_X_FILES) { - // Go back to just before the first filename character. - if (startcol > 0) { - char_u *p = line + startcol; - - MB_PTR_BACK(line, p); - while (p > line && vim_isfilec(utf_ptr2char((char *)p))) { - MB_PTR_BACK(line, p); - } - if (p == line && vim_isfilec(utf_ptr2char((char *)p))) { - startcol = 0; - } else { - startcol = (int)(p - line) + 1; - } - } - - compl_col += startcol; - compl_length = (int)curs_col - startcol; - compl_pattern = addstar(line + compl_col, (size_t)compl_length, EXPAND_FILES); - } else if (ctrl_x_mode == CTRL_X_CMDLINE || ctrl_x_mode == CTRL_X_CMDLINE_CTRL_X) { - compl_pattern = vim_strnsave(line, (size_t)curs_col); - set_cmd_context(&compl_xp, compl_pattern, - (int)STRLEN(compl_pattern), curs_col, false); - if (compl_xp.xp_context == EXPAND_UNSUCCESSFUL - || compl_xp.xp_context == EXPAND_NOTHING) { - // No completion possible, use an empty pattern to get a - // "pattern not found" message. - compl_col = curs_col; - } else { - compl_col = (int)((char_u *)compl_xp.xp_pattern - compl_pattern); - } - compl_length = curs_col - compl_col; - } else if (ctrl_x_mode == CTRL_X_FUNCTION || ctrl_x_mode == CTRL_X_OMNI - || thesaurus_func_complete(ctrl_x_mode)) { - // Call user defined function 'completefunc' with "a:findstart" - // set to 1 to obtain the length of text to use for completion. - char_u *funcname; - pos_T pos; - const int save_State = State; - - // Call 'completefunc' or 'omnifunc' and get pattern length as a string - funcname = get_complete_funcname(ctrl_x_mode); - if (*funcname == NUL) { - semsg(_(e_notset), ctrl_x_mode == CTRL_X_FUNCTION - ? "completefunc" : "omnifunc"); - // restore did_ai, so that adding comment leader works - did_ai = save_did_ai; - return FAIL; - } - - typval_T args[3]; - args[0].v_type = VAR_NUMBER; - args[1].v_type = VAR_STRING; - args[2].v_type = VAR_UNKNOWN; - args[0].vval.v_number = 1; - args[1].vval.v_string = ""; - - pos = curwin->w_cursor; - textlock++; - colnr_T col = (colnr_T)call_func_retnr((char *)funcname, 2, args); - textlock--; - - State = save_State; - curwin->w_cursor = pos; // restore the cursor position - validate_cursor(); - if (!equalpos(curwin->w_cursor, pos)) { - emsg(_(e_compldel)); - return FAIL; - } - - // Return value -2 means the user complete function wants to cancel the - // complete without an error, do the same if the function did not execute - // successfully. - if (col == -2 || aborting()) { - return FAIL; - } - // Return value -3 does the same as -2 and leaves CTRL-X mode. - if (col == -3) { - ctrl_x_mode = CTRL_X_NORMAL; - edit_submode = NULL; - if (!shortmess(SHM_COMPLETIONMENU)) { - msg_clr_cmdline(); - } - return FAIL; - } - - // Reset extended parameters of completion, when start new - // completion. - compl_opt_refresh_always = false; - - if (col < 0) { - col = curs_col; - } - compl_col = col; - if (compl_col > curs_col) { - compl_col = curs_col; - } - - /* Setup variables for completion. Need to obtain "line" again, - * it may have become invalid. */ - line = ml_get(curwin->w_cursor.lnum); - compl_length = curs_col - compl_col; - compl_pattern = vim_strnsave(line + compl_col, (size_t)compl_length); - } else if (ctrl_x_mode == CTRL_X_SPELL) { - if (spell_bad_len > 0) { - assert(spell_bad_len <= INT_MAX); - compl_col = curs_col - (int)spell_bad_len; - } else { - compl_col = spell_word_start(startcol); - } - if (compl_col >= (colnr_T)startcol) { - compl_length = 0; - compl_col = curs_col; - } else { - spell_expand_check_cap(compl_col); - compl_length = (int)curs_col - compl_col; - } - // Need to obtain "line" again, it may have become invalid. - line = ml_get(curwin->w_cursor.lnum); - compl_pattern = vim_strnsave(line + compl_col, (size_t)compl_length); - } else { - internal_error("ins_complete()"); - return FAIL; - } - - if (compl_cont_status & CONT_ADDING) { - edit_submode_pre = (char_u *)_(" Adding"); - if (CTRL_X_MODE_LINE_OR_EVAL(ctrl_x_mode)) { - // Insert a new line, keep indentation but ignore 'comments' - char_u *old = curbuf->b_p_com; - - curbuf->b_p_com = (char_u *)""; - compl_startpos.lnum = curwin->w_cursor.lnum; - compl_startpos.col = compl_col; - ins_eol('\r'); - curbuf->b_p_com = old; - compl_length = 0; - compl_col = curwin->w_cursor.col; - } - } else { - edit_submode_pre = NULL; - compl_startpos.col = compl_col; - } - - if (compl_cont_status & CONT_LOCAL) { - edit_submode = (char_u *)_(ctrl_x_msgs[CTRL_X_LOCAL_MSG]); - } else { - edit_submode = (char_u *)_(CTRL_X_MSG(ctrl_x_mode)); - } - - /* If any of the original typed text has been changed we need to fix - * the redo buffer. */ - ins_compl_fixRedoBufForLeader(NULL); - - // Always add completion for the original text. - xfree(compl_orig_text); - compl_orig_text = vim_strnsave(line + compl_col, (size_t)compl_length); - if (p_ic) { - flags |= CP_ICASE; - } - if (ins_compl_add(compl_orig_text, -1, NULL, NULL, false, NULL, 0, - flags, false) != OK) { - XFREE_CLEAR(compl_pattern); - XFREE_CLEAR(compl_orig_text); - return FAIL; - } - - /* showmode might reset the internal line pointers, so it must - * be called before line = ml_get(), or when this address is no - * longer needed. -- Acevedo. - */ - edit_submode_extra = (char_u *)_("-- Searching..."); - edit_submode_highl = HLF_COUNT; - showmode(); - edit_submode_extra = NULL; - ui_flush(); - } else if (insert_match && stop_arrow() == FAIL) { - return FAIL; - } - - compl_shown_match = compl_curr_match; - compl_shows_dir = compl_direction; - - /* - * Find next match (and following matches). - */ - save_w_wrow = curwin->w_wrow; - save_w_leftcol = curwin->w_leftcol; - n = ins_compl_next(true, ins_compl_key2count(c), insert_match, false); - - if (n > 1) { // all matches have been found - compl_matches = n; - } - compl_curr_match = compl_shown_match; - compl_direction = compl_shows_dir; - - /* Eat the ESC that vgetc() returns after a CTRL-C to avoid leaving Insert - * mode. */ - if (got_int && !global_busy) { - (void)vgetc(); - got_int = FALSE; - } - - // we found no match if the list has only the "compl_orig_text"-entry - if (compl_first_match == compl_first_match->cp_next) { - edit_submode_extra = (compl_cont_status & CONT_ADDING) - && compl_length > 1 - ? (char_u *)_(e_hitend) : (char_u *)_(e_patnotf); - edit_submode_highl = HLF_E; - /* remove N_ADDS flag, so next ^X<> won't try to go to ADDING mode, - * because we couldn't expand anything at first place, but if we used - * ^P, ^N, ^X^I or ^X^D we might want to add-expand a single-char-word - * (such as M in M'exico) if not tried already. -- Acevedo */ - if (compl_length > 1 - || (compl_cont_status & CONT_ADDING) - || (ctrl_x_mode != CTRL_X_NORMAL - && ctrl_x_mode != CTRL_X_PATH_PATTERNS - && ctrl_x_mode != CTRL_X_PATH_DEFINES)) { - compl_cont_status &= ~CONT_N_ADDS; - } - } - - if (compl_curr_match->cp_flags & CP_CONT_S_IPOS) { - compl_cont_status |= CONT_S_IPOS; - } else { - compl_cont_status &= ~CONT_S_IPOS; - } - - if (edit_submode_extra == NULL) { - if (compl_curr_match->cp_flags & CP_ORIGINAL_TEXT) { - edit_submode_extra = (char_u *)_("Back at original"); - edit_submode_highl = HLF_W; - } else if (compl_cont_status & CONT_S_IPOS) { - edit_submode_extra = (char_u *)_("Word from other line"); - edit_submode_highl = HLF_COUNT; - } else if (compl_curr_match->cp_next == compl_curr_match->cp_prev) { - edit_submode_extra = (char_u *)_("The only match"); - edit_submode_highl = HLF_COUNT; - compl_curr_match->cp_number = 1; - } else { - // Update completion sequence number when needed. - if (compl_curr_match->cp_number == -1) { - ins_compl_update_sequence_numbers(); - } - - /* The match should always have a sequence number now, this is - * just a safety check. */ - if (compl_curr_match->cp_number != -1) { - /* Space for 10 text chars. + 2x10-digit no.s = 31. - * Translations may need more than twice that. */ - static char_u match_ref[81]; - - if (compl_matches > 0) { - vim_snprintf((char *)match_ref, sizeof(match_ref), - _("match %d of %d"), - compl_curr_match->cp_number, compl_matches); - } else { - vim_snprintf((char *)match_ref, sizeof(match_ref), - _("match %d"), - compl_curr_match->cp_number); - } - edit_submode_extra = match_ref; - edit_submode_highl = HLF_R; - if (dollar_vcol >= 0) { - curs_columns(curwin, false); - } - } - } - } - - // Show a message about what (completion) mode we're in. - showmode(); - if (!shortmess(SHM_COMPLETIONMENU)) { - if (edit_submode_extra != NULL) { - if (!p_smd) { - msg_hist_off = true; - msg_attr((const char *)edit_submode_extra, - (edit_submode_highl < HLF_COUNT - ? HL_ATTR(edit_submode_highl) : 0)); - msg_hist_off = false; - } - } else { - msg_clr_cmdline(); // necessary for "noshowmode" - } - } - - // Show the popup menu, unless we got interrupted. - if (enable_pum && !compl_interrupted) { - show_pum(save_w_wrow, save_w_leftcol); - } - compl_was_interrupted = compl_interrupted; - compl_interrupted = FALSE; - - return OK; -} - -/* - * Looks in the first "len" chars. of "src" for search-metachars. - * If dest is not NULL the chars. are copied there quoting (with - * a backslash) the metachars, and dest would be NUL terminated. - * Returns the length (needed) of dest - */ -static unsigned quote_meta(char_u *dest, char_u *src, int len) -{ - unsigned m = (unsigned)len + 1; // one extra for the NUL - - for (; --len >= 0; src++) { - switch (*src) { - case '.': - case '*': - case '[': - if (ctrl_x_mode == CTRL_X_DICTIONARY - || ctrl_x_mode == CTRL_X_THESAURUS) { - break; - } - FALLTHROUGH; - case '~': - if (!p_magic) { // quote these only if magic is set - break; - } - FALLTHROUGH; - case '\\': - if (ctrl_x_mode == CTRL_X_DICTIONARY - || ctrl_x_mode == CTRL_X_THESAURUS) { - break; - } - FALLTHROUGH; - case '^': // currently it's not needed. - case '$': - m++; - if (dest != NULL) { - *dest++ = '\\'; - } - break; - } - if (dest != NULL) { - *dest++ = *src; - } - // Copy remaining bytes of a multibyte character. - const int mb_len = utfc_ptr2len((char *)src) - 1; - if (mb_len > 0 && len >= mb_len) { - for (int i = 0; i < mb_len; i++) { - len--; - src++; - if (dest != NULL) { - *dest++ = *src; - } - } - } - } - if (dest != NULL) { - *dest = NUL; - } - - return m; -} - /// Next character is interpreted literally. /// A one, two or three digit decimal number is interpreted as its byte value. /// If one or two digits are entered, the next character is given to vungetc(). @@ -6500,7 +2817,7 @@ static void redo_literal(int c) /// For undo/redo it resembles hitting the <ESC> key. /// /// @param end_insert_pos can be NULL -static void start_arrow(pos_T *end_insert_pos) +void start_arrow(pos_T *end_insert_pos) { start_arrow_common(end_insert_pos, true); } @@ -6546,19 +2863,6 @@ static void check_spell_redraw(void) } /* - * Called when starting CTRL_X_SPELL mode: Move backwards to a previous badly - * spelled word, if there is one. - */ -static void spell_back_to_badword(void) -{ - pos_T tpos = curwin->w_cursor; - spell_bad_len = spell_move_to(curwin, BACKWARD, true, true, NULL); - if (curwin->w_cursor.col != tpos.col) { - start_arrow(&tpos); - } -} - -/* * stop_arrow() is called before a change is made in insert mode. * If an arrow key has been used, start a new insertion. * Returns FAIL if undo is impossible, shouldn't insert then. @@ -6748,9 +3052,7 @@ void set_last_insert(int c) void free_last_insert(void) { XFREE_CLEAR(last_insert); - XFREE_CLEAR(compl_orig_text); } - #endif /* @@ -8009,8 +4311,8 @@ static bool ins_start_select(int c) case K_S_DOWN: case K_S_END: case K_S_HOME: - // Start selection right away, the cursor can move with - // CTRL-O when beyond the end of the line. + // Start selection right away, the cursor can move with CTRL-O when + // beyond the end of the line. start_selection(); // Execute the key in (insert) Select mode. @@ -9016,7 +5318,7 @@ static bool ins_tab(void) /// Handle CR or NL in insert mode. /// /// @return false when it can't undo. -static bool ins_eol(int c) +bool ins_eol(int c) { if (echeck_abbr(c + ABBR_OFF)) { return true; @@ -9180,7 +5482,7 @@ static int ins_ctrl_ey(int tc) { int c = tc; - if (ctrl_x_mode == CTRL_X_SCROLL) { + if (ctrl_x_mode_scroll()) { if (c == Ctrl_Y) { scrolldown_clamp(); } else { @@ -9353,8 +5655,13 @@ static char_u *do_insert_char_pre(int c) return res; } +bool can_cindent_get(void) +{ + return can_cindent; +} + /// Trigger "event" and take care of fixing undo. -static int ins_apply_autocmds(event_T event) +int ins_apply_autocmds(event_T event) { varnumber_T tick = buf_get_changedtick(curbuf); int r; @@ -9370,21 +5677,3 @@ static int ins_apply_autocmds(event_T event) return r; } - -static void show_pum(int prev_w_wrow, int prev_w_leftcol) -{ - // RedrawingDisabled may be set when invoked through complete(). - int n = RedrawingDisabled; - RedrawingDisabled = 0; - - // If the cursor moved or the display scrolled we need to remove the pum - // first. - setcursor(); - if (prev_w_wrow != curwin->w_wrow || prev_w_leftcol != curwin->w_leftcol) { - ins_compl_del_pum(); - } - - ins_compl_show_pum(); - setcursor(); - RedrawingDisabled = n; -} diff --git a/src/nvim/edit.h b/src/nvim/edit.h index 894e23ee9f..eda6d8c9db 100644 --- a/src/nvim/edit.h +++ b/src/nvim/edit.h @@ -1,27 +1,9 @@ #ifndef NVIM_EDIT_H #define NVIM_EDIT_H +#include "nvim/autocmd.h" #include "nvim/vim.h" -/* - * Array indexes used for cptext argument of ins_compl_add(). - */ -#define CPT_ABBR 0 // "abbr" -#define CPT_MENU 1 // "menu" -#define CPT_KIND 2 // "kind" -#define CPT_INFO 3 // "info" -#define CPT_COUNT 4 // Number of entries - -// values for cp_flags -typedef enum { - CP_ORIGINAL_TEXT = 1, // the original text when the expansion begun - CP_FREE_FNAME = 2, // cp_fname is allocated - CP_CONT_S_IPOS = 4, // use CONT_S_IPOS for compl_cont_status - CP_EQUAL = 8, // ins_compl_equal() always returns true - CP_ICASE = 16, // ins_compl_equal ignores case - CP_FAST = 32, // use fast_breakcheck instead of os_breakcheck -} cp_flags_T; - typedef int (*IndentGetter)(void); // Values for in_cinkeys() diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 16c3e72c5b..9cc92cb62e 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -27,6 +27,7 @@ #include "nvim/eval/gc.h" #include "nvim/eval/typval.h" #include "nvim/eval/userfunc.h" +#include "nvim/eval/vars.h" #include "nvim/ex_cmds2.h" #include "nvim/ex_getln.h" #include "nvim/ex_session.h" @@ -58,20 +59,13 @@ #define DICT_MAXNEST 100 // maximum nesting of lists and dicts -static char *e_letunexp = N_("E18: Unexpected characters in :let"); static char *e_missbrac = N_("E111: Missing ']'"); static char *e_dictrange = N_("E719: Cannot use [:] with a Dictionary"); -static char *e_illvar = N_("E461: Illegal variable name: %s"); -static char *e_cannot_mod = N_("E995: Cannot modify existing variable"); static char *e_nowhitespace = N_("E274: No white space allowed before parenthesis"); -static char *e_lock_unlock = N_("E940: Cannot lock or unlock variable %s"); static char *e_write2 = N_("E80: Error while writing: %s"); static char *e_string_list_or_blob_required = N_("E1098: String, List or Blob required"); -// TODO(ZyX-I): move to eval/executor -static char *e_letwrong = N_("E734: Wrong variable type for %s="); - static char * const namespace_char = "abglstvw"; /// Variable used for g: @@ -212,7 +206,7 @@ static struct vimvar { VV(VV_OLDFILES, "oldfiles", VAR_LIST, 0), VV(VV_WINDOWID, "windowid", VAR_NUMBER, VV_RO_SBX), VV(VV_PROGPATH, "progpath", VAR_STRING, VV_RO), - VV(VV_COMPLETED_ITEM, "completed_item", VAR_DICT, VV_RO), + VV(VV_COMPLETED_ITEM, "completed_item", VAR_DICT, 0), VV(VV_OPTION_NEW, "option_new", VAR_STRING, VV_RO), VV(VV_OPTION_OLD, "option_old", VAR_STRING, VV_RO), VV(VV_OPTION_OLDLOCAL, "option_oldlocal", VAR_STRING, VV_RO), @@ -967,6 +961,30 @@ typval_T *eval_expr(char *arg) return tv; } +/// List Vim variables. +void list_vim_vars(int *first) +{ + list_hashtable_vars(&vimvarht, "v:", false, first); +} + +/// List script-local variables, if there is a script. +void list_script_vars(int *first) +{ + if (current_sctx.sc_sid > 0 && current_sctx.sc_sid <= ga_scripts.ga_len) { + list_hashtable_vars(&SCRIPT_VARS(current_sctx.sc_sid), "s:", false, first); + } +} + +bool is_vimvarht(const hashtab_T *ht) +{ + return ht == &vimvarht; +} + +bool is_compatht(const hashtab_T *ht) +{ + return ht == &compat_hashtab; +} + /// Prepare v: variable "idx" to be used. /// Save the current typeval in "save_tv". /// When not used yet add the variable to the v: hashtable. @@ -1251,723 +1269,6 @@ int eval_foldexpr(char *arg, int *cp) return (int)retval; } -/// ":cons[t] var = expr1" define constant -/// ":cons[t] [name1, name2, ...] = expr1" define constants unpacking list -/// ":cons[t] [name, ..., ; lastname] = expr" define constants unpacking list -void ex_const(exarg_T *eap) -{ - ex_let_const(eap, true); -} - -/// Get a list of lines from a HERE document. The here document is a list of -/// lines surrounded by a marker. -/// cmd << {marker} -/// {line1} -/// {line2} -/// .... -/// {marker} -/// -/// The {marker} is a string. If the optional 'trim' word is supplied before the -/// marker, then the leading indentation before the lines (matching the -/// indentation in the 'cmd' line) is stripped. -/// -/// @return a List with {lines} or NULL. -static list_T *heredoc_get(exarg_T *eap, char *cmd) -{ - char *marker; - char *p; - int marker_indent_len = 0; - int text_indent_len = 0; - char *text_indent = NULL; - - if (eap->getline == NULL) { - emsg(_("E991: cannot use =<< here")); - return NULL; - } - - // Check for the optional 'trim' word before the marker - cmd = skipwhite(cmd); - if (STRNCMP(cmd, "trim", 4) == 0 - && (cmd[4] == NUL || ascii_iswhite(cmd[4]))) { - cmd = skipwhite(cmd + 4); - - // Trim the indentation from all the lines in the here document. - // The amount of indentation trimmed is the same as the indentation of - // the first line after the :let command line. To find the end marker - // the indent of the :let command line is trimmed. - p = *eap->cmdlinep; - while (ascii_iswhite(*p)) { - p++; - marker_indent_len++; - } - text_indent_len = -1; - } - - // The marker is the next word. - if (*cmd != NUL && *cmd != '"') { - marker = skipwhite(cmd); - p = (char *)skiptowhite((char_u *)marker); - if (*skipwhite(p) != NUL && *skipwhite(p) != '"') { - emsg(_(e_trailing)); - return NULL; - } - *p = NUL; - if (islower(*marker)) { - emsg(_("E221: Marker cannot start with lower case letter")); - return NULL; - } - } else { - emsg(_("E172: Missing marker")); - return NULL; - } - - list_T *l = tv_list_alloc(0); - for (;;) { - int mi = 0; - int ti = 0; - - char *theline = eap->getline(NUL, eap->cookie, 0, false); - if (theline == NULL) { - semsg(_("E990: Missing end marker '%s'"), marker); - break; - } - - // with "trim": skip the indent matching the :let line to find the - // marker - if (marker_indent_len > 0 - && STRNCMP(theline, *eap->cmdlinep, marker_indent_len) == 0) { - mi = marker_indent_len; - } - if (STRCMP(marker, theline + mi) == 0) { - xfree(theline); - break; - } - if (text_indent_len == -1 && *theline != NUL) { - // set the text indent from the first line. - p = theline; - text_indent_len = 0; - while (ascii_iswhite(*p)) { - p++; - text_indent_len++; - } - text_indent = xstrnsave(theline, (size_t)text_indent_len); - } - // with "trim": skip the indent matching the first line - if (text_indent != NULL) { - for (ti = 0; ti < text_indent_len; ti++) { - if (theline[ti] != text_indent[ti]) { - break; - } - } - } - - tv_list_append_string(l, theline + ti, -1); - xfree(theline); - } - xfree(text_indent); - - return l; -} - -/// ":let" list all variable values -/// ":let var1 var2" list variable values -/// ":let var = expr" assignment command. -/// ":let var += expr" assignment command. -/// ":let var -= expr" assignment command. -/// ":let var *= expr" assignment command. -/// ":let var /= expr" assignment command. -/// ":let var %= expr" assignment command. -/// ":let var .= expr" assignment command. -/// ":let var ..= expr" assignment command. -/// ":let [var1, var2] = expr" unpack list. -/// ":let [name, ..., ; lastname] = expr" unpack list. -void ex_let(exarg_T *eap) -{ - ex_let_const(eap, false); -} - -static void ex_let_const(exarg_T *eap, const bool is_const) -{ - char *arg = eap->arg; - char *expr = NULL; - typval_T rettv; - int i; - int var_count = 0; - int semicolon = 0; - char op[2]; - char *argend; - int first = true; - - argend = (char *)skip_var_list(arg, &var_count, &semicolon); - if (argend == NULL) { - return; - } - if (argend > arg && argend[-1] == '.') { // For var.='str'. - argend--; - } - expr = skipwhite(argend); - if (*expr != '=' && !((vim_strchr("+-*/%.", *expr) != NULL - && expr[1] == '=') || STRNCMP(expr, "..=", 3) == 0)) { - // ":let" without "=": list variables - if (*arg == '[') { - emsg(_(e_invarg)); - } else if (!ends_excmd(*arg)) { - // ":let var1 var2" - arg = (char *)list_arg_vars(eap, (const char *)arg, &first); - } else if (!eap->skip) { - // ":let" - list_glob_vars(&first); - list_buf_vars(&first); - list_win_vars(&first); - list_tab_vars(&first); - list_script_vars(&first); - list_func_vars(&first); - list_vim_vars(&first); - } - eap->nextcmd = (char *)check_nextcmd((char_u *)arg); - } else if (expr[0] == '=' && expr[1] == '<' && expr[2] == '<') { - // HERE document - list_T *l = heredoc_get(eap, expr + 3); - if (l != NULL) { - tv_list_set_ret(&rettv, l); - if (!eap->skip) { - op[0] = '='; - op[1] = NUL; - (void)ex_let_vars(eap->arg, &rettv, false, semicolon, var_count, - is_const, (char *)op); - } - tv_clear(&rettv); - } - } else { - op[0] = '='; - op[1] = NUL; - if (*expr != '=') { - if (vim_strchr("+-*/%.", *expr) != NULL) { - op[0] = *expr; // +=, -=, *=, /=, %= or .= - if (expr[0] == '.' && expr[1] == '.') { // ..= - expr++; - } - } - expr = skipwhite(expr + 2); - } else { - expr = skipwhite(expr + 1); - } - - if (eap->skip) { - ++emsg_skip; - } - i = eval0(expr, &rettv, &eap->nextcmd, !eap->skip); - if (eap->skip) { - if (i != FAIL) { - tv_clear(&rettv); - } - emsg_skip--; - } else if (i != FAIL) { - (void)ex_let_vars(eap->arg, &rettv, false, semicolon, var_count, - is_const, (char *)op); - tv_clear(&rettv); - } - } -} - -/// Assign the typevalue "tv" to the variable or variables at "arg_start". -/// Handles both "var" with any type and "[var, var; var]" with a list type. -/// When "op" is not NULL it points to a string with characters that -/// must appear after the variable(s). Use "+", "-" or "." for add, subtract -/// or concatenate. -/// -/// @param copy copy values from "tv", don't move -/// @param semicolon from skip_var_list() -/// @param var_count from skip_var_list() -/// @param is_const lock variables for :const -/// -/// @return OK or FAIL; -static int ex_let_vars(char *arg_start, typval_T *tv, int copy, int semicolon, int var_count, - int is_const, char *op) -{ - char *arg = arg_start; - typval_T ltv; - - if (*arg != '[') { - /* - * ":let var = expr" or ":for var in list" - */ - if (ex_let_one(arg, tv, copy, is_const, op, op) == NULL) { - return FAIL; - } - return OK; - } - - // ":let [v1, v2] = list" or ":for [v1, v2] in listlist" - if (tv->v_type != VAR_LIST) { - emsg(_(e_listreq)); - return FAIL; - } - list_T *const l = tv->vval.v_list; - - const int len = tv_list_len(l); - if (semicolon == 0 && var_count < len) { - emsg(_("E687: Less targets than List items")); - return FAIL; - } - if (var_count - semicolon > len) { - emsg(_("E688: More targets than List items")); - return FAIL; - } - // List l may actually be NULL, but it should fail with E688 or even earlier - // if you try to do ":let [] = v:_null_list". - assert(l != NULL); - - listitem_T *item = tv_list_first(l); - size_t rest_len = (size_t)tv_list_len(l); - while (*arg != ']') { - arg = skipwhite(arg + 1); - arg = ex_let_one(arg, TV_LIST_ITEM_TV(item), true, is_const, ",;]", op); - if (arg == NULL) { - return FAIL; - } - rest_len--; - - item = TV_LIST_ITEM_NEXT(l, item); - arg = skipwhite(arg); - if (*arg == ';') { - /* Put the rest of the list (may be empty) in the var after ';'. - * Create a new list for this. */ - list_T *const rest_list = tv_list_alloc((ptrdiff_t)rest_len); - while (item != NULL) { - tv_list_append_tv(rest_list, TV_LIST_ITEM_TV(item)); - item = TV_LIST_ITEM_NEXT(l, item); - } - - ltv.v_type = VAR_LIST; - ltv.v_lock = VAR_UNLOCKED; - ltv.vval.v_list = rest_list; - tv_list_ref(rest_list); - - arg = ex_let_one(skipwhite(arg + 1), <v, false, is_const, "]", op); - tv_clear(<v); - if (arg == NULL) { - return FAIL; - } - break; - } else if (*arg != ',' && *arg != ']') { - internal_error("ex_let_vars()"); - return FAIL; - } - } - - return OK; -} - -/// Skip over assignable variable "var" or list of variables "[var, var]". -/// Used for ":let varvar = expr" and ":for varvar in expr". -/// For "[var, var]" increment "*var_count" for each variable. -/// for "[var, var; var]" set "semicolon". -/// -/// @return NULL for an error. -static const char *skip_var_list(const char *arg, int *var_count, int *semicolon) -{ - const char *p; - const char *s; - - if (*arg == '[') { - // "[var, var]": find the matching ']'. - p = arg; - for (;;) { - p = skipwhite(p + 1); // skip whites after '[', ';' or ',' - s = skip_var_one((char *)p); - if (s == p) { - semsg(_(e_invarg2), p); - return NULL; - } - ++*var_count; - - p = skipwhite(s); - if (*p == ']') { - break; - } else if (*p == ';') { - if (*semicolon == 1) { - emsg(_("E452: Double ; in list of variables")); - return NULL; - } - *semicolon = 1; - } else if (*p != ',') { - semsg(_(e_invarg2), p); - return NULL; - } - } - return p + 1; - } else { - return skip_var_one((char *)arg); - } -} - -/// Skip one (assignable) variable name, including @r, $VAR, &option, d.key, -/// l[idx]. -static const char *skip_var_one(const char *arg) -{ - if (*arg == '@' && arg[1] != NUL) { - return arg + 1 + utfc_ptr2len(arg + 1); - } - return (char *)find_name_end(*arg == '$' || *arg == '&' ? arg + 1 : arg, - NULL, NULL, FNE_INCL_BR | FNE_CHECK_START); -} - -/// List variables for hashtab "ht" with prefix "prefix". -/// -/// @param empty if TRUE also list NULL strings as empty strings. -void list_hashtable_vars(hashtab_T *ht, const char *prefix, int empty, int *first) -{ - hashitem_T *hi; - dictitem_T *di; - int todo; - - todo = (int)ht->ht_used; - for (hi = ht->ht_array; todo > 0 && !got_int; ++hi) { - if (!HASHITEM_EMPTY(hi)) { - todo--; - di = TV_DICT_HI2DI(hi); - char buf[IOSIZE]; - - // apply :filter /pat/ to variable name - xstrlcpy(buf, prefix, IOSIZE); - xstrlcat(buf, (char *)di->di_key, IOSIZE); - if (message_filtered((char_u *)buf)) { - continue; - } - - if (empty || di->di_tv.v_type != VAR_STRING - || di->di_tv.vval.v_string != NULL) { - list_one_var(di, prefix, first); - } - } - } -} - -/// List global variables. -static void list_glob_vars(int *first) -{ - list_hashtable_vars(&globvarht, "", true, first); -} - -/// List buffer variables. -static void list_buf_vars(int *first) -{ - list_hashtable_vars(&curbuf->b_vars->dv_hashtab, "b:", true, first); -} - -/// List window variables. -static void list_win_vars(int *first) -{ - list_hashtable_vars(&curwin->w_vars->dv_hashtab, "w:", true, first); -} - -/// List tab page variables. -static void list_tab_vars(int *first) -{ - list_hashtable_vars(&curtab->tp_vars->dv_hashtab, "t:", true, first); -} - -/// List Vim variables. -static void list_vim_vars(int *first) -{ - list_hashtable_vars(&vimvarht, "v:", false, first); -} - -/// List script-local variables, if there is a script. -static void list_script_vars(int *first) -{ - if (current_sctx.sc_sid > 0 && current_sctx.sc_sid <= ga_scripts.ga_len) { - list_hashtable_vars(&SCRIPT_VARS(current_sctx.sc_sid), "s:", false, first); - } -} - -/// List variables in "arg". -static const char *list_arg_vars(exarg_T *eap, const char *arg, int *first) -{ - int error = FALSE; - int len; - const char *name; - const char *name_start; - typval_T tv; - - while (!ends_excmd(*arg) && !got_int) { - if (error || eap->skip) { - arg = find_name_end(arg, NULL, NULL, FNE_INCL_BR | FNE_CHECK_START); - if (!ascii_iswhite(*arg) && !ends_excmd(*arg)) { - emsg_severe = true; - emsg(_(e_trailing)); - break; - } - } else { - // get_name_len() takes care of expanding curly braces - name_start = name = arg; - char *tofree; - len = get_name_len(&arg, &tofree, true, true); - if (len <= 0) { - /* This is mainly to keep test 49 working: when expanding - * curly braces fails overrule the exception error message. */ - if (len < 0 && !aborting()) { - emsg_severe = true; - semsg(_(e_invarg2), arg); - break; - } - error = TRUE; - } else { - if (tofree != NULL) { - name = tofree; - } - if (get_var_tv(name, len, &tv, NULL, true, false) - == FAIL) { - error = true; - } else { - // handle d.key, l[idx], f(expr) - const char *const arg_subsc = arg; - if (handle_subscript(&arg, &tv, true, true, name, &name) == FAIL) { - error = true; - } else { - if (arg == arg_subsc && len == 2 && name[1] == ':') { - switch (*name) { - case 'g': - list_glob_vars(first); break; - case 'b': - list_buf_vars(first); break; - case 'w': - list_win_vars(first); break; - case 't': - list_tab_vars(first); break; - case 'v': - list_vim_vars(first); break; - case 's': - list_script_vars(first); break; - case 'l': - list_func_vars(first); break; - default: - semsg(_("E738: Can't list variables for %s"), name); - } - } else { - char *const s = encode_tv2echo(&tv, NULL); - const char *const used_name = (arg == arg_subsc - ? name - : name_start); - const ptrdiff_t name_size = (used_name == tofree - ? (ptrdiff_t)strlen(used_name) - : (arg - used_name)); - list_one_var_a("", used_name, name_size, - tv.v_type, s == NULL ? "" : s, first); - xfree(s); - } - tv_clear(&tv); - } - } - } - - xfree(tofree); - } - - arg = (const char *)skipwhite(arg); - } - - return arg; -} - -// TODO(ZyX-I): move to eval/ex_cmds - -/// Set one item of `:let var = expr` or `:let [v1, v2] = list` to its value -/// -/// @param[in] arg Start of the variable name. -/// @param[in] tv Value to assign to the variable. -/// @param[in] copy If true, copy value from `tv`. -/// @param[in] endchars Valid characters after variable name or NULL. -/// @param[in] op Operation performed: *op is `+`, `-`, `.` for `+=`, etc. -/// NULL for `=`. -/// -/// @return a pointer to the char just after the var name or NULL in case of -/// error. -static char *ex_let_one(char *arg, typval_T *const tv, const bool copy, const bool is_const, - const char *const endchars, const char *const op) - FUNC_ATTR_NONNULL_ARG(1, 2) FUNC_ATTR_WARN_UNUSED_RESULT -{ - char *arg_end = NULL; - int len; - int opt_flags; - char *tofree = NULL; - - /* - * ":let $VAR = expr": Set environment variable. - */ - if (*arg == '$') { - if (is_const) { - emsg(_("E996: Cannot lock an environment variable")); - return NULL; - } - // Find the end of the name. - arg++; - char *name = arg; - len = get_env_len((const char **)&arg); - if (len == 0) { - semsg(_(e_invarg2), name - 1); - } else { - if (op != NULL && vim_strchr("+-*/%", *op) != NULL) { - semsg(_(e_letwrong), op); - } else if (endchars != NULL - && vim_strchr(endchars, *skipwhite(arg)) == NULL) { - emsg(_(e_letunexp)); - } else if (!check_secure()) { - const char c1 = name[len]; - name[len] = NUL; - const char *p = tv_get_string_chk(tv); - if (p != NULL && op != NULL && *op == '.') { - char *s = vim_getenv(name); - - if (s != NULL) { - tofree = (char *)concat_str((const char_u *)s, (const char_u *)p); - p = (const char *)tofree; - xfree(s); - } - } - if (p != NULL) { - os_setenv(name, p, 1); - if (STRICMP(name, "HOME") == 0) { - init_homedir(); - } else if (didset_vim && STRICMP(name, "VIM") == 0) { - didset_vim = false; - } else if (didset_vimruntime - && STRICMP(name, "VIMRUNTIME") == 0) { - didset_vimruntime = false; - } - arg_end = arg; - } - name[len] = c1; - xfree(tofree); - } - } - // ":let &option = expr": Set option value. - // ":let &l:option = expr": Set local option value. - // ":let &g:option = expr": Set global option value. - } else if (*arg == '&') { - if (is_const) { - emsg(_("E996: Cannot lock an option")); - return NULL; - } - // Find the end of the name. - char *const p = (char *)find_option_end((const char **)&arg, &opt_flags); - if (p == NULL - || (endchars != NULL - && vim_strchr(endchars, *skipwhite(p)) == NULL)) { - emsg(_(e_letunexp)); - } else { - int opt_type; - long numval; - char *stringval = NULL; - const char *s = NULL; - - const char c1 = *p; - *p = NUL; - - varnumber_T n = tv_get_number(tv); - if (tv->v_type != VAR_BOOL && tv->v_type != VAR_SPECIAL) { - s = tv_get_string_chk(tv); // != NULL if number or string. - } - if (s != NULL && op != NULL && *op != '=') { - opt_type = get_option_value(arg, &numval, &stringval, opt_flags); - if ((opt_type == 1 && *op == '.') - || (opt_type == 0 && *op != '.')) { - semsg(_(e_letwrong), op); - s = NULL; // don't set the value - } else { - if (opt_type == 1) { // number - switch (*op) { - case '+': - n = numval + n; break; - case '-': - n = numval - n; break; - case '*': - n = numval * n; break; - case '/': - n = num_divide(numval, n); break; - case '%': - n = num_modulus(numval, n); break; - } - } else if (opt_type == 0 && stringval != NULL) { // string - char *const oldstringval = stringval; - stringval = (char *)concat_str((const char_u *)stringval, - (const char_u *)s); - xfree(oldstringval); - s = stringval; - } - } - } - if (s != NULL || tv->v_type == VAR_BOOL - || tv->v_type == VAR_SPECIAL) { - set_option_value((const char *)arg, n, s, opt_flags); - arg_end = p; - } - *p = c1; - xfree(stringval); - } - // ":let @r = expr": Set register contents. - } else if (*arg == '@') { - if (is_const) { - emsg(_("E996: Cannot lock a register")); - return NULL; - } - arg++; - - int regname = utf_ptr2char(arg); - int mblen = utf_ptr2len(arg); - - if (op != NULL && vim_strchr("+-*/%", *op) != NULL) { - semsg(_(e_letwrong), op); - } else if (endchars != NULL - && vim_strchr(endchars, *skipwhite(arg + mblen)) == NULL) { - emsg(_(e_letunexp)); - } else { - char *s; - - char *ptofree = NULL; - const char *p = tv_get_string_chk(tv); - if (p != NULL && op != NULL && *op == '.') { - s = get_reg_contents(regname == '@' ? '"' : regname, kGRegExprSrc); - if (s != NULL) { - ptofree = (char *)concat_str((char_u *)s, (const char_u *)p); - p = (const char *)ptofree; - xfree(s); - } - } - if (p != NULL) { - - write_reg_contents(regname == '@' ? '"' : regname, - (const char_u *)p, (ssize_t)STRLEN(p), false); - arg_end = arg + mblen; - } - xfree(ptofree); - } - } - /* - * ":let var = expr": Set internal variable. - * ":let {expr} = expr": Idem, name made with curly braces - */ - else if (eval_isnamec1(*arg) || *arg == '{') { - lval_T lv; - - char *const p = get_lval(arg, tv, &lv, false, false, 0, FNE_CHECK_START); - if (p != NULL && lv.ll_name != NULL) { - if (endchars != NULL && vim_strchr(endchars, *skipwhite(p)) == NULL) { - emsg(_(e_letunexp)); - } else { - set_var_lval(&lv, p, tv, copy, is_const, op); - arg_end = p; - } - } - clear_lval(&lv); - } else { - semsg(_(e_invarg2), arg); - } - - return arg_end; -} - // TODO(ZyX-I): move to eval/executor /// Get an lvalue @@ -2357,8 +1658,8 @@ void clear_lval(lval_T *lp) /// @param endp points to just after the parsed name. /// @param op NULL, "+" for "+=", "-" for "-=", "*" for "*=", "/" for "/=", /// "%" for "%=", "." for ".=" or "=" for "=". -static void set_var_lval(lval_T *lp, char *endp, typval_T *rettv, int copy, const bool is_const, - const char *op) +void set_var_lval(lval_T *lp, char *endp, typval_T *rettv, int copy, const bool is_const, + const char *op) { int cc; listitem_T *ri; @@ -2810,327 +2111,6 @@ void set_context_for_expression(expand_T *xp, char *arg, cmdidx_T cmdidx) xp->xp_pattern = arg; } -/// ":unlet[!] var1 ... " command. -void ex_unlet(exarg_T *eap) -{ - ex_unletlock(eap, eap->arg, 0, do_unlet_var); -} - -// TODO(ZyX-I): move to eval/ex_cmds - -/// ":lockvar" and ":unlockvar" commands -void ex_lockvar(exarg_T *eap) -{ - char *arg = eap->arg; - int deep = 2; - - if (eap->forceit) { - deep = -1; - } else if (ascii_isdigit(*arg)) { - deep = getdigits_int(&arg, false, -1); - arg = skipwhite(arg); - } - - ex_unletlock(eap, arg, deep, do_lock_var); -} - -// TODO(ZyX-I): move to eval/ex_cmds - -/// Common parsing logic for :unlet, :lockvar and :unlockvar. -/// -/// Invokes `callback` afterwards if successful and `eap->skip == false`. -/// -/// @param[in] eap Ex command arguments for the command. -/// @param[in] argstart Start of the string argument for the command. -/// @param[in] deep Levels to (un)lock for :(un)lockvar, -1 to (un)lock -/// everything. -/// @param[in] callback Appropriate handler for the command. -static void ex_unletlock(exarg_T *eap, char *argstart, int deep, ex_unletlock_callback callback) - FUNC_ATTR_NONNULL_ALL -{ - char *arg = argstart; - char *name_end; - bool error = false; - lval_T lv; - - do { - if (*arg == '$') { - lv.ll_name = (const char *)arg; - lv.ll_tv = NULL; - arg++; - if (get_env_len((const char **)&arg) == 0) { - semsg(_(e_invarg2), arg - 1); - return; - } - if (!error && !eap->skip && callback(&lv, arg, eap, deep) == FAIL) { - error = true; - } - name_end = arg; - } else { - // Parse the name and find the end. - name_end = get_lval(arg, NULL, &lv, true, eap->skip || error, - 0, FNE_CHECK_START); - if (lv.ll_name == NULL) { - error = true; // error, but continue parsing. - } - if (name_end == NULL - || (!ascii_iswhite(*name_end) && !ends_excmd(*name_end))) { - if (name_end != NULL) { - emsg_severe = true; - emsg(_(e_trailing)); - } - if (!(eap->skip || error)) { - clear_lval(&lv); - } - break; - } - - if (!error && !eap->skip && callback(&lv, name_end, eap, deep) == FAIL) { - error = true; - } - - if (!eap->skip) { - clear_lval(&lv); - } - } - arg = skipwhite(name_end); - } while (!ends_excmd(*arg)); - - eap->nextcmd = (char *)check_nextcmd((char_u *)arg); -} - -// TODO(ZyX-I): move to eval/ex_cmds - -/// Unlet a variable indicated by `lp`. -/// -/// @param[in] lp The lvalue. -/// @param[in] name_end End of the string argument for the command. -/// @param[in] eap Ex command arguments for :unlet. -/// @param[in] deep Unused. -/// -/// @return OK on success, or FAIL on failure. -static int do_unlet_var(lval_T *lp, char *name_end, exarg_T *eap, int deep FUNC_ATTR_UNUSED) - FUNC_ATTR_NONNULL_ALL -{ - int forceit = eap->forceit; - int ret = OK; - int cc; - - if (lp->ll_tv == NULL) { - cc = (char_u)(*name_end); - *name_end = NUL; - - // Environment variable, normal name or expanded name. - if (*lp->ll_name == '$') { - os_unsetenv(lp->ll_name + 1); - } else if (do_unlet(lp->ll_name, lp->ll_name_len, forceit) == FAIL) { - ret = FAIL; - } - *name_end = (char)cc; - } else if ((lp->ll_list != NULL - // ll_list is not NULL when lvalue is not in a list, NULL lists - // yield E689. - && var_check_lock(tv_list_locked(lp->ll_list), - lp->ll_name, - lp->ll_name_len)) - || (lp->ll_dict != NULL - && var_check_lock(lp->ll_dict->dv_lock, - lp->ll_name, - lp->ll_name_len))) { - return FAIL; - } else if (lp->ll_range) { - assert(lp->ll_list != NULL); - // Delete a range of List items. - listitem_T *const first_li = lp->ll_li; - listitem_T *last_li = first_li; - for (;;) { - listitem_T *const li = TV_LIST_ITEM_NEXT(lp->ll_list, lp->ll_li); - if (var_check_lock(TV_LIST_ITEM_TV(lp->ll_li)->v_lock, - lp->ll_name, - lp->ll_name_len)) { - return false; - } - lp->ll_li = li; - lp->ll_n1++; - if (lp->ll_li == NULL || (!lp->ll_empty2 && lp->ll_n2 < lp->ll_n1)) { - break; - } else { - last_li = lp->ll_li; - } - } - tv_list_remove_items(lp->ll_list, first_li, last_li); - } else { - if (lp->ll_list != NULL) { - // unlet a List item. - tv_list_item_remove(lp->ll_list, lp->ll_li); - } else { - // unlet a Dictionary item. - dict_T *d = lp->ll_dict; - assert(d != NULL); - dictitem_T *di = lp->ll_di; - bool watched = tv_dict_is_watched(d); - char *key = NULL; - typval_T oldtv; - - if (watched) { - tv_copy(&di->di_tv, &oldtv); - // need to save key because dictitem_remove will free it - key = xstrdup((char *)di->di_key); - } - - tv_dict_item_remove(d, di); - - if (watched) { - tv_dict_watcher_notify(d, key, NULL, &oldtv); - tv_clear(&oldtv); - xfree(key); - } - } - } - - return ret; -} - -// TODO(ZyX-I): move to eval/ex_cmds - -/// unlet a variable -/// -/// @param[in] name Variable name to unlet. -/// @param[in] name_len Variable name length. -/// @param[in] forceit If true, do not complain if variable doesn’t exist. -/// -/// @return OK if it existed, FAIL otherwise. -int do_unlet(const char *const name, const size_t name_len, const bool forceit) - FUNC_ATTR_NONNULL_ALL -{ - const char *varname; - dict_T *dict; - hashtab_T *ht = find_var_ht_dict(name, name_len, &varname, &dict); - - if (ht != NULL && *varname != NUL) { - dict_T *d = get_current_funccal_dict(ht); - if (d == NULL) { - if (ht == &globvarht) { - d = &globvardict; - } else if (ht == &compat_hashtab) { - d = &vimvardict; - } else { - dictitem_T *const di = find_var_in_ht(ht, *name, "", 0, false); - d = di->di_tv.vval.v_dict; - } - if (d == NULL) { - internal_error("do_unlet()"); - return FAIL; - } - } - - hashitem_T *hi = hash_find(ht, varname); - if (HASHITEM_EMPTY(hi)) { - hi = find_hi_in_scoped_ht(name, &ht); - } - if (hi != NULL && !HASHITEM_EMPTY(hi)) { - dictitem_T *const di = TV_DICT_HI2DI(hi); - if (var_check_fixed(di->di_flags, name, TV_CSTRING) - || var_check_ro(di->di_flags, name, TV_CSTRING) - || var_check_lock(d->dv_lock, name, TV_CSTRING)) { - return FAIL; - } - - if (var_check_lock(d->dv_lock, name, TV_CSTRING)) { - return FAIL; - } - - typval_T oldtv; - bool watched = tv_dict_is_watched(dict); - - if (watched) { - tv_copy(&di->di_tv, &oldtv); - } - - delete_var(ht, hi); - - if (watched) { - tv_dict_watcher_notify(dict, varname, NULL, &oldtv); - tv_clear(&oldtv); - } - return OK; - } - } - if (forceit) { - return OK; - } - semsg(_("E108: No such variable: \"%s\""), name); - return FAIL; -} - -// TODO(ZyX-I): move to eval/ex_cmds - -/// Lock or unlock variable indicated by `lp`. -/// -/// Locks if `eap->cmdidx == CMD_lockvar`, unlocks otherwise. -/// -/// @param[in] lp The lvalue. -/// @param[in] name_end Unused. -/// @param[in] eap Ex command arguments for :(un)lockvar. -/// @param[in] deep Levels to (un)lock, -1 to (un)lock everything. -/// -/// @return OK on success, or FAIL on failure. -static int do_lock_var(lval_T *lp, char *name_end FUNC_ATTR_UNUSED, exarg_T *eap, int deep) - FUNC_ATTR_NONNULL_ARG(1, 3) -{ - bool lock = eap->cmdidx == CMD_lockvar; - int ret = OK; - - if (deep == 0) { // Nothing to do. - return OK; - } - - if (lp->ll_tv == NULL) { - if (*lp->ll_name == '$') { - semsg(_(e_lock_unlock), lp->ll_name); - ret = FAIL; - } else { - // Normal name or expanded name. - dictitem_T *const di = find_var(lp->ll_name, lp->ll_name_len, NULL, - true); - if (di == NULL) { - ret = FAIL; - } else if ((di->di_flags & DI_FLAGS_FIX) - && di->di_tv.v_type != VAR_DICT - && di->di_tv.v_type != VAR_LIST) { - // For historical reasons this error is not given for Lists and - // Dictionaries. E.g. b: dictionary may be locked/unlocked. - semsg(_(e_lock_unlock), lp->ll_name); - ret = FAIL; - } else { - if (lock) { - di->di_flags |= DI_FLAGS_LOCK; - } else { - di->di_flags &= (uint8_t)(~DI_FLAGS_LOCK); - } - tv_item_lock(&di->di_tv, deep, lock, false); - } - } - } else if (lp->ll_range) { - listitem_T *li = lp->ll_li; - - // (un)lock a range of List items. - while (li != NULL && (lp->ll_empty2 || lp->ll_n2 >= lp->ll_n1)) { - tv_item_lock(TV_LIST_ITEM_TV(li), deep, lock, false); - li = TV_LIST_ITEM_NEXT(lp->ll_list, li); - lp->ll_n1++; - } - } else if (lp->ll_list != NULL) { - // (un)lock a List item. - tv_item_lock(TV_LIST_ITEM_TV(lp->ll_li), deep, lock, false); - } else { - // (un)lock a Dictionary item. - tv_item_lock(&lp->ll_di->di_tv, deep, lock, false); - } - - return ret; -} - /// Delete all "menutrans_" variables. void del_menutrans_vars(void) { @@ -4795,7 +3775,7 @@ int get_option_tv(const char **const arg, typval_T *const rettv, const bool eval { long numval; char *stringval; - int opt_type; + getoption_T opt_type; bool working = (**arg == '+'); // has("+option") int ret = OK; int opt_flags; @@ -4819,26 +3799,28 @@ int get_option_tv(const char **const arg, typval_T *const rettv, const bool eval opt_type = get_option_value(*arg, &numval, rettv == NULL ? NULL : &stringval, opt_flags); - if (opt_type == -3) { // invalid name + if (opt_type == gov_unknown) { if (rettv != NULL) { semsg(_("E113: Unknown option: %s"), *arg); } ret = FAIL; } else if (rettv != NULL) { - if (opt_type == -2) { // hidden string option + if (opt_type == gov_hidden_string) { rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; - } else if (opt_type == -1) { // hidden number option + } else if (opt_type == gov_hidden_bool || opt_type == gov_hidden_number) { rettv->v_type = VAR_NUMBER; rettv->vval.v_number = 0; - } else if (opt_type == 1 || opt_type == 2) { // number or boolean option + } else if (opt_type == gov_bool || opt_type == gov_number) { rettv->v_type = VAR_NUMBER; rettv->vval.v_number = numval; } else { // string option rettv->v_type = VAR_STRING; rettv->vval.v_string = stringval; } - } else if (working && (opt_type == -2 || opt_type == -1)) { + } else if (working && (opt_type == gov_hidden_bool + || opt_type == gov_hidden_number + || opt_type == gov_hidden_string)) { ret = FAIL; } @@ -5685,7 +4667,7 @@ static inline bool set_ref_dict(dict_T *dict, int copyID) return false; } -/// Get the key for *{key: val} into "tv" and advance "arg". +/// Get the key for #{key: val} into "tv" and advance "arg". /// /// @return FAIL when there is no valid key. static int get_literal_key(char **arg, typval_T *tv) @@ -5705,7 +4687,7 @@ static int get_literal_key(char **arg, typval_T *tv) } /// Allocate a variable for a Dictionary and fill it from "*arg". -/// "literal" is true for *{key: val} +/// "literal" is true for #{key: val} /// /// @return OK or FAIL. Returns NOTDONE for {expr}. static int dict_get_tv(char **arg, typval_T *rettv, int evaluate, bool literal) @@ -6480,73 +5462,6 @@ win_T *find_tabwin(typval_T *wvp, typval_T *tvp) return wp; } -/// getwinvar() and gettabwinvar() -/// -/// @param off 1 for gettabwinvar() -void getwinvar(typval_T *argvars, typval_T *rettv, int off) -{ - win_T *win; - dictitem_T *v; - tabpage_T *tp = NULL; - bool done = false; - - if (off == 1) { - tp = find_tabpage((int)tv_get_number_chk(&argvars[0], NULL)); - } else { - tp = curtab; - } - win = find_win_by_nr(&argvars[off], tp); - const char *varname = tv_get_string_chk(&argvars[off + 1]); - - rettv->v_type = VAR_STRING; - rettv->vval.v_string = NULL; - - emsg_off++; - if (win != NULL && varname != NULL) { - // Set curwin to be our win, temporarily. Also set the tabpage, - // otherwise the window is not valid. Only do this when needed, - // autocommands get blocked. - bool need_switch_win = tp != curtab || win != curwin; - switchwin_T switchwin; - if (!need_switch_win || switch_win(&switchwin, win, tp, true) == OK) { - if (*varname == '&') { - if (varname[1] == NUL) { - // get all window-local options in a dict - dict_T *opts = get_winbuf_options(false); - - if (opts != NULL) { - tv_dict_set_ret(rettv, opts); - done = true; - } - } else if (get_option_tv(&varname, rettv, 1) == OK) { - // window-local-option - done = true; - } - } else { - // Look up the variable. - // Let getwinvar({nr}, "") return the "w:" dictionary. - v = find_var_in_ht(&win->w_vars->dv_hashtab, 'w', varname, - strlen(varname), false); - if (v != NULL) { - tv_copy(&v->di_tv, rettv); - done = true; - } - } - } - - if (need_switch_win) { - // restore previous notion of curwin - restore_win(&switchwin, true); - } - } - emsg_off--; - - if (!done && argvars[off + 2].v_type != VAR_UNKNOWN) { - // use the default return value - tv_copy(&argvars[off + 2], rettv); - } -} - /// This function is used by f_input() and f_inputdialog() functions. The third /// argument to f_input() specifies the type of completion to use at the /// prompt. The third argument to f_inputdialog() specifies the value to return @@ -6677,58 +5592,6 @@ void get_user_input(const typval_T *const argvars, typval_T *const rettv, const cmd_silent = cmd_silent_save; } -/// Turn a dictionary into a list -/// -/// @param[in] tv Dictionary to convert. Is checked for actually being -/// a dictionary, will give an error if not. -/// @param[out] rettv Location where result will be saved. -/// @param[in] what What to save in rettv. -void dict_list(typval_T *const tv, typval_T *const rettv, const DictListType what) -{ - if (tv->v_type != VAR_DICT) { - emsg(_(e_dictreq)); - return; - } - if (tv->vval.v_dict == NULL) { - return; - } - - tv_list_alloc_ret(rettv, tv_dict_len(tv->vval.v_dict)); - - TV_DICT_ITER(tv->vval.v_dict, di, { - typval_T tv_item = { .v_lock = VAR_UNLOCKED }; - - switch (what) { - case kDictListKeys: - tv_item.v_type = VAR_STRING; - tv_item.vval.v_string = (char *)vim_strsave(di->di_key); - break; - case kDictListValues: - tv_copy(&di->di_tv, &tv_item); - break; - case kDictListItems: { - // items() - list_T *const sub_l = tv_list_alloc(2); - tv_item.v_type = VAR_LIST; - tv_item.vval.v_list = sub_l; - tv_list_ref(sub_l); - - tv_list_append_owned_tv(sub_l, (typval_T) { - .v_type = VAR_STRING, - .v_lock = VAR_UNLOCKED, - .vval.v_string = xstrdup((const char *)di->di_key), - }); - - tv_list_append_tv(sub_l, &di->di_tv); - - break; - } - } - - tv_list_append_owned_tv(rettv->vval.v_list, tv_item); - }); -} - /// Builds a process argument vector from a VimL object (typval_T). /// /// @param[in] cmd_tv VimL object @@ -6940,56 +5803,6 @@ void set_buffer_lines(buf_T *buf, linenr_T lnum_arg, bool append, const typval_T } } -/* - * "setwinvar()" and "settabwinvar()" functions - */ - -void setwinvar(typval_T *argvars, typval_T *rettv, int off) -{ - if (check_secure()) { - return; - } - - tabpage_T *tp = NULL; - if (off == 1) { - tp = find_tabpage((int)tv_get_number_chk(&argvars[0], NULL)); - } else { - tp = curtab; - } - win_T *const win = find_win_by_nr(&argvars[off], tp); - const char *varname = tv_get_string_chk(&argvars[off + 1]); - typval_T *varp = &argvars[off + 2]; - - if (win != NULL && varname != NULL && varp != NULL) { - bool need_switch_win = tp != curtab || win != curwin; - switchwin_T switchwin; - if (!need_switch_win || switch_win(&switchwin, win, tp, true) == OK) { - if (*varname == '&') { - long numval; - bool error = false; - - varname++; - numval = tv_get_number_chk(varp, &error); - char nbuf[NUMBUFLEN]; - const char *const strval = tv_get_string_buf_chk(varp, nbuf); - if (!error && strval != NULL) { - set_option_value(varname, numval, strval, OPT_LOCAL); - } - } else { - const size_t varname_len = strlen(varname); - char *const winvarname = xmalloc(varname_len + 3); - memcpy(winvarname, "w:", 2); - memcpy(winvarname + 2, varname, varname_len + 1); - set_var(winvarname, varname_len + 2, varp, true); - xfree(winvarname); - } - } - if (need_switch_win) { - restore_win(&switchwin, true); - } - } -} - /// "stdpath()" helper for list results void get_xdg_var_list(const XDGVarType xdg, typval_T *rettv) FUNC_ATTR_NONNULL_ALL @@ -7877,7 +6690,7 @@ int list2fpos(typval_T *arg, pos_T *posp, int *fnump, colnr_T *curswantp, bool c /// Advance "arg" to the first character after the name. /// /// @return 0 for error. -static int get_env_len(const char **arg) +int get_env_len(const char **arg) { int len; @@ -8410,41 +7223,6 @@ char *set_cmdarg(exarg_T *eap, char *oldarg) return oldval; } -/// Get the value of internal variable "name". -/// Return OK or FAIL. If OK is returned "rettv" must be cleared. -/// -/// @param len length of "name" -/// @param rettv NULL when only checking existence -/// @param dip non-NULL when typval's dict item is needed -/// @param verbose may give error message -/// @param no_autoload do not use script autoloading -int get_var_tv(const char *name, int len, typval_T *rettv, dictitem_T **dip, int verbose, - int no_autoload) -{ - int ret = OK; - typval_T *tv = NULL; - dictitem_T *v; - - v = find_var(name, (size_t)len, NULL, no_autoload); - if (v != NULL) { - tv = &v->di_tv; - if (dip != NULL) { - *dip = v; - } - } - - if (tv == NULL) { - if (rettv != NULL && verbose) { - semsg(_("E121: Undefined variable: %.*s"), len, name); - } - ret = FAIL; - } else if (rettv != NULL) { - tv_copy(tv, rettv); - } - - return ret; -} - /// Check if variable "name[len]" is a local variable or an argument. /// If so, "*eval_lavars_used" is set to true. static void check_vars(const char *name, size_t len) @@ -8710,8 +7488,8 @@ dictitem_T *find_var_in_ht(hashtab_T *const ht, int htname, const char *const va /// @param[out] d Scope dictionary. /// /// @return Scope hashtab, NULL if name is not valid. -static hashtab_T *find_var_ht_dict(const char *name, const size_t name_len, const char **varname, - dict_T **d) +hashtab_T *find_var_ht_dict(const char *name, const size_t name_len, const char **varname, + dict_T **d) { hashitem_T *hi; funccall_T *funccal = get_funccal(); @@ -8814,21 +7592,6 @@ hashtab_T *find_var_ht(const char *name, const size_t name_len, const char **var return find_var_ht_dict(name, name_len, varname, &d); } -/// @return the string value of a (global/local) variable or -/// NULL when it doesn't exist. -/// -/// @see tv_get_string() for how long the pointer remains valid. -char_u *get_var_value(const char *const name) -{ - dictitem_T *v; - - v = find_var(name, strlen(name), NULL, false); - if (v == NULL) { - return NULL; - } - return (char_u *)tv_get_string(&v->di_tv); -} - /// Allocate a new hashtab for a sourced script. It will be used while /// sourcing this script and when executing functions defined in the script. void new_script_vars(scid_T id) @@ -8884,391 +7647,6 @@ void unref_var_dict(dict_T *dict) tv_dict_unref(dict); } -/// Clean up a list of internal variables. -/// Frees all allocated variables and the value they contain. -/// Clears hashtab "ht", does not free it. -void vars_clear(hashtab_T *ht) -{ - vars_clear_ext(ht, TRUE); -} - -/// Like vars_clear(), but only free the value if "free_val" is TRUE. -void vars_clear_ext(hashtab_T *ht, int free_val) -{ - int todo; - hashitem_T *hi; - dictitem_T *v; - - hash_lock(ht); - todo = (int)ht->ht_used; - for (hi = ht->ht_array; todo > 0; ++hi) { - if (!HASHITEM_EMPTY(hi)) { - --todo; - - // Free the variable. Don't remove it from the hashtab, - // ht_array might change then. hash_clear() takes care of it - // later. - v = TV_DICT_HI2DI(hi); - if (free_val) { - tv_clear(&v->di_tv); - } - if (v->di_flags & DI_FLAGS_ALLOC) { - xfree(v); - } - } - } - hash_clear(ht); - ht->ht_used = 0; -} - -/// Delete a variable from hashtab "ht" at item "hi". -/// Clear the variable value and free the dictitem. -static void delete_var(hashtab_T *ht, hashitem_T *hi) -{ - dictitem_T *di = TV_DICT_HI2DI(hi); - - hash_remove(ht, hi); - tv_clear(&di->di_tv); - xfree(di); -} - -/// List the value of one internal variable. -static void list_one_var(dictitem_T *v, const char *prefix, int *first) -{ - char *const s = encode_tv2echo(&v->di_tv, NULL); - list_one_var_a(prefix, (const char *)v->di_key, (ptrdiff_t)STRLEN(v->di_key), - v->di_tv.v_type, (s == NULL ? "" : s), first); - xfree(s); -} - -/// @param[in] name_len Length of the name. May be -1, in this case strlen() -/// will be used. -/// @param[in,out] first When true clear rest of screen and set to false. -static void list_one_var_a(const char *prefix, const char *name, const ptrdiff_t name_len, - const VarType type, const char *string, int *first) -{ - // don't use msg() or msg_attr() to avoid overwriting "v:statusmsg" - msg_start(); - msg_puts(prefix); - if (name != NULL) { // "a:" vars don't have a name stored - msg_puts_attr_len(name, name_len, 0); - } - msg_putchar(' '); - msg_advance(22); - if (type == VAR_NUMBER) { - msg_putchar('#'); - } else if (type == VAR_FUNC || type == VAR_PARTIAL) { - msg_putchar('*'); - } else if (type == VAR_LIST) { - msg_putchar('['); - if (*string == '[') { - ++string; - } - } else if (type == VAR_DICT) { - msg_putchar('{'); - if (*string == '{') { - ++string; - } - } else { - msg_putchar(' '); - } - - msg_outtrans((char *)string); - - if (type == VAR_FUNC || type == VAR_PARTIAL) { - msg_puts("()"); - } - if (*first) { - msg_clr_eos(); - *first = FALSE; - } -} - -/// Set variable to the given value -/// -/// If the variable already exists, the value is updated. Otherwise the variable -/// is created. -/// -/// @param[in] name Variable name to set. -/// @param[in] name_len Length of the variable name. -/// @param tv Variable value. -/// @param[in] copy True if value in tv is to be copied. -void set_var(const char *name, const size_t name_len, typval_T *const tv, const bool copy) - FUNC_ATTR_NONNULL_ALL -{ - set_var_const(name, name_len, tv, copy, false); -} - -/// Set variable to the given value -/// -/// If the variable already exists, the value is updated. Otherwise the variable -/// is created. -/// -/// @param[in] name Variable name to set. -/// @param[in] name_len Length of the variable name. -/// @param tv Variable value. -/// @param[in] copy True if value in tv is to be copied. -/// @param[in] is_const True if value in tv is to be locked. -static void set_var_const(const char *name, const size_t name_len, typval_T *const tv, - const bool copy, const bool is_const) - FUNC_ATTR_NONNULL_ALL -{ - dictitem_T *v; - hashtab_T *ht; - dict_T *dict; - - const char *varname; - ht = find_var_ht_dict(name, name_len, &varname, &dict); - const bool watched = tv_dict_is_watched(dict); - - if (ht == NULL || *varname == NUL) { - semsg(_(e_illvar), name); - return; - } - v = find_var_in_ht(ht, 0, varname, name_len - (size_t)(varname - name), true); - - // Search in parent scope which is possible to reference from lambda - if (v == NULL) { - v = find_var_in_scoped_ht(name, name_len, true); - } - - if (tv_is_func(*tv) && !var_check_func_name(name, v == NULL)) { - return; - } - - typval_T oldtv = TV_INITIAL_VALUE; - if (v != NULL) { - if (is_const) { - emsg(_(e_cannot_mod)); - return; - } - - // existing variable, need to clear the value - if (var_check_ro(v->di_flags, name, name_len) - || var_check_lock(v->di_tv.v_lock, name, name_len)) { - return; - } - - // Handle setting internal v: variables separately where needed to - // prevent changing the type. - if (ht == &vimvarht) { - if (v->di_tv.v_type == VAR_STRING) { - XFREE_CLEAR(v->di_tv.vval.v_string); - if (copy || tv->v_type != VAR_STRING) { - const char *const val = tv_get_string(tv); - - // Careful: when assigning to v:errmsg and tv_get_string() - // causes an error message the variable will already be set. - if (v->di_tv.vval.v_string == NULL) { - v->di_tv.vval.v_string = xstrdup(val); - } - } else { - // Take over the string to avoid an extra alloc/free. - v->di_tv.vval.v_string = tv->vval.v_string; - tv->vval.v_string = NULL; - } - return; - } else if (v->di_tv.v_type == VAR_NUMBER) { - v->di_tv.vval.v_number = tv_get_number(tv); - if (strcmp(varname, "searchforward") == 0) { - set_search_direction(v->di_tv.vval.v_number ? '/' : '?'); - } else if (strcmp(varname, "hlsearch") == 0) { - no_hlsearch = !v->di_tv.vval.v_number; - redraw_all_later(SOME_VALID); - } - return; - } else if (v->di_tv.v_type != tv->v_type) { - semsg(_("E963: setting %s to value with wrong type"), name); - return; - } - } - - if (watched) { - tv_copy(&v->di_tv, &oldtv); - } - tv_clear(&v->di_tv); - } else { // Add a new variable. - // Can't add "v:" or "a:" variable. - if (ht == &vimvarht || ht == get_funccal_args_ht()) { - semsg(_(e_illvar), name); - return; - } - - // Make sure the variable name is valid. - if (!valid_varname(varname)) { - return; - } - - // Make sure dict is valid - assert(dict != NULL); - - v = xmalloc(sizeof(dictitem_T) + strlen(varname)); - STRCPY(v->di_key, varname); - if (tv_dict_add(dict, v) == FAIL) { - xfree(v); - return; - } - v->di_flags = DI_FLAGS_ALLOC; - if (is_const) { - v->di_flags |= DI_FLAGS_LOCK; - } - } - - if (copy || tv->v_type == VAR_NUMBER || tv->v_type == VAR_FLOAT) { - tv_copy(tv, &v->di_tv); - } else { - v->di_tv = *tv; - v->di_tv.v_lock = VAR_UNLOCKED; - tv_init(tv); - } - - if (watched) { - if (oldtv.v_type == VAR_UNKNOWN) { - tv_dict_watcher_notify(dict, (char *)v->di_key, &v->di_tv, NULL); - } else { - tv_dict_watcher_notify(dict, (char *)v->di_key, &v->di_tv, &oldtv); - tv_clear(&oldtv); - } - } - - if (is_const) { - // Like :lockvar! name: lock the value and what it contains, but only - // if the reference count is up to one. That locks only literal - // values. - tv_item_lock(&v->di_tv, DICT_MAXNEST, true, true); - } -} - -/// Check whether variable is read-only (DI_FLAGS_RO, DI_FLAGS_RO_SBX) -/// -/// Also gives an error message. -/// -/// @param[in] flags di_flags attribute value. -/// @param[in] name Variable name, for use in error message. -/// @param[in] name_len Variable name length. Use #TV_TRANSLATE to translate -/// variable name and compute the length. Use #TV_CSTRING -/// to compute the length with strlen() without -/// translating. -/// -/// Both #TV_… values are used for optimization purposes: -/// variable name with its length is needed only in case -/// of error, when no error occurs computing them is -/// a waste of CPU resources. This especially applies to -/// gettext. -/// -/// @return True if variable is read-only: either always or in sandbox when -/// sandbox is enabled, false otherwise. -bool var_check_ro(const int flags, const char *name, size_t name_len) - FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL -{ - const char *error_message = NULL; - if (flags & DI_FLAGS_RO) { - error_message = _(e_readonlyvar); - } else if ((flags & DI_FLAGS_RO_SBX) && sandbox) { - error_message = N_("E794: Cannot set variable in the sandbox: \"%.*s\""); - } - - if (error_message == NULL) { - return false; - } - if (name_len == TV_TRANSLATE) { - name = _(name); - name_len = strlen(name); - } else if (name_len == TV_CSTRING) { - name_len = strlen(name); - } - - semsg(_(error_message), (int)name_len, name); - - return true; -} - -/// Check whether variable is fixed (DI_FLAGS_FIX) -/// -/// Also gives an error message. -/// -/// @param[in] flags di_flags attribute value. -/// @param[in] name Variable name, for use in error message. -/// @param[in] name_len Variable name length. Use #TV_TRANSLATE to translate -/// variable name and compute the length. Use #TV_CSTRING -/// to compute the length with strlen() without -/// translating. -/// -/// Both #TV_… values are used for optimization purposes: -/// variable name with its length is needed only in case -/// of error, when no error occurs computing them is -/// a waste of CPU resources. This especially applies to -/// gettext. -/// -/// @return True if variable is fixed, false otherwise. -bool var_check_fixed(const int flags, const char *name, size_t name_len) - FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL -{ - if (flags & DI_FLAGS_FIX) { - if (name_len == TV_TRANSLATE) { - name = _(name); - name_len = strlen(name); - } else if (name_len == TV_CSTRING) { - name_len = strlen(name); - } - semsg(_("E795: Cannot delete variable %.*s"), (int)name_len, name); - return true; - } - return false; -} - -// TODO(ZyX-I): move to eval/expressions - -/// Check if name is a valid name to assign funcref to -/// -/// @param[in] name Possible function/funcref name. -/// @param[in] new_var True if it is a name for a variable. -/// -/// @return false in case of error, true in case of success. Also gives an -/// error message if appropriate. -bool var_check_func_name(const char *const name, const bool new_var) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT -{ - // Allow for w: b: s: and t:. - if (!(vim_strchr("wbst", name[0]) != NULL && name[1] == ':') - && !ASCII_ISUPPER((name[0] != NUL && name[1] == ':') - ? name[2] : name[0])) { - semsg(_("E704: Funcref variable name must start with a capital: %s"), name); - return false; - } - // Don't allow hiding a function. When "v" is not NULL we might be - // assigning another function to the same var, the type is checked - // below. - if (new_var && function_exists(name, false)) { - semsg(_("E705: Variable name conflicts with existing function: %s"), - name); - return false; - } - return true; -} - -// TODO(ZyX-I): move to eval/expressions - -/// Check if a variable name is valid -/// -/// @param[in] varname Variable name to check. -/// -/// @return false when variable name is not valid, true when it is. Also gives -/// an error message if appropriate. -bool valid_varname(const char *varname) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT -{ - for (const char *p = varname; *p != NUL; p++) { - if (!eval_isnamec1((int)(uint8_t)(*p)) - && (p == varname || !ascii_isdigit(*p)) - && *p != AUTOLOAD_CHAR) { - semsg(_(e_illvar), varname); - return false; - } - } - return true; -} - /// Make a copy of an item /// /// Lists and Dictionaries are also copied. @@ -9533,7 +7911,7 @@ void ex_execute(exarg_T *eap) /// /// @return NULL when no option name found. Otherwise pointer to the char /// after the option name. -static const char *find_option_end(const char **const arg, int *const opt_flags) +const char *find_option_end(const char **const arg, int *const opt_flags) { const char *p = *arg; @@ -10895,35 +9273,3 @@ char *typval_tostring(typval_T *arg) } return encode_tv2string(arg, NULL); } - -bool var_exists(const char *var) - FUNC_ATTR_NONNULL_ALL -{ - char *tofree; - bool n = false; - - // get_name_len() takes care of expanding curly braces - const char *name = var; - const int len = get_name_len(&var, &tofree, true, false); - if (len > 0) { - typval_T tv; - - if (tofree != NULL) { - name = tofree; - } - n = get_var_tv(name, len, &tv, NULL, false, true) == OK; - if (n) { - // Handle d.key, l[idx], f(expr). - n = handle_subscript(&var, &tv, true, false, name, &name) == OK; - if (n) { - tv_clear(&tv); - } - } - } - if (*var != NUL) { - n = false; - } - - xfree(tofree); - return n; -} diff --git a/src/nvim/eval.c.orig b/src/nvim/eval.c.orig new file mode 100644 index 0000000000..9bfb2cd7d6 --- /dev/null +++ b/src/nvim/eval.c.orig @@ -0,0 +1,9995 @@ +// This is an open source non-commercial project. Dear PVS-Studio, please check +// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com + +/* + * eval.c: Expression evaluation. + */ + +#include <math.h> +#include <stdlib.h> + +#include "auto/config.h" + +#ifdef HAVE_LOCALE_H +# include <locale.h> +#endif + +#include "nvim/ascii.h" +#include "nvim/buffer.h" +#include "nvim/change.h" +#include "nvim/channel.h" +#include "nvim/charset.h" +#include "nvim/cursor.h" +#include "nvim/edit.h" +#include "nvim/eval.h" +#include "nvim/eval/encode.h" +#include "nvim/eval/executor.h" +#include "nvim/eval/gc.h" +#include "nvim/eval/typval.h" +#include "nvim/eval/userfunc.h" +#include "nvim/eval/vars.h" +#include "nvim/ex_cmds2.h" +#include "nvim/ex_getln.h" +#include "nvim/ex_session.h" +#include "nvim/fileio.h" +#include "nvim/getchar.h" +#include "nvim/highlight_group.h" +#include "nvim/lua/executor.h" +#include "nvim/mark.h" +#include "nvim/memline.h" +#include "nvim/move.h" +#include "nvim/ops.h" +#include "nvim/option.h" +#include "nvim/os/input.h" +#include "nvim/os/shell.h" +#include "nvim/path.h" +#include "nvim/quickfix.h" +#include "nvim/regexp.h" +#include "nvim/screen.h" +#include "nvim/search.h" +#include "nvim/sign.h" +#include "nvim/syntax.h" +#include "nvim/ui.h" +#include "nvim/ui_compositor.h" +#include "nvim/undo.h" +#include "nvim/version.h" +#include "nvim/window.h" + +// TODO(ZyX-I): Remove DICT_MAXNEST, make users be non-recursive instead + +#define DICT_MAXNEST 100 // maximum nesting of lists and dicts + +static char *e_missbrac = N_("E111: Missing ']'"); +static char *e_dictrange = N_("E719: Cannot use [:] with a Dictionary"); +static char *e_nowhitespace + = N_("E274: No white space allowed before parenthesis"); +static char *e_write2 = N_("E80: Error while writing: %s"); +static char *e_string_list_or_blob_required = N_("E1098: String, List or Blob required"); + +static char * const namespace_char = "abglstvw"; + +/// Variable used for g: +static ScopeDictDictItem globvars_var; + +/* + * Old Vim variables such as "v:version" are also available without the "v:". + * Also in functions. We need a special hashtable for them. + */ +static hashtab_T compat_hashtab; + +/// Used for checking if local variables or arguments used in a lambda. +bool *eval_lavars_used = NULL; + +/* + * Array to hold the hashtab with variables local to each sourced script. + * Each item holds a variable (nameless) that points to the dict_T. + */ +typedef struct { + ScopeDictDictItem sv_var; + dict_T sv_dict; +} scriptvar_T; + +static garray_T ga_scripts = { 0, 0, sizeof(scriptvar_T *), 4, NULL }; +#define SCRIPT_SV(id) (((scriptvar_T **)ga_scripts.ga_data)[(id) - 1]) +#define SCRIPT_VARS(id) (SCRIPT_SV(id)->sv_dict.dv_hashtab) + +static int echo_attr = 0; // attributes used for ":echo" + +// The names of packages that once were loaded are remembered. +static garray_T ga_loaded = { 0, 0, sizeof(char *), 4, NULL }; + +/* + * Info used by a ":for" loop. + */ +typedef struct { + int fi_semicolon; // TRUE if ending in '; var]' + int fi_varcount; // nr of variables in the list + listwatch_T fi_lw; // keep an eye on the item used. + list_T *fi_list; // list being used + int fi_bi; // index of blob + blob_T *fi_blob; // blob being used + char *fi_string; // copy of string being used + int fi_byte_idx; // byte index in fi_string +} forinfo_T; + +// values for vv_flags: +#define VV_COMPAT 1 // compatible, also used without "v:" +#define VV_RO 2 // read-only +#define VV_RO_SBX 4 // read-only in the sandbox + +#define VV(idx, name, type, flags) \ + [idx] = { \ + .vv_name = (name), \ + .vv_di = { \ + .di_tv = { .v_type = (type) }, \ + .di_flags = 0, \ + .di_key = { 0 }, \ + }, \ + .vv_flags = (flags), \ + } + +#define VIMVAR_KEY_LEN 16 // Maximum length of the key of v:variables + +// Array to hold the value of v: variables. +// The value is in a dictitem, so that it can also be used in the v: scope. +// The reason to use this table anyway is for very quick access to the +// variables with the VV_ defines. +static struct vimvar { + char *vv_name; ///< Name of the variable, without v:. + TV_DICTITEM_STRUCT(VIMVAR_KEY_LEN + 1) vv_di; ///< Value and name for key (max 16 chars). + char vv_flags; ///< Flags: #VV_COMPAT, #VV_RO, #VV_RO_SBX. +} vimvars[] = +{ + // VV_ tails differing from upcased string literals: + // VV_CC_FROM "charconvert_from" + // VV_CC_TO "charconvert_to" + // VV_SEND_SERVER "servername" + // VV_REG "register" + // VV_OP "operator" + VV(VV_COUNT, "count", VAR_NUMBER, VV_RO), + VV(VV_COUNT1, "count1", VAR_NUMBER, VV_RO), + VV(VV_PREVCOUNT, "prevcount", VAR_NUMBER, VV_RO), + VV(VV_ERRMSG, "errmsg", VAR_STRING, 0), + VV(VV_WARNINGMSG, "warningmsg", VAR_STRING, 0), + VV(VV_STATUSMSG, "statusmsg", VAR_STRING, 0), + VV(VV_SHELL_ERROR, "shell_error", VAR_NUMBER, VV_RO), + VV(VV_THIS_SESSION, "this_session", VAR_STRING, 0), + VV(VV_VERSION, "version", VAR_NUMBER, VV_COMPAT + VV_RO), + VV(VV_LNUM, "lnum", VAR_NUMBER, VV_RO_SBX), + VV(VV_TERMRESPONSE, "termresponse", VAR_STRING, VV_RO), + VV(VV_FNAME, "fname", VAR_STRING, VV_RO), + VV(VV_LANG, "lang", VAR_STRING, VV_RO), + VV(VV_LC_TIME, "lc_time", VAR_STRING, VV_RO), + VV(VV_CTYPE, "ctype", VAR_STRING, VV_RO), + VV(VV_CC_FROM, "charconvert_from", VAR_STRING, VV_RO), + VV(VV_CC_TO, "charconvert_to", VAR_STRING, VV_RO), + VV(VV_FNAME_IN, "fname_in", VAR_STRING, VV_RO), + VV(VV_FNAME_OUT, "fname_out", VAR_STRING, VV_RO), + VV(VV_FNAME_NEW, "fname_new", VAR_STRING, VV_RO), + VV(VV_FNAME_DIFF, "fname_diff", VAR_STRING, VV_RO), + VV(VV_CMDARG, "cmdarg", VAR_STRING, VV_RO), + VV(VV_FOLDSTART, "foldstart", VAR_NUMBER, VV_RO_SBX), + VV(VV_FOLDEND, "foldend", VAR_NUMBER, VV_RO_SBX), + VV(VV_FOLDDASHES, "folddashes", VAR_STRING, VV_RO_SBX), + VV(VV_FOLDLEVEL, "foldlevel", VAR_NUMBER, VV_RO_SBX), + VV(VV_PROGNAME, "progname", VAR_STRING, VV_RO), + VV(VV_SEND_SERVER, "servername", VAR_STRING, VV_RO), + VV(VV_DYING, "dying", VAR_NUMBER, VV_RO), + VV(VV_EXCEPTION, "exception", VAR_STRING, VV_RO), + VV(VV_THROWPOINT, "throwpoint", VAR_STRING, VV_RO), + VV(VV_REG, "register", VAR_STRING, VV_RO), + VV(VV_CMDBANG, "cmdbang", VAR_NUMBER, VV_RO), + VV(VV_INSERTMODE, "insertmode", VAR_STRING, VV_RO), + VV(VV_VAL, "val", VAR_UNKNOWN, VV_RO), + VV(VV_KEY, "key", VAR_UNKNOWN, VV_RO), + VV(VV_PROFILING, "profiling", VAR_NUMBER, VV_RO), + VV(VV_FCS_REASON, "fcs_reason", VAR_STRING, VV_RO), + VV(VV_FCS_CHOICE, "fcs_choice", VAR_STRING, 0), + VV(VV_BEVAL_BUFNR, "beval_bufnr", VAR_NUMBER, VV_RO), + VV(VV_BEVAL_WINNR, "beval_winnr", VAR_NUMBER, VV_RO), + VV(VV_BEVAL_WINID, "beval_winid", VAR_NUMBER, VV_RO), + VV(VV_BEVAL_LNUM, "beval_lnum", VAR_NUMBER, VV_RO), + VV(VV_BEVAL_COL, "beval_col", VAR_NUMBER, VV_RO), + VV(VV_BEVAL_TEXT, "beval_text", VAR_STRING, VV_RO), + VV(VV_SCROLLSTART, "scrollstart", VAR_STRING, 0), + VV(VV_SWAPNAME, "swapname", VAR_STRING, VV_RO), + VV(VV_SWAPCHOICE, "swapchoice", VAR_STRING, 0), + VV(VV_SWAPCOMMAND, "swapcommand", VAR_STRING, VV_RO), + VV(VV_CHAR, "char", VAR_STRING, 0), + VV(VV_MOUSE_WIN, "mouse_win", VAR_NUMBER, 0), + VV(VV_MOUSE_WINID, "mouse_winid", VAR_NUMBER, 0), + VV(VV_MOUSE_LNUM, "mouse_lnum", VAR_NUMBER, 0), + VV(VV_MOUSE_COL, "mouse_col", VAR_NUMBER, 0), + VV(VV_OP, "operator", VAR_STRING, VV_RO), + VV(VV_SEARCHFORWARD, "searchforward", VAR_NUMBER, 0), + VV(VV_HLSEARCH, "hlsearch", VAR_NUMBER, 0), + VV(VV_OLDFILES, "oldfiles", VAR_LIST, 0), + VV(VV_WINDOWID, "windowid", VAR_NUMBER, VV_RO_SBX), + VV(VV_PROGPATH, "progpath", VAR_STRING, VV_RO), + VV(VV_COMPLETED_ITEM, "completed_item", VAR_DICT, 0), + VV(VV_OPTION_NEW, "option_new", VAR_STRING, VV_RO), + VV(VV_OPTION_OLD, "option_old", VAR_STRING, VV_RO), + VV(VV_OPTION_OLDLOCAL, "option_oldlocal", VAR_STRING, VV_RO), + VV(VV_OPTION_OLDGLOBAL, "option_oldglobal", VAR_STRING, VV_RO), + VV(VV_OPTION_COMMAND, "option_command", VAR_STRING, VV_RO), + VV(VV_OPTION_TYPE, "option_type", VAR_STRING, VV_RO), + VV(VV_ERRORS, "errors", VAR_LIST, 0), + VV(VV_FALSE, "false", VAR_BOOL, VV_RO), + VV(VV_TRUE, "true", VAR_BOOL, VV_RO), + VV(VV_NULL, "null", VAR_SPECIAL, VV_RO), + VV(VV_NUMBERMAX, "numbermax", VAR_NUMBER, VV_RO), + VV(VV_NUMBERMIN, "numbermin", VAR_NUMBER, VV_RO), + VV(VV_NUMBERSIZE, "numbersize", VAR_NUMBER, VV_RO), + VV(VV_VIM_DID_ENTER, "vim_did_enter", VAR_NUMBER, VV_RO), + VV(VV_TESTING, "testing", VAR_NUMBER, 0), + VV(VV_TYPE_NUMBER, "t_number", VAR_NUMBER, VV_RO), + VV(VV_TYPE_STRING, "t_string", VAR_NUMBER, VV_RO), + VV(VV_TYPE_FUNC, "t_func", VAR_NUMBER, VV_RO), + VV(VV_TYPE_LIST, "t_list", VAR_NUMBER, VV_RO), + VV(VV_TYPE_DICT, "t_dict", VAR_NUMBER, VV_RO), + VV(VV_TYPE_FLOAT, "t_float", VAR_NUMBER, VV_RO), + VV(VV_TYPE_BOOL, "t_bool", VAR_NUMBER, VV_RO), + VV(VV_TYPE_BLOB, "t_blob", VAR_NUMBER, VV_RO), + VV(VV_EVENT, "event", VAR_DICT, VV_RO), + VV(VV_ECHOSPACE, "echospace", VAR_NUMBER, VV_RO), + VV(VV_ARGV, "argv", VAR_LIST, VV_RO), + VV(VV_COLLATE, "collate", VAR_STRING, VV_RO), + VV(VV_EXITING, "exiting", VAR_NUMBER, VV_RO), + // Neovim + VV(VV_STDERR, "stderr", VAR_NUMBER, VV_RO), + VV(VV_MSGPACK_TYPES, "msgpack_types", VAR_DICT, VV_RO), + VV(VV__NULL_STRING, "_null_string", VAR_STRING, VV_RO), + VV(VV__NULL_LIST, "_null_list", VAR_LIST, VV_RO), + VV(VV__NULL_DICT, "_null_dict", VAR_DICT, VV_RO), + VV(VV__NULL_BLOB, "_null_blob", VAR_BLOB, VV_RO), + VV(VV_LUA, "lua", VAR_PARTIAL, VV_RO), +}; +#undef VV + +// shorthand +#define vv_type vv_di.di_tv.v_type +#define vv_nr vv_di.di_tv.vval.v_number +#define vv_bool vv_di.di_tv.vval.v_bool +#define vv_special vv_di.di_tv.vval.v_special +#define vv_float vv_di.di_tv.vval.v_float +#define vv_str vv_di.di_tv.vval.v_string +#define vv_list vv_di.di_tv.vval.v_list +#define vv_dict vv_di.di_tv.vval.v_dict +#define vv_blob vv_di.di_tv.vval.v_blob +#define vv_partial vv_di.di_tv.vval.v_partial +#define vv_tv vv_di.di_tv + +/// Variable used for v: +static ScopeDictDictItem vimvars_var; + +static partial_T *vvlua_partial; + +/// v: hashtab +#define vimvarht vimvardict.dv_hashtab + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "eval.c.generated.h" +#endif + +static uint64_t last_timer_id = 1; +static PMap(uint64_t) timers = MAP_INIT; + +static const char *const msgpack_type_names[] = { + [kMPNil] = "nil", + [kMPBoolean] = "boolean", + [kMPInteger] = "integer", + [kMPFloat] = "float", + [kMPString] = "string", + [kMPBinary] = "binary", + [kMPArray] = "array", + [kMPMap] = "map", + [kMPExt] = "ext", +}; +const list_T *eval_msgpack_type_lists[] = { + [kMPNil] = NULL, + [kMPBoolean] = NULL, + [kMPInteger] = NULL, + [kMPFloat] = NULL, + [kMPString] = NULL, + [kMPBinary] = NULL, + [kMPArray] = NULL, + [kMPMap] = NULL, + [kMPExt] = NULL, +}; + +dict_T *get_v_event(save_v_event_T *sve) +{ + dict_T *v_event = get_vim_var_dict(VV_EVENT); + + if (v_event->dv_hashtab.ht_used > 0) { + // recursive use of v:event, save, make empty and restore later + sve->sve_did_save = true; + sve->sve_hashtab = v_event->dv_hashtab; + hash_init(&v_event->dv_hashtab); + } else { + sve->sve_did_save = false; + } + return v_event; +} + +void restore_v_event(dict_T *v_event, save_v_event_T *sve) +{ + tv_dict_free_contents(v_event); + if (sve->sve_did_save) { + v_event->dv_hashtab = sve->sve_hashtab; + } else { + hash_init(&v_event->dv_hashtab); + } +} + +/// @return "n1" divided by "n2", taking care of dividing by zero. +varnumber_T num_divide(varnumber_T n1, varnumber_T n2) + FUNC_ATTR_CONST FUNC_ATTR_WARN_UNUSED_RESULT +{ + varnumber_T result; + + if (n2 == 0) { // give an error message? + if (n1 == 0) { + result = VARNUMBER_MIN; // similar to NaN + } else if (n1 < 0) { + result = -VARNUMBER_MAX; + } else { + result = VARNUMBER_MAX; + } + } else { + result = n1 / n2; + } + + return result; +} + +/// @return "n1" modulus "n2", taking care of dividing by zero. +varnumber_T num_modulus(varnumber_T n1, varnumber_T n2) + FUNC_ATTR_CONST FUNC_ATTR_WARN_UNUSED_RESULT +{ + // Give an error when n2 is 0? + return (n2 == 0) ? 0 : (n1 % n2); +} + +/// Initialize the global and v: variables. +void eval_init(void) +{ + vimvars[VV_VERSION].vv_nr = VIM_VERSION_100; + + struct vimvar *p; + + init_var_dict(&globvardict, &globvars_var, VAR_DEF_SCOPE); + init_var_dict(&vimvardict, &vimvars_var, VAR_SCOPE); + vimvardict.dv_lock = VAR_FIXED; + hash_init(&compat_hashtab); + func_init(); + + for (size_t i = 0; i < ARRAY_SIZE(vimvars); i++) { + p = &vimvars[i]; + assert(STRLEN(p->vv_name) <= VIMVAR_KEY_LEN); + STRCPY(p->vv_di.di_key, p->vv_name); + if (p->vv_flags & VV_RO) { + p->vv_di.di_flags = DI_FLAGS_RO | DI_FLAGS_FIX; + } else if (p->vv_flags & VV_RO_SBX) { + p->vv_di.di_flags = DI_FLAGS_RO_SBX | DI_FLAGS_FIX; + } else { + p->vv_di.di_flags = DI_FLAGS_FIX; + } + + // add to v: scope dict, unless the value is not always available + if (p->vv_type != VAR_UNKNOWN) { + hash_add(&vimvarht, p->vv_di.di_key); + } + if (p->vv_flags & VV_COMPAT) { + // add to compat scope dict + hash_add(&compat_hashtab, p->vv_di.di_key); + } + } + vimvars[VV_VERSION].vv_nr = VIM_VERSION_100; + + dict_T *const msgpack_types_dict = tv_dict_alloc(); + for (size_t i = 0; i < ARRAY_SIZE(msgpack_type_names); i++) { + list_T *const type_list = tv_list_alloc(0); + tv_list_set_lock(type_list, VAR_FIXED); + tv_list_ref(type_list); + dictitem_T *const di = tv_dict_item_alloc(msgpack_type_names[i]); + di->di_flags |= DI_FLAGS_RO|DI_FLAGS_FIX; + di->di_tv = (typval_T) { + .v_type = VAR_LIST, + .vval = { .v_list = type_list, }, + }; + eval_msgpack_type_lists[i] = type_list; + if (tv_dict_add(msgpack_types_dict, di) == FAIL) { + // There must not be duplicate items in this dictionary by definition. + abort(); + } + } + msgpack_types_dict->dv_lock = VAR_FIXED; + + set_vim_var_dict(VV_MSGPACK_TYPES, msgpack_types_dict); + set_vim_var_dict(VV_COMPLETED_ITEM, tv_dict_alloc_lock(VAR_FIXED)); + + set_vim_var_dict(VV_EVENT, tv_dict_alloc_lock(VAR_FIXED)); + set_vim_var_list(VV_ERRORS, tv_list_alloc(kListLenUnknown)); + set_vim_var_nr(VV_STDERR, CHAN_STDERR); + set_vim_var_nr(VV_SEARCHFORWARD, 1L); + set_vim_var_nr(VV_HLSEARCH, 1L); + set_vim_var_nr(VV_COUNT1, 1); + set_vim_var_nr(VV_TYPE_NUMBER, VAR_TYPE_NUMBER); + set_vim_var_nr(VV_TYPE_STRING, VAR_TYPE_STRING); + set_vim_var_nr(VV_TYPE_FUNC, VAR_TYPE_FUNC); + set_vim_var_nr(VV_TYPE_LIST, VAR_TYPE_LIST); + set_vim_var_nr(VV_TYPE_DICT, VAR_TYPE_DICT); + set_vim_var_nr(VV_TYPE_FLOAT, VAR_TYPE_FLOAT); + set_vim_var_nr(VV_TYPE_BOOL, VAR_TYPE_BOOL); + set_vim_var_nr(VV_TYPE_BLOB, VAR_TYPE_BLOB); + + set_vim_var_bool(VV_FALSE, kBoolVarFalse); + set_vim_var_bool(VV_TRUE, kBoolVarTrue); + set_vim_var_special(VV_NULL, kSpecialVarNull); + set_vim_var_nr(VV_NUMBERMAX, VARNUMBER_MAX); + set_vim_var_nr(VV_NUMBERMIN, VARNUMBER_MIN); + set_vim_var_nr(VV_NUMBERSIZE, sizeof(varnumber_T) * 8); + set_vim_var_special(VV_EXITING, kSpecialVarNull); + + set_vim_var_nr(VV_ECHOSPACE, sc_col - 1); + + vimvars[VV_LUA].vv_type = VAR_PARTIAL; + vvlua_partial = xcalloc(1, sizeof(partial_T)); + vimvars[VV_LUA].vv_partial = vvlua_partial; + // this value shouldn't be printed, but if it is, do not crash + vvlua_partial->pt_name = xmallocz(0); + vvlua_partial->pt_refcount++; + + set_reg_var(0); // default for v:register is not 0 but '"' +} + +#if defined(EXITFREE) +void eval_clear(void) +{ + struct vimvar *p; + + for (size_t i = 0; i < ARRAY_SIZE(vimvars); i++) { + p = &vimvars[i]; + if (p->vv_di.di_tv.v_type == VAR_STRING) { + XFREE_CLEAR(p->vv_str); + } else if (p->vv_di.di_tv.v_type == VAR_LIST) { + tv_list_unref(p->vv_list); + p->vv_list = NULL; + } + } + hash_clear(&vimvarht); + hash_init(&vimvarht); // garbage_collect() will access it + hash_clear(&compat_hashtab); + + free_scriptnames(); +# ifdef HAVE_WORKING_LIBINTL + free_locales(); +# endif + + // global variables + vars_clear(&globvarht); + + // autoloaded script names + ga_clear_strings(&ga_loaded); + + /* Script-local variables. First clear all the variables and in a second + * loop free the scriptvar_T, because a variable in one script might hold + * a reference to the whole scope of another script. */ + for (int i = 1; i <= ga_scripts.ga_len; ++i) { + vars_clear(&SCRIPT_VARS(i)); + } + for (int i = 1; i <= ga_scripts.ga_len; ++i) { + xfree(SCRIPT_SV(i)); + } + ga_clear(&ga_scripts); + + // unreferenced lists and dicts + (void)garbage_collect(false); + + // functions not garbage collected + free_all_functions(); +} + +#endif + +/// Set an internal variable to a string value. Creates the variable if it does +/// not already exist. +void set_internal_string_var(const char *name, char *value) + FUNC_ATTR_NONNULL_ARG(1) +{ + typval_T tv = { + .v_type = VAR_STRING, + .vval.v_string = value, + }; + + set_var(name, strlen(name), &tv, true); +} + +static lval_T *redir_lval = NULL; +static garray_T redir_ga; // Only valid when redir_lval is not NULL. +static char *redir_endp = NULL; +static char *redir_varname = NULL; + +/// Start recording command output to a variable +/// +/// @param append append to an existing variable +/// +/// @return OK if successfully completed the setup. FAIL otherwise. +int var_redir_start(char *name, int append) +{ + int save_emsg; + int err; + typval_T tv; + + // Catch a bad name early. + if (!eval_isnamec1(*name)) { + emsg(_(e_invarg)); + return FAIL; + } + + // Make a copy of the name, it is used in redir_lval until redir ends. + redir_varname = xstrdup(name); + + redir_lval = xcalloc(1, sizeof(lval_T)); + + // The output is stored in growarray "redir_ga" until redirection ends. + ga_init(&redir_ga, (int)sizeof(char), 500); + + // Parse the variable name (can be a dict or list entry). + redir_endp = get_lval(redir_varname, NULL, redir_lval, false, false, + 0, FNE_CHECK_START); + if (redir_endp == NULL || redir_lval->ll_name == NULL + || *redir_endp != NUL) { + clear_lval(redir_lval); + if (redir_endp != NULL && *redir_endp != NUL) { + // Trailing characters are present after the variable name + emsg(_(e_trailing)); + } else { + emsg(_(e_invarg)); + } + redir_endp = NULL; // don't store a value, only cleanup + var_redir_stop(); + return FAIL; + } + + /* check if we can write to the variable: set it to or append an empty + * string */ + save_emsg = did_emsg; + did_emsg = FALSE; + tv.v_type = VAR_STRING; + tv.vval.v_string = ""; + if (append) { + set_var_lval(redir_lval, redir_endp, &tv, true, false, "."); + } else { + set_var_lval(redir_lval, redir_endp, &tv, true, false, "="); + } + clear_lval(redir_lval); + err = did_emsg; + did_emsg |= save_emsg; + if (err) { + redir_endp = NULL; // don't store a value, only cleanup + var_redir_stop(); + return FAIL; + } + + return OK; +} + +/// Append "value[value_len]" to the variable set by var_redir_start(). +/// The actual appending is postponed until redirection ends, because the value +/// appended may in fact be the string we write to, changing it may cause freed +/// memory to be used: +/// :redir => foo +/// :let foo +/// :redir END +void var_redir_str(char *value, int value_len) +{ + int len; + + if (redir_lval == NULL) { + return; + } + + if (value_len == -1) { + len = (int)STRLEN(value); // Append the entire string + } else { + len = value_len; // Append only "value_len" characters + } + + ga_grow(&redir_ga, len); + memmove((char *)redir_ga.ga_data + redir_ga.ga_len, value, (size_t)len); + redir_ga.ga_len += len; +} + +/// Stop redirecting command output to a variable. +/// Frees the allocated memory. +void var_redir_stop(void) +{ + typval_T tv; + + if (redir_lval != NULL) { + // If there was no error: assign the text to the variable. + if (redir_endp != NULL) { + ga_append(&redir_ga, NUL); // Append the trailing NUL. + tv.v_type = VAR_STRING; + tv.vval.v_string = redir_ga.ga_data; + // Call get_lval() again, if it's inside a Dict or List it may + // have changed. + redir_endp = get_lval(redir_varname, NULL, redir_lval, + false, false, 0, FNE_CHECK_START); + if (redir_endp != NULL && redir_lval->ll_name != NULL) { + set_var_lval(redir_lval, redir_endp, &tv, false, false, "."); + } + clear_lval(redir_lval); + } + + // free the collected output + XFREE_CLEAR(redir_ga.ga_data); + + XFREE_CLEAR(redir_lval); + } + XFREE_CLEAR(redir_varname); +} + +int eval_charconvert(const char *const enc_from, const char *const enc_to, + const char *const fname_from, const char *const fname_to) +{ + bool err = false; + + set_vim_var_string(VV_CC_FROM, enc_from, -1); + set_vim_var_string(VV_CC_TO, enc_to, -1); + set_vim_var_string(VV_FNAME_IN, fname_from, -1); + set_vim_var_string(VV_FNAME_OUT, fname_to, -1); + if (eval_to_bool((char *)p_ccv, &err, NULL, false)) { + err = true; + } + set_vim_var_string(VV_CC_FROM, NULL, -1); + set_vim_var_string(VV_CC_TO, NULL, -1); + set_vim_var_string(VV_FNAME_IN, NULL, -1); + set_vim_var_string(VV_FNAME_OUT, NULL, -1); + + if (err) { + return FAIL; + } + return OK; +} + +int eval_printexpr(const char *const fname, const char *const args) +{ + bool err = false; + + set_vim_var_string(VV_FNAME_IN, fname, -1); + set_vim_var_string(VV_CMDARG, args, -1); + if (eval_to_bool((char *)p_pexpr, &err, NULL, false)) { + err = true; + } + set_vim_var_string(VV_FNAME_IN, NULL, -1); + set_vim_var_string(VV_CMDARG, NULL, -1); + + if (err) { + os_remove(fname); + return FAIL; + } + return OK; +} + +void eval_diff(const char *const origfile, const char *const newfile, const char *const outfile) +{ + bool err = false; + + set_vim_var_string(VV_FNAME_IN, origfile, -1); + set_vim_var_string(VV_FNAME_NEW, newfile, -1); + set_vim_var_string(VV_FNAME_OUT, outfile, -1); + (void)eval_to_bool((char *)p_dex, &err, NULL, false); + set_vim_var_string(VV_FNAME_IN, NULL, -1); + set_vim_var_string(VV_FNAME_NEW, NULL, -1); + set_vim_var_string(VV_FNAME_OUT, NULL, -1); +} + +void eval_patch(const char *const origfile, const char *const difffile, const char *const outfile) +{ + bool err = false; + + set_vim_var_string(VV_FNAME_IN, origfile, -1); + set_vim_var_string(VV_FNAME_DIFF, difffile, -1); + set_vim_var_string(VV_FNAME_OUT, outfile, -1); + (void)eval_to_bool((char *)p_pex, &err, NULL, false); + set_vim_var_string(VV_FNAME_IN, NULL, -1); + set_vim_var_string(VV_FNAME_DIFF, NULL, -1); + set_vim_var_string(VV_FNAME_OUT, NULL, -1); +} + +/// Top level evaluation function, returning a boolean. +/// Sets "error" to TRUE if there was an error. +/// +/// @param skip only parse, don't execute +/// +/// @return TRUE or FALSE. +int eval_to_bool(char *arg, bool *error, char **nextcmd, int skip) +{ + typval_T tv; + bool retval = false; + + if (skip) { + emsg_skip++; + } + if (eval0(arg, &tv, nextcmd, !skip) == FAIL) { + *error = true; + } else { + *error = false; + if (!skip) { + retval = (tv_get_number_chk(&tv, error) != 0); + tv_clear(&tv); + } + } + if (skip) { + emsg_skip--; + } + + return retval; +} + +/// Call eval1() and give an error message if not done at a lower level. +static int eval1_emsg(char **arg, typval_T *rettv, bool evaluate) + FUNC_ATTR_NONNULL_ARG(1, 2) +{ + const char *const start = *arg; + const int did_emsg_before = did_emsg; + const int called_emsg_before = called_emsg; + + const int ret = eval1(arg, rettv, evaluate); + if (ret == FAIL) { + // Report the invalid expression unless the expression evaluation has + // been cancelled due to an aborting error, an interrupt, or an + // exception, or we already gave a more specific error. + // Also check called_emsg for when using assert_fails(). + if (!aborting() + && did_emsg == did_emsg_before + && called_emsg == called_emsg_before) { + semsg(_(e_invexpr2), start); + } + } + return ret; +} + +/// @return whether a typval is a valid expression to pass to eval_expr_typval() +/// or eval_expr_to_bool(). An empty string returns false; +bool eval_expr_valid_arg(const typval_T *const tv) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_CONST +{ + return tv->v_type != VAR_UNKNOWN + && (tv->v_type != VAR_STRING || (tv->vval.v_string != NULL && *tv->vval.v_string != NUL)); +} + +int eval_expr_typval(const typval_T *expr, typval_T *argv, int argc, typval_T *rettv) + FUNC_ATTR_NONNULL_ARG(1, 2, 4) +{ + funcexe_T funcexe = FUNCEXE_INIT; + + if (expr->v_type == VAR_FUNC) { + const char *const s = expr->vval.v_string; + if (s == NULL || *s == NUL) { + return FAIL; + } + funcexe.evaluate = true; + if (call_func(s, -1, rettv, argc, argv, &funcexe) == FAIL) { + return FAIL; + } + } else if (expr->v_type == VAR_PARTIAL) { + partial_T *const partial = expr->vval.v_partial; + const char *const s = partial_name(partial); + if (s == NULL || *s == NUL) { + return FAIL; + } + funcexe.evaluate = true; + funcexe.partial = partial; + if (call_func(s, -1, rettv, argc, argv, &funcexe) == FAIL) { + return FAIL; + } + } else { + char buf[NUMBUFLEN]; + char *s = (char *)tv_get_string_buf_chk(expr, buf); + if (s == NULL) { + return FAIL; + } + s = skipwhite(s); + if (eval1_emsg(&s, rettv, true) == FAIL) { + return FAIL; + } + if (*skipwhite(s) != NUL) { // check for trailing chars after expr + tv_clear(rettv); + semsg(_(e_invexpr2), s); + return FAIL; + } + } + return OK; +} + +/// Like eval_to_bool() but using a typval_T instead of a string. +/// Works for string, funcref and partial. +bool eval_expr_to_bool(const typval_T *expr, bool *error) + FUNC_ATTR_NONNULL_ARG(1, 2) +{ + typval_T argv, rettv; + + if (eval_expr_typval(expr, &argv, 0, &rettv) == FAIL) { + *error = true; + return false; + } + const bool res = (tv_get_number_chk(&rettv, error) != 0); + tv_clear(&rettv); + return res; +} + +/// Top level evaluation function, returning a string +/// +/// @param[in] arg String to evaluate. +/// @param nextcmd Pointer to the start of the next Ex command. +/// @param[in] skip If true, only do parsing to nextcmd without reporting +/// errors or actually evaluating anything. +/// +/// @return [allocated] string result of evaluation or NULL in case of error or +/// when skipping. +char *eval_to_string_skip(const char *arg, const char **nextcmd, const bool skip) + FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_WARN_UNUSED_RESULT +{ + typval_T tv; + char *retval; + + if (skip) { + emsg_skip++; + } + if (eval0((char *)arg, &tv, (char **)nextcmd, !skip) == FAIL || skip) { + retval = NULL; + } else { + retval = xstrdup(tv_get_string(&tv)); + tv_clear(&tv); + } + if (skip) { + emsg_skip--; + } + + return retval; +} + +/// Skip over an expression at "*pp". +/// +/// @return FAIL for an error, OK otherwise. +int skip_expr(char **pp) +{ + typval_T rettv; + + *pp = skipwhite(*pp); + return eval1(pp, &rettv, false); +} + +/// Top level evaluation function, returning a string. +/// +/// @param convert when true convert a List into a sequence of lines and convert +/// a Float to a String. +/// +/// @return pointer to allocated memory, or NULL for failure. +char *eval_to_string(char *arg, char **nextcmd, bool convert) +{ + typval_T tv; + char *retval; + garray_T ga; + + if (eval0(arg, &tv, nextcmd, true) == FAIL) { + retval = NULL; + } else { + if (convert && tv.v_type == VAR_LIST) { + ga_init(&ga, (int)sizeof(char), 80); + if (tv.vval.v_list != NULL) { + tv_list_join(&ga, tv.vval.v_list, "\n"); + if (tv_list_len(tv.vval.v_list) > 0) { + ga_append(&ga, NL); + } + } + ga_append(&ga, NUL); + retval = (char *)ga.ga_data; + } else if (convert && tv.v_type == VAR_FLOAT) { + char numbuf[NUMBUFLEN]; + vim_snprintf(numbuf, NUMBUFLEN, "%g", tv.vval.v_float); + retval = xstrdup(numbuf); + } else { + retval = xstrdup(tv_get_string(&tv)); + } + tv_clear(&tv); + } + + return retval; +} + +/// Call eval_to_string() without using current local variables and using +/// textlock. +/// +/// @param use_sandbox when TRUE, use the sandbox. +char *eval_to_string_safe(char *arg, char **nextcmd, int use_sandbox) +{ + char *retval; + funccal_entry_T funccal_entry; + + save_funccal(&funccal_entry); + if (use_sandbox) { + sandbox++; + } + textlock++; + retval = eval_to_string(arg, nextcmd, false); + if (use_sandbox) { + sandbox--; + } + textlock--; + restore_funccal(); + return retval; +} + +/// Top level evaluation function, returning a number. +/// Evaluates "expr" silently. +/// +/// @return -1 for an error. +varnumber_T eval_to_number(char *expr) +{ + typval_T rettv; + varnumber_T retval; + char *p = skipwhite(expr); + + ++emsg_off; + + if (eval1(&p, &rettv, true) == FAIL) { + retval = -1; + } else { + retval = tv_get_number_chk(&rettv, NULL); + tv_clear(&rettv); + } + --emsg_off; + + return retval; +} + +/// Top level evaluation function. +/// +/// @return an allocated typval_T with the result or +/// NULL when there is an error. +typval_T *eval_expr(char *arg) +{ + typval_T *tv = xmalloc(sizeof(*tv)); + if (eval0(arg, tv, NULL, true) == FAIL) { + XFREE_CLEAR(tv); + } + return tv; +} + +/// List Vim variables. +void list_vim_vars(int *first) +{ + list_hashtable_vars(&vimvarht, "v:", false, first); +} + +/// List script-local variables, if there is a script. +void list_script_vars(int *first) +{ + if (current_sctx.sc_sid > 0 && current_sctx.sc_sid <= ga_scripts.ga_len) { + list_hashtable_vars(&SCRIPT_VARS(current_sctx.sc_sid), "s:", false, first); + } +} + +bool is_vimvarht(const hashtab_T *ht) +{ + return ht == &vimvarht; +} + +bool is_compatht(const hashtab_T *ht) +{ + return ht == &compat_hashtab; +} + +/// Prepare v: variable "idx" to be used. +/// Save the current typeval in "save_tv". +/// When not used yet add the variable to the v: hashtable. +void prepare_vimvar(int idx, typval_T *save_tv) +{ + *save_tv = vimvars[idx].vv_tv; + if (vimvars[idx].vv_type == VAR_UNKNOWN) { + hash_add(&vimvarht, vimvars[idx].vv_di.di_key); + } +} + +/// Restore v: variable "idx" to typeval "save_tv". +/// When no longer defined, remove the variable from the v: hashtable. +void restore_vimvar(int idx, typval_T *save_tv) +{ + hashitem_T *hi; + + vimvars[idx].vv_tv = *save_tv; + if (vimvars[idx].vv_type == VAR_UNKNOWN) { + hi = hash_find(&vimvarht, (char *)vimvars[idx].vv_di.di_key); + if (HASHITEM_EMPTY(hi)) { + internal_error("restore_vimvar()"); + } else { + hash_remove(&vimvarht, hi); + } + } +} + +/// If there is a window for "curbuf", make it the current window. +void find_win_for_curbuf(void) +{ + for (wininfo_T *wip = curbuf->b_wininfo; wip != NULL; wip = wip->wi_next) { + if (wip->wi_win != NULL) { + curwin = wip->wi_win; + break; + } + } +} + +/// Evaluate an expression to a list with suggestions. +/// For the "expr:" part of 'spellsuggest'. +/// +/// @return NULL when there is an error. +list_T *eval_spell_expr(char *badword, char *expr) +{ + typval_T save_val; + typval_T rettv; + list_T *list = NULL; + char *p = skipwhite(expr); + + // Set "v:val" to the bad word. + prepare_vimvar(VV_VAL, &save_val); + vimvars[VV_VAL].vv_type = VAR_STRING; + vimvars[VV_VAL].vv_str = badword; + if (p_verbose == 0) { + ++emsg_off; + } + + if (eval1(&p, &rettv, true) == OK) { + if (rettv.v_type != VAR_LIST) { + tv_clear(&rettv); + } else { + list = rettv.vval.v_list; + } + } + + if (p_verbose == 0) { + --emsg_off; + } + restore_vimvar(VV_VAL, &save_val); + + return list; +} + +/// Get spell word from an entry from spellsuggest=expr: +/// +/// Entry in question is supposed to be a list (to be checked by the caller) +/// with two items: a word and a score represented as an unsigned number +/// (whether it actually is unsigned is not checked). +/// +/// Used to get the good word and score from the eval_spell_expr() result. +/// +/// @param[in] list List to get values from. +/// @param[out] ret_word Suggested word. Not initialized if return value is +/// -1. +/// +/// @return -1 in case of error, score otherwise. +int get_spellword(list_T *const list, const char **ret_word) +{ + if (tv_list_len(list) != 2) { + emsg(_("E5700: Expression from 'spellsuggest' must yield lists with " + "exactly two values")); + return -1; + } + *ret_word = tv_list_find_str(list, 0); + if (*ret_word == NULL) { + return -1; + } + return (int)tv_list_find_nr(list, -1, NULL); +} + +// Call some vim script function and return the result in "*rettv". +// Uses argv[0] to argv[argc-1] for the function arguments. argv[argc] +// should have type VAR_UNKNOWN. +// +// @return OK or FAIL. +int call_vim_function(const char *func, int argc, typval_T *argv, typval_T *rettv) + FUNC_ATTR_NONNULL_ALL +{ + int ret; + int len = (int)STRLEN(func); + partial_T *pt = NULL; + + if (len >= 6 && !memcmp(func, "v:lua.", 6)) { + func += 6; + len = check_luafunc_name(func, false); + if (len == 0) { + ret = FAIL; + goto fail; + } + pt = vvlua_partial; + } + + rettv->v_type = VAR_UNKNOWN; // tv_clear() uses this. + funcexe_T funcexe = FUNCEXE_INIT; + funcexe.firstline = curwin->w_cursor.lnum; + funcexe.lastline = curwin->w_cursor.lnum; + funcexe.evaluate = true; + funcexe.partial = pt; + ret = call_func(func, len, rettv, argc, argv, &funcexe); + +fail: + if (ret == FAIL) { + tv_clear(rettv); + } + + return ret; +} +/// Call Vim script function and return the result as a number +/// +/// @param[in] func Function name. +/// @param[in] argc Number of arguments. +/// @param[in] argv Array with typval_T arguments. +/// +/// @return -1 when calling function fails, result of function otherwise. +varnumber_T call_func_retnr(const char *func, int argc, typval_T *argv) + FUNC_ATTR_NONNULL_ALL +{ + typval_T rettv; + varnumber_T retval; + + if (call_vim_function((char *)func, argc, argv, &rettv) == FAIL) { + return -1; + } + retval = tv_get_number_chk(&rettv, NULL); + tv_clear(&rettv); + return retval; +} +/// Call Vim script function and return the result as a string +/// +/// @param[in] func Function name. +/// @param[in] argc Number of arguments. +/// @param[in] argv Array with typval_T arguments. +/// +/// @return [allocated] NULL when calling function fails, allocated string +/// otherwise. +char *call_func_retstr(const char *const func, int argc, typval_T *argv) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_MALLOC +{ + typval_T rettv; + // All arguments are passed as strings, no conversion to number. + if (call_vim_function(func, argc, argv, &rettv) + == FAIL) { + return NULL; + } + + char *const retval = xstrdup(tv_get_string(&rettv)); + tv_clear(&rettv); + return retval; +} +/// Call Vim script function and return the result as a List +/// +/// @param[in] func Function name. +/// @param[in] argc Number of arguments. +/// @param[in] argv Array with typval_T arguments. +/// +/// @return [allocated] NULL when calling function fails or return tv is not a +/// List, allocated List otherwise. +void *call_func_retlist(const char *func, int argc, typval_T *argv) + FUNC_ATTR_NONNULL_ALL +{ + typval_T rettv; + + // All arguments are passed as strings, no conversion to number. + if (call_vim_function((char *)func, argc, argv, &rettv) == FAIL) { + return NULL; + } + + if (rettv.v_type != VAR_LIST) { + tv_clear(&rettv); + return NULL; + } + + return rettv.vval.v_list; +} + +/// Prepare profiling for entering a child or something else that is not +/// counted for the script/function itself. +/// Should always be called in pair with prof_child_exit(). +/// +/// @param tm place to store waittime +void prof_child_enter(proftime_T *tm) +{ + funccall_T *fc = get_current_funccal(); + + if (fc != NULL && fc->func->uf_profiling) { + fc->prof_child = profile_start(); + } + + script_prof_save(tm); +} + +/// Take care of time spent in a child. +/// Should always be called after prof_child_enter(). +/// +/// @param tm where waittime was stored +void prof_child_exit(proftime_T *tm) +{ + funccall_T *fc = get_current_funccal(); + + if (fc != NULL && fc->func->uf_profiling) { + fc->prof_child = profile_end(fc->prof_child); + // don't count waiting time + fc->prof_child = profile_sub_wait(*tm, fc->prof_child); + fc->func->uf_tm_children = + profile_add(fc->func->uf_tm_children, fc->prof_child); + fc->func->uf_tml_children = + profile_add(fc->func->uf_tml_children, fc->prof_child); + } + script_prof_restore(tm); +} + +/// Evaluate 'foldexpr'. Returns the foldlevel, and any character preceding +/// it in "*cp". Doesn't give error messages. +int eval_foldexpr(char *arg, int *cp) +{ + typval_T tv; + varnumber_T retval; + int use_sandbox = was_set_insecurely(curwin, "foldexpr", OPT_LOCAL); + + ++emsg_off; + if (use_sandbox) { + ++sandbox; + } + ++textlock; + *cp = NUL; + if (eval0(arg, &tv, NULL, true) == FAIL) { + retval = 0; + } else { + // If the result is a number, just return the number. + if (tv.v_type == VAR_NUMBER) { + retval = tv.vval.v_number; + } else if (tv.v_type != VAR_STRING || tv.vval.v_string == NULL) { + retval = 0; + } else { + // If the result is a string, check if there is a non-digit before + // the number. + char *s = tv.vval.v_string; + if (!ascii_isdigit(*s) && *s != '-') { + *cp = (char_u)(*s++); + } + retval = atol(s); + } + tv_clear(&tv); + } + --emsg_off; + if (use_sandbox) { + --sandbox; + } + --textlock; + + return (int)retval; +} + +<<<<<<< HEAD +/// ":cons[t] var = expr1" define constant +/// ":cons[t] [name1, name2, ...] = expr1" define constants unpacking list +/// ":cons[t] [name, ..., ; lastname] = expr" define constants unpacking list +void ex_const(exarg_T *eap) +{ + ex_let_const(eap, true); +} + +/// Get a list of lines from a HERE document. The here document is a list of +/// lines surrounded by a marker. +/// cmd << {marker} +/// {line1} +/// {line2} +/// .... +/// {marker} +/// +/// The {marker} is a string. If the optional 'trim' word is supplied before the +/// marker, then the leading indentation before the lines (matching the +/// indentation in the 'cmd' line) is stripped. +/// +/// @return a List with {lines} or NULL. +static list_T *heredoc_get(exarg_T *eap, char *cmd) +{ + char *marker; + char *p; + int marker_indent_len = 0; + int text_indent_len = 0; + char *text_indent = NULL; + + if (eap->getline == NULL) { + emsg(_("E991: cannot use =<< here")); + return NULL; + } + + // Check for the optional 'trim' word before the marker + cmd = skipwhite(cmd); + if (STRNCMP(cmd, "trim", 4) == 0 + && (cmd[4] == NUL || ascii_iswhite(cmd[4]))) { + cmd = skipwhite(cmd + 4); + + // Trim the indentation from all the lines in the here document. + // The amount of indentation trimmed is the same as the indentation of + // the first line after the :let command line. To find the end marker + // the indent of the :let command line is trimmed. + p = *eap->cmdlinep; + while (ascii_iswhite(*p)) { + p++; + marker_indent_len++; + } + text_indent_len = -1; + } + + // The marker is the next word. + if (*cmd != NUL && *cmd != '"') { + marker = skipwhite(cmd); + p = (char *)skiptowhite((char_u *)marker); + if (*skipwhite(p) != NUL && *skipwhite(p) != '"') { + emsg(_(e_trailing)); + return NULL; + } + *p = NUL; + if (islower(*marker)) { + emsg(_("E221: Marker cannot start with lower case letter")); + return NULL; + } + } else { + emsg(_("E172: Missing marker")); + return NULL; + } + + list_T *l = tv_list_alloc(0); + for (;;) { + int mi = 0; + int ti = 0; + + char *theline = eap->getline(NUL, eap->cookie, 0, false); + if (theline == NULL) { + semsg(_("E990: Missing end marker '%s'"), marker); + break; + } + + // with "trim": skip the indent matching the :let line to find the + // marker + if (marker_indent_len > 0 + && STRNCMP(theline, *eap->cmdlinep, marker_indent_len) == 0) { + mi = marker_indent_len; + } + if (STRCMP(marker, theline + mi) == 0) { + xfree(theline); + break; + } + if (text_indent_len == -1 && *theline != NUL) { + // set the text indent from the first line. + p = theline; + text_indent_len = 0; + while (ascii_iswhite(*p)) { + p++; + text_indent_len++; + } + text_indent = xstrnsave(theline, (size_t)text_indent_len); + } + // with "trim": skip the indent matching the first line + if (text_indent != NULL) { + for (ti = 0; ti < text_indent_len; ti++) { + if (theline[ti] != text_indent[ti]) { + break; + } + } + } + + tv_list_append_string(l, theline + ti, -1); + xfree(theline); + } + xfree(text_indent); + + return l; +} + +/// ":let" list all variable values +/// ":let var1 var2" list variable values +/// ":let var = expr" assignment command. +/// ":let var += expr" assignment command. +/// ":let var -= expr" assignment command. +/// ":let var *= expr" assignment command. +/// ":let var /= expr" assignment command. +/// ":let var %= expr" assignment command. +/// ":let var .= expr" assignment command. +/// ":let var ..= expr" assignment command. +/// ":let [var1, var2] = expr" unpack list. +/// ":let [name, ..., ; lastname] = expr" unpack list. +void ex_let(exarg_T *eap) +{ + ex_let_const(eap, false); +} + +static void ex_let_const(exarg_T *eap, const bool is_const) +{ + char *arg = eap->arg; + char *expr = NULL; + typval_T rettv; + int i; + int var_count = 0; + int semicolon = 0; + char op[2]; + char *argend; + int first = true; + + argend = (char *)skip_var_list(arg, &var_count, &semicolon); + if (argend == NULL) { + return; + } + if (argend > arg && argend[-1] == '.') { // For var.='str'. + argend--; + } + expr = skipwhite(argend); + if (*expr != '=' && !((vim_strchr("+-*/%.", *expr) != NULL + && expr[1] == '=') || STRNCMP(expr, "..=", 3) == 0)) { + // ":let" without "=": list variables + if (*arg == '[') { + emsg(_(e_invarg)); + } else if (!ends_excmd(*arg)) { + // ":let var1 var2" + arg = (char *)list_arg_vars(eap, (const char *)arg, &first); + } else if (!eap->skip) { + // ":let" + list_glob_vars(&first); + list_buf_vars(&first); + list_win_vars(&first); + list_tab_vars(&first); + list_script_vars(&first); + list_func_vars(&first); + list_vim_vars(&first); + } + eap->nextcmd = (char *)check_nextcmd((char_u *)arg); + } else if (expr[0] == '=' && expr[1] == '<' && expr[2] == '<') { + // HERE document + list_T *l = heredoc_get(eap, expr + 3); + if (l != NULL) { + tv_list_set_ret(&rettv, l); + if (!eap->skip) { + op[0] = '='; + op[1] = NUL; + (void)ex_let_vars(eap->arg, &rettv, false, semicolon, var_count, + is_const, (char *)op); + } + tv_clear(&rettv); + } + } else { + op[0] = '='; + op[1] = NUL; + if (*expr != '=') { + if (vim_strchr("+-*/%.", *expr) != NULL) { + op[0] = *expr; // +=, -=, *=, /=, %= or .= + if (expr[0] == '.' && expr[1] == '.') { // ..= + expr++; + } + } + expr = skipwhite(expr + 2); + } else { + expr = skipwhite(expr + 1); + } + + if (eap->skip) { + ++emsg_skip; + } + i = eval0(expr, &rettv, &eap->nextcmd, !eap->skip); + if (eap->skip) { + if (i != FAIL) { + tv_clear(&rettv); + } + emsg_skip--; + } else if (i != FAIL) { + (void)ex_let_vars(eap->arg, &rettv, false, semicolon, var_count, + is_const, (char *)op); + tv_clear(&rettv); + } + } +} + +/// Assign the typevalue "tv" to the variable or variables at "arg_start". +/// Handles both "var" with any type and "[var, var; var]" with a list type. +/// When "op" is not NULL it points to a string with characters that +/// must appear after the variable(s). Use "+", "-" or "." for add, subtract +/// or concatenate. +/// +/// @param copy copy values from "tv", don't move +/// @param semicolon from skip_var_list() +/// @param var_count from skip_var_list() +/// @param is_const lock variables for :const +/// +/// @return OK or FAIL; +static int ex_let_vars(char *arg_start, typval_T *tv, int copy, int semicolon, int var_count, + int is_const, char *op) +{ + char *arg = arg_start; + typval_T ltv; + + if (*arg != '[') { + /* + * ":let var = expr" or ":for var in list" + */ + if (ex_let_one(arg, tv, copy, is_const, op, op) == NULL) { + return FAIL; + } + return OK; + } + + // ":let [v1, v2] = list" or ":for [v1, v2] in listlist" + if (tv->v_type != VAR_LIST) { + emsg(_(e_listreq)); + return FAIL; + } + list_T *const l = tv->vval.v_list; + + const int len = tv_list_len(l); + if (semicolon == 0 && var_count < len) { + emsg(_("E687: Less targets than List items")); + return FAIL; + } + if (var_count - semicolon > len) { + emsg(_("E688: More targets than List items")); + return FAIL; + } + // List l may actually be NULL, but it should fail with E688 or even earlier + // if you try to do ":let [] = v:_null_list". + assert(l != NULL); + + listitem_T *item = tv_list_first(l); + size_t rest_len = (size_t)tv_list_len(l); + while (*arg != ']') { + arg = skipwhite(arg + 1); + arg = ex_let_one(arg, TV_LIST_ITEM_TV(item), true, is_const, ",;]", op); + if (arg == NULL) { + return FAIL; + } + rest_len--; + + item = TV_LIST_ITEM_NEXT(l, item); + arg = skipwhite(arg); + if (*arg == ';') { + /* Put the rest of the list (may be empty) in the var after ';'. + * Create a new list for this. */ + list_T *const rest_list = tv_list_alloc((ptrdiff_t)rest_len); + while (item != NULL) { + tv_list_append_tv(rest_list, TV_LIST_ITEM_TV(item)); + item = TV_LIST_ITEM_NEXT(l, item); + } + + ltv.v_type = VAR_LIST; + ltv.v_lock = VAR_UNLOCKED; + ltv.vval.v_list = rest_list; + tv_list_ref(rest_list); + + arg = ex_let_one(skipwhite(arg + 1), <v, false, is_const, "]", op); + tv_clear(<v); + if (arg == NULL) { + return FAIL; + } + break; + } else if (*arg != ',' && *arg != ']') { + internal_error("ex_let_vars()"); + return FAIL; + } + } + + return OK; +} + +/// Skip over assignable variable "var" or list of variables "[var, var]". +/// Used for ":let varvar = expr" and ":for varvar in expr". +/// For "[var, var]" increment "*var_count" for each variable. +/// for "[var, var; var]" set "semicolon". +/// +/// @return NULL for an error. +static const char *skip_var_list(const char *arg, int *var_count, int *semicolon) +{ + const char *p; + const char *s; + + if (*arg == '[') { + // "[var, var]": find the matching ']'. + p = arg; + for (;;) { + p = skipwhite(p + 1); // skip whites after '[', ';' or ',' + s = skip_var_one((char *)p); + if (s == p) { + semsg(_(e_invarg2), p); + return NULL; + } + ++*var_count; + + p = skipwhite(s); + if (*p == ']') { + break; + } else if (*p == ';') { + if (*semicolon == 1) { + emsg(_("E452: Double ; in list of variables")); + return NULL; + } + *semicolon = 1; + } else if (*p != ',') { + semsg(_(e_invarg2), p); + return NULL; + } + } + return p + 1; + } else { + return skip_var_one((char *)arg); + } +} + +/// Skip one (assignable) variable name, including @r, $VAR, &option, d.key, +/// l[idx]. +static const char *skip_var_one(const char *arg) +{ + if (*arg == '@' && arg[1] != NUL) { + return arg + 1 + utfc_ptr2len(arg + 1); + } + return (char *)find_name_end(*arg == '$' || *arg == '&' ? arg + 1 : arg, + NULL, NULL, FNE_INCL_BR | FNE_CHECK_START); +} + +/// List variables for hashtab "ht" with prefix "prefix". +/// +/// @param empty if TRUE also list NULL strings as empty strings. +void list_hashtable_vars(hashtab_T *ht, const char *prefix, int empty, int *first) +{ + hashitem_T *hi; + dictitem_T *di; + int todo; + + todo = (int)ht->ht_used; + for (hi = ht->ht_array; todo > 0 && !got_int; ++hi) { + if (!HASHITEM_EMPTY(hi)) { + todo--; + di = TV_DICT_HI2DI(hi); + char buf[IOSIZE]; + + // apply :filter /pat/ to variable name + xstrlcpy(buf, prefix, IOSIZE); + xstrlcat(buf, (char *)di->di_key, IOSIZE); + if (message_filtered((char_u *)buf)) { + continue; + } + + if (empty || di->di_tv.v_type != VAR_STRING + || di->di_tv.vval.v_string != NULL) { + list_one_var(di, prefix, first); + } + } + } +} + +/// List global variables. +static void list_glob_vars(int *first) +{ + list_hashtable_vars(&globvarht, "", true, first); +} + +/// List buffer variables. +static void list_buf_vars(int *first) +{ + list_hashtable_vars(&curbuf->b_vars->dv_hashtab, "b:", true, first); +} + +/// List window variables. +static void list_win_vars(int *first) +{ + list_hashtable_vars(&curwin->w_vars->dv_hashtab, "w:", true, first); +} + +/// List tab page variables. +static void list_tab_vars(int *first) +{ + list_hashtable_vars(&curtab->tp_vars->dv_hashtab, "t:", true, first); +} + +/// List Vim variables. +static void list_vim_vars(int *first) +{ + list_hashtable_vars(&vimvarht, "v:", false, first); +} + +/// List script-local variables, if there is a script. +static void list_script_vars(int *first) +{ + if (current_sctx.sc_sid > 0 && current_sctx.sc_sid <= ga_scripts.ga_len) { + list_hashtable_vars(&SCRIPT_VARS(current_sctx.sc_sid), "s:", false, first); + } +} + +/// List variables in "arg". +static const char *list_arg_vars(exarg_T *eap, const char *arg, int *first) +{ + int error = FALSE; + int len; + const char *name; + const char *name_start; + typval_T tv; + + while (!ends_excmd(*arg) && !got_int) { + if (error || eap->skip) { + arg = find_name_end(arg, NULL, NULL, FNE_INCL_BR | FNE_CHECK_START); + if (!ascii_iswhite(*arg) && !ends_excmd(*arg)) { + emsg_severe = true; + emsg(_(e_trailing)); + break; + } + } else { + // get_name_len() takes care of expanding curly braces + name_start = name = arg; + char *tofree; + len = get_name_len(&arg, &tofree, true, true); + if (len <= 0) { + /* This is mainly to keep test 49 working: when expanding + * curly braces fails overrule the exception error message. */ + if (len < 0 && !aborting()) { + emsg_severe = true; + semsg(_(e_invarg2), arg); + break; + } + error = TRUE; + } else { + if (tofree != NULL) { + name = tofree; + } + if (get_var_tv(name, len, &tv, NULL, true, false) + == FAIL) { + error = true; + } else { + // handle d.key, l[idx], f(expr) + const char *const arg_subsc = arg; + if (handle_subscript(&arg, &tv, true, true, name, &name) == FAIL) { + error = true; + } else { + if (arg == arg_subsc && len == 2 && name[1] == ':') { + switch (*name) { + case 'g': + list_glob_vars(first); break; + case 'b': + list_buf_vars(first); break; + case 'w': + list_win_vars(first); break; + case 't': + list_tab_vars(first); break; + case 'v': + list_vim_vars(first); break; + case 's': + list_script_vars(first); break; + case 'l': + list_func_vars(first); break; + default: + semsg(_("E738: Can't list variables for %s"), name); + } + } else { + char *const s = encode_tv2echo(&tv, NULL); + const char *const used_name = (arg == arg_subsc + ? name + : name_start); + const ptrdiff_t name_size = (used_name == tofree + ? (ptrdiff_t)strlen(used_name) + : (arg - used_name)); + list_one_var_a("", used_name, name_size, + tv.v_type, s == NULL ? "" : s, first); + xfree(s); + } + tv_clear(&tv); + } + } + } + + xfree(tofree); + } + + arg = (const char *)skipwhite(arg); + } + + return arg; +} + +// TODO(ZyX-I): move to eval/ex_cmds + +/// Set one item of `:let var = expr` or `:let [v1, v2] = list` to its value +/// +/// @param[in] arg Start of the variable name. +/// @param[in] tv Value to assign to the variable. +/// @param[in] copy If true, copy value from `tv`. +/// @param[in] endchars Valid characters after variable name or NULL. +/// @param[in] op Operation performed: *op is `+`, `-`, `.` for `+=`, etc. +/// NULL for `=`. +/// +/// @return a pointer to the char just after the var name or NULL in case of +/// error. +static char *ex_let_one(char *arg, typval_T *const tv, const bool copy, const bool is_const, + const char *const endchars, const char *const op) + FUNC_ATTR_NONNULL_ARG(1, 2) FUNC_ATTR_WARN_UNUSED_RESULT +{ + char *arg_end = NULL; + int len; + int opt_flags; + char *tofree = NULL; + + /* + * ":let $VAR = expr": Set environment variable. + */ + if (*arg == '$') { + if (is_const) { + emsg(_("E996: Cannot lock an environment variable")); + return NULL; + } + // Find the end of the name. + arg++; + char *name = arg; + len = get_env_len((const char **)&arg); + if (len == 0) { + semsg(_(e_invarg2), name - 1); + } else { + if (op != NULL && vim_strchr("+-*/%", *op) != NULL) { + semsg(_(e_letwrong), op); + } else if (endchars != NULL + && vim_strchr(endchars, *skipwhite(arg)) == NULL) { + emsg(_(e_letunexp)); + } else if (!check_secure()) { + const char c1 = name[len]; + name[len] = NUL; + const char *p = tv_get_string_chk(tv); + if (p != NULL && op != NULL && *op == '.') { + char *s = vim_getenv(name); + + if (s != NULL) { + tofree = (char *)concat_str((const char_u *)s, (const char_u *)p); + p = (const char *)tofree; + xfree(s); + } + } + if (p != NULL) { + os_setenv(name, p, 1); + if (STRICMP(name, "HOME") == 0) { + init_homedir(); + } else if (didset_vim && STRICMP(name, "VIM") == 0) { + didset_vim = false; + } else if (didset_vimruntime + && STRICMP(name, "VIMRUNTIME") == 0) { + didset_vimruntime = false; + } + arg_end = arg; + } + name[len] = c1; + xfree(tofree); + } + } + // ":let &option = expr": Set option value. + // ":let &l:option = expr": Set local option value. + // ":let &g:option = expr": Set global option value. + } else if (*arg == '&') { + if (is_const) { + emsg(_("E996: Cannot lock an option")); + return NULL; + } + // Find the end of the name. + char *const p = (char *)find_option_end((const char **)&arg, &opt_flags); + if (p == NULL + || (endchars != NULL + && vim_strchr(endchars, *skipwhite(p)) == NULL)) { + emsg(_(e_letunexp)); + } else { + int opt_type; + long numval; + char *stringval = NULL; + const char *s = NULL; + + const char c1 = *p; + *p = NUL; + + varnumber_T n = tv_get_number(tv); + if (tv->v_type != VAR_BOOL && tv->v_type != VAR_SPECIAL) { + s = tv_get_string_chk(tv); // != NULL if number or string. + } + if (s != NULL && op != NULL && *op != '=') { + opt_type = get_option_value(arg, &numval, &stringval, opt_flags); + if ((opt_type == 1 && *op == '.') + || (opt_type == 0 && *op != '.')) { + semsg(_(e_letwrong), op); + s = NULL; // don't set the value + } else { + if (opt_type == 1) { // number + switch (*op) { + case '+': + n = numval + n; break; + case '-': + n = numval - n; break; + case '*': + n = numval * n; break; + case '/': + n = num_divide(numval, n); break; + case '%': + n = num_modulus(numval, n); break; + } + } else if (opt_type == 0 && stringval != NULL) { // string + char *const oldstringval = stringval; + stringval = (char *)concat_str((const char_u *)stringval, + (const char_u *)s); + xfree(oldstringval); + s = stringval; + } + } + } + if (s != NULL || tv->v_type == VAR_BOOL + || tv->v_type == VAR_SPECIAL) { + set_option_value((const char *)arg, n, s, opt_flags); + arg_end = p; + } + *p = c1; + xfree(stringval); + } + // ":let @r = expr": Set register contents. + } else if (*arg == '@') { + if (is_const) { + emsg(_("E996: Cannot lock a register")); + return NULL; + } + arg++; + + int regname = utf_ptr2char(arg); + int mblen = utf_ptr2len(arg); + + if (op != NULL && vim_strchr("+-*/%", *op) != NULL) { + semsg(_(e_letwrong), op); + } else if (endchars != NULL + && vim_strchr(endchars, *skipwhite(arg + mblen)) == NULL) { + emsg(_(e_letunexp)); + } else { + char *s; + + char *ptofree = NULL; + const char *p = tv_get_string_chk(tv); + if (p != NULL && op != NULL && *op == '.') { + s = get_reg_contents(regname == '@' ? '"' : regname, kGRegExprSrc); + if (s != NULL) { + ptofree = (char *)concat_str((char_u *)s, (const char_u *)p); + p = (const char *)ptofree; + xfree(s); + } + } + if (p != NULL) { + + write_reg_contents(regname == '@' ? '"' : regname, + (const char_u *)p, (ssize_t)STRLEN(p), false); + arg_end = arg + mblen; + } + xfree(ptofree); + } + } + /* + * ":let var = expr": Set internal variable. + * ":let {expr} = expr": Idem, name made with curly braces + */ + else if (eval_isnamec1(*arg) || *arg == '{') { + lval_T lv; + + char *const p = get_lval(arg, tv, &lv, false, false, 0, FNE_CHECK_START); + if (p != NULL && lv.ll_name != NULL) { + if (endchars != NULL && vim_strchr(endchars, *skipwhite(p)) == NULL) { + emsg(_(e_letunexp)); + } else { + set_var_lval(&lv, p, tv, copy, is_const, op); + arg_end = p; + } + } + clear_lval(&lv); + } else { + semsg(_(e_invarg2), arg); + } + + return arg_end; +} + +======= +>>>>>>> upstream/master +// TODO(ZyX-I): move to eval/executor + +/// Get an lvalue +/// +/// Lvalue may be +/// - variable: "name", "na{me}" +/// - dictionary item: "dict.key", "dict['key']" +/// - list item: "list[expr]" +/// - list slice: "list[expr:expr]" +/// +/// Indexing only works if trying to use it with an existing List or Dictionary. +/// +/// @param[in] name Name to parse. +/// @param rettv Pointer to the value to be assigned or NULL. +/// @param[out] lp Lvalue definition. When evaluation errors occur `->ll_name` +/// is NULL. +/// @param[in] unlet True if using `:unlet`. This results in slightly +/// different behaviour when something is wrong; must end in +/// space or cmd separator. +/// @param[in] skip True when skipping. +/// @param[in] flags @see GetLvalFlags. +/// @param[in] fne_flags Flags for find_name_end(). +/// +/// @return A pointer to just after the name, including indexes. Returns NULL +/// for a parsing error, but it is still needed to free items in lp. +char *get_lval(char *const name, typval_T *const rettv, lval_T *const lp, const bool unlet, + const bool skip, const int flags, const int fne_flags) + FUNC_ATTR_NONNULL_ARG(1, 3) +{ + dictitem_T *v; + typval_T var1; + typval_T var2; + int empty1 = FALSE; + listitem_T *ni; + hashtab_T *ht = NULL; + int quiet = flags & GLV_QUIET; + + // Clear everything in "lp". + memset(lp, 0, sizeof(lval_T)); + + if (skip) { + // When skipping just find the end of the name. + lp->ll_name = (const char *)name; + return (char *)find_name_end(name, NULL, NULL, + FNE_INCL_BR | fne_flags); + } + + // Find the end of the name. + char *expr_start; + char *expr_end; + char *p = (char *)find_name_end(name, (const char **)&expr_start, + (const char **)&expr_end, + fne_flags); + if (expr_start != NULL) { + // Don't expand the name when we already know there is an error. + if (unlet && !ascii_iswhite(*p) && !ends_excmd(*p) + && *p != '[' && *p != '.') { + emsg(_(e_trailing)); + return NULL; + } + + lp->ll_exp_name = make_expanded_name(name, expr_start, expr_end, p); + lp->ll_name = lp->ll_exp_name; + if (lp->ll_exp_name == NULL) { + // Report an invalid expression in braces, unless the + // expression evaluation has been cancelled due to an + // aborting error, an interrupt, or an exception. + if (!aborting() && !quiet) { + emsg_severe = true; + semsg(_(e_invarg2), name); + return NULL; + } + lp->ll_name_len = 0; + } else { + lp->ll_name_len = strlen(lp->ll_name); + } + } else { + lp->ll_name = (const char *)name; + lp->ll_name_len = (size_t)((const char *)p - lp->ll_name); + } + + // Without [idx] or .key we are done. + if ((*p != '[' && *p != '.') || lp->ll_name == NULL) { + return p; + } + + // Only pass &ht when we would write to the variable, it prevents autoload + // as well. + v = find_var(lp->ll_name, lp->ll_name_len, + (flags & GLV_READ_ONLY) ? NULL : &ht, + flags & GLV_NO_AUTOLOAD); + if (v == NULL && !quiet) { + semsg(_("E121: Undefined variable: %.*s"), + (int)lp->ll_name_len, lp->ll_name); + } + if (v == NULL) { + return NULL; + } + + // Loop until no more [idx] or .key is following. + lp->ll_tv = &v->di_tv; + var1.v_type = VAR_UNKNOWN; + var2.v_type = VAR_UNKNOWN; + while (*p == '[' || (*p == '.' && lp->ll_tv->v_type == VAR_DICT)) { + if (!(lp->ll_tv->v_type == VAR_LIST && lp->ll_tv->vval.v_list != NULL) + && !(lp->ll_tv->v_type == VAR_DICT && lp->ll_tv->vval.v_dict != NULL) + && !(lp->ll_tv->v_type == VAR_BLOB && lp->ll_tv->vval.v_blob != NULL)) { + if (!quiet) { + emsg(_("E689: Can only index a List, Dictionary or Blob")); + } + return NULL; + } + if (lp->ll_range) { + if (!quiet) { + emsg(_("E708: [:] must come last")); + } + return NULL; + } + + int len = -1; + char *key = NULL; + if (*p == '.') { + key = p + 1; + for (len = 0; ASCII_ISALNUM(key[len]) || key[len] == '_'; len++) {} + if (len == 0) { + if (!quiet) { + emsg(_("E713: Cannot use empty key after .")); + } + return NULL; + } + p = key + len; + } else { + // Get the index [expr] or the first index [expr: ]. + p = skipwhite(p + 1); + if (*p == ':') { + empty1 = true; + } else { + empty1 = false; + if (eval1(&p, &var1, true) == FAIL) { // Recursive! + return NULL; + } + if (!tv_check_str(&var1)) { + // Not a number or string. + tv_clear(&var1); + return NULL; + } + p = skipwhite(p); + } + + // Optionally get the second index [ :expr]. + if (*p == ':') { + if (lp->ll_tv->v_type == VAR_DICT) { + if (!quiet) { + emsg(_(e_dictrange)); + } + tv_clear(&var1); + return NULL; + } + if (rettv != NULL + && !(rettv->v_type == VAR_LIST && rettv->vval.v_list != NULL) + && !(rettv->v_type == VAR_BLOB && rettv->vval.v_blob != NULL)) { + if (!quiet) { + emsg(_("E709: [:] requires a List or Blob value")); + } + tv_clear(&var1); + return NULL; + } + p = skipwhite(p + 1); + if (*p == ']') { + lp->ll_empty2 = true; + } else { + lp->ll_empty2 = false; + if (eval1(&p, &var2, true) == FAIL) { // Recursive! + tv_clear(&var1); + return NULL; + } + if (!tv_check_str(&var2)) { + // Not a number or string. + tv_clear(&var1); + tv_clear(&var2); + return NULL; + } + } + lp->ll_range = true; + } else { + lp->ll_range = false; + } + + if (*p != ']') { + if (!quiet) { + emsg(_(e_missbrac)); + } + tv_clear(&var1); + tv_clear(&var2); + return NULL; + } + + // Skip to past ']'. + p++; + } + + if (lp->ll_tv->v_type == VAR_DICT) { + if (len == -1) { + // "[key]": get key from "var1" + key = (char *)tv_get_string(&var1); // is number or string + } + lp->ll_list = NULL; + lp->ll_dict = lp->ll_tv->vval.v_dict; + lp->ll_di = tv_dict_find(lp->ll_dict, (const char *)key, len); + + // When assigning to a scope dictionary check that a function and + // variable name is valid (only variable name unless it is l: or + // g: dictionary). Disallow overwriting a builtin function. + if (rettv != NULL && lp->ll_dict->dv_scope != 0) { + char prevval; + int wrong; + + if (len != -1) { + prevval = key[len]; + key[len] = NUL; + } else { + prevval = 0; // Avoid compiler warning. + } + wrong = ((lp->ll_dict->dv_scope == VAR_DEF_SCOPE + && tv_is_func(*rettv) + && !var_check_func_name((const char *)key, lp->ll_di == NULL)) + || !valid_varname((const char *)key)); + if (len != -1) { + key[len] = prevval; + } + if (wrong) { + return NULL; + } + } + + if (lp->ll_di != NULL && tv_is_luafunc(&lp->ll_di->di_tv) + && len == -1 && rettv == NULL) { + tv_clear(&var1); + semsg(e_illvar, "v:['lua']"); + return NULL; + } + + if (lp->ll_di == NULL) { + // Can't add "v:" or "a:" variable. + if (lp->ll_dict == &vimvardict + || &lp->ll_dict->dv_hashtab == get_funccal_args_ht()) { + semsg(_(e_illvar), name); + tv_clear(&var1); + return NULL; + } + + // Key does not exist in dict: may need to add it. + if (*p == '[' || *p == '.' || unlet) { + if (!quiet) { + semsg(_(e_dictkey), key); + } + tv_clear(&var1); + return NULL; + } + if (len == -1) { + lp->ll_newkey = xstrdup(key); + } else { + lp->ll_newkey = xstrnsave(key, (size_t)len); + } + tv_clear(&var1); + break; + // existing variable, need to check if it can be changed + } else if (!(flags & GLV_READ_ONLY) && var_check_ro(lp->ll_di->di_flags, + (const char *)name, + (size_t)(p - name))) { + tv_clear(&var1); + return NULL; + } + + tv_clear(&var1); + lp->ll_tv = &lp->ll_di->di_tv; + } else if (lp->ll_tv->v_type == VAR_BLOB) { + // Get the number and item for the only or first index of the List. + if (empty1) { + lp->ll_n1 = 0; + } else { + // Is number or string. + lp->ll_n1 = (long)tv_get_number(&var1); + } + tv_clear(&var1); + + const int bloblen = tv_blob_len(lp->ll_tv->vval.v_blob); + if (lp->ll_n1 < 0 || lp->ll_n1 > bloblen + || (lp->ll_range && lp->ll_n1 == bloblen)) { + if (!quiet) { + semsg(_(e_blobidx), (int64_t)lp->ll_n1); + } + tv_clear(&var2); + return NULL; + } + if (lp->ll_range && !lp->ll_empty2) { + lp->ll_n2 = (long)tv_get_number(&var2); + tv_clear(&var2); + if (lp->ll_n2 < 0 || lp->ll_n2 >= bloblen || lp->ll_n2 < lp->ll_n1) { + if (!quiet) { + semsg(_(e_blobidx), (int64_t)lp->ll_n2); + } + return NULL; + } + } + lp->ll_blob = lp->ll_tv->vval.v_blob; + lp->ll_tv = NULL; + break; + } else { + // Get the number and item for the only or first index of the List. + if (empty1) { + lp->ll_n1 = 0; + } else { + // Is number or string. + lp->ll_n1 = (long)tv_get_number(&var1); + } + tv_clear(&var1); + + lp->ll_dict = NULL; + lp->ll_list = lp->ll_tv->vval.v_list; + lp->ll_li = tv_list_find(lp->ll_list, (int)lp->ll_n1); + if (lp->ll_li == NULL) { + if (lp->ll_n1 < 0) { + lp->ll_n1 = 0; + lp->ll_li = tv_list_find(lp->ll_list, (int)lp->ll_n1); + } + } + if (lp->ll_li == NULL) { + tv_clear(&var2); + if (!quiet) { + semsg(_(e_listidx), (int64_t)lp->ll_n1); + } + return NULL; + } + + // May need to find the item or absolute index for the second + // index of a range. + // When no index given: "lp->ll_empty2" is true. + // Otherwise "lp->ll_n2" is set to the second index. + if (lp->ll_range && !lp->ll_empty2) { + lp->ll_n2 = (long)tv_get_number(&var2); // Is number or string. + tv_clear(&var2); + if (lp->ll_n2 < 0) { + ni = tv_list_find(lp->ll_list, (int)lp->ll_n2); + if (ni == NULL) { + if (!quiet) { + semsg(_(e_listidx), (int64_t)lp->ll_n2); + } + return NULL; + } + lp->ll_n2 = tv_list_idx_of_item(lp->ll_list, ni); + } + + // Check that lp->ll_n2 isn't before lp->ll_n1. + if (lp->ll_n1 < 0) { + lp->ll_n1 = tv_list_idx_of_item(lp->ll_list, lp->ll_li); + } + if (lp->ll_n2 < lp->ll_n1) { + if (!quiet) { + semsg(_(e_listidx), (int64_t)lp->ll_n2); + } + return NULL; + } + } + + lp->ll_tv = TV_LIST_ITEM_TV(lp->ll_li); + } + } + + tv_clear(&var1); + return p; +} + +// TODO(ZyX-I): move to eval/executor + +/// Clear lval "lp" that was filled by get_lval(). +void clear_lval(lval_T *lp) +{ + xfree(lp->ll_exp_name); + xfree(lp->ll_newkey); +} + +// TODO(ZyX-I): move to eval/executor + +/// Set a variable that was parsed by get_lval() to "rettv". +/// +/// @param endp points to just after the parsed name. +/// @param op NULL, "+" for "+=", "-" for "-=", "*" for "*=", "/" for "/=", +/// "%" for "%=", "." for ".=" or "=" for "=". +void set_var_lval(lval_T *lp, char *endp, typval_T *rettv, int copy, const bool is_const, + const char *op) +{ + int cc; + listitem_T *ri; + dictitem_T *di; + + if (lp->ll_tv == NULL) { + cc = (char_u)(*endp); + *endp = NUL; + if (lp->ll_blob != NULL) { + if (op != NULL && *op != '=') { + semsg(_(e_letwrong), op); + return; + } + if (var_check_lock(lp->ll_blob->bv_lock, lp->ll_name, TV_CSTRING)) { + return; + } + + if (lp->ll_range && rettv->v_type == VAR_BLOB) { + if (lp->ll_empty2) { + lp->ll_n2 = tv_blob_len(lp->ll_blob) - 1; + } + + if (lp->ll_n2 - lp->ll_n1 + 1 != tv_blob_len(rettv->vval.v_blob)) { + emsg(_("E972: Blob value does not have the right number of bytes")); + return; + } + if (lp->ll_empty2) { + lp->ll_n2 = tv_blob_len(lp->ll_blob); + } + + for (int il = (int)lp->ll_n1, ir = 0; il <= (int)lp->ll_n2; il++) { + tv_blob_set(lp->ll_blob, il, tv_blob_get(rettv->vval.v_blob, ir++)); + } + } else { + bool error = false; + const char val = (char)tv_get_number_chk(rettv, &error); + if (!error) { + garray_T *const gap = &lp->ll_blob->bv_ga; + + // Allow for appending a byte. Setting a byte beyond + // the end is an error otherwise. + if (lp->ll_n1 < gap->ga_len || lp->ll_n1 == gap->ga_len) { + ga_grow(&lp->ll_blob->bv_ga, 1); + tv_blob_set(lp->ll_blob, (int)lp->ll_n1, (char_u)val); + if (lp->ll_n1 == gap->ga_len) { + gap->ga_len++; + } + } + // error for invalid range was already given in get_lval() + } + } + } else if (op != NULL && *op != '=') { + typval_T tv; + + if (is_const) { + emsg(_(e_cannot_mod)); + *endp = (char)cc; + return; + } + + // handle +=, -=, *=, /=, %= and .= + di = NULL; + if (get_var_tv(lp->ll_name, (int)STRLEN(lp->ll_name), + &tv, &di, true, false) == OK) { + if ((di == NULL + || (!var_check_ro(di->di_flags, lp->ll_name, TV_CSTRING) + && !tv_check_lock(&di->di_tv, lp->ll_name, TV_CSTRING))) + && eexe_mod_op(&tv, rettv, op) == OK) { + set_var(lp->ll_name, lp->ll_name_len, &tv, false); + } + tv_clear(&tv); + } + } else { + set_var_const(lp->ll_name, lp->ll_name_len, rettv, copy, is_const); + } + *endp = (char)cc; + } else if (var_check_lock(lp->ll_newkey == NULL + ? lp->ll_tv->v_lock + : lp->ll_tv->vval.v_dict->dv_lock, + lp->ll_name, TV_CSTRING)) { + // Skip + } else if (lp->ll_range) { + listitem_T *ll_li = lp->ll_li; + int ll_n1 = (int)lp->ll_n1; + + if (is_const) { + emsg(_("E996: Cannot lock a range")); + return; + } + + // Check whether any of the list items is locked + for (ri = tv_list_first(rettv->vval.v_list); + ri != NULL && ll_li != NULL;) { + if (var_check_lock(TV_LIST_ITEM_TV(ll_li)->v_lock, lp->ll_name, + TV_CSTRING)) { + return; + } + ri = TV_LIST_ITEM_NEXT(rettv->vval.v_list, ri); + if (ri == NULL || (!lp->ll_empty2 && lp->ll_n2 == ll_n1)) { + break; + } + ll_li = TV_LIST_ITEM_NEXT(lp->ll_list, ll_li); + ll_n1++; + } + + /* + * Assign the List values to the list items. + */ + for (ri = tv_list_first(rettv->vval.v_list); ri != NULL;) { + if (op != NULL && *op != '=') { + eexe_mod_op(TV_LIST_ITEM_TV(lp->ll_li), TV_LIST_ITEM_TV(ri), op); + } else { + tv_clear(TV_LIST_ITEM_TV(lp->ll_li)); + tv_copy(TV_LIST_ITEM_TV(ri), TV_LIST_ITEM_TV(lp->ll_li)); + } + ri = TV_LIST_ITEM_NEXT(rettv->vval.v_list, ri); + if (ri == NULL || (!lp->ll_empty2 && lp->ll_n2 == lp->ll_n1)) { + break; + } + assert(lp->ll_li != NULL); + if (TV_LIST_ITEM_NEXT(lp->ll_list, lp->ll_li) == NULL) { + // Need to add an empty item. + tv_list_append_number(lp->ll_list, 0); + // ll_li may have become invalid after append, don’t use it. + lp->ll_li = tv_list_last(lp->ll_list); // Valid again. + } else { + lp->ll_li = TV_LIST_ITEM_NEXT(lp->ll_list, lp->ll_li); + } + lp->ll_n1++; + } + if (ri != NULL) { + emsg(_("E710: List value has more items than target")); + } else if (lp->ll_empty2 + ? (lp->ll_li != NULL + && TV_LIST_ITEM_NEXT(lp->ll_list, lp->ll_li) != NULL) + : lp->ll_n1 != lp->ll_n2) { + emsg(_("E711: List value has not enough items")); + } + } else { + typval_T oldtv = TV_INITIAL_VALUE; + dict_T *dict = lp->ll_dict; + bool watched = tv_dict_is_watched(dict); + + if (is_const) { + emsg(_("E996: Cannot lock a list or dict")); + return; + } + + // Assign to a List or Dictionary item. + if (lp->ll_newkey != NULL) { + if (op != NULL && *op != '=') { + semsg(_(e_letwrong), op); + return; + } + + // Need to add an item to the Dictionary. + di = tv_dict_item_alloc((const char *)lp->ll_newkey); + if (tv_dict_add(lp->ll_tv->vval.v_dict, di) == FAIL) { + xfree(di); + return; + } + lp->ll_tv = &di->di_tv; + } else { + if (watched) { + tv_copy(lp->ll_tv, &oldtv); + } + + if (op != NULL && *op != '=') { + eexe_mod_op(lp->ll_tv, rettv, op); + goto notify; + } else { + tv_clear(lp->ll_tv); + } + } + + // Assign the value to the variable or list item. + if (copy) { + tv_copy(rettv, lp->ll_tv); + } else { + *lp->ll_tv = *rettv; + lp->ll_tv->v_lock = VAR_UNLOCKED; + tv_init(rettv); + } + +notify: + if (watched) { + if (oldtv.v_type == VAR_UNKNOWN) { + assert(lp->ll_newkey != NULL); + tv_dict_watcher_notify(dict, lp->ll_newkey, lp->ll_tv, NULL); + } else { + dictitem_T *di_ = lp->ll_di; + assert(di_->di_key != NULL); + tv_dict_watcher_notify(dict, (char *)di_->di_key, lp->ll_tv, &oldtv); + tv_clear(&oldtv); + } + } + } +} + +// TODO(ZyX-I): move to eval/ex_cmds + +/// Evaluate the expression used in a ":for var in expr" command. +/// "arg" points to "var". +/// +/// @param[out] *errp set to TRUE for an error, FALSE otherwise; +/// +/// @return a pointer that holds the info. Null when there is an error. +void *eval_for_line(const char *arg, bool *errp, char **nextcmdp, int skip) +{ + forinfo_T *fi = xcalloc(1, sizeof(forinfo_T)); + const char *expr; + typval_T tv; + list_T *l; + + *errp = true; // Default: there is an error. + + expr = skip_var_list((char *)arg, &fi->fi_varcount, &fi->fi_semicolon); + if (expr == NULL) { + return fi; + } + + expr = skipwhite(expr); + if (expr[0] != 'i' || expr[1] != 'n' || !ascii_iswhite(expr[2])) { + emsg(_("E690: Missing \"in\" after :for")); + return fi; + } + + if (skip) { + ++emsg_skip; + } + if (eval0(skipwhite(expr + 2), &tv, nextcmdp, !skip) == OK) { + *errp = false; + if (!skip) { + if (tv.v_type == VAR_LIST) { + l = tv.vval.v_list; + if (l == NULL) { + // a null list is like an empty list: do nothing + tv_clear(&tv); + } else { + // No need to increment the refcount, it's already set for + // the list being used in "tv". + fi->fi_list = l; + tv_list_watch_add(l, &fi->fi_lw); + fi->fi_lw.lw_item = tv_list_first(l); + } + } else if (tv.v_type == VAR_BLOB) { + fi->fi_bi = 0; + if (tv.vval.v_blob != NULL) { + typval_T btv; + + // Make a copy, so that the iteration still works when the + // blob is changed. + tv_blob_copy(&tv, &btv); + fi->fi_blob = btv.vval.v_blob; + } + tv_clear(&tv); + } else if (tv.v_type == VAR_STRING) { + fi->fi_byte_idx = 0; + fi->fi_string = tv.vval.v_string; + tv.vval.v_string = NULL; + if (fi->fi_string == NULL) { + fi->fi_string = xstrdup(""); + } + } else { + emsg(_(e_string_list_or_blob_required)); + tv_clear(&tv); + } + } + } + if (skip) { + --emsg_skip; + } + + return fi; +} + +// TODO(ZyX-I): move to eval/ex_cmds + +/// Use the first item in a ":for" list. Advance to the next. +/// Assign the values to the variable (list). "arg" points to the first one. +/// +/// @return true when a valid item was found, false when at end of list or +/// something wrong. +bool next_for_item(void *fi_void, char *arg) +{ + forinfo_T *fi = (forinfo_T *)fi_void; + + if (fi->fi_blob != NULL) { + if (fi->fi_bi >= tv_blob_len(fi->fi_blob)) { + return false; + } + typval_T tv; + tv.v_type = VAR_NUMBER; + tv.v_lock = VAR_FIXED; + tv.vval.v_number = tv_blob_get(fi->fi_blob, fi->fi_bi); + fi->fi_bi++; + return ex_let_vars(arg, &tv, true, fi->fi_semicolon, fi->fi_varcount, false, NULL) == OK; + } + + if (fi->fi_string != NULL) { + const int len = utfc_ptr2len(fi->fi_string + fi->fi_byte_idx); + if (len == 0) { + return false; + } + typval_T tv; + tv.v_type = VAR_STRING; + tv.v_lock = VAR_FIXED; + tv.vval.v_string = xstrnsave(fi->fi_string + fi->fi_byte_idx, (size_t)len); + fi->fi_byte_idx += len; + const int result + = ex_let_vars(arg, &tv, true, fi->fi_semicolon, fi->fi_varcount, false, NULL) == OK; + xfree(tv.vval.v_string); + return result; + } + + listitem_T *item = fi->fi_lw.lw_item; + if (item == NULL) { + return false; + } else { + fi->fi_lw.lw_item = TV_LIST_ITEM_NEXT(fi->fi_list, item); + return (ex_let_vars(arg, TV_LIST_ITEM_TV(item), true, + fi->fi_semicolon, fi->fi_varcount, false, NULL) == OK); + } +} + +// TODO(ZyX-I): move to eval/ex_cmds + +/// Free the structure used to store info used by ":for". +void free_for_info(void *fi_void) +{ + forinfo_T *fi = (forinfo_T *)fi_void; + + if (fi == NULL) { + return; + } + if (fi->fi_list != NULL) { + tv_list_watch_remove(fi->fi_list, &fi->fi_lw); + tv_list_unref(fi->fi_list); + } else if (fi->fi_blob != NULL) { + tv_blob_unref(fi->fi_blob); + } else { + xfree(fi->fi_string); + } + xfree(fi); +} + +void set_context_for_expression(expand_T *xp, char *arg, cmdidx_T cmdidx) + FUNC_ATTR_NONNULL_ALL +{ + int got_eq = FALSE; + int c; + char *p; + + if (cmdidx == CMD_let || cmdidx == CMD_const) { + xp->xp_context = EXPAND_USER_VARS; + if (strpbrk(arg, "\"'+-*/%.=!?~|&$([<>,#") == NULL) { + // ":let var1 var2 ...": find last space. + for (p = arg + STRLEN(arg); p >= arg;) { + xp->xp_pattern = p; + MB_PTR_BACK(arg, p); + if (ascii_iswhite(*p)) { + break; + } + } + return; + } + } else { + xp->xp_context = cmdidx == CMD_call ? EXPAND_FUNCTIONS + : EXPAND_EXPRESSION; + } + while ((xp->xp_pattern = strpbrk(arg, "\"'+-*/%.=!?~|&$([<>,#")) != NULL) { + c = (uint8_t)(*xp->xp_pattern); + if (c == '&') { + c = (uint8_t)xp->xp_pattern[1]; + if (c == '&') { + ++xp->xp_pattern; + xp->xp_context = cmdidx != CMD_let || got_eq + ? EXPAND_EXPRESSION : EXPAND_NOTHING; + } else if (c != ' ') { + xp->xp_context = EXPAND_SETTINGS; + if ((c == 'l' || c == 'g') && xp->xp_pattern[2] == ':') { + xp->xp_pattern += 2; + } + } + } else if (c == '$') { + // environment variable + xp->xp_context = EXPAND_ENV_VARS; + } else if (c == '=') { + got_eq = TRUE; + xp->xp_context = EXPAND_EXPRESSION; + } else if (c == '#' + && xp->xp_context == EXPAND_EXPRESSION) { + // Autoload function/variable contains '#' + break; + } else if ((c == '<' || c == '#') + && xp->xp_context == EXPAND_FUNCTIONS + && vim_strchr(xp->xp_pattern, '(') == NULL) { + // Function name can start with "<SNR>" and contain '#'. + break; + } else if (cmdidx != CMD_let || got_eq) { + if (c == '"') { // string + while ((c = (uint8_t)(*++xp->xp_pattern)) != NUL && c != '"') { + if (c == '\\' && xp->xp_pattern[1] != NUL) { + xp->xp_pattern++; + } + } + xp->xp_context = EXPAND_NOTHING; + } else if (c == '\'') { // literal string + // Trick: '' is like stopping and starting a literal string. + while ((c = (uint8_t)(*++xp->xp_pattern)) != NUL && c != '\'') {} + xp->xp_context = EXPAND_NOTHING; + } else if (c == '|') { + if (xp->xp_pattern[1] == '|') { + ++xp->xp_pattern; + xp->xp_context = EXPAND_EXPRESSION; + } else { + xp->xp_context = EXPAND_COMMANDS; + } + } else { + xp->xp_context = EXPAND_EXPRESSION; + } + } else { + // Doesn't look like something valid, expand as an expression + // anyway. + xp->xp_context = EXPAND_EXPRESSION; + } + arg = xp->xp_pattern; + if (*arg != NUL) { + while ((c = (char_u)(*++arg)) != NUL && (c == ' ' || c == '\t')) {} + } + } + + // ":exe one two" completes "two" + if ((cmdidx == CMD_execute + || cmdidx == CMD_echo + || cmdidx == CMD_echon + || cmdidx == CMD_echomsg) + && xp->xp_context == EXPAND_EXPRESSION) { + for (;;) { + char *const n = (char *)skiptowhite((char_u *)arg); + + if (n == arg || ascii_iswhite_or_nul(*skipwhite(n))) { + break; + } + arg = skipwhite(n); + } + } + + xp->xp_pattern = arg; +} + +/// Delete all "menutrans_" variables. +void del_menutrans_vars(void) +{ + hash_lock(&globvarht); + HASHTAB_ITER(&globvarht, hi, { + if (STRNCMP(hi->hi_key, "menutrans_", 10) == 0) { + delete_var(&globvarht, hi); + } + }); + hash_unlock(&globvarht); +} + +/* + * Local string buffer for the next two functions to store a variable name + * with its prefix. Allocated in cat_prefix_varname(), freed later in + * get_user_var_name(). + */ + +static char *varnamebuf = NULL; +static size_t varnamebuflen = 0; + +/// Function to concatenate a prefix and a variable name. +char *cat_prefix_varname(int prefix, const char *name) + FUNC_ATTR_NONNULL_ALL +{ + size_t len = STRLEN(name) + 3; + + if (len > varnamebuflen) { + xfree(varnamebuf); + len += 10; // some additional space + varnamebuf = xmalloc(len); + varnamebuflen = len; + } + *varnamebuf = (char)prefix; + varnamebuf[1] = ':'; + STRCPY(varnamebuf + 2, name); + return varnamebuf; +} + +/// Function given to ExpandGeneric() to obtain the list of user defined +/// (global/buffer/window/built-in) variable names. +char *get_user_var_name(expand_T *xp, int idx) +{ + static size_t gdone; + static size_t bdone; + static size_t wdone; + static size_t tdone; + static size_t vidx; + static hashitem_T *hi; + + if (idx == 0) { + gdone = bdone = wdone = vidx = 0; + tdone = 0; + } + + // Global variables + if (gdone < globvarht.ht_used) { + if (gdone++ == 0) { + hi = globvarht.ht_array; + } else { + ++hi; + } + while (HASHITEM_EMPTY(hi)) { + ++hi; + } + if (STRNCMP("g:", xp->xp_pattern, 2) == 0) { + return cat_prefix_varname('g', (char *)hi->hi_key); + } + return (char *)hi->hi_key; + } + + // b: variables + const hashtab_T *ht = &prevwin_curwin()->w_buffer->b_vars->dv_hashtab; + if (bdone < ht->ht_used) { + if (bdone++ == 0) { + hi = ht->ht_array; + } else { + ++hi; + } + while (HASHITEM_EMPTY(hi)) { + ++hi; + } + return cat_prefix_varname('b', (char *)hi->hi_key); + } + + // w: variables + ht = &prevwin_curwin()->w_vars->dv_hashtab; + if (wdone < ht->ht_used) { + if (wdone++ == 0) { + hi = ht->ht_array; + } else { + ++hi; + } + while (HASHITEM_EMPTY(hi)) { + ++hi; + } + return cat_prefix_varname('w', (char *)hi->hi_key); + } + + // t: variables + ht = &curtab->tp_vars->dv_hashtab; + if (tdone < ht->ht_used) { + if (tdone++ == 0) { + hi = ht->ht_array; + } else { + ++hi; + } + while (HASHITEM_EMPTY(hi)) { + ++hi; + } + return cat_prefix_varname('t', (char *)hi->hi_key); + } + + // v: variables + if (vidx < ARRAY_SIZE(vimvars)) { + return cat_prefix_varname('v', vimvars[vidx++].vv_name); + } + + XFREE_CLEAR(varnamebuf); + varnamebuflen = 0; + return NULL; +} + +// TODO(ZyX-I): move to eval/expressions + +/// Does not use 'cpo' and always uses 'magic'. +/// +/// @return TRUE if "pat" matches "text". +int pattern_match(char *pat, char *text, bool ic) +{ + int matches = 0; + regmatch_T regmatch; + + // avoid 'l' flag in 'cpoptions' + char *save_cpo = p_cpo; + p_cpo = ""; + regmatch.regprog = vim_regcomp(pat, RE_MAGIC + RE_STRING); + if (regmatch.regprog != NULL) { + regmatch.rm_ic = ic; + matches = vim_regexec_nl(®match, (char_u *)text, (colnr_T)0); + vim_regfree(regmatch.regprog); + } + p_cpo = save_cpo; + return matches; +} + +/// Handle a name followed by "(". Both for just "name(arg)" and for +/// "expr->name(arg)". +/// +/// @param arg Points to "(", will be advanced +/// @param basetv "expr" for "expr->name(arg)" +/// +/// @return OK or FAIL. +static int eval_func(char **const arg, char *const name, const int name_len, typval_T *const rettv, + const bool evaluate, typval_T *const basetv) + FUNC_ATTR_NONNULL_ARG(1, 2, 4) +{ + char *s = name; + int len = name_len; + + if (!evaluate) { + check_vars((const char *)s, (size_t)len); + } + + // If "s" is the name of a variable of type VAR_FUNC + // use its contents. + partial_T *partial; + s = (char *)deref_func_name((const char *)s, &len, &partial, !evaluate); + + // Need to make a copy, in case evaluating the arguments makes + // the name invalid. + s = xmemdupz(s, (size_t)len); + + // Invoke the function. + funcexe_T funcexe = FUNCEXE_INIT; + funcexe.firstline = curwin->w_cursor.lnum; + funcexe.lastline = curwin->w_cursor.lnum; + funcexe.evaluate = evaluate; + funcexe.partial = partial; + funcexe.basetv = basetv; + int ret = get_func_tv((char_u *)s, len, rettv, (char_u **)arg, &funcexe); + + xfree(s); + + // If evaluate is false rettv->v_type was not set in + // get_func_tv, but it's needed in handle_subscript() to parse + // what follows. So set it here. + if (rettv->v_type == VAR_UNKNOWN && !evaluate && **arg == '(') { + rettv->vval.v_string = (char *)tv_empty_string; + rettv->v_type = VAR_FUNC; + } + + // Stop the expression evaluation when immediately + // aborting on error, or when an interrupt occurred or + // an exception was thrown but not caught. + if (evaluate && aborting()) { + if (ret == OK) { + tv_clear(rettv); + } + ret = FAIL; + } + return ret; +} + +// TODO(ZyX-I): move to eval/expressions + +/* + * The "evaluate" argument: When FALSE, the argument is only parsed but not + * executed. The function may return OK, but the rettv will be of type + * VAR_UNKNOWN. The function still returns FAIL for a syntax error. + */ + +/// Handle zero level expression. +/// This calls eval1() and handles error message and nextcmd. +/// Put the result in "rettv" when returning OK and "evaluate" is TRUE. +/// Note: "rettv.v_lock" is not set. +/// +/// @return OK or FAIL. +int eval0(char *arg, typval_T *rettv, char **nextcmd, int evaluate) +{ + int ret; + char *p; + const int did_emsg_before = did_emsg; + const int called_emsg_before = called_emsg; + + p = skipwhite(arg); + ret = eval1(&p, rettv, evaluate); + if (ret == FAIL || !ends_excmd(*p)) { + if (ret != FAIL) { + tv_clear(rettv); + } + // Report the invalid expression unless the expression evaluation has + // been cancelled due to an aborting error, an interrupt, or an + // exception, or we already gave a more specific error. + // Also check called_emsg for when using assert_fails(). + if (!aborting() && did_emsg == did_emsg_before + && called_emsg == called_emsg_before) { + semsg(_(e_invexpr2), arg); + } + ret = FAIL; + } + if (nextcmd != NULL) { + *nextcmd = (char *)check_nextcmd((char_u *)p); + } + + return ret; +} + +// TODO(ZyX-I): move to eval/expressions + +/// Handle top level expression: +/// expr2 ? expr1 : expr1 +/// +/// "arg" must point to the first non-white of the expression. +/// "arg" is advanced to the next non-white after the recognized expression. +/// +/// Note: "rettv.v_lock" is not set. +/// +/// @return OK or FAIL. +int eval1(char **arg, typval_T *rettv, int evaluate) +{ + int result; + typval_T var2; + + /* + * Get the first variable. + */ + if (eval2(arg, rettv, evaluate) == FAIL) { + return FAIL; + } + + if ((*arg)[0] == '?') { + result = FALSE; + if (evaluate) { + bool error = false; + + if (tv_get_number_chk(rettv, &error) != 0) { + result = true; + } + tv_clear(rettv); + if (error) { + return FAIL; + } + } + + /* + * Get the second variable. + */ + *arg = skipwhite(*arg + 1); + if (eval1(arg, rettv, evaluate && result) == FAIL) { // recursive! + return FAIL; + } + + /* + * Check for the ":". + */ + if ((*arg)[0] != ':') { + emsg(_("E109: Missing ':' after '?'")); + if (evaluate && result) { + tv_clear(rettv); + } + return FAIL; + } + + /* + * Get the third variable. + */ + *arg = skipwhite(*arg + 1); + if (eval1(arg, &var2, evaluate && !result) == FAIL) { // Recursive! + if (evaluate && result) { + tv_clear(rettv); + } + return FAIL; + } + if (evaluate && !result) { + *rettv = var2; + } + } + + return OK; +} + +// TODO(ZyX-I): move to eval/expressions + +/// Handle first level expression: +/// expr2 || expr2 || expr2 logical OR +/// +/// "arg" must point to the first non-white of the expression. +/// "arg" is advanced to the next non-white after the recognized expression. +/// +/// @return OK or FAIL. +static int eval2(char **arg, typval_T *rettv, int evaluate) +{ + typval_T var2; + long result; + int first; + bool error = false; + + /* + * Get the first variable. + */ + if (eval3(arg, rettv, evaluate) == FAIL) { + return FAIL; + } + + /* + * Repeat until there is no following "||". + */ + first = TRUE; + result = FALSE; + while ((*arg)[0] == '|' && (*arg)[1] == '|') { + if (evaluate && first) { + if (tv_get_number_chk(rettv, &error) != 0) { + result = true; + } + tv_clear(rettv); + if (error) { + return FAIL; + } + first = false; + } + + /* + * Get the second variable. + */ + *arg = skipwhite(*arg + 2); + if (eval3(arg, &var2, evaluate && !result) == FAIL) { + return FAIL; + } + + /* + * Compute the result. + */ + if (evaluate && !result) { + if (tv_get_number_chk(&var2, &error) != 0) { + result = true; + } + tv_clear(&var2); + if (error) { + return FAIL; + } + } + if (evaluate) { + rettv->v_type = VAR_NUMBER; + rettv->vval.v_number = result; + } + } + + return OK; +} + +// TODO(ZyX-I): move to eval/expressions + +/// Handle second level expression: +/// expr3 && expr3 && expr3 logical AND +/// +/// @param arg must point to the first non-white of the expression. +/// `arg` is advanced to the next non-white after the recognized expression. +/// +/// @return OK or FAIL. +static int eval3(char **arg, typval_T *rettv, int evaluate) +{ + typval_T var2; + long result; + int first; + bool error = false; + + /* + * Get the first variable. + */ + if (eval4(arg, rettv, evaluate) == FAIL) { + return FAIL; + } + + /* + * Repeat until there is no following "&&". + */ + first = TRUE; + result = TRUE; + while ((*arg)[0] == '&' && (*arg)[1] == '&') { + if (evaluate && first) { + if (tv_get_number_chk(rettv, &error) == 0) { + result = false; + } + tv_clear(rettv); + if (error) { + return FAIL; + } + first = false; + } + + /* + * Get the second variable. + */ + *arg = skipwhite(*arg + 2); + if (eval4(arg, &var2, evaluate && result) == FAIL) { + return FAIL; + } + + /* + * Compute the result. + */ + if (evaluate && result) { + if (tv_get_number_chk(&var2, &error) == 0) { + result = false; + } + tv_clear(&var2); + if (error) { + return FAIL; + } + } + if (evaluate) { + rettv->v_type = VAR_NUMBER; + rettv->vval.v_number = result; + } + } + + return OK; +} + +// TODO(ZyX-I): move to eval/expressions + +/// Handle third level expression: +/// var1 == var2 +/// var1 =~ var2 +/// var1 != var2 +/// var1 !~ var2 +/// var1 > var2 +/// var1 >= var2 +/// var1 < var2 +/// var1 <= var2 +/// var1 is var2 +/// var1 isnot var2 +/// +/// "arg" must point to the first non-white of the expression. +/// "arg" is advanced to the next non-white after the recognized expression. +/// +/// @return OK or FAIL. +static int eval4(char **arg, typval_T *rettv, int evaluate) +{ + typval_T var2; + char *p; + exprtype_T type = EXPR_UNKNOWN; + int len = 2; + bool ic; + + /* + * Get the first variable. + */ + if (eval5(arg, rettv, evaluate) == FAIL) { + return FAIL; + } + + p = *arg; + switch (p[0]) { + case '=': + if (p[1] == '=') { + type = EXPR_EQUAL; + } else if (p[1] == '~') { + type = EXPR_MATCH; + } + break; + case '!': + if (p[1] == '=') { + type = EXPR_NEQUAL; + } else if (p[1] == '~') { + type = EXPR_NOMATCH; + } + break; + case '>': + if (p[1] != '=') { + type = EXPR_GREATER; + len = 1; + } else { + type = EXPR_GEQUAL; + } + break; + case '<': + if (p[1] != '=') { + type = EXPR_SMALLER; + len = 1; + } else { + type = EXPR_SEQUAL; + } + break; + case 'i': + if (p[1] == 's') { + if (p[2] == 'n' && p[3] == 'o' && p[4] == 't') { + len = 5; + } + if (!isalnum(p[len]) && p[len] != '_') { + type = len == 2 ? EXPR_IS : EXPR_ISNOT; + } + } + break; + } + + /* + * If there is a comparative operator, use it. + */ + if (type != EXPR_UNKNOWN) { + // extra question mark appended: ignore case + if (p[len] == '?') { + ic = true; + len++; + } else if (p[len] == '#') { // extra '#' appended: match case + ic = false; + len++; + } else { // nothing appended: use 'ignorecase' + ic = p_ic; + } + + // Get the second variable. + *arg = skipwhite(p + len); + if (eval5(arg, &var2, evaluate) == FAIL) { + tv_clear(rettv); + return FAIL; + } + if (evaluate) { + const int ret = typval_compare(rettv, &var2, type, ic); + + tv_clear(&var2); + return ret; + } + } + + return OK; +} + +// TODO(ZyX-I): move to eval/expressions + +/// Handle fourth level expression: +/// + number addition +/// - number subtraction +/// . string concatenation +/// .. string concatenation +/// +/// @param arg must point to the first non-white of the expression. +/// `arg` is advanced to the next non-white after the recognized expression. +/// +/// @return OK or FAIL. +static int eval5(char **arg, typval_T *rettv, int evaluate) +{ + typval_T var2; + typval_T var3; + int op; + varnumber_T n1, n2; + float_T f1 = 0, f2 = 0; + char *p; + + /* + * Get the first variable. + */ + if (eval6(arg, rettv, evaluate, FALSE) == FAIL) { + return FAIL; + } + + /* + * Repeat computing, until no '+', '-' or '.' is following. + */ + for (;;) { + op = (char_u)(**arg); + if (op != '+' && op != '-' && op != '.') { + break; + } + + if ((op != '+' || (rettv->v_type != VAR_LIST && rettv->v_type != VAR_BLOB)) + && (op == '.' || rettv->v_type != VAR_FLOAT)) { + // For "list + ...", an illegal use of the first operand as + // a number cannot be determined before evaluating the 2nd + // operand: if this is also a list, all is ok. + // For "something . ...", "something - ..." or "non-list + ...", + // we know that the first operand needs to be a string or number + // without evaluating the 2nd operand. So check before to avoid + // side effects after an error. + if (evaluate && !tv_check_str(rettv)) { + tv_clear(rettv); + return FAIL; + } + } + + /* + * Get the second variable. + */ + if (op == '.' && *(*arg + 1) == '.') { // ..string concatenation + (*arg)++; + } + *arg = skipwhite(*arg + 1); + if (eval6(arg, &var2, evaluate, op == '.') == FAIL) { + tv_clear(rettv); + return FAIL; + } + + if (evaluate) { + /* + * Compute the result. + */ + if (op == '.') { + char buf1[NUMBUFLEN]; + char buf2[NUMBUFLEN]; + // s1 already checked + const char *const s1 = tv_get_string_buf(rettv, buf1); + const char *const s2 = tv_get_string_buf_chk(&var2, buf2); + if (s2 == NULL) { // Type error? + tv_clear(rettv); + tv_clear(&var2); + return FAIL; + } + p = (char *)concat_str((const char_u *)s1, (const char_u *)s2); + tv_clear(rettv); + rettv->v_type = VAR_STRING; + rettv->vval.v_string = p; + } else if (op == '+' && rettv->v_type == VAR_BLOB + && var2.v_type == VAR_BLOB) { + const blob_T *const b1 = rettv->vval.v_blob; + const blob_T *const b2 = var2.vval.v_blob; + blob_T *const b = tv_blob_alloc(); + + for (int i = 0; i < tv_blob_len(b1); i++) { + ga_append(&b->bv_ga, (char)tv_blob_get(b1, i)); + } + for (int i = 0; i < tv_blob_len(b2); i++) { + ga_append(&b->bv_ga, (char)tv_blob_get(b2, i)); + } + + tv_clear(rettv); + tv_blob_set_ret(rettv, b); + } else if (op == '+' && rettv->v_type == VAR_LIST + && var2.v_type == VAR_LIST) { + // Concatenate Lists. + if (tv_list_concat(rettv->vval.v_list, var2.vval.v_list, &var3) + == FAIL) { + tv_clear(rettv); + tv_clear(&var2); + return FAIL; + } + tv_clear(rettv); + *rettv = var3; + } else { + bool error = false; + + if (rettv->v_type == VAR_FLOAT) { + f1 = rettv->vval.v_float; + n1 = 0; + } else { + n1 = tv_get_number_chk(rettv, &error); + if (error) { + // This can only happen for "list + non-list" or + // "blob + non-blob". For "non-list + ..." or + // "something - ...", we returned before evaluating the + // 2nd operand. + tv_clear(rettv); + tv_clear(&var2); + return FAIL; + } + if (var2.v_type == VAR_FLOAT) { + f1 = (float_T)n1; + } + } + if (var2.v_type == VAR_FLOAT) { + f2 = var2.vval.v_float; + n2 = 0; + } else { + n2 = tv_get_number_chk(&var2, &error); + if (error) { + tv_clear(rettv); + tv_clear(&var2); + return FAIL; + } + if (rettv->v_type == VAR_FLOAT) { + f2 = (float_T)n2; + } + } + tv_clear(rettv); + + // If there is a float on either side the result is a float. + if (rettv->v_type == VAR_FLOAT || var2.v_type == VAR_FLOAT) { + if (op == '+') { + f1 = f1 + f2; + } else { + f1 = f1 - f2; + } + rettv->v_type = VAR_FLOAT; + rettv->vval.v_float = f1; + } else { + if (op == '+') { + n1 = n1 + n2; + } else { + n1 = n1 - n2; + } + rettv->v_type = VAR_NUMBER; + rettv->vval.v_number = n1; + } + } + tv_clear(&var2); + } + } + return OK; +} + +// TODO(ZyX-I): move to eval/expressions + +/// Handle fifth level expression: +/// - * number multiplication +/// - / number division +/// - % number modulo +/// +/// @param[in,out] arg Points to the first non-whitespace character of the +/// expression. Is advanced to the next non-whitespace +/// character after the recognized expression. +/// @param[out] rettv Location where result is saved. +/// @param[in] evaluate If not true, rettv is not populated. +/// @param[in] want_string True if "." is string_concatenation, otherwise +/// float +/// @return OK or FAIL. +static int eval6(char **arg, typval_T *rettv, int evaluate, int want_string) + FUNC_ATTR_NO_SANITIZE_UNDEFINED +{ + typval_T var2; + int op; + varnumber_T n1, n2; + bool use_float = false; + float_T f1 = 0, f2 = 0; + bool error = false; + + /* + * Get the first variable. + */ + if (eval7(arg, rettv, evaluate, want_string) == FAIL) { + return FAIL; + } + + /* + * Repeat computing, until no '*', '/' or '%' is following. + */ + for (;;) { + op = (char_u)(**arg); + if (op != '*' && op != '/' && op != '%') { + break; + } + + if (evaluate) { + if (rettv->v_type == VAR_FLOAT) { + f1 = rettv->vval.v_float; + use_float = true; + n1 = 0; + } else { + n1 = tv_get_number_chk(rettv, &error); + } + tv_clear(rettv); + if (error) { + return FAIL; + } + } else { + n1 = 0; + } + + /* + * Get the second variable. + */ + *arg = skipwhite(*arg + 1); + if (eval7(arg, &var2, evaluate, false) == FAIL) { + return FAIL; + } + + if (evaluate) { + if (var2.v_type == VAR_FLOAT) { + if (!use_float) { + f1 = (float_T)n1; + use_float = true; + } + f2 = var2.vval.v_float; + n2 = 0; + } else { + n2 = tv_get_number_chk(&var2, &error); + tv_clear(&var2); + if (error) { + return FAIL; + } + if (use_float) { + f2 = (float_T)n2; + } + } + + /* + * Compute the result. + * When either side is a float the result is a float. + */ + if (use_float) { + if (op == '*') { + f1 = f1 * f2; + } else if (op == '/') { + // uncrustify:off + + // Division by zero triggers error from AddressSanitizer + f1 = (f2 == 0 ? ( +#ifdef NAN + f1 == 0 ? (float_T)NAN : +#endif + (f1 > 0 ? (float_T)INFINITY : (float_T)-INFINITY)) : f1 / f2); + + // uncrustify:on + } else { + emsg(_("E804: Cannot use '%' with Float")); + return FAIL; + } + rettv->v_type = VAR_FLOAT; + rettv->vval.v_float = f1; + } else { + if (op == '*') { + n1 = n1 * n2; + } else if (op == '/') { + n1 = num_divide(n1, n2); + } else { + n1 = num_modulus(n1, n2); + } + rettv->v_type = VAR_NUMBER; + rettv->vval.v_number = n1; + } + } + } + + return OK; +} + +// TODO(ZyX-I): move to eval/expressions + +/// Handle sixth level expression: +/// number number constant +/// 0zFFFFFFFF Blob constant +/// "string" string constant +/// 'string' literal string constant +/// &option-name option value +/// @r register contents +/// identifier variable value +/// function() function call +/// $VAR environment variable +/// (expression) nested expression +/// [expr, expr] List +/// {key: val, key: val} Dictionary +/// #{key: val, key: val} Dictionary with literal keys +/// +/// Also handle: +/// ! in front logical NOT +/// - in front unary minus +/// + in front unary plus (ignored) +/// trailing [] subscript in String or List +/// trailing .name entry in Dictionary +/// trailing ->name() method call +/// +/// "arg" must point to the first non-white of the expression. +/// "arg" is advanced to the next non-white after the recognized expression. +/// +/// @param want_string after "." operator +/// +/// @return OK or FAIL. +static int eval7(char **arg, typval_T *rettv, int evaluate, int want_string) +{ + varnumber_T n; + int len; + char *s; + const char *start_leader, *end_leader; + int ret = OK; + char *alias; + + // Initialise variable so that tv_clear() can't mistake this for a + // string and free a string that isn't there. + rettv->v_type = VAR_UNKNOWN; + + // Skip '!', '-' and '+' characters. They are handled later. + start_leader = *arg; + while (**arg == '!' || **arg == '-' || **arg == '+') { + *arg = skipwhite(*arg + 1); + } + end_leader = *arg; + + switch (**arg) { + // Number constant. + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': { + char *p = skipdigits(*arg + 1); + int get_float = false; + + // We accept a float when the format matches + // "[0-9]\+\.[0-9]\+\([eE][+-]\?[0-9]\+\)\?". This is very + // strict to avoid backwards compatibility problems. + // Don't look for a float after the "." operator, so that + // ":let vers = 1.2.3" doesn't fail. + if (!want_string && p[0] == '.' && ascii_isdigit(p[1])) { + get_float = true; + p = skipdigits(p + 2); + if (*p == 'e' || *p == 'E') { + ++p; + if (*p == '-' || *p == '+') { + ++p; + } + if (!ascii_isdigit(*p)) { + get_float = false; + } else { + p = skipdigits(p + 1); + } + } + if (ASCII_ISALPHA(*p) || *p == '.') { + get_float = false; + } + } + if (get_float) { + float_T f; + + *arg += string2float(*arg, &f); + if (evaluate) { + rettv->v_type = VAR_FLOAT; + rettv->vval.v_float = f; + } + } else if (**arg == '0' && ((*arg)[1] == 'z' || (*arg)[1] == 'Z')) { + blob_T *blob = NULL; + // Blob constant: 0z0123456789abcdef + if (evaluate) { + blob = tv_blob_alloc(); + } + char *bp; + for (bp = *arg + 2; ascii_isxdigit(bp[0]); bp += 2) { + if (!ascii_isxdigit(bp[1])) { + if (blob != NULL) { + emsg(_("E973: Blob literal should have an even number of hex " + "characters")); + ga_clear(&blob->bv_ga); + XFREE_CLEAR(blob); + } + ret = FAIL; + break; + } + if (blob != NULL) { + ga_append(&blob->bv_ga, (char)((hex2nr(*bp) << 4) + hex2nr(*(bp + 1)))); + } + if (bp[2] == '.' && ascii_isxdigit(bp[3])) { + bp++; + } + } + if (blob != NULL) { + tv_blob_set_ret(rettv, blob); + } + *arg = bp; + } else { + // decimal, hex or octal number + vim_str2nr((char_u *)(*arg), NULL, &len, STR2NR_ALL, &n, NULL, 0, true); + if (len == 0) { + semsg(_(e_invexpr2), *arg); + ret = FAIL; + break; + } + *arg += len; + if (evaluate) { + rettv->v_type = VAR_NUMBER; + rettv->vval.v_number = n; + } + } + break; + } + + // String constant: "string". + case '"': + ret = get_string_tv(arg, rettv, evaluate); + break; + + // Literal string constant: 'str''ing'. + case '\'': + ret = get_lit_string_tv(arg, rettv, evaluate); + break; + + // List: [expr, expr] + case '[': + ret = get_list_tv(arg, rettv, evaluate); + break; + + // Dictionary: #{key: val, key: val} + case '#': + if ((*arg)[1] == '{') { + (*arg)++; + ret = dict_get_tv(arg, rettv, evaluate, true); + } else { + ret = NOTDONE; + } + break; + + // Lambda: {arg, arg -> expr} + // Dictionary: {'key': val, 'key': val} + case '{': + ret = get_lambda_tv((char_u **)arg, rettv, evaluate); + if (ret == NOTDONE) { + ret = dict_get_tv(arg, rettv, evaluate, false); + } + break; + + // Option value: &name + case '&': + ret = get_option_tv((const char **)arg, rettv, evaluate); + break; + // Environment variable: $VAR. + case '$': + ret = get_env_tv(arg, rettv, evaluate); + break; + + // Register contents: @r. + case '@': + ++*arg; + int regname = mb_cptr2char_adv((const char_u**) arg); + if (evaluate) { + rettv->v_type = VAR_STRING; + rettv->vval.v_string = get_reg_contents(regname, kGRegExprSrc); + } + // if (**arg != NUL) { + // ++*arg; + // } + break; + + // nested expression: (expression). + case '(': + *arg = skipwhite(*arg + 1); + ret = eval1(arg, rettv, evaluate); // recursive! + if (**arg == ')') { + ++*arg; + } else if (ret == OK) { + emsg(_("E110: Missing ')'")); + tv_clear(rettv); + ret = FAIL; + } + break; + + default: + ret = NOTDONE; + break; + } + + if (ret == NOTDONE) { + // Must be a variable or function name. + // Can also be a curly-braces kind of name: {expr}. + s = *arg; + len = get_name_len((const char **)arg, &alias, evaluate, true); + if (alias != NULL) { + s = alias; + } + + if (len <= 0) { + ret = FAIL; + } else { + if (**arg == '(') { // recursive! + ret = eval_func(arg, s, len, rettv, evaluate, NULL); + } else if (evaluate) { + ret = get_var_tv((const char *)s, len, rettv, NULL, true, false); + } else { + check_vars((const char *)s, (size_t)len); + ret = OK; + } + } + xfree(alias); + } + + *arg = skipwhite(*arg); + + // Handle following '[', '(' and '.' for expr[expr], expr.name, + // expr(expr), expr->name(expr) + if (ret == OK) { + ret = handle_subscript((const char **)arg, rettv, evaluate, true, + (char *)start_leader, &end_leader); + } + + // Apply logical NOT and unary '-', from right to left, ignore '+'. + if (ret == OK && evaluate && end_leader > start_leader) { + ret = eval7_leader(rettv, (char *)start_leader, &end_leader); + } + return ret; +} + +/// Apply the leading "!" and "-" before an eval7 expression to "rettv". +/// Adjusts "end_leaderp" until it is at "start_leader". +/// +/// @return OK on success, FAIL on failure. +static int eval7_leader(typval_T *const rettv, const char *const start_leader, + const char **const end_leaderp) + FUNC_ATTR_NONNULL_ALL +{ + const char *end_leader = (char *)(*end_leaderp); + int ret = OK; + bool error = false; + varnumber_T val = 0; + float_T f = 0.0; + + if (rettv->v_type == VAR_FLOAT) { + f = rettv->vval.v_float; + } else { + val = tv_get_number_chk(rettv, &error); + } + if (error) { + tv_clear(rettv); + ret = FAIL; + } else { + while (end_leader > start_leader) { + end_leader--; + if (*end_leader == '!') { + if (rettv->v_type == VAR_FLOAT) { + f = !(bool)f; + } else { + val = !val; + } + } else if (*end_leader == '-') { + if (rettv->v_type == VAR_FLOAT) { + f = -f; + } else { + val = -val; + } + } + } + if (rettv->v_type == VAR_FLOAT) { + tv_clear(rettv); + rettv->vval.v_float = f; + } else { + tv_clear(rettv); + rettv->v_type = VAR_NUMBER; + rettv->vval.v_number = val; + } + } + + *end_leaderp = end_leader; + return ret; +} + +/// Call the function referred to in "rettv". +/// @param lua_funcname If `rettv` refers to a v:lua function, this must point +/// to the name of the Lua function to call (after the +/// "v:lua." prefix). +/// @return OK on success, FAIL on failure. +static int call_func_rettv(char **const arg, typval_T *const rettv, const bool evaluate, + dict_T *const selfdict, typval_T *const basetv, + const char *const lua_funcname) + FUNC_ATTR_NONNULL_ARG(1, 2) +{ + partial_T *pt = NULL; + typval_T functv; + const char *funcname; + bool is_lua = false; + + // need to copy the funcref so that we can clear rettv + if (evaluate) { + functv = *rettv; + rettv->v_type = VAR_UNKNOWN; + + // Invoke the function. Recursive! + if (functv.v_type == VAR_PARTIAL) { + pt = functv.vval.v_partial; + is_lua = is_luafunc(pt); + funcname = is_lua ? lua_funcname : partial_name(pt); + } else { + funcname = functv.vval.v_string; + } + } else { + funcname = ""; + } + + funcexe_T funcexe = FUNCEXE_INIT; + funcexe.firstline = curwin->w_cursor.lnum; + funcexe.lastline = curwin->w_cursor.lnum; + funcexe.evaluate = evaluate; + funcexe.partial = pt; + funcexe.selfdict = selfdict; + funcexe.basetv = basetv; + const int ret = get_func_tv((char_u *)funcname, is_lua ? (int)(*arg - funcname) : -1, rettv, + (char_u **)arg, &funcexe); + + // Clear the funcref afterwards, so that deleting it while + // evaluating the arguments is possible (see test55). + if (evaluate) { + tv_clear(&functv); + } + + return ret; +} + +/// Evaluate "->method()". +/// +/// @param verbose if true, give error messages. +/// @param *arg points to the '-'. +/// +/// @return FAIL or OK. +/// +/// @note "*arg" is advanced to after the ')'. +static int eval_lambda(char **const arg, typval_T *const rettv, const bool evaluate, + const bool verbose) + FUNC_ATTR_NONNULL_ALL +{ + // Skip over the ->. + *arg += 2; + typval_T base = *rettv; + rettv->v_type = VAR_UNKNOWN; + + int ret = get_lambda_tv((char_u **)arg, rettv, evaluate); + if (ret != OK) { + return FAIL; + } else if (**arg != '(') { + if (verbose) { + if (*skipwhite(*arg) == '(') { + emsg(_(e_nowhitespace)); + } else { + semsg(_(e_missingparen), "lambda"); + } + } + tv_clear(rettv); + ret = FAIL; + } else { + ret = call_func_rettv(arg, rettv, evaluate, NULL, &base, NULL); + } + + // Clear the funcref afterwards, so that deleting it while + // evaluating the arguments is possible (see test55). + if (evaluate) { + tv_clear(&base); + } + + return ret; +} + +/// Evaluate "->method()" or "->v:lua.method()". +/// +/// @param *arg points to the '-'. +/// +/// @return FAIL or OK. "*arg" is advanced to after the ')'. +static int eval_method(char **const arg, typval_T *const rettv, const bool evaluate, + const bool verbose) + FUNC_ATTR_NONNULL_ALL +{ + // Skip over the ->. + *arg += 2; + typval_T base = *rettv; + rettv->v_type = VAR_UNKNOWN; + + // Locate the method name. + int len; + char *name = *arg; + char *lua_funcname = NULL; + if (STRNCMP(name, "v:lua.", 6) == 0) { + lua_funcname = name + 6; + *arg = (char *)skip_luafunc_name((const char *)lua_funcname); + *arg = skipwhite(*arg); // to detect trailing whitespace later + len = (int)(*arg - lua_funcname); + } else { + char *alias; + len = get_name_len((const char **)arg, &alias, evaluate, true); + if (alias != NULL) { + name = alias; + } + } + + int ret; + if (len <= 0) { + if (verbose) { + if (lua_funcname == NULL) { + emsg(_("E260: Missing name after ->")); + } else { + semsg(_(e_invexpr2), name); + } + } + ret = FAIL; + } else { + if (**arg != '(') { + if (verbose) { + semsg(_(e_missingparen), name); + } + ret = FAIL; + } else if (ascii_iswhite((*arg)[-1])) { + if (verbose) { + emsg(_(e_nowhitespace)); + } + ret = FAIL; + } else if (lua_funcname != NULL) { + if (evaluate) { + rettv->v_type = VAR_PARTIAL; + rettv->vval.v_partial = vvlua_partial; + rettv->vval.v_partial->pt_refcount++; + } + ret = call_func_rettv(arg, rettv, evaluate, NULL, &base, lua_funcname); + } else { + ret = eval_func(arg, name, len, rettv, evaluate, &base); + } + } + + // Clear the funcref afterwards, so that deleting it while + // evaluating the arguments is possible (see test55). + if (evaluate) { + tv_clear(&base); + } + + return ret; +} + +// TODO(ZyX-I): move to eval/expressions + +/// Evaluate an "[expr]" or "[expr:expr]" index. Also "dict.key". +/// "*arg" points to the '[' or '.'. +/// +/// @param verbose give error messages +/// +/// @returns FAIL or OK. "*arg" is advanced to after the ']'. +static int eval_index(char **arg, typval_T *rettv, int evaluate, int verbose) +{ + bool empty1 = false; + bool empty2 = false; + long n1, n2 = 0; + ptrdiff_t len = -1; + int range = false; + char *key = NULL; + + switch (rettv->v_type) { + case VAR_FUNC: + case VAR_PARTIAL: + if (verbose) { + emsg(_("E695: Cannot index a Funcref")); + } + return FAIL; + case VAR_FLOAT: + if (verbose) { + emsg(_(e_float_as_string)); + } + return FAIL; + case VAR_BOOL: + case VAR_SPECIAL: + if (verbose) { + emsg(_("E909: Cannot index a special variable")); + } + return FAIL; + case VAR_UNKNOWN: + if (evaluate) { + return FAIL; + } + FALLTHROUGH; + case VAR_STRING: + case VAR_NUMBER: + case VAR_LIST: + case VAR_DICT: + case VAR_BLOB: + break; + } + + typval_T var1 = TV_INITIAL_VALUE; + typval_T var2 = TV_INITIAL_VALUE; + if (**arg == '.') { + /* + * dict.name + */ + key = *arg + 1; + for (len = 0; ASCII_ISALNUM(key[len]) || key[len] == '_'; len++) {} + if (len == 0) { + return FAIL; + } + *arg = skipwhite(key + len); + } else { + /* + * something[idx] + * + * Get the (first) variable from inside the []. + */ + *arg = skipwhite(*arg + 1); + if (**arg == ':') { + empty1 = true; + } else if (eval1(arg, &var1, evaluate) == FAIL) { // Recursive! + return FAIL; + } else if (evaluate && !tv_check_str(&var1)) { + // Not a number or string. + tv_clear(&var1); + return FAIL; + } + + /* + * Get the second variable from inside the [:]. + */ + if (**arg == ':') { + range = true; + *arg = skipwhite(*arg + 1); + if (**arg == ']') { + empty2 = true; + } else if (eval1(arg, &var2, evaluate) == FAIL) { // Recursive! + if (!empty1) { + tv_clear(&var1); + } + return FAIL; + } else if (evaluate && !tv_check_str(&var2)) { + // Not a number or string. + if (!empty1) { + tv_clear(&var1); + } + tv_clear(&var2); + return FAIL; + } + } + + // Check for the ']'. + if (**arg != ']') { + if (verbose) { + emsg(_(e_missbrac)); + } + tv_clear(&var1); + if (range) { + tv_clear(&var2); + } + return FAIL; + } + *arg = skipwhite(*arg + 1); // skip the ']' + } + + if (evaluate) { + n1 = 0; + if (!empty1 && rettv->v_type != VAR_DICT && !tv_is_luafunc(rettv)) { + n1 = tv_get_number(&var1); + tv_clear(&var1); + } + if (range) { + if (empty2) { + n2 = -1; + } else { + n2 = tv_get_number(&var2); + tv_clear(&var2); + } + } + + switch (rettv->v_type) { + case VAR_NUMBER: + case VAR_STRING: { + const char *const s = tv_get_string(rettv); + char *v; + len = (ptrdiff_t)strlen(s); + if (range) { + // The resulting variable is a substring. If the indexes + // are out of range the result is empty. + if (n1 < 0) { + n1 = len + n1; + if (n1 < 0) { + n1 = 0; + } + } + if (n2 < 0) { + n2 = len + n2; + } else if (n2 >= len) { + n2 = len; + } + if (n1 >= len || n2 < 0 || n1 > n2) { + v = NULL; + } else { + v = xmemdupz(s + n1, (size_t)(n2 - n1 + 1)); + } + } else { + // The resulting variable is a string of a single + // character. If the index is too big or negative the + // result is empty. + if (n1 >= len || n1 < 0) { + v = NULL; + } else { + v = xmemdupz(s + n1, 1); + } + } + tv_clear(rettv); + rettv->v_type = VAR_STRING; + rettv->vval.v_string = v; + break; + } + case VAR_BLOB: + len = tv_blob_len(rettv->vval.v_blob); + if (range) { + // The resulting variable is a sub-blob. If the indexes + // are out of range the result is empty. + if (n1 < 0) { + n1 = len + n1; + if (n1 < 0) { + n1 = 0; + } + } + if (n2 < 0) { + n2 = len + n2; + } else if (n2 >= len) { + n2 = len - 1; + } + if (n1 >= len || n2 < 0 || n1 > n2) { + tv_clear(rettv); + rettv->v_type = VAR_BLOB; + rettv->vval.v_blob = NULL; + } else { + blob_T *const blob = tv_blob_alloc(); + ga_grow(&blob->bv_ga, (int)(n2 - n1 + 1)); + blob->bv_ga.ga_len = (int)(n2 - n1 + 1); + for (long i = n1; i <= n2; i++) { + tv_blob_set(blob, (int)(i - n1), tv_blob_get(rettv->vval.v_blob, (int)i)); + } + tv_clear(rettv); + tv_blob_set_ret(rettv, blob); + } + } else { + // The resulting variable is a byte value. + // If the index is too big or negative that is an error. + if (n1 < 0) { + n1 = len + n1; + } + if (n1 < len && n1 >= 0) { + const int v = (int)tv_blob_get(rettv->vval.v_blob, (int)n1); + tv_clear(rettv); + rettv->v_type = VAR_NUMBER; + rettv->vval.v_number = v; + } else { + semsg(_(e_blobidx), (int64_t)n1); + } + } + break; + case VAR_LIST: + len = tv_list_len(rettv->vval.v_list); + if (n1 < 0) { + n1 = len + n1; + } + if (!empty1 && (n1 < 0 || n1 >= len)) { + // For a range we allow invalid values and return an empty + // list. A list index out of range is an error. + if (!range) { + if (verbose) { + semsg(_(e_listidx), (int64_t)n1); + } + return FAIL; + } + n1 = len; + } + if (range) { + list_T *l; + listitem_T *item; + + if (n2 < 0) { + n2 = len + n2; + } else if (n2 >= len) { + n2 = len - 1; + } + if (!empty2 && (n2 < 0 || n2 + 1 < n1)) { + n2 = -1; + } + l = tv_list_alloc(n2 - n1 + 1); + item = tv_list_find(rettv->vval.v_list, (int)n1); + while (n1++ <= n2) { + tv_list_append_tv(l, TV_LIST_ITEM_TV(item)); + item = TV_LIST_ITEM_NEXT(rettv->vval.v_list, item); + } + tv_clear(rettv); + tv_list_set_ret(rettv, l); + } else { + tv_copy(TV_LIST_ITEM_TV(tv_list_find(rettv->vval.v_list, (int)n1)), &var1); + tv_clear(rettv); + *rettv = var1; + } + break; + case VAR_DICT: { + if (range) { + if (verbose) { + emsg(_(e_dictrange)); + } + if (len == -1) { + tv_clear(&var1); + } + return FAIL; + } + + if (len == -1) { + key = (char *)tv_get_string_chk(&var1); + if (key == NULL) { + tv_clear(&var1); + return FAIL; + } + } + + dictitem_T *const item = tv_dict_find(rettv->vval.v_dict, + (const char *)key, len); + + if (item == NULL && verbose) { + semsg(_(e_dictkey), key); + } + if (len == -1) { + tv_clear(&var1); + } + if (item == NULL || tv_is_luafunc(&item->di_tv)) { + return FAIL; + } + + tv_copy(&item->di_tv, &var1); + tv_clear(rettv); + *rettv = var1; + break; + } + case VAR_BOOL: + case VAR_SPECIAL: + case VAR_FUNC: + case VAR_FLOAT: + case VAR_PARTIAL: + case VAR_UNKNOWN: + break; // Not evaluating, skipping over subscript + } + } + + return OK; +} + +// TODO(ZyX-I): move to eval/executor + +/// Get an option value +/// +/// @param[in,out] arg Points to the '&' or '+' before the option name. Is +/// advanced to the character after the option name. +/// @param[out] rettv Location where result is saved. +/// @param[in] evaluate If not true, rettv is not populated. +/// +/// @return OK or FAIL. +int get_option_tv(const char **const arg, typval_T *const rettv, const bool evaluate) + FUNC_ATTR_NONNULL_ARG(1) +{ + long numval; + char *stringval; + getoption_T opt_type; + bool working = (**arg == '+'); // has("+option") + int ret = OK; + int opt_flags; + + // Isolate the option name and find its value. + char *option_end = (char *)find_option_end(arg, &opt_flags); + if (option_end == NULL) { + if (rettv != NULL) { + semsg(_("E112: Option name missing: %s"), *arg); + } + return FAIL; + } + + if (!evaluate) { + *arg = option_end; + return OK; + } + + char c = *option_end; + *option_end = NUL; + opt_type = get_option_value(*arg, &numval, + rettv == NULL ? NULL : &stringval, opt_flags); + + if (opt_type == gov_unknown) { + if (rettv != NULL) { + semsg(_("E113: Unknown option: %s"), *arg); + } + ret = FAIL; + } else if (rettv != NULL) { + if (opt_type == gov_hidden_string) { + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; + } else if (opt_type == gov_hidden_bool || opt_type == gov_hidden_number) { + rettv->v_type = VAR_NUMBER; + rettv->vval.v_number = 0; + } else if (opt_type == gov_bool || opt_type == gov_number) { + rettv->v_type = VAR_NUMBER; + rettv->vval.v_number = numval; + } else { // string option + rettv->v_type = VAR_STRING; + rettv->vval.v_string = stringval; + } + } else if (working && (opt_type == gov_hidden_bool + || opt_type == gov_hidden_number + || opt_type == gov_hidden_string)) { + ret = FAIL; + } + + *option_end = c; // put back for error messages + *arg = option_end; + + return ret; +} + +/// Allocate a variable for a string constant. +/// +/// @return OK or FAIL. +static int get_string_tv(char **arg, typval_T *rettv, int evaluate) +{ + char *p; + unsigned int extra = 0; + + /* + * Find the end of the string, skipping backslashed characters. + */ + for (p = *arg + 1; *p != NUL && *p != '"'; MB_PTR_ADV(p)) { + if (*p == '\\' && p[1] != NUL) { + p++; + // A "\<x>" form occupies at least 4 characters, and produces up + // to 9 characters (6 for the char and 3 for a modifier): + // reserve space for 5 extra. + if (*p == '<') { + extra += 5; + } + } + } + + if (*p != '"') { + semsg(_("E114: Missing quote: %s"), *arg); + return FAIL; + } + + // If only parsing, set *arg and return here + if (!evaluate) { + *arg = p + 1; + return OK; + } + + /* + * Copy the string into allocated memory, handling backslashed + * characters. + */ + const int len = (int)(p - *arg + extra); + char *name = xmalloc((size_t)len); + rettv->v_type = VAR_STRING; + rettv->vval.v_string = name; + + for (p = *arg + 1; *p != NUL && *p != '"';) { + if (*p == '\\') { + switch (*++p) { + case 'b': + *name++ = BS; ++p; break; + case 'e': + *name++ = ESC; ++p; break; + case 'f': + *name++ = FF; ++p; break; + case 'n': + *name++ = NL; ++p; break; + case 'r': + *name++ = CAR; ++p; break; + case 't': + *name++ = TAB; ++p; break; + + case 'X': // hex: "\x1", "\x12" + case 'x': + case 'u': // Unicode: "\u0023" + case 'U': + if (ascii_isxdigit(p[1])) { + int n, nr; + int c = toupper(*p); + + if (c == 'X') { + n = 2; + } else if (*p == 'u') { + n = 4; + } else { + n = 8; + } + nr = 0; + while (--n >= 0 && ascii_isxdigit(p[1])) { + ++p; + nr = (nr << 4) + hex2nr(*p); + } + p++; + // For "\u" store the number according to + // 'encoding'. + if (c != 'X') { + name += utf_char2bytes(nr, name); + } else { + *name++ = (char)nr; + } + } + break; + + // octal: "\1", "\12", "\123" + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + *name = (char)(*p++ - '0'); + if (*p >= '0' && *p <= '7') { + *name = (char)((*name << 3) + *p++ - '0'); + if (*p >= '0' && *p <= '7') { + *name = (char)((*name << 3) + *p++ - '0'); + } + } + ++name; + break; + + // Special key, e.g.: "\<C-W>" + case '<': { + int flags = FSK_KEYCODE | FSK_IN_STRING; + + if (p[1] != '*') { + flags |= FSK_SIMPLIFY; + } + extra = trans_special((const char_u **)&p, STRLEN(p), (char_u *)name, flags, false, NULL); + if (extra != 0) { + name += extra; + if (name >= rettv->vval.v_string + len) { + iemsg("get_string_tv() used more space than allocated"); + } + break; + } + } + FALLTHROUGH; + + default: + mb_copy_char((const char_u **)&p, (char_u **)&name); + break; + } + } else { + mb_copy_char((const char_u **)&p, (char_u **)&name); + } + } + *name = NUL; + if (*p != NUL) { // just in case + p++; + } + *arg = p; + + return OK; +} + +/// Allocate a variable for a 'str''ing' constant. +/// +/// @return OK or FAIL. +static int get_lit_string_tv(char **arg, typval_T *rettv, int evaluate) +{ + char *p; + char *str; + int reduce = 0; + + /* + * Find the end of the string, skipping ''. + */ + for (p = *arg + 1; *p != NUL; MB_PTR_ADV(p)) { + if (*p == '\'') { + if (p[1] != '\'') { + break; + } + ++reduce; + ++p; + } + } + + if (*p != '\'') { + semsg(_("E115: Missing quote: %s"), *arg); + return FAIL; + } + + // If only parsing return after setting "*arg" + if (!evaluate) { + *arg = p + 1; + return OK; + } + + /* + * Copy the string into allocated memory, handling '' to ' reduction. + */ + str = xmalloc((size_t)((p - *arg) - reduce)); + rettv->v_type = VAR_STRING; + rettv->vval.v_string = str; + + for (p = *arg + 1; *p != NUL;) { + if (*p == '\'') { + if (p[1] != '\'') { + break; + } + ++p; + } + mb_copy_char((const char_u **)&p, (char_u **)&str); + } + *str = NUL; + *arg = p + 1; + + return OK; +} + +/// @return the function name of the partial. +char *partial_name(partial_T *pt) + FUNC_ATTR_PURE +{ + if (pt->pt_name != NULL) { + return (char *)pt->pt_name; + } + return (char *)pt->pt_func->uf_name; +} + +// TODO(ZyX-I): Move to eval/typval.h + +static void partial_free(partial_T *pt) +{ + for (int i = 0; i < pt->pt_argc; i++) { + tv_clear(&pt->pt_argv[i]); + } + xfree(pt->pt_argv); + tv_dict_unref(pt->pt_dict); + if (pt->pt_name != NULL) { + func_unref(pt->pt_name); + xfree(pt->pt_name); + } else { + func_ptr_unref(pt->pt_func); + } + xfree(pt); +} + +// TODO(ZyX-I): Move to eval/typval.h + +/// Unreference a closure: decrement the reference count and free it when it +/// becomes zero. +void partial_unref(partial_T *pt) +{ + if (pt != NULL && --pt->pt_refcount <= 0) { + partial_free(pt); + } +} + +/// Allocate a variable for a List and fill it from "*arg". +/// +/// @return OK or FAIL. +static int get_list_tv(char **arg, typval_T *rettv, int evaluate) +{ + list_T *l = NULL; + + if (evaluate) { + l = tv_list_alloc(kListLenShouldKnow); + } + + *arg = skipwhite(*arg + 1); + while (**arg != ']' && **arg != NUL) { + typval_T tv; + if (eval1(arg, &tv, evaluate) == FAIL) { // Recursive! + goto failret; + } + if (evaluate) { + tv.v_lock = VAR_UNLOCKED; + tv_list_append_owned_tv(l, tv); + } + + if (**arg == ']') { + break; + } + if (**arg != ',') { + semsg(_("E696: Missing comma in List: %s"), *arg); + goto failret; + } + *arg = skipwhite(*arg + 1); + } + + if (**arg != ']') { + semsg(_("E697: Missing end of List ']': %s"), *arg); +failret: + if (evaluate) { + tv_list_free(l); + } + return FAIL; + } + + *arg = skipwhite(*arg + 1); + if (evaluate) { + tv_list_set_ret(rettv, l); + } + + return OK; +} + +/// @param ic ignore case +bool func_equal(typval_T *tv1, typval_T *tv2, bool ic) +{ + char_u *s1, *s2; + dict_T *d1, *d2; + int a1, a2; + + // empty and NULL function name considered the same + s1 = (char_u *)(tv1->v_type == VAR_FUNC ? tv1->vval.v_string : partial_name(tv1->vval.v_partial)); + if (s1 != NULL && *s1 == NUL) { + s1 = NULL; + } + s2 = (char_u *)(tv2->v_type == VAR_FUNC ? tv2->vval.v_string : partial_name(tv2->vval.v_partial)); + if (s2 != NULL && *s2 == NUL) { + s2 = NULL; + } + if (s1 == NULL || s2 == NULL) { + if (s1 != s2) { + return false; + } + } else if (STRCMP(s1, s2) != 0) { + return false; + } + + // empty dict and NULL dict is different + d1 = tv1->v_type == VAR_FUNC ? NULL : tv1->vval.v_partial->pt_dict; + d2 = tv2->v_type == VAR_FUNC ? NULL : tv2->vval.v_partial->pt_dict; + if (d1 == NULL || d2 == NULL) { + if (d1 != d2) { + return false; + } + } else if (!tv_dict_equal(d1, d2, ic, true)) { + return false; + } + + // empty list and no list considered the same + a1 = tv1->v_type == VAR_FUNC ? 0 : tv1->vval.v_partial->pt_argc; + a2 = tv2->v_type == VAR_FUNC ? 0 : tv2->vval.v_partial->pt_argc; + if (a1 != a2) { + return false; + } + for (int i = 0; i < a1; i++) { + if (!tv_equal(tv1->vval.v_partial->pt_argv + i, + tv2->vval.v_partial->pt_argv + i, ic, true)) { + return false; + } + } + return true; +} + +/// Get next (unique) copy ID +/// +/// Used for traversing nested structures e.g. when serializing them or garbage +/// collecting. +int get_copyID(void) + FUNC_ATTR_WARN_UNUSED_RESULT +{ + // CopyID for recursively traversing lists and dicts + // + // This value is needed to avoid endless recursiveness. Last bit is used for + // previous_funccal and normally ignored when comparing. + static int current_copyID = 0; + current_copyID += COPYID_INC; + return current_copyID; +} + +/* + * Garbage collection for lists and dictionaries. + * + * We use reference counts to be able to free most items right away when they + * are no longer used. But for composite items it's possible that it becomes + * unused while the reference count is > 0: When there is a recursive + * reference. Example: + * :let l = [1, 2, 3] + * :let d = {9: l} + * :let l[1] = d + * + * Since this is quite unusual we handle this with garbage collection: every + * once in a while find out which lists and dicts are not referenced from any + * variable. + * + * Here is a good reference text about garbage collection (refers to Python + * but it applies to all reference-counting mechanisms): + * http://python.ca/nas/python/gc/ + */ + +/// Do garbage collection for lists and dicts. +/// +/// @param testing true if called from test_garbagecollect_now(). +/// +/// @return true if some memory was freed. +bool garbage_collect(bool testing) +{ + bool abort = false; +#define ABORTING(func) abort = abort || func + + if (!testing) { + // Only do this once. + want_garbage_collect = false; + may_garbage_collect = false; + garbage_collect_at_exit = false; + } + + // We advance by two (COPYID_INC) because we add one for items referenced + // through previous_funccal. + const int copyID = get_copyID(); + + // 1. Go through all accessible variables and mark all lists and dicts + // with copyID. + + // Don't free variables in the previous_funccal list unless they are only + // referenced through previous_funccal. This must be first, because if + // the item is referenced elsewhere the funccal must not be freed. + ABORTING(set_ref_in_previous_funccal)(copyID); + + // script-local variables + for (int i = 1; i <= ga_scripts.ga_len; ++i) { + ABORTING(set_ref_in_ht)(&SCRIPT_VARS(i), copyID, NULL); + } + + FOR_ALL_BUFFERS(buf) { + // buffer-local variables + ABORTING(set_ref_in_item)(&buf->b_bufvar.di_tv, copyID, NULL, NULL); + // buffer marks (ShaDa additional data) + ABORTING(set_ref_in_fmark)(buf->b_last_cursor, copyID); + ABORTING(set_ref_in_fmark)(buf->b_last_insert, copyID); + ABORTING(set_ref_in_fmark)(buf->b_last_change, copyID); + for (size_t i = 0; i < NMARKS; i++) { + ABORTING(set_ref_in_fmark)(buf->b_namedm[i], copyID); + } + // buffer change list (ShaDa additional data) + for (int i = 0; i < buf->b_changelistlen; i++) { + ABORTING(set_ref_in_fmark)(buf->b_changelist[i], copyID); + } + // buffer ShaDa additional data + ABORTING(set_ref_dict)(buf->additional_data, copyID); + + // buffer callback functions + set_ref_in_callback(&buf->b_prompt_callback, copyID, NULL, NULL); + set_ref_in_callback(&buf->b_prompt_interrupt, copyID, NULL, NULL); + } + + FOR_ALL_TAB_WINDOWS(tp, wp) { + // window-local variables + ABORTING(set_ref_in_item)(&wp->w_winvar.di_tv, copyID, NULL, NULL); + // window jump list (ShaDa additional data) + for (int i = 0; i < wp->w_jumplistlen; i++) { + ABORTING(set_ref_in_fmark)(wp->w_jumplist[i].fmark, copyID); + } + } + if (aucmd_win != NULL) { + ABORTING(set_ref_in_item)(&aucmd_win->w_winvar.di_tv, copyID, NULL, NULL); + } + + // registers (ShaDa additional data) + { + iter_register_T reg_iter = ITER_REGISTER_NULL; + do { + yankreg_T reg; + char name = NUL; + bool is_unnamed = false; + reg_iter = op_global_reg_iter(reg_iter, &name, ®, &is_unnamed); + if (name != NUL) { + ABORTING(set_ref_dict)(reg.additional_data, copyID); + } + } while (reg_iter != ITER_REGISTER_NULL); + } + + // global marks (ShaDa additional data) + { + const void *mark_iter = NULL; + do { + xfmark_T fm; + char name = NUL; + mark_iter = mark_global_iter(mark_iter, &name, &fm); + if (name != NUL) { + ABORTING(set_ref_dict)(fm.fmark.additional_data, copyID); + } + } while (mark_iter != NULL); + } + + // tabpage-local variables + FOR_ALL_TABS(tp) { + ABORTING(set_ref_in_item)(&tp->tp_winvar.di_tv, copyID, NULL, NULL); + } + + // global variables + ABORTING(set_ref_in_ht)(&globvarht, copyID, NULL); + + // function-local variables + ABORTING(set_ref_in_call_stack)(copyID); + + // named functions (matters for closures) + ABORTING(set_ref_in_functions)(copyID); + + // Channels + { + Channel *data; + map_foreach_value(&channels, data, { + set_ref_in_callback_reader(&data->on_data, copyID, NULL, NULL); + set_ref_in_callback_reader(&data->on_stderr, copyID, NULL, NULL); + set_ref_in_callback(&data->on_exit, copyID, NULL, NULL); + }) + } + + // Timers + { + timer_T *timer; + map_foreach_value(&timers, timer, { + set_ref_in_callback(&timer->callback, copyID, NULL, NULL); + }) + } + + // function call arguments, if v:testing is set. + ABORTING(set_ref_in_func_args)(copyID); + + // v: vars + ABORTING(set_ref_in_ht)(&vimvarht, copyID, NULL); + + // history items (ShaDa additional elements) + if (p_hi) { + for (uint8_t i = 0; i < HIST_COUNT; i++) { + const void *iter = NULL; + do { + histentry_T hist; + iter = hist_iter(iter, i, false, &hist); + if (hist.hisstr != NULL) { + ABORTING(set_ref_list)(hist.additional_elements, copyID); + } + } while (iter != NULL); + } + } + + // previously used search/substitute patterns (ShaDa additional data) + { + SearchPattern pat; + get_search_pattern(&pat); + ABORTING(set_ref_dict)(pat.additional_data, copyID); + get_substitute_pattern(&pat); + ABORTING(set_ref_dict)(pat.additional_data, copyID); + } + + // previously used replacement string + { + SubReplacementString sub; + sub_get_replacement(&sub); + ABORTING(set_ref_list)(sub.additional_elements, copyID); + } + + ABORTING(set_ref_in_quickfix)(copyID); + + bool did_free = false; + if (!abort) { + // 2. Free lists and dictionaries that are not referenced. + did_free = free_unref_items(copyID); + + // 3. Check if any funccal can be freed now. + // This may call us back recursively. + did_free = free_unref_funccal(copyID, testing) || did_free; + } else if (p_verbose > 0) { + verb_msg(_("Not enough memory to set references, garbage collection aborted!")); + } +#undef ABORTING + return did_free; +} + +/// Free lists and dictionaries that are no longer referenced. +/// +/// @note This function may only be called from garbage_collect(). +/// +/// @param copyID Free lists/dictionaries that don't have this ID. +/// +/// @return true, if something was freed. +static int free_unref_items(int copyID) +{ + bool did_free = false; + + // Let all "free" functions know that we are here. This means no + // dictionaries, lists, or jobs are to be freed, because we will + // do that here. + tv_in_free_unref_items = true; + + // PASS 1: free the contents of the items. We don't free the items + // themselves yet, so that it is possible to decrement refcount counters. + + // Go through the list of dicts and free items without the copyID. + // Don't free dicts that are referenced internally. + for (dict_T *dd = gc_first_dict; dd != NULL; dd = dd->dv_used_next) { + if ((dd->dv_copyID & COPYID_MASK) != (copyID & COPYID_MASK)) { + // Free the Dictionary and ordinary items it contains, but don't + // recurse into Lists and Dictionaries, they will be in the list + // of dicts or list of lists. + tv_dict_free_contents(dd); + did_free = true; + } + } + + // Go through the list of lists and free items without the copyID. + // But don't free a list that has a watcher (used in a for loop), these + // are not referenced anywhere. + for (list_T *ll = gc_first_list; ll != NULL; ll = ll->lv_used_next) { + if ((tv_list_copyid(ll) & COPYID_MASK) != (copyID & COPYID_MASK) + && !tv_list_has_watchers(ll)) { + // Free the List and ordinary items it contains, but don't recurse + // into Lists and Dictionaries, they will be in the list of dicts + // or list of lists. + tv_list_free_contents(ll); + did_free = true; + } + } + + // PASS 2: free the items themselves. + dict_T *dd_next; + for (dict_T *dd = gc_first_dict; dd != NULL; dd = dd_next) { + dd_next = dd->dv_used_next; + if ((dd->dv_copyID & COPYID_MASK) != (copyID & COPYID_MASK)) { + tv_dict_free_dict(dd); + } + } + + list_T *ll_next; + for (list_T *ll = gc_first_list; ll != NULL; ll = ll_next) { + ll_next = ll->lv_used_next; + if ((ll->lv_copyID & COPYID_MASK) != (copyID & COPYID_MASK) + && !tv_list_has_watchers(ll)) { + // Free the List and ordinary items it contains, but don't recurse + // into Lists and Dictionaries, they will be in the list of dicts + // or list of lists. + tv_list_free_list(ll); + } + } + tv_in_free_unref_items = false; + return did_free; +} + +/// Mark all lists and dicts referenced through hashtab "ht" with "copyID". +/// +/// @param ht Hashtab content will be marked. +/// @param copyID New mark for lists and dicts. +/// @param list_stack Used to add lists to be marked. Can be NULL. +/// +/// @returns true if setting references failed somehow. +bool set_ref_in_ht(hashtab_T *ht, int copyID, list_stack_T **list_stack) + FUNC_ATTR_WARN_UNUSED_RESULT +{ + bool abort = false; + ht_stack_T *ht_stack = NULL; + + hashtab_T *cur_ht = ht; + for (;;) { + if (!abort) { + // Mark each item in the hashtab. If the item contains a hashtab + // it is added to ht_stack, if it contains a list it is added to + // list_stack. + HASHTAB_ITER(cur_ht, hi, { + abort = abort || set_ref_in_item(&TV_DICT_HI2DI(hi)->di_tv, copyID, &ht_stack, list_stack); + }); + } + + if (ht_stack == NULL) { + break; + } + + // take an item from the stack + cur_ht = ht_stack->ht; + ht_stack_T *tempitem = ht_stack; + ht_stack = ht_stack->prev; + xfree(tempitem); + } + + return abort; +} + +/// Mark all lists and dicts referenced through list "l" with "copyID". +/// +/// @param l List content will be marked. +/// @param copyID New mark for lists and dicts. +/// @param ht_stack Used to add hashtabs to be marked. Can be NULL. +/// +/// @returns true if setting references failed somehow. +bool set_ref_in_list(list_T *l, int copyID, ht_stack_T **ht_stack) + FUNC_ATTR_WARN_UNUSED_RESULT +{ + bool abort = false; + list_stack_T *list_stack = NULL; + + list_T *cur_l = l; + for (;;) { + // Mark each item in the list. If the item contains a hashtab + // it is added to ht_stack, if it contains a list it is added to + // list_stack. + TV_LIST_ITER(cur_l, li, { + if (abort) { + break; + } + abort = set_ref_in_item(TV_LIST_ITEM_TV(li), copyID, ht_stack, + &list_stack); + }); + + if (list_stack == NULL) { + break; + } + + // take an item from the stack + cur_l = list_stack->list; + list_stack_T *tempitem = list_stack; + list_stack = list_stack->prev; + xfree(tempitem); + } + + return abort; +} + +/// Mark all lists and dicts referenced through typval "tv" with "copyID". +/// +/// @param tv Typval content will be marked. +/// @param copyID New mark for lists and dicts. +/// @param ht_stack Used to add hashtabs to be marked. Can be NULL. +/// @param list_stack Used to add lists to be marked. Can be NULL. +/// +/// @returns true if setting references failed somehow. +bool set_ref_in_item(typval_T *tv, int copyID, ht_stack_T **ht_stack, list_stack_T **list_stack) + FUNC_ATTR_WARN_UNUSED_RESULT +{ + bool abort = false; + + switch (tv->v_type) { + case VAR_DICT: { + dict_T *dd = tv->vval.v_dict; + if (dd != NULL && dd->dv_copyID != copyID) { + // Didn't see this dict yet. + dd->dv_copyID = copyID; + if (ht_stack == NULL) { + abort = set_ref_in_ht(&dd->dv_hashtab, copyID, list_stack); + } else { + ht_stack_T *const newitem = xmalloc(sizeof(ht_stack_T)); + newitem->ht = &dd->dv_hashtab; + newitem->prev = *ht_stack; + *ht_stack = newitem; + } + + QUEUE *w = NULL; + DictWatcher *watcher = NULL; + QUEUE_FOREACH(w, &dd->watchers, { + watcher = tv_dict_watcher_node_data(w); + set_ref_in_callback(&watcher->callback, copyID, ht_stack, list_stack); + }) + } + break; + } + + case VAR_LIST: { + list_T *ll = tv->vval.v_list; + if (ll != NULL && ll->lv_copyID != copyID) { + // Didn't see this list yet. + ll->lv_copyID = copyID; + if (list_stack == NULL) { + abort = set_ref_in_list(ll, copyID, ht_stack); + } else { + list_stack_T *const newitem = xmalloc(sizeof(list_stack_T)); + newitem->list = ll; + newitem->prev = *list_stack; + *list_stack = newitem; + } + } + break; + } + + case VAR_PARTIAL: { + partial_T *pt = tv->vval.v_partial; + + // A partial does not have a copyID, because it cannot contain itself. + if (pt != NULL) { + abort = set_ref_in_func(pt->pt_name, pt->pt_func, copyID); + if (pt->pt_dict != NULL) { + typval_T dtv; + + dtv.v_type = VAR_DICT; + dtv.vval.v_dict = pt->pt_dict; + abort = abort || set_ref_in_item(&dtv, copyID, ht_stack, list_stack); + } + + for (int i = 0; i < pt->pt_argc; i++) { + abort = abort || set_ref_in_item(&pt->pt_argv[i], copyID, + ht_stack, list_stack); + } + } + break; + } + case VAR_FUNC: + abort = set_ref_in_func((char_u *)tv->vval.v_string, NULL, copyID); + break; + case VAR_UNKNOWN: + case VAR_BOOL: + case VAR_SPECIAL: + case VAR_FLOAT: + case VAR_NUMBER: + case VAR_STRING: + case VAR_BLOB: + break; + } + return abort; +} + +/// Mark all lists and dicts referenced in given mark +/// +/// @return true if setting references failed somehow. +static inline bool set_ref_in_fmark(fmark_T fm, int copyID) + FUNC_ATTR_WARN_UNUSED_RESULT +{ + if (fm.additional_data != NULL + && fm.additional_data->dv_copyID != copyID) { + fm.additional_data->dv_copyID = copyID; + return set_ref_in_ht(&fm.additional_data->dv_hashtab, copyID, NULL); + } + return false; +} + +/// Mark all lists and dicts referenced in given list and the list itself +/// +/// @return true if setting references failed somehow. +static inline bool set_ref_list(list_T *list, int copyID) + FUNC_ATTR_WARN_UNUSED_RESULT +{ + if (list != NULL) { + typval_T tv = (typval_T) { + .v_type = VAR_LIST, + .vval = { .v_list = list } + }; + return set_ref_in_item(&tv, copyID, NULL, NULL); + } + return false; +} + +/// Mark all lists and dicts referenced in given dict and the dict itself +/// +/// @return true if setting references failed somehow. +static inline bool set_ref_dict(dict_T *dict, int copyID) + FUNC_ATTR_WARN_UNUSED_RESULT +{ + if (dict != NULL) { + typval_T tv = (typval_T) { + .v_type = VAR_DICT, + .vval = { .v_dict = dict } + }; + return set_ref_in_item(&tv, copyID, NULL, NULL); + } + return false; +} + +/// Get the key for #{key: val} into "tv" and advance "arg". +/// +/// @return FAIL when there is no valid key. +static int get_literal_key(char **arg, typval_T *tv) + FUNC_ATTR_NONNULL_ALL +{ + char *p; + + if (!ASCII_ISALNUM(**arg) && **arg != '_' && **arg != '-') { + return FAIL; + } + for (p = *arg; ASCII_ISALNUM(*p) || *p == '_' || *p == '-'; p++) {} + tv->v_type = VAR_STRING; + tv->vval.v_string = xstrnsave(*arg, (size_t)(p - *arg)); + + *arg = skipwhite(p); + return OK; +} + +/// Allocate a variable for a Dictionary and fill it from "*arg". +/// "literal" is true for #{key: val} +/// +/// @return OK or FAIL. Returns NOTDONE for {expr}. +static int dict_get_tv(char **arg, typval_T *rettv, int evaluate, bool literal) +{ + dict_T *d = NULL; + typval_T tvkey; + typval_T tv; + char *key = NULL; + dictitem_T *item; + char *start = skipwhite(*arg + 1); + char buf[NUMBUFLEN]; + + /* + * First check if it's not a curly-braces thing: {expr}. + * Must do this without evaluating, otherwise a function may be called + * twice. Unfortunately this means we need to call eval1() twice for the + * first item. + * But {} is an empty Dictionary. + */ + if (*start != '}') { + if (eval1(&start, &tv, false) == FAIL) { // recursive! + return FAIL; + } + if (*skipwhite(start) == '}') { + return NOTDONE; + } + } + + if (evaluate) { + d = tv_dict_alloc(); + } + tvkey.v_type = VAR_UNKNOWN; + tv.v_type = VAR_UNKNOWN; + + *arg = skipwhite(*arg + 1); + while (**arg != '}' && **arg != NUL) { + if ((literal + ? get_literal_key(arg, &tvkey) + : eval1(arg, &tvkey, evaluate)) == FAIL) { // recursive! + goto failret; + } + if (**arg != ':') { + semsg(_("E720: Missing colon in Dictionary: %s"), *arg); + tv_clear(&tvkey); + goto failret; + } + if (evaluate) { + key = (char *)tv_get_string_buf_chk(&tvkey, buf); + if (key == NULL) { + // "key" is NULL when tv_get_string_buf_chk() gave an errmsg + tv_clear(&tvkey); + goto failret; + } + } + + *arg = skipwhite(*arg + 1); + if (eval1(arg, &tv, evaluate) == FAIL) { // Recursive! + if (evaluate) { + tv_clear(&tvkey); + } + goto failret; + } + if (evaluate) { + item = tv_dict_find(d, (const char *)key, -1); + if (item != NULL) { + semsg(_("E721: Duplicate key in Dictionary: \"%s\""), key); + tv_clear(&tvkey); + tv_clear(&tv); + goto failret; + } + item = tv_dict_item_alloc((const char *)key); + item->di_tv = tv; + item->di_tv.v_lock = VAR_UNLOCKED; + if (tv_dict_add(d, item) == FAIL) { + tv_dict_item_free(item); + } + } + tv_clear(&tvkey); + + if (**arg == '}') { + break; + } + if (**arg != ',') { + semsg(_("E722: Missing comma in Dictionary: %s"), *arg); + goto failret; + } + *arg = skipwhite(*arg + 1); + } + + if (**arg != '}') { + semsg(_("E723: Missing end of Dictionary '}': %s"), *arg); +failret: + if (d != NULL) { + tv_dict_free(d); + } + return FAIL; + } + + *arg = skipwhite(*arg + 1); + if (evaluate) { + tv_dict_set_ret(rettv, d); + } + + return OK; +} + +/// Convert the string to a floating point number +/// +/// This uses strtod(). setlocale(LC_NUMERIC, "C") has been used earlier to +/// make sure this always uses a decimal point. +/// +/// @param[in] text String to convert. +/// @param[out] ret_value Location where conversion result is saved. +/// +/// @return Length of the text that was consumed. +size_t string2float(const char *const text, float_T *const ret_value) + FUNC_ATTR_NONNULL_ALL +{ + char *s = NULL; + + // MS-Windows does not deal with "inf" and "nan" properly + if (STRNICMP(text, "inf", 3) == 0) { + *ret_value = (float_T)INFINITY; + return 3; + } + if (STRNICMP(text, "-inf", 3) == 0) { + *ret_value = (float_T) - INFINITY; + return 4; + } + if (STRNICMP(text, "nan", 3) == 0) { + *ret_value = (float_T)NAN; + return 3; + } + *ret_value = strtod(text, &s); + return (size_t)(s - text); +} + +/// Get the value of an environment variable. +/// +/// If the environment variable was not set, silently assume it is empty. +/// +/// @param arg Points to the '$'. It is advanced to after the name. +/// +/// @return FAIL if the name is invalid. +static int get_env_tv(char **arg, typval_T *rettv, int evaluate) +{ + char *name; + char *string = NULL; + int len; + int cc; + + ++*arg; + name = *arg; + len = get_env_len((const char **)arg); + + if (evaluate) { + if (len == 0) { + return FAIL; // Invalid empty name. + } + cc = (char_u)name[len]; + name[len] = NUL; + // First try vim_getenv(), fast for normal environment vars. + string = vim_getenv(name); + if (string == NULL || *string == NUL) { + xfree(string); + + // Next try expanding things like $VIM and ${HOME}. + string = expand_env_save(name - 1); + if (string != NULL && *string == '$') { + XFREE_CLEAR(string); + } + } + name[len] = (char)cc; + rettv->v_type = VAR_STRING; + rettv->vval.v_string = string; + } + + return OK; +} + +/// Get the argument list for a given window +void get_arglist_as_rettv(aentry_T *arglist, int argcount, typval_T *rettv) +{ + tv_list_alloc_ret(rettv, argcount); + if (arglist != NULL) { + for (int idx = 0; idx < argcount; idx++) { + tv_list_append_string(rettv->vval.v_list, + (const char *)alist_name(&arglist[idx]), -1); + } + } +} + +/// Add an assert error to v:errors. +void assert_error(garray_T *gap) +{ + struct vimvar *vp = &vimvars[VV_ERRORS]; + + if (vp->vv_type != VAR_LIST || vimvars[VV_ERRORS].vv_list == NULL) { + // Make sure v:errors is a list. + set_vim_var_list(VV_ERRORS, tv_list_alloc(1)); + } + tv_list_append_string(vimvars[VV_ERRORS].vv_list, + (const char *)gap->ga_data, (ptrdiff_t)gap->ga_len); +} + +/// Find a window: When using a Window ID in any tab page, when using a number +/// in the current tab page. +win_T *find_win_by_nr_or_id(typval_T *vp) +{ + int nr = (int)tv_get_number_chk(vp, NULL); + + if (nr >= LOWEST_WIN_ID) { + return win_id2wp((int)tv_get_number(vp)); + } + + return find_win_by_nr(vp, NULL); +} + +/// Implementation of map() and filter(). +void filter_map(typval_T *argvars, typval_T *rettv, int map) +{ + typval_T *expr; + list_T *l = NULL; + dictitem_T *di; + hashtab_T *ht; + hashitem_T *hi; + dict_T *d = NULL; + typval_T save_val; + typval_T save_key; + blob_T *b = NULL; + int rem = false; + int todo; + char *ermsg = map ? "map()" : "filter()"; + const char *const arg_errmsg = (map + ? N_("map() argument") + : N_("filter() argument")); + int save_did_emsg; + int idx = 0; + + if (argvars[0].v_type == VAR_BLOB) { + tv_copy(&argvars[0], rettv); + if ((b = argvars[0].vval.v_blob) == NULL) { + return; + } + } else if (argvars[0].v_type == VAR_LIST) { + tv_copy(&argvars[0], rettv); + if ((l = argvars[0].vval.v_list) == NULL + || (!map + && var_check_lock(tv_list_locked(l), arg_errmsg, TV_TRANSLATE))) { + return; + } + } else if (argvars[0].v_type == VAR_DICT) { + tv_copy(&argvars[0], rettv); + if ((d = argvars[0].vval.v_dict) == NULL + || (!map && var_check_lock(d->dv_lock, arg_errmsg, TV_TRANSLATE))) { + return; + } + } else { + semsg(_(e_listdictblobarg), ermsg); + return; + } + + expr = &argvars[1]; + // On type errors, the preceding call has already displayed an error + // message. Avoid a misleading error message for an empty string that + // was not passed as argument. + if (expr->v_type != VAR_UNKNOWN) { + prepare_vimvar(VV_VAL, &save_val); + + // We reset "did_emsg" to be able to detect whether an error + // occurred during evaluation of the expression. + save_did_emsg = did_emsg; + did_emsg = FALSE; + + prepare_vimvar(VV_KEY, &save_key); + if (argvars[0].v_type == VAR_DICT) { + vimvars[VV_KEY].vv_type = VAR_STRING; + + const VarLockStatus prev_lock = d->dv_lock; + if (map && d->dv_lock == VAR_UNLOCKED) { + d->dv_lock = VAR_LOCKED; + } + ht = &d->dv_hashtab; + hash_lock(ht); + todo = (int)ht->ht_used; + for (hi = ht->ht_array; todo > 0; ++hi) { + if (!HASHITEM_EMPTY(hi)) { + --todo; + + di = TV_DICT_HI2DI(hi); + if (map + && (var_check_lock(di->di_tv.v_lock, arg_errmsg, TV_TRANSLATE) + || var_check_ro(di->di_flags, arg_errmsg, TV_TRANSLATE))) { + break; + } + + vimvars[VV_KEY].vv_str = (char *)vim_strsave(di->di_key); + int r = filter_map_one(&di->di_tv, expr, map, &rem); + tv_clear(&vimvars[VV_KEY].vv_tv); + if (r == FAIL || did_emsg) { + break; + } + if (!map && rem) { + if (var_check_fixed(di->di_flags, arg_errmsg, TV_TRANSLATE) + || var_check_ro(di->di_flags, arg_errmsg, TV_TRANSLATE)) { + break; + } + tv_dict_item_remove(d, di); + } + } + } + hash_unlock(ht); + d->dv_lock = prev_lock; + } else if (argvars[0].v_type == VAR_BLOB) { + vimvars[VV_KEY].vv_type = VAR_NUMBER; + + for (int i = 0; i < b->bv_ga.ga_len; i++) { + typval_T tv; + tv.v_type = VAR_NUMBER; + const varnumber_T val = tv_blob_get(b, i); + tv.vval.v_number = val; + vimvars[VV_KEY].vv_nr = idx; + if (filter_map_one(&tv, expr, map, &rem) == FAIL || did_emsg) { + break; + } + if (tv.v_type != VAR_NUMBER) { + emsg(_(e_invalblob)); + return; + } + if (map) { + if (tv.vval.v_number != val) { + tv_blob_set(b, i, (char_u)tv.vval.v_number); + } + } else if (rem) { + char *const p = argvars[0].vval.v_blob->bv_ga.ga_data; + memmove(p + i, p + i + 1, (size_t)(b->bv_ga.ga_len - i - 1)); + b->bv_ga.ga_len--; + i--; + } + idx++; + } + } else { + assert(argvars[0].v_type == VAR_LIST); + vimvars[VV_KEY].vv_type = VAR_NUMBER; + + const VarLockStatus prev_lock = tv_list_locked(l); + if (map && tv_list_locked(l) == VAR_UNLOCKED) { + tv_list_set_lock(l, VAR_LOCKED); + } + for (listitem_T *li = tv_list_first(l); li != NULL;) { + if (map + && var_check_lock(TV_LIST_ITEM_TV(li)->v_lock, arg_errmsg, + TV_TRANSLATE)) { + break; + } + vimvars[VV_KEY].vv_nr = idx; + if (filter_map_one(TV_LIST_ITEM_TV(li), expr, map, &rem) == FAIL + || did_emsg) { + break; + } + if (!map && rem) { + li = tv_list_item_remove(l, li); + } else { + li = TV_LIST_ITEM_NEXT(l, li); + } + idx++; + } + tv_list_set_lock(l, prev_lock); + } + + restore_vimvar(VV_KEY, &save_key); + restore_vimvar(VV_VAL, &save_val); + + did_emsg |= save_did_emsg; + } +} + +static int filter_map_one(typval_T *tv, typval_T *expr, int map, int *remp) + FUNC_ATTR_NONNULL_ARG(1, 2) +{ + typval_T rettv; + typval_T argv[3]; + int retval = FAIL; + + tv_copy(tv, &vimvars[VV_VAL].vv_tv); + argv[0] = vimvars[VV_KEY].vv_tv; + argv[1] = vimvars[VV_VAL].vv_tv; + if (eval_expr_typval(expr, argv, 2, &rettv) == FAIL) { + goto theend; + } + if (map) { + // map(): replace the list item value. + tv_clear(tv); + rettv.v_lock = VAR_UNLOCKED; + *tv = rettv; + } else { + bool error = false; + + // filter(): when expr is zero remove the item + *remp = (tv_get_number_chk(&rettv, &error) == 0); + tv_clear(&rettv); + // On type error, nothing has been removed; return FAIL to stop the + // loop. The error message was given by tv_get_number_chk(). + if (error) { + goto theend; + } + } + retval = OK; +theend: + tv_clear(&vimvars[VV_VAL].vv_tv); + return retval; +} + +void common_function(typval_T *argvars, typval_T *rettv, bool is_funcref, FunPtr fptr) +{ + char *s; + char *name; + bool use_string = false; + partial_T *arg_pt = NULL; + char *trans_name = NULL; + + if (argvars[0].v_type == VAR_FUNC) { + // function(MyFunc, [arg], dict) + s = argvars[0].vval.v_string; + } else if (argvars[0].v_type == VAR_PARTIAL + && argvars[0].vval.v_partial != NULL) { + // function(dict.MyFunc, [arg]) + arg_pt = argvars[0].vval.v_partial; + s = partial_name(arg_pt); + // TODO(bfredl): do the entire nlua_is_table_from_lua dance + } else { + // function('MyFunc', [arg], dict) + s = (char *)tv_get_string(&argvars[0]); + use_string = true; + } + + if ((use_string && vim_strchr(s, AUTOLOAD_CHAR) == NULL) || is_funcref) { + name = s; + trans_name = (char *)trans_function_name((char_u **)&name, false, + TFN_INT | TFN_QUIET | TFN_NO_AUTOLOAD + | TFN_NO_DEREF, NULL, NULL); + if (*name != NUL) { + s = NULL; + } + } + if (s == NULL || *s == NUL || (use_string && ascii_isdigit(*s)) + || (is_funcref && trans_name == NULL)) { + semsg(_(e_invarg2), (use_string + ? tv_get_string(&argvars[0]) + : (const char *)s)); + // Don't check an autoload name for existence here. + } else if (trans_name != NULL + && (is_funcref + ? find_func((char_u *)trans_name) == NULL + : !translated_function_exists((const char *)trans_name))) { + semsg(_("E700: Unknown function: %s"), s); + } else { + int dict_idx = 0; + int arg_idx = 0; + list_T *list = NULL; + if (STRNCMP(s, "s:", 2) == 0 || STRNCMP(s, "<SID>", 5) == 0) { + char sid_buf[25]; + int off = *s == 's' ? 2 : 5; + + // Expand s: and <SID> into <SNR>nr_, so that the function can + // also be called from another script. Using trans_function_name() + // would also work, but some plugins depend on the name being + // printable text. + snprintf(sid_buf, sizeof(sid_buf), "<SNR>%" PRId64 "_", + (int64_t)current_sctx.sc_sid); + name = xmalloc(STRLEN(sid_buf) + STRLEN(s + off) + 1); + STRCPY(name, sid_buf); + STRCAT(name, s + off); + } else { + name = xstrdup(s); + } + + if (argvars[1].v_type != VAR_UNKNOWN) { + if (argvars[2].v_type != VAR_UNKNOWN) { + // function(name, [args], dict) + arg_idx = 1; + dict_idx = 2; + } else if (argvars[1].v_type == VAR_DICT) { + // function(name, dict) + dict_idx = 1; + } else { + // function(name, [args]) + arg_idx = 1; + } + if (dict_idx > 0) { + if (argvars[dict_idx].v_type != VAR_DICT) { + emsg(_("E922: expected a dict")); + xfree(name); + goto theend; + } + if (argvars[dict_idx].vval.v_dict == NULL) { + dict_idx = 0; + } + } + if (arg_idx > 0) { + if (argvars[arg_idx].v_type != VAR_LIST) { + emsg(_("E923: Second argument of function() must be " + "a list or a dict")); + xfree(name); + goto theend; + } + list = argvars[arg_idx].vval.v_list; + if (tv_list_len(list) == 0) { + arg_idx = 0; + } else if (tv_list_len(list) > MAX_FUNC_ARGS) { + emsg_funcname((char *)e_toomanyarg, (char_u *)s); + xfree(name); + goto theend; + } + } + } + if (dict_idx > 0 || arg_idx > 0 || arg_pt != NULL || is_funcref) { + partial_T *const pt = xcalloc(1, sizeof(*pt)); + + // result is a VAR_PARTIAL + if (arg_idx > 0 || (arg_pt != NULL && arg_pt->pt_argc > 0)) { + const int arg_len = (arg_pt == NULL ? 0 : arg_pt->pt_argc); + const int lv_len = tv_list_len(list); + + pt->pt_argc = arg_len + lv_len; + pt->pt_argv = xmalloc(sizeof(pt->pt_argv[0]) * (size_t)pt->pt_argc); + int i = 0; + for (; i < arg_len; i++) { + tv_copy(&arg_pt->pt_argv[i], &pt->pt_argv[i]); + } + if (lv_len > 0) { + TV_LIST_ITER(list, li, { + tv_copy(TV_LIST_ITEM_TV(li), &pt->pt_argv[i++]); + }); + } + } + + // For "function(dict.func, [], dict)" and "func" is a partial + // use "dict". That is backwards compatible. + if (dict_idx > 0) { + // The dict is bound explicitly, pt_auto is false + pt->pt_dict = argvars[dict_idx].vval.v_dict; + (pt->pt_dict->dv_refcount)++; + } else if (arg_pt != NULL) { + // If the dict was bound automatically the result is also + // bound automatically. + pt->pt_dict = arg_pt->pt_dict; + pt->pt_auto = arg_pt->pt_auto; + if (pt->pt_dict != NULL) { + (pt->pt_dict->dv_refcount)++; + } + } + + pt->pt_refcount = 1; + if (arg_pt != NULL && arg_pt->pt_func != NULL) { + pt->pt_func = arg_pt->pt_func; + func_ptr_ref(pt->pt_func); + xfree(name); + } else if (is_funcref) { + pt->pt_func = find_func((char_u *)trans_name); + func_ptr_ref(pt->pt_func); + xfree(name); + } else { + pt->pt_name = (char_u *)name; + func_ref((char_u *)name); + } + + rettv->v_type = VAR_PARTIAL; + rettv->vval.v_partial = pt; + } else { + // result is a VAR_FUNC + rettv->v_type = VAR_FUNC; + rettv->vval.v_string = name; + func_ref((char_u *)name); + } + } +theend: + xfree(trans_name); +} + +/// @return buffer options, variables and other attributes in a dictionary. +dict_T *get_buffer_info(buf_T *buf) +{ + dict_T *const dict = tv_dict_alloc(); + + tv_dict_add_nr(dict, S_LEN("bufnr"), buf->b_fnum); + tv_dict_add_str(dict, S_LEN("name"), + buf->b_ffname != NULL ? (const char *)buf->b_ffname : ""); + tv_dict_add_nr(dict, S_LEN("lnum"), + buf == curbuf ? curwin->w_cursor.lnum : buflist_findlnum(buf)); + tv_dict_add_nr(dict, S_LEN("linecount"), buf->b_ml.ml_line_count); + tv_dict_add_nr(dict, S_LEN("loaded"), buf->b_ml.ml_mfp != NULL); + tv_dict_add_nr(dict, S_LEN("listed"), buf->b_p_bl); + tv_dict_add_nr(dict, S_LEN("changed"), bufIsChanged(buf)); + tv_dict_add_nr(dict, S_LEN("changedtick"), buf_get_changedtick(buf)); + tv_dict_add_nr(dict, S_LEN("hidden"), + buf->b_ml.ml_mfp != NULL && buf->b_nwindows == 0); + + // Get a reference to buffer variables + tv_dict_add_dict(dict, S_LEN("variables"), buf->b_vars); + + // List of windows displaying this buffer + list_T *const windows = tv_list_alloc(kListLenMayKnow); + FOR_ALL_TAB_WINDOWS(tp, wp) { + if (wp->w_buffer == buf) { + tv_list_append_number(windows, (varnumber_T)wp->handle); + } + } + tv_dict_add_list(dict, S_LEN("windows"), windows); + + if (buf->b_signlist != NULL) { + // List of signs placed in this buffer + tv_dict_add_list(dict, S_LEN("signs"), get_buffer_signs(buf)); + } + + tv_dict_add_nr(dict, S_LEN("lastused"), buf->b_last_used); + + return dict; +} + +/// Get the line number from VimL object +/// +/// @note Unlike tv_get_lnum(), this one supports only "$" special string. +/// +/// @param[in] tv Object to get value from. Is expected to be a number or +/// a special string "$". +/// @param[in] buf Buffer to take last line number from in case tv is "$". May +/// be NULL, in this case "$" results in zero return. +/// +/// @return Line number or 0 in case of error. +linenr_T tv_get_lnum_buf(const typval_T *const tv, const buf_T *const buf) + FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_WARN_UNUSED_RESULT +{ + if (tv->v_type == VAR_STRING + && tv->vval.v_string != NULL + && tv->vval.v_string[0] == '$' + && buf != NULL) { + return buf->b_ml.ml_line_count; + } + return (linenr_T)tv_get_number_chk(tv, NULL); +} + +void get_qf_loc_list(int is_qf, win_T *wp, typval_T *what_arg, typval_T *rettv) +{ + if (what_arg->v_type == VAR_UNKNOWN) { + tv_list_alloc_ret(rettv, kListLenMayKnow); + if (is_qf || wp != NULL) { + (void)get_errorlist(NULL, wp, -1, 0, rettv->vval.v_list); + } + } else { + tv_dict_alloc_ret(rettv); + if (is_qf || wp != NULL) { + if (what_arg->v_type == VAR_DICT) { + dict_T *d = what_arg->vval.v_dict; + + if (d != NULL) { + qf_get_properties(wp, d, rettv->vval.v_dict); + } + } else { + emsg(_(e_dictreq)); + } + } + } +} + +/// @return information (variables, options, etc.) about a tab page +/// as a dictionary. +dict_T *get_tabpage_info(tabpage_T *tp, int tp_idx) +{ + dict_T *const dict = tv_dict_alloc(); + + tv_dict_add_nr(dict, S_LEN("tabnr"), tp_idx); + + list_T *const l = tv_list_alloc(kListLenMayKnow); + FOR_ALL_WINDOWS_IN_TAB(wp, tp) { + tv_list_append_number(l, (varnumber_T)wp->handle); + } + tv_dict_add_list(dict, S_LEN("windows"), l); + + // Make a reference to tabpage variables + tv_dict_add_dict(dict, S_LEN("variables"), tp->tp_vars); + + return dict; +} + +/// @return information about a window as a dictionary. +dict_T *get_win_info(win_T *wp, int16_t tpnr, int16_t winnr) +{ + dict_T *const dict = tv_dict_alloc(); + + // make sure w_botline is valid + validate_botline(wp); + + tv_dict_add_nr(dict, S_LEN("tabnr"), tpnr); + tv_dict_add_nr(dict, S_LEN("winnr"), winnr); + tv_dict_add_nr(dict, S_LEN("winid"), wp->handle); + tv_dict_add_nr(dict, S_LEN("height"), wp->w_height); + tv_dict_add_nr(dict, S_LEN("winrow"), wp->w_winrow + 1); + tv_dict_add_nr(dict, S_LEN("topline"), wp->w_topline); + tv_dict_add_nr(dict, S_LEN("botline"), wp->w_botline - 1); + tv_dict_add_nr(dict, S_LEN("winbar"), wp->w_winbar_height); + tv_dict_add_nr(dict, S_LEN("width"), wp->w_width); + tv_dict_add_nr(dict, S_LEN("bufnr"), wp->w_buffer->b_fnum); + tv_dict_add_nr(dict, S_LEN("wincol"), wp->w_wincol + 1); + tv_dict_add_nr(dict, S_LEN("textoff"), win_col_off(wp)); + tv_dict_add_nr(dict, S_LEN("terminal"), bt_terminal(wp->w_buffer)); + tv_dict_add_nr(dict, S_LEN("quickfix"), bt_quickfix(wp->w_buffer)); + tv_dict_add_nr(dict, S_LEN("loclist"), + (bt_quickfix(wp->w_buffer) && wp->w_llist_ref != NULL)); + + // Add a reference to window variables + tv_dict_add_dict(dict, S_LEN("variables"), wp->w_vars); + + return dict; +} + +/// Find window specified by "vp" in tabpage "tp". +/// +/// @param tp NULL for current tab page +win_T *find_win_by_nr(typval_T *vp, tabpage_T *tp) +{ + int nr = (int)tv_get_number_chk(vp, NULL); + + if (nr < 0) { + return NULL; + } + + if (nr == 0) { + return curwin; + } + + // This method accepts NULL as an alias for curtab. + if (tp == NULL) { + tp = curtab; + } + + FOR_ALL_WINDOWS_IN_TAB(wp, tp) { + if (nr >= LOWEST_WIN_ID) { + if (wp->handle == nr) { + return wp; + } + } else if (--nr <= 0) { + return wp; + } + } + return NULL; +} + +/// Find window specified by "wvp" in tabpage "tvp". +win_T *find_tabwin(typval_T *wvp, typval_T *tvp) +{ + win_T *wp = NULL; + tabpage_T *tp = NULL; + + if (wvp->v_type != VAR_UNKNOWN) { + if (tvp->v_type != VAR_UNKNOWN) { + long n = tv_get_number(tvp); + if (n >= 0) { + tp = find_tabpage((int)n); + } + } else { + tp = curtab; + } + + if (tp != NULL) { + wp = find_win_by_nr(wvp, tp); + } + } else { + wp = curwin; + } + + return wp; +} + +/// This function is used by f_input() and f_inputdialog() functions. The third +/// argument to f_input() specifies the type of completion to use at the +/// prompt. The third argument to f_inputdialog() specifies the value to return +/// when the user cancels the prompt. +void get_user_input(const typval_T *const argvars, typval_T *const rettv, const bool inputdialog, + const bool secret) + FUNC_ATTR_NONNULL_ALL +{ + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; + + const char *prompt = ""; + const char *defstr = ""; + typval_T *cancelreturn = NULL; + typval_T cancelreturn_strarg2 = TV_INITIAL_VALUE; + const char *xp_name = NULL; + Callback input_callback = { .type = kCallbackNone }; + char prompt_buf[NUMBUFLEN]; + char defstr_buf[NUMBUFLEN]; + char cancelreturn_buf[NUMBUFLEN]; + char xp_name_buf[NUMBUFLEN]; + char def[1] = { 0 }; + if (argvars[0].v_type == VAR_DICT) { + if (argvars[1].v_type != VAR_UNKNOWN) { + emsg(_("E5050: {opts} must be the only argument")); + return; + } + dict_T *const dict = argvars[0].vval.v_dict; + prompt = tv_dict_get_string_buf_chk(dict, S_LEN("prompt"), prompt_buf, ""); + if (prompt == NULL) { + return; + } + defstr = tv_dict_get_string_buf_chk(dict, S_LEN("default"), defstr_buf, ""); + if (defstr == NULL) { + return; + } + dictitem_T *cancelreturn_di = tv_dict_find(dict, S_LEN("cancelreturn")); + if (cancelreturn_di != NULL) { + cancelreturn = &cancelreturn_di->di_tv; + } + xp_name = tv_dict_get_string_buf_chk(dict, S_LEN("completion"), + xp_name_buf, def); + if (xp_name == NULL) { // error + return; + } + if (xp_name == def) { // default to NULL + xp_name = NULL; + } + if (!tv_dict_get_callback(dict, S_LEN("highlight"), &input_callback)) { + return; + } + } else { + prompt = tv_get_string_buf_chk(&argvars[0], prompt_buf); + if (prompt == NULL) { + return; + } + if (argvars[1].v_type != VAR_UNKNOWN) { + defstr = tv_get_string_buf_chk(&argvars[1], defstr_buf); + if (defstr == NULL) { + return; + } + if (argvars[2].v_type != VAR_UNKNOWN) { + const char *const strarg2 = tv_get_string_buf_chk(&argvars[2], cancelreturn_buf); + if (strarg2 == NULL) { + return; + } + if (inputdialog) { + cancelreturn_strarg2.v_type = VAR_STRING; + cancelreturn_strarg2.vval.v_string = (char *)strarg2; + cancelreturn = &cancelreturn_strarg2; + } else { + xp_name = strarg2; + } + } + } + } + + int xp_type = EXPAND_NOTHING; + char *xp_arg = NULL; + if (xp_name != NULL) { + // input() with a third argument: completion + const int xp_namelen = (int)strlen(xp_name); + + uint32_t argt = 0; + if (parse_compl_arg(xp_name, xp_namelen, &xp_type, + &argt, &xp_arg) == FAIL) { + return; + } + } + + const bool cmd_silent_save = cmd_silent; + + cmd_silent = false; // Want to see the prompt. + // Only the part of the message after the last NL is considered as + // prompt for the command line, unlsess cmdline is externalized + const char *p = prompt; + if (!ui_has(kUICmdline)) { + const char *lastnl = strrchr(prompt, '\n'); + if (lastnl != NULL) { + p = lastnl + 1; + msg_start(); + msg_clr_eos(); + msg_puts_attr_len(prompt, p - prompt, echo_attr); + msg_didout = false; + msg_starthere(); + } + } + cmdline_row = msg_row; + + stuffReadbuffSpec(defstr); + + const int save_ex_normal_busy = ex_normal_busy; + ex_normal_busy = 0; + rettv->vval.v_string = getcmdline_prompt(secret ? NUL : '@', p, echo_attr, xp_type, xp_arg, + input_callback); + ex_normal_busy = save_ex_normal_busy; + callback_free(&input_callback); + + if (rettv->vval.v_string == NULL && cancelreturn != NULL) { + tv_copy(cancelreturn, rettv); + } + + xfree(xp_arg); + + // Since the user typed this, no need to wait for return. + need_wait_return = false; + msg_didout = false; + cmd_silent = cmd_silent_save; +} + +/// Builds a process argument vector from a VimL object (typval_T). +/// +/// @param[in] cmd_tv VimL object +/// @param[out] cmd Returns the command or executable name. +/// @param[out] executable Returns `false` if argv[0] is not executable. +/// +/// @return Result of `shell_build_argv()` if `cmd_tv` is a String. +/// Else, string values of `cmd_tv` copied to a (char **) list with +/// argv[0] resolved to full path ($PATHEXT-resolved on Windows). +char **tv_to_argv(typval_T *cmd_tv, const char **cmd, bool *executable) +{ + if (cmd_tv->v_type == VAR_STRING) { // String => "shell semantics". + const char *cmd_str = tv_get_string(cmd_tv); + if (cmd) { + *cmd = cmd_str; + } + return shell_build_argv(cmd_str, NULL); + } + + if (cmd_tv->v_type != VAR_LIST) { + semsg(_(e_invarg2), "expected String or List"); + return NULL; + } + + list_T *argl = cmd_tv->vval.v_list; + int argc = tv_list_len(argl); + if (!argc) { + emsg(_(e_invarg)); // List must have at least one item. + return NULL; + } + + const char *arg0 = tv_get_string_chk(TV_LIST_ITEM_TV(tv_list_first(argl))); + char *exe_resolved = NULL; + if (!arg0 || !os_can_exe(arg0, &exe_resolved, true)) { + if (arg0 && executable) { + char buf[IOSIZE]; + snprintf(buf, sizeof(buf), "'%s' is not executable", arg0); + semsg(_(e_invargNval), "cmd", buf); + *executable = false; + } + return NULL; + } + + if (cmd) { + *cmd = exe_resolved; + } + + // Build the argument vector + int i = 0; + char **argv = xcalloc((size_t)argc + 1, sizeof(char *)); + TV_LIST_ITER_CONST(argl, arg, { + const char *a = tv_get_string_chk(TV_LIST_ITEM_TV(arg)); + if (!a) { + // Did emsg in tv_get_string_chk; just deallocate argv. + shell_free_argv(argv); + xfree(exe_resolved); + return NULL; + } + argv[i++] = xstrdup(a); + }); + // Replace argv[0] with absolute path. The only reason for this is to make + // $PATHEXT work on Windows with jobstart([…]). #9569 + xfree(argv[0]); + argv[0] = exe_resolved; + + return argv; +} + +void return_register(int regname, typval_T *rettv) +{ + char buf[2] = { (char)regname, 0 }; + + rettv->v_type = VAR_STRING; + rettv->vval.v_string = xstrdup(buf); +} + +void screenchar_adjust(ScreenGrid **grid, int *row, int *col) +{ + // TODO(bfredl): this is a hack for legacy tests which use screenchar() + // to check printed messages on the screen (but not floats etc + // as these are not legacy features). If the compositor is refactored to + // have its own buffer, this should just read from it instead. + msg_scroll_flush(); + + *grid = ui_comp_get_grid_at_coord(*row, *col); + + // Make `row` and `col` relative to the grid + *row -= (*grid)->comp_row; + *col -= (*grid)->comp_col; +} + +/// Set line or list of lines in buffer "buf". +void set_buffer_lines(buf_T *buf, linenr_T lnum_arg, bool append, const typval_T *lines, + typval_T *rettv) + FUNC_ATTR_NONNULL_ARG(4, 5) +{ + linenr_T lnum = lnum_arg + (append ? 1 : 0); + const char *line = NULL; + list_T *l = NULL; + listitem_T *li = NULL; + long added = 0; + linenr_T append_lnum; + buf_T *curbuf_save = NULL; + win_T *curwin_save = NULL; + const bool is_curbuf = buf == curbuf; + const bool save_VIsual_active = VIsual_active; + + // When using the current buffer ml_mfp will be set if needed. Useful when + // setline() is used on startup. For other buffers the buffer must be + // loaded. + if (buf == NULL || (!is_curbuf && buf->b_ml.ml_mfp == NULL) || lnum < 1) { + rettv->vval.v_number = 1; // FAIL + return; + } + + if (!is_curbuf) { + VIsual_active = false; + curbuf_save = curbuf; + curwin_save = curwin; + curbuf = buf; + find_win_for_curbuf(); + } + + if (append) { + // appendbufline() uses the line number below which we insert + append_lnum = lnum - 1; + } else { + // setbufline() uses the line number above which we insert, we only + // append if it's below the last line + append_lnum = curbuf->b_ml.ml_line_count; + } + + if (lines->v_type == VAR_LIST) { + l = lines->vval.v_list; + li = tv_list_first(l); + } else { + line = tv_get_string_chk(lines); + } + + // Default result is zero == OK. + for (;;) { + if (lines->v_type == VAR_LIST) { + // List argument, get next string. + if (li == NULL) { + break; + } + line = tv_get_string_chk(TV_LIST_ITEM_TV(li)); + li = TV_LIST_ITEM_NEXT(l, li); + } + + rettv->vval.v_number = 1; // FAIL + if (line == NULL || lnum > curbuf->b_ml.ml_line_count + 1) { + break; + } + + // When coming here from Insert mode, sync undo, so that this can be + // undone separately from what was previously inserted. + if (u_sync_once == 2) { + u_sync_once = 1; // notify that u_sync() was called + u_sync(true); + } + + if (!append && lnum <= curbuf->b_ml.ml_line_count) { + // Existing line, replace it. + int old_len = (int)STRLEN(ml_get(lnum)); + if (u_savesub(lnum) == OK + && ml_replace(lnum, (char *)line, true) == OK) { + inserted_bytes(lnum, 0, old_len, (int)STRLEN(line)); + if (is_curbuf && lnum == curwin->w_cursor.lnum) { + check_cursor_col(); + } + rettv->vval.v_number = 0; // OK + } + } else if (added > 0 || u_save(lnum - 1, lnum) == OK) { + // append the line. + added++; + if (ml_append(lnum - 1, (char *)line, 0, false) == OK) { + rettv->vval.v_number = 0; // OK + } + } + + if (l == NULL) { // only one string argument + break; + } + lnum++; + } + + if (added > 0) { + appended_lines_mark(append_lnum, added); + + // Only adjust the cursor for buffers other than the current, unless it + // is the current window. For curbuf and other windows it has been done + // in mark_adjust_internal(). + FOR_ALL_TAB_WINDOWS(tp, wp) { + if (wp->w_buffer == buf + && (wp->w_buffer != curbuf || wp == curwin) + && wp->w_cursor.lnum > append_lnum) { + wp->w_cursor.lnum += (linenr_T)added; + } + } + check_cursor_col(); + update_topline(curwin); + } + + if (!is_curbuf) { + curbuf = curbuf_save; + curwin = curwin_save; + VIsual_active = save_VIsual_active; + } +} + +/// "stdpath()" helper for list results +void get_xdg_var_list(const XDGVarType xdg, typval_T *rettv) + FUNC_ATTR_NONNULL_ALL +{ + const void *iter = NULL; + list_T *const list = tv_list_alloc(kListLenShouldKnow); + rettv->v_type = VAR_LIST; + rettv->vval.v_list = list; + tv_list_ref(list); + char *const dirs = stdpaths_get_xdg_var(xdg); + if (dirs == NULL) { + return; + } + do { + size_t dir_len; + const char *dir; + iter = vim_env_iter(ENV_SEPCHAR, dirs, iter, &dir, &dir_len); + if (dir != NULL && dir_len > 0) { + char *dir_with_nvim = xmemdupz(dir, dir_len); + dir_with_nvim = concat_fnames_realloc(dir_with_nvim, "nvim", true); + tv_list_append_string(list, dir_with_nvim, (ssize_t)strlen(dir_with_nvim)); + xfree(dir_with_nvim); + } + } while (iter != NULL); + xfree(dirs); +} + +static list_T *string_to_list(const char *str, size_t len, const bool keepempty) +{ + if (!keepempty && str[len - 1] == NL) { + len--; + } + list_T *const list = tv_list_alloc(kListLenMayKnow); + encode_list_write(list, str, len); + return list; +} + +/// os_system wrapper. Handles 'verbose', :profile, and v:shell_error. +void get_system_output_as_rettv(typval_T *argvars, typval_T *rettv, bool retlist) +{ + proftime_T wait_time; + bool profiling = do_profiling == PROF_YES; + + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; + + if (check_secure()) { + return; + } + + // get input to the shell command (if any), and its length + ptrdiff_t input_len; + char *input = save_tv_as_string(&argvars[1], &input_len, false); + if (input_len < 0) { + assert(input == NULL); + return; + } + + // get shell command to execute + bool executable = true; + char **argv = tv_to_argv(&argvars[0], NULL, &executable); + if (!argv) { + if (!executable) { + set_vim_var_nr(VV_SHELL_ERROR, (long)-1); + } + xfree(input); + return; // Already did emsg. + } + + if (p_verbose > 3) { + char *cmdstr = shell_argv_to_str(argv); + verbose_enter_scroll(); + smsg(_("Executing command: \"%s\""), cmdstr); + msg_puts("\n\n"); + verbose_leave_scroll(); + xfree(cmdstr); + } + + if (profiling) { + prof_child_enter(&wait_time); + } + + // execute the command + size_t nread = 0; + char *res = NULL; + int status = os_system(argv, input, (size_t)input_len, &res, &nread); + + if (profiling) { + prof_child_exit(&wait_time); + } + + xfree(input); + + set_vim_var_nr(VV_SHELL_ERROR, (long)status); + + if (res == NULL) { + if (retlist) { + // return an empty list when there's no output + tv_list_alloc_ret(rettv, 0); + } else { + rettv->vval.v_string = xstrdup(""); + } + return; + } + + if (retlist) { + int keepempty = 0; + if (argvars[1].v_type != VAR_UNKNOWN && argvars[2].v_type != VAR_UNKNOWN) { + keepempty = (int)tv_get_number(&argvars[2]); + } + rettv->vval.v_list = string_to_list(res, nread, (bool)keepempty); + tv_list_ref(rettv->vval.v_list); + rettv->v_type = VAR_LIST; + + xfree(res); + } else { + // res may contain several NULs before the final terminating one. + // Replace them with SOH (1) like in get_cmd_output() to avoid truncation. + memchrsub(res, NUL, 1, nread); +#ifdef USE_CRNL + // translate <CR><NL> into <NL> + char *d = res; + for (char *s = res; *s; ++s) { + if (s[0] == CAR && s[1] == NL) { + ++s; + } + + *d++ = *s; + } + + *d = NUL; +#endif + rettv->vval.v_string = res; + } +} + +bool callback_from_typval(Callback *const callback, typval_T *const arg) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ + int r = OK; + + if (arg->v_type == VAR_PARTIAL && arg->vval.v_partial != NULL) { + callback->data.partial = arg->vval.v_partial; + callback->data.partial->pt_refcount++; + callback->type = kCallbackPartial; + } else if (arg->v_type == VAR_STRING + && arg->vval.v_string != NULL + && ascii_isdigit(*arg->vval.v_string)) { + r = FAIL; + } else if (arg->v_type == VAR_FUNC || arg->v_type == VAR_STRING) { + char *name = arg->vval.v_string; + if (name == NULL) { + r = FAIL; + } else if (*name == NUL) { + callback->type = kCallbackNone; + callback->data.funcref = NULL; + } else { + func_ref((char_u *)name); + callback->data.funcref = xstrdup(name); + callback->type = kCallbackFuncref; + } + } else if (nlua_is_table_from_lua(arg)) { + // TODO(tjdvries): UnifiedCallback + char *name = (char *)nlua_register_table_as_callable(arg); + + if (name != NULL) { + callback->data.funcref = xstrdup(name); + callback->type = kCallbackFuncref; + } else { + r = FAIL; + } + } else if (arg->v_type == VAR_SPECIAL + || (arg->v_type == VAR_NUMBER && arg->vval.v_number == 0)) { + callback->type = kCallbackNone; + callback->data.funcref = NULL; + } else { + r = FAIL; + } + + if (r == FAIL) { + emsg(_("E921: Invalid callback argument")); + return false; + } + return true; +} + +bool callback_call(Callback *const callback, const int argcount_in, typval_T *const argvars_in, + typval_T *const rettv) + FUNC_ATTR_NONNULL_ALL +{ + partial_T *partial; + char *name; + Array args = ARRAY_DICT_INIT; + Object rv; + switch (callback->type) { + case kCallbackFuncref: + name = callback->data.funcref; + partial = NULL; + break; + + case kCallbackPartial: + partial = callback->data.partial; + name = partial_name(partial); + break; + + case kCallbackLua: + rv = nlua_call_ref(callback->data.luaref, NULL, args, true, NULL); + switch (rv.type) { + case kObjectTypeBoolean: + return rv.data.boolean; + default: + return false; + } + + case kCallbackNone: + return false; + break; + + default: + abort(); + } + + funcexe_T funcexe = FUNCEXE_INIT; + funcexe.firstline = curwin->w_cursor.lnum; + funcexe.lastline = curwin->w_cursor.lnum; + funcexe.evaluate = true; + funcexe.partial = partial; + return call_func(name, -1, rettv, argcount_in, argvars_in, &funcexe); +} + +static bool set_ref_in_callback(Callback *callback, int copyID, ht_stack_T **ht_stack, + list_stack_T **list_stack) +{ + typval_T tv; + switch (callback->type) { + case kCallbackFuncref: + case kCallbackNone: + break; + + case kCallbackPartial: + tv.v_type = VAR_PARTIAL; + tv.vval.v_partial = callback->data.partial; + return set_ref_in_item(&tv, copyID, ht_stack, list_stack); + break; + + default: + abort(); + } + return false; +} + +static bool set_ref_in_callback_reader(CallbackReader *reader, int copyID, ht_stack_T **ht_stack, + list_stack_T **list_stack) +{ + if (set_ref_in_callback(&reader->cb, copyID, ht_stack, list_stack)) { + return true; + } + + if (reader->self) { + typval_T tv; + tv.v_type = VAR_DICT; + tv.vval.v_dict = reader->self; + return set_ref_in_item(&tv, copyID, ht_stack, list_stack); + } + return false; +} + +timer_T *find_timer_by_nr(varnumber_T xx) +{ + return pmap_get(uint64_t)(&timers, (uint64_t)xx); +} + +void add_timer_info(typval_T *rettv, timer_T *timer) +{ + list_T *list = rettv->vval.v_list; + dict_T *dict = tv_dict_alloc(); + + tv_list_append_dict(list, dict); + tv_dict_add_nr(dict, S_LEN("id"), timer->timer_id); + tv_dict_add_nr(dict, S_LEN("time"), timer->timeout); + tv_dict_add_nr(dict, S_LEN("paused"), timer->paused); + + tv_dict_add_nr(dict, S_LEN("repeat"), + (timer->repeat_count < 0 ? -1 : timer->repeat_count)); + + dictitem_T *di = tv_dict_item_alloc("callback"); + if (tv_dict_add(dict, di) == FAIL) { + xfree(di); + return; + } + + callback_put(&timer->callback, &di->di_tv); +} + +void add_timer_info_all(typval_T *rettv) +{ + tv_list_alloc_ret(rettv, map_size(&timers)); + timer_T *timer; + map_foreach_value(&timers, timer, { + if (!timer->stopped) { + add_timer_info(rettv, timer); + } + }) +} + +/// invoked on the main loop +void timer_due_cb(TimeWatcher *tw, void *data) +{ + timer_T *timer = (timer_T *)data; + int save_did_emsg = did_emsg; + const int called_emsg_before = called_emsg; + const bool save_ex_pressedreturn = get_pressedreturn(); + + if (timer->stopped || timer->paused) { + return; + } + + timer->refcount++; + // if repeat was negative repeat forever + if (timer->repeat_count >= 0 && --timer->repeat_count == 0) { + timer_stop(timer); + } + + typval_T argv[2] = { TV_INITIAL_VALUE, TV_INITIAL_VALUE }; + argv[0].v_type = VAR_NUMBER; + argv[0].vval.v_number = timer->timer_id; + typval_T rettv = TV_INITIAL_VALUE; + + callback_call(&timer->callback, 1, argv, &rettv); + + // Handle error message + if (called_emsg > called_emsg_before && did_emsg) { + timer->emsg_count++; + if (current_exception != NULL) { + discard_current_exception(); + } + } + did_emsg = save_did_emsg; + set_pressedreturn(save_ex_pressedreturn); + + if (timer->emsg_count >= 3) { + timer_stop(timer); + } + + tv_clear(&rettv); + + if (!timer->stopped && timer->timeout == 0) { + // special case: timeout=0 means the callback will be + // invoked again on the next event loop tick. + // we don't use uv_idle_t to not spin the event loop + // when the main loop is blocked. + time_watcher_start(&timer->tw, timer_due_cb, 0, 0); + } + timer_decref(timer); +} + +uint64_t timer_start(const long timeout, const int repeat_count, const Callback *const callback) +{ + timer_T *timer = xmalloc(sizeof *timer); + timer->refcount = 1; + timer->stopped = false; + timer->paused = false; + timer->emsg_count = 0; + timer->repeat_count = repeat_count; + timer->timeout = timeout; + timer->timer_id = (int)last_timer_id++; + timer->callback = *callback; + + time_watcher_init(&main_loop, &timer->tw, timer); + timer->tw.events = multiqueue_new_child(main_loop.events); + // if main loop is blocked, don't queue up multiple events + timer->tw.blockable = true; + time_watcher_start(&timer->tw, timer_due_cb, (uint64_t)timeout, (uint64_t)timeout); + + pmap_put(uint64_t)(&timers, (uint64_t)timer->timer_id, timer); + return (uint64_t)timer->timer_id; +} + +void timer_stop(timer_T *timer) +{ + if (timer->stopped) { + // avoid double free + return; + } + timer->stopped = true; + time_watcher_stop(&timer->tw); + time_watcher_close(&timer->tw, timer_close_cb); +} + +/// This will be run on the main loop after the last timer_due_cb, so at this +/// point it is safe to free the callback. +static void timer_close_cb(TimeWatcher *tw, void *data) +{ + timer_T *timer = (timer_T *)data; + multiqueue_free(timer->tw.events); + callback_free(&timer->callback); + pmap_del(uint64_t)(&timers, (uint64_t)timer->timer_id); + timer_decref(timer); +} + +static void timer_decref(timer_T *timer) +{ + if (--timer->refcount == 0) { + xfree(timer); + } +} + +void timer_stop_all(void) +{ + timer_T *timer; + map_foreach_value(&timers, timer, { + timer_stop(timer); + }) +} + +void timer_teardown(void) +{ + timer_stop_all(); +} + +/// Write "list" of strings to file "fd". +/// +/// @param fp File to write to. +/// @param[in] list List to write. +/// @param[in] binary Whether to write in binary mode. +/// +/// @return true in case of success, false otherwise. +bool write_list(FileDescriptor *const fp, const list_T *const list, const bool binary) + FUNC_ATTR_NONNULL_ARG(1) +{ + int error = 0; + TV_LIST_ITER_CONST(list, li, { + const char *const s = tv_get_string_chk(TV_LIST_ITEM_TV(li)); + if (s == NULL) { + return false; + } + const char *hunk_start = s; + for (const char *p = hunk_start;; p++) { + if (*p == NUL || *p == NL) { + if (p != hunk_start) { + const ptrdiff_t written = file_write(fp, hunk_start, + (size_t)(p - hunk_start)); + if (written < 0) { + error = (int)written; + goto write_list_error; + } + } + if (*p == NUL) { + break; + } else { + hunk_start = p + 1; + const ptrdiff_t written = file_write(fp, (char[]){ NUL }, 1); + if (written < 0) { + error = (int)written; + break; + } + } + } + } + if (!binary || TV_LIST_ITEM_NEXT(list, li) != NULL) { + const ptrdiff_t written = file_write(fp, "\n", 1); + if (written < 0) { + error = (int)written; + goto write_list_error; + } + } + }); + if ((error = file_flush(fp)) != 0) { + goto write_list_error; + } + return true; +write_list_error: + semsg(_(e_write2), os_strerror(error)); + return false; +} + +/// Write a blob to file with descriptor `fp`. +/// +/// @param[in] fp File to write to. +/// @param[in] blob Blob to write. +/// +/// @return true on success, or false on failure. +bool write_blob(FileDescriptor *const fp, const blob_T *const blob) + FUNC_ATTR_NONNULL_ARG(1) +{ + int error = 0; + const int len = tv_blob_len(blob); + if (len > 0) { + const ptrdiff_t written = file_write(fp, blob->bv_ga.ga_data, (size_t)len); + if (written < (ptrdiff_t)len) { + error = (int)written; + goto write_blob_error; + } + } + error = file_flush(fp); + if (error != 0) { + goto write_blob_error; + } + return true; +write_blob_error: + semsg(_(e_write2), os_strerror(error)); + return false; +} + +/// Read a blob from a file `fd`. +/// +/// @param[in] fd File to read from. +/// @param[in,out] blob Blob to write to. +/// +/// @return true on success, or false on failure. +bool read_blob(FILE *const fd, blob_T *const blob) + FUNC_ATTR_NONNULL_ALL +{ + FileInfo file_info; + if (!os_fileinfo_fd(fileno(fd), &file_info)) { + return false; + } + const int size = (int)os_fileinfo_size(&file_info); + ga_grow(&blob->bv_ga, size); + blob->bv_ga.ga_len = size; + if (fread(blob->bv_ga.ga_data, 1, (size_t)blob->bv_ga.ga_len, fd) + < (size_t)blob->bv_ga.ga_len) { + return false; + } + return true; +} + +/// Saves a typval_T as a string. +/// +/// For lists or buffers, replaces NLs with NUL and separates items with NLs. +/// +/// @param[in] tv Value to store as a string. +/// @param[out] len Length of the resulting string or -1 on error. +/// @param[in] endnl If true, the output will end in a newline (if a list). +/// @returns an allocated string if `tv` represents a VimL string, list, or +/// number; NULL otherwise. +char *save_tv_as_string(typval_T *tv, ptrdiff_t *const len, bool endnl) + FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ALL +{ + *len = 0; + if (tv->v_type == VAR_UNKNOWN) { + return NULL; + } + + // For other types, let tv_get_string_buf_chk() get the value or + // print an error. + if (tv->v_type != VAR_LIST && tv->v_type != VAR_NUMBER) { + const char *ret = tv_get_string_chk(tv); + if (ret) { + *len = (ptrdiff_t)strlen(ret); + return xmemdupz(ret, (size_t)(*len)); + } else { + *len = -1; + return NULL; + } + } + + if (tv->v_type == VAR_NUMBER) { // Treat number as a buffer-id. + buf_T *buf = buflist_findnr((int)tv->vval.v_number); + if (buf) { + for (linenr_T lnum = 1; lnum <= buf->b_ml.ml_line_count; lnum++) { + for (char *p = (char *)ml_get_buf(buf, lnum, false); *p != NUL; p++) { + *len += 1; + } + *len += 1; + } + } else { + semsg(_(e_nobufnr), tv->vval.v_number); + *len = -1; + return NULL; + } + + if (*len == 0) { + return NULL; + } + + char *ret = xmalloc((size_t)(*len) + 1); + char *end = ret; + for (linenr_T lnum = 1; lnum <= buf->b_ml.ml_line_count; lnum++) { + for (char *p = (char *)ml_get_buf(buf, lnum, false); *p != NUL; p++) { + *end++ = (*p == '\n') ? NUL : *p; + } + *end++ = '\n'; + } + *end = NUL; + *len = end - ret; + return ret; + } + + assert(tv->v_type == VAR_LIST); + // Pre-calculate the resulting length. + list_T *list = tv->vval.v_list; + TV_LIST_ITER_CONST(list, li, { + *len += (ptrdiff_t)strlen(tv_get_string(TV_LIST_ITEM_TV(li))) + 1; + }); + + if (*len == 0) { + return NULL; + } + + char *ret = xmalloc((size_t)(*len) + endnl); + char *end = ret; + TV_LIST_ITER_CONST(list, li, { + for (const char *s = tv_get_string(TV_LIST_ITEM_TV(li)); *s != NUL; s++) { + *end++ = (*s == '\n') ? NUL : *s; + } + if (endnl || TV_LIST_ITEM_NEXT(list, li) != NULL) { + *end++ = '\n'; + } + }); + *end = NUL; + *len = end - ret; + return ret; +} + +/// Convert the specified byte index of line 'lnum' in buffer 'buf' to a +/// character index. Works only for loaded buffers. Returns -1 on failure. +/// The index of the first byte and the first character is zero. +int buf_byteidx_to_charidx(buf_T *buf, linenr_T lnum, int byteidx) +{ + if (buf == NULL || buf->b_ml.ml_mfp == NULL) { + return -1; + } + + if (lnum > buf->b_ml.ml_line_count) { + lnum = buf->b_ml.ml_line_count; + } + + char *str = (char *)ml_get_buf(buf, lnum, false); + + if (*str == NUL) { + return 0; + } + + // count the number of characters + char *t = str; + int count; + for (count = 0; *t != NUL && t <= str + byteidx; count++) { + t += utfc_ptr2len(t); + } + + // In insert mode, when the cursor is at the end of a non-empty line, + // byteidx points to the NUL character immediately past the end of the + // string. In this case, add one to the character count. + if (*t == NUL && byteidx != 0 && t == str + byteidx) { + count++; + } + + return count - 1; +} + +/// Convert the specified character index of line 'lnum' in buffer 'buf' to a +/// byte index. Works only for loaded buffers. +/// The index of the first byte and the first character is zero. +/// +/// @return -1 on failure. +int buf_charidx_to_byteidx(buf_T *buf, linenr_T lnum, int charidx) +{ + if (buf == NULL || buf->b_ml.ml_mfp == NULL) { + return -1; + } + + if (lnum > buf->b_ml.ml_line_count) { + lnum = buf->b_ml.ml_line_count; + } + + char *str = (char *)ml_get_buf(buf, lnum, false); + + // Convert the character offset to a byte offset + char *t = str; + while (*t != NUL && --charidx > 0) { + t += utfc_ptr2len(t); + } + + return (int)(t - str); +} + +/// Translate a VimL object into a position +/// +/// Accepts VAR_LIST and VAR_STRING objects. Does not give an error for invalid +/// type. +/// +/// @param[in] tv Object to translate. +/// @param[in] dollar_lnum True when "$" is last line. +/// @param[out] ret_fnum Set to fnum for marks. +/// @param[in] charcol True to return character column. +/// +/// @return Pointer to position or NULL in case of error (e.g. invalid type). +pos_T *var2fpos(const typval_T *const tv, const bool dollar_lnum, int *const ret_fnum, + const bool charcol) + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL +{ + static pos_T pos; + + // Argument can be [lnum, col, coladd]. + if (tv->v_type == VAR_LIST) { + list_T *l; + int len; + bool error = false; + listitem_T *li; + + l = tv->vval.v_list; + if (l == NULL) { + return NULL; + } + + // Get the line number. + pos.lnum = (linenr_T)tv_list_find_nr(l, 0L, &error); + if (error || pos.lnum <= 0 || pos.lnum > curbuf->b_ml.ml_line_count) { + // Invalid line number. + return NULL; + } + + // Get the column number. + pos.col = (colnr_T)tv_list_find_nr(l, 1L, &error); + if (error) { + return NULL; + } + if (charcol) { + len = mb_charlen(ml_get(pos.lnum)); + } else { + len = (int)STRLEN(ml_get(pos.lnum)); + } + + // We accept "$" for the column number: last column. + li = tv_list_find(l, 1L); + if (li != NULL && TV_LIST_ITEM_TV(li)->v_type == VAR_STRING + && TV_LIST_ITEM_TV(li)->vval.v_string != NULL + && STRCMP(TV_LIST_ITEM_TV(li)->vval.v_string, "$") == 0) { + pos.col = len + 1; + } + + // Accept a position up to the NUL after the line. + if (pos.col == 0 || (int)pos.col > len + 1) { + // Invalid column number. + return NULL; + } + pos.col--; + + // Get the virtual offset. Defaults to zero. + pos.coladd = (colnr_T)tv_list_find_nr(l, 2L, &error); + if (error) { + pos.coladd = 0; + } + + return &pos; + } + + const char *const name = tv_get_string_chk(tv); + if (name == NULL) { + return NULL; + } + + pos.lnum = 0; + if (name[0] == '.') { + // cursor + pos = curwin->w_cursor; + } else if (name[0] == 'v' && name[1] == NUL) { + // Visual start + if (VIsual_active) { + pos = VIsual; + } else { + pos = curwin->w_cursor; + } + } else if (name[0] == '\'') { + // mark + int mname = (uint8_t)name[1]; + const fmark_T *const fm = mark_get(curbuf, curwin, NULL, kMarkAll, mname); + if (fm == NULL || fm->mark.lnum <= 0) { + return NULL; + } + pos = fm->mark; + // Vimscript behavior, only provide fnum if mark is global. + *ret_fnum = ASCII_ISUPPER(mname) || ascii_isdigit(mname) ? fm->fnum: *ret_fnum; + } + if (pos.lnum != 0) { + if (charcol) { + pos.col = buf_byteidx_to_charidx(curbuf, pos.lnum, pos.col); + } + return &pos; + } + + pos.coladd = 0; + + if (name[0] == 'w' && dollar_lnum) { + pos.col = 0; + if (name[1] == '0') { // "w0": first visible line + update_topline(curwin); + // In silent Ex mode topline is zero, but that's not a valid line + // number; use one instead. + pos.lnum = curwin->w_topline > 0 ? curwin->w_topline : 1; + return &pos; + } else if (name[1] == '$') { // "w$": last visible line + validate_botline(curwin); + // In silent Ex mode botline is zero, return zero then. + pos.lnum = curwin->w_botline > 0 ? curwin->w_botline - 1 : 0; + return &pos; + } + } else if (name[0] == '$') { // last column or line + if (dollar_lnum) { + pos.lnum = curbuf->b_ml.ml_line_count; + pos.col = 0; + } else { + pos.lnum = curwin->w_cursor.lnum; + if (charcol) { + pos.col = (colnr_T)mb_charlen(get_cursor_line_ptr()); + } else { + pos.col = (colnr_T)STRLEN(get_cursor_line_ptr()); + } + } + return &pos; + } + return NULL; +} + +/// Convert list in "arg" into a position and optional file number. +/// When "fnump" is NULL there is no file number, only 3 items. +/// Note that the column is passed on as-is, the caller may want to decrement +/// it to use 1 for the first column. +/// +/// @return FAIL when conversion is not possible, doesn't check the position for +/// validity. +int list2fpos(typval_T *arg, pos_T *posp, int *fnump, colnr_T *curswantp, bool charcol) +{ + list_T *l; + int i = 0; + long n; + + // List must be: [fnum, lnum, col, coladd, curswant], where "fnum" is only + // there when "fnump" isn't NULL; "coladd" and "curswant" are optional. + if (arg->v_type != VAR_LIST + || (l = arg->vval.v_list) == NULL + || tv_list_len(l) < (fnump == NULL ? 2 : 3) + || tv_list_len(l) > (fnump == NULL ? 4 : 5)) { + return FAIL; + } + + if (fnump != NULL) { + n = tv_list_find_nr(l, i++, NULL); // fnum + if (n < 0) { + return FAIL; + } + if (n == 0) { + n = curbuf->b_fnum; // Current buffer. + } + *fnump = (int)n; + } + + n = tv_list_find_nr(l, i++, NULL); // lnum + if (n < 0) { + return FAIL; + } + posp->lnum = (linenr_T)n; + + n = tv_list_find_nr(l, i++, NULL); // col + if (n < 0) { + return FAIL; + } + // If character position is specified, then convert to byte position + if (charcol) { + // Get the text for the specified line in a loaded buffer + buf_T *buf = buflist_findnr(fnump == NULL ? curbuf->b_fnum : *fnump); + if (buf == NULL || buf->b_ml.ml_mfp == NULL) { + return FAIL; + } + n = buf_charidx_to_byteidx(buf, posp->lnum, (int)n) + 1; + } + posp->col = (colnr_T)n; + + n = tv_list_find_nr(l, i, NULL); // off + if (n < 0) { + posp->coladd = 0; + } else { + posp->coladd = (colnr_T)n; + } + + if (curswantp != NULL) { + *curswantp = (colnr_T)tv_list_find_nr(l, i + 1, NULL); // curswant + } + + return OK; +} + +/// Get the length of an environment variable name. +/// Advance "arg" to the first character after the name. +/// +/// @return 0 for error. +int get_env_len(const char **arg) +{ + int len; + + const char *p; + for (p = *arg; vim_isIDc(*p); p++) {} + if (p == *arg) { // No name found. + return 0; + } + + len = (int)(p - *arg); + *arg = p; + return len; +} + +/// Get the length of the name of a function or internal variable. +/// +/// @param arg is advanced to the first non-white character after the name. +/// +/// @return 0 if something is wrong. +int get_id_len(const char **const arg) +{ + int len; + + // Find the end of the name. + const char *p; + for (p = *arg; eval_isnamec(*p); p++) { + if (*p == ':') { + // "s:" is start of "s:var", but "n:" is not and can be used in + // slice "[n:]". Also "xx:" is not a namespace. + len = (int)(p - *arg); + if (len > 1 + || (len == 1 && vim_strchr(namespace_char, **arg) == NULL)) { + break; + } + } + } + if (p == *arg) { // no name found + return 0; + } + + len = (int)(p - *arg); + *arg = (const char *)skipwhite(p); + + return len; +} + +/// Get the length of the name of a variable or function. +/// Only the name is recognized, does not handle ".key" or "[idx]". +/// +/// @param arg is advanced to the first non-white character after the name. +/// If the name contains 'magic' {}'s, expand them and return the +/// expanded name in an allocated string via 'alias' - caller must free. +/// +/// @return -1 if curly braces expansion failed or +/// 0 if something else is wrong. +int get_name_len(const char **const arg, char **alias, bool evaluate, bool verbose) +{ + int len; + + *alias = NULL; // default to no alias + + if ((*arg)[0] == (char)K_SPECIAL && (*arg)[1] == (char)KS_EXTRA + && (*arg)[2] == (char)KE_SNR) { + // Hard coded <SNR>, already translated. + *arg += 3; + return get_id_len(arg) + 3; + } + len = eval_fname_script(*arg); + if (len > 0) { + // literal "<SID>", "s:" or "<SNR>" + *arg += len; + } + + // Find the end of the name; check for {} construction. + char *expr_start; + char *expr_end; + const char *p = find_name_end((*arg), (const char **)&expr_start, (const char **)&expr_end, + len > 0 ? 0 : FNE_CHECK_START); + if (expr_start != NULL) { + if (!evaluate) { + len += (int)(p - *arg); + *arg = (const char *)skipwhite(p); + return len; + } + + /* + * Include any <SID> etc in the expanded string: + * Thus the -len here. + */ + char *temp_string = make_expanded_name(*arg - len, expr_start, expr_end, (char *)p); + if (temp_string == NULL) { + return -1; + } + *alias = temp_string; + *arg = (const char *)skipwhite(p); + return (int)STRLEN(temp_string); + } + + len += get_id_len(arg); + // Only give an error when there is something, otherwise it will be + // reported at a higher level. + if (len == 0 && verbose && **arg != NUL) { + semsg(_(e_invexpr2), *arg); + } + + return len; +} + +/// Find the end of a variable or function name, taking care of magic braces. +/// +/// @param expr_start if not NULL, then `expr_start` and `expr_end` are set to the +/// start and end of the first magic braces item. +/// +/// @param flags can have FNE_INCL_BR and FNE_CHECK_START. +/// +/// @return a pointer to just after the name. Equal to "arg" if there is no +/// valid name. +const char *find_name_end(const char *arg, const char **expr_start, const char **expr_end, + int flags) +{ + int mb_nest = 0; + int br_nest = 0; + int len; + + if (expr_start != NULL) { + *expr_start = NULL; + *expr_end = NULL; + } + + // Quick check for valid starting character. + if ((flags & FNE_CHECK_START) && !eval_isnamec1(*arg) && *arg != '{') { + return arg; + } + + const char *p; + for (p = arg; *p != NUL + && (eval_isnamec(*p) + || *p == '{' + || ((flags & FNE_INCL_BR) && (*p == '[' || *p == '.')) + || mb_nest != 0 + || br_nest != 0); MB_PTR_ADV(p)) { + if (*p == '\'') { + // skip over 'string' to avoid counting [ and ] inside it. + for (p = p + 1; *p != NUL && *p != '\''; MB_PTR_ADV(p)) {} + if (*p == NUL) { + break; + } + } else if (*p == '"') { + // skip over "str\"ing" to avoid counting [ and ] inside it. + for (p = p + 1; *p != NUL && *p != '"'; MB_PTR_ADV(p)) { + if (*p == '\\' && p[1] != NUL) { + ++p; + } + } + if (*p == NUL) { + break; + } + } else if (br_nest == 0 && mb_nest == 0 && *p == ':') { + // "s:" is start of "s:var", but "n:" is not and can be used in + // slice "[n:]". Also "xx:" is not a namespace. But {ns}: is. + len = (int)(p - arg); + if ((len > 1 && p[-1] != '}') + || (len == 1 && vim_strchr(namespace_char, *arg) == NULL)) { + break; + } + } + + if (mb_nest == 0) { + if (*p == '[') { + ++br_nest; + } else if (*p == ']') { + --br_nest; + } + } + + if (br_nest == 0) { + if (*p == '{') { + mb_nest++; + if (expr_start != NULL && *expr_start == NULL) { + *expr_start = p; + } + } else if (*p == '}') { + mb_nest--; + if (expr_start != NULL && mb_nest == 0 && *expr_end == NULL) { + *expr_end = p; + } + } + } + } + + return p; +} + +/// Expands out the 'magic' {}'s in a variable/function name. +/// Note that this can call itself recursively, to deal with +/// constructs like foo{bar}{baz}{bam} +/// The four pointer arguments point to "foo{expre}ss{ion}bar" +/// "in_start" ^ +/// "expr_start" ^ +/// "expr_end" ^ +/// "in_end" ^ +/// +/// @return a new allocated string, which the caller must free or +/// NULL for failure. +static char *make_expanded_name(const char *in_start, char *expr_start, char *expr_end, + char *in_end) +{ + char c1; + char *retval = NULL; + char *temp_result; + char *nextcmd = NULL; + + if (expr_end == NULL || in_end == NULL) { + return NULL; + } + *expr_start = NUL; + *expr_end = NUL; + c1 = *in_end; + *in_end = NUL; + + temp_result = eval_to_string(expr_start + 1, &nextcmd, false); + if (temp_result != NULL && nextcmd == NULL) { + retval = xmalloc(STRLEN(temp_result) + (size_t)(expr_start - in_start) + + (size_t)(in_end - expr_end) + 1); + STRCPY(retval, in_start); + STRCAT(retval, temp_result); + STRCAT(retval, expr_end + 1); + } + xfree(temp_result); + + *in_end = c1; // put char back for error messages + *expr_start = '{'; + *expr_end = '}'; + + if (retval != NULL) { + temp_result = (char *)find_name_end(retval, + (const char **)&expr_start, + (const char **)&expr_end, 0); + if (expr_start != NULL) { + // Further expansion! + temp_result = make_expanded_name(retval, expr_start, + expr_end, temp_result); + xfree(retval); + retval = temp_result; + } + } + + return retval; +} + +/// @return TRUE if character "c" can be used in a variable or function name. +/// Does not include '{' or '}' for magic braces. +int eval_isnamec(int c) +{ + return ASCII_ISALNUM(c) || c == '_' || c == ':' || c == AUTOLOAD_CHAR; +} + +/// @return TRUE if character "c" can be used as the first character in a +/// variable or function name (excluding '{' and '}'). +int eval_isnamec1(int c) +{ + return ASCII_ISALPHA(c) || c == '_'; +} + +/// Get typval_T v: variable value. +typval_T *get_vim_var_tv(int idx) +{ + return &vimvars[idx].vv_tv; +} + +/// Get number v: variable value. +varnumber_T get_vim_var_nr(int idx) FUNC_ATTR_PURE +{ + return vimvars[idx].vv_nr; +} + +/// Get string v: variable value. Uses a static buffer, can only be used once. +/// If the String variable has never been set, return an empty string. +/// Never returns NULL. +char *get_vim_var_str(int idx) + FUNC_ATTR_PURE FUNC_ATTR_NONNULL_RET +{ + return (char *)tv_get_string(&vimvars[idx].vv_tv); +} + +/// Get List v: variable value. Caller must take care of reference count when +/// needed. +list_T *get_vim_var_list(int idx) FUNC_ATTR_PURE +{ + return vimvars[idx].vv_list; +} + +/// Get Dictionary v: variable value. Caller must take care of reference count +/// when needed. +dict_T *get_vim_var_dict(int idx) FUNC_ATTR_PURE +{ + return vimvars[idx].vv_dict; +} + +/// Set v:char to character "c". +void set_vim_var_char(int c) +{ + char buf[MB_MAXBYTES + 1]; + + buf[utf_char2bytes(c, buf)] = NUL; + set_vim_var_string(VV_CHAR, buf, -1); +} + +/// Set v:count to "count" and v:count1 to "count1". +/// +/// @param set_prevcount if TRUE, first set v:prevcount from v:count. +void set_vcount(long count, long count1, int set_prevcount) +{ + if (set_prevcount) { + vimvars[VV_PREVCOUNT].vv_nr = vimvars[VV_COUNT].vv_nr; + } + vimvars[VV_COUNT].vv_nr = count; + vimvars[VV_COUNT1].vv_nr = count1; +} + +/// Set number v: variable to the given value +/// +/// @param[in] idx Index of variable to set. +/// @param[in] val Value to set to. +void set_vim_var_nr(const VimVarIndex idx, const varnumber_T val) +{ + tv_clear(&vimvars[idx].vv_tv); + vimvars[idx].vv_type = VAR_NUMBER; + vimvars[idx].vv_nr = val; +} + +/// Set boolean v: {true, false} to the given value +/// +/// @param[in] idx Index of variable to set. +/// @param[in] val Value to set to. +void set_vim_var_bool(const VimVarIndex idx, const BoolVarValue val) +{ + tv_clear(&vimvars[idx].vv_tv); + vimvars[idx].vv_type = VAR_BOOL; + vimvars[idx].vv_bool = val; +} + +/// Set special v: variable to the given value +/// +/// @param[in] idx Index of variable to set. +/// @param[in] val Value to set to. +void set_vim_var_special(const VimVarIndex idx, const SpecialVarValue val) +{ + tv_clear(&vimvars[idx].vv_tv); + vimvars[idx].vv_type = VAR_SPECIAL; + vimvars[idx].vv_special = val; +} + +/// Set string v: variable to the given string +/// +/// @param[in] idx Index of variable to set. +/// @param[in] val Value to set to. Will be copied. +/// @param[in] len Length of that value or -1 in which case strlen() will be +/// used. +void set_vim_var_string(const VimVarIndex idx, const char *const val, const ptrdiff_t len) +{ + tv_clear(&vimvars[idx].vv_di.di_tv); + vimvars[idx].vv_type = VAR_STRING; + if (val == NULL) { + vimvars[idx].vv_str = NULL; + } else if (len == -1) { + vimvars[idx].vv_str = xstrdup(val); + } else { + vimvars[idx].vv_str = xstrndup(val, (size_t)len); + } +} + +/// Set list v: variable to the given list +/// +/// @param[in] idx Index of variable to set. +/// @param[in,out] val Value to set to. Reference count will be incremented. +void set_vim_var_list(const VimVarIndex idx, list_T *const val) +{ + tv_clear(&vimvars[idx].vv_di.di_tv); + vimvars[idx].vv_type = VAR_LIST; + vimvars[idx].vv_list = val; + if (val != NULL) { + tv_list_ref(val); + } +} + +/// Set Dictionary v: variable to the given dictionary +/// +/// @param[in] idx Index of variable to set. +/// @param[in,out] val Value to set to. Reference count will be incremented. +/// Also keys of the dictionary will be made read-only. +void set_vim_var_dict(const VimVarIndex idx, dict_T *const val) +{ + tv_clear(&vimvars[idx].vv_di.di_tv); + vimvars[idx].vv_type = VAR_DICT; + vimvars[idx].vv_dict = val; + + if (val != NULL) { + val->dv_refcount++; + // Set readonly + tv_dict_set_keys_readonly(val); + } +} + +/// Set the v:argv list. +void set_argv_var(char **argv, int argc) +{ + list_T *l = tv_list_alloc(argc); + int i; + + tv_list_set_lock(l, VAR_FIXED); + for (i = 0; i < argc; i++) { + tv_list_append_string(l, (const char *const)argv[i], -1); + TV_LIST_ITEM_TV(tv_list_last(l))->v_lock = VAR_FIXED; + } + set_vim_var_list(VV_ARGV, l); +} + +/// Set v:register if needed. +void set_reg_var(int c) +{ + char regname; + + if (c == 0 || c == ' ') { + regname = '"'; + } else { + regname = (char)c; + } + // Avoid free/alloc when the value is already right. + if (vimvars[VV_REG].vv_str == NULL || vimvars[VV_REG].vv_str[0] != c) { + set_vim_var_string(VV_REG, ®name, 1); + } +} + +/// Get or set v:exception. If "oldval" == NULL, return the current value. +/// Otherwise, restore the value to "oldval" and return NULL. +/// Must always be called in pairs to save and restore v:exception! Does not +/// take care of memory allocations. +char *v_exception(char *oldval) +{ + if (oldval == NULL) { + return vimvars[VV_EXCEPTION].vv_str; + } + + vimvars[VV_EXCEPTION].vv_str = oldval; + return NULL; +} + +/// Get or set v:throwpoint. If "oldval" == NULL, return the current value. +/// Otherwise, restore the value to "oldval" and return NULL. +/// Must always be called in pairs to save and restore v:throwpoint! Does not +/// take care of memory allocations. +char *v_throwpoint(char *oldval) +{ + if (oldval == NULL) { + return vimvars[VV_THROWPOINT].vv_str; + } + + vimvars[VV_THROWPOINT].vv_str = oldval; + return NULL; +} + +/// Set v:cmdarg. +/// If "eap" != NULL, use "eap" to generate the value and return the old value. +/// If "oldarg" != NULL, restore the value to "oldarg" and return NULL. +/// Must always be called in pairs! +char *set_cmdarg(exarg_T *eap, char *oldarg) +{ + char *oldval = vimvars[VV_CMDARG].vv_str; + if (eap == NULL) { + xfree(oldval); + vimvars[VV_CMDARG].vv_str = oldarg; + return NULL; + } + + size_t len = 0; + if (eap->force_bin == FORCE_BIN) { + len = 6; + } else if (eap->force_bin == FORCE_NOBIN) { + len = 8; + } + + if (eap->read_edit) { + len += 7; + } + + if (eap->force_ff != 0) { + len += 10; // " ++ff=unix" + } + if (eap->force_enc != 0) { + len += STRLEN(eap->cmd + eap->force_enc) + 7; + } + if (eap->bad_char != 0) { + len += 7 + 4; // " ++bad=" + "keep" or "drop" + } + + const size_t newval_len = len + 1; + char *newval = xmalloc(newval_len); + + if (eap->force_bin == FORCE_BIN) { + snprintf(newval, newval_len, " ++bin"); + } else if (eap->force_bin == FORCE_NOBIN) { + snprintf(newval, newval_len, " ++nobin"); + } else { + *newval = NUL; + } + + if (eap->read_edit) { + STRCAT(newval, " ++edit"); + } + + if (eap->force_ff != 0) { + snprintf(newval + STRLEN(newval), newval_len, " ++ff=%s", + eap->force_ff == 'u' ? "unix" : + eap->force_ff == 'd' ? "dos" : "mac"); + } + if (eap->force_enc != 0) { + snprintf(newval + STRLEN(newval), newval_len, " ++enc=%s", + eap->cmd + eap->force_enc); + } + if (eap->bad_char == BAD_KEEP) { + STRCPY(newval + STRLEN(newval), " ++bad=keep"); + } else if (eap->bad_char == BAD_DROP) { + STRCPY(newval + STRLEN(newval), " ++bad=drop"); + } else if (eap->bad_char != 0) { + snprintf(newval + STRLEN(newval), newval_len, " ++bad=%c", + eap->bad_char); + } + vimvars[VV_CMDARG].vv_str = newval; + return oldval; +} + +/// Check if variable "name[len]" is a local variable or an argument. +/// If so, "*eval_lavars_used" is set to true. +static void check_vars(const char *name, size_t len) +{ + if (eval_lavars_used == NULL) { + return; + } + + const char *varname; + hashtab_T *ht = find_var_ht(name, len, &varname); + + if (ht == get_funccal_local_ht() || ht == get_funccal_args_ht()) { + if (find_var(name, len, NULL, true) != NULL) { + *eval_lavars_used = true; + } + } +} + +/// check if special v:lua value for calling lua functions +bool is_luafunc(partial_T *partial) + FUNC_ATTR_PURE +{ + return partial == vvlua_partial; +} + +/// check if special v:lua value for calling lua functions +static bool tv_is_luafunc(typval_T *tv) +{ + return tv->v_type == VAR_PARTIAL && is_luafunc(tv->vval.v_partial); +} + +/// Skips one character past the end of the name of a v:lua function. +/// @param p Pointer to the char AFTER the "v:lua." prefix. +/// @return Pointer to the char one past the end of the function's name. +const char *skip_luafunc_name(const char *p) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT +{ + while (ASCII_ISALNUM(*p) || *p == '_' || *p == '-' || *p == '.' || *p == '\'') { + p++; + } + return p; +} + +/// check the function name after "v:lua." +int check_luafunc_name(const char *const str, const bool paren) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT +{ + const char *const p = skip_luafunc_name(str); + if (*p != (paren ? '(' : NUL)) { + return 0; + } else { + return (int)(p - str); + } +} + +/// Handle: +/// - expr[expr], expr[expr:expr] subscript +/// - ".name" lookup +/// - function call with Funcref variable: func(expr) +/// - method call: var->method() +/// +/// Can all be combined in any order: dict.func(expr)[idx]['func'](expr)->len() +/// +/// @param evaluate do more than finding the end +/// @param verbose give error messages +/// @param start_leader start of '!' and '-' prefixes +/// @param end_leaderp end of '!' and '-' prefixes +int handle_subscript(const char **const arg, typval_T *rettv, int evaluate, int verbose, + const char *const start_leader, const char **const end_leaderp) +{ + int ret = OK; + dict_T *selfdict = NULL; + const char *lua_funcname = NULL; + + if (tv_is_luafunc(rettv)) { + if (**arg != '.') { + tv_clear(rettv); + ret = FAIL; + } else { + (*arg)++; + + lua_funcname = *arg; + const int len = check_luafunc_name(*arg, true); + if (len == 0) { + tv_clear(rettv); + ret = FAIL; + } + (*arg) += len; + } + } + + // "." is ".name" lookup when we found a dict. + while (ret == OK + && (((**arg == '[' || (**arg == '.' && rettv->v_type == VAR_DICT) + || (**arg == '(' && (!evaluate || tv_is_func(*rettv)))) + && !ascii_iswhite(*(*arg - 1))) + || (**arg == '-' && (*arg)[1] == '>'))) { + if (**arg == '(') { + ret = call_func_rettv((char **)arg, rettv, evaluate, selfdict, NULL, lua_funcname); + + // Stop the expression evaluation when immediately aborting on + // error, or when an interrupt occurred or an exception was thrown + // but not caught. + if (aborting()) { + if (ret == OK) { + tv_clear(rettv); + } + ret = FAIL; + } + tv_dict_unref(selfdict); + selfdict = NULL; + } else if (**arg == '-') { + // Expression "-1.0->method()" applies the leader "-" before + // applying ->. + if (evaluate && *end_leaderp > start_leader) { + ret = eval7_leader(rettv, (char *)start_leader, end_leaderp); + } + if (ret == OK) { + if ((*arg)[2] == '{') { + // expr->{lambda}() + ret = eval_lambda((char **)arg, rettv, evaluate, verbose); + } else { + // expr->name() + ret = eval_method((char **)arg, rettv, evaluate, verbose); + } + } + } else { // **arg == '[' || **arg == '.' + tv_dict_unref(selfdict); + if (rettv->v_type == VAR_DICT) { + selfdict = rettv->vval.v_dict; + if (selfdict != NULL) { + ++selfdict->dv_refcount; + } + } else { + selfdict = NULL; + } + if (eval_index((char **)arg, rettv, evaluate, verbose) == FAIL) { + tv_clear(rettv); + ret = FAIL; + } + } + } + + // Turn "dict.Func" into a partial for "Func" bound to "dict". + if (selfdict != NULL && tv_is_func(*rettv)) { + set_selfdict(rettv, selfdict); + } + + tv_dict_unref(selfdict); + return ret; +} + +void set_selfdict(typval_T *const rettv, dict_T *const selfdict) +{ + // Don't do this when "dict.Func" is already a partial that was bound + // explicitly (pt_auto is false). + if (rettv->v_type == VAR_PARTIAL && !rettv->vval.v_partial->pt_auto + && rettv->vval.v_partial->pt_dict != NULL) { + return; + } + make_partial(selfdict, rettv); +} + +/// Find variable "name" in the list of variables. +/// Careful: "a:0" variables don't have a name. +/// When "htp" is not NULL we are writing to the variable, set "htp" to the +/// hashtab_T used. +/// +/// @return a pointer to it if found, NULL if not found. +dictitem_T *find_var(const char *const name, const size_t name_len, hashtab_T **htp, + int no_autoload) +{ + const char *varname; + hashtab_T *const ht = find_var_ht(name, name_len, &varname); + if (htp != NULL) { + *htp = ht; + } + if (ht == NULL) { + return NULL; + } + dictitem_T *const ret = find_var_in_ht(ht, *name, + varname, + name_len - (size_t)(varname - name), + no_autoload || htp != NULL); + if (ret != NULL) { + return ret; + } + + // Search in parent scope for lambda + return find_var_in_scoped_ht(name, name_len, no_autoload || htp != NULL); +} + +/// Find variable in hashtab. +/// When "varname" is empty returns curwin/curtab/etc vars dictionary. +/// +/// @param[in] ht Hashtab to find variable in. +/// @param[in] htname Hashtab name (first character). +/// @param[in] varname Variable name. +/// @param[in] varname_len Variable name length. +/// @param[in] no_autoload If true then autoload scripts will not be sourced +/// if autoload variable was not found. +/// +/// @return pointer to the dictionary item with the found variable or NULL if it +/// was not found. +dictitem_T *find_var_in_ht(hashtab_T *const ht, int htname, const char *const varname, + const size_t varname_len, int no_autoload) + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL +{ + hashitem_T *hi; + + if (varname_len == 0) { + // Must be something like "s:", otherwise "ht" would be NULL. + switch (htname) { + case 's': + return (dictitem_T *)&SCRIPT_SV(current_sctx.sc_sid)->sv_var; + case 'g': + return (dictitem_T *)&globvars_var; + case 'v': + return (dictitem_T *)&vimvars_var; + case 'b': + return (dictitem_T *)&curbuf->b_bufvar; + case 'w': + return (dictitem_T *)&curwin->w_winvar; + case 't': + return (dictitem_T *)&curtab->tp_winvar; + case 'l': + return get_funccal_local_var(); + case 'a': + return get_funccal_args_var(); + } + return NULL; + } + + hi = hash_find_len(ht, varname, varname_len); + if (HASHITEM_EMPTY(hi)) { + // For global variables we may try auto-loading the script. If it + // worked find the variable again. Don't auto-load a script if it was + // loaded already, otherwise it would be loaded every time when + // checking if a function name is a Funcref variable. + if (ht == &globvarht && !no_autoload) { + // Note: script_autoload() may make "hi" invalid. It must either + // be obtained again or not used. + if (!script_autoload(varname, varname_len, false) || aborting()) { + return NULL; + } + hi = hash_find_len(ht, varname, varname_len); + } + if (HASHITEM_EMPTY(hi)) { + return NULL; + } + } + return TV_DICT_HI2DI(hi); +} + +/// Finds the dict (g:, l:, s:, …) and hashtable used for a variable. +/// +/// Assigns SID if s: scope is accessed from Lua or anonymous Vimscript. #15994 +/// +/// @param[in] name Variable name, possibly with scope prefix. +/// @param[in] name_len Variable name length. +/// @param[out] varname Will be set to the start of the name without scope +/// prefix. +/// @param[out] d Scope dictionary. +/// +/// @return Scope hashtab, NULL if name is not valid. +hashtab_T *find_var_ht_dict(const char *name, const size_t name_len, const char **varname, + dict_T **d) +{ + hashitem_T *hi; + funccall_T *funccal = get_funccal(); + *d = NULL; + + if (name_len == 0) { + return NULL; + } + if (name_len == 1 || name[1] != ':') { + // name has implicit scope + if (name[0] == ':' || name[0] == AUTOLOAD_CHAR) { + // The name must not start with a colon or #. + return NULL; + } + *varname = name; + + // "version" is "v:version" in all scopes + hi = hash_find_len(&compat_hashtab, name, name_len); + if (!HASHITEM_EMPTY(hi)) { + return &compat_hashtab; + } + + if (funccal == NULL) { // global variable + *d = &globvardict; + } else { // l: variable + *d = &funccal->l_vars; + } + goto end; + } + + *varname = name + 2; + if (*name == 'g') { // global variable + *d = &globvardict; + } else if (name_len > 2 + && (memchr(name + 2, ':', name_len - 2) != NULL + || memchr(name + 2, AUTOLOAD_CHAR, name_len - 2) != NULL)) { + // There must be no ':' or '#' in the rest of the name if g: was not used + return NULL; + } + + if (*name == 'b') { // buffer variable + *d = curbuf->b_vars; + } else if (*name == 'w') { // window variable + *d = curwin->w_vars; + } else if (*name == 't') { // tab page variable + *d = curtab->tp_vars; + } else if (*name == 'v') { // v: variable + *d = &vimvardict; + } else if (*name == 'a' && funccal != NULL) { // function argument + *d = &funccal->l_avars; + } else if (*name == 'l' && funccal != NULL) { // local variable + *d = &funccal->l_vars; + } else if (*name == 's' // script variable + && (current_sctx.sc_sid > 0 || current_sctx.sc_sid == SID_STR + || current_sctx.sc_sid == SID_LUA) + && current_sctx.sc_sid <= ga_scripts.ga_len) { + // For anonymous scripts without a script item, create one now so script vars can be used + if (current_sctx.sc_sid == SID_LUA) { + // try to resolve lua filename & line no so it can be shown in lastset messages. + nlua_set_sctx(¤t_sctx); + if (current_sctx.sc_sid != SID_LUA) { + // Great we have valid location. Now here this out we'll create a new + // script context with the name and lineno of this one. why ? + // for behavioral consistency. With this different anonymous exec from + // same file can't access each others script local stuff. We need to do + // this all other cases except this will act like that otherwise. + const LastSet last_set = (LastSet){ + .script_ctx = current_sctx, + .channel_id = LUA_INTERNAL_CALL, + }; + bool should_free; + // should_free is ignored as script_sctx will be resolved to a fnmae + // & new_script_item will consume it. + char *sc_name = (char *)get_scriptname(last_set, &should_free); + new_script_item(sc_name, ¤t_sctx.sc_sid); + } + } + if (current_sctx.sc_sid == SID_STR || current_sctx.sc_sid == SID_LUA) { + // Create SID if s: scope is accessed from Lua or anon Vimscript. #15994 + new_script_item(NULL, ¤t_sctx.sc_sid); + } + *d = &SCRIPT_SV(current_sctx.sc_sid)->sv_dict; + } + +end: + return *d ? &(*d)->dv_hashtab : NULL; +} + +/// Find the hashtable used for a variable +/// +/// @param[in] name Variable name, possibly with scope prefix. +/// @param[in] name_len Variable name length. +/// @param[out] varname Will be set to the start of the name without scope +/// prefix. +/// +/// @return Scope hashtab, NULL if name is not valid. +hashtab_T *find_var_ht(const char *name, const size_t name_len, const char **varname) +{ + dict_T *d; + return find_var_ht_dict(name, name_len, varname, &d); +} + +/// Allocate a new hashtab for a sourced script. It will be used while +/// sourcing this script and when executing functions defined in the script. +void new_script_vars(scid_T id) +{ + hashtab_T *ht; + scriptvar_T *sv; + + ga_grow(&ga_scripts, id - ga_scripts.ga_len); + { + /* Re-allocating ga_data means that an ht_array pointing to + * ht_smallarray becomes invalid. We can recognize this: ht_mask is + * at its init value. Also reset "v_dict", it's always the same. */ + for (int i = 1; i <= ga_scripts.ga_len; ++i) { + ht = &SCRIPT_VARS(i); + if (ht->ht_mask == HT_INIT_SIZE - 1) { + ht->ht_array = ht->ht_smallarray; + } + sv = SCRIPT_SV(i); + sv->sv_var.di_tv.vval.v_dict = &sv->sv_dict; + } + + while (ga_scripts.ga_len < id) { + sv = SCRIPT_SV(ga_scripts.ga_len + 1) = xcalloc(1, sizeof(scriptvar_T)); + init_var_dict(&sv->sv_dict, &sv->sv_var, VAR_SCOPE); + ++ga_scripts.ga_len; + } + } +} + +/// Initialize dictionary "dict" as a scope and set variable "dict_var" to +/// point to it. +void init_var_dict(dict_T *dict, ScopeDictDictItem *dict_var, ScopeType scope) +{ + hash_init(&dict->dv_hashtab); + dict->dv_lock = VAR_UNLOCKED; + dict->dv_scope = scope; + dict->dv_refcount = DO_NOT_FREE_CNT; + dict->dv_copyID = 0; + dict_var->di_tv.vval.v_dict = dict; + dict_var->di_tv.v_type = VAR_DICT; + dict_var->di_tv.v_lock = VAR_FIXED; + dict_var->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX; + dict_var->di_key[0] = NUL; + QUEUE_INIT(&dict->watchers); +} + +/// Unreference a dictionary initialized by init_var_dict(). +void unref_var_dict(dict_T *dict) +{ + /* Now the dict needs to be freed if no one else is using it, go back to + * normal reference counting. */ + dict->dv_refcount -= DO_NOT_FREE_CNT - 1; + tv_dict_unref(dict); +} + +/// Make a copy of an item +/// +/// Lists and Dictionaries are also copied. +/// +/// @param[in] conv If not NULL, convert all copied strings. +/// @param[in] from Value to copy. +/// @param[out] to Location where to copy to. +/// @param[in] deep If true, use copy the container and all of the contained +/// containers (nested). +/// @param[in] copyID If non-zero then when container is referenced more then +/// once then copy of it that was already done is used. E.g. +/// when copying list `list = [list2, list2]` (`list[0] is +/// list[1]`) var_item_copy with zero copyID will emit +/// a copy with (`copy[0] isnot copy[1]`), with non-zero it +/// will emit a copy with (`copy[0] is copy[1]`) like in the +/// original list. Not used when deep is false. +int var_item_copy(const vimconv_T *const conv, typval_T *const from, typval_T *const to, + const bool deep, const int copyID) + FUNC_ATTR_NONNULL_ARG(2, 3) +{ + static int recurse = 0; + int ret = OK; + + if (recurse >= DICT_MAXNEST) { + emsg(_("E698: variable nested too deep for making a copy")); + return FAIL; + } + ++recurse; + + switch (from->v_type) { + case VAR_NUMBER: + case VAR_FLOAT: + case VAR_FUNC: + case VAR_PARTIAL: + case VAR_BOOL: + case VAR_SPECIAL: + tv_copy(from, to); + break; + case VAR_STRING: + if (conv == NULL || conv->vc_type == CONV_NONE + || from->vval.v_string == NULL) { + tv_copy(from, to); + } else { + to->v_type = VAR_STRING; + to->v_lock = VAR_UNLOCKED; + if ((to->vval.v_string = (char *)string_convert((vimconv_T *)conv, + (char_u *)from->vval.v_string, + NULL)) + == NULL) { + to->vval.v_string = xstrdup(from->vval.v_string); + } + } + break; + case VAR_LIST: + to->v_type = VAR_LIST; + to->v_lock = VAR_UNLOCKED; + if (from->vval.v_list == NULL) { + to->vval.v_list = NULL; + } else if (copyID != 0 && tv_list_copyid(from->vval.v_list) == copyID) { + // Use the copy made earlier. + to->vval.v_list = tv_list_latest_copy(from->vval.v_list); + tv_list_ref(to->vval.v_list); + } else { + to->vval.v_list = tv_list_copy(conv, from->vval.v_list, deep, copyID); + } + if (to->vval.v_list == NULL && from->vval.v_list != NULL) { + ret = FAIL; + } + break; + case VAR_BLOB: + tv_blob_copy(from, to); + break; + case VAR_DICT: + to->v_type = VAR_DICT; + to->v_lock = VAR_UNLOCKED; + if (from->vval.v_dict == NULL) { + to->vval.v_dict = NULL; + } else if (copyID != 0 && from->vval.v_dict->dv_copyID == copyID) { + // use the copy made earlier + to->vval.v_dict = from->vval.v_dict->dv_copydict; + ++to->vval.v_dict->dv_refcount; + } else { + to->vval.v_dict = tv_dict_copy(conv, from->vval.v_dict, deep, copyID); + } + if (to->vval.v_dict == NULL && from->vval.v_dict != NULL) { + ret = FAIL; + } + break; + case VAR_UNKNOWN: + internal_error("var_item_copy(UNKNOWN)"); + ret = FAIL; + } + --recurse; + return ret; +} + +/// ":echo expr1 ..." print each argument separated with a space, add a +/// newline at the end. +/// ":echon expr1 ..." print each argument plain. +void ex_echo(exarg_T *eap) +{ + char *arg = eap->arg; + typval_T rettv; + bool atstart = true; + bool need_clear = true; + const int did_emsg_before = did_emsg; + const int called_emsg_before = called_emsg; + + if (eap->skip) { + ++emsg_skip; + } + while (*arg != NUL && *arg != '|' && *arg != '\n' && !got_int) { + // If eval1() causes an error message the text from the command may + // still need to be cleared. E.g., "echo 22,44". + need_clr_eos = true; + + { + char *p = arg; + if (eval1(&arg, &rettv, !eap->skip) == FAIL) { + // Report the invalid expression unless the expression evaluation + // has been cancelled due to an aborting error, an interrupt, or an + // exception. + if (!aborting() && did_emsg == did_emsg_before + && called_emsg == called_emsg_before) { + semsg(_(e_invexpr2), p); + } + need_clr_eos = false; + break; + } + need_clr_eos = false; + } + + if (!eap->skip) { + if (atstart) { + atstart = false; + // Call msg_start() after eval1(), evaluating the expression + // may cause a message to appear. + if (eap->cmdidx == CMD_echo) { + // Mark the saved text as finishing the line, so that what + // follows is displayed on a new line when scrolling back + // at the more prompt. + msg_sb_eol(); + msg_start(); + } + } else if (eap->cmdidx == CMD_echo) { + msg_puts_attr(" ", echo_attr); + } + char *tofree = encode_tv2echo(&rettv, NULL); + if (*tofree != NUL) { + msg_ext_set_kind("echo"); + msg_multiline_attr(tofree, echo_attr, true, &need_clear); + } + xfree(tofree); + } + tv_clear(&rettv); + arg = skipwhite(arg); + } + eap->nextcmd = (char *)check_nextcmd((char_u *)arg); + + if (eap->skip) { + emsg_skip--; + } else { + // remove text that may still be there from the command + if (need_clear) { + msg_clr_eos(); + } + if (eap->cmdidx == CMD_echo) { + msg_end(); + } + } +} + +/// ":echohl {name}". +void ex_echohl(exarg_T *eap) +{ + echo_attr = syn_name2attr((char_u *)eap->arg); +} + +/// ":execute expr1 ..." execute the result of an expression. +/// ":echomsg expr1 ..." Print a message +/// ":echoerr expr1 ..." Print an error +/// Each gets spaces around each argument and a newline at the end for +/// echo commands +void ex_execute(exarg_T *eap) +{ + char *arg = eap->arg; + typval_T rettv; + int ret = OK; + garray_T ga; + int save_did_emsg; + + ga_init(&ga, 1, 80); + + if (eap->skip) { + ++emsg_skip; + } + while (*arg != NUL && *arg != '|' && *arg != '\n') { + ret = eval1_emsg(&arg, &rettv, !eap->skip); + if (ret == FAIL) { + break; + } + + if (!eap->skip) { + const char *const argstr = eap->cmdidx == CMD_execute + ? tv_get_string(&rettv) + : rettv.v_type == VAR_STRING + ? encode_tv2echo(&rettv, NULL) + : encode_tv2string(&rettv, NULL); + const size_t len = strlen(argstr); + ga_grow(&ga, (int)len + 2); + if (!GA_EMPTY(&ga)) { + ((char_u *)(ga.ga_data))[ga.ga_len++] = ' '; + } + memcpy((char_u *)(ga.ga_data) + ga.ga_len, argstr, len + 1); + if (eap->cmdidx != CMD_execute) { + xfree((void *)argstr); + } + ga.ga_len += (int)len; + } + + tv_clear(&rettv); + arg = skipwhite(arg); + } + + if (ret != FAIL && ga.ga_data != NULL) { + if (eap->cmdidx == CMD_echomsg || eap->cmdidx == CMD_echoerr) { + // Mark the already saved text as finishing the line, so that what + // follows is displayed on a new line when scrolling back at the + // more prompt. + msg_sb_eol(); + } + + if (eap->cmdidx == CMD_echomsg) { + msg_ext_set_kind("echomsg"); + msg_attr(ga.ga_data, echo_attr); + ui_flush(); + } else if (eap->cmdidx == CMD_echoerr) { + // We don't want to abort following commands, restore did_emsg. + save_did_emsg = did_emsg; + msg_ext_set_kind("echoerr"); + emsg(ga.ga_data); + if (!force_abort) { + did_emsg = save_did_emsg; + } + } else if (eap->cmdidx == CMD_execute) { + do_cmdline(ga.ga_data, eap->getline, eap->cookie, DOCMD_NOWAIT|DOCMD_VERBOSE); + } + } + + ga_clear(&ga); + + if (eap->skip) { + --emsg_skip; + } + + eap->nextcmd = (char *)check_nextcmd((char_u *)arg); +} + +/// Skip over the name of an option: "&option", "&g:option" or "&l:option". +/// +/// @param arg points to the "&" or '+' when called, to "option" when returning. +/// +/// @return NULL when no option name found. Otherwise pointer to the char +/// after the option name. +const char *find_option_end(const char **const arg, int *const opt_flags) +{ + const char *p = *arg; + + ++p; + if (*p == 'g' && p[1] == ':') { + *opt_flags = OPT_GLOBAL; + p += 2; + } else if (*p == 'l' && p[1] == ':') { + *opt_flags = OPT_LOCAL; + p += 2; + } else { + *opt_flags = 0; + } + + if (!ASCII_ISALPHA(*p)) { + return NULL; + } + *arg = p; + + if (p[0] == 't' && p[1] == '_' && p[2] != NUL && p[3] != NUL) { + p += 4; // t_xx/termcap option + } else { + while (ASCII_ISALPHA(*p)) { + p++; + } + } + return p; +} + +/// Start profiling function "fp". +void func_do_profile(ufunc_T *fp) +{ + int len = fp->uf_lines.ga_len; + + if (!fp->uf_prof_initialized) { + if (len == 0) { + len = 1; // avoid getting error for allocating zero bytes + } + fp->uf_tm_count = 0; + fp->uf_tm_self = profile_zero(); + fp->uf_tm_total = profile_zero(); + + if (fp->uf_tml_count == NULL) { + fp->uf_tml_count = xcalloc((size_t)len, sizeof(int)); + } + + if (fp->uf_tml_total == NULL) { + fp->uf_tml_total = xcalloc((size_t)len, sizeof(proftime_T)); + } + + if (fp->uf_tml_self == NULL) { + fp->uf_tml_self = xcalloc((size_t)len, sizeof(proftime_T)); + } + + fp->uf_tml_idx = -1; + fp->uf_prof_initialized = true; + } + + fp->uf_profiling = TRUE; +} + +/// Dump the profiling results for all functions in file "fd". +void func_dump_profile(FILE *fd) +{ + hashitem_T *hi; + int todo; + ufunc_T *fp; + ufunc_T **sorttab; + int st_len = 0; + + todo = (int)func_hashtab.ht_used; + if (todo == 0) { + return; // nothing to dump + } + + sorttab = xmalloc(sizeof(ufunc_T *) * (size_t)todo); + + for (hi = func_hashtab.ht_array; todo > 0; ++hi) { + if (!HASHITEM_EMPTY(hi)) { + --todo; + fp = HI2UF(hi); + if (fp->uf_prof_initialized) { + sorttab[st_len++] = fp; + + if (fp->uf_name[0] == K_SPECIAL) { + fprintf(fd, "FUNCTION <SNR>%s()\n", fp->uf_name + 3); + } else { + fprintf(fd, "FUNCTION %s()\n", fp->uf_name); + } + if (fp->uf_script_ctx.sc_sid != 0) { + bool should_free; + const LastSet last_set = (LastSet){ + .script_ctx = fp->uf_script_ctx, + .channel_id = 0, + }; + char *p = (char *)get_scriptname(last_set, &should_free); + fprintf(fd, " Defined: %s:%" PRIdLINENR "\n", + p, fp->uf_script_ctx.sc_lnum); + if (should_free) { + xfree(p); + } + } + if (fp->uf_tm_count == 1) { + fprintf(fd, "Called 1 time\n"); + } else { + fprintf(fd, "Called %d times\n", fp->uf_tm_count); + } + fprintf(fd, "Total time: %s\n", profile_msg(fp->uf_tm_total)); + fprintf(fd, " Self time: %s\n", profile_msg(fp->uf_tm_self)); + fprintf(fd, "\n"); + fprintf(fd, "count total (s) self (s)\n"); + + for (int i = 0; i < fp->uf_lines.ga_len; ++i) { + if (FUNCLINE(fp, i) == NULL) { + continue; + } + prof_func_line(fd, fp->uf_tml_count[i], + &fp->uf_tml_total[i], &fp->uf_tml_self[i], TRUE); + fprintf(fd, "%s\n", FUNCLINE(fp, i)); + } + fprintf(fd, "\n"); + } + } + } + + if (st_len > 0) { + qsort((void *)sorttab, (size_t)st_len, sizeof(ufunc_T *), + prof_total_cmp); + prof_sort_list(fd, sorttab, st_len, "TOTAL", FALSE); + qsort((void *)sorttab, (size_t)st_len, sizeof(ufunc_T *), + prof_self_cmp); + prof_sort_list(fd, sorttab, st_len, "SELF", TRUE); + } + + xfree(sorttab); +} + +/// @param prefer_self when equal print only self time +static void prof_sort_list(FILE *fd, ufunc_T **sorttab, int st_len, char *title, int prefer_self) +{ + int i; + ufunc_T *fp; + + fprintf(fd, "FUNCTIONS SORTED ON %s TIME\n", title); + fprintf(fd, "count total (s) self (s) function\n"); + for (i = 0; i < 20 && i < st_len; ++i) { + fp = sorttab[i]; + prof_func_line(fd, fp->uf_tm_count, &fp->uf_tm_total, &fp->uf_tm_self, + prefer_self); + if (fp->uf_name[0] == K_SPECIAL) { + fprintf(fd, " <SNR>%s()\n", fp->uf_name + 3); + } else { + fprintf(fd, " %s()\n", fp->uf_name); + } + } + fprintf(fd, "\n"); +} + +/// Print the count and times for one function or function line. +/// +/// @param prefer_self when equal print only self time +static void prof_func_line(FILE *fd, int count, proftime_T *total, proftime_T *self, + int prefer_self) +{ + if (count > 0) { + fprintf(fd, "%5d ", count); + if (prefer_self && profile_equal(*total, *self)) { + fprintf(fd, " "); + } else { + fprintf(fd, "%s ", profile_msg(*total)); + } + if (!prefer_self && profile_equal(*total, *self)) { + fprintf(fd, " "); + } else { + fprintf(fd, "%s ", profile_msg(*self)); + } + } else { + fprintf(fd, " "); + } +} + +/// Compare function for total time sorting. +static int prof_total_cmp(const void *s1, const void *s2) +{ + ufunc_T *p1 = *(ufunc_T **)s1; + ufunc_T *p2 = *(ufunc_T **)s2; + return profile_cmp(p1->uf_tm_total, p2->uf_tm_total); +} + +/// Compare function for self time sorting. +static int prof_self_cmp(const void *s1, const void *s2) +{ + ufunc_T *p1 = *(ufunc_T **)s1; + ufunc_T *p2 = *(ufunc_T **)s2; + return profile_cmp(p1->uf_tm_self, p2->uf_tm_self); +} + +/// Return the autoload script name for a function or variable name +/// Caller must make sure that "name" contains AUTOLOAD_CHAR. +/// +/// @param[in] name Variable/function name. +/// @param[in] name_len Name length. +/// +/// @return [allocated] autoload script name. +char *autoload_name(const char *const name, const size_t name_len) + FUNC_ATTR_MALLOC FUNC_ATTR_WARN_UNUSED_RESULT +{ + // Get the script file name: replace '#' with '/', append ".vim". + char *const scriptname = xmalloc(name_len + sizeof("autoload/.vim")); + memcpy(scriptname, "autoload/", sizeof("autoload/") - 1); + memcpy(scriptname + sizeof("autoload/") - 1, name, name_len); + size_t auchar_idx = 0; + for (size_t i = sizeof("autoload/") - 1; + i - sizeof("autoload/") + 1 < name_len; + i++) { + if (scriptname[i] == AUTOLOAD_CHAR) { + scriptname[i] = '/'; + auchar_idx = i; + } + } + memcpy(scriptname + auchar_idx, ".vim", sizeof(".vim")); + + return scriptname; +} + +/// If name has a package name try autoloading the script for it +/// +/// @param[in] name Variable/function name. +/// @param[in] name_len Name length. +/// @param[in] reload If true, load script again when already loaded. +/// +/// @return true if a package was loaded. +bool script_autoload(const char *const name, const size_t name_len, const bool reload) +{ + // If there is no '#' after name[0] there is no package name. + const char *p = memchr(name, AUTOLOAD_CHAR, name_len); + if (p == NULL || p == name) { + return false; + } + + bool ret = false; + char *tofree = autoload_name(name, name_len); + char *scriptname = tofree; + + // Find the name in the list of previously loaded package names. Skip + // "autoload/", it's always the same. + int i = 0; + for (; i < ga_loaded.ga_len; i++) { + if (STRCMP(((char **)ga_loaded.ga_data)[i] + 9, scriptname + 9) == 0) { + break; + } + } + if (!reload && i < ga_loaded.ga_len) { + ret = false; // Was loaded already. + } else { + // Remember the name if it wasn't loaded already. + if (i == ga_loaded.ga_len) { + GA_APPEND(char *, &ga_loaded, scriptname); + tofree = NULL; + } + + // Try loading the package from $VIMRUNTIME/autoload/<name>.vim + if (source_runtime(scriptname, 0) == OK) { + ret = true; + } + } + + xfree(tofree); + return ret; +} + +/// Called when starting to read a function line. +/// "sourcing_lnum" must be correct! +/// When skipping lines it may not actually be executed, but we won't find out +/// until later and we need to store the time now. +void func_line_start(void *cookie) +{ + funccall_T *fcp = (funccall_T *)cookie; + ufunc_T *fp = fcp->func; + + if (fp->uf_profiling && sourcing_lnum >= 1 + && sourcing_lnum <= fp->uf_lines.ga_len) { + fp->uf_tml_idx = sourcing_lnum - 1; + // Skip continuation lines. + while (fp->uf_tml_idx > 0 && FUNCLINE(fp, fp->uf_tml_idx) == NULL) { + fp->uf_tml_idx--; + } + fp->uf_tml_execed = false; + fp->uf_tml_start = profile_start(); + fp->uf_tml_children = profile_zero(); + fp->uf_tml_wait = profile_get_wait(); + } +} + +/// Called when actually executing a function line. +void func_line_exec(void *cookie) +{ + funccall_T *fcp = (funccall_T *)cookie; + ufunc_T *fp = fcp->func; + + if (fp->uf_profiling && fp->uf_tml_idx >= 0) { + fp->uf_tml_execed = TRUE; + } +} + +/// Called when done with a function line. +void func_line_end(void *cookie) +{ + funccall_T *fcp = (funccall_T *)cookie; + ufunc_T *fp = fcp->func; + + if (fp->uf_profiling && fp->uf_tml_idx >= 0) { + if (fp->uf_tml_execed) { + ++fp->uf_tml_count[fp->uf_tml_idx]; + fp->uf_tml_start = profile_end(fp->uf_tml_start); + fp->uf_tml_start = profile_sub_wait(fp->uf_tml_wait, fp->uf_tml_start); + fp->uf_tml_total[fp->uf_tml_idx] = + profile_add(fp->uf_tml_total[fp->uf_tml_idx], fp->uf_tml_start); + fp->uf_tml_self[fp->uf_tml_idx] = + profile_self(fp->uf_tml_self[fp->uf_tml_idx], fp->uf_tml_start, + fp->uf_tml_children); + } + fp->uf_tml_idx = -1; + } +} + +static var_flavour_T var_flavour(char *varname) + FUNC_ATTR_PURE +{ + char *p = varname; + + if (ASCII_ISUPPER(*p)) { + while (*(++p)) { + if (ASCII_ISLOWER(*p)) { + return VAR_FLAVOUR_SESSION; + } + } + return VAR_FLAVOUR_SHADA; + } else { + return VAR_FLAVOUR_DEFAULT; + } +} + +/// Iterate over global variables +/// +/// @warning No modifications to global variable dictionary must be performed +/// while iteration is in progress. +/// +/// @param[in] iter Iterator. Pass NULL to start iteration. +/// @param[out] name Variable name. +/// @param[out] rettv Variable value. +/// +/// @return Pointer that needs to be passed to next `var_shada_iter` invocation +/// or NULL to indicate that iteration is over. +const void *var_shada_iter(const void *const iter, const char **const name, typval_T *rettv, + var_flavour_T flavour) + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ARG(2, 3) +{ + const hashitem_T *hi; + const hashitem_T *hifirst = globvarht.ht_array; + const size_t hinum = (size_t)globvarht.ht_mask + 1; + *name = NULL; + if (iter == NULL) { + hi = globvarht.ht_array; + while ((size_t)(hi - hifirst) < hinum + && (HASHITEM_EMPTY(hi) + || !(var_flavour((char *)hi->hi_key) & flavour))) { + hi++; + } + if ((size_t)(hi - hifirst) == hinum) { + return NULL; + } + } else { + hi = (const hashitem_T *)iter; + } + *name = (char *)TV_DICT_HI2DI(hi)->di_key; + tv_copy(&TV_DICT_HI2DI(hi)->di_tv, rettv); + while ((size_t)(++hi - hifirst) < hinum) { + if (!HASHITEM_EMPTY(hi) && (var_flavour((char *)hi->hi_key) & flavour)) { + return hi; + } + } + return NULL; +} + +void var_set_global(const char *const name, typval_T vartv) +{ + funccal_entry_T funccall_entry; + + save_funccal(&funccall_entry); + set_var(name, strlen(name), &vartv, false); + restore_funccal(); +} + +int store_session_globals(FILE *fd) +{ + TV_DICT_ITER(&globvardict, this_var, { + if ((this_var->di_tv.v_type == VAR_NUMBER + || this_var->di_tv.v_type == VAR_STRING) + && var_flavour((char *)this_var->di_key) == VAR_FLAVOUR_SESSION) { + // Escape special characters with a backslash. Turn a LF and + // CR into \n and \r. + char *const p = (char *)vim_strsave_escaped((const char_u *)tv_get_string(&this_var->di_tv), + (const char_u *)"\\\"\n\r"); + for (char *t = p; *t != NUL; t++) { + if (*t == '\n') { + *t = 'n'; + } else if (*t == '\r') { + *t = 'r'; + } + } + if ((fprintf(fd, "let %s = %c%s%c", + this_var->di_key, + ((this_var->di_tv.v_type == VAR_STRING) ? '"' + : ' '), + p, + ((this_var->di_tv.v_type == VAR_STRING) ? '"' + : ' ')) < 0) + || put_eol(fd) == FAIL) { + xfree(p); + return FAIL; + } + xfree(p); + } else if (this_var->di_tv.v_type == VAR_FLOAT + && var_flavour((char *)this_var->di_key) == VAR_FLAVOUR_SESSION) { + float_T f = this_var->di_tv.vval.v_float; + int sign = ' '; + + if (f < 0) { + f = -f; + sign = '-'; + } + if ((fprintf(fd, "let %s = %c%f", this_var->di_key, sign, f) < 0) + || put_eol(fd) == FAIL) { + return FAIL; + } + } + }); + return OK; +} + +/// Display script name where an item was last set. +/// Should only be invoked when 'verbose' is non-zero. +void last_set_msg(sctx_T script_ctx) +{ + const LastSet last_set = (LastSet){ + .script_ctx = script_ctx, + .channel_id = 0, + }; + option_last_set_msg(last_set); +} + +/// Displays where an option was last set. +/// +/// Should only be invoked when 'verbose' is non-zero. +void option_last_set_msg(LastSet last_set) +{ + if (last_set.script_ctx.sc_sid != 0) { + bool should_free; + char *p = (char *)get_scriptname(last_set, &should_free); + verbose_enter(); + msg_puts(_("\n\tLast set from ")); + msg_puts(p); + if (last_set.script_ctx.sc_lnum > 0) { + msg_puts(_(line_msg)); + msg_outnum((long)last_set.script_ctx.sc_lnum); + } + if (should_free) { + xfree(p); + } + verbose_leave(); + } +} + +// reset v:option_new, v:option_old, v:option_oldlocal, v:option_oldglobal, +// v:option_type, and v:option_command. +void reset_v_option_vars(void) +{ + set_vim_var_string(VV_OPTION_NEW, NULL, -1); + set_vim_var_string(VV_OPTION_OLD, NULL, -1); + set_vim_var_string(VV_OPTION_OLDLOCAL, NULL, -1); + set_vim_var_string(VV_OPTION_OLDGLOBAL, NULL, -1); + set_vim_var_string(VV_OPTION_COMMAND, NULL, -1); + set_vim_var_string(VV_OPTION_TYPE, NULL, -1); +} + +/// Adjust a filename, according to a string of modifiers. +/// *fnamep must be NUL terminated when called. When returning, the length is +/// determined by *fnamelen. +/// Returns VALID_ flags or -1 for failure. +/// When there is an error, *fnamep is set to NULL. +/// +/// @param src string with modifiers +/// @param tilde_file "~" is a file name, not $HOME +/// @param usedlen characters after src that are used +/// @param fnamep file name so far +/// @param bufp buffer for allocated file name or NULL +/// @param fnamelen length of fnamep +int modify_fname(char *src, bool tilde_file, size_t *usedlen, char **fnamep, char **bufp, + size_t *fnamelen) +{ + int valid = 0; + char *tail; + char *s, *p, *pbuf; + char dirname[MAXPATHL]; + int c; + bool has_fullname = false; + bool has_homerelative = false; + +repeat: + // ":p" - full path/file_name + if (src[*usedlen] == ':' && src[*usedlen + 1] == 'p') { + has_fullname = true; + + valid |= VALID_PATH; + *usedlen += 2; + + // Expand "~/path" for all systems and "~user/path" for Unix + if ((*fnamep)[0] == '~' +#if !defined(UNIX) + && ((*fnamep)[1] == '/' +# ifdef BACKSLASH_IN_FILENAME + || (*fnamep)[1] == '\\' +# endif + || (*fnamep)[1] == NUL) +#endif + && !(tilde_file && (*fnamep)[1] == NUL)) { + *fnamep = expand_env_save(*fnamep); + xfree(*bufp); // free any allocated file name + *bufp = *fnamep; + if (*fnamep == NULL) { + return -1; + } + } + + // When "/." or "/.." is used: force expansion to get rid of it. + for (p = *fnamep; *p != NUL; MB_PTR_ADV(p)) { + if (vim_ispathsep(*p) + && p[1] == '.' + && (p[2] == NUL + || vim_ispathsep(p[2]) + || (p[2] == '.' + && (p[3] == NUL || vim_ispathsep(p[3]))))) { + break; + } + } + + // FullName_save() is slow, don't use it when not needed. + if (*p != NUL || !vim_isAbsName((char_u *)(*fnamep))) { + *fnamep = FullName_save((*fnamep), *p != NUL); + xfree(*bufp); // free any allocated file name + *bufp = *fnamep; + if (*fnamep == NULL) { + return -1; + } + } + + // Append a path separator to a directory. + if (os_isdir((char_u *)(*fnamep))) { + // Make room for one or two extra characters. + *fnamep = xstrnsave(*fnamep, STRLEN(*fnamep) + 2); + xfree(*bufp); // free any allocated file name + *bufp = *fnamep; + add_pathsep(*fnamep); + } + } + + // ":." - path relative to the current directory + // ":~" - path relative to the home directory + // ":8" - shortname path - postponed till after + while (src[*usedlen] == ':' + && ((c = (char_u)src[*usedlen + 1]) == '.' || c == '~' || c == '8')) { + *usedlen += 2; + if (c == '8') { + continue; + } + pbuf = NULL; + // Need full path first (use expand_env() to remove a "~/") + if (!has_fullname && !has_homerelative) { + if (**fnamep == '~') { + p = pbuf = expand_env_save(*fnamep); + } else { + p = pbuf = FullName_save(*fnamep, false); + } + } else { + p = *fnamep; + } + + has_fullname = false; + + if (p != NULL) { + if (c == '.') { + os_dirname((char_u *)dirname, MAXPATHL); + if (has_homerelative) { + s = xstrdup(dirname); + home_replace(NULL, s, dirname, MAXPATHL, true); + xfree(s); + } + size_t namelen = STRLEN(dirname); + + // Do not call shorten_fname() here since it removes the prefix + // even though the path does not have a prefix. + if (FNAMENCMP(p, dirname, namelen) == 0) { + p += namelen; + if (vim_ispathsep(*p)) { + while (*p && vim_ispathsep(*p)) { + p++; + } + *fnamep = p; + if (pbuf != NULL) { + // free any allocated file name + xfree(*bufp); + *bufp = pbuf; + pbuf = NULL; + } + } + } + } else { + home_replace(NULL, p, dirname, MAXPATHL, true); + // Only replace it when it starts with '~' + if (*dirname == '~') { + s = xstrdup(dirname); + *fnamep = s; + xfree(*bufp); + *bufp = s; + has_homerelative = true; + } + } + xfree(pbuf); + } + } + + tail = path_tail(*fnamep); + *fnamelen = STRLEN(*fnamep); + + // ":h" - head, remove "/file_name", can be repeated + // Don't remove the first "/" or "c:\" + while (src[*usedlen] == ':' && src[*usedlen + 1] == 'h') { + valid |= VALID_HEAD; + *usedlen += 2; + s = (char *)get_past_head((char_u *)(*fnamep)); + while (tail > s && after_pathsep(s, tail)) { + MB_PTR_BACK(*fnamep, tail); + } + *fnamelen = (size_t)(tail - *fnamep); + if (*fnamelen == 0) { + // Result is empty. Turn it into "." to make ":cd %:h" work. + xfree(*bufp); + *bufp = *fnamep = tail = xstrdup("."); + *fnamelen = 1; + } else { + while (tail > s && !after_pathsep(s, tail)) { + MB_PTR_BACK(*fnamep, tail); + } + } + } + + // ":8" - shortname + if (src[*usedlen] == ':' && src[*usedlen + 1] == '8') { + *usedlen += 2; + } + + // ":t" - tail, just the basename + if (src[*usedlen] == ':' && src[*usedlen + 1] == 't') { + *usedlen += 2; + *fnamelen -= (size_t)(tail - *fnamep); + *fnamep = tail; + } + + // ":e" - extension, can be repeated + // ":r" - root, without extension, can be repeated + while (src[*usedlen] == ':' + && (src[*usedlen + 1] == 'e' || src[*usedlen + 1] == 'r')) { + /* find a '.' in the tail: + * - for second :e: before the current fname + * - otherwise: The last '.' + */ + const bool is_second_e = *fnamep > tail; + if (src[*usedlen + 1] == 'e' && is_second_e) { + s = (*fnamep) - 2; + } else { + s = (*fnamep) + *fnamelen - 1; + } + + for (; s > tail; s--) { + if (s[0] == '.') { + break; + } + } + if (src[*usedlen + 1] == 'e') { + if (s > tail || (0 && is_second_e && s == tail)) { + // we stopped at a '.' (so anchor to &'.' + 1) + char *newstart = s + 1; + size_t distance_stepped_back = (size_t)(*fnamep - newstart); + *fnamelen += distance_stepped_back; + *fnamep = newstart; + } else if (*fnamep <= tail) { + *fnamelen = 0; + } + } else { + // :r - Remove one extension + // + // Ensure that `s` doesn't go before `*fnamep`, + // since then we're taking too many roots: + // + // "path/to/this.file.ext" :e:e:r:r + // ^ ^-------- *fnamep + // +------------- tail + // + // Also ensure `s` doesn't go before `tail`, + // since then we're taking too many roots again: + // + // "path/to/this.file.ext" :r:r:r + // ^ ^------------- tail + // +--------------------- *fnamep + if (s > MAX(tail, (char *)(*fnamep))) { + *fnamelen = (size_t)(s - *fnamep); + } + } + *usedlen += 2; + } + + // ":s?pat?foo?" - substitute + // ":gs?pat?foo?" - global substitute + if (src[*usedlen] == ':' + && (src[*usedlen + 1] == 's' + || (src[*usedlen + 1] == 'g' && src[*usedlen + 2] == 's'))) { + int sep; + char *flags; + int didit = false; + + flags = ""; + s = src + *usedlen + 2; + if (src[*usedlen + 1] == 'g') { + flags = "g"; + s++; + } + + sep = (char_u)(*s++); + if (sep) { + // find end of pattern + p = vim_strchr(s, sep); + if (p != NULL) { + char *const pat = xstrnsave(s, (size_t)(p - s)); + s = p + 1; + // find end of substitution + p = vim_strchr(s, sep); + if (p != NULL) { + char *const sub = xstrnsave(s, (size_t)(p - s)); + char *const str = xstrnsave(*fnamep, *fnamelen); + *usedlen = (size_t)(p + 1 - src); + s = do_string_sub(str, pat, sub, NULL, flags); + *fnamep = s; + *fnamelen = STRLEN(s); + xfree(*bufp); + *bufp = s; + didit = TRUE; + xfree(sub); + xfree(str); + } + xfree(pat); + } + // after using ":s", repeat all the modifiers + if (didit) { + goto repeat; + } + } + } + + if (src[*usedlen] == ':' && src[*usedlen + 1] == 'S') { + // vim_strsave_shellescape() needs a NUL terminated string. + c = (char_u)(*fnamep)[*fnamelen]; + if (c != NUL) { + (*fnamep)[*fnamelen] = NUL; + } + p = (char *)vim_strsave_shellescape((char_u *)(*fnamep), false, false); + if (c != NUL) { + (*fnamep)[*fnamelen] = (char)c; + } + xfree(*bufp); + *bufp = *fnamep = p; + *fnamelen = STRLEN(p); + *usedlen += 2; + } + + return valid; +} + +/// Perform a substitution on "str" with pattern "pat" and substitute "sub". +/// When "sub" is NULL "expr" is used, must be a VAR_FUNC or VAR_PARTIAL. +/// "flags" can be "g" to do a global substitute. +/// +/// @return an allocated string, NULL for error. +char *do_string_sub(char *str, char *pat, char *sub, typval_T *expr, char *flags) +{ + int sublen; + regmatch_T regmatch; + int do_all; + char *tail; + char *end; + garray_T ga; + char *save_cpo; + char *zero_width = NULL; + + // Make 'cpoptions' empty, so that the 'l' flag doesn't work here + save_cpo = p_cpo; + p_cpo = (char *)empty_option; + + ga_init(&ga, 1, 200); + + do_all = (flags[0] == 'g'); + + regmatch.rm_ic = p_ic; + regmatch.regprog = vim_regcomp(pat, RE_MAGIC + RE_STRING); + if (regmatch.regprog != NULL) { + tail = str; + end = str + STRLEN(str); + while (vim_regexec_nl(®match, (char_u *)str, (colnr_T)(tail - str))) { + // Skip empty match except for first match. + if (regmatch.startp[0] == regmatch.endp[0]) { + if ((char_u *)zero_width == regmatch.startp[0]) { + // avoid getting stuck on a match with an empty string + int i = utfc_ptr2len(tail); + memmove((char_u *)ga.ga_data + ga.ga_len, tail, (size_t)i); + ga.ga_len += i; + tail += i; + continue; + } + zero_width = (char *)regmatch.startp[0]; + } + + // Get some space for a temporary buffer to do the substitution + // into. It will contain: + // - The text up to where the match is. + // - The substituted text. + // - The text after the match. + sublen = vim_regsub(®match, (char_u *)sub, expr, (char_u *)tail, 0, REGSUB_MAGIC); + ga_grow(&ga, (int)((end - tail) + sublen - + (regmatch.endp[0] - regmatch.startp[0]))); + + // copy the text up to where the match is + int i = (int)(regmatch.startp[0] - (char_u *)tail); + memmove((char_u *)ga.ga_data + ga.ga_len, tail, (size_t)i); + // add the substituted text + (void)vim_regsub(®match, (char_u *)sub, expr, + (char_u *)ga.ga_data + ga.ga_len + i, sublen, + REGSUB_COPY | REGSUB_MAGIC); + ga.ga_len += i + sublen - 1; + tail = (char *)regmatch.endp[0]; + if (*tail == NUL) { + break; + } + if (!do_all) { + break; + } + } + + if (ga.ga_data != NULL) { + STRCPY((char *)ga.ga_data + ga.ga_len, tail); + } + + vim_regfree(regmatch.regprog); + } + + char *ret = xstrdup(ga.ga_data == NULL ? str : ga.ga_data); + ga_clear(&ga); + if ((char_u *)p_cpo == empty_option) { + p_cpo = save_cpo; + } else { + // Darn, evaluating {sub} expression or {expr} changed the value. + free_string_option((char_u *)save_cpo); + } + + return ret; +} + +/// common code for getting job callbacks for jobstart, termopen and rpcstart +/// +/// @return true/false on success/failure. +bool common_job_callbacks(dict_T *vopts, CallbackReader *on_stdout, CallbackReader *on_stderr, + Callback *on_exit) +{ + if (tv_dict_get_callback(vopts, S_LEN("on_stdout"), &on_stdout->cb) + && tv_dict_get_callback(vopts, S_LEN("on_stderr"), &on_stderr->cb) + && tv_dict_get_callback(vopts, S_LEN("on_exit"), on_exit)) { + on_stdout->buffered = tv_dict_get_number(vopts, "stdout_buffered"); + on_stderr->buffered = tv_dict_get_number(vopts, "stderr_buffered"); + if (on_stdout->buffered && on_stdout->cb.type == kCallbackNone) { + on_stdout->self = vopts; + } + if (on_stderr->buffered && on_stderr->cb.type == kCallbackNone) { + on_stderr->self = vopts; + } + vopts->dv_refcount++; + return true; + } + + callback_reader_free(on_stdout); + callback_reader_free(on_stderr); + callback_free(on_exit); + return false; +} + +Channel *find_job(uint64_t id, bool show_error) +{ + Channel *data = find_channel(id); + if (!data || data->streamtype != kChannelStreamProc + || process_is_stopped(&data->stream.proc)) { + if (show_error) { + if (data && data->streamtype != kChannelStreamProc) { + emsg(_(e_invchanjob)); + } else { + emsg(_(e_invchan)); + } + } + return NULL; + } + return data; +} + +void script_host_eval(char *name, typval_T *argvars, typval_T *rettv) +{ + if (check_secure()) { + return; + } + + if (argvars[0].v_type != VAR_STRING) { + emsg(_(e_invarg)); + return; + } + + list_T *args = tv_list_alloc(1); + tv_list_append_string(args, (const char *)argvars[0].vval.v_string, -1); + *rettv = eval_call_provider(name, "eval", args, false); +} + +/// @param discard Clears the value returned by the provider and returns +/// an empty typval_T. +typval_T eval_call_provider(char *provider, char *method, list_T *arguments, bool discard) +{ + if (!eval_has_provider(provider)) { + semsg("E319: No \"%s\" provider found. Run \":checkhealth provider\"", + provider); + return (typval_T){ + .v_type = VAR_NUMBER, + .v_lock = VAR_UNLOCKED, + .vval.v_number = (varnumber_T)0 + }; + } + + char func[256]; + int name_len = snprintf(func, sizeof(func), "provider#%s#Call", provider); + + // Save caller scope information + struct caller_scope saved_provider_caller_scope = provider_caller_scope; + provider_caller_scope = (struct caller_scope) { + .script_ctx = current_sctx, + .sourcing_name = sourcing_name, + .sourcing_lnum = sourcing_lnum, + .autocmd_fname = autocmd_fname, + .autocmd_match = autocmd_match, + .autocmd_bufnr = autocmd_bufnr, + .funccalp = (void *)get_current_funccal() + }; + funccal_entry_T funccal_entry; + save_funccal(&funccal_entry); + provider_call_nesting++; + + typval_T argvars[3] = { + { .v_type = VAR_STRING, .vval.v_string = method, + .v_lock = VAR_UNLOCKED }, + { .v_type = VAR_LIST, .vval.v_list = arguments, .v_lock = VAR_UNLOCKED }, + { .v_type = VAR_UNKNOWN } + }; + typval_T rettv = { .v_type = VAR_UNKNOWN, .v_lock = VAR_UNLOCKED }; + tv_list_ref(arguments); + + funcexe_T funcexe = FUNCEXE_INIT; + funcexe.firstline = curwin->w_cursor.lnum; + funcexe.lastline = curwin->w_cursor.lnum; + funcexe.evaluate = true; + (void)call_func(func, name_len, &rettv, 2, argvars, &funcexe); + + tv_list_unref(arguments); + // Restore caller scope information + restore_funccal(); + provider_caller_scope = saved_provider_caller_scope; + provider_call_nesting--; + assert(provider_call_nesting >= 0); + + if (discard) { + tv_clear(&rettv); + } + + return rettv; +} + +/// Checks if provider for feature `feat` is enabled. +bool eval_has_provider(const char *feat) +{ + if (!strequal(feat, "clipboard") + && !strequal(feat, "python3") + && !strequal(feat, "python3_compiled") + && !strequal(feat, "python3_dynamic") + && !strequal(feat, "perl") + && !strequal(feat, "ruby") + && !strequal(feat, "node")) { + // Avoid autoload for non-provider has() features. + return false; + } + + char name[32]; // Normalized: "python_compiled" => "python". + snprintf(name, sizeof(name), "%s", feat); + strchrsub(name, '_', '\0'); // Chop any "_xx" suffix. + + char buf[256]; + typval_T tv; + // Get the g:loaded_xx_provider variable. + int len = snprintf(buf, sizeof(buf), "g:loaded_%s_provider", name); + if (get_var_tv(buf, len, &tv, NULL, false, true) == FAIL) { + // Trigger autoload once. + len = snprintf(buf, sizeof(buf), "provider#%s#bogus", name); + script_autoload(buf, (size_t)len, false); + + // Retry the (non-autoload-style) variable. + len = snprintf(buf, sizeof(buf), "g:loaded_%s_provider", name); + if (get_var_tv(buf, len, &tv, NULL, false, true) == FAIL) { + // Show a hint if Call() is defined but g:loaded_xx_provider is missing. + snprintf(buf, sizeof(buf), "provider#%s#Call", name); + if (!!find_func((char_u *)buf) && p_lpl) { + semsg("provider: %s: missing required variable g:loaded_%s_provider", + name, name); + } + return false; + } + } + + bool ok = (tv.v_type == VAR_NUMBER) + ? 2 == tv.vval.v_number // Value of 2 means "loaded and working". + : false; + + if (ok) { + // Call() must be defined if provider claims to be working. + snprintf(buf, sizeof(buf), "provider#%s#Call", name); + if (!find_func((char_u *)buf)) { + semsg("provider: %s: g:loaded_%s_provider=2 but %s is not defined", + name, name, buf); + ok = false; + } + } + + return ok; +} + +/// Writes "<sourcing_name>:<sourcing_lnum>" to `buf[bufsize]`. +void eval_fmt_source_name_line(char *buf, size_t bufsize) +{ + if (sourcing_name) { + snprintf(buf, bufsize, "%s:%" PRIdLINENR, sourcing_name, sourcing_lnum); + } else { + snprintf(buf, bufsize, "?"); + } +} + +/// ":checkhealth [plugins]" +void ex_checkhealth(exarg_T *eap) +{ + bool found = !!find_func((char_u *)"health#check"); + if (!found + && script_autoload("health#check", sizeof("health#check") - 1, false)) { + found = !!find_func((char_u *)"health#check"); + } + if (!found) { + const char *vimruntime_env = os_getenv("VIMRUNTIME"); + if (vimruntime_env == NULL) { + emsg(_("E5009: $VIMRUNTIME is empty or unset")); + } else { + bool rtp_ok = NULL != strstr((char *)p_rtp, vimruntime_env); + if (rtp_ok) { + semsg(_("E5009: Invalid $VIMRUNTIME: %s"), vimruntime_env); + } else { + emsg(_("E5009: Invalid 'runtimepath'")); + } + } + return; + } + + size_t bufsize = STRLEN(eap->arg) + sizeof("call health#check('')"); + char *buf = xmalloc(bufsize); + snprintf(buf, bufsize, "call health#check('%s')", eap->arg); + + do_cmdline_cmd(buf); + + xfree(buf); +} + +void invoke_prompt_callback(void) +{ + typval_T rettv; + typval_T argv[2]; + char *text; + char *prompt; + linenr_T lnum = curbuf->b_ml.ml_line_count; + + // Add a new line for the prompt before invoking the callback, so that + // text can always be inserted above the last line. + ml_append(lnum, "", 0, false); + curwin->w_cursor.lnum = lnum + 1; + curwin->w_cursor.col = 0; + + if (curbuf->b_prompt_callback.type == kCallbackNone) { + return; + } + text = (char *)ml_get(lnum); + prompt = (char *)prompt_text(); + if (STRLEN(text) >= STRLEN(prompt)) { + text += STRLEN(prompt); + } + argv[0].v_type = VAR_STRING; + argv[0].vval.v_string = xstrdup(text); + argv[1].v_type = VAR_UNKNOWN; + + callback_call(&curbuf->b_prompt_callback, 1, argv, &rettv); + tv_clear(&argv[0]); + tv_clear(&rettv); +} + +/// @return true when the interrupt callback was invoked. +bool invoke_prompt_interrupt(void) +{ + typval_T rettv; + typval_T argv[1]; + + if (curbuf->b_prompt_interrupt.type == kCallbackNone) { + return false; + } + argv[0].v_type = VAR_UNKNOWN; + + got_int = false; // don't skip executing commands + callback_call(&curbuf->b_prompt_interrupt, 0, argv, &rettv); + tv_clear(&rettv); + return true; +} + +/// Compare "typ1" and "typ2". Put the result in "typ1". +/// +/// @param typ1 first operand +/// @param typ2 second operand +/// @param type operator +/// @param ic ignore case +int typval_compare(typval_T *typ1, typval_T *typ2, exprtype_T type, bool ic) + FUNC_ATTR_NONNULL_ALL +{ + varnumber_T n1, n2; + const bool type_is = type == EXPR_IS || type == EXPR_ISNOT; + + if (type_is && typ1->v_type != typ2->v_type) { + // For "is" a different type always means false, for "notis" + // it means true. + n1 = type == EXPR_ISNOT; + } else if (typ1->v_type == VAR_BLOB || typ2->v_type == VAR_BLOB) { + if (type_is) { + n1 = typ1->v_type == typ2->v_type + && typ1->vval.v_blob == typ2->vval.v_blob; + if (type == EXPR_ISNOT) { + n1 = !n1; + } + } else if (typ1->v_type != typ2->v_type + || (type != EXPR_EQUAL && type != EXPR_NEQUAL)) { + if (typ1->v_type != typ2->v_type) { + emsg(_("E977: Can only compare Blob with Blob")); + } else { + emsg(_(e_invalblob)); + } + tv_clear(typ1); + return FAIL; + } else { + // Compare two Blobs for being equal or unequal. + n1 = tv_blob_equal(typ1->vval.v_blob, typ2->vval.v_blob); + if (type == EXPR_NEQUAL) { + n1 = !n1; + } + } + } else if (typ1->v_type == VAR_LIST || typ2->v_type == VAR_LIST) { + if (type_is) { + n1 = typ1->v_type == typ2->v_type + && typ1->vval.v_list == typ2->vval.v_list; + if (type == EXPR_ISNOT) { + n1 = !n1; + } + } else if (typ1->v_type != typ2->v_type + || (type != EXPR_EQUAL && type != EXPR_NEQUAL)) { + if (typ1->v_type != typ2->v_type) { + emsg(_("E691: Can only compare List with List")); + } else { + emsg(_("E692: Invalid operation for List")); + } + tv_clear(typ1); + return FAIL; + } else { + // Compare two Lists for being equal or unequal. + n1 = tv_list_equal(typ1->vval.v_list, typ2->vval.v_list, ic, false); + if (type == EXPR_NEQUAL) { + n1 = !n1; + } + } + } else if (typ1->v_type == VAR_DICT || typ2->v_type == VAR_DICT) { + if (type_is) { + n1 = typ1->v_type == typ2->v_type + && typ1->vval.v_dict == typ2->vval.v_dict; + if (type == EXPR_ISNOT) { + n1 = !n1; + } + } else if (typ1->v_type != typ2->v_type + || (type != EXPR_EQUAL && type != EXPR_NEQUAL)) { + if (typ1->v_type != typ2->v_type) { + emsg(_("E735: Can only compare Dictionary with Dictionary")); + } else { + emsg(_("E736: Invalid operation for Dictionary")); + } + tv_clear(typ1); + return FAIL; + } else { + // Compare two Dictionaries for being equal or unequal. + n1 = tv_dict_equal(typ1->vval.v_dict, typ2->vval.v_dict, ic, false); + if (type == EXPR_NEQUAL) { + n1 = !n1; + } + } + } else if (tv_is_func(*typ1) || tv_is_func(*typ2)) { + if (type != EXPR_EQUAL && type != EXPR_NEQUAL + && type != EXPR_IS && type != EXPR_ISNOT) { + emsg(_("E694: Invalid operation for Funcrefs")); + tv_clear(typ1); + return FAIL; + } + if ((typ1->v_type == VAR_PARTIAL && typ1->vval.v_partial == NULL) + || (typ2->v_type == VAR_PARTIAL && typ2->vval.v_partial == NULL)) { + // when a partial is NULL assume not equal + n1 = false; + } else if (type_is) { + if (typ1->v_type == VAR_FUNC && typ2->v_type == VAR_FUNC) { + // strings are considered the same if their value is + // the same + n1 = tv_equal(typ1, typ2, ic, false); + } else if (typ1->v_type == VAR_PARTIAL && typ2->v_type == VAR_PARTIAL) { + n1 = typ1->vval.v_partial == typ2->vval.v_partial; + } else { + n1 = false; + } + } else { + n1 = tv_equal(typ1, typ2, ic, false); + } + if (type == EXPR_NEQUAL || type == EXPR_ISNOT) { + n1 = !n1; + } + } else if ((typ1->v_type == VAR_FLOAT || typ2->v_type == VAR_FLOAT) + && type != EXPR_MATCH && type != EXPR_NOMATCH) { + // If one of the two variables is a float, compare as a float. + // When using "=~" or "!~", always compare as string. + const float_T f1 = tv_get_float(typ1); + const float_T f2 = tv_get_float(typ2); + n1 = false; + switch (type) { + case EXPR_IS: + case EXPR_EQUAL: + n1 = f1 == f2; break; + case EXPR_ISNOT: + case EXPR_NEQUAL: + n1 = f1 != f2; break; + case EXPR_GREATER: + n1 = f1 > f2; break; + case EXPR_GEQUAL: + n1 = f1 >= f2; break; + case EXPR_SMALLER: + n1 = f1 < f2; break; + case EXPR_SEQUAL: + n1 = f1 <= f2; break; + case EXPR_UNKNOWN: + case EXPR_MATCH: + case EXPR_NOMATCH: + break; // avoid gcc warning + } + } else if ((typ1->v_type == VAR_NUMBER || typ2->v_type == VAR_NUMBER) + && type != EXPR_MATCH && type != EXPR_NOMATCH) { + // If one of the two variables is a number, compare as a number. + // When using "=~" or "!~", always compare as string. + n1 = tv_get_number(typ1); + n2 = tv_get_number(typ2); + switch (type) { + case EXPR_IS: + case EXPR_EQUAL: + n1 = n1 == n2; break; + case EXPR_ISNOT: + case EXPR_NEQUAL: + n1 = n1 != n2; break; + case EXPR_GREATER: + n1 = n1 > n2; break; + case EXPR_GEQUAL: + n1 = n1 >= n2; break; + case EXPR_SMALLER: + n1 = n1 < n2; break; + case EXPR_SEQUAL: + n1 = n1 <= n2; break; + case EXPR_UNKNOWN: + case EXPR_MATCH: + case EXPR_NOMATCH: + break; // avoid gcc warning + } + } else { + char buf1[NUMBUFLEN]; + char buf2[NUMBUFLEN]; + const char *const s1 = tv_get_string_buf(typ1, buf1); + const char *const s2 = tv_get_string_buf(typ2, buf2); + int i; + if (type != EXPR_MATCH && type != EXPR_NOMATCH) { + i = mb_strcmp_ic(ic, s1, s2); + } else { + i = 0; + } + n1 = false; + switch (type) { + case EXPR_IS: + case EXPR_EQUAL: + n1 = i == 0; break; + case EXPR_ISNOT: + case EXPR_NEQUAL: + n1 = i != 0; break; + case EXPR_GREATER: + n1 = i > 0; break; + case EXPR_GEQUAL: + n1 = i >= 0; break; + case EXPR_SMALLER: + n1 = i < 0; break; + case EXPR_SEQUAL: + n1 = i <= 0; break; + + case EXPR_MATCH: + case EXPR_NOMATCH: + n1 = pattern_match((char *)s2, (char *)s1, ic); + if (type == EXPR_NOMATCH) { + n1 = !n1; + } + break; + case EXPR_UNKNOWN: + break; // avoid gcc warning + } + } + tv_clear(typ1); + typ1->v_type = VAR_NUMBER; + typ1->vval.v_number = n1; + return OK; +} + +char *typval_tostring(typval_T *arg) +{ + if (arg == NULL) { + return xstrdup("(does not exist)"); + } + return encode_tv2string(arg, NULL); +} diff --git a/src/nvim/eval.h b/src/nvim/eval.h index fa02b1ea0f..7dbd18737a 100644 --- a/src/nvim/eval.h +++ b/src/nvim/eval.h @@ -3,7 +3,6 @@ #include "nvim/buffer_defs.h" #include "nvim/channel.h" -#include "nvim/eval/funcs.h" // For FunPtr #include "nvim/event/time.h" // For TimeWatcher #include "nvim/ex_cmds_defs.h" // For exarg_T #include "nvim/os/fileio.h" // For FileDescriptor diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index 3db0d27018..c8eb0334fa 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -250,6 +250,7 @@ return { map={args=2, base=1}, maparg={args={1, 4}, base=1}, mapcheck={args={1, 3}, base=1}, + mapset={args=3, base=1}, match={args={2, 4}, base=1}, matchadd={args={2, 5}, base=1}, matchaddpos={args={2, 5}, base=1}, diff --git a/src/nvim/eval/executor.c b/src/nvim/eval/executor.c index 3e66150180..b461456a3a 100644 --- a/src/nvim/eval/executor.c +++ b/src/nvim/eval/executor.c @@ -12,8 +12,6 @@ # include "eval/executor.c.generated.h" #endif -static char *e_letwrong = N_("E734: Wrong variable type for %s="); - char *e_listidx = N_("E684: list index out of range: %" PRId64); /// Handle tv1 += tv2, -=, *=, /=, %=, .= diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 7bed21e99b..691ccfe535 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -25,6 +25,7 @@ #include "nvim/eval/funcs.h" #include "nvim/eval/typval.h" #include "nvim/eval/userfunc.h" +#include "nvim/eval/vars.h" #include "nvim/ex_docmd.h" #include "nvim/ex_getln.h" #include "nvim/file_search.h" @@ -36,6 +37,7 @@ #include "nvim/indent.h" #include "nvim/indent_c.h" #include "nvim/input.h" +#include "nvim/insexpand.h" #include "nvim/lua/executor.h" #include "nvim/macros.h" #include "nvim/mapping.h" @@ -213,12 +215,11 @@ int call_internal_method(const char_u *const fname, const int argcount, typval_T } typval_T argv[MAX_FUNC_ARGS + 1]; - const ptrdiff_t base_index - = fdef->base_arg == BASE_LAST ? argcount : fdef->base_arg - 1; - memcpy(argv, argvars, base_index * sizeof(typval_T)); + const ptrdiff_t base_index = fdef->base_arg == BASE_LAST ? argcount : fdef->base_arg - 1; + memcpy(argv, argvars, (size_t)base_index * sizeof(typval_T)); argv[base_index] = *basetv; memcpy(argv + base_index + 1, argvars + base_index, - (argcount - base_index) * sizeof(typval_T)); + (size_t)(argcount - base_index) * sizeof(typval_T)); argv[argcount + 1].v_type = VAR_UNKNOWN; fdef->func(argv, rettv, fdef->data); @@ -326,7 +327,7 @@ static void f_add(typval_T *argvars, typval_T *rettv, FunPtr fptr) const varnumber_T n = tv_get_number_chk(&argvars[1], &error); if (!error) { - ga_append(&b->bv_ga, (int)n); + ga_append(&b->bv_ga, (char)n); tv_copy(&argvars[0], rettv); } } @@ -430,7 +431,7 @@ static void f_argv(typval_T *argvars, typval_T *rettv, FunPtr fptr) } rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; - int idx = tv_get_number_chk(&argvars[0], NULL); + int idx = (int)tv_get_number_chk(&argvars[0], NULL); if (arglist != NULL && idx >= 0 && idx < argcount) { rettv->vval.v_string = xstrdup((const char *)alist_name(&arglist[idx])); } else if (idx == -1) { @@ -831,7 +832,7 @@ static void f_chanclose(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } const char *error; - rettv->vval.v_number = channel_close(argvars[0].vval.v_number, part, &error); + rettv->vval.v_number = channel_close((uint64_t)argvars[0].vval.v_number, part, &error); if (!rettv->vval.v_number) { emsg(error); } @@ -859,7 +860,7 @@ static void f_chansend(typval_T *argvars, typval_T *rettv, FunPtr fptr) const blob_T *const b = argvars[1].vval.v_blob; input_len = tv_blob_len(b); if (input_len > 0) { - input = xmemdup(b->bv_ga.ga_data, input_len); + input = xmemdup(b->bv_ga.ga_data, (size_t)input_len); } } else { input = save_tv_as_string(&argvars[1], &input_len, false); @@ -870,9 +871,9 @@ static void f_chansend(typval_T *argvars, typval_T *rettv, FunPtr fptr) // or there is no input to send. return; } - uint64_t id = argvars[0].vval.v_number; + uint64_t id = (uint64_t)argvars[0].vval.v_number; const char *error = NULL; - rettv->vval.v_number = channel_send(id, input, input_len, true, &error); + rettv->vval.v_number = (varnumber_T)channel_send(id, input, (size_t)input_len, true, &error); if (error) { emsg(error); } @@ -1054,64 +1055,6 @@ static void f_col(typval_T *argvars, typval_T *rettv, FunPtr fptr) get_col(argvars, rettv, false); } -/// "complete()" function -static void f_complete(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - if ((State & MODE_INSERT) == 0) { - emsg(_("E785: complete() can only be used in Insert mode")); - return; - } - - // Check for undo allowed here, because if something was already inserted - // the line was already saved for undo and this check isn't done. - if (!undo_allowed(curbuf)) { - return; - } - - if (argvars[1].v_type != VAR_LIST) { - emsg(_(e_invarg)); - } else { - const colnr_T startcol = tv_get_number_chk(&argvars[0], NULL); - if (startcol > 0) { - set_completion(startcol - 1, argvars[1].vval.v_list); - } - } -} - -/// "complete_add()" function -static void f_complete_add(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - rettv->vval.v_number = ins_compl_add_tv(&argvars[0], 0, false); -} - -/// "complete_check()" function -static void f_complete_check(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - int saved = RedrawingDisabled; - - RedrawingDisabled = 0; - ins_compl_check_keys(0, true); - rettv->vval.v_number = compl_interrupted; - RedrawingDisabled = saved; -} - -/// "complete_info()" function -static void f_complete_info(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - tv_dict_alloc_ret(rettv); - - list_T *what_list = NULL; - - if (argvars[0].v_type != VAR_UNKNOWN) { - if (argvars[0].v_type != VAR_LIST) { - emsg(_(e_listreq)); - return; - } - what_list = argvars[0].vval.v_list; - } - get_complete_info(what_list, rettv->vval.v_dict); -} - /// "confirm(message, buttons[, default [, type]])" function static void f_confirm(typval_T *argvars, typval_T *rettv, FunPtr fptr) { @@ -1134,7 +1077,7 @@ static void f_confirm(typval_T *argvars, typval_T *rettv, FunPtr fptr) error = true; } if (argvars[2].v_type != VAR_UNKNOWN) { - def = tv_get_number_chk(&argvars[2], &error); + def = (int)tv_get_number_chk(&argvars[2], &error); if (argvars[3].v_type != VAR_UNKNOWN) { typestr = tv_get_string_buf_chk(&argvars[3], buf2); if (typestr == NULL) { @@ -1181,7 +1124,7 @@ static void f_count(typval_T *argvars, typval_T *rettv, FunPtr fptr) bool error = false; if (argvars[2].v_type != VAR_UNKNOWN) { - ic = tv_get_number_chk(&argvars[2], &error); + ic = (int)tv_get_number_chk(&argvars[2], &error); } if (argvars[0].v_type == VAR_STRING) { @@ -1219,7 +1162,7 @@ static void f_count(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (argvars[3].v_type != VAR_UNKNOWN) { idx = tv_get_number_chk(&argvars[3], &error); if (!error) { - li = tv_list_find(l, idx); + li = tv_list_find(l, (int)idx); if (li == NULL) { semsg(_(e_listidx), (int64_t)idx); } @@ -1292,7 +1235,7 @@ static void f_ctxget(typval_T *argvars, typval_T *rettv, FunPtr fptr) { size_t index = 0; if (argvars[0].v_type == VAR_NUMBER) { - index = argvars[0].vval.v_number; + index = (size_t)argvars[0].vval.v_number; } else if (argvars[0].v_type != VAR_UNKNOWN) { semsg(_(e_invarg2), "expected nothing or a Number as an argument"); return; @@ -1360,7 +1303,7 @@ static void f_ctxset(typval_T *argvars, typval_T *rettv, FunPtr fptr) size_t index = 0; if (argvars[1].v_type == VAR_NUMBER) { - index = argvars[1].vval.v_number; + index = (size_t)argvars[1].vval.v_number; } else if (argvars[1].v_type != VAR_UNKNOWN) { semsg(_(e_invarg2), "expected nothing or a Number as second argument"); return; @@ -1394,7 +1337,7 @@ static void f_ctxset(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_ctxsize(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->v_type = VAR_NUMBER; - rettv->vval.v_number = ctx_size(); + rettv->vval.v_number = (varnumber_T)ctx_size(); } /// Set the cursor position. @@ -1428,7 +1371,7 @@ static void set_cursorpos(typval_T *argvars, typval_T *rettv, bool charcol) line = tv_get_lnum(argvars); col = (long)tv_get_number_chk(&argvars[1], NULL); if (charcol) { - col = buf_charidx_to_byteidx(curbuf, line, col) + 1; + col = buf_charidx_to_byteidx(curbuf, (linenr_T)line, (int)col) + 1; } if (argvars[2].v_type != VAR_UNKNOWN) { coladd = (long)tv_get_number_chk(&argvars[2], NULL); @@ -1441,12 +1384,12 @@ static void set_cursorpos(typval_T *argvars, typval_T *rettv, bool charcol) return; // type error; errmsg already given } if (line > 0) { - curwin->w_cursor.lnum = line; + curwin->w_cursor.lnum = (linenr_T)line; } if (col > 0) { - curwin->w_cursor.col = col - 1; + curwin->w_cursor.col = (colnr_T)col - 1; } - curwin->w_cursor.coladd = coladd; + curwin->w_cursor.coladd = (colnr_T)coladd; // Make sure the cursor is in a valid position. check_cursor(); @@ -1498,7 +1441,7 @@ static void f_deepcopy(typval_T *argvars, typval_T *rettv, FunPtr fptr) int noref = 0; if (argvars[1].v_type != VAR_UNKNOWN) { - noref = tv_get_number_chk(&argvars[1], NULL); + noref = (int)tv_get_number_chk(&argvars[1], NULL); } if (noref < 0 || noref > 1) { emsg(_(e_invarg)); @@ -1675,7 +1618,7 @@ static void f_deletebufline(typval_T *argvars, typval_T *rettv, FunPtr fptr) FOR_ALL_TAB_WINDOWS(tp, wp) { if (wp->w_buffer == buf) { if (wp->w_cursor.lnum > last) { - wp->w_cursor.lnum -= count; + wp->w_cursor.lnum -= (linenr_T)count; } else if (wp->w_cursor.lnum > first) { wp->w_cursor.lnum = first; } @@ -1712,7 +1655,7 @@ static void f_diff_hlID(typval_T *argvars, typval_T *rettv, FunPtr fptr) { linenr_T lnum = tv_get_lnum(argvars); static linenr_T prev_lnum = 0; - static int changedtick = 0; + static varnumber_T changedtick = 0; static int fnum = 0; static int change_start = 0; static int change_end = 0; @@ -1749,7 +1692,7 @@ static void f_diff_hlID(typval_T *argvars, typval_T *rettv, FunPtr fptr) } if (hlID == HLF_CHD || hlID == HLF_TXD) { - col = tv_get_number(&argvars[1]) - 1; // Ignore type error in {col}. + col = (int)tv_get_number(&argvars[1]) - 1; // Ignore type error in {col}. if (col >= change_start && col <= change_end) { hlID = HLF_TXD; // Changed text. } else { @@ -1820,7 +1763,7 @@ static void f_environ(typval_T *argvars, typval_T *rettv, FunPtr fptr) os_copy_fullenv(env, env_size); - for (ssize_t i = env_size - 1; i >= 0; i--) { + for (ssize_t i = (ssize_t)env_size - 1; i >= 0; i--) { const char *str = env[i]; const char * const end = strchr(str + (str[0] == '=' ? 1 : 0), '='); @@ -1848,9 +1791,7 @@ static void f_environ(typval_T *argvars, typval_T *rettv, FunPtr fptr) xfree(key); continue; } - tv_dict_add_str(rettv->vval.v_dict, - key, len, - value); + tv_dict_add_str(rettv->vval.v_dict, key, (size_t)len, value); xfree(key); } os_free_fullenv(env); @@ -2029,7 +1970,7 @@ static void f_win_execute(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; - int id = tv_get_number(argvars); + int id = (int)tv_get_number(argvars); tabpage_T *tp; win_T *wp = win_id2wp_tp(id, &tp); if (wp != NULL && tp != NULL) { @@ -2189,11 +2130,11 @@ static void f_expandcmd(typval_T *argvars, typval_T *rettv, FunPtr fptr) char *errormsg = NULL; rettv->v_type = VAR_STRING; - char_u *cmdstr = (char_u *)xstrdup(tv_get_string(&argvars[0])); + char *cmdstr = xstrdup(tv_get_string(&argvars[0])); exarg_T eap = { - .cmd = (char *)cmdstr, - .arg = (char *)cmdstr, + .cmd = cmdstr, + .arg = cmdstr, .usefilter = false, .nextcmd = NULL, .cmdidx = CMD_USER, @@ -2204,7 +2145,7 @@ static void f_expandcmd(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (errormsg != NULL && *errormsg != NUL) { emsg(errormsg); } - rettv->vval.v_string = (char *)cmdstr; + rettv->vval.v_string = cmdstr; } /// "flatten(list[, {maxdepth}])" function @@ -2265,7 +2206,7 @@ static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (before == tv_list_len(l1)) { item = NULL; } else { - item = tv_list_find(l1, before); + item = tv_list_find(l1, (int)before); if (item == NULL) { semsg(_(e_listidx), (int64_t)before); return; @@ -2382,7 +2323,7 @@ static void findfilendir(typval_T *argvars, typval_T *rettv, int find_what) } if (argvars[2].v_type != VAR_UNKNOWN) { - count = tv_get_number_chk(&argvars[2], &error); + count = (int)tv_get_number_chk(&argvars[2], &error); } } } @@ -2577,7 +2518,7 @@ static void f_foldtext(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } } - unsigned long count = (unsigned long)foldend - foldstart + 1; + unsigned long count = (unsigned long)foldend - (unsigned long)foldstart + 1; txt = NGETTEXT("+-%s%3ld line: ", "+-%s%3ld lines: ", count); r = xmalloc(STRLEN(txt) + STRLEN(dashes) // for %s @@ -2613,7 +2554,7 @@ static void f_foldtextresult(typval_T *argvars, typval_T *rettv, FunPtr fptr) foldinfo_T info = fold_info(curwin, lnum); if (info.fi_lines > 0) { - text = get_foldtext(curwin, lnum, lnum + info.fi_lines - 1, info, buf); + text = get_foldtext(curwin, lnum, lnum + (linenr_T)info.fi_lines - 1, info, buf); if (text == buf) { text = vim_strsave(text); } @@ -2661,7 +2602,7 @@ static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (argvars[0].v_type == VAR_BLOB) { bool error = false; - int idx = tv_get_number_chk(&argvars[1], &error); + int idx = (int)tv_get_number_chk(&argvars[1], &error); if (!error) { rettv->v_type = VAR_NUMBER; @@ -2679,7 +2620,7 @@ static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr) if ((l = argvars[0].vval.v_list) != NULL) { bool error = false; - li = tv_list_find(l, tv_get_number_chk(&argvars[1], &error)); + li = tv_list_find(l, (int)tv_get_number_chk(&argvars[1], &error)); if (!error && li != NULL) { tv = TV_LIST_ITEM_TV(li); } @@ -2860,66 +2801,6 @@ static void f_getbufline(typval_T *argvars, typval_T *rettv, FunPtr fptr) get_buffer_lines(buf, lnum, end, true, rettv); } -/// "getbufvar()" function -static void f_getbufvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - bool done = false; - - rettv->v_type = VAR_STRING; - rettv->vval.v_string = NULL; - - if (!tv_check_str_or_nr(&argvars[0])) { - goto f_getbufvar_end; - } - - const char *varname = tv_get_string_chk(&argvars[1]); - emsg_off++; - buf_T *const buf = tv_get_buf(&argvars[0], false); - - if (buf != NULL && varname != NULL) { - if (*varname == '&') { // buffer-local-option - buf_T *const save_curbuf = curbuf; - - // set curbuf to be our buf, temporarily - curbuf = buf; - - if (varname[1] == NUL) { - // get all buffer-local options in a dict - dict_T *opts = get_winbuf_options(true); - - if (opts != NULL) { - tv_dict_set_ret(rettv, opts); - done = true; - } - } else if (get_option_tv(&varname, rettv, true) == OK) { - // buffer-local-option - done = true; - } - - // restore previous notion of curbuf - curbuf = save_curbuf; - } else { - // Look up the variable. - // Let getbufvar({nr}, "") return the "b:" dictionary. - dictitem_T *const v = *varname == NUL - ? (dictitem_T *)&buf->b_bufvar - : find_var_in_ht(&buf->b_vars->dv_hashtab, 'b', - varname, strlen(varname), false); - if (v != NULL) { - tv_copy(&v->di_tv, rettv); - done = true; - } - } - } - emsg_off--; - -f_getbufvar_end: - if (!done && argvars[2].v_type != VAR_UNKNOWN) { - // use the default value - tv_copy(&argvars[2], rettv); - } -} - /// "getchangelist()" function static void f_getchangelist(typval_T *argvars, typval_T *rettv, FunPtr fptr) { @@ -2929,7 +2810,7 @@ static void f_getchangelist(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (argvars[0].v_type == VAR_UNKNOWN) { buf = curbuf; } else { - vim_ignored = tv_get_number(&argvars[0]); // issue errmsg if type error + vim_ignored = (int)tv_get_number(&argvars[0]); // issue errmsg if type error emsg_off++; buf = tv_get_buf(&argvars[0], false); emsg_off--; @@ -2940,13 +2821,23 @@ static void f_getchangelist(typval_T *argvars, typval_T *rettv, FunPtr fptr) list_T *const l = tv_list_alloc(buf->b_changelistlen); tv_list_append_list(rettv->vval.v_list, l); - // The current window change list index tracks only the position in the - // current buffer change list. For other buffers, use the change list - // length as the current index. - tv_list_append_number(rettv->vval.v_list, - (buf == curwin->w_buffer) - ? curwin->w_changelistidx - : buf->b_changelistlen); + // The current window change list index tracks only the position for the + // current buffer. For other buffers use the stored index for the current + // window, or, if that's not available, the change list length. + int changelistindex; + if (buf == curwin->w_buffer) { + changelistindex = curwin->w_changelistidx; + } else { + wininfo_T *wip; + + FOR_ALL_BUF_WININFO(buf, wip) { + if (wip->wi_win == curwin) { + break; + } + } + changelistindex = wip != NULL ? wip->wi_changelistidx : buf->b_changelistlen; + } + tv_list_append_number(rettv->vval.v_list, (varnumber_T)changelistindex); for (int i = 0; i < buf->b_changelistlen; i++) { if (buf->b_changelist[i].mark.lnum == 0) { @@ -3008,6 +2899,11 @@ static void getchar_common(typval_T *argvars, typval_T *rettv) no_mapping--; allow_keys--; + if (!ui_has_messages()) { + // redraw the screen after getchar() + update_screen(CLEAR); + } + set_vim_var_nr(VV_MOUSE_WIN, 0); set_vim_var_nr(VV_MOUSE_WINID, 0); set_vim_var_nr(VV_MOUSE_LNUM, 0); @@ -3022,21 +2918,21 @@ static void getchar_common(typval_T *argvars, typval_T *rettv) if (mod_mask != 0) { temp[i++] = K_SPECIAL; temp[i++] = KS_MODIFIER; - temp[i++] = mod_mask; + temp[i++] = (char_u)mod_mask; } if (IS_SPECIAL(n)) { temp[i++] = K_SPECIAL; - temp[i++] = K_SECOND(n); + temp[i++] = (char_u)K_SECOND(n); temp[i++] = K_THIRD(n); } else { - i += utf_char2bytes(n, (char *)temp + i); + i += utf_char2bytes((int)n, (char *)temp + i); } assert(i < 10); temp[i++] = NUL; rettv->v_type = VAR_STRING; rettv->vval.v_string = (char *)vim_strsave(temp); - if (is_mouse_key(n)) { + if (is_mouse_key((int)n)) { int row = mouse_row; int col = mouse_col; int grid = mouse_grid; @@ -3081,7 +2977,7 @@ static void f_getcharstr(typval_T *argvars, typval_T *rettv, FunPtr fptr) int i = 0; if (n != 0) { - i += utf_char2bytes(n, (char *)temp); + i += utf_char2bytes((int)n, (char *)temp); } assert(i < 7); temp[i++] = NUL; @@ -3200,7 +3096,7 @@ static void f_getcmdtype(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->v_type = VAR_STRING; rettv->vval.v_string = xmallocz(1); - rettv->vval.v_string[0] = get_cmdline_type(); + rettv->vval.v_string[0] = (char)get_cmdline_type(); } /// "getcmdwintype()" function @@ -3209,7 +3105,7 @@ static void f_getcmdwintype(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; rettv->vval.v_string = xmallocz(1); - rettv->vval.v_string[0] = cmdwin_type; + rettv->vval.v_string[0] = (char)cmdwin_type; } /// "getcompletion()" function @@ -3249,7 +3145,7 @@ static void f_getcompletion(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (strcmp(type, "cmdline") == 0) { set_one_cmd_context(&xpc, pattern); xpc.xp_pattern_len = STRLEN(xpc.xp_pattern); - xpc.xp_col = STRLEN(pattern); + xpc.xp_col = (int)STRLEN(pattern); goto theend; } @@ -3330,7 +3226,7 @@ static void f_getcwd(typval_T *argvars, typval_T *rettv, FunPtr fptr) emsg(_(e_invarg)); return; } - scope_number[i] = argvars[i].vval.v_number; + scope_number[i] = (int)argvars[i].vval.v_number; // It is an error for the scope number to be less than `-1`. if (scope_number[i] < -1) { emsg(_(e_invarg)); @@ -3430,7 +3326,7 @@ static void f_getfperm(typval_T *argvars, typval_T *rettv, FunPtr fptr) perm = xstrdup("---------"); for (int i = 0; i < 9; i++) { if (file_perm & (1 << (8 - i))) { - perm[i] = flags[i % 3]; + perm[i] = (char)flags[i % 3]; } } } @@ -3764,50 +3660,6 @@ static void f_gettabinfo(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -/// "gettabvar()" function -static void f_gettabvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - bool done = false; - - rettv->v_type = VAR_STRING; - rettv->vval.v_string = NULL; - - const char *const varname = tv_get_string_chk(&argvars[1]); - tabpage_T *const tp = find_tabpage((int)tv_get_number_chk(&argvars[0], NULL)); - if (tp != NULL && varname != NULL) { - // Set tp to be our tabpage, temporarily. Also set the window to the - // first window in the tabpage, otherwise the window is not valid. - win_T *const window = tp == curtab || tp->tp_firstwin == NULL - ? firstwin - : tp->tp_firstwin; - switchwin_T switchwin; - if (switch_win(&switchwin, window, tp, true) == OK) { - // look up the variable - // Let gettabvar({nr}, "") return the "t:" dictionary. - const dictitem_T *const v = find_var_in_ht(&tp->tp_vars->dv_hashtab, 't', - varname, strlen(varname), - false); - if (v != NULL) { - tv_copy(&v->di_tv, rettv); - done = true; - } - } - - // restore previous notion of curwin - restore_win(&switchwin, true); - } - - if (!done && argvars[2].v_type != VAR_UNKNOWN) { - tv_copy(&argvars[2], rettv); - } -} - -/// "gettabwinvar()" function -static void f_gettabwinvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - getwinvar(argvars, rettv, 1); -} - /// "gettagstack()" function static void f_gettagstack(typval_T *argvars, typval_T *rettv, FunPtr fptr) { @@ -3833,7 +3685,7 @@ static void f_getwininfo(typval_T *argvars, typval_T *rettv, FunPtr fptr) tv_list_alloc_ret(rettv, kListLenMayKnow); if (argvars[0].v_type != VAR_UNKNOWN) { - wparg = win_id2wp(tv_get_number(&argvars[0])); + wparg = win_id2wp((int)tv_get_number(&argvars[0])); if (wparg == NULL) { return; } @@ -3886,10 +3738,10 @@ static void f_wait(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - int timeout = argvars[0].vval.v_number; + int timeout = (int)argvars[0].vval.v_number; typval_T expr = argvars[1]; int interval = argvars[2].v_type == VAR_NUMBER - ? argvars[2].vval.v_number + ? (int)argvars[2].vval.v_number : 200; // Default. TimeWatcher *tw = xmalloc(sizeof(TimeWatcher)); @@ -3897,7 +3749,7 @@ static void f_wait(typval_T *argvars, typval_T *rettv, FunPtr fptr) time_watcher_init(&main_loop, tw, NULL); tw->events = main_loop.events; tw->blockable = true; - time_watcher_start(tw, dummy_timer_due_cb, interval, interval); + time_watcher_start(tw, dummy_timer_due_cb, (uint64_t)interval, (uint64_t)interval); typval_T argv = TV_INITIAL_VALUE; typval_T exprval = TV_INITIAL_VALUE; @@ -4005,7 +3857,7 @@ static void f_win_splitmove(typval_T *argvars, typval_T *rettv, FunPtr fptr) if ((di = tv_dict_find(d, "rightbelow", -1)) != NULL) { flags |= tv_get_number(&di->di_tv) ? WSP_BELOW : WSP_ABOVE; } - size = tv_dict_get_number(d, "size"); + size = (int)tv_dict_get_number(d, "size"); } win_move_into_split(wp, targetwin, size, flags); @@ -4031,12 +3883,6 @@ static void f_getwinposy(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_number = -1; } -/// "getwinvar()" function -static void f_getwinvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - getwinvar(argvars, rettv, 0); -} - /// "glob()" function static void f_glob(typval_T *argvars, typval_T *rettv, FunPtr fptr) { @@ -4181,6 +4027,7 @@ static void f_has(typval_T *argvars, typval_T *rettv, FunPtr fptr) "cindent", "cmdline_compl", "cmdline_hist", + "cmdwin", "comments", "conceal", "cscope", @@ -4282,7 +4129,7 @@ static void f_has(typval_T *argvars, typval_T *rettv, FunPtr fptr) }; // XXX: eval_has_provider() may shell out :( - const int save_shell_error = get_vim_var_nr(VV_SHELL_ERROR); + const int save_shell_error = (int)get_vim_var_nr(VV_SHELL_ERROR); bool n = false; const char *const name = tv_get_string(&argvars[0]); for (size_t i = 0; i < ARRAY_SIZE(has_list); i++) { @@ -4359,22 +4206,6 @@ static bool has_wsl(void) return has_wsl == kTrue; } -/// "has_key()" function -static void f_has_key(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - if (argvars[0].v_type != VAR_DICT) { - emsg(_(e_dictreq)); - return; - } - if (argvars[0].vval.v_dict == NULL) { - return; - } - - rettv->vval.v_number = tv_dict_find(argvars[0].vval.v_dict, - tv_get_string(&argvars[1]), - -1) != NULL; -} - /// `haslocaldir([{win}[, {tab}]])` function /// /// Returns `1` if the scope object has a local directory, `0` otherwise. If a @@ -4413,7 +4244,7 @@ static void f_haslocaldir(typval_T *argvars, typval_T *rettv, FunPtr fptr) emsg(_(e_invarg)); return; } - scope_number[i] = argvars[i].vval.v_number; + scope_number[i] = (int)argvars[i].vval.v_number; if (scope_number[i] < -1) { emsg(_(e_invarg)); return; @@ -4629,7 +4460,7 @@ static void f_index(typval_T *argvars, typval_T *rettv, FunPtr fptr) int start = 0; if (argvars[2].v_type != VAR_UNKNOWN) { - start = tv_get_number_chk(&argvars[2], &error); + start = (int)tv_get_number_chk(&argvars[2], &error); if (error) { return; } @@ -4647,7 +4478,7 @@ static void f_index(typval_T *argvars, typval_T *rettv, FunPtr fptr) for (idx = start; idx < tv_blob_len(b); idx++) { typval_T tv; tv.v_type = VAR_NUMBER; - tv.vval.v_number = tv_blob_get(b, idx); + tv.vval.v_number = tv_blob_get(b, (int)idx); if (tv_equal(&tv, &argvars[1], ic, false)) { rettv->vval.v_number = idx; return; @@ -4665,11 +4496,11 @@ static void f_index(typval_T *argvars, typval_T *rettv, FunPtr fptr) bool error = false; // Start at specified item. - idx = tv_list_uidx(l, tv_get_number_chk(&argvars[2], &error)); + idx = tv_list_uidx(l, (int)tv_get_number_chk(&argvars[2], &error)); if (error || idx == -1) { item = NULL; } else { - item = tv_list_find(l, idx); + item = tv_list_find(l, (int)idx); assert(item != NULL); } if (argvars[3].v_type != VAR_UNKNOWN) { @@ -4797,7 +4628,7 @@ static void f_insert(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } } - const int val = tv_get_number_chk(&argvars[1], &error); + const int val = (int)tv_get_number_chk(&argvars[1], &error); if (error) { return; } @@ -4808,8 +4639,8 @@ static void f_insert(typval_T *argvars, typval_T *rettv, FunPtr fptr) ga_grow(&b->bv_ga, 1); char_u *const p = (char_u *)b->bv_ga.ga_data; - memmove(p + before + 1, p + before, (size_t)len - before); - *(p + before) = val; + memmove(p + before + 1, p + before, (size_t)(len - before)); + *(p + before) = (char_u)val; b->bv_ga.ga_len++; tv_copy(&argvars[0], rettv); @@ -4828,7 +4659,7 @@ static void f_insert(typval_T *argvars, typval_T *rettv, FunPtr fptr) listitem_T *item = NULL; if (before != tv_list_len(l)) { - item = tv_list_find(l, before); + item = tv_list_find(l, (int)before); if (item == NULL) { semsg(_(e_listidx), (int64_t)before); l = NULL; @@ -4925,14 +4756,8 @@ static void f_id(typval_T *argvars, typval_T *rettv, FunPtr fptr) { const int len = vim_vsnprintf_typval(NULL, 0, "%p", dummy_ap, argvars); rettv->v_type = VAR_STRING; - rettv->vval.v_string = xmalloc(len + 1); - vim_vsnprintf_typval(rettv->vval.v_string, len + 1, "%p", dummy_ap, argvars); -} - -/// "items(dict)" function -static void f_items(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - dict_list(argvars, rettv, 2); + rettv->vval.v_string = xmalloc((size_t)len + 1); + vim_vsnprintf_typval(rettv->vval.v_string, (size_t)len + 1, "%p", dummy_ap, argvars); } /// "jobpid(id)" function @@ -4950,7 +4775,7 @@ static void f_jobpid(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - Channel *data = find_job(argvars[0].vval.v_number, true); + Channel *data = find_job((uint64_t)argvars[0].vval.v_number, true); if (!data) { return; } @@ -4976,7 +4801,7 @@ static void f_jobresize(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - Channel *data = find_job(argvars[0].vval.v_number, true); + Channel *data = find_job((uint64_t)argvars[0].vval.v_number, true); if (!data) { return; } @@ -4986,8 +4811,8 @@ static void f_jobresize(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - pty_process_resize(&data->stream.pty, argvars[1].vval.v_number, - argvars[2].vval.v_number); + pty_process_resize(&data->stream.pty, (uint16_t)argvars[1].vval.v_number, + (uint16_t)argvars[2].vval.v_number); rettv->vval.v_number = 1; } @@ -5102,7 +4927,7 @@ static dict_T *create_environment(const dictitem_T *job_env, const bool clear_en i < ARRAY_SIZE(required_env_vars) && required_env_vars[i]; i++) { size_t len = strlen(required_env_vars[i]); - dictitem_T *dv = tv_dict_find(env, required_env_vars[i], len); + dictitem_T *dv = tv_dict_find(env, required_env_vars[i], (ptrdiff_t)len); if (!dv) { const char *env_var = os_getenv(required_env_vars[i]); if (env_var) { @@ -5251,7 +5076,7 @@ static void f_jobstop(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - Channel *data = find_job(argvars[0].vval.v_number, false); + Channel *data = find_job((uint64_t)argvars[0].vval.v_number, false); if (!data) { return; } @@ -5285,7 +5110,7 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, FunPtr fptr) ui_busy_start(); list_T *args = argvars[0].vval.v_list; - Channel **jobs = xcalloc(tv_list_len(args), sizeof(*jobs)); + Channel **jobs = xcalloc((size_t)tv_list_len(args), sizeof(*jobs)); MultiQueue *waiting_jobs = multiqueue_new_parent(loop_on_put, &main_loop); // Validate, prepare jobs for waiting. @@ -5293,7 +5118,7 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, FunPtr fptr) TV_LIST_ITER_CONST(args, arg, { Channel *chan = NULL; if (TV_LIST_ITEM_TV(arg)->v_type != VAR_NUMBER - || !(chan = find_channel(TV_LIST_ITEM_TV(arg)->vval.v_number)) + || !(chan = find_channel((uint64_t)TV_LIST_ITEM_TV(arg)->vval.v_number)) || chan->streamtype != kChannelStreamProc) { jobs[i] = NULL; // Invalid job. } else if (process_is_stopped(&chan->stream.proc)) { @@ -5316,7 +5141,7 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, FunPtr fptr) int remaining = -1; uint64_t before = 0; if (argvars[1].v_type == VAR_NUMBER && argvars[1].vval.v_number >= 0) { - remaining = argvars[1].vval.v_number; + remaining = (int)argvars[1].vval.v_number; before = os_hrtime(); } @@ -5367,30 +5192,6 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_list = rv; } -/// "join()" function -static void f_join(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - if (argvars[0].v_type != VAR_LIST) { - emsg(_(e_listreq)); - return; - } - const char *const sep = (argvars[1].v_type == VAR_UNKNOWN - ? " " - : tv_get_string_chk(&argvars[1])); - - rettv->v_type = VAR_STRING; - - if (sep != NULL) { - garray_T ga; - ga_init(&ga, (int)sizeof(char), 80); - tv_list_join(&ga, argvars[0].vval.v_list, sep); - ga_append(&ga, NUL); - rettv->vval.v_string = ga.ga_data; - } else { - rettv->vval.v_string = NULL; - } -} - /// json_decode() function static void f_json_decode(typval_T *argvars, typval_T *rettv, FunPtr fptr) { @@ -5432,12 +5233,6 @@ static void f_json_encode(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_string = encode_tv2json(&argvars[0], NULL); } -/// "keys()" function -static void f_keys(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - dict_list(argvars, rettv, 0); -} - /// "last_buffer_nr()" function. static void f_last_buffer_nr(typval_T *argvars, typval_T *rettv, FunPtr fptr) { @@ -5482,7 +5277,7 @@ static void f_len(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void libcall_common(typval_T *argvars, typval_T *rettv, int out_type) { - rettv->v_type = out_type; + rettv->v_type = (VarType)out_type; if (out_type != VAR_NUMBER) { rettv->vval.v_string = NULL; } @@ -5503,7 +5298,7 @@ static void libcall_common(typval_T *argvars, typval_T *rettv, int out_type) // input variables char *str_in = (in_type == VAR_STRING) ? argvars[2].vval.v_string : NULL; - int int_in = argvars[2].vval.v_number; + int int_in = (int)argvars[2].vval.v_number; // output variables char **str_out = (out_type == VAR_STRING) ? &rettv->vval.v_string : NULL; @@ -5594,35 +5389,6 @@ static void f_lispindent(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -/// "list2str()" function -static void f_list2str(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - garray_T ga; - - rettv->v_type = VAR_STRING; - rettv->vval.v_string = NULL; - if (argvars[0].v_type != VAR_LIST) { - emsg(_(e_invarg)); - return; - } - - list_T *const l = argvars[0].vval.v_list; - if (l == NULL) { - return; // empty list results in empty string - } - - ga_init(&ga, 1, 80); - char buf[MB_MAXBYTES + 1]; - - TV_LIST_ITER_CONST(l, li, { - buf[utf_char2bytes(tv_get_number(TV_LIST_ITEM_TV(li)), (char *)buf)] = NUL; - ga_concat(&ga, (char *)buf); - }); - ga_append(&ga, NUL); - - rettv->vval.v_string = ga.ga_data; -} - /// "localtime()" function static void f_localtime(typval_T *argvars, typval_T *rettv, FunPtr fptr) { @@ -5716,11 +5482,11 @@ static void find_some_match(typval_T *const argvars, typval_T *const rettv, goto theend; } if (l != NULL) { - idx = tv_list_uidx(l, start); + idx = tv_list_uidx(l, (int)start); if (idx == -1) { goto theend; } - li = tv_list_find(l, idx); + li = tv_list_find(l, (int)idx); } else { if (start < 0) { start = 0; @@ -5732,7 +5498,7 @@ static void find_some_match(typval_T *const argvars, typval_T *const rettv, // otherwise skip part of the string. Differs when pattern is "^" // or "\<". if (argvars[3].v_type != VAR_UNKNOWN) { - startcol = start; + startcol = (colnr_T)start; } else { str += start; len -= start; @@ -5976,7 +5742,7 @@ static void f_mkdir(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (argvars[1].v_type != VAR_UNKNOWN) { if (argvars[2].v_type != VAR_UNKNOWN) { - prot = tv_get_number_chk(&argvars[2], NULL); + prot = (int)tv_get_number_chk(&argvars[2], NULL); if (prot == -1) { return; } @@ -6144,7 +5910,7 @@ static void msgpackparse_unpack_blob(const blob_T *const blob, list_T *const ret msgpack_unpacked_init(&unpacked); for (size_t offset = 0; offset < (size_t)len;) { const msgpack_unpack_return result - = msgpack_unpack_next(&unpacked, blob->bv_ga.ga_data, len, &offset); + = msgpack_unpack_next(&unpacked, blob->bv_ga.ga_data, (size_t)len, &offset); if (msgpackparse_convert_item(unpacked.data, result, ret_list, true) != OK) { break; @@ -6290,9 +6056,9 @@ static void f_printf(typval_T *argvars, typval_T *rettv, FunPtr fptr) const char *fmt = tv_get_string_buf(&argvars[0], buf); len = vim_vsnprintf_typval(NULL, 0, fmt, dummy_ap, argvars + 1); if (!did_emsg) { - char *s = xmalloc(len + 1); + char *s = xmalloc((size_t)len + 1); rettv->vval.v_string = s; - (void)vim_vsnprintf_typval(s, len + 1, fmt, dummy_ap, argvars + 1); + (void)vim_vsnprintf_typval(s, (size_t)len + 1, fmt, dummy_ap, argvars + 1); } did_emsg |= saved_did_emsg; } @@ -6436,7 +6202,7 @@ static void init_srand(uint32_t *const x) // Reading /dev/urandom doesn't work, fall back to time(). #endif // uncrustify:off - *x = time(NULL); + *x = (uint32_t)time(NULL); #ifndef MSWIN } #endif @@ -6514,10 +6280,10 @@ static void f_rand(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (tvw->v_type != VAR_NUMBER) { goto theend; } - uint32_t x = tvx->vval.v_number; - uint32_t y = tvy->vval.v_number; - uint32_t z = tvz->vval.v_number; - uint32_t w = tvw->vval.v_number; + uint32_t x = (uint32_t)tvx->vval.v_number; + uint32_t y = (uint32_t)tvy->vval.v_number; + uint32_t z = (uint32_t)tvz->vval.v_number; + uint32_t w = (uint32_t)tvw->vval.v_number; result = shuffle_xoshiro128starstar(&x, &y, &z, &w); @@ -6549,7 +6315,7 @@ static void f_srand(typval_T *argvars, typval_T *rettv, FunPtr fptr) init_srand(&x); } else { bool error = false; - x = tv_get_number_chk(&argvars[0], &error); + x = (uint32_t)tv_get_number_chk(&argvars[0], &error); if (error) { return; } @@ -6716,7 +6482,7 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) list_T *const l = tv_list_alloc_ret(rettv, kListLenUnknown); while (maxline < 0 || tv_list_len(l) < maxline) { - readlen = (int)fread(buf, 1, io_size, fd); + readlen = (int)fread(buf, 1, (size_t)io_size, fd); // This for loop processes what was read, but is also entered at end // of file so that either: @@ -6730,7 +6496,7 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) p++) { if (*p == '\n' || readlen <= 0) { char_u *s = NULL; - size_t len = p - start; + size_t len = (size_t)(p - start); // Finished a line. Remove CRs before NL. if (readlen > 0 && !binary) { @@ -6751,9 +6517,9 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) /* Change "prev" buffer to be the right size. This way * the bytes are only copied once, and very long lines are * allocated only once. */ - s = xrealloc(prev, prevlen + len + 1); + s = xrealloc(prev, (size_t)prevlen + len + 1); memcpy(s + prevlen, start, len); - s[prevlen + len] = NUL; + s[(size_t)prevlen + len] = NUL; prev = NULL; // the list will own the string prevlen = prevsize = 0; } @@ -6808,7 +6574,7 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) dest = buf; } if (readlen > p - buf + 1) { - memmove(dest, p + 1, readlen - (p - buf) - 1); + memmove(dest, p + 1, (size_t)readlen - (size_t)(p - buf) - 1); } readlen -= 3 - adjust_prevlen; prevlen -= adjust_prevlen; @@ -6835,10 +6601,10 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) long growmin = (long)((p - start) * 2 + prevlen); prevsize = grow50pc > growmin ? grow50pc : growmin; } - prev = xrealloc(prev, prevsize); + prev = xrealloc(prev, (size_t)prevsize); } // Add the line part to end of "prev". - memmove(prev + prevlen, start, p - start); + memmove(prev + prevlen, start, (size_t)(p - start)); prevlen += (long)(p - start); } } // while @@ -6887,7 +6653,7 @@ static void f_getreginfo(typval_T *argvars, typval_T *rettv, FunPtr fptr) } (void)tv_dict_add_str(dict, S_LEN("regtype"), buf); - buf[0] = get_register_name(get_unname_register()); + buf[0] = (char)get_register_name(get_unname_register()); buf[1] = NUL; if (regname == '"') { (void)tv_dict_add_str(dict, S_LEN("points_to"), buf); @@ -6938,7 +6704,7 @@ static int list2proftime(typval_T *arg, proftime_T *tm) FUNC_ATTR_NONNULL_ALL union { struct { int32_t low, high; } split; proftime_T prof; - } u = { .split.high = n1, .split.low = n2 }; + } u = { .split.high = (int32_t)n1, .split.low = (int32_t)n2 }; *tm = u.prof; @@ -7008,134 +6774,16 @@ static void f_reltimestr(typval_T *argvars, typval_T *rettv, FunPtr fptr) /// "remove()" function static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - list_T *l; - listitem_T *item, *item2; - listitem_T *li; - long idx; - long end; - dict_T *d; - dictitem_T *di; const char *const arg_errmsg = N_("remove() argument"); if (argvars[0].v_type == VAR_DICT) { - if (argvars[2].v_type != VAR_UNKNOWN) { - semsg(_(e_toomanyarg), "remove()"); - } else if ((d = argvars[0].vval.v_dict) != NULL - && !var_check_lock(d->dv_lock, arg_errmsg, TV_TRANSLATE)) { - const char *key = tv_get_string_chk(&argvars[1]); - if (key != NULL) { - di = tv_dict_find(d, key, -1); - if (di == NULL) { - semsg(_(e_dictkey), key); - } else if (!var_check_fixed(di->di_flags, arg_errmsg, TV_TRANSLATE) - && !var_check_ro(di->di_flags, arg_errmsg, TV_TRANSLATE)) { - *rettv = di->di_tv; - di->di_tv = TV_INITIAL_VALUE; - tv_dict_item_remove(d, di); - if (tv_dict_is_watched(d)) { - tv_dict_watcher_notify(d, key, NULL, rettv); - } - } - } - } + tv_dict_remove(argvars, rettv, arg_errmsg); } else if (argvars[0].v_type == VAR_BLOB) { - blob_T *const b = argvars[0].vval.v_blob; - - if (b != NULL && var_check_lock(b->bv_lock, arg_errmsg, TV_TRANSLATE)) { - return; - } - - bool error = false; - idx = (long)tv_get_number_chk(&argvars[1], &error); - - if (!error) { - const int len = tv_blob_len(b); - - if (idx < 0) { - // count from the end - idx = len + idx; - } - if (idx < 0 || idx >= len) { - semsg(_(e_blobidx), (int64_t)idx); - return; - } - if (argvars[2].v_type == VAR_UNKNOWN) { - // Remove one item, return its value. - char_u *const p = (char_u *)b->bv_ga.ga_data; - rettv->vval.v_number = (varnumber_T)(*(p + idx)); - memmove(p + idx, p + idx + 1, (size_t)len - idx - 1); - b->bv_ga.ga_len--; - } else { - // Remove range of items, return blob with values. - end = (long)tv_get_number_chk(&argvars[2], &error); - if (error) { - return; - } - if (end < 0) { - // count from the end - end = len + end; - } - if (end >= len || idx > end) { - semsg(_(e_blobidx), (int64_t)end); - return; - } - blob_T *const blob = tv_blob_alloc(); - blob->bv_ga.ga_len = end - idx + 1; - ga_grow(&blob->bv_ga, end - idx + 1); - - char_u *const p = (char_u *)b->bv_ga.ga_data; - memmove((char_u *)blob->bv_ga.ga_data, p + idx, - (size_t)(end - idx + 1)); - tv_blob_set_ret(rettv, blob); - - if (len - end - 1 > 0) { - memmove(p + idx, p + end + 1, (size_t)(len - end - 1)); - } - b->bv_ga.ga_len -= end - idx + 1; - } - } - } else if (argvars[0].v_type != VAR_LIST) { + tv_blob_remove(argvars, rettv, arg_errmsg); + } else if (argvars[0].v_type == VAR_LIST) { + tv_list_remove(argvars, rettv, arg_errmsg); + } else { semsg(_(e_listdictblobarg), "remove()"); - } else if (!var_check_lock(tv_list_locked((l = argvars[0].vval.v_list)), - arg_errmsg, TV_TRANSLATE)) { - bool error = false; - - idx = tv_get_number_chk(&argvars[1], &error); - if (error) { - // Type error: do nothing, errmsg already given. - } else if ((item = tv_list_find(l, idx)) == NULL) { - semsg(_(e_listidx), (int64_t)idx); - } else { - if (argvars[2].v_type == VAR_UNKNOWN) { - // Remove one item, return its value. - tv_list_drop_items(l, item, item); - *rettv = *TV_LIST_ITEM_TV(item); - xfree(item); - } else { - // Remove range of items, return list with values. - end = tv_get_number_chk(&argvars[2], &error); - if (error) { - // Type error: do nothing. - } else if ((item2 = tv_list_find(l, end)) == NULL) { - semsg(_(e_listidx), (int64_t)end); - } else { - int cnt = 0; - - for (li = item; li != NULL; li = TV_LIST_ITEM_NEXT(l, li)) { - cnt++; - if (li == item2) { - break; - } - } - if (li == NULL) { // Didn't find "item2" after "item". - emsg(_(e_invrange)); - } else { - tv_list_move_items(l, item, item2, tv_list_alloc_ret(rettv, cnt), - cnt); - } - } - } - } } } @@ -7173,15 +6821,15 @@ static void f_repeat(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (slen == 0) { return; } - const size_t len = slen * n; + const size_t len = slen * (size_t)n; // Detect overflow. - if (len / n != slen) { + if (len / (size_t)n != slen) { return; } char *const r = xmallocz(len); for (varnumber_T i = 0; i < n; i++) { - memmove(r + i * slen, p, slen); + memmove(r + (size_t)i * slen, p, slen); } rettv->vval.v_string = r; @@ -7295,9 +6943,9 @@ static void f_resolve(typval_T *argvars, typval_T *rettv, FunPtr fptr) q = (char *)path_next_component(remain + 1); len = q - remain - (*q != NUL); const size_t p_len = strlen(p); - cpy = xmallocz(p_len + len); + cpy = xmallocz(p_len + (size_t)len); memcpy(cpy, p, p_len + 1); - xstrlcat(cpy + p_len, remain, len + 1); + xstrlcat(cpy + p_len, remain, (size_t)len + 1); xfree(p); p = cpy; @@ -7861,7 +7509,7 @@ static void f_rpcstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) // Allocate extra memory for the argument vector and the NULL pointer int argvl = argsl + 2; - char **argv = xmalloc(sizeof(char_u *) * argvl); + char **argv = xmalloc(sizeof(char_u *) * (size_t)argvl); // Copy program name argv[0] = xstrdup(argvars[0].vval.v_string); @@ -7904,13 +7552,13 @@ static void f_rpcstop(typval_T *argvars, typval_T *rettv, FunPtr fptr) } // if called with a job, stop it, else closes the channel - uint64_t id = argvars[0].vval.v_number; + uint64_t id = (uint64_t)argvars[0].vval.v_number; if (find_job(id, false)) { f_jobstop(argvars, rettv, NULL); } else { const char *error; - rettv->vval.v_number = channel_close(argvars[0].vval.v_number, - kChannelPartRpc, &error); + rettv->vval.v_number = + channel_close((uint64_t)argvars[0].vval.v_number, kChannelPartRpc, &error); if (!rettv->vval.v_number) { emsg(error); } @@ -7931,7 +7579,7 @@ static void f_screenattr(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (row < 0 || row >= grid->rows || col < 0 || col >= grid->cols) { c = -1; } else { - c = grid->attrs[grid->line_offset[row] + col]; + c = grid->attrs[grid->line_offset[row] + (size_t)col]; } rettv->vval.v_number = c; } @@ -7942,15 +7590,15 @@ static void f_screenchar(typval_T *argvars, typval_T *rettv, FunPtr fptr) int c; ScreenGrid *grid; - int row = tv_get_number_chk(&argvars[0], NULL) - 1; - int col = tv_get_number_chk(&argvars[1], NULL) - 1; + int row = (int)tv_get_number_chk(&argvars[0], NULL) - 1; + int col = (int)tv_get_number_chk(&argvars[1], NULL) - 1; screenchar_adjust(&grid, &row, &col); if (row < 0 || row >= grid->rows || col < 0 || col >= grid->cols) { c = -1; } else { - c = utf_ptr2char((char *)grid->chars[grid->line_offset[row] + col]); + c = utf_ptr2char((char *)grid->chars[grid->line_offset[row] + (size_t)col]); } rettv->vval.v_number = c; } @@ -7959,8 +7607,8 @@ static void f_screenchar(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_screenchars(typval_T *argvars, typval_T *rettv, FunPtr fptr) { ScreenGrid *grid; - int row = tv_get_number_chk(&argvars[0], NULL) - 1; - int col = tv_get_number_chk(&argvars[1], NULL) - 1; + int row = (int)tv_get_number_chk(&argvars[0], NULL) - 1; + int col = (int)tv_get_number_chk(&argvars[1], NULL) - 1; screenchar_adjust(&grid, &row, &col); @@ -7969,7 +7617,7 @@ static void f_screenchars(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } int pcc[MAX_MCO]; - int c = utfc_ptr2char(grid->chars[grid->line_offset[row] + col], pcc); + int c = utfc_ptr2char(grid->chars[grid->line_offset[row] + (size_t)col], pcc); int composing_len = 0; while (pcc[composing_len] != 0) { composing_len++; @@ -8004,8 +7652,8 @@ static void f_screenpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - pos.lnum = tv_get_number(&argvars[1]); - pos.col = tv_get_number(&argvars[2]) - 1; + pos.lnum = (linenr_T)tv_get_number(&argvars[1]); + pos.col = (colnr_T)tv_get_number(&argvars[2]) - 1; pos.coladd = 0; textpos2screenpos(wp, &pos, &row, &scol, &ccol, &ecol, false); @@ -8028,8 +7676,8 @@ static void f_screenstring(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->v_type = VAR_STRING; ScreenGrid *grid; - int row = tv_get_number_chk(&argvars[0], NULL) - 1; - int col = tv_get_number_chk(&argvars[1], NULL) - 1; + int row = (int)tv_get_number_chk(&argvars[0], NULL) - 1; + int col = (int)tv_get_number_chk(&argvars[1], NULL) - 1; screenchar_adjust(&grid, &row, &col); @@ -8037,7 +7685,7 @@ static void f_screenstring(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - rettv->vval.v_string = (char *)vim_strsave(grid->chars[grid->line_offset[row] + col]); + rettv->vval.v_string = (char *)vim_strsave(grid->chars[grid->line_offset[row] + (size_t)col]); } /// "search()" function @@ -8135,8 +7783,8 @@ static int searchpair_cmn(typval_T *argvars, pos_T *match_pos) } } - retval = do_searchpair(spat, mpat, epat, dir, skip, - flags, match_pos, lnum_stop, time_limit); + retval = (int)do_searchpair(spat, mpat, epat, dir, skip, + flags, match_pos, (linenr_T)lnum_stop, time_limit); theend: p_ws = save_p_ws; @@ -8363,7 +8011,7 @@ static void f_serverlist(typval_T *argvars, typval_T *rettv, FunPtr fptr) char **addrs = server_address_list(&n); // Copy addrs into a linked list. - list_T *const l = tv_list_alloc_ret(rettv, n); + list_T *const l = tv_list_alloc_ret(rettv, (ptrdiff_t)n); for (size_t i = 0; i < n; i++) { tv_list_append_allocated_string(l, addrs[i]); } @@ -8450,50 +8098,6 @@ static void f_setbufline(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -/// "setbufvar()" function -static void f_setbufvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - if (check_secure() - || !tv_check_str_or_nr(&argvars[0])) { - return; - } - const char *varname = tv_get_string_chk(&argvars[1]); - buf_T *const buf = tv_get_buf(&argvars[0], false); - typval_T *varp = &argvars[2]; - - if (buf != NULL && varname != NULL) { - if (*varname == '&') { - long numval; - bool error = false; - aco_save_T aco; - - // set curbuf to be our buf, temporarily - aucmd_prepbuf(&aco, buf); - - varname++; - numval = tv_get_number_chk(varp, &error); - char nbuf[NUMBUFLEN]; - const char *const strval = tv_get_string_buf_chk(varp, nbuf); - if (!error && strval != NULL) { - set_option_value(varname, numval, strval, OPT_LOCAL); - } - - // reset notion of buffer - aucmd_restbuf(&aco); - } else { - const size_t varname_len = STRLEN(varname); - char *const bufvarname = xmalloc(varname_len + 3); - buf_T *const save_curbuf = curbuf; - curbuf = buf; - memcpy(bufvarname, "b:", 2); - memcpy(bufvarname + 2, varname, varname_len + 1); - set_var(bufvarname, varname_len + 2, varp, true); - xfree(bufvarname); - curbuf = save_curbuf; - } - } -} - /// Set the cursor or mark position. /// If 'charpos' is TRUE, then use the column number as a character offset. /// Otherwise use the column number as a byte offset. @@ -8746,7 +8350,7 @@ static void f_setqflist(typval_T *argvars, typval_T *rettv, FunPtr fptr) static int get_yank_type(char_u **const pp, MotionType *const yank_type, long *const block_len) FUNC_ATTR_NONNULL_ALL { - char_u *stropt = *pp; + char *stropt = (char *)(*pp); switch (*stropt) { case 'v': case 'c': // character-wise selection @@ -8768,7 +8372,7 @@ static int get_yank_type(char_u **const pp, MotionType *const yank_type, long *c default: return FAIL; } - *pp = stropt; + *pp = (char_u *)stropt; return OK; } @@ -8788,7 +8392,7 @@ static void f_setreg(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (strregname == NULL) { return; // Type error; errmsg already given. } - char regname = (uint8_t)(*strregname); + char regname = *strregname; if (regname == 0 || regname == '@') { regname = '"'; } @@ -8867,7 +8471,7 @@ static void f_setreg(typval_T *argvars, typval_T *rettv, FunPtr fptr) // First half: use for pointers to result lines; second half: use for // pointers to allocated copies. - char **lstval = xmalloc(sizeof(char *) * ((len + 1) * 2)); + char **lstval = xmalloc(sizeof(char *) * (((size_t)len + 1) * 2)); const char **curval = (const char **)lstval; char **allocval = lstval + len + 2; char **curallocval = allocval; @@ -8889,8 +8493,7 @@ static void f_setreg(typval_T *argvars, typval_T *rettv, FunPtr fptr) }); *curval++ = NULL; - write_reg_contents_lst(regname, (char_u **)lstval, append, yank_type, - block_len); + write_reg_contents_lst(regname, (char_u **)lstval, append, yank_type, (colnr_T)block_len); free_lstval: while (curallocval > allocval) { @@ -8902,8 +8505,8 @@ free_lstval: if (strval == NULL) { return; } - write_reg_contents_ex(regname, (const char_u *)strval, STRLEN(strval), - append, yank_type, block_len); + write_reg_contents_ex(regname, (const char_u *)strval, (ssize_t)STRLEN(strval), + append, yank_type, (colnr_T)block_len); } if (pointreg != 0) { get_yank_register(pointreg, YREG_YANK); @@ -8916,43 +8519,6 @@ free_lstval: } } -/// "settabvar()" function -static void f_settabvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - rettv->vval.v_number = 0; - - if (check_secure()) { - return; - } - - tabpage_T *const tp = find_tabpage((int)tv_get_number_chk(&argvars[0], NULL)); - const char *const varname = tv_get_string_chk(&argvars[1]); - typval_T *const varp = &argvars[2]; - - if (varname != NULL && tp != NULL) { - tabpage_T *const save_curtab = curtab; - goto_tabpage_tp(tp, false, false); - - const size_t varname_len = strlen(varname); - char *const tabvarname = xmalloc(varname_len + 3); - memcpy(tabvarname, "t:", 2); - memcpy(tabvarname + 2, varname, varname_len + 1); - set_var(tabvarname, varname_len + 2, varp, true); - xfree(tabvarname); - - // Restore current tabpage. - if (valid_tabpage(save_curtab)) { - goto_tabpage_tp(save_curtab, false, false); - } - } -} - -/// "settabwinvar()" function -static void f_settabwinvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - setwinvar(argvars, rettv, 1); -} - /// "settagstack()" function static void f_settagstack(typval_T *argvars, typval_T *rettv, FunPtr fptr) { @@ -9006,12 +8572,6 @@ static void f_settagstack(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -/// "setwinvar()" function -static void f_setwinvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - setwinvar(argvars, rettv, 0); -} - /// f_sha256 - sha256({string}) function static void f_sha256(typval_T *argvars, typval_T *rettv, FunPtr fptr) { @@ -9046,7 +8606,7 @@ static void f_shiftwidth(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (col < 0) { return; // type error; errmsg already given } - rettv->vval.v_number = get_sw_value_col(curbuf, col); + rettv->vval.v_number = get_sw_value_col(curbuf, (colnr_T)col); return; } rettv->vval.v_number = get_sw_value(curbuf); @@ -9113,341 +8673,6 @@ static void f_sockconnect(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->v_type = VAR_NUMBER; } -/// struct storing information about current sort -typedef struct { - int item_compare_ic; - bool item_compare_lc; - bool item_compare_numeric; - bool item_compare_numbers; - bool item_compare_float; - const char *item_compare_func; - partial_T *item_compare_partial; - dict_T *item_compare_selfdict; - bool item_compare_func_err; -} sortinfo_T; -static sortinfo_T *sortinfo = NULL; - -#define ITEM_COMPARE_FAIL 999 - -/// Compare functions for f_sort() and f_uniq() below. -static int item_compare(const void *s1, const void *s2, bool keep_zero) -{ - ListSortItem *const si1 = (ListSortItem *)s1; - ListSortItem *const si2 = (ListSortItem *)s2; - - typval_T *const tv1 = TV_LIST_ITEM_TV(si1->item); - typval_T *const tv2 = TV_LIST_ITEM_TV(si2->item); - - int res; - - if (sortinfo->item_compare_numbers) { - const varnumber_T v1 = tv_get_number(tv1); - const varnumber_T v2 = tv_get_number(tv2); - - res = v1 == v2 ? 0 : v1 > v2 ? 1 : -1; - goto item_compare_end; - } - - if (sortinfo->item_compare_float) { - const float_T v1 = tv_get_float(tv1); - const float_T v2 = tv_get_float(tv2); - - res = v1 == v2 ? 0 : v1 > v2 ? 1 : -1; - goto item_compare_end; - } - - char *tofree1 = NULL; - char *tofree2 = NULL; - char *p1; - char *p2; - - // encode_tv2string() puts quotes around a string and allocates memory. Don't - // do that for string variables. Use a single quote when comparing with - // a non-string to do what the docs promise. - if (tv1->v_type == VAR_STRING) { - if (tv2->v_type != VAR_STRING || sortinfo->item_compare_numeric) { - p1 = "'"; - } else { - p1 = tv1->vval.v_string; - } - } else { - tofree1 = p1 = encode_tv2string(tv1, NULL); - } - if (tv2->v_type == VAR_STRING) { - if (tv1->v_type != VAR_STRING || sortinfo->item_compare_numeric) { - p2 = "'"; - } else { - p2 = tv2->vval.v_string; - } - } else { - tofree2 = p2 = encode_tv2string(tv2, NULL); - } - if (p1 == NULL) { - p1 = ""; - } - if (p2 == NULL) { - p2 = ""; - } - if (!sortinfo->item_compare_numeric) { - if (sortinfo->item_compare_lc) { - res = strcoll(p1, p2); - } else { - res = sortinfo->item_compare_ic ? STRICMP(p1, p2): STRCMP(p1, p2); - } - } else { - double n1, n2; - n1 = strtod(p1, &p1); - n2 = strtod(p2, &p2); - res = n1 == n2 ? 0 : n1 > n2 ? 1 : -1; - } - - xfree(tofree1); - xfree(tofree2); - -item_compare_end: - // When the result would be zero, compare the item indexes. Makes the - // sort stable. - if (res == 0 && !keep_zero) { - // WARNING: When using uniq si1 and si2 are actually listitem_T **, no - // indexes are there. - res = si1->idx > si2->idx ? 1 : -1; - } - return res; -} - -static int item_compare_keeping_zero(const void *s1, const void *s2) -{ - return item_compare(s1, s2, true); -} - -static int item_compare_not_keeping_zero(const void *s1, const void *s2) -{ - return item_compare(s1, s2, false); -} - -static int item_compare2(const void *s1, const void *s2, bool keep_zero) -{ - ListSortItem *si1, *si2; - int res; - typval_T rettv; - typval_T argv[3]; - const char *func_name; - partial_T *partial = sortinfo->item_compare_partial; - - // shortcut after failure in previous call; compare all items equal - if (sortinfo->item_compare_func_err) { - return 0; - } - - si1 = (ListSortItem *)s1; - si2 = (ListSortItem *)s2; - - if (partial == NULL) { - func_name = sortinfo->item_compare_func; - } else { - func_name = (const char *)partial_name(partial); - } - - // Copy the values. This is needed to be able to set v_lock to VAR_FIXED - // in the copy without changing the original list items. - tv_copy(TV_LIST_ITEM_TV(si1->item), &argv[0]); - tv_copy(TV_LIST_ITEM_TV(si2->item), &argv[1]); - - rettv.v_type = VAR_UNKNOWN; // tv_clear() uses this - funcexe_T funcexe = FUNCEXE_INIT; - funcexe.evaluate = true; - funcexe.partial = partial; - funcexe.selfdict = sortinfo->item_compare_selfdict; - res = call_func(func_name, -1, &rettv, 2, argv, &funcexe); - tv_clear(&argv[0]); - tv_clear(&argv[1]); - - if (res == FAIL) { - res = ITEM_COMPARE_FAIL; - } else { - res = tv_get_number_chk(&rettv, &sortinfo->item_compare_func_err); - if (res > 0) { - res = 1; - } else if (res < 0) { - res = -1; - } - } - if (sortinfo->item_compare_func_err) { - res = ITEM_COMPARE_FAIL; // return value has wrong type - } - tv_clear(&rettv); - - // When the result would be zero, compare the pointers themselves. Makes - // the sort stable. - if (res == 0 && !keep_zero) { - // WARNING: When using uniq si1 and si2 are actually listitem_T **, no - // indexes are there. - res = si1->idx > si2->idx ? 1 : -1; - } - - return res; -} - -static int item_compare2_keeping_zero(const void *s1, const void *s2) -{ - return item_compare2(s1, s2, true); -} - -static int item_compare2_not_keeping_zero(const void *s1, const void *s2) -{ - return item_compare2(s1, s2, false); -} - -/// "sort({list})" function -static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) -{ - ListSortItem *ptrs; - long len; - long i; - - // Pointer to current info struct used in compare function. Save and restore - // the current one for nested calls. - sortinfo_T info; - sortinfo_T *old_sortinfo = sortinfo; - sortinfo = &info; - - const char *const arg_errmsg = (sort - ? N_("sort() argument") - : N_("uniq() argument")); - - if (argvars[0].v_type != VAR_LIST) { - semsg(_(e_listarg), sort ? "sort()" : "uniq()"); - } else { - list_T *const l = argvars[0].vval.v_list; - if (var_check_lock(tv_list_locked(l), arg_errmsg, TV_TRANSLATE)) { - goto theend; - } - tv_list_set_ret(rettv, l); - - len = tv_list_len(l); - if (len <= 1) { - goto theend; // short list sorts pretty quickly - } - - info.item_compare_ic = false; - info.item_compare_lc = false; - info.item_compare_numeric = false; - info.item_compare_numbers = false; - info.item_compare_float = false; - info.item_compare_func = NULL; - info.item_compare_partial = NULL; - info.item_compare_selfdict = NULL; - - if (argvars[1].v_type != VAR_UNKNOWN) { - // optional second argument: {func} - if (argvars[1].v_type == VAR_FUNC) { - info.item_compare_func = (const char *)argvars[1].vval.v_string; - } else if (argvars[1].v_type == VAR_PARTIAL) { - info.item_compare_partial = argvars[1].vval.v_partial; - } else { - bool error = false; - - i = tv_get_number_chk(&argvars[1], &error); - if (error) { - goto theend; // type error; errmsg already given - } - if (i == 1) { - info.item_compare_ic = true; - } else if (argvars[1].v_type != VAR_NUMBER) { - info.item_compare_func = tv_get_string(&argvars[1]); - } else if (i != 0) { - emsg(_(e_invarg)); - goto theend; - } - if (info.item_compare_func != NULL) { - if (*info.item_compare_func == NUL) { - // empty string means default sort - info.item_compare_func = NULL; - } else if (strcmp(info.item_compare_func, "n") == 0) { - info.item_compare_func = NULL; - info.item_compare_numeric = true; - } else if (strcmp(info.item_compare_func, "N") == 0) { - info.item_compare_func = NULL; - info.item_compare_numbers = true; - } else if (strcmp(info.item_compare_func, "f") == 0) { - info.item_compare_func = NULL; - info.item_compare_float = true; - } else if (strcmp(info.item_compare_func, "i") == 0) { - info.item_compare_func = NULL; - info.item_compare_ic = true; - } else if (strcmp(info.item_compare_func, "l") == 0) { - info.item_compare_func = NULL; - info.item_compare_lc = true; - } - } - } - - if (argvars[2].v_type != VAR_UNKNOWN) { - // optional third argument: {dict} - if (argvars[2].v_type != VAR_DICT) { - emsg(_(e_dictreq)); - goto theend; - } - info.item_compare_selfdict = argvars[2].vval.v_dict; - } - } - - // Make an array with each entry pointing to an item in the List. - ptrs = xmalloc((size_t)(len * sizeof(ListSortItem))); - - if (sort) { - info.item_compare_func_err = false; - tv_list_item_sort(l, ptrs, - ((info.item_compare_func == NULL - && info.item_compare_partial == NULL) - ? item_compare_not_keeping_zero - : item_compare2_not_keeping_zero), - &info.item_compare_func_err); - if (info.item_compare_func_err) { - emsg(_("E702: Sort compare function failed")); - } - } else { - ListSorter item_compare_func_ptr; - - // f_uniq(): ptrs will be a stack of items to remove. - info.item_compare_func_err = false; - if (info.item_compare_func != NULL - || info.item_compare_partial != NULL) { - item_compare_func_ptr = item_compare2_keeping_zero; - } else { - item_compare_func_ptr = item_compare_keeping_zero; - } - - int idx = 0; - for (listitem_T *li = TV_LIST_ITEM_NEXT(l, tv_list_first(l)) - ; li != NULL;) { - listitem_T *const prev_li = TV_LIST_ITEM_PREV(l, li); - if (item_compare_func_ptr(&prev_li, &li) == 0) { - if (info.item_compare_func_err) { // -V547 - emsg(_("E882: Uniq compare function failed")); - break; - } - li = tv_list_item_remove(l, li); - } else { - idx++; - li = TV_LIST_ITEM_NEXT(l, li); - } - } - } - - xfree(ptrs); - } - -theend: - sortinfo = old_sortinfo; -} - -/// "sort"({list})" function -static void f_sort(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - do_sort_uniq(argvars, rettv, true); -} - /// "stdioopen()" function static void f_stdioopen(typval_T *argvars, typval_T *rettv, FunPtr fptr) { @@ -9483,12 +8708,6 @@ static void f_stdioopen(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->v_type = VAR_NUMBER; } -/// "uniq({list})" function -static void f_uniq(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - do_sort_uniq(argvars, rettv, false); -} - /// "reltimefloat()" function static void f_reltimefloat(typval_T *argvars, typval_T *rettv, FunPtr fptr) FUNC_ATTR_NONNULL_ALL @@ -9549,7 +8768,7 @@ static void f_spellbadword(typval_T *argvars, typval_T *rettv, FunPtr fptr) break; } str += len; - capcol -= len; + capcol -= (int)len; len = 0; } } @@ -9558,7 +8777,7 @@ static void f_spellbadword(typval_T *argvars, typval_T *rettv, FunPtr fptr) assert(len <= INT_MAX); tv_list_alloc_ret(rettv, 2); - tv_list_append_string(rettv->vval.v_list, word, len); + tv_list_append_string(rettv->vval.v_list, word, (ssize_t)len); tv_list_append_string(rettv->vval.v_list, (attr == HLF_SPB ? "bad" : attr == HLF_SPR ? "rare" @@ -9591,7 +8810,7 @@ static void f_spellsuggest(typval_T *argvars, typval_T *rettv, FunPtr fptr) const char *const str = tv_get_string(&argvars[0]); if (argvars[1].v_type != VAR_UNKNOWN) { - maxcount = tv_get_number_chk(&argvars[1], &typeerr); + maxcount = (int)tv_get_number_chk(&argvars[1], &typeerr); if (maxcount <= 0) { goto f_spellsuggest_return; } @@ -9763,7 +8982,7 @@ static void f_str2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr) int what = 0; if (argvars[1].v_type != VAR_UNKNOWN) { - base = tv_get_number(&argvars[1]); + base = (int)tv_get_number(&argvars[1]); if (base != 2 && base != 8 && base != 10 && base != 16) { emsg(_(e_invarg)); return; @@ -9874,7 +9093,7 @@ static void f_strgetchar(typval_T *argvars, typval_T *rettv, FunPtr fptr) break; } charidx--; - byteidx += utf_ptr2len(str + byteidx); + byteidx += (size_t)utf_ptr2len(str + byteidx); } } @@ -9932,7 +9151,7 @@ static void f_strchars(typval_T *argvars, typval_T *rettv, FunPtr fptr) int (*func_mb_ptr2char_adv)(const char_u **pp); if (argvars[1].v_type != VAR_UNKNOWN) { - skipcc = tv_get_number_chk(&argvars[1], NULL); + skipcc = (int)tv_get_number_chk(&argvars[1], NULL); } if (skipcc < 0 || skipcc > 1) { emsg(_(e_invarg)); @@ -9953,7 +9172,7 @@ static void f_strdisplaywidth(typval_T *argvars, typval_T *rettv, FunPtr fptr) int col = 0; if (argvars[1].v_type != VAR_UNKNOWN) { - col = tv_get_number(&argvars[1]); + col = (int)tv_get_number(&argvars[1]); } rettv->vval.v_number = (varnumber_T)(linetabsize_col(col, (char_u *)s) - col); @@ -9983,12 +9202,12 @@ static void f_strcharpart(typval_T *argvars, typval_T *rettv, FunPtr fptr) nchar--; } } else { - nbyte = nchar; + nbyte = (int)nchar; } } int len = 0; if (argvars[2].v_type != VAR_UNKNOWN) { - int charlen = tv_get_number(&argvars[2]); + int charlen = (int)tv_get_number(&argvars[2]); while (charlen > 0 && nbyte + len < (int)slen) { int off = nbyte + len; @@ -10000,7 +9219,7 @@ static void f_strcharpart(typval_T *argvars, typval_T *rettv, FunPtr fptr) charlen--; } } else { - len = slen - nbyte; // default: all bytes that are available. + len = (int)slen - nbyte; // default: all bytes that are available. } // Only return the overlap between the specified part and the actual @@ -10009,12 +9228,12 @@ static void f_strcharpart(typval_T *argvars, typval_T *rettv, FunPtr fptr) len += nbyte; nbyte = 0; } else if ((size_t)nbyte > slen) { - nbyte = slen; + nbyte = (int)slen; } if (len < 0) { len = 0; } else if (nbyte + len > (int)slen) { - len = slen - nbyte; + len = (int)slen - nbyte; } rettv->v_type = VAR_STRING; @@ -10036,7 +9255,7 @@ static void f_strpart(typval_T *argvars, typval_T *rettv, FunPtr fptr) } else if (argvars[2].v_type != VAR_UNKNOWN) { len = tv_get_number(&argvars[2]); } else { - len = slen - n; // Default len: all bytes that are available. + len = (varnumber_T)slen - n; // Default len: all bytes that are available. } // Only return the overlap between the specified part and the actual @@ -10045,19 +9264,19 @@ static void f_strpart(typval_T *argvars, typval_T *rettv, FunPtr fptr) len += n; n = 0; } else if (n > (varnumber_T)slen) { - n = slen; + n = (varnumber_T)slen; } if (len < 0) { len = 0; } else if (n + len > (varnumber_T)slen) { - len = slen - n; + len = (varnumber_T)slen - n; } if (argvars[2].v_type != VAR_UNKNOWN && argvars[3].v_type != VAR_UNKNOWN) { int off; // length in characters - for (off = n; off < (int)slen && len > 0; len--) { + for (off = (int)n; off < (int)slen && len > 0; len--) { off += utfc_ptr2len(p + off); } len = off - n; @@ -10165,7 +9384,7 @@ static void f_submatch(typval_T *argvars, typval_T *rettv, FunPtr fptr) int retList = 0; if (argvars[1].v_type != VAR_UNKNOWN) { - retList = tv_get_number_chk(&argvars[1], &error); + retList = (int)tv_get_number_chk(&argvars[1], &error); if (error) { return; } @@ -10238,7 +9457,7 @@ static void f_synID(typval_T *argvars, typval_T *rettv, FunPtr fptr) const colnr_T col = (colnr_T)tv_get_number(&argvars[1]) - 1; bool transerr = false; - const int trans = tv_get_number_chk(&argvars[2], &transerr); + const int trans = (int)tv_get_number_chk(&argvars[2], &transerr); int id = 0; if (!transerr && lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count @@ -10287,8 +9506,12 @@ static void f_synIDattr(typval_T *argvars, typval_T *rettv, FunPtr fptr) p = highlight_has_attr(id, HL_ITALIC, modec); } break; - case 'n': // name - p = get_highlight_name_ext(NULL, id - 1, false); + case 'n': + if (TOLOWER_ASC(what[1]) == 'o') { // nocombine + p = highlight_has_attr(id, HL_NOCOMBINE, modec); + } else { // name + p = get_highlight_name_ext(NULL, id - 1, false); + } break; case 'r': // reverse p = highlight_has_attr(id, HL_INVERSE, modec); @@ -10335,7 +9558,7 @@ static void f_synIDattr(typval_T *argvars, typval_T *rettv, FunPtr fptr) /// "synIDtrans(id)" function static void f_synIDtrans(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - int id = tv_get_number(&argvars[0]); + int id = (int)tv_get_number(&argvars[0]); if (id > 0) { id = syn_get_final_id(id); @@ -10662,10 +9885,10 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr) const bool overlapped = false; const bool detach = false; ChannelStdinMode stdin_mode = kChannelStdinPipe; - uint16_t term_width = MAX(0, curwin->w_width_inner - win_col_off(curwin)); + uint16_t term_width = (uint16_t)MAX(0, curwin->w_width_inner - win_col_off(curwin)); Channel *chan = channel_job_start(argv, on_stdout, on_stderr, on_exit, pty, rpc, overlapped, detach, stdin_mode, - cwd, term_width, curwin->w_height_inner, + cwd, term_width, (uint16_t)curwin->w_height_inner, env, &rettv->vval.v_number); if (rettv->vval.v_number <= 0) { return; @@ -10699,7 +9922,7 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr) Error err = ERROR_INIT; // deprecated: use 'channel' buffer option dict_set_var(curbuf->b_vars, cstr_as_string("terminal_job_id"), - INTEGER_OBJ(chan->id), false, false, &err); + INTEGER_OBJ((Integer)chan->id), false, false, &err); api_clear_error(&err); dict_set_var(curbuf->b_vars, cstr_as_string("terminal_job_pid"), INTEGER_OBJ(pid), false, false, &err); @@ -10740,8 +9963,8 @@ static void f_timer_pause(typval_T *argvars, typval_T *unused, FunPtr fptr) if (!timer->paused && paused) { time_watcher_stop(&timer->tw); } else if (timer->paused && !paused) { - time_watcher_start(&timer->tw, timer_due_cb, timer->timeout, - timer->timeout); + time_watcher_start(&timer->tw, timer_due_cb, (uint64_t)timer->timeout, + (uint64_t)timer->timeout); } timer->paused = paused; } @@ -10766,7 +9989,7 @@ static void f_timer_start(typval_T *argvars, typval_T *rettv, FunPtr fptr) } dictitem_T *const di = tv_dict_find(dict, S_LEN("repeat")); if (di != NULL) { - repeat = tv_get_number(&di->di_tv); + repeat = (int)tv_get_number(&di->di_tv); if (repeat == 0) { repeat = 1; } @@ -10777,8 +10000,7 @@ static void f_timer_start(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (!callback_from_typval(&callback, &argvars[1])) { return; } - rettv->vval.v_number = - timer_start(tv_get_number(&argvars[0]), repeat, &callback); + rettv->vval.v_number = (varnumber_T)timer_start(tv_get_number(&argvars[0]), repeat, &callback); } /// "timer_stop(timerid)" function @@ -10975,7 +10197,7 @@ static void f_trim(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } } - rettv->vval.v_string = (char *)vim_strnsave(head, tail - head); + rettv->vval.v_string = (char *)vim_strnsave(head, (size_t)(tail - head)); } /// "type(expr)" function @@ -11047,12 +10269,6 @@ static void f_undotree(typval_T *argvars, typval_T *rettv, FunPtr fptr) tv_dict_add_list(dict, S_LEN("entries"), u_eval_tree(curbuf->b_u_oldhead)); } -/// "values(dict)" function -static void f_values(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - dict_list(argvars, rettv, 1); -} - /// "virtcol(string)" function static void f_virtcol(typval_T *argvars, typval_T *rettv, FunPtr fptr) { @@ -11085,7 +10301,7 @@ static void f_visualmode(typval_T *argvars, typval_T *rettv, FunPtr fptr) char_u str[2]; rettv->v_type = VAR_STRING; - str[0] = curbuf->b_visual_mode_eval; + str[0] = (char_u)curbuf->b_visual_mode_eval; str[1] = NUL; rettv->vval.v_string = (char *)vim_strsave(str); @@ -11299,29 +10515,29 @@ static void f_winrestview(typval_T *argvars, typval_T *rettv, FunPtr fptr) } else { dictitem_T *di; if ((di = tv_dict_find(dict, S_LEN("lnum"))) != NULL) { - curwin->w_cursor.lnum = tv_get_number(&di->di_tv); + curwin->w_cursor.lnum = (linenr_T)tv_get_number(&di->di_tv); } if ((di = tv_dict_find(dict, S_LEN("col"))) != NULL) { - curwin->w_cursor.col = tv_get_number(&di->di_tv); + curwin->w_cursor.col = (colnr_T)tv_get_number(&di->di_tv); } if ((di = tv_dict_find(dict, S_LEN("coladd"))) != NULL) { - curwin->w_cursor.coladd = tv_get_number(&di->di_tv); + curwin->w_cursor.coladd = (colnr_T)tv_get_number(&di->di_tv); } if ((di = tv_dict_find(dict, S_LEN("curswant"))) != NULL) { - curwin->w_curswant = tv_get_number(&di->di_tv); + curwin->w_curswant = (colnr_T)tv_get_number(&di->di_tv); curwin->w_set_curswant = false; } if ((di = tv_dict_find(dict, S_LEN("topline"))) != NULL) { - set_topline(curwin, tv_get_number(&di->di_tv)); + set_topline(curwin, (linenr_T)tv_get_number(&di->di_tv)); } if ((di = tv_dict_find(dict, S_LEN("topfill"))) != NULL) { - curwin->w_topfill = tv_get_number(&di->di_tv); + curwin->w_topfill = (int)tv_get_number(&di->di_tv); } if ((di = tv_dict_find(dict, S_LEN("leftcol"))) != NULL) { - curwin->w_leftcol = tv_get_number(&di->di_tv); + curwin->w_leftcol = (colnr_T)tv_get_number(&di->di_tv); } if ((di = tv_dict_find(dict, S_LEN("skipcol"))) != NULL) { - curwin->w_skipcol = tv_get_number(&di->di_tv); + curwin->w_skipcol = (colnr_T)tv_get_number(&di->di_tv); } check_cursor(); diff --git a/src/nvim/eval/funcs.h b/src/nvim/eval/funcs.h index 5f8d81c989..583ee0e75e 100644 --- a/src/nvim/eval/funcs.h +++ b/src/nvim/eval/funcs.h @@ -4,8 +4,6 @@ #include "nvim/buffer_defs.h" #include "nvim/eval/typval.h" -typedef void (*FunPtr)(void); - /// Prototype of C function that implements VimL function typedef void (*VimLFunc)(typval_T *args, typval_T *rvar, FunPtr data); diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index e19cf411c0..fd57b45e86 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -19,6 +19,7 @@ #include "nvim/eval/typval.h" #include "nvim/eval/typval_encode.h" #include "nvim/eval/userfunc.h" +#include "nvim/eval/vars.h" #include "nvim/garray.h" #include "nvim/gettext.h" #include "nvim/globals.h" @@ -829,6 +830,454 @@ int tv_list_join(garray_T *const gap, list_T *const l, const char *const sep) return retval; } +/// "join()" function +void f_join(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + if (argvars[0].v_type != VAR_LIST) { + emsg(_(e_listreq)); + return; + } + const char *const sep = (argvars[1].v_type == VAR_UNKNOWN + ? " " + : tv_get_string_chk(&argvars[1])); + + rettv->v_type = VAR_STRING; + + if (sep != NULL) { + garray_T ga; + ga_init(&ga, (int)sizeof(char), 80); + tv_list_join(&ga, argvars[0].vval.v_list, sep); + ga_append(&ga, NUL); + rettv->vval.v_string = ga.ga_data; + } else { + rettv->vval.v_string = NULL; + } +} + +/// "list2str()" function +void f_list2str(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + garray_T ga; + + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; + if (argvars[0].v_type != VAR_LIST) { + emsg(_(e_invarg)); + return; + } + + list_T *const l = argvars[0].vval.v_list; + if (l == NULL) { + return; // empty list results in empty string + } + + ga_init(&ga, 1, 80); + char buf[MB_MAXBYTES + 1]; + + TV_LIST_ITER_CONST(l, li, { + buf[utf_char2bytes((int)tv_get_number(TV_LIST_ITEM_TV(li)), (char *)buf)] = NUL; + ga_concat(&ga, (char *)buf); + }); + ga_append(&ga, NUL); + + rettv->vval.v_string = ga.ga_data; +} + +/// "remove({list})" function +void tv_list_remove(typval_T *argvars, typval_T *rettv, const char *arg_errmsg) +{ + list_T *l; + bool error = false; + + if (var_check_lock(tv_list_locked((l = argvars[0].vval.v_list)), + arg_errmsg, TV_TRANSLATE)) { + return; + } + + long idx = tv_get_number_chk(&argvars[1], &error); + + listitem_T *item; + + if (error) { + // Type error: do nothing, errmsg already given. + } else if ((item = tv_list_find(l, (int)idx)) == NULL) { + semsg(_(e_listidx), (int64_t)idx); + } else { + if (argvars[2].v_type == VAR_UNKNOWN) { + // Remove one item, return its value. + tv_list_drop_items(l, item, item); + *rettv = *TV_LIST_ITEM_TV(item); + xfree(item); + } else { + listitem_T *item2; + // Remove range of items, return list with values. + long end = tv_get_number_chk(&argvars[2], &error); + if (error) { + // Type error: do nothing. + } else if ((item2 = tv_list_find(l, (int)end)) == NULL) { + semsg(_(e_listidx), (int64_t)end); + } else { + int cnt = 0; + + listitem_T *li; + for (li = item; li != NULL; li = TV_LIST_ITEM_NEXT(l, li)) { + cnt++; + if (li == item2) { + break; + } + } + if (li == NULL) { // Didn't find "item2" after "item". + emsg(_(e_invrange)); + } else { + tv_list_move_items(l, item, item2, tv_list_alloc_ret(rettv, cnt), + cnt); + } + } + } + } +} + +/// struct storing information about current sort +typedef struct { + int item_compare_ic; + bool item_compare_lc; + bool item_compare_numeric; + bool item_compare_numbers; + bool item_compare_float; + const char *item_compare_func; + partial_T *item_compare_partial; + dict_T *item_compare_selfdict; + bool item_compare_func_err; +} sortinfo_T; +static sortinfo_T *sortinfo = NULL; + +#define ITEM_COMPARE_FAIL 999 + +/// Compare functions for f_sort() and f_uniq() below. +static int item_compare(const void *s1, const void *s2, bool keep_zero) +{ + ListSortItem *const si1 = (ListSortItem *)s1; + ListSortItem *const si2 = (ListSortItem *)s2; + + typval_T *const tv1 = TV_LIST_ITEM_TV(si1->item); + typval_T *const tv2 = TV_LIST_ITEM_TV(si2->item); + + int res; + + if (sortinfo->item_compare_numbers) { + const varnumber_T v1 = tv_get_number(tv1); + const varnumber_T v2 = tv_get_number(tv2); + + res = v1 == v2 ? 0 : v1 > v2 ? 1 : -1; + goto item_compare_end; + } + + if (sortinfo->item_compare_float) { + const float_T v1 = tv_get_float(tv1); + const float_T v2 = tv_get_float(tv2); + + res = v1 == v2 ? 0 : v1 > v2 ? 1 : -1; + goto item_compare_end; + } + + char *tofree1 = NULL; + char *tofree2 = NULL; + char *p1; + char *p2; + + // encode_tv2string() puts quotes around a string and allocates memory. Don't + // do that for string variables. Use a single quote when comparing with + // a non-string to do what the docs promise. + if (tv1->v_type == VAR_STRING) { + if (tv2->v_type != VAR_STRING || sortinfo->item_compare_numeric) { + p1 = "'"; + } else { + p1 = tv1->vval.v_string; + } + } else { + tofree1 = p1 = encode_tv2string(tv1, NULL); + } + if (tv2->v_type == VAR_STRING) { + if (tv1->v_type != VAR_STRING || sortinfo->item_compare_numeric) { + p2 = "'"; + } else { + p2 = tv2->vval.v_string; + } + } else { + tofree2 = p2 = encode_tv2string(tv2, NULL); + } + if (p1 == NULL) { + p1 = ""; + } + if (p2 == NULL) { + p2 = ""; + } + if (!sortinfo->item_compare_numeric) { + if (sortinfo->item_compare_lc) { + res = strcoll(p1, p2); + } else { + res = sortinfo->item_compare_ic ? STRICMP(p1, p2): STRCMP(p1, p2); + } + } else { + double n1, n2; + n1 = strtod(p1, &p1); + n2 = strtod(p2, &p2); + res = n1 == n2 ? 0 : n1 > n2 ? 1 : -1; + } + + xfree(tofree1); + xfree(tofree2); + +item_compare_end: + // When the result would be zero, compare the item indexes. Makes the + // sort stable. + if (res == 0 && !keep_zero) { + // WARNING: When using uniq si1 and si2 are actually listitem_T **, no + // indexes are there. + res = si1->idx > si2->idx ? 1 : -1; + } + return res; +} + +static int item_compare_keeping_zero(const void *s1, const void *s2) +{ + return item_compare(s1, s2, true); +} + +static int item_compare_not_keeping_zero(const void *s1, const void *s2) +{ + return item_compare(s1, s2, false); +} + +static int item_compare2(const void *s1, const void *s2, bool keep_zero) +{ + ListSortItem *si1, *si2; + int res; + typval_T rettv; + typval_T argv[3]; + const char *func_name; + partial_T *partial = sortinfo->item_compare_partial; + + // shortcut after failure in previous call; compare all items equal + if (sortinfo->item_compare_func_err) { + return 0; + } + + si1 = (ListSortItem *)s1; + si2 = (ListSortItem *)s2; + + if (partial == NULL) { + func_name = sortinfo->item_compare_func; + } else { + func_name = (const char *)partial_name(partial); + } + + // Copy the values. This is needed to be able to set v_lock to VAR_FIXED + // in the copy without changing the original list items. + tv_copy(TV_LIST_ITEM_TV(si1->item), &argv[0]); + tv_copy(TV_LIST_ITEM_TV(si2->item), &argv[1]); + + rettv.v_type = VAR_UNKNOWN; // tv_clear() uses this + funcexe_T funcexe = FUNCEXE_INIT; + funcexe.evaluate = true; + funcexe.partial = partial; + funcexe.selfdict = sortinfo->item_compare_selfdict; + res = call_func(func_name, -1, &rettv, 2, argv, &funcexe); + tv_clear(&argv[0]); + tv_clear(&argv[1]); + + if (res == FAIL) { + res = ITEM_COMPARE_FAIL; + } else { + res = (int)tv_get_number_chk(&rettv, &sortinfo->item_compare_func_err); + if (res > 0) { + res = 1; + } else if (res < 0) { + res = -1; + } + } + if (sortinfo->item_compare_func_err) { + res = ITEM_COMPARE_FAIL; // return value has wrong type + } + tv_clear(&rettv); + + // When the result would be zero, compare the pointers themselves. Makes + // the sort stable. + if (res == 0 && !keep_zero) { + // WARNING: When using uniq si1 and si2 are actually listitem_T **, no + // indexes are there. + res = si1->idx > si2->idx ? 1 : -1; + } + + return res; +} + +static int item_compare2_keeping_zero(const void *s1, const void *s2) +{ + return item_compare2(s1, s2, true); +} + +static int item_compare2_not_keeping_zero(const void *s1, const void *s2) +{ + return item_compare2(s1, s2, false); +} + +/// "sort({list})" function +static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) +{ + ListSortItem *ptrs; + long len; + long i; + + // Pointer to current info struct used in compare function. Save and restore + // the current one for nested calls. + sortinfo_T info; + sortinfo_T *old_sortinfo = sortinfo; + sortinfo = &info; + + const char *const arg_errmsg = (sort + ? N_("sort() argument") + : N_("uniq() argument")); + + if (argvars[0].v_type != VAR_LIST) { + semsg(_(e_listarg), sort ? "sort()" : "uniq()"); + } else { + list_T *const l = argvars[0].vval.v_list; + if (var_check_lock(tv_list_locked(l), arg_errmsg, TV_TRANSLATE)) { + goto theend; + } + tv_list_set_ret(rettv, l); + + len = tv_list_len(l); + if (len <= 1) { + goto theend; // short list sorts pretty quickly + } + + info.item_compare_ic = false; + info.item_compare_lc = false; + info.item_compare_numeric = false; + info.item_compare_numbers = false; + info.item_compare_float = false; + info.item_compare_func = NULL; + info.item_compare_partial = NULL; + info.item_compare_selfdict = NULL; + + if (argvars[1].v_type != VAR_UNKNOWN) { + // optional second argument: {func} + if (argvars[1].v_type == VAR_FUNC) { + info.item_compare_func = (const char *)argvars[1].vval.v_string; + } else if (argvars[1].v_type == VAR_PARTIAL) { + info.item_compare_partial = argvars[1].vval.v_partial; + } else { + bool error = false; + + i = tv_get_number_chk(&argvars[1], &error); + if (error) { + goto theend; // type error; errmsg already given + } + if (i == 1) { + info.item_compare_ic = true; + } else if (argvars[1].v_type != VAR_NUMBER) { + info.item_compare_func = tv_get_string(&argvars[1]); + } else if (i != 0) { + emsg(_(e_invarg)); + goto theend; + } + if (info.item_compare_func != NULL) { + if (*info.item_compare_func == NUL) { + // empty string means default sort + info.item_compare_func = NULL; + } else if (strcmp(info.item_compare_func, "n") == 0) { + info.item_compare_func = NULL; + info.item_compare_numeric = true; + } else if (strcmp(info.item_compare_func, "N") == 0) { + info.item_compare_func = NULL; + info.item_compare_numbers = true; + } else if (strcmp(info.item_compare_func, "f") == 0) { + info.item_compare_func = NULL; + info.item_compare_float = true; + } else if (strcmp(info.item_compare_func, "i") == 0) { + info.item_compare_func = NULL; + info.item_compare_ic = true; + } else if (strcmp(info.item_compare_func, "l") == 0) { + info.item_compare_func = NULL; + info.item_compare_lc = true; + } + } + } + + if (argvars[2].v_type != VAR_UNKNOWN) { + // optional third argument: {dict} + if (argvars[2].v_type != VAR_DICT) { + emsg(_(e_dictreq)); + goto theend; + } + info.item_compare_selfdict = argvars[2].vval.v_dict; + } + } + + // Make an array with each entry pointing to an item in the List. + ptrs = xmalloc((size_t)((unsigned)len * sizeof(ListSortItem))); + + if (sort) { + info.item_compare_func_err = false; + tv_list_item_sort(l, ptrs, + ((info.item_compare_func == NULL + && info.item_compare_partial == NULL) + ? item_compare_not_keeping_zero + : item_compare2_not_keeping_zero), + &info.item_compare_func_err); + if (info.item_compare_func_err) { + emsg(_("E702: Sort compare function failed")); + } + } else { + ListSorter item_compare_func_ptr; + + // f_uniq(): ptrs will be a stack of items to remove. + info.item_compare_func_err = false; + if (info.item_compare_func != NULL + || info.item_compare_partial != NULL) { + item_compare_func_ptr = item_compare2_keeping_zero; + } else { + item_compare_func_ptr = item_compare_keeping_zero; + } + + int idx = 0; + for (listitem_T *li = TV_LIST_ITEM_NEXT(l, tv_list_first(l)) + ; li != NULL;) { + listitem_T *const prev_li = TV_LIST_ITEM_PREV(l, li); + if (item_compare_func_ptr(&prev_li, &li) == 0) { + if (info.item_compare_func_err) { // -V547 + emsg(_("E882: Uniq compare function failed")); + break; + } + li = tv_list_item_remove(l, li); + } else { + idx++; + li = TV_LIST_ITEM_NEXT(l, li); + } + } + } + + xfree(ptrs); + } + +theend: + sortinfo = old_sortinfo; +} + +/// "sort"({list})" function +void f_sort(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + do_sort_uniq(argvars, rettv, true); +} + +/// "uniq({list})" function +void f_uniq(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + do_sort_uniq(argvars, rettv, false); +} + /// Check whether two lists are equal /// /// @param[in] l1 First list to compare. @@ -2199,6 +2648,66 @@ bool tv_blob_equal(const blob_T *const b1, const blob_T *const b2) return true; } +/// "remove({blob})" function +void tv_blob_remove(typval_T *argvars, typval_T *rettv, const char *arg_errmsg) +{ + blob_T *const b = argvars[0].vval.v_blob; + + if (b != NULL && var_check_lock(b->bv_lock, arg_errmsg, TV_TRANSLATE)) { + return; + } + + bool error = false; + long idx = tv_get_number_chk(&argvars[1], &error); + + if (!error) { + const int len = tv_blob_len(b); + + if (idx < 0) { + // count from the end + idx = len + idx; + } + if (idx < 0 || idx >= len) { + semsg(_(e_blobidx), (int64_t)idx); + return; + } + if (argvars[2].v_type == VAR_UNKNOWN) { + // Remove one item, return its value. + char_u *const p = (char_u *)b->bv_ga.ga_data; + rettv->vval.v_number = (varnumber_T)(*(p + idx)); + memmove(p + idx, p + idx + 1, (size_t)(len - idx - 1)); + b->bv_ga.ga_len--; + } else { + // Remove range of items, return blob with values. + long end = tv_get_number_chk(&argvars[2], &error); + if (error) { + return; + } + if (end < 0) { + // count from the end + end = len + end; + } + if (end >= len || idx > end) { + semsg(_(e_blobidx), (int64_t)end); + return; + } + blob_T *const blob = tv_blob_alloc(); + blob->bv_ga.ga_len = (int)(end - idx + 1); + ga_grow(&blob->bv_ga, (int)(end - idx + 1)); + + char_u *const p = (char_u *)b->bv_ga.ga_data; + memmove((char_u *)blob->bv_ga.ga_data, p + idx, + (size_t)(end - idx + 1)); + tv_blob_set_ret(rettv, blob); + + if (len - end - 1 > 0) { + memmove(p + idx, p + end + 1, (size_t)(len - end - 1)); + } + b->bv_ga.ga_len -= (int)(end - idx + 1); + } + } +} + //{{{1 Generic typval operations //{{{2 Init/alloc/clear //{{{3 Alloc @@ -2243,6 +2752,118 @@ void tv_dict_alloc_ret(typval_T *const ret_tv) tv_dict_set_ret(ret_tv, d); } +/// Turn a dictionary into a list +/// +/// @param[in] tv Dictionary to convert. Is checked for actually being +/// a dictionary, will give an error if not. +/// @param[out] rettv Location where result will be saved. +/// @param[in] what What to save in rettv. +static void tv_dict_list(typval_T *const tv, typval_T *const rettv, const DictListType what) +{ + if (tv->v_type != VAR_DICT) { + emsg(_(e_dictreq)); + return; + } + if (tv->vval.v_dict == NULL) { + return; + } + + tv_list_alloc_ret(rettv, tv_dict_len(tv->vval.v_dict)); + + TV_DICT_ITER(tv->vval.v_dict, di, { + typval_T tv_item = { .v_lock = VAR_UNLOCKED }; + + switch (what) { + case kDictListKeys: + tv_item.v_type = VAR_STRING; + tv_item.vval.v_string = (char *)vim_strsave(di->di_key); + break; + case kDictListValues: + tv_copy(&di->di_tv, &tv_item); + break; + case kDictListItems: { + // items() + list_T *const sub_l = tv_list_alloc(2); + tv_item.v_type = VAR_LIST; + tv_item.vval.v_list = sub_l; + tv_list_ref(sub_l); + + tv_list_append_owned_tv(sub_l, (typval_T) { + .v_type = VAR_STRING, + .v_lock = VAR_UNLOCKED, + .vval.v_string = xstrdup((const char *)di->di_key), + }); + + tv_list_append_tv(sub_l, &di->di_tv); + + break; + } + } + + tv_list_append_owned_tv(rettv->vval.v_list, tv_item); + }); +} + +/// "items(dict)" function +void f_items(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + tv_dict_list(argvars, rettv, 2); +} + +/// "keys()" function +void f_keys(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + tv_dict_list(argvars, rettv, 0); +} + +/// "values(dict)" function +void f_values(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + tv_dict_list(argvars, rettv, 1); +} + +/// "has_key()" function +void f_has_key(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + if (argvars[0].v_type != VAR_DICT) { + emsg(_(e_dictreq)); + return; + } + if (argvars[0].vval.v_dict == NULL) { + return; + } + + rettv->vval.v_number = tv_dict_find(argvars[0].vval.v_dict, + tv_get_string(&argvars[1]), + -1) != NULL; +} + +/// "remove({dict})" function +void tv_dict_remove(typval_T *argvars, typval_T *rettv, const char *arg_errmsg) +{ + dict_T *d; + if (argvars[2].v_type != VAR_UNKNOWN) { + semsg(_(e_toomanyarg), "remove()"); + } else if ((d = argvars[0].vval.v_dict) != NULL + && !var_check_lock(d->dv_lock, arg_errmsg, TV_TRANSLATE)) { + const char *key = tv_get_string_chk(&argvars[1]); + if (key != NULL) { + dictitem_T *di = tv_dict_find(d, key, -1); + if (di == NULL) { + semsg(_(e_dictkey), key); + } else if (!var_check_fixed(di->di_flags, arg_errmsg, TV_TRANSLATE) + && !var_check_ro(di->di_flags, arg_errmsg, TV_TRANSLATE)) { + *rettv = di->di_tv; + di->di_tv = TV_INITIAL_VALUE; + tv_dict_item_remove(d, di); + if (tv_dict_is_watched(d)) { + tv_dict_watcher_notify(d, key, NULL, rettv); + } + } + } + } +} + /// Allocate an empty blob for a return value. /// /// Also sets reference count. diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c index c2579944e4..a90148bf23 100644 --- a/src/nvim/eval/userfunc.c +++ b/src/nvim/eval/userfunc.c @@ -9,13 +9,16 @@ #include "nvim/edit.h" #include "nvim/eval.h" #include "nvim/eval/encode.h" +#include "nvim/eval/funcs.h" #include "nvim/eval/userfunc.h" +#include "nvim/eval/vars.h" #include "nvim/ex_cmds2.h" #include "nvim/ex_docmd.h" #include "nvim/ex_getln.h" #include "nvim/fileio.h" #include "nvim/getchar.h" #include "nvim/globals.h" +#include "nvim/insexpand.h" #include "nvim/lua/executor.h" #include "nvim/os/input.h" #include "nvim/regexp.h" @@ -1265,7 +1268,7 @@ void free_all_functions(void) // Clean up the current_funccal chain and the funccal stack. while (current_funccal != NULL) { tv_clear(current_funccal->rettv); - cleanup_function_call(current_funccal); + cleanup_function_call(current_funccal); // -V595 if (current_funccal == NULL && funccal_stack != NULL) { restore_funccal(); } diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c new file mode 100644 index 0000000000..d01fff6b94 --- /dev/null +++ b/src/nvim/eval/vars.c @@ -0,0 +1,1822 @@ +// This is an open source non-commercial project. Dear PVS-Studio, please check +// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com + +// eval/vars.c: functions for dealing with variables + +#include "nvim/ascii.h" +#include "nvim/autocmd.h" +#include "nvim/buffer.h" +#include "nvim/charset.h" +#include "nvim/eval.h" +#include "nvim/eval/encode.h" +#include "nvim/eval/funcs.h" +#include "nvim/eval/typval.h" +#include "nvim/eval/userfunc.h" +#include "nvim/eval/vars.h" +#include "nvim/ex_cmds.h" +#include "nvim/ex_docmd.h" +#include "nvim/ops.h" +#include "nvim/option.h" +#include "nvim/search.h" +#include "nvim/window.h" + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "eval/vars.c.generated.h" +#endif + +// TODO(ZyX-I): Remove DICT_MAXNEST, make users be non-recursive instead + +#define DICT_MAXNEST 100 // maximum nesting of lists and dicts + +static char *e_letunexp = N_("E18: Unexpected characters in :let"); +static char *e_lock_unlock = N_("E940: Cannot lock or unlock variable %s"); + +/// Get a list of lines from a HERE document. The here document is a list of +/// lines surrounded by a marker. +/// cmd << {marker} +/// {line1} +/// {line2} +/// .... +/// {marker} +/// +/// The {marker} is a string. If the optional 'trim' word is supplied before the +/// marker, then the leading indentation before the lines (matching the +/// indentation in the 'cmd' line) is stripped. +/// +/// @return a List with {lines} or NULL. +static list_T *heredoc_get(exarg_T *eap, char *cmd) +{ + char *marker; + char *p; + int marker_indent_len = 0; + int text_indent_len = 0; + char *text_indent = NULL; + + if (eap->getline == NULL) { + emsg(_("E991: cannot use =<< here")); + return NULL; + } + + // Check for the optional 'trim' word before the marker + cmd = skipwhite(cmd); + if (STRNCMP(cmd, "trim", 4) == 0 + && (cmd[4] == NUL || ascii_iswhite(cmd[4]))) { + cmd = skipwhite(cmd + 4); + + // Trim the indentation from all the lines in the here document. + // The amount of indentation trimmed is the same as the indentation of + // the first line after the :let command line. To find the end marker + // the indent of the :let command line is trimmed. + p = *eap->cmdlinep; + while (ascii_iswhite(*p)) { + p++; + marker_indent_len++; + } + text_indent_len = -1; + } + + // The marker is the next word. + if (*cmd != NUL && *cmd != '"') { + marker = skipwhite(cmd); + p = (char *)skiptowhite((char_u *)marker); + if (*skipwhite(p) != NUL && *skipwhite(p) != '"') { + emsg(_(e_trailing)); + return NULL; + } + *p = NUL; + if (islower(*marker)) { + emsg(_("E221: Marker cannot start with lower case letter")); + return NULL; + } + } else { + emsg(_("E172: Missing marker")); + return NULL; + } + + list_T *l = tv_list_alloc(0); + for (;;) { + int mi = 0; + int ti = 0; + + char *theline = eap->getline(NUL, eap->cookie, 0, false); + if (theline == NULL) { + semsg(_("E990: Missing end marker '%s'"), marker); + break; + } + + // with "trim": skip the indent matching the :let line to find the + // marker + if (marker_indent_len > 0 + && STRNCMP(theline, *eap->cmdlinep, marker_indent_len) == 0) { + mi = marker_indent_len; + } + if (STRCMP(marker, theline + mi) == 0) { + xfree(theline); + break; + } + if (text_indent_len == -1 && *theline != NUL) { + // set the text indent from the first line. + p = theline; + text_indent_len = 0; + while (ascii_iswhite(*p)) { + p++; + text_indent_len++; + } + text_indent = xstrnsave(theline, (size_t)text_indent_len); + } + // with "trim": skip the indent matching the first line + if (text_indent != NULL) { + for (ti = 0; ti < text_indent_len; ti++) { + if (theline[ti] != text_indent[ti]) { + break; + } + } + } + + tv_list_append_string(l, theline + ti, -1); + xfree(theline); + } + xfree(text_indent); + + return l; +} + +/// ":let" list all variable values +/// ":let var1 var2" list variable values +/// ":let var = expr" assignment command. +/// ":let var += expr" assignment command. +/// ":let var -= expr" assignment command. +/// ":let var *= expr" assignment command. +/// ":let var /= expr" assignment command. +/// ":let var %= expr" assignment command. +/// ":let var .= expr" assignment command. +/// ":let var ..= expr" assignment command. +/// ":let [var1, var2] = expr" unpack list. +/// ":let [name, ..., ; lastname] = expr" unpack list. +void ex_let(exarg_T *eap) +{ + ex_let_const(eap, false); +} + +/// ":cons[t] var = expr1" define constant +/// ":cons[t] [name1, name2, ...] = expr1" define constants unpacking list +/// ":cons[t] [name, ..., ; lastname] = expr" define constants unpacking list +void ex_const(exarg_T *eap) +{ + ex_let_const(eap, true); +} + +static void ex_let_const(exarg_T *eap, const bool is_const) +{ + char *arg = eap->arg; + char *expr = NULL; + typval_T rettv; + int i; + int var_count = 0; + int semicolon = 0; + char op[2]; + char *argend; + int first = true; + + argend = (char *)skip_var_list(arg, &var_count, &semicolon); + if (argend == NULL) { + return; + } + if (argend > arg && argend[-1] == '.') { // For var.='str'. + argend--; + } + expr = skipwhite(argend); + if (*expr != '=' && !((vim_strchr("+-*/%.", *expr) != NULL + && expr[1] == '=') || STRNCMP(expr, "..=", 3) == 0)) { + // ":let" without "=": list variables + if (*arg == '[') { + emsg(_(e_invarg)); + } else if (!ends_excmd(*arg)) { + // ":let var1 var2" + arg = (char *)list_arg_vars(eap, (const char *)arg, &first); + } else if (!eap->skip) { + // ":let" + list_glob_vars(&first); + list_buf_vars(&first); + list_win_vars(&first); + list_tab_vars(&first); + list_script_vars(&first); + list_func_vars(&first); + list_vim_vars(&first); + } + eap->nextcmd = (char *)check_nextcmd((char_u *)arg); + } else if (expr[0] == '=' && expr[1] == '<' && expr[2] == '<') { + // HERE document + list_T *l = heredoc_get(eap, expr + 3); + if (l != NULL) { + tv_list_set_ret(&rettv, l); + if (!eap->skip) { + op[0] = '='; + op[1] = NUL; + (void)ex_let_vars(eap->arg, &rettv, false, semicolon, var_count, + is_const, (char *)op); + } + tv_clear(&rettv); + } + } else { + op[0] = '='; + op[1] = NUL; + if (*expr != '=') { + if (vim_strchr("+-*/%.", *expr) != NULL) { + op[0] = *expr; // +=, -=, *=, /=, %= or .= + if (expr[0] == '.' && expr[1] == '.') { // ..= + expr++; + } + } + expr = skipwhite(expr + 2); + } else { + expr = skipwhite(expr + 1); + } + + if (eap->skip) { + emsg_skip++; + } + i = eval0(expr, &rettv, &eap->nextcmd, !eap->skip); + if (eap->skip) { + if (i != FAIL) { + tv_clear(&rettv); + } + emsg_skip--; + } else if (i != FAIL) { + (void)ex_let_vars(eap->arg, &rettv, false, semicolon, var_count, + is_const, (char *)op); + tv_clear(&rettv); + } + } +} + +/// Assign the typevalue "tv" to the variable or variables at "arg_start". +/// Handles both "var" with any type and "[var, var; var]" with a list type. +/// When "op" is not NULL it points to a string with characters that +/// must appear after the variable(s). Use "+", "-" or "." for add, subtract +/// or concatenate. +/// +/// @param copy copy values from "tv", don't move +/// @param semicolon from skip_var_list() +/// @param var_count from skip_var_list() +/// @param is_const lock variables for :const +/// +/// @return OK or FAIL; +int ex_let_vars(char *arg_start, typval_T *tv, int copy, int semicolon, int var_count, int is_const, + char *op) +{ + char *arg = arg_start; + typval_T ltv; + + if (*arg != '[') { + // ":let var = expr" or ":for var in list" + if (ex_let_one(arg, tv, copy, is_const, op, op) == NULL) { + return FAIL; + } + return OK; + } + + // ":let [v1, v2] = list" or ":for [v1, v2] in listlist" + if (tv->v_type != VAR_LIST) { + emsg(_(e_listreq)); + return FAIL; + } + list_T *const l = tv->vval.v_list; + + const int len = tv_list_len(l); + if (semicolon == 0 && var_count < len) { + emsg(_("E687: Less targets than List items")); + return FAIL; + } + if (var_count - semicolon > len) { + emsg(_("E688: More targets than List items")); + return FAIL; + } + // List l may actually be NULL, but it should fail with E688 or even earlier + // if you try to do ":let [] = v:_null_list". + assert(l != NULL); + + listitem_T *item = tv_list_first(l); + size_t rest_len = (size_t)tv_list_len(l); + while (*arg != ']') { + arg = skipwhite(arg + 1); + arg = ex_let_one(arg, TV_LIST_ITEM_TV(item), true, is_const, ",;]", op); + if (arg == NULL) { + return FAIL; + } + rest_len--; + + item = TV_LIST_ITEM_NEXT(l, item); + arg = skipwhite(arg); + if (*arg == ';') { + // Put the rest of the list (may be empty) in the var after ';'. + // Create a new list for this. + list_T *const rest_list = tv_list_alloc((ptrdiff_t)rest_len); + while (item != NULL) { + tv_list_append_tv(rest_list, TV_LIST_ITEM_TV(item)); + item = TV_LIST_ITEM_NEXT(l, item); + } + + ltv.v_type = VAR_LIST; + ltv.v_lock = VAR_UNLOCKED; + ltv.vval.v_list = rest_list; + tv_list_ref(rest_list); + + arg = ex_let_one(skipwhite(arg + 1), <v, false, is_const, "]", op); + tv_clear(<v); + if (arg == NULL) { + return FAIL; + } + break; + } else if (*arg != ',' && *arg != ']') { + internal_error("ex_let_vars()"); + return FAIL; + } + } + + return OK; +} + +/// Skip over assignable variable "var" or list of variables "[var, var]". +/// Used for ":let varvar = expr" and ":for varvar in expr". +/// For "[var, var]" increment "*var_count" for each variable. +/// for "[var, var; var]" set "semicolon". +/// +/// @return NULL for an error. +const char *skip_var_list(const char *arg, int *var_count, int *semicolon) +{ + const char *p; + const char *s; + + if (*arg == '[') { + // "[var, var]": find the matching ']'. + p = arg; + for (;;) { + p = skipwhite(p + 1); // skip whites after '[', ';' or ',' + s = skip_var_one((char *)p); + if (s == p) { + semsg(_(e_invarg2), p); + return NULL; + } + (*var_count)++; + + p = skipwhite(s); + if (*p == ']') { + break; + } else if (*p == ';') { + if (*semicolon == 1) { + emsg(_("E452: Double ; in list of variables")); + return NULL; + } + *semicolon = 1; + } else if (*p != ',') { + semsg(_(e_invarg2), p); + return NULL; + } + } + return p + 1; + } else { + return skip_var_one((char *)arg); + } +} + +/// Skip one (assignable) variable name, including @r, $VAR, &option, d.key, +/// l[idx]. +static const char *skip_var_one(const char *arg) +{ + if (*arg == '@' && arg[1] != NUL) { + return arg + 1 + utfc_ptr2len(arg + 1); + } + return (char *)find_name_end(*arg == '$' || *arg == '&' ? arg + 1 : arg, + NULL, NULL, FNE_INCL_BR | FNE_CHECK_START); +} + +/// List variables for hashtab "ht" with prefix "prefix". +/// +/// @param empty if true also list NULL strings as empty strings. +void list_hashtable_vars(hashtab_T *ht, const char *prefix, int empty, int *first) +{ + hashitem_T *hi; + dictitem_T *di; + int todo; + + todo = (int)ht->ht_used; + for (hi = ht->ht_array; todo > 0 && !got_int; hi++) { + if (!HASHITEM_EMPTY(hi)) { + todo--; + di = TV_DICT_HI2DI(hi); + char buf[IOSIZE]; + + // apply :filter /pat/ to variable name + xstrlcpy(buf, prefix, IOSIZE); + xstrlcat(buf, (char *)di->di_key, IOSIZE); + if (message_filtered((char_u *)buf)) { + continue; + } + + if (empty || di->di_tv.v_type != VAR_STRING + || di->di_tv.vval.v_string != NULL) { + list_one_var(di, prefix, first); + } + } + } +} + +/// List global variables. +static void list_glob_vars(int *first) +{ + list_hashtable_vars(&globvarht, "", true, first); +} + +/// List buffer variables. +static void list_buf_vars(int *first) +{ + list_hashtable_vars(&curbuf->b_vars->dv_hashtab, "b:", true, first); +} + +/// List window variables. +static void list_win_vars(int *first) +{ + list_hashtable_vars(&curwin->w_vars->dv_hashtab, "w:", true, first); +} + +/// List tab page variables. +static void list_tab_vars(int *first) +{ + list_hashtable_vars(&curtab->tp_vars->dv_hashtab, "t:", true, first); +} + +/// List variables in "arg". +static const char *list_arg_vars(exarg_T *eap, const char *arg, int *first) +{ + int error = false; + int len; + const char *name; + const char *name_start; + typval_T tv; + + while (!ends_excmd(*arg) && !got_int) { + if (error || eap->skip) { + arg = find_name_end(arg, NULL, NULL, FNE_INCL_BR | FNE_CHECK_START); + if (!ascii_iswhite(*arg) && !ends_excmd(*arg)) { + emsg_severe = true; + emsg(_(e_trailing)); + break; + } + } else { + // get_name_len() takes care of expanding curly braces + name_start = name = arg; + char *tofree; + len = get_name_len(&arg, &tofree, true, true); + if (len <= 0) { + // This is mainly to keep test 49 working: when expanding + // curly braces fails overrule the exception error message. + if (len < 0 && !aborting()) { + emsg_severe = true; + semsg(_(e_invarg2), arg); + break; + } + error = true; + } else { + if (tofree != NULL) { + name = tofree; + } + if (get_var_tv(name, len, &tv, NULL, true, false) + == FAIL) { + error = true; + } else { + // handle d.key, l[idx], f(expr) + const char *const arg_subsc = arg; + if (handle_subscript(&arg, &tv, true, true, name, &name) == FAIL) { + error = true; + } else { + if (arg == arg_subsc && len == 2 && name[1] == ':') { + switch (*name) { + case 'g': + list_glob_vars(first); break; + case 'b': + list_buf_vars(first); break; + case 'w': + list_win_vars(first); break; + case 't': + list_tab_vars(first); break; + case 'v': + list_vim_vars(first); break; + case 's': + list_script_vars(first); break; + case 'l': + list_func_vars(first); break; + default: + semsg(_("E738: Can't list variables for %s"), name); + } + } else { + char *const s = encode_tv2echo(&tv, NULL); + const char *const used_name = (arg == arg_subsc + ? name + : name_start); + assert(used_name != NULL); + const ptrdiff_t name_size = (used_name == tofree + ? (ptrdiff_t)strlen(used_name) + : (arg - used_name)); + list_one_var_a("", used_name, name_size, + tv.v_type, s == NULL ? "" : s, first); + xfree(s); + } + tv_clear(&tv); + } + } + } + + xfree(tofree); + } + + arg = (const char *)skipwhite(arg); + } + + return arg; +} + +// TODO(ZyX-I): move to eval/ex_cmds + +/// Set one item of `:let var = expr` or `:let [v1, v2] = list` to its value +/// +/// @param[in] arg Start of the variable name. +/// @param[in] tv Value to assign to the variable. +/// @param[in] copy If true, copy value from `tv`. +/// @param[in] endchars Valid characters after variable name or NULL. +/// @param[in] op Operation performed: *op is `+`, `-`, `.` for `+=`, etc. +/// NULL for `=`. +/// +/// @return a pointer to the char just after the var name or NULL in case of +/// error. +static char *ex_let_one(char *arg, typval_T *const tv, const bool copy, const bool is_const, + const char *const endchars, const char *const op) + FUNC_ATTR_NONNULL_ARG(1, 2) FUNC_ATTR_WARN_UNUSED_RESULT +{ + char *arg_end = NULL; + int len; + int opt_flags; + char *tofree = NULL; + + // ":let $VAR = expr": Set environment variable. + if (*arg == '$') { + if (is_const) { + emsg(_("E996: Cannot lock an environment variable")); + return NULL; + } + // Find the end of the name. + arg++; + char *name = arg; + len = get_env_len((const char **)&arg); + if (len == 0) { + semsg(_(e_invarg2), name - 1); + } else { + if (op != NULL && vim_strchr("+-*/%", *op) != NULL) { + semsg(_(e_letwrong), op); + } else if (endchars != NULL + && vim_strchr(endchars, *skipwhite(arg)) == NULL) { + emsg(_(e_letunexp)); + } else if (!check_secure()) { + const char c1 = name[len]; + name[len] = NUL; + const char *p = tv_get_string_chk(tv); + if (p != NULL && op != NULL && *op == '.') { + char *s = vim_getenv(name); + + if (s != NULL) { + tofree = (char *)concat_str((const char_u *)s, (const char_u *)p); + p = (const char *)tofree; + xfree(s); + } + } + if (p != NULL) { + os_setenv(name, p, 1); + if (STRICMP(name, "HOME") == 0) { + init_homedir(); + } else if (didset_vim && STRICMP(name, "VIM") == 0) { + didset_vim = false; + } else if (didset_vimruntime + && STRICMP(name, "VIMRUNTIME") == 0) { + didset_vimruntime = false; + } + arg_end = arg; + } + name[len] = c1; + xfree(tofree); + } + } + // ":let &option = expr": Set option value. + // ":let &l:option = expr": Set local option value. + // ":let &g:option = expr": Set global option value. + } else if (*arg == '&') { + if (is_const) { + emsg(_("E996: Cannot lock an option")); + return NULL; + } + // Find the end of the name. + char *const p = (char *)find_option_end((const char **)&arg, &opt_flags); + if (p == NULL + || (endchars != NULL + && vim_strchr(endchars, *skipwhite(p)) == NULL)) { + emsg(_(e_letunexp)); + } else { + varnumber_T n = 0; + getoption_T opt_type; + long numval; + char *stringval = NULL; + const char *s = NULL; + bool failed = false; + + const char c1 = *p; + *p = NUL; + + opt_type = get_option_value(arg, &numval, &stringval, opt_flags); + if (opt_type == gov_bool + || opt_type == gov_number + || opt_type == gov_hidden_bool + || opt_type == gov_hidden_number) { + // number, possibly hidden + n = (long)tv_get_number(tv); + } + + // Avoid setting a string option to the text "v:false" or similar. + if (tv->v_type != VAR_BOOL && tv->v_type != VAR_SPECIAL) { + s = tv_get_string_chk(tv); + } + + if (op != NULL && *op != '=') { + if (((opt_type == gov_bool || opt_type == gov_number) && *op == '.') + || (opt_type == gov_string && *op != '.')) { + semsg(_(e_letwrong), op); + failed = true; // don't set the value + } else { + // number or bool + if (opt_type == gov_number || opt_type == gov_bool) { + switch (*op) { + case '+': + n = numval + n; break; + case '-': + n = numval - n; break; + case '*': + n = numval * n; break; + case '/': + n = num_divide(numval, n); break; + case '%': + n = num_modulus(numval, n); break; + } + s = NULL; + } else if (opt_type == gov_string && stringval != NULL && s != NULL) { + // string + char *const oldstringval = stringval; + stringval = (char *)concat_str((const char_u *)stringval, + (const char_u *)s); + xfree(oldstringval); + s = stringval; + } + } + } + + if (!failed) { + if (opt_type != gov_string || s != NULL) { + set_option_value(arg, n, s, opt_flags); + arg_end = p; + } else { + emsg(_(e_stringreq)); + } + } + *p = c1; + xfree(stringval); + } + // ":let @r = expr": Set register contents. + } else if (*arg == '@') { + if (is_const) { + emsg(_("E996: Cannot lock a register")); + return NULL; + } + arg++; + + int regname = utf_ptr2char(arg); + int mblen = utf_ptr2len(arg); + + if (op != NULL && vim_strchr("+-*/%", *op) != NULL) { + semsg(_(e_letwrong), op); + } else if (endchars != NULL + && vim_strchr(endchars, *skipwhite(arg + mblen)) == NULL) { + emsg(_(e_letunexp)); + } else { + char *s; + + char *ptofree = NULL; + const char *p = tv_get_string_chk(tv); + if (p != NULL && op != NULL && *op == '.') { + s = get_reg_contents(*arg == '@' ? '"' : regname, kGRegExprSrc); + if (s != NULL) { + ptofree = (char *)concat_str((char_u *)s, (const char_u *)p); + p = (const char *)ptofree; + xfree(s); + } + } + if (p != NULL) { + write_reg_contents(*arg == '@' ? '"' : regname, + (const char_u *)p, (ssize_t)STRLEN(p), false); + arg_end = arg + mblen; + } + xfree(ptofree); + } + // ":let var = expr": Set internal variable. + // ":let {expr} = expr": Idem, name made with curly braces + } else if (eval_isnamec1(*arg) || *arg == '{') { + lval_T lv; + + char *const p = get_lval(arg, tv, &lv, false, false, 0, FNE_CHECK_START); + if (p != NULL && lv.ll_name != NULL) { + if (endchars != NULL && vim_strchr(endchars, *skipwhite(p)) == NULL) { + emsg(_(e_letunexp)); + } else { + set_var_lval(&lv, p, tv, copy, is_const, op); + arg_end = p; + } + } + clear_lval(&lv); + } else { + semsg(_(e_invarg2), arg); + } + + return arg_end; +} + +/// ":unlet[!] var1 ... " command. +void ex_unlet(exarg_T *eap) +{ + ex_unletlock(eap, eap->arg, 0, do_unlet_var); +} + +// TODO(ZyX-I): move to eval/ex_cmds + +/// ":lockvar" and ":unlockvar" commands +void ex_lockvar(exarg_T *eap) +{ + char *arg = eap->arg; + int deep = 2; + + if (eap->forceit) { + deep = -1; + } else if (ascii_isdigit(*arg)) { + deep = getdigits_int(&arg, false, -1); + arg = skipwhite(arg); + } + + ex_unletlock(eap, arg, deep, do_lock_var); +} + +// TODO(ZyX-I): move to eval/ex_cmds + +/// Common parsing logic for :unlet, :lockvar and :unlockvar. +/// +/// Invokes `callback` afterwards if successful and `eap->skip == false`. +/// +/// @param[in] eap Ex command arguments for the command. +/// @param[in] argstart Start of the string argument for the command. +/// @param[in] deep Levels to (un)lock for :(un)lockvar, -1 to (un)lock +/// everything. +/// @param[in] callback Appropriate handler for the command. +static void ex_unletlock(exarg_T *eap, char *argstart, int deep, ex_unletlock_callback callback) + FUNC_ATTR_NONNULL_ALL +{ + char *arg = argstart; + char *name_end; + bool error = false; + lval_T lv; + + do { + if (*arg == '$') { + lv.ll_name = (const char *)arg; + lv.ll_tv = NULL; + arg++; + if (get_env_len((const char **)&arg) == 0) { + semsg(_(e_invarg2), arg - 1); + return; + } + if (!error && !eap->skip && callback(&lv, arg, eap, deep) == FAIL) { + error = true; + } + name_end = arg; + } else { + // Parse the name and find the end. + name_end = get_lval(arg, NULL, &lv, true, eap->skip || error, + 0, FNE_CHECK_START); + if (lv.ll_name == NULL) { + error = true; // error, but continue parsing. + } + if (name_end == NULL + || (!ascii_iswhite(*name_end) && !ends_excmd(*name_end))) { + if (name_end != NULL) { + emsg_severe = true; + emsg(_(e_trailing)); + } + if (!(eap->skip || error)) { + clear_lval(&lv); + } + break; + } + + if (!error && !eap->skip && callback(&lv, name_end, eap, deep) == FAIL) { + error = true; + } + + if (!eap->skip) { + clear_lval(&lv); + } + } + arg = skipwhite(name_end); + } while (!ends_excmd(*arg)); + + eap->nextcmd = (char *)check_nextcmd((char_u *)arg); +} + +// TODO(ZyX-I): move to eval/ex_cmds + +/// Unlet a variable indicated by `lp`. +/// +/// @param[in] lp The lvalue. +/// @param[in] name_end End of the string argument for the command. +/// @param[in] eap Ex command arguments for :unlet. +/// @param[in] deep Unused. +/// +/// @return OK on success, or FAIL on failure. +static int do_unlet_var(lval_T *lp, char *name_end, exarg_T *eap, int deep FUNC_ATTR_UNUSED) + FUNC_ATTR_NONNULL_ALL +{ + int forceit = eap->forceit; + int ret = OK; + int cc; + + if (lp->ll_tv == NULL) { + cc = (char_u)(*name_end); + *name_end = NUL; + + // Environment variable, normal name or expanded name. + if (*lp->ll_name == '$') { + os_unsetenv(lp->ll_name + 1); + } else if (do_unlet(lp->ll_name, lp->ll_name_len, forceit) == FAIL) { + ret = FAIL; + } + *name_end = (char)cc; + } else if ((lp->ll_list != NULL + // ll_list is not NULL when lvalue is not in a list, NULL lists + // yield E689. + && var_check_lock(tv_list_locked(lp->ll_list), + lp->ll_name, + lp->ll_name_len)) + || (lp->ll_dict != NULL + && var_check_lock(lp->ll_dict->dv_lock, + lp->ll_name, + lp->ll_name_len))) { + return FAIL; + } else if (lp->ll_range) { + assert(lp->ll_list != NULL); + // Delete a range of List items. + listitem_T *const first_li = lp->ll_li; + listitem_T *last_li = first_li; + for (;;) { + listitem_T *const li = TV_LIST_ITEM_NEXT(lp->ll_list, lp->ll_li); + if (var_check_lock(TV_LIST_ITEM_TV(lp->ll_li)->v_lock, + lp->ll_name, + lp->ll_name_len)) { + return false; + } + lp->ll_li = li; + lp->ll_n1++; + if (lp->ll_li == NULL || (!lp->ll_empty2 && lp->ll_n2 < lp->ll_n1)) { + break; + } else { + last_li = lp->ll_li; + } + } + tv_list_remove_items(lp->ll_list, first_li, last_li); + } else { + if (lp->ll_list != NULL) { + // unlet a List item. + tv_list_item_remove(lp->ll_list, lp->ll_li); + } else { + // unlet a Dictionary item. + dict_T *d = lp->ll_dict; + assert(d != NULL); + dictitem_T *di = lp->ll_di; + bool watched = tv_dict_is_watched(d); + char *key = NULL; + typval_T oldtv; + + if (watched) { + tv_copy(&di->di_tv, &oldtv); + // need to save key because dictitem_remove will free it + key = xstrdup((char *)di->di_key); + } + + tv_dict_item_remove(d, di); + + if (watched) { + tv_dict_watcher_notify(d, key, NULL, &oldtv); + tv_clear(&oldtv); + xfree(key); + } + } + } + + return ret; +} + +// TODO(ZyX-I): move to eval/ex_cmds + +/// unlet a variable +/// +/// @param[in] name Variable name to unlet. +/// @param[in] name_len Variable name length. +/// @param[in] forceit If true, do not complain if variable doesn’t exist. +/// +/// @return OK if it existed, FAIL otherwise. +int do_unlet(const char *const name, const size_t name_len, const bool forceit) + FUNC_ATTR_NONNULL_ALL +{ + const char *varname; + dict_T *dict; + hashtab_T *ht = find_var_ht_dict(name, name_len, &varname, &dict); + + if (ht != NULL && *varname != NUL) { + dict_T *d = get_current_funccal_dict(ht); + if (d == NULL) { + if (ht == &globvarht) { + d = &globvardict; + } else if (is_compatht(ht)) { + d = &vimvardict; + } else { + dictitem_T *const di = find_var_in_ht(ht, *name, "", 0, false); + d = di->di_tv.vval.v_dict; + } + if (d == NULL) { + internal_error("do_unlet()"); + return FAIL; + } + } + + hashitem_T *hi = hash_find(ht, varname); + if (HASHITEM_EMPTY(hi)) { + hi = find_hi_in_scoped_ht(name, &ht); + } + if (hi != NULL && !HASHITEM_EMPTY(hi)) { + dictitem_T *const di = TV_DICT_HI2DI(hi); + if (var_check_fixed(di->di_flags, name, TV_CSTRING) + || var_check_ro(di->di_flags, name, TV_CSTRING) + || var_check_lock(d->dv_lock, name, TV_CSTRING)) { + return FAIL; + } + + if (var_check_lock(d->dv_lock, name, TV_CSTRING)) { + return FAIL; + } + + typval_T oldtv; + bool watched = tv_dict_is_watched(dict); + + if (watched) { + tv_copy(&di->di_tv, &oldtv); + } + + delete_var(ht, hi); + + if (watched) { + tv_dict_watcher_notify(dict, varname, NULL, &oldtv); + tv_clear(&oldtv); + } + return OK; + } + } + if (forceit) { + return OK; + } + semsg(_("E108: No such variable: \"%s\""), name); + return FAIL; +} + +// TODO(ZyX-I): move to eval/ex_cmds + +/// Lock or unlock variable indicated by `lp`. +/// +/// Locks if `eap->cmdidx == CMD_lockvar`, unlocks otherwise. +/// +/// @param[in] lp The lvalue. +/// @param[in] name_end Unused. +/// @param[in] eap Ex command arguments for :(un)lockvar. +/// @param[in] deep Levels to (un)lock, -1 to (un)lock everything. +/// +/// @return OK on success, or FAIL on failure. +static int do_lock_var(lval_T *lp, char *name_end FUNC_ATTR_UNUSED, exarg_T *eap, int deep) + FUNC_ATTR_NONNULL_ARG(1, 3) +{ + bool lock = eap->cmdidx == CMD_lockvar; + int ret = OK; + + if (deep == 0) { // Nothing to do. + return OK; + } + + if (lp->ll_tv == NULL) { + if (*lp->ll_name == '$') { + semsg(_(e_lock_unlock), lp->ll_name); + ret = FAIL; + } else { + // Normal name or expanded name. + dictitem_T *const di = find_var(lp->ll_name, lp->ll_name_len, NULL, + true); + if (di == NULL) { + ret = FAIL; + } else if ((di->di_flags & DI_FLAGS_FIX) + && di->di_tv.v_type != VAR_DICT + && di->di_tv.v_type != VAR_LIST) { + // For historical reasons this error is not given for Lists and + // Dictionaries. E.g. b: dictionary may be locked/unlocked. + semsg(_(e_lock_unlock), lp->ll_name); + ret = FAIL; + } else { + if (lock) { + di->di_flags |= DI_FLAGS_LOCK; + } else { + di->di_flags &= (uint8_t)(~DI_FLAGS_LOCK); + } + tv_item_lock(&di->di_tv, deep, lock, false); + } + } + } else if (lp->ll_range) { + listitem_T *li = lp->ll_li; + + // (un)lock a range of List items. + while (li != NULL && (lp->ll_empty2 || lp->ll_n2 >= lp->ll_n1)) { + tv_item_lock(TV_LIST_ITEM_TV(li), deep, lock, false); + li = TV_LIST_ITEM_NEXT(lp->ll_list, li); + lp->ll_n1++; + } + } else if (lp->ll_list != NULL) { + // (un)lock a List item. + tv_item_lock(TV_LIST_ITEM_TV(lp->ll_li), deep, lock, false); + } else { + // (un)lock a Dictionary item. + tv_item_lock(&lp->ll_di->di_tv, deep, lock, false); + } + + return ret; +} + +/// Get the value of internal variable "name". +/// Return OK or FAIL. If OK is returned "rettv" must be cleared. +/// +/// @param len length of "name" +/// @param rettv NULL when only checking existence +/// @param dip non-NULL when typval's dict item is needed +/// @param verbose may give error message +/// @param no_autoload do not use script autoloading +int get_var_tv(const char *name, int len, typval_T *rettv, dictitem_T **dip, bool verbose, + bool no_autoload) +{ + int ret = OK; + typval_T *tv = NULL; + dictitem_T *v; + + v = find_var(name, (size_t)len, NULL, no_autoload); + if (v != NULL) { + tv = &v->di_tv; + if (dip != NULL) { + *dip = v; + } + } + + if (tv == NULL) { + if (rettv != NULL && verbose) { + semsg(_("E121: Undefined variable: %.*s"), len, name); + } + ret = FAIL; + } else if (rettv != NULL) { + tv_copy(tv, rettv); + } + + return ret; +} + +/// @return the string value of a (global/local) variable or +/// NULL when it doesn't exist. +/// +/// @see tv_get_string() for how long the pointer remains valid. +char_u *get_var_value(const char *const name) +{ + dictitem_T *v; + + v = find_var(name, strlen(name), NULL, false); + if (v == NULL) { + return NULL; + } + return (char_u *)tv_get_string(&v->di_tv); +} + +/// Clean up a list of internal variables. +/// Frees all allocated variables and the value they contain. +/// Clears hashtab "ht", does not free it. +void vars_clear(hashtab_T *ht) +{ + vars_clear_ext(ht, true); +} + +/// Like vars_clear(), but only free the value if "free_val" is TRUE. +void vars_clear_ext(hashtab_T *ht, int free_val) +{ + int todo; + hashitem_T *hi; + dictitem_T *v; + + hash_lock(ht); + todo = (int)ht->ht_used; + for (hi = ht->ht_array; todo > 0; hi++) { + if (!HASHITEM_EMPTY(hi)) { + todo--; + + // Free the variable. Don't remove it from the hashtab, + // ht_array might change then. hash_clear() takes care of it + // later. + v = TV_DICT_HI2DI(hi); + if (free_val) { + tv_clear(&v->di_tv); + } + if (v->di_flags & DI_FLAGS_ALLOC) { + xfree(v); + } + } + } + hash_clear(ht); + ht->ht_used = 0; +} + +/// Delete a variable from hashtab "ht" at item "hi". +/// Clear the variable value and free the dictitem. +void delete_var(hashtab_T *ht, hashitem_T *hi) +{ + dictitem_T *di = TV_DICT_HI2DI(hi); + + hash_remove(ht, hi); + tv_clear(&di->di_tv); + xfree(di); +} + +/// List the value of one internal variable. +static void list_one_var(dictitem_T *v, const char *prefix, int *first) +{ + char *const s = encode_tv2echo(&v->di_tv, NULL); + list_one_var_a(prefix, (const char *)v->di_key, (ptrdiff_t)STRLEN(v->di_key), + v->di_tv.v_type, (s == NULL ? "" : s), first); + xfree(s); +} + +/// @param[in] name_len Length of the name. May be -1, in this case strlen() +/// will be used. +/// @param[in,out] first When true clear rest of screen and set to false. +static void list_one_var_a(const char *prefix, const char *name, const ptrdiff_t name_len, + const VarType type, const char *string, int *first) +{ + // don't use msg() or msg_attr() to avoid overwriting "v:statusmsg" + msg_start(); + msg_puts(prefix); + if (name != NULL) { // "a:" vars don't have a name stored + msg_puts_attr_len(name, name_len, 0); + } + msg_putchar(' '); + msg_advance(22); + if (type == VAR_NUMBER) { + msg_putchar('#'); + } else if (type == VAR_FUNC || type == VAR_PARTIAL) { + msg_putchar('*'); + } else if (type == VAR_LIST) { + msg_putchar('['); + if (*string == '[') { + string++; + } + } else if (type == VAR_DICT) { + msg_putchar('{'); + if (*string == '{') { + string++; + } + } else { + msg_putchar(' '); + } + + msg_outtrans((char *)string); + + if (type == VAR_FUNC || type == VAR_PARTIAL) { + msg_puts("()"); + } + if (*first) { + msg_clr_eos(); + *first = false; + } +} + +/// Set variable to the given value +/// +/// If the variable already exists, the value is updated. Otherwise the variable +/// is created. +/// +/// @param[in] name Variable name to set. +/// @param[in] name_len Length of the variable name. +/// @param tv Variable value. +/// @param[in] copy True if value in tv is to be copied. +void set_var(const char *name, const size_t name_len, typval_T *const tv, const bool copy) + FUNC_ATTR_NONNULL_ALL +{ + set_var_const(name, name_len, tv, copy, false); +} + +/// Set variable to the given value +/// +/// If the variable already exists, the value is updated. Otherwise the variable +/// is created. +/// +/// @param[in] name Variable name to set. +/// @param[in] name_len Length of the variable name. +/// @param tv Variable value. +/// @param[in] copy True if value in tv is to be copied. +/// @param[in] is_const True if value in tv is to be locked. +void set_var_const(const char *name, const size_t name_len, typval_T *const tv, const bool copy, + const bool is_const) + FUNC_ATTR_NONNULL_ALL +{ + dictitem_T *v; + hashtab_T *ht; + dict_T *dict; + + const char *varname; + ht = find_var_ht_dict(name, name_len, &varname, &dict); + const bool watched = tv_dict_is_watched(dict); + + if (ht == NULL || *varname == NUL) { + semsg(_(e_illvar), name); + return; + } + v = find_var_in_ht(ht, 0, varname, name_len - (size_t)(varname - name), true); + + // Search in parent scope which is possible to reference from lambda + if (v == NULL) { + v = find_var_in_scoped_ht(name, name_len, true); + } + + if (tv_is_func(*tv) && !var_check_func_name(name, v == NULL)) { + return; + } + + typval_T oldtv = TV_INITIAL_VALUE; + if (v != NULL) { + if (is_const) { + emsg(_(e_cannot_mod)); + return; + } + + // existing variable, need to clear the value + if (var_check_ro(v->di_flags, name, name_len) + || var_check_lock(v->di_tv.v_lock, name, name_len)) { + return; + } + + // Handle setting internal v: variables separately where needed to + // prevent changing the type. + if (is_vimvarht(ht)) { + if (v->di_tv.v_type == VAR_STRING) { + XFREE_CLEAR(v->di_tv.vval.v_string); + if (copy || tv->v_type != VAR_STRING) { + const char *const val = tv_get_string(tv); + + // Careful: when assigning to v:errmsg and tv_get_string() + // causes an error message the variable will already be set. + if (v->di_tv.vval.v_string == NULL) { + v->di_tv.vval.v_string = xstrdup(val); + } + } else { + // Take over the string to avoid an extra alloc/free. + v->di_tv.vval.v_string = tv->vval.v_string; + tv->vval.v_string = NULL; + } + return; + } else if (v->di_tv.v_type == VAR_NUMBER) { + v->di_tv.vval.v_number = tv_get_number(tv); + if (strcmp(varname, "searchforward") == 0) { + set_search_direction(v->di_tv.vval.v_number ? '/' : '?'); + } else if (strcmp(varname, "hlsearch") == 0) { + no_hlsearch = !v->di_tv.vval.v_number; + redraw_all_later(SOME_VALID); + } + return; + } else if (v->di_tv.v_type != tv->v_type) { + semsg(_("E963: setting %s to value with wrong type"), name); + return; + } + } + + if (watched) { + tv_copy(&v->di_tv, &oldtv); + } + tv_clear(&v->di_tv); + } else { // Add a new variable. + // Can't add "v:" or "a:" variable. + if (is_vimvarht(ht) || ht == get_funccal_args_ht()) { + semsg(_(e_illvar), name); + return; + } + + // Make sure the variable name is valid. + if (!valid_varname(varname)) { + return; + } + + // Make sure dict is valid + assert(dict != NULL); + + v = xmalloc(sizeof(dictitem_T) + strlen(varname)); + STRCPY(v->di_key, varname); + if (tv_dict_add(dict, v) == FAIL) { + xfree(v); + return; + } + v->di_flags = DI_FLAGS_ALLOC; + if (is_const) { + v->di_flags |= DI_FLAGS_LOCK; + } + } + + if (copy || tv->v_type == VAR_NUMBER || tv->v_type == VAR_FLOAT) { + tv_copy(tv, &v->di_tv); + } else { + v->di_tv = *tv; + v->di_tv.v_lock = VAR_UNLOCKED; + tv_init(tv); + } + + if (watched) { + if (oldtv.v_type == VAR_UNKNOWN) { + tv_dict_watcher_notify(dict, (char *)v->di_key, &v->di_tv, NULL); + } else { + tv_dict_watcher_notify(dict, (char *)v->di_key, &v->di_tv, &oldtv); + tv_clear(&oldtv); + } + } + + if (is_const) { + // Like :lockvar! name: lock the value and what it contains, but only + // if the reference count is up to one. That locks only literal + // values. + tv_item_lock(&v->di_tv, DICT_MAXNEST, true, true); + } +} + +/// Check whether variable is read-only (DI_FLAGS_RO, DI_FLAGS_RO_SBX) +/// +/// Also gives an error message. +/// +/// @param[in] flags di_flags attribute value. +/// @param[in] name Variable name, for use in error message. +/// @param[in] name_len Variable name length. Use #TV_TRANSLATE to translate +/// variable name and compute the length. Use #TV_CSTRING +/// to compute the length with strlen() without +/// translating. +/// +/// Both #TV_… values are used for optimization purposes: +/// variable name with its length is needed only in case +/// of error, when no error occurs computing them is +/// a waste of CPU resources. This especially applies to +/// gettext. +/// +/// @return True if variable is read-only: either always or in sandbox when +/// sandbox is enabled, false otherwise. +bool var_check_ro(const int flags, const char *name, size_t name_len) + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL +{ + const char *error_message = NULL; + if (flags & DI_FLAGS_RO) { + error_message = _(e_readonlyvar); + } else if ((flags & DI_FLAGS_RO_SBX) && sandbox) { + error_message = N_("E794: Cannot set variable in the sandbox: \"%.*s\""); + } + + if (error_message == NULL) { + return false; + } + if (name_len == TV_TRANSLATE) { + name = _(name); + name_len = strlen(name); + } else if (name_len == TV_CSTRING) { + name_len = strlen(name); + } + + semsg(_(error_message), (int)name_len, name); + + return true; +} + +/// Check whether variable is fixed (DI_FLAGS_FIX) +/// +/// Also gives an error message. +/// +/// @param[in] flags di_flags attribute value. +/// @param[in] name Variable name, for use in error message. +/// @param[in] name_len Variable name length. Use #TV_TRANSLATE to translate +/// variable name and compute the length. Use #TV_CSTRING +/// to compute the length with strlen() without +/// translating. +/// +/// Both #TV_… values are used for optimization purposes: +/// variable name with its length is needed only in case +/// of error, when no error occurs computing them is +/// a waste of CPU resources. This especially applies to +/// gettext. +/// +/// @return True if variable is fixed, false otherwise. +bool var_check_fixed(const int flags, const char *name, size_t name_len) + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL +{ + if (flags & DI_FLAGS_FIX) { + if (name_len == TV_TRANSLATE) { + name = _(name); + name_len = strlen(name); + } else if (name_len == TV_CSTRING) { + name_len = strlen(name); + } + semsg(_("E795: Cannot delete variable %.*s"), (int)name_len, name); + return true; + } + return false; +} + +// TODO(ZyX-I): move to eval/expressions + +/// Check if name is a valid name to assign funcref to +/// +/// @param[in] name Possible function/funcref name. +/// @param[in] new_var True if it is a name for a variable. +/// +/// @return false in case of error, true in case of success. Also gives an +/// error message if appropriate. +bool var_check_func_name(const char *const name, const bool new_var) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ + // Allow for w: b: s: and t:. + if (!(vim_strchr("wbst", name[0]) != NULL && name[1] == ':') + && !ASCII_ISUPPER((name[0] != NUL && name[1] == ':') + ? name[2] : name[0])) { + semsg(_("E704: Funcref variable name must start with a capital: %s"), name); + return false; + } + // Don't allow hiding a function. When "v" is not NULL we might be + // assigning another function to the same var, the type is checked + // below. + if (new_var && function_exists(name, false)) { + semsg(_("E705: Variable name conflicts with existing function: %s"), name); + return false; + } + return true; +} + +// TODO(ZyX-I): move to eval/expressions + +/// Check if a variable name is valid +/// +/// @param[in] varname Variable name to check. +/// +/// @return false when variable name is not valid, true when it is. Also gives +/// an error message if appropriate. +bool valid_varname(const char *varname) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ + for (const char *p = varname; *p != NUL; p++) { + if (!eval_isnamec1((int)(uint8_t)(*p)) + && (p == varname || !ascii_isdigit(*p)) + && *p != AUTOLOAD_CHAR) { + semsg(_(e_illvar), varname); + return false; + } + } + return true; +} + +/// Implements the logic to retrieve local variable and option values. +/// Used by "getwinvar()" "gettabvar()" "gettabwinvar()" "getbufvar()". +/// +/// @param deftv default value if not found +/// @param htname 't'ab, 'w'indow or 'b'uffer local +/// @param tp can be NULL +/// @param buf ignored if htname is not 'b' +static void get_var_from(const char *varname, typval_T *rettv, typval_T *deftv, int htname, + tabpage_T *tp, win_T *win, buf_T *buf) +{ + bool done = false; + const bool do_change_curbuf = buf != NULL && htname == 'b'; + + emsg_off++; + + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; + + if (varname != NULL && tp != NULL && win != NULL && (htname != 'b' || buf != NULL)) { + // Set curwin to be our win, temporarily. Also set the tabpage, + // otherwise the window is not valid. Only do this when needed, + // autocommands get blocked. + // If we have a buffer reference avoid the switching, we're saving and + // restoring curbuf directly. + const bool need_switch_win = !(tp == curtab && win == curwin) && !do_change_curbuf; + switchwin_T switchwin; + if (!need_switch_win || switch_win(&switchwin, win, tp, true) == OK) { + if (*varname == '&' && htname != 't') { + buf_T *const save_curbuf = curbuf; + + // Change curbuf so the option is read from the correct buffer. + if (do_change_curbuf) { + curbuf = buf; + } + + if (varname[1] == NUL) { + // get all window-local or buffer-local options in a dict + dict_T *opts = get_winbuf_options(htname == 'b'); + + if (opts != NULL) { + tv_dict_set_ret(rettv, opts); + done = true; + } + } else if (get_option_tv(&varname, rettv, true) == OK) { + // Local option + done = true; + } + + curbuf = save_curbuf; + } else if (*varname == NUL) { + const ScopeDictDictItem *v; + // Empty string: return a dict with all the local variables. + if (htname == 'b') { + v = &buf->b_bufvar; + } else if (htname == 'w') { + v = &win->w_winvar; + } else { + v = &tp->tp_winvar; + } + tv_copy(&v->di_tv, rettv); + done = true; + } else { + hashtab_T *ht; + + if (htname == 'b') { + ht = &buf->b_vars->dv_hashtab; + } else if (htname == 'w') { + ht = &win->w_vars->dv_hashtab; + } else { + ht = &tp->tp_vars->dv_hashtab; + } + + // Look up the variable. + const dictitem_T *const v = find_var_in_ht(ht, htname, varname, strlen(varname), false); + if (v != NULL) { + tv_copy(&v->di_tv, rettv); + done = true; + } + } + } + + if (need_switch_win) { + // restore previous notion of curwin + restore_win(&switchwin, true); + } + } + + if (!done && deftv->v_type != VAR_UNKNOWN) { + // use the default value + tv_copy(deftv, rettv); + } + + emsg_off--; +} + +/// getwinvar() and gettabwinvar() +/// +/// @param off 1 for gettabwinvar() +static void getwinvar(typval_T *argvars, typval_T *rettv, int off) +{ + tabpage_T *tp; + + if (off == 1) { + tp = find_tabpage((int)tv_get_number_chk(&argvars[0], NULL)); + } else { + tp = curtab; + } + win_T *const win = find_win_by_nr(&argvars[off], tp); + const char *const varname = tv_get_string_chk(&argvars[off + 1]); + + get_var_from(varname, rettv, &argvars[off + 2], 'w', tp, win, NULL); +} + +/// Set option "varname" to the value of "varp" for the current buffer/window. +static void set_option_from_tv(const char *varname, typval_T *varp) +{ + long numval = 0; + const char *strval; + bool error = false; + char nbuf[NUMBUFLEN]; + + if (varp->v_type == VAR_BOOL) { + if (is_string_option(varname)) { + emsg(_(e_stringreq)); + return; + } + numval = (long)varp->vval.v_number; + strval = "0"; // avoid using "false" + } else { + numval = (long)tv_get_number_chk(varp, &error); + strval = tv_get_string_buf_chk(varp, nbuf); + } + if (!error && strval != NULL) { + set_option_value(varname, numval, strval, OPT_LOCAL); + } +} + +/// "setwinvar()" and "settabwinvar()" functions +static void setwinvar(typval_T *argvars, typval_T *rettv, int off) +{ + if (check_secure()) { + return; + } + + tabpage_T *tp = NULL; + if (off == 1) { + tp = find_tabpage((int)tv_get_number_chk(&argvars[0], NULL)); + } else { + tp = curtab; + } + win_T *const win = find_win_by_nr(&argvars[off], tp); + const char *varname = tv_get_string_chk(&argvars[off + 1]); + typval_T *varp = &argvars[off + 2]; + + if (win != NULL && varname != NULL && varp != NULL) { + bool need_switch_win = !(tp == curtab && win == curwin); + switchwin_T switchwin; + if (!need_switch_win || switch_win(&switchwin, win, tp, true) == OK) { + if (*varname == '&') { + set_option_from_tv(varname + 1, varp); + } else { + const size_t varname_len = strlen(varname); + char *const winvarname = xmalloc(varname_len + 3); + memcpy(winvarname, "w:", 2); + memcpy(winvarname + 2, varname, varname_len + 1); + set_var(winvarname, varname_len + 2, varp, true); + xfree(winvarname); + } + } + if (need_switch_win) { + restore_win(&switchwin, true); + } + } +} + +bool var_exists(const char *var) + FUNC_ATTR_NONNULL_ALL +{ + char *tofree; + bool n = false; + + // get_name_len() takes care of expanding curly braces + const char *name = var; + const int len = get_name_len(&var, &tofree, true, false); + if (len > 0) { + typval_T tv; + + if (tofree != NULL) { + name = tofree; + } + n = get_var_tv(name, len, &tv, NULL, false, true) == OK; + if (n) { + // Handle d.key, l[idx], f(expr). + n = handle_subscript(&var, &tv, true, false, name, &name) == OK; + if (n) { + tv_clear(&tv); + } + } + } + if (*var != NUL) { + n = false; + } + + xfree(tofree); + return n; +} + +/// "gettabvar()" function +void f_gettabvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + const char *const varname = tv_get_string_chk(&argvars[1]); + tabpage_T *const tp = find_tabpage((int)tv_get_number_chk(&argvars[0], NULL)); + win_T *win = NULL; + + if (tp != NULL) { + win = tp == curtab || tp->tp_firstwin == NULL ? firstwin : tp->tp_firstwin; + } + + get_var_from(varname, rettv, &argvars[2], 't', tp, win, NULL); +} + +/// "gettabwinvar()" function +void f_gettabwinvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + getwinvar(argvars, rettv, 1); +} + +/// "getwinvar()" function +void f_getwinvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + getwinvar(argvars, rettv, 0); +} + +/// "getbufvar()" function +void f_getbufvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + const char *const varname = tv_get_string_chk(&argvars[1]); + buf_T *const buf = tv_get_buf_from_arg(&argvars[0]); + + get_var_from(varname, rettv, &argvars[2], 'b', curtab, curwin, buf); +} + +/// "settabvar()" function +void f_settabvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + rettv->vval.v_number = 0; + + if (check_secure()) { + return; + } + + tabpage_T *const tp = find_tabpage((int)tv_get_number_chk(&argvars[0], NULL)); + const char *const varname = tv_get_string_chk(&argvars[1]); + typval_T *const varp = &argvars[2]; + + if (varname != NULL && tp != NULL) { + tabpage_T *const save_curtab = curtab; + goto_tabpage_tp(tp, false, false); + + const size_t varname_len = strlen(varname); + char *const tabvarname = xmalloc(varname_len + 3); + memcpy(tabvarname, "t:", 2); + memcpy(tabvarname + 2, varname, varname_len + 1); + set_var(tabvarname, varname_len + 2, varp, true); + xfree(tabvarname); + + // Restore current tabpage. + if (valid_tabpage(save_curtab)) { + goto_tabpage_tp(save_curtab, false, false); + } + } +} + +/// "settabwinvar()" function +void f_settabwinvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + setwinvar(argvars, rettv, 1); +} + +/// "setwinvar()" function +void f_setwinvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + setwinvar(argvars, rettv, 0); +} + +/// "setbufvar()" function +void f_setbufvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + if (check_secure() + || !tv_check_str_or_nr(&argvars[0])) { + return; + } + const char *varname = tv_get_string_chk(&argvars[1]); + buf_T *const buf = tv_get_buf(&argvars[0], false); + typval_T *varp = &argvars[2]; + + if (buf != NULL && varname != NULL) { + if (*varname == '&') { + aco_save_T aco; + + // set curbuf to be our buf, temporarily + aucmd_prepbuf(&aco, buf); + + set_option_from_tv(varname + 1, varp); + + // reset notion of buffer + aucmd_restbuf(&aco); + } else { + const size_t varname_len = STRLEN(varname); + char *const bufvarname = xmalloc(varname_len + 3); + buf_T *const save_curbuf = curbuf; + curbuf = buf; + memcpy(bufvarname, "b:", 2); + memcpy(bufvarname + 2, varname, varname_len + 1); + set_var(bufvarname, varname_len + 2, varp, true); + xfree(bufvarname); + curbuf = save_curbuf; + } + } +} diff --git a/src/nvim/eval/vars.h b/src/nvim/eval/vars.h new file mode 100644 index 0000000000..73efc4938a --- /dev/null +++ b/src/nvim/eval/vars.h @@ -0,0 +1,9 @@ +#ifndef NVIM_EVAL_VARS_H +#define NVIM_EVAL_VARS_H + +#include "nvim/ex_cmds_defs.h" // For exarg_T + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "eval/vars.h.generated.h" +#endif +#endif // NVIM_EVAL_VARS_H diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 28e1893b31..23e7660606 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -1583,22 +1583,39 @@ char *make_filter_cmd(char *cmd, char *itmp, char *otmp) if (otmp != NULL) { len += STRLEN(otmp) + STRLEN(p_srr) + 2; // two extra spaces (" "), } + + const char *const cmd_args = strchr(cmd, ' '); + len += (is_pwsh && cmd_args) + ? STRLEN(" -ArgumentList ") + 2 // two extra quotes + : 0; + char *const buf = xmalloc(len); -#if defined(UNIX) - // Put delimiters around the command (for concatenated commands) when - // redirecting input and/or output. if (is_pwsh) { xstrlcpy(buf, "Start-Process ", len); - xstrlcat(buf, cmd, len); + if (cmd_args == NULL) { + xstrlcat(buf, cmd, len); + } else { + xstrlcpy(buf + STRLEN(buf), cmd, (size_t)(cmd_args - cmd + 1)); + xstrlcat(buf, " -ArgumentList \"", len); + xstrlcat(buf, cmd_args + 1, len); // +1 to skip the leading space. + xstrlcat(buf, "\"", len); + } +#if defined(UNIX) + // Put delimiters around the command (for concatenated commands) when + // redirecting input and/or output. } else if (itmp != NULL || otmp != NULL) { char *fmt = is_fish_shell ? "begin; %s; end" : "(%s)"; vim_snprintf(buf, len, fmt, cmd); +#endif + // For shells that don't understand braces around commands, at least allow + // the use of commands in a pipe. } else { xstrlcpy(buf, cmd, len); } +#if defined(UNIX) if (itmp != NULL) { if (is_pwsh) { xstrlcat(buf, " -RedirectStandardInput ", len - 1); @@ -1608,14 +1625,6 @@ char *make_filter_cmd(char *cmd, char *itmp, char *otmp) xstrlcat(buf, itmp, len - 1); } #else - // For shells that don't understand braces around commands, at least allow - // the use of commands in a pipe. - if (is_pwsh) { - xstrlcpy(buf, "Start-Process ", len); - xstrlcat(buf, cmd, len); - } else { - xstrlcpy(buf, cmd, len); - } if (itmp != NULL) { // If there is a pipe, we have to put the '<' in front of it. // Don't do this when 'shellquote' is not empty, otherwise the @@ -2716,6 +2725,12 @@ int do_ecmd(int fnum, char *ffname, char *sfname, exarg_T *eap, linenr_T newlnum // Assume success now retval = OK; + // If the file name was changed, reset the not-edit flag so that ":write" + // works. + if (!other_file) { + curbuf->b_flags &= ~BF_NOTEDITED; + } + /* * Check if we are editing the w_arg_idx file in the argument list. */ @@ -3582,7 +3597,7 @@ static int do_sub(exarg_T *eap, proftime_T timeout, long cmdpreview_ns, handle_T // check for a trailing count cmd = skipwhite(cmd); if (ascii_isdigit(*cmd)) { - i = getdigits_long((char_u **)&cmd, true, 0); + i = getdigits_long(&cmd, true, 0); if (i <= 0 && !eap->skip && subflags.do_error) { emsg(_(e_zerocount)); return 0; @@ -3635,7 +3650,6 @@ static int do_sub(exarg_T *eap, proftime_T timeout, long cmdpreview_ns, handle_T assert(sub != NULL); - bool sub_needs_free = false; char *sub_copy = NULL; // If the substitute pattern starts with "\=" then it's an expression. @@ -3647,14 +3661,15 @@ static int do_sub(exarg_T *eap, proftime_T timeout, long cmdpreview_ns, handle_T sub = xstrdup(sub); sub_copy = sub; } else { - char *source = sub; - sub = (char *)regtilde((char_u *)sub, p_magic, cmdpreview); - // When previewing, the new pattern allocated by regtilde() needs to be freed - // in this function because it will not be used or freed by regtilde() later. - sub_needs_free = cmdpreview && sub != source; + char *newsub = (char *)regtilde((char_u *)sub, p_magic, cmdpreview); + if (newsub != sub) { + // newsub was allocated, free it later. + sub_copy = newsub; + sub = newsub; + } } - bool cmdheight0 = p_ch < 1 && !ui_has(kUIMessages); + const bool cmdheight0 = !ui_has_messages(); if (cmdheight0) { // If cmdheight is 0, cmdheight must be set to 1 when we enter command line. set_option_value("ch", 1L, NULL, 0); @@ -4450,9 +4465,6 @@ skip: vim_regfree(regmatch.regprog); xfree(sub_copy); - if (sub_needs_free) { - xfree(sub); - } // Restore the flag values, they can be used for ":&&". subflags.do_all = save_do_all; @@ -4846,14 +4858,14 @@ void ex_help(exarg_T *eap) semsg(_("E149: Sorry, no help for %s"), arg); } if (n != FAIL) { - FreeWild(num_matches, (char_u **)matches); + FreeWild(num_matches, matches); } return; } // The first match (in the requested language) is the best match. tag = xstrdup(matches[i]); - FreeWild(num_matches, (char_u **)matches); + FreeWild(num_matches, matches); /* * Re-use an existing help window or open a new one. @@ -5042,7 +5054,7 @@ int find_help_tags(const char *arg, int *num_matches, char ***matches, bool keep int i; // Specific tags that either have a specific replacement or won't go - // throught the generic rules. + // through the generic rules. static char *(except_tbl[][2]) = { { "*", "star" }, { "g*", "gstar" }, @@ -5275,7 +5287,7 @@ int find_help_tags(const char *arg, int *num_matches, char ***matches, bool keep if (keep_lang) { flags |= TAG_KEEP_LANG; } - if (find_tags(IObuff, num_matches, (char_u ***)matches, flags, MAXCOL, NULL) == OK + if (find_tags(IObuff, num_matches, matches, flags, MAXCOL, NULL) == OK && *num_matches > 0) { // Sort the matches found on the heuristic number that is after the // tag name. @@ -5412,8 +5424,8 @@ void fix_help_buffer(void) // Note: We cannot just do `&NameBuff` because it is a statically sized array // so `NameBuff == &NameBuff` according to C semantics. char *buff_list[1] = { (char *)NameBuff }; - if (gen_expand_wildcards(1, (char_u **)buff_list, &fcount, - (char_u ***)&fnames, EW_FILE|EW_SILENT) == OK + if (gen_expand_wildcards(1, buff_list, &fcount, + &fnames, EW_FILE|EW_SILENT) == OK && fcount > 0) { // If foo.abx is found use it instead of foo.txt in // the same directory. @@ -5514,7 +5526,7 @@ void fix_help_buffer(void) } fclose(fd); } - FreeWild(fcount, (char_u **)fnames); + FreeWild(fcount, fnames); } } xfree(rt); @@ -5568,12 +5580,15 @@ static void helptags_one(char *dir, const char *ext, const char *tagfname, bool // Note: We cannot just do `&NameBuff` because it is a statically sized array // so `NameBuff == &NameBuff` according to C semantics. char *buff_list[1] = { (char *)NameBuff }; - if (gen_expand_wildcards(1, (char_u **)buff_list, &filecount, (char_u ***)&files, - EW_FILE|EW_SILENT) == FAIL - || filecount == 0) { + const int res = gen_expand_wildcards(1, buff_list, &filecount, &files, + EW_FILE|EW_SILENT); + if (res == FAIL || filecount == 0) { if (!got_int) { semsg(_("E151: No match: %s"), NameBuff); } + if (res != FAIL) { + FreeWild(filecount, files); + } return; } @@ -5593,7 +5608,7 @@ static void helptags_one(char *dir, const char *ext, const char *tagfname, bool if (!ignore_writeerr) { semsg(_("E152: Cannot open %s for writing"), NameBuff); } - FreeWild(filecount, (char_u **)files); + FreeWild(filecount, files); return; } @@ -5683,11 +5698,11 @@ static void helptags_one(char *dir, const char *ext, const char *tagfname, bool fclose(fd); } - FreeWild(filecount, (char_u **)files); + FreeWild(filecount, files); if (!got_int && ga.ga_data != NULL) { // Sort the tags. - sort_strings((char_u **)ga.ga_data, ga.ga_len); + sort_strings(ga.ga_data, ga.ga_len); // Check for duplicates. for (int i = 1; i < ga.ga_len; i++) { @@ -5762,7 +5777,7 @@ static void do_helptags(char *dirname, bool add_help_tags, bool ignore_writeerr) // Note: We cannot just do `&NameBuff` because it is a statically sized array // so `NameBuff == &NameBuff` according to C semantics. char *buff_list[1] = { (char *)NameBuff }; - if (gen_expand_wildcards(1, (char_u **)buff_list, &filecount, (char_u ***)&files, + if (gen_expand_wildcards(1, buff_list, &filecount, &files, EW_FILE|EW_SILENT) == FAIL || filecount == 0) { semsg(_("E151: No match: %s"), NameBuff); @@ -5778,6 +5793,7 @@ static void do_helptags(char *dirname, bool add_help_tags, bool ignore_writeerr) if (len <= 4) { continue; } + if (STRICMP(files[i] + len - 4, ".txt") == 0) { // ".txt" -> language "en" lang[0] = 'e'; @@ -5828,7 +5844,7 @@ static void do_helptags(char *dirname, bool add_help_tags, bool ignore_writeerr) } ga_clear(&ga); - FreeWild(filecount, (char_u **)files); + FreeWild(filecount, files); } static void helptags_cb(char *fname, void *cookie) diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c index e57dc5d13f..730a2f1b69 100644 --- a/src/nvim/ex_cmds2.c +++ b/src/nvim/ex_cmds2.c @@ -24,6 +24,7 @@ #include "nvim/charset.h" #include "nvim/debugger.h" #include "nvim/eval/userfunc.h" +#include "nvim/eval/vars.h" #include "nvim/ex_cmds.h" #include "nvim/ex_cmds2.h" #include "nvim/ex_eval.h" @@ -856,7 +857,7 @@ static void get_arglist(garray_T *gap, char *str, int escaped) /// "fnames[fcountp]". When "wig" is true, removes files matching 'wildignore'. /// /// @return FAIL or OK. -int get_arglist_exp(char_u *str, int *fcountp, char_u ***fnamesp, bool wig) +int get_arglist_exp(char_u *str, int *fcountp, char ***fnamesp, bool wig) { garray_T ga; int i; @@ -864,10 +865,10 @@ int get_arglist_exp(char_u *str, int *fcountp, char_u ***fnamesp, bool wig) get_arglist(&ga, (char *)str, true); if (wig) { - i = expand_wildcards(ga.ga_len, (char_u **)ga.ga_data, + i = expand_wildcards(ga.ga_len, ga.ga_data, fcountp, fnamesp, EW_FILE|EW_NOTFOUND|EW_NOTWILD); } else { - i = gen_expand_wildcards(ga.ga_len, (char_u **)ga.ga_data, + i = gen_expand_wildcards(ga.ga_len, ga.ga_data, fcountp, fnamesp, EW_FILE|EW_NOTFOUND|EW_NOTWILD); } @@ -949,8 +950,8 @@ static int do_arglist(char *str, int what, int after, bool will_edit) } ga_clear(&new_ga); } else { - int i = expand_wildcards(new_ga.ga_len, (char_u **)new_ga.ga_data, - &exp_count, (char_u ***)&exp_files, + int i = expand_wildcards(new_ga.ga_len, new_ga.ga_data, + &exp_count, &exp_files, EW_DIR|EW_FILE|EW_ADDSLASH|EW_NOTFOUND); ga_clear(&new_ga); if (i == FAIL || exp_count == 0) { diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index c4d2821e79..a7d91a47d7 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -21,6 +21,7 @@ #include "nvim/edit.h" #include "nvim/eval.h" #include "nvim/eval/userfunc.h" +#include "nvim/eval/vars.h" #include "nvim/event/rstream.h" #include "nvim/event/wstream.h" #include "nvim/ex_cmds.h" @@ -1395,7 +1396,7 @@ static int parse_count(exarg_T *eap, char **errormsg, bool validate) if ((eap->argt & EX_COUNT) && ascii_isdigit(*eap->arg) && (!(eap->argt & EX_BUFNAME) || *(p = skipdigits(eap->arg + 1)) == NUL || ascii_iswhite(*p))) { - n = getdigits_long((char_u **)&eap->arg, false, -1); + n = getdigits_long(&eap->arg, false, -1); eap->arg = skipwhite(eap->arg); if (n <= 0 && (eap->argt & EX_ZEROR) == 0) { if (errormsg != NULL) { @@ -1623,7 +1624,7 @@ int execute_cmd(exarg_T *eap, CmdParseInfo *cmdinfo, bool preview) // If filename expansion is enabled, expand filenames if (cmdinfo->magic.file) { - if (expand_filename(eap, (char_u **)eap->cmdlinep, &errormsg) == FAIL) { + if (expand_filename(eap, eap->cmdlinep, &errormsg) == FAIL) { goto end; } } @@ -2289,7 +2290,7 @@ static char *do_one_cmd(char **cmdlinep, int flags, cstack_T *cstack, LineGetter } if (ea.argt & EX_XFILE) { - if (expand_filename(&ea, (char_u **)cmdlinep, &errormsg) == FAIL) { + if (expand_filename(&ea, cmdlinep, &errormsg) == FAIL) { goto doend; } } @@ -2852,11 +2853,13 @@ int parse_cmd_address(exarg_T *eap, char **errormsg, bool silent) if (!mark_check(fm)) { goto theend; } + assert(fm != NULL); eap->line1 = fm->mark.lnum; fm = mark_get_visual(curbuf, '>'); if (!mark_check(fm)) { goto theend; } + assert(fm != NULL); eap->line2 = fm->mark.lnum; eap->addr_count++; } @@ -3049,6 +3052,7 @@ char *find_ex_command(exarg_T *eap, int *full) } else { eap->cmdidx = CMD_bang; } + assert(eap->cmdidx >= 0); for (; (int)eap->cmdidx < CMD_SIZE; eap->cmdidx = (cmdidx_T)((int)eap->cmdidx + 1)) { @@ -3268,6 +3272,7 @@ int cmd_exists(const char *const name) // For ":2match" and ":3match" we need to skip the number. ea.cmd = (char *)((*name == '2' || *name == '3') ? name + 1 : name); ea.cmdidx = (cmdidx_T)0; + ea.flags = 0; int full = false; p = find_ex_command(&ea, &full); if (p == NULL) { @@ -3301,6 +3306,7 @@ void f_fullcommand(typval_T *argvars, typval_T *rettv, FunPtr fptr) ea.cmd = (*name == '2' || *name == '3') ? name + 1 : name; ea.cmdidx = (cmdidx_T)0; + ea.flags = 0; char *p = find_ex_command(&ea, NULL); if (p == NULL || ea.cmdidx == CMD_SIZE) { return; @@ -4390,6 +4396,7 @@ static linenr_T get_address(exarg_T *eap, char **ptr, cmd_addr_T addr_type, int cmd = NULL; goto error; } + assert(fm != NULL); lnum = fm->mark.lnum; } } @@ -4482,7 +4489,7 @@ static linenr_T get_address(exarg_T *eap, char **ptr, cmd_addr_T addr_type, int default: if (ascii_isdigit(*cmd)) { // absolute line number - lnum = (linenr_T)getdigits((char_u **)&cmd, false, 0); + lnum = (linenr_T)getdigits(&cmd, false, 0); } } @@ -4731,78 +4738,46 @@ static char *skip_grep_pat(exarg_T *eap) /// For the ":make" and ":grep" commands insert the 'makeprg'/'grepprg' option /// in the command line, so that things like % get expanded. -char *replace_makeprg(exarg_T *eap, char *p, char **cmdlinep) +char *replace_makeprg(exarg_T *eap, char *arg, char **cmdlinep) { - char *new_cmdline; - char *program; - char *pos; - char *ptr; - int len; - size_t i; + bool isgrep = eap->cmdidx == CMD_grep + || eap->cmdidx == CMD_lgrep + || eap->cmdidx == CMD_grepadd + || eap->cmdidx == CMD_lgrepadd; - /* - * Don't do it when ":vimgrep" is used for ":grep". - */ - if ((eap->cmdidx == CMD_make || eap->cmdidx == CMD_lmake - || eap->cmdidx == CMD_grep || eap->cmdidx == CMD_lgrep - || eap->cmdidx == CMD_grepadd - || eap->cmdidx == CMD_lgrepadd) + // Don't do it when ":vimgrep" is used for ":grep". + if ((eap->cmdidx == CMD_make || eap->cmdidx == CMD_lmake || isgrep) && !grep_internal(eap->cmdidx)) { - if (eap->cmdidx == CMD_grep || eap->cmdidx == CMD_lgrep - || eap->cmdidx == CMD_grepadd || eap->cmdidx == CMD_lgrepadd) { - if (*curbuf->b_p_gp == NUL) { - program = (char *)p_gp; - } else { - program = (char *)curbuf->b_p_gp; - } - } else { - if (*curbuf->b_p_mp == NUL) { - program = (char *)p_mp; - } else { - program = (char *)curbuf->b_p_mp; - } - } + const char *program = isgrep ? (*curbuf->b_p_gp == NUL ? (char *)p_gp : (char *)curbuf->b_p_gp) + : (*curbuf->b_p_mp == NUL ? (char *)p_mp : (char *)curbuf->b_p_mp); - p = skipwhite(p); + arg = skipwhite(arg); - if ((pos = strstr(program, "$*")) != NULL) { - // replace $* by given arguments - i = 1; - while ((pos = strstr(pos + 2, "$*")) != NULL) { - i++; - } - len = (int)STRLEN(p); - new_cmdline = xmalloc(STRLEN(program) + i * (size_t)(len - 2) + 1); - ptr = new_cmdline; - while ((pos = strstr(program, "$*")) != NULL) { - i = (size_t)(pos - program); - memcpy(ptr, program, i); - STRCPY(ptr += i, p); - ptr += len; - program = pos + 2; - } - STRCPY(ptr, program); - } else { - new_cmdline = xmalloc(STRLEN(program) + STRLEN(p) + 2); + char *new_cmdline; + // Replace $* by given arguments + if ((new_cmdline = strrep(program, "$*", arg)) == NULL) { + // No $* in arg, build "<makeprg> <arg>" instead + new_cmdline = xmalloc(STRLEN(program) + STRLEN(arg) + 2); STRCPY(new_cmdline, program); STRCAT(new_cmdline, " "); - STRCAT(new_cmdline, p); + STRCAT(new_cmdline, arg); } - msg_make((char_u *)p); + + msg_make((char_u *)arg); // 'eap->cmd' is not set here, because it is not used at CMD_make xfree(*cmdlinep); *cmdlinep = new_cmdline; - p = new_cmdline; + arg = new_cmdline; } - return p; + return arg; } /// Expand file name in Ex command argument. /// When an error is detected, "errormsgp" is set to a non-NULL pointer. /// /// @return FAIL for failure, OK otherwise. -int expand_filename(exarg_T *eap, char_u **cmdlinep, char **errormsgp) +int expand_filename(exarg_T *eap, char **cmdlinep, char **errormsgp) { int has_wildcards; // need to expand wildcards char *repl; @@ -4908,7 +4883,7 @@ int expand_filename(exarg_T *eap, char_u **cmdlinep, char **errormsgp) repl = l; } - p = repl_cmdline(eap, p, srclen, repl, (char **)cmdlinep); + p = repl_cmdline(eap, p, srclen, repl, cmdlinep); xfree(repl); } @@ -4935,7 +4910,7 @@ int expand_filename(exarg_T *eap, char_u **cmdlinep, char **errormsgp) p = NULL; } if (p != NULL) { - (void)repl_cmdline(eap, eap->arg, STRLEN(eap->arg), p, (char **)cmdlinep); + (void)repl_cmdline(eap, eap->arg, STRLEN(eap->arg), p, cmdlinep); } } @@ -4962,7 +4937,7 @@ int expand_filename(exarg_T *eap, char_u **cmdlinep, char **errormsgp) if (p == NULL) { return FAIL; } - (void)repl_cmdline(eap, eap->arg, STRLEN(eap->arg), p, (char **)cmdlinep); + (void)repl_cmdline(eap, eap->arg, STRLEN(eap->arg), p, cmdlinep); xfree(p); } } @@ -5246,7 +5221,7 @@ static int get_tabpage_arg(exarg_T *eap) } p_save = p; - tab_number = (int)getdigits((char_u **)&p, false, tab_number); + tab_number = (int)getdigits(&p, false, tab_number); if (relative == 0) { if (STRCMP(p, "$") == 0) { @@ -5942,7 +5917,7 @@ two_count: return FAIL; } - *def = getdigits_long((char_u **)&p, true, 0); + *def = getdigits_long(&p, true, 0); *argt |= EX_ZEROR; if (p != val + vallen || vallen == 0) { @@ -5968,7 +5943,7 @@ invalid_count: goto two_count; } - *def = getdigits_long((char_u **)&p, true, 0); + *def = getdigits_long(&p, true, 0); if (p != val + vallen) { goto invalid_count; @@ -6783,9 +6758,18 @@ char *get_user_commands(expand_T *xp FUNC_ATTR_UNUSED, int idx) if (idx < buf->b_ucmds.ga_len) { return (char *)USER_CMD_GA(&buf->b_ucmds, idx)->uc_name; } + idx -= buf->b_ucmds.ga_len; if (idx < ucmds.ga_len) { - return (char *)USER_CMD(idx)->uc_name; + char *name = (char *)USER_CMD(idx)->uc_name; + + for (int i = 0; i < buf->b_ucmds.ga_len; i++) { + if (STRCMP(name, USER_CMD_GA(&buf->b_ucmds, i)->uc_name) == 0) { + // global command is overruled by buffer-local one + return ""; + } + } + return name; } return NULL; } @@ -7741,7 +7725,7 @@ static void ex_tabnext(exarg_T *eap) if (eap->arg && *eap->arg != NUL) { char *p = eap->arg; char *p_save = p; - tab_number = (int)getdigits((char_u **)&p, false, 0); + tab_number = (int)getdigits(&p, false, 0); if (p == p_save || *p_save == '-' || *p_save == '+' || *p != NUL || tab_number == 0) { // No numbers as argument. @@ -8722,7 +8706,7 @@ static void ex_later(exarg_T *eap) if (*p == NUL) { count = 1; } else if (isdigit(*p)) { - count = getdigits_long((char_u **)&p, false, 0); + count = getdigits_long(&p, false, 0); switch (*p) { case 's': ++p; sec = true; break; @@ -9239,7 +9223,7 @@ static void ex_findpat(exarg_T *eap) n = 1; if (ascii_isdigit(*eap->arg)) { // get count - n = getdigits_long((char_u **)&eap->arg, false, 0); + n = getdigits_long(&eap->arg, false, 0); eap->arg = skipwhite(eap->arg); } if (*eap->arg == '/') { // Match regexp, not just whole words @@ -9997,7 +9981,7 @@ static void ex_setfiletype(exarg_T *eap) static void ex_digraphs(exarg_T *eap) { if (*eap->arg != NUL) { - putdigraph((char_u *)eap->arg); + putdigraph(eap->arg); } else { listdigraphs(eap->forceit); } diff --git a/src/nvim/ex_docmd.h b/src/nvim/ex_docmd.h index 7e0d3016bc..ddb25c53e4 100644 --- a/src/nvim/ex_docmd.h +++ b/src/nvim/ex_docmd.h @@ -1,7 +1,6 @@ #ifndef NVIM_EX_DOCMD_H #define NVIM_EX_DOCMD_H -#include "nvim/eval/funcs.h" #include "nvim/ex_cmds_defs.h" #include "nvim/globals.h" diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 4c26cfe500..6240ac6b37 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -24,6 +24,7 @@ #include "nvim/digraph.h" #include "nvim/edit.h" #include "nvim/eval.h" +#include "nvim/eval/funcs.h" #include "nvim/eval/userfunc.h" #include "nvim/event/loop.h" #include "nvim/ex_cmds.h" @@ -688,12 +689,22 @@ static void finish_incsearch_highlighting(int gotesc, incsearch_state_T *s, bool /// @param init_ccline clear ccline first static uint8_t *command_line_enter(int firstc, long count, int indent, bool init_ccline) { - bool cmdheight0 = p_ch < 1 && !ui_has(kUIMessages); + const bool cmdheight0 = !ui_has_messages(); if (cmdheight0) { - // If cmdheight is 0, cmdheight must be set to 1 when we enter command line. + const long save_so = lastwin->w_p_so; + + // If cmdheight is 0, cmdheight must be set to 1 when we enter the + // command line. Set "made_cmdheight_nonzero" and reset 'scrolloff' to + // avoid scrolling the last window. + made_cmdheight_nonzero = true; + + lastwin->w_p_so = 0; set_option_value("ch", 1L, NULL, 0); update_screen(VALID); // redraw the screen NOW + + made_cmdheight_nonzero = false; + lastwin->w_p_so = save_so; } // can be invoked recursively, identify each level @@ -827,6 +838,36 @@ static uint8_t *command_line_enter(int firstc, long count, int indent, bool init setmouse(); ui_cursor_shape(); // may show different cursor shape + TryState tstate; + Error err = ERROR_INIT; + bool tl_ret = true; + save_v_event_T save_v_event; + dict_T *dict = get_v_event(&save_v_event); + char firstcbuf[2]; + firstcbuf[0] = (char)(firstc > 0 ? firstc : '-'); + firstcbuf[1] = 0; + + if (has_event(EVENT_CMDLINEENTER)) { + // set v:event to a dictionary with information about the commandline + tv_dict_add_str(dict, S_LEN("cmdtype"), firstcbuf); + tv_dict_add_nr(dict, S_LEN("cmdlevel"), ccline.level); + tv_dict_set_keys_readonly(dict); + try_enter(&tstate); + + apply_autocmds(EVENT_CMDLINEENTER, firstcbuf, firstcbuf, false, curbuf); + restore_v_event(dict, &save_v_event); + + tl_ret = try_leave(&tstate, &err); + if (!tl_ret && ERROR_SET(&err)) { + msg_putchar('\n'); + msg_printf_attr(HL_ATTR(HLF_E)|MSG_HIST, (char *)e_autocmd_err, err.msg); + api_clear_error(&err); + redrawcmd(); + } + tl_ret = true; + } + may_trigger_modechanged(); + init_history(); s->hiscnt = hislen; // set hiscnt to impossible history value s->histype = hist_char2type(s->firstc); @@ -849,6 +890,12 @@ static uint8_t *command_line_enter(int firstc, long count, int indent, bool init found_one = true; } } + + if (*p_tal != NUL) { + redraw_tabline = true; + found_one = true; + } + if (found_one) { redraw_statuslines(); } @@ -859,36 +906,6 @@ static uint8_t *command_line_enter(int firstc, long count, int indent, bool init s->state.check = command_line_check; s->state.execute = command_line_execute; - TryState tstate; - Error err = ERROR_INIT; - bool tl_ret = true; - save_v_event_T save_v_event; - dict_T *dict = get_v_event(&save_v_event); - char firstcbuf[2]; - firstcbuf[0] = (char)(firstc > 0 ? firstc : '-'); - firstcbuf[1] = 0; - - if (has_event(EVENT_CMDLINEENTER)) { - // set v:event to a dictionary with information about the commandline - tv_dict_add_str(dict, S_LEN("cmdtype"), firstcbuf); - tv_dict_add_nr(dict, S_LEN("cmdlevel"), ccline.level); - tv_dict_set_keys_readonly(dict); - try_enter(&tstate); - - apply_autocmds(EVENT_CMDLINEENTER, firstcbuf, firstcbuf, false, curbuf); - restore_v_event(dict, &save_v_event); - - tl_ret = try_leave(&tstate, &err); - if (!tl_ret && ERROR_SET(&err)) { - msg_putchar('\n'); - msg_printf_attr(HL_ATTR(HLF_E)|MSG_HIST, (char *)e_autocmd_err, err.msg); - api_clear_error(&err); - redrawcmd(); - } - tl_ret = true; - } - may_trigger_modechanged(); - state_enter(&s->state); if (has_event(EVENT_CMDLINELEAVE)) { @@ -958,6 +975,7 @@ static uint8_t *command_line_enter(int firstc, long count, int indent, bool init cmdpreview = save_cmdpreview; // restore preview state redraw_all_later(SOME_VALID); } + may_trigger_modechanged(); setmouse(); ui_cursor_shape(); // may show different cursor shape sb_text_end_cmdline(); @@ -983,11 +1001,14 @@ theend: } if (cmdheight0) { + made_cmdheight_nonzero = true; + // Restore cmdheight set_option_value("ch", 0L, NULL, 0); - // Redraw is needed for command line completion redraw_all_later(CLEAR); + + made_cmdheight_nonzero = false; } return p; @@ -1066,7 +1087,8 @@ static int command_line_execute(VimState *state, int key) // Don't ignore it for the input() function. if ((s->c == Ctrl_C) && s->firstc != '@' - && !s->break_ctrl_c + // do clear got_int in Ex mode to avoid infinite Ctrl-C loop + && (!s->break_ctrl_c || exmode_active) && !global_busy) { got_int = false; } @@ -3327,7 +3349,7 @@ static void ui_ext_cmdline_show(CmdlineInfo *line) { Arena arena = ARENA_EMPTY; arena_start(&arena, &ui_ext_fixblk); - Array content = ARRAY_DICT_INIT; + Array content; if (cmdline_star) { content = arena_array(&arena, 1); size_t len = 0; @@ -3814,6 +3836,7 @@ void redrawcmd(void) redrawing_cmdline = true; + sb_text_restart_cmdline(); msg_start(); redrawcmdprompt(); @@ -4142,8 +4165,7 @@ char_u *ExpandOne(expand_T *xp, char_u *str, char_u *orig, int options, int mode compl_selected = findex; cmdline_pum_display(false); } else if (p_wmnu) { - win_redr_status_matches(xp, xp->xp_numfiles, (char_u **)xp->xp_files, - findex, cmd_showtail); + win_redr_status_matches(xp, xp->xp_numfiles, xp->xp_files, findex, cmd_showtail); } if (findex == -1) { return vim_strsave(orig_save); @@ -4163,7 +4185,7 @@ char_u *ExpandOne(expand_T *xp, char_u *str, char_u *orig, int options, int mode // free old names if (xp->xp_numfiles != -1 && mode != WILD_ALL && mode != WILD_LONGEST) { - FreeWild(xp->xp_numfiles, (char_u **)xp->xp_files); + FreeWild(xp->xp_numfiles, xp->xp_files); xp->xp_numfiles = -1; XFREE_CLEAR(orig_save); } @@ -4181,8 +4203,7 @@ char_u *ExpandOne(expand_T *xp, char_u *str, char_u *orig, int options, int mode /* * Do the expansion. */ - if (ExpandFromContext(xp, str, &xp->xp_numfiles, (char_u ***)&xp->xp_files, - options) == FAIL) { + if (ExpandFromContext(xp, str, &xp->xp_numfiles, &xp->xp_files, options) == FAIL) { #ifdef FNAME_ILLEGAL /* Illegal file name has been silently skipped. But when there * are wildcards, the real problem is that there was no match, @@ -4198,7 +4219,7 @@ char_u *ExpandOne(expand_T *xp, char_u *str, char_u *orig, int options, int mode } } else { // Escape the matches for use on the command line. - ExpandEscape(xp, str, xp->xp_numfiles, (char_u **)xp->xp_files, options); + ExpandEscape(xp, str, xp->xp_numfiles, xp->xp_files, options); /* * Check for matching suffixes in file names. @@ -4323,12 +4344,12 @@ void ExpandInit(expand_T *xp) void ExpandCleanup(expand_T *xp) { if (xp->xp_numfiles >= 0) { - FreeWild(xp->xp_numfiles, (char_u **)xp->xp_files); + FreeWild(xp->xp_numfiles, xp->xp_files); xp->xp_numfiles = -1; } } -void ExpandEscape(expand_T *xp, char_u *str, int numfiles, char_u **files, int options) +void ExpandEscape(expand_T *xp, char_u *str, int numfiles, char **files, int options) { int i; char_u *p; @@ -4354,9 +4375,9 @@ void ExpandEscape(expand_T *xp, char_u *str, int numfiles, char_u **files, int o for (i = 0; i < numfiles; ++i) { // for ":set path=" we need to escape spaces twice if (xp->xp_backslash == XP_BS_THREE) { - p = vim_strsave_escaped(files[i], (char_u *)" "); + p = vim_strsave_escaped((char_u *)files[i], (char_u *)" "); xfree(files[i]); - files[i] = p; + files[i] = (char *)p; #if defined(BACKSLASH_IN_FILENAME) p = vim_strsave_escaped(files[i], (char_u *)" "); xfree(files[i]); @@ -4370,7 +4391,7 @@ void ExpandEscape(expand_T *xp, char_u *str, int numfiles, char_u **files, int o xp->xp_shell ? VSE_SHELL : vse_what); #endif xfree(files[i]); - files[i] = p; + files[i] = (char *)p; /* If 'str' starts with "\~", replace "~" at start of * files[i] with "\~". */ @@ -4390,10 +4411,10 @@ void ExpandEscape(expand_T *xp, char_u *str, int numfiles, char_u **files, int o * Insert a backslash before characters in a tag name that * would terminate the ":tag" command. */ - for (i = 0; i < numfiles; ++i) { - p = vim_strsave_escaped(files[i], (char_u *)"\\|\""); + for (i = 0; i < numfiles; i++) { + p = vim_strsave_escaped((char_u *)files[i], (char_u *)"\\|\""); xfree(files[i]); - files[i] = p; + files[i] = (char *)p; } } } @@ -4449,7 +4470,7 @@ char *vim_strsave_fnameescape(const char *const fname, const int what) // '>' and '+' are special at the start of some commands, e.g. ":edit" and // ":write". "cd -" has a special meaning. if (*p == '>' || *p == '+' || (*p == '-' && p[1] == NUL)) { - escape_fname((char_u **)&p); + escape_fname(&p); } return p; @@ -4458,28 +4479,26 @@ char *vim_strsave_fnameescape(const char *const fname, const int what) /* * Put a backslash before the file name in "pp", which is in allocated memory. */ -static void escape_fname(char_u **pp) +static void escape_fname(char **pp) { char_u *p = xmalloc(STRLEN(*pp) + 2); p[0] = '\\'; STRCPY(p + 1, *pp); xfree(*pp); - *pp = p; + *pp = (char *)p; } -/* - * For each file name in files[num_files]: - * If 'orig_pat' starts with "~/", replace the home directory with "~". - */ -void tilde_replace(char_u *orig_pat, int num_files, char_u **files) +/// For each file name in files[num_files]: +/// If 'orig_pat' starts with "~/", replace the home directory with "~". +void tilde_replace(char_u *orig_pat, int num_files, char **files) { char *p; if (orig_pat[0] == '~' && vim_ispathsep(orig_pat[1])) { for (int i = 0; i < num_files; i++) { - p = home_replace_save(NULL, (char *)files[i]); + p = home_replace_save(NULL, files[i]); xfree(files[i]); - files[i] = (char_u *)p; + files[i] = p; } } } @@ -4500,7 +4519,7 @@ static int showmatches(expand_T *xp, int wildmenu) #define L_SHOWFILE(m) (showtail \ ? sm_gettail(files_found[m], false) : files_found[m]) int num_files; - char_u **files_found; + char **files_found; int i, j, k; int maxlen; int lines; @@ -4520,7 +4539,7 @@ static int showmatches(expand_T *xp, int wildmenu) } } else { num_files = xp->xp_numfiles; - files_found = (char_u **)xp->xp_files; + files_found = xp->xp_files; showtail = cmd_showtail; } @@ -4535,10 +4554,9 @@ static int showmatches(expand_T *xp, int wildmenu) compl_match_array = xcalloc((size_t)compl_match_arraysize, sizeof(pumitem_T)); for (i = 0; i < num_files; i++) { - compl_match_array[i].pum_text = L_SHOWFILE(i); + compl_match_array[i].pum_text = (char_u *)L_SHOWFILE(i); } - char_u *endpos = (showtail - ? sm_gettail((char_u *)xp->xp_pattern, true) : (char_u *)xp->xp_pattern); + char_u *endpos = (char_u *)(showtail ? sm_gettail(xp->xp_pattern, true) : xp->xp_pattern); if (ui_has(kUICmdline)) { compl_startcol = (int)(endpos - ccline.cmdbuff); } else { @@ -4570,10 +4588,10 @@ static int showmatches(expand_T *xp, int wildmenu) if (!showtail && (xp->xp_context == EXPAND_FILES || xp->xp_context == EXPAND_SHELLCMD || xp->xp_context == EXPAND_BUFFERS)) { - home_replace(NULL, (char *)files_found[i], (char *)NameBuff, MAXPATHL, true); + home_replace(NULL, files_found[i], (char *)NameBuff, MAXPATHL, true); j = vim_strsize((char *)NameBuff); } else { - j = vim_strsize((char *)L_SHOWFILE(i)); + j = vim_strsize(L_SHOWFILE(i)); } if (j > maxlen) { maxlen = j; @@ -4606,8 +4624,8 @@ static int showmatches(expand_T *xp, int wildmenu) lastlen = 999; for (k = i; k < num_files; k += lines) { if (xp->xp_context == EXPAND_TAGS_LISTFILES) { - msg_outtrans_attr(files_found[k], HL_ATTR(HLF_D)); - p = files_found[k] + STRLEN(files_found[k]) + 1; + msg_outtrans_attr((char_u *)files_found[k], HL_ATTR(HLF_D)); + p = (char_u *)files_found[k] + STRLEN(files_found[k]) + 1; msg_advance(maxlen + 1); msg_puts((const char *)p); msg_advance(maxlen + 3); @@ -4625,8 +4643,8 @@ static int showmatches(expand_T *xp, int wildmenu) // Expansion was done before and special characters // were escaped, need to halve backslashes. Also // $HOME has been replaced with ~/. - char_u *exp_path = expand_env_save_opt(files_found[k], true); - char_u *path = exp_path != NULL ? exp_path : files_found[k]; + char_u *exp_path = expand_env_save_opt((char_u *)files_found[k], true); + char_u *path = exp_path != NULL ? exp_path : (char_u *)files_found[k]; char_u *halved_slash = backslash_halve_save(path); j = os_isdir(halved_slash); xfree(exp_path); @@ -4635,17 +4653,17 @@ static int showmatches(expand_T *xp, int wildmenu) } } else { // Expansion was done here, file names are literal. - j = os_isdir(files_found[k]); + j = os_isdir((char_u *)files_found[k]); } if (showtail) { - p = L_SHOWFILE(k); + p = (char_u *)L_SHOWFILE(k); } else { - home_replace(NULL, (char *)files_found[k], (char *)NameBuff, MAXPATHL, true); + home_replace(NULL, files_found[k], (char *)NameBuff, MAXPATHL, true); p = NameBuff; } } else { - j = FALSE; - p = L_SHOWFILE(k); + j = false; + p = (char_u *)L_SHOWFILE(k); } lastlen = msg_outtrans_attr(p, j ? attr : 0); } @@ -4674,17 +4692,15 @@ static int showmatches(expand_T *xp, int wildmenu) return EXPAND_OK; } -/* - * Private path_tail for showmatches() (and win_redr_status_matches()): - * Find tail of file name path, but ignore trailing "/". - */ -char_u *sm_gettail(char_u *s, bool eager) +/// Private path_tail for showmatches() (and win_redr_status_matches()): +/// Find tail of file name path, but ignore trailing "/". +char *sm_gettail(char *s, bool eager) { char_u *p; - char_u *t = s; - int had_sep = FALSE; + char_u *t = (char_u *)s; + int had_sep = false; - for (p = s; *p != NUL;) { + for (p = (char_u *)s; *p != NUL;) { if (vim_ispathsep(*p) #ifdef BACKSLASH_IN_FILENAME && !rem_backslash(p) @@ -4701,7 +4717,7 @@ char_u *sm_gettail(char_u *s, bool eager) } MB_PTR_ADV(p); } - return t; + return (char *)t; } /* @@ -4979,7 +4995,7 @@ void set_cmd_context(expand_T *xp, char_u *str, int len, int col, int use_ccline /// @param col position of cursor /// @param matchcount return: nr of matches /// @param matches return: array of pointers to matches -int expand_cmdline(expand_T *xp, char_u *str, int col, int *matchcount, char_u ***matches) +int expand_cmdline(expand_T *xp, char_u *str, int col, int *matchcount, char ***matches) { char_u *file_str = NULL; int options = WILD_ADD_SLASH|WILD_SILENT; @@ -5015,7 +5031,7 @@ int expand_cmdline(expand_T *xp, char_u *str, int col, int *matchcount, char_u * // Cleanup matches for help tags: // Remove "@ab" if the top of 'helplang' is "ab" and the language of the first // tag matches it. Otherwise remove "@en" if "en" is the only language. -static void cleanup_help_tags(int num_file, char_u **file) +static void cleanup_help_tags(int num_file, char **file) { char_u buf[4]; char_u *p = buf; @@ -5069,7 +5085,7 @@ typedef char *(*ExpandFunc)(expand_T *, int); /// Do the expansion based on xp->xp_context and "pat". /// /// @param options WILD_ flags -static int ExpandFromContext(expand_T *xp, char_u *pat, int *num_file, char_u ***file, int options) +static int ExpandFromContext(expand_T *xp, char_u *pat, int *num_file, char ***file, int options) { regmatch_T regmatch; int ret; @@ -5164,7 +5180,7 @@ static int ExpandFromContext(expand_T *xp, char_u *pat, int *num_file, char_u ** /* With an empty argument we would get all the help tags, which is * very slow. Get matches for "help" instead. */ if (find_help_tags(*pat == NUL ? "help" : (char *)pat, - num_file, (char ***)file, false) == OK) { + num_file, file, false) == OK) { cleanup_help_tags(*num_file, *file); return OK; } @@ -5181,10 +5197,10 @@ static int ExpandFromContext(expand_T *xp, char_u *pat, int *num_file, char_u ** return OK; } if (xp->xp_context == EXPAND_BUFFERS) { - return ExpandBufnames((char *)pat, num_file, (char ***)file, options); + return ExpandBufnames((char *)pat, num_file, file, options); } if (xp->xp_context == EXPAND_DIFF_BUFFERS) { - return ExpandBufnames((char *)pat, num_file, (char ***)file, options | BUF_DIFF_FILTER); + return ExpandBufnames((char *)pat, num_file, file, options | BUF_DIFF_FILTER); } if (xp->xp_context == EXPAND_TAGS || xp->xp_context == EXPAND_TAGS_LISTFILES) { @@ -5192,8 +5208,7 @@ static int ExpandFromContext(expand_T *xp, char_u *pat, int *num_file, char_u ** } if (xp->xp_context == EXPAND_COLORS) { char *directories[] = { "colors", NULL }; - return ExpandRTDir(pat, DIP_START + DIP_OPT + DIP_LUA, num_file, file, - directories); + return ExpandRTDir(pat, DIP_START + DIP_OPT + DIP_LUA, num_file, file, directories); } if (xp->xp_context == EXPAND_COMPILER) { char *directories[] = { "compiler", NULL }; @@ -5300,8 +5315,7 @@ static int ExpandFromContext(expand_T *xp, char_u *pat, int *num_file, char_u ** if (tab[i].ic) { regmatch.rm_ic = TRUE; } - ExpandGeneric(xp, ®match, num_file, file, tab[i].func, - tab[i].escaped); + ExpandGeneric(xp, ®match, num_file, file, tab[i].func, tab[i].escaped); ret = OK; break; } @@ -5321,7 +5335,7 @@ static int ExpandFromContext(expand_T *xp, char_u *pat, int *num_file, char_u ** /// program. Matching strings are copied into an array, which is returned. /// /// @param func returns a string from the list -static void ExpandGeneric(expand_T *xp, regmatch_T *regmatch, int *num_file, char_u ***file, +static void ExpandGeneric(expand_T *xp, regmatch_T *regmatch, int *num_file, char ***file, CompleteListItemGetter func, int escaped) { int i; @@ -5346,7 +5360,7 @@ static void ExpandGeneric(expand_T *xp, regmatch_T *regmatch, int *num_file, cha } assert(count < INT_MAX); *num_file = (int)count; - *file = (char_u **)xmalloc(count * sizeof(char_u *)); + *file = xmalloc(count * sizeof(char_u *)); // copy the matching names into allocated memory count = 0; @@ -5364,7 +5378,7 @@ static void ExpandGeneric(expand_T *xp, regmatch_T *regmatch, int *num_file, cha } else { str = vim_strsave(str); } - (*file)[count++] = str; + (*file)[count++] = (char *)str; if (func == get_menu_names) { // Test for separator added by get_menu_names(). str += STRLEN(str) - 1; @@ -5401,14 +5415,14 @@ static void ExpandGeneric(expand_T *xp, regmatch_T *regmatch, int *num_file, cha /// *file will either be set to NULL or point to /// allocated memory. /// @param flagsarg is a combination of EW_* flags. -static void expand_shellcmd(char_u *filepat, int *num_file, char_u ***file, int flagsarg) +static void expand_shellcmd(char_u *filepat, int *num_file, char ***file, int flagsarg) FUNC_ATTR_NONNULL_ALL { char_u *pat; int i; char_u *path = NULL; garray_T ga; - char_u *buf = xmalloc(MAXPATHL); + char *buf = xmalloc(MAXPATHL); size_t l; char_u *s, *e; int flags = flagsarg; @@ -5475,7 +5489,7 @@ static void expand_shellcmd(char_u *filepat, int *num_file, char_u ***file, int break; } STRLCPY(buf, s, l + 1); - add_pathsep((char *)buf); + add_pathsep(buf); l = STRLEN(buf); STRLCPY(buf + l, pat, MAXPATHL - l); @@ -5485,7 +5499,7 @@ static void expand_shellcmd(char_u *filepat, int *num_file, char_u ***file, int ga_grow(&ga, *num_file); { for (i = 0; i < *num_file; i++) { - char_u *name = (*file)[i]; + char_u *name = (char_u *)(*file)[i]; if (STRLEN(name) > l) { // Check if this name was already found. @@ -5524,7 +5538,7 @@ static void expand_shellcmd(char_u *filepat, int *num_file, char_u ***file, int /// Call "user_expand_func()" to invoke a user defined Vim script function and /// return the result (either a string, a List or NULL). static void *call_user_expand_func(user_expand_func_T user_expand_func, expand_T *xp, int *num_file, - char_u ***file) + char ***file) FUNC_ATTR_NONNULL_ALL { char_u keep = 0; @@ -5565,10 +5579,8 @@ static void *call_user_expand_func(user_expand_func_T user_expand_func, expand_T return ret; } -/* - * Expand names with a function defined by the user. - */ -static int ExpandUserDefined(expand_T *xp, regmatch_T *regmatch, int *num_file, char_u ***file) +/// Expand names with a function defined by the user. +static int ExpandUserDefined(expand_T *xp, regmatch_T *regmatch, int *num_file, char ***file) { char_u *e; garray_T ga; @@ -5606,10 +5618,8 @@ static int ExpandUserDefined(expand_T *xp, regmatch_T *regmatch, int *num_file, return OK; } -/* - * Expand names with a list returned by a function defined by the user. - */ -static int ExpandUserList(expand_T *xp, int *num_file, char_u ***file) +/// Expand names with a list returned by a function defined by the user. +static int ExpandUserList(expand_T *xp, int *num_file, char ***file) { list_T *const retlist = call_user_expand_func((user_expand_func_T)call_func_retlist, xp, num_file, file); @@ -5635,7 +5645,7 @@ static int ExpandUserList(expand_T *xp, int *num_file, char_u ***file) return OK; } -static int ExpandUserLua(expand_T *xp, int *num_file, char_u ***file) +static int ExpandUserLua(expand_T *xp, int *num_file, char ***file) { typval_T rettv; nlua_call_user_expand_func(xp, &rettv); @@ -5673,7 +5683,7 @@ static int ExpandUserLua(expand_T *xp, int *num_file, char_u ***file) /// 'packpath'/pack/ * /opt/ * /{dirnames}/{pat}.vim /// When "flags" has DIP_LUA: search also performed for .lua files /// "dirnames" is an array with one or more directory names. -static int ExpandRTDir(char_u *pat, int flags, int *num_file, char_u ***file, char *dirnames[]) +static int ExpandRTDir(char_u *pat, int flags, int *num_file, char ***file, char *dirnames[]) { *num_file = 0; *file = NULL; @@ -5782,7 +5792,7 @@ static int ExpandRTDir(char_u *pat, int flags, int *num_file, char_u ***file, ch /// Expand loadplugin names: /// 'packpath'/pack/ * /opt/{pat} -static int ExpandPackAddDir(char_u *pat, int *num_file, char_u ***file) +static int ExpandPackAddDir(char_u *pat, int *num_file, char ***file) { garray_T ga; @@ -5836,7 +5846,7 @@ void globpath(char_u *path, char_u *file, garray_T *ga, int expand_options) add_pathsep((char *)buf); STRCAT(buf, file); // NOLINT - char_u **p; + char **p; int num_p = 0; (void)ExpandFromContext(&xpc, buf, &num_p, &p, WILD_SILENT | expand_options); @@ -5847,7 +5857,7 @@ void globpath(char_u *path, char_u *file, garray_T *ga, int expand_options) ga_grow(ga, num_p); // take over the pointers and put them in "ga" for (int i = 0; i < num_p; i++) { - ((char_u **)ga->ga_data)[ga->ga_len] = p[i]; + ((char_u **)ga->ga_data)[ga->ga_len] = (char_u *)p[i]; ga->ga_len++; } xfree(p); @@ -6802,9 +6812,13 @@ static int open_cmdwin(void) // Avoid command-line window first character being concealed. curwin->w_p_cole = 0; + // First go back to the original window. wp = curwin; set_bufref(&bufref, curbuf); win_goto(old_curwin); + + // win_goto() may trigger an autocommand that already closes the + // cmdline window. if (win_valid(wp) && wp != curwin) { win_close(wp, true, false); } diff --git a/src/nvim/ex_session.c b/src/nvim/ex_session.c index 6ca6da9cd0..1d670afa6d 100644 --- a/src/nvim/ex_session.c +++ b/src/nvim/ex_session.c @@ -193,6 +193,9 @@ static int ses_do_win(win_T *wp) if (bt_help(wp->w_buffer)) { return ssop_flags & SSOP_HELP; } + if (bt_terminal(wp->w_buffer)) { + return ssop_flags & SSOP_TERMINAL; + } return true; } @@ -407,6 +410,8 @@ static int put_view(FILE *fd, win_T *wp, int add_edit, unsigned *flagp, int curr if ((flagp == &ssop_flags) && alt != NULL && alt->b_fname != NULL && *alt->b_fname != NUL && alt->b_p_bl + // do not set balt if buffer is terminal and "terminal" is not set in options + && !(bt_terminal(alt) && !(ssop_flags & SSOP_TERMINAL)) && (fputs("balt ", fd) < 0 || ses_fname(fd, alt, flagp, true) == FAIL)) { return FAIL; @@ -616,6 +621,7 @@ static int makeopens(FILE *fd, char_u *dirnow) FOR_ALL_BUFFERS(buf) { if (!(only_save_windows && buf->b_nwindows == 0) && !(buf->b_help && !(ssop_flags & SSOP_HELP)) + && !(bt_terminal(buf) && !(ssop_flags & SSOP_TERMINAL)) && buf->b_fname != NULL && buf->b_p_bl) { if (fprintf(fd, "badd +%" PRId64 " ", diff --git a/src/nvim/file_search.c b/src/nvim/file_search.c index ca276b8a40..ebefd1157c 100644 --- a/src/nvim/file_search.c +++ b/src/nvim/file_search.c @@ -82,12 +82,11 @@ typedef struct ff_stack { char_u *ffs_fix_path; char_u *ffs_wc_path; - /* files/dirs found in the above directory, matched by the first wildcard - * of wc_part - */ - char_u **ffs_filearray; + // files/dirs found in the above directory, matched by the first wildcard + // of wc_part + char **ffs_filearray; int ffs_filearray_size; - char_u ffs_filearray_cur; // needed for partly handled dirs + int ffs_filearray_cur; // needed for partly handled dirs /* to store status of partly handled directories * 0: we work on this directory for the first time @@ -683,12 +682,12 @@ char_u *vim_findfile(void *search_ctx_arg) * to handle the expansion of '**' into an empty string. */ if (stackp->ffs_filearray == NULL) { - char_u *dirptrs[2]; + char *dirptrs[2]; /* we use filepath to build the path expand_wildcards() should * expand. */ - dirptrs[0] = file_path; + dirptrs[0] = (char *)file_path; dirptrs[1] = NULL; // if we have a start dir copy it in @@ -743,7 +742,7 @@ char_u *vim_findfile(void *search_ctx_arg) if (stackp->ffs_star_star_empty == 0) { // if not done before, expand '**' to empty stackp->ffs_star_star_empty = 1; - dirptrs[1] = stackp->ffs_fix_path; + dirptrs[1] = (char *)stackp->ffs_fix_path; } } @@ -773,9 +772,9 @@ char_u *vim_findfile(void *search_ctx_arg) * Expand wildcards like "*" and "$VAR". * If the path is a URL don't try this. */ - if (path_with_url((char *)dirptrs[0])) { + if (path_with_url(dirptrs[0])) { stackp->ffs_filearray = xmalloc(sizeof(char *)); - stackp->ffs_filearray[0] = vim_strsave(dirptrs[0]); + stackp->ffs_filearray[0] = xstrdup(dirptrs[0]); stackp->ffs_filearray_size = 1; } else { /* Add EW_NOTWILD because the expanded path may contain @@ -801,10 +800,9 @@ char_u *vim_findfile(void *search_ctx_arg) * We don't have further wildcards to expand, so we have to * check for the final file now. */ - for (int i = stackp->ffs_filearray_cur; - i < stackp->ffs_filearray_size; ++i) { - if (!path_with_url((char *)stackp->ffs_filearray[i]) - && !os_isdir(stackp->ffs_filearray[i])) { + for (int i = stackp->ffs_filearray_cur; i < stackp->ffs_filearray_size; i++) { + if (!path_with_url(stackp->ffs_filearray[i]) + && !os_isdir((char_u *)stackp->ffs_filearray[i])) { continue; // not a directory } // prepare the filename to be checked for existence below @@ -862,8 +860,8 @@ char_u *vim_findfile(void *search_ctx_arg) #endif // push dir to examine rest of subdirs later - assert(i < UCHAR_MAX - 1); - stackp->ffs_filearray_cur = (char_u)(i + 1); + assert(i < INT_MAX); + stackp->ffs_filearray_cur = i + 1; ff_push(search_ctx, stackp); if (!path_with_url((char *)file_path)) { @@ -897,17 +895,13 @@ char_u *vim_findfile(void *search_ctx_arg) } } } else { - /* - * still wildcards left, push the directories for further - * search - */ - for (int i = stackp->ffs_filearray_cur; - i < stackp->ffs_filearray_size; ++i) { - if (!os_isdir(stackp->ffs_filearray[i])) { + // still wildcards left, push the directories for further search + for (int i = stackp->ffs_filearray_cur; i < stackp->ffs_filearray_size; i++) { + if (!os_isdir((char_u *)stackp->ffs_filearray[i])) { continue; // not a directory } ff_push(search_ctx, - ff_create_stack_element(stackp->ffs_filearray[i], + ff_create_stack_element((char_u *)stackp->ffs_filearray[i], rest_of_wildcards, stackp->ffs_level - 1, 0)); } @@ -927,11 +921,11 @@ char_u *vim_findfile(void *search_ctx_arg) stackp->ffs_fix_path) == 0) { continue; // don't repush same directory } - if (!os_isdir(stackp->ffs_filearray[i])) { + if (!os_isdir((char_u *)stackp->ffs_filearray[i])) { continue; // not a directory } ff_push(search_ctx, - ff_create_stack_element(stackp->ffs_filearray[i], + ff_create_stack_element((char_u *)stackp->ffs_filearray[i], stackp->ffs_wc_path, stackp->ffs_level - 1, 1)); } } diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index 6782465ef1..b98984017b 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -710,7 +710,7 @@ int readfile(char *fname, char *sfname, linenr_T from, linenr_T lines_to_skip, fenc_alloced = false; } else { fenc_next = (char *)p_fencs; // try items in 'fileencodings' - fenc = (char *)next_fenc((char_u **)&fenc_next, &fenc_alloced); + fenc = (char *)next_fenc(&fenc_next, &fenc_alloced); } /* @@ -804,7 +804,7 @@ retry: xfree(fenc); } if (fenc_next != NULL) { - fenc = (char *)next_fenc((char_u **)&fenc_next, &fenc_alloced); + fenc = (char *)next_fenc(&fenc_next, &fenc_alloced); } else { fenc = ""; fenc_alloced = false; @@ -2060,7 +2060,7 @@ void set_forced_fenc(exarg_T *eap) /// NULL. /// When *pp is not set to NULL, the result is in allocated memory and "alloced" /// is set to true. -static char_u *next_fenc(char_u **pp, bool *alloced) +static char_u *next_fenc(char **pp, bool *alloced) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET { char_u *p; @@ -2071,13 +2071,13 @@ static char_u *next_fenc(char_u **pp, bool *alloced) *pp = NULL; return (char_u *)""; } - p = (char_u *)vim_strchr((char *)(*pp), ','); + p = (char_u *)vim_strchr((*pp), ','); if (p == NULL) { - r = enc_canonize(*pp); + r = enc_canonize((char_u *)(*pp)); *pp += STRLEN(*pp); } else { - r = vim_strnsave(*pp, (size_t)(p - *pp)); - *pp = p + 1; + r = vim_strnsave((char_u *)(*pp), (size_t)(p - (char_u *)(*pp))); + *pp = (char *)p + 1; p = enc_canonize(r); xfree(r); r = p; @@ -5270,7 +5270,6 @@ void forward_slash(char_u *fname) return; } for (p = fname; *p != NUL; p++) { - // The Big5 encoding can have '\' in the trail byte. if (*p == '\\') { *p = '/'; } @@ -5296,6 +5295,9 @@ static void vim_mktempdir(void) char user[40] = { 0 }; (void)os_get_username(user, sizeof(user)); + // Usernames may contain slashes! #19240 + memchrsub(user, '/', '_', sizeof(user)); + memchrsub(user, '\\', '_', sizeof(user)); // Make sure the umask doesn't remove the executable bit. // "repl" has been reported to use "0177". @@ -5398,7 +5400,7 @@ int readdir_core(garray_T *gap, const char *path, void *context, CheckItem check os_closedir(&dir); if (gap->ga_len > 0) { - sort_strings((char_u **)gap->ga_data, gap->ga_len); + sort_strings(gap->ga_data, gap->ga_len); } return OK; diff --git a/src/nvim/fold.h b/src/nvim/fold.h index 60ea4b322e..f34e6d43c3 100644 --- a/src/nvim/fold.h +++ b/src/nvim/fold.h @@ -18,7 +18,7 @@ typedef struct foldinfo { // other fields are invalid int fi_low_level; // lowest fold level that starts in the same // line - long fi_lines; + linenr_T fi_lines; } foldinfo_T; #define FOLDINFO_INIT { 0, 0, 0, 0 } diff --git a/src/nvim/garray.c b/src/nvim/garray.c index 0c76e1a919..7a3c14b1bb 100644 --- a/src/nvim/garray.c +++ b/src/nvim/garray.c @@ -116,7 +116,7 @@ void ga_grow(garray_T *gap, int n) /// @param gap void ga_remove_duplicate_strings(garray_T *gap) { - char_u **fnames = gap->ga_data; + char **fnames = gap->ga_data; // sort the growing array, which puts duplicates next to each other sort_strings(fnames, gap->ga_len); diff --git a/src/nvim/generators/gen_api_dispatch.lua b/src/nvim/generators/gen_api_dispatch.lua index 4cf282770d..b167767f7a 100644 --- a/src/nvim/generators/gen_api_dispatch.lua +++ b/src/nvim/generators/gen_api_dispatch.lua @@ -91,7 +91,7 @@ local deprecated_aliases = require("api.dispatch_deprecated") for _,f in ipairs(shallowcopy(functions)) do local ismethod = false if startswith(f.name, "nvim_") then - if startswith(f.name, "nvim__") then + if startswith(f.name, "nvim__") or f.name == "nvim_error_event" then f.since = -1 elseif f.since == nil then print("Function "..f.name.." lacks since field.\n") @@ -149,7 +149,7 @@ local exported_attributes = {'name', 'return_type', 'method', 'since', 'deprecated_since'} local exported_functions = {} for _,f in ipairs(functions) do - if not startswith(f.name, "nvim__") then + if not (startswith(f.name, "nvim__") or f.name == "nvim_error_event") then local f_exported = {} for _,attr in ipairs(exported_attributes) do f_exported[attr] = f[attr] diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index 00372d4f3d..28ff0cbd59 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -21,6 +21,7 @@ #include "nvim/garray.h" #include "nvim/getchar.h" #include "nvim/input.h" +#include "nvim/insexpand.h" #include "nvim/keycodes.h" #include "nvim/lua/executor.h" #include "nvim/main.h" @@ -29,6 +30,7 @@ #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/message.h" +#include "nvim/mouse.h" #include "nvim/move.h" #include "nvim/normal.h" #include "nvim/ops.h" @@ -1583,16 +1585,18 @@ int vgetc(void) vgetc_char = c; } - // If mappings are enabled (i.e., not Ctrl-v) and the user directly typed - // something with a meta- or alt- modifier that was not mapped, interpret - // <M-x> as <Esc>x rather than as an unbound meta keypress. #8213 - // In Terminal mode, however, this is not desirable. #16220 - if (!no_mapping && KeyTyped && !(State & MODE_TERMINAL) - && (mod_mask == MOD_MASK_ALT || mod_mask == MOD_MASK_META)) { + // If mappings are enabled (i.e., not i_CTRL-V) and the user directly typed something with + // MOD_MASK_ALT (<M-/<A- modifier) that was not mapped, interpret <M-x> as <Esc>x rather + // than as an unbound <M-x> keypress. #8213 + // In Terminal mode, however, this is not desirable. #16202 #16220 + // Also do not do this for mouse keys, as terminals encode mouse events as CSI sequences, and + // MOD_MASK_ALT has a meaning even for unmapped mouse keys. + if (!no_mapping && KeyTyped && mod_mask == MOD_MASK_ALT && !(State & MODE_TERMINAL) + && !is_mouse_key(c)) { mod_mask = 0; int len = ins_char_typebuf(c, 0); (void)ins_char_typebuf(ESC, 0); - ungetchars(len + 3); // The ALT/META modifier takes three more bytes + ungetchars(len + 3); // K_SPECIAL KS_MODIFIER MOD_MASK_ALT takes 3 more bytes continue; } @@ -1733,7 +1737,7 @@ static bool at_ins_compl_key(void) || ((compl_cont_status & CONT_LOCAL) && (c == Ctrl_N || c == Ctrl_P)); } -/// Check if typebuf.tb_buf[] contains a modifer plus key that can be changed +/// Check if typebuf.tb_buf[] contains a modifier plus key that can be changed /// into just a key, apply that. /// Check from typebuf.tb_buf[typebuf.tb_off] to typebuf.tb_buf[typebuf.tb_off + "max_offset"]. /// @return the length of the replaced bytes, 0 if nothing changed, -1 for error. @@ -1807,7 +1811,7 @@ static int handle_mapping(int *keylenp, bool *timedout, int *mapdepth) int local_State = get_real_state(); bool is_plug_map = false; - // If typehead starts with <Plug> then remap, even for a "noremap" mapping. + // If typeahead starts with <Plug> then remap, even for a "noremap" mapping. if (typebuf.tb_len >= 3 && typebuf.tb_buf[typebuf.tb_off] == K_SPECIAL && typebuf.tb_buf[typebuf.tb_off + 1] == KS_EXTRA diff --git a/src/nvim/globals.h b/src/nvim/globals.h index 8d896aef31..a41836353a 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -163,10 +163,6 @@ EXTERN colnr_T dollar_vcol INIT(= -1); // by the match.) EXTERN int compl_length INIT(= 0); -// Set when character typed while looking for matches and it means we should -// stop looking for matches. -EXTERN int compl_interrupted INIT(= false); - // Set when doing something for completion that may call edit() recursively, // which is not allowed. Also used to disable folding during completion EXTERN bool compl_busy INIT(= false); @@ -471,6 +467,9 @@ EXTERN buf_T *curbuf INIT(= NULL); // currently active buffer #define FOR_ALL_BUFFERS_BACKWARDS(buf) \ for (buf_T *buf = lastbuf; buf != NULL; buf = buf->b_prev) +#define FOR_ALL_BUF_WININFO(buf, wip) \ + for ((wip) = (buf)->b_wininfo; (wip) != NULL; (wip) = (wip)->wi_next) // NOLINT + // Iterate through all the signs placed in a buffer #define FOR_ALL_SIGNS_IN_BUF(buf, sign) \ for ((sign) = (buf)->b_signlist; (sign) != NULL; (sign) = (sign)->se_next) // NOLINT @@ -945,6 +944,9 @@ EXTERN char e_loclist[] INIT(= N_("E776: No location list")); EXTERN char e_re_damg[] INIT(= N_("E43: Damaged match string")); EXTERN char e_re_corr[] INIT(= N_("E44: Corrupted regexp program")); EXTERN char e_readonly[] INIT(= N_("E45: 'readonly' option is set (add ! to override)")); +EXTERN char e_letwrong[] INIT(= N_("E734: Wrong variable type for %s=")); +EXTERN char e_illvar[] INIT(= N_("E461: Illegal variable name: %s")); +EXTERN char e_cannot_mod[] INIT(= N_("E995: Cannot modify existing variable")); EXTERN char e_readonlyvar[] INIT(= N_("E46: Cannot change read-only variable \"%.*s\"")); EXTERN char e_stringreq[] INIT(= N_("E928: String required")); EXTERN char e_dictreq[] INIT(= N_("E715: Dictionary required")); @@ -1082,4 +1084,7 @@ EXTERN char windowsVersion[20] INIT(= { 0 }); EXTERN int exit_need_delay INIT(= 0); +// Set when 'cmdheight' is changed from zero to one temporarily. +EXTERN bool made_cmdheight_nonzero INIT(= false); + #endif // NVIM_GLOBALS_H diff --git a/src/nvim/grid.c b/src/nvim/grid.c index 1268f987e1..72e85c425d 100644 --- a/src/nvim/grid.c +++ b/src/nvim/grid.c @@ -230,16 +230,12 @@ void grid_puts_len(ScreenGrid *grid, char_u *text, int textlen, int row, int col && *ptr != NUL) { c = *ptr; // check if this is the first byte of a multibyte - if (len > 0) { - mbyte_blen = utfc_ptr2len_len(ptr, (int)((text + len) - ptr)); - } else { - mbyte_blen = utfc_ptr2len((char *)ptr); - } - if (len >= 0) { - u8c = utfc_ptr2char_len(ptr, u8cc, (int)((text + len) - ptr)); - } else { - u8c = utfc_ptr2char(ptr, u8cc); - } + mbyte_blen = len > 0 + ? utfc_ptr2len_len(ptr, (int)((text + len) - ptr)) + : utfc_ptr2len((char *)ptr); + u8c = len >= 0 + ? utfc_ptr2char_len(ptr, u8cc, (int)((text + len) - ptr)) + : utfc_ptr2char(ptr, u8cc); mbyte_cells = utf_char2cells(u8c); if (p_arshape && !p_tbidi && ARABIC_CHAR(u8c)) { // Do Arabic shaping. @@ -248,8 +244,9 @@ void grid_puts_len(ScreenGrid *grid, char_u *text, int textlen, int row, int col nc = NUL; nc1 = NUL; } else { - nc = utfc_ptr2char_len(ptr + mbyte_blen, pcc, - (int)((text + len) - ptr - mbyte_blen)); + nc = len >= 0 + ? utfc_ptr2char_len(ptr + mbyte_blen, pcc, (int)((text + len) - ptr - mbyte_blen)) + : utfc_ptr2char(ptr + mbyte_blen, pcc); nc1 = pcc[0]; } pc = prev_c; diff --git a/src/nvim/grid_defs.h b/src/nvim/grid_defs.h index 1571340849..9252b8a371 100644 --- a/src/nvim/grid_defs.h +++ b/src/nvim/grid_defs.h @@ -128,4 +128,13 @@ typedef struct { const char *start; ///< Location where region starts. } StlClickRecord; +typedef struct { + int args[3]; + int icell; + int ncells; + int coloff; + int cur_attr; + int clear_width; +} GridLineEvent; + #endif // NVIM_GRID_DEFS_H diff --git a/src/nvim/hardcopy.c b/src/nvim/hardcopy.c index a4cf65e816..d7f7b8eb92 100644 --- a/src/nvim/hardcopy.c +++ b/src/nvim/hardcopy.c @@ -639,7 +639,7 @@ void ex_hardcopy(exarg_T *eap) char *errormsg = NULL; // Expand things like "%.ps". - if (expand_filename(eap, (char_u **)eap->cmdlinep, &errormsg) == FAIL) { + if (expand_filename(eap, eap->cmdlinep, &errormsg) == FAIL) { if (errormsg != NULL) { emsg(errormsg); } diff --git a/src/nvim/highlight.c b/src/nvim/highlight.c index 0f20eb1905..71c7194479 100644 --- a/src/nvim/highlight.c +++ b/src/nvim/highlight.c @@ -183,10 +183,10 @@ int ns_get_hl(NS ns_id, int hl_id, bool link, bool nodefault) bool valid_cache = it.version >= p->hl_valid; if (!valid_cache && p->hl_def != LUA_NOREF && !recursive) { - FIXED_TEMP_ARRAY(args, 3); - args.items[0] = INTEGER_OBJ((Integer)ns_id); - args.items[1] = STRING_OBJ(cstr_to_string((char *)syn_id2name(hl_id))); - args.items[2] = BOOLEAN_OBJ(link); + MAXSIZE_TEMP_ARRAY(args, 3); + ADD_C(args, INTEGER_OBJ((Integer)ns_id)); + ADD_C(args, STRING_OBJ(cstr_to_string((char *)syn_id2name(hl_id)))); + ADD_C(args, BOOLEAN_OBJ(link)); // TODO(bfredl): preload the "global" attr dict? Error err = ERROR_INIT; @@ -719,93 +719,111 @@ Dictionary hl_get_attr_by_id(Integer attr_id, Boolean rgb, Error *err) return dic; } - return hlattrs2dict(syn_attr2entry((int)attr_id), rgb); + return hlattrs2dict(NULL, syn_attr2entry((int)attr_id), rgb); } /// Converts an HlAttrs into Dictionary /// +/// @param[out] hl optional pre-allocated dictionary for return value +/// if present, must be allocated with at least 16 elements! /// @param[in] aep data to convert /// @param use_rgb use 'gui*' settings if true, else resorts to 'cterm*' -Dictionary hlattrs2dict(HlAttrs ae, bool use_rgb) +Dictionary hlattrs2dict(Dictionary *hl_alloc, HlAttrs ae, bool use_rgb) { - Dictionary hl = ARRAY_DICT_INIT; int mask = use_rgb ? ae.rgb_ae_attr : ae.cterm_ae_attr; + Dictionary hl = ARRAY_DICT_INIT; + if (hl_alloc) { + hl = *hl_alloc; + } else { + kv_ensure_space(hl, 16); + } if (mask & HL_BOLD) { - PUT(hl, "bold", BOOLEAN_OBJ(true)); + PUT_C(hl, "bold", BOOLEAN_OBJ(true)); } if (mask & HL_STANDOUT) { - PUT(hl, "standout", BOOLEAN_OBJ(true)); + PUT_C(hl, "standout", BOOLEAN_OBJ(true)); } if (mask & HL_UNDERLINE) { - PUT(hl, "underline", BOOLEAN_OBJ(true)); + PUT_C(hl, "underline", BOOLEAN_OBJ(true)); } if (mask & HL_UNDERCURL) { - PUT(hl, "undercurl", BOOLEAN_OBJ(true)); + PUT_C(hl, "undercurl", BOOLEAN_OBJ(true)); } if (mask & HL_UNDERDOUBLE) { - PUT(hl, "underdouble", BOOLEAN_OBJ(true)); + PUT_C(hl, "underdouble", BOOLEAN_OBJ(true)); } if (mask & HL_UNDERDOTTED) { - PUT(hl, "underdotted", BOOLEAN_OBJ(true)); + PUT_C(hl, "underdotted", BOOLEAN_OBJ(true)); } if (mask & HL_UNDERDASHED) { - PUT(hl, "underdashed", BOOLEAN_OBJ(true)); + PUT_C(hl, "underdashed", BOOLEAN_OBJ(true)); } if (mask & HL_ITALIC) { - PUT(hl, "italic", BOOLEAN_OBJ(true)); + PUT_C(hl, "italic", BOOLEAN_OBJ(true)); } if (mask & HL_INVERSE) { - PUT(hl, "reverse", BOOLEAN_OBJ(true)); + PUT_C(hl, "reverse", BOOLEAN_OBJ(true)); } if (mask & HL_STRIKETHROUGH) { - PUT(hl, "strikethrough", BOOLEAN_OBJ(true)); + PUT_C(hl, "strikethrough", BOOLEAN_OBJ(true)); + } + + if (mask & HL_NOCOMBINE) { + PUT_C(hl, "nocombine", BOOLEAN_OBJ(true)); } if (use_rgb) { if (mask & HL_FG_INDEXED) { - PUT(hl, "fg_indexed", BOOLEAN_OBJ(true)); + PUT_C(hl, "fg_indexed", BOOLEAN_OBJ(true)); } if (mask & HL_BG_INDEXED) { - PUT(hl, "bg_indexed", BOOLEAN_OBJ(true)); + PUT_C(hl, "bg_indexed", BOOLEAN_OBJ(true)); } if (ae.rgb_fg_color != -1) { - PUT(hl, "foreground", INTEGER_OBJ(ae.rgb_fg_color)); + PUT_C(hl, "foreground", INTEGER_OBJ(ae.rgb_fg_color)); } if (ae.rgb_bg_color != -1) { - PUT(hl, "background", INTEGER_OBJ(ae.rgb_bg_color)); + PUT_C(hl, "background", INTEGER_OBJ(ae.rgb_bg_color)); } if (ae.rgb_sp_color != -1) { - PUT(hl, "special", INTEGER_OBJ(ae.rgb_sp_color)); + PUT_C(hl, "special", INTEGER_OBJ(ae.rgb_sp_color)); } } else { if (ae.cterm_fg_color != 0) { - PUT(hl, "foreground", INTEGER_OBJ(ae.cterm_fg_color - 1)); + PUT_C(hl, "foreground", INTEGER_OBJ(ae.cterm_fg_color - 1)); } if (ae.cterm_bg_color != 0) { - PUT(hl, "background", INTEGER_OBJ(ae.cterm_bg_color - 1)); + PUT_C(hl, "background", INTEGER_OBJ(ae.cterm_bg_color - 1)); } } if (ae.hl_blend > -1) { - PUT(hl, "blend", INTEGER_OBJ(ae.hl_blend)); + PUT_C(hl, "blend", INTEGER_OBJ(ae.hl_blend)); } - return hl; + if (hl_alloc) { + *hl_alloc = hl; + return hl; + } else { + Dictionary allocated = copy_dictionary(hl); + kv_destroy(hl); + return allocated; + } } HlAttrs dict2hlattrs(Dict(highlight) *dict, bool use_rgb, int *link_id, Error *err) diff --git a/src/nvim/highlight_group.c b/src/nvim/highlight_group.c index d958b7b344..f6ec03fb14 100644 --- a/src/nvim/highlight_group.c +++ b/src/nvim/highlight_group.c @@ -9,6 +9,7 @@ #include "nvim/autocmd.h" #include "nvim/charset.h" #include "nvim/cursor_shape.h" +#include "nvim/eval/vars.h" #include "nvim/fold.h" #include "nvim/highlight.h" #include "nvim/highlight_group.h" @@ -955,8 +956,8 @@ void do_highlight(const char *line, const bool forceit, const bool init) linep++; } size_t key_len = (size_t)(linep - key_start); - if (key_len > sizeof key - 1) { - semsg(_("E423: Illegal argument")); + if (key_len > sizeof(key) - 1) { + emsg(_("E423: Illegal argument")); error = true; break; } @@ -1003,8 +1004,8 @@ void do_highlight(const char *line, const bool forceit, const bool init) break; } size_t arg_len = (size_t)(linep - arg_start); - if (arg_len > sizeof arg - 1) { - semsg(_("E423: Illegal argument")); + if (arg_len > sizeof(arg) - 1) { + emsg(_("E423: Illegal argument")); error = true; break; } @@ -1409,7 +1410,7 @@ Dictionary get_global_hl_defs(void) Dictionary attrs = ARRAY_DICT_INIT; HlGroup *h = &hl_table[i - 1]; if (h->sg_attr > 0) { - attrs = hlattrs2dict(syn_attr2entry(h->sg_attr), true); + attrs = hlattrs2dict(NULL, syn_attr2entry(h->sg_attr), true); } else if (h->sg_link > 0) { const char *link = (const char *)hl_table[h->sg_link - 1].sg_name; PUT(attrs, "link", STRING_OBJ(cstr_to_string(link))); diff --git a/src/nvim/indent_c.c b/src/nvim/indent_c.c index 3c74b4bd8d..c5e030ce25 100644 --- a/src/nvim/indent_c.c +++ b/src/nvim/indent_c.c @@ -2002,7 +2002,7 @@ int get_c_indent(void) } // #defines and so on go at the left when included in 'cinkeys', - // exluding pragmas when customized in 'cinoptions' + // excluding pragmas when customized in 'cinoptions' if (*theline == '#' && (*linecopy == '#' || in_cinkeys('#', ' ', true))) { const char_u *const directive = (char_u *)skipwhite((char *)theline + 1); if (curbuf->b_ind_pragma == 0 || STRNCMP(directive, "pragma", 6) != 0) { @@ -3569,7 +3569,7 @@ term_again: // Are we at the start of a cpp base class declaration or // constructor initialization? XXX n = 0; - if (curbuf->b_ind_cpp_baseclass != 0 && theline[0] != '{') { + if (curbuf->b_ind_cpp_baseclass != 0) { n = cin_is_cpp_baseclass(&cache_cpp_baseclass); l = get_cursor_line_ptr(); } diff --git a/src/nvim/insexpand.c b/src/nvim/insexpand.c new file mode 100644 index 0000000000..2fc8f1dadc --- /dev/null +++ b/src/nvim/insexpand.c @@ -0,0 +1,3967 @@ +// This is an open source non-commercial project. Dear PVS-Studio, please check +// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com + +// insexpand.c: functions for Insert mode completion + +#include <assert.h> +#include <inttypes.h> +#include <stdbool.h> +#include <string.h> + +#include "nvim/ascii.h" +#include "nvim/buffer.h" +#include "nvim/change.h" +#include "nvim/charset.h" +#include "nvim/cursor.h" +#include "nvim/edit.h" +#include "nvim/eval.h" +#include "nvim/eval/typval.h" +#include "nvim/ex_docmd.h" +#include "nvim/ex_getln.h" +#include "nvim/fileio.h" +#include "nvim/getchar.h" +#include "nvim/indent.h" +#include "nvim/indent_c.h" +#include "nvim/insexpand.h" +#include "nvim/keycodes.h" +#include "nvim/mbyte.h" +#include "nvim/memline.h" +#include "nvim/memory.h" +#include "nvim/message.h" +#include "nvim/move.h" +#include "nvim/option.h" +#include "nvim/os/input.h" +#include "nvim/os/time.h" +#include "nvim/path.h" +#include "nvim/popupmnu.h" +#include "nvim/regexp.h" +#include "nvim/screen.h" +#include "nvim/search.h" +#include "nvim/spell.h" +#include "nvim/state.h" +#include "nvim/strings.h" +#include "nvim/tag.h" +#include "nvim/ui.h" +#include "nvim/undo.h" +#include "nvim/vim.h" +#include "nvim/window.h" + +// Definitions used for CTRL-X submode. +// Note: If you change CTRL-X submode, you must also maintain ctrl_x_msgs[] +// and ctrl_x_mode_names[]. + +#define CTRL_X_WANT_IDENT 0x100 + +#define CTRL_X_NORMAL 0 ///< CTRL-N CTRL-P completion, default +#define CTRL_X_NOT_DEFINED_YET 1 +#define CTRL_X_SCROLL 2 +#define CTRL_X_WHOLE_LINE 3 +#define CTRL_X_FILES 4 +#define CTRL_X_TAGS (5 + CTRL_X_WANT_IDENT) +#define CTRL_X_PATH_PATTERNS (6 + CTRL_X_WANT_IDENT) +#define CTRL_X_PATH_DEFINES (7 + CTRL_X_WANT_IDENT) +#define CTRL_X_FINISHED 8 +#define CTRL_X_DICTIONARY (9 + CTRL_X_WANT_IDENT) +#define CTRL_X_THESAURUS (10 + CTRL_X_WANT_IDENT) +#define CTRL_X_CMDLINE 11 +#define CTRL_X_FUNCTION 12 +#define CTRL_X_OMNI 13 +#define CTRL_X_SPELL 14 +#define CTRL_X_LOCAL_MSG 15 ///< only used in "ctrl_x_msgs" +#define CTRL_X_EVAL 16 ///< for builtin function complete() +#define CTRL_X_CMDLINE_CTRL_X 17 ///< CTRL-X typed in CTRL_X_CMDLINE + +#define CTRL_X_MSG(i) ctrl_x_msgs[(i) & ~CTRL_X_WANT_IDENT] + +/// Message for CTRL-X mode, index is ctrl_x_mode. +static char *ctrl_x_msgs[] = +{ + N_(" Keyword completion (^N^P)"), // CTRL_X_NORMAL, ^P/^N compl. + N_(" ^X mode (^]^D^E^F^I^K^L^N^O^Ps^U^V^Y)"), + NULL, // CTRL_X_SCROLL: depends on state + N_(" Whole line completion (^L^N^P)"), + N_(" File name completion (^F^N^P)"), + N_(" Tag completion (^]^N^P)"), + N_(" Path pattern completion (^N^P)"), + N_(" Definition completion (^D^N^P)"), + NULL, // CTRL_X_FINISHED + N_(" Dictionary completion (^K^N^P)"), + N_(" Thesaurus completion (^T^N^P)"), + N_(" Command-line completion (^V^N^P)"), + N_(" User defined completion (^U^N^P)"), + N_(" Omni completion (^O^N^P)"), + N_(" Spelling suggestion (s^N^P)"), + N_(" Keyword Local completion (^N^P)"), + NULL, // CTRL_X_EVAL doesn't use msg. + N_(" Command-line completion (^V^N^P)"), +}; + +static char *ctrl_x_mode_names[] = { + "keyword", + "ctrl_x", + "scroll", + "whole_line", + "files", + "tags", + "path_patterns", + "path_defines", + "unknown", // CTRL_X_FINISHED + "dictionary", + "thesaurus", + "cmdline", + "function", + "omni", + "spell", + NULL, // CTRL_X_LOCAL_MSG only used in "ctrl_x_msgs" + "eval", + "cmdline", +}; + +// Array indexes used for cp_text[]. +#define CPT_ABBR 0 ///< "abbr" +#define CPT_MENU 1 ///< "menu" +#define CPT_KIND 2 ///< "kind" +#define CPT_INFO 3 ///< "info" +#define CPT_COUNT 4 ///< Number of entries + +/// Structure used to store one match for insert completion. +typedef struct compl_S compl_T; +struct compl_S { + compl_T *cp_next; + compl_T *cp_prev; + char_u *cp_str; ///< matched text + char_u *(cp_text[CPT_COUNT]); ///< text for the menu + typval_T cp_user_data; + char_u *cp_fname; ///< file containing the match, allocated when + ///< cp_flags has CP_FREE_FNAME + int cp_flags; ///< CP_ values + int cp_number; ///< sequence number +}; + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "insexpand.c.generated.h" +#endif + +/// values for cp_flags +typedef enum { + CP_ORIGINAL_TEXT = 1, ///< the original text when the expansion begun + CP_FREE_FNAME = 2, ///< cp_fname is allocated + CP_CONT_S_IPOS = 4, ///< use CONT_S_IPOS for compl_cont_status + CP_EQUAL = 8, ///< ins_compl_equal() always returns true + CP_ICASE = 16, ///< ins_compl_equal ignores case + CP_FAST = 32, ///< use fast_breakcheck instead of os_breakcheck +} cp_flags_T; + +static char e_hitend[] = N_("Hit end of paragraph"); +static char e_compldel[] = N_("E840: Completion function deleted text"); + +// All the current matches are stored in a list. +// "compl_first_match" points to the start of the list. +// "compl_curr_match" points to the currently selected entry. +// "compl_shown_match" is different from compl_curr_match during +// ins_compl_get_exp(). + +static compl_T *compl_first_match = NULL; +static compl_T *compl_curr_match = NULL; +static compl_T *compl_shown_match = NULL; +static compl_T *compl_old_match = NULL; + +/// After using a cursor key <Enter> selects a match in the popup menu, +/// otherwise it inserts a line break. +static bool compl_enter_selects = false; + +/// When "compl_leader" is not NULL only matches that start with this string +/// are used. +static char_u *compl_leader = NULL; + +static bool compl_get_longest = false; ///< put longest common string in compl_leader + +static bool compl_no_insert = false; ///< false: select & insert + ///< true: noinsert +static bool compl_no_select = false; ///< false: select & insert + ///< true: noselect + +/// Selected one of the matches. When false the match was edited or using the +/// longest common string. +static bool compl_used_match; + +/// didn't finish finding completions. +static bool compl_was_interrupted = false; + +// Set when character typed while looking for matches and it means we should +// stop looking for matches. +static bool compl_interrupted = false; + +static bool compl_restarting = false; ///< don't insert match + +///< When the first completion is done "compl_started" is set. When it's +///< false the word to be completed must be located. +static bool compl_started = false; + +///< Which Ctrl-X mode are we in? +static int ctrl_x_mode = CTRL_X_NORMAL; + +static int compl_matches = 0; +static char *compl_pattern = NULL; +static Direction compl_direction = FORWARD; +static Direction compl_shows_dir = FORWARD; +static int compl_pending = 0; ///< > 1 for postponed CTRL-N +static pos_T compl_startpos; +static colnr_T compl_col = 0; ///< column where the text starts + ///< that is being completed +static char_u *compl_orig_text = NULL; ///< text as it was before + ///< completion started +static int compl_cont_mode = 0; +static expand_T compl_xp; + +static bool compl_opt_refresh_always = false; + +static size_t spell_bad_len = 0; // length of located bad word + +static int pum_selected_item = -1; + +/// CTRL-X pressed in Insert mode. +void ins_ctrl_x(void) +{ + if (!ctrl_x_mode_cmdline()) { + // if the next ^X<> won't ADD nothing, then reset compl_cont_status + if (compl_cont_status & CONT_N_ADDS) { + compl_cont_status |= CONT_INTRPT; + } else { + compl_cont_status = 0; + } + // We're not sure which CTRL-X mode it will be yet + ctrl_x_mode = CTRL_X_NOT_DEFINED_YET; + edit_submode = (char_u *)_(CTRL_X_MSG(ctrl_x_mode)); + edit_submode_pre = NULL; + showmode(); + } else { + // CTRL-X in CTRL-X CTRL-V mode behaves differently to make CTRL-X + // CTRL-V look like CTRL-N + ctrl_x_mode = CTRL_X_CMDLINE_CTRL_X; + } + + may_trigger_modechanged(); +} + +// Functions to check the current CTRL-X mode. + +bool ctrl_x_mode_none(void) + FUNC_ATTR_PURE +{ + return ctrl_x_mode == 0; +} + +bool ctrl_x_mode_normal(void) + FUNC_ATTR_PURE +{ + return ctrl_x_mode == CTRL_X_NORMAL; +} + +bool ctrl_x_mode_scroll(void) + FUNC_ATTR_PURE +{ + return ctrl_x_mode == CTRL_X_SCROLL; +} + +bool ctrl_x_mode_whole_line(void) + FUNC_ATTR_PURE +{ + return ctrl_x_mode == CTRL_X_WHOLE_LINE; +} + +bool ctrl_x_mode_files(void) + FUNC_ATTR_PURE +{ + return ctrl_x_mode == CTRL_X_FILES; +} + +bool ctrl_x_mode_tags(void) + FUNC_ATTR_PURE +{ + return ctrl_x_mode == CTRL_X_TAGS; +} + +bool ctrl_x_mode_path_patterns(void) + FUNC_ATTR_PURE +{ + return ctrl_x_mode == CTRL_X_PATH_PATTERNS; +} + +bool ctrl_x_mode_path_defines(void) + FUNC_ATTR_PURE +{ + return ctrl_x_mode == CTRL_X_PATH_DEFINES; +} + +bool ctrl_x_mode_dictionary(void) + FUNC_ATTR_PURE +{ + return ctrl_x_mode == CTRL_X_DICTIONARY; +} + +bool ctrl_x_mode_thesaurus(void) + FUNC_ATTR_PURE +{ + return ctrl_x_mode == CTRL_X_THESAURUS; +} + +bool ctrl_x_mode_cmdline(void) + FUNC_ATTR_PURE +{ + return ctrl_x_mode == CTRL_X_CMDLINE || ctrl_x_mode == CTRL_X_CMDLINE_CTRL_X; +} + +bool ctrl_x_mode_function(void) + FUNC_ATTR_PURE +{ + return ctrl_x_mode == CTRL_X_FUNCTION; +} + +bool ctrl_x_mode_omni(void) + FUNC_ATTR_PURE +{ + return ctrl_x_mode == CTRL_X_OMNI; +} + +bool ctrl_x_mode_spell(void) + FUNC_ATTR_PURE +{ + return ctrl_x_mode == CTRL_X_SPELL; +} + +static bool ctrl_x_mode_eval(void) + FUNC_ATTR_PURE +{ + return ctrl_x_mode == CTRL_X_EVAL; +} + +bool ctrl_x_mode_line_or_eval(void) + FUNC_ATTR_PURE +{ + return ctrl_x_mode == CTRL_X_WHOLE_LINE || ctrl_x_mode == CTRL_X_EVAL; +} + +/// Whether other than default completion has been selected. +bool ctrl_x_mode_not_default(void) + FUNC_ATTR_PURE +{ + return ctrl_x_mode != CTRL_X_NORMAL; +} + +/// Whether CTRL-X was typed without a following character, +/// not including when in CTRL-X CTRL-V mode. +bool ctrl_x_mode_not_defined_yet(void) + FUNC_ATTR_PURE +{ + return ctrl_x_mode == CTRL_X_NOT_DEFINED_YET; +} + +/// Check that the "dict" or "tsr" option can be used. +/// +/// @param dict_opt check "dict" when true, "tsr" when false. +bool check_compl_option(bool dict_opt) +{ + if (dict_opt + ? (*curbuf->b_p_dict == NUL && *p_dict == NUL && !curwin->w_p_spell) + : (*curbuf->b_p_tsr == NUL && *p_tsr == NUL + && *curbuf->b_p_tsrfu == NUL && *p_tsrfu == NUL)) { + ctrl_x_mode = CTRL_X_NORMAL; + edit_submode = NULL; + msg_attr((dict_opt + ? _("'dictionary' option is empty") + : _("'thesaurus' option is empty")), HL_ATTR(HLF_E)); + if (emsg_silent == 0) { + vim_beep(BO_COMPL); + setcursor(); + ui_flush(); + os_delay(2004L, false); + } + return false; + } + return true; +} + +/// Check that the character "c" a valid key to go to or keep us in CTRL-X mode? +/// This depends on the current mode. +/// +/// @param c character to check +bool vim_is_ctrl_x_key(int c) + FUNC_ATTR_WARN_UNUSED_RESULT +{ + // Always allow ^R - let its results then be checked + if (c == Ctrl_R) { + return true; + } + + // Accept <PageUp> and <PageDown> if the popup menu is visible. + if (ins_compl_pum_key(c)) { + return true; + } + + switch (ctrl_x_mode) { + case 0: // Not in any CTRL-X mode + return c == Ctrl_N || c == Ctrl_P || c == Ctrl_X; + case CTRL_X_NOT_DEFINED_YET: + case CTRL_X_CMDLINE_CTRL_X: + return c == Ctrl_X || c == Ctrl_Y || c == Ctrl_E + || c == Ctrl_L || c == Ctrl_F || c == Ctrl_RSB + || c == Ctrl_I || c == Ctrl_D || c == Ctrl_P + || c == Ctrl_N || c == Ctrl_T || c == Ctrl_V + || c == Ctrl_Q || c == Ctrl_U || c == Ctrl_O + || c == Ctrl_S || c == Ctrl_K || c == 's' + || c == Ctrl_Z; + case CTRL_X_SCROLL: + return c == Ctrl_Y || c == Ctrl_E; + case CTRL_X_WHOLE_LINE: + return c == Ctrl_L || c == Ctrl_P || c == Ctrl_N; + case CTRL_X_FILES: + return c == Ctrl_F || c == Ctrl_P || c == Ctrl_N; + case CTRL_X_DICTIONARY: + return c == Ctrl_K || c == Ctrl_P || c == Ctrl_N; + case CTRL_X_THESAURUS: + return c == Ctrl_T || c == Ctrl_P || c == Ctrl_N; + case CTRL_X_TAGS: + return c == Ctrl_RSB || c == Ctrl_P || c == Ctrl_N; + case CTRL_X_PATH_PATTERNS: + return c == Ctrl_P || c == Ctrl_N; + case CTRL_X_PATH_DEFINES: + return c == Ctrl_D || c == Ctrl_P || c == Ctrl_N; + case CTRL_X_CMDLINE: + return c == Ctrl_V || c == Ctrl_Q || c == Ctrl_P || c == Ctrl_N + || c == Ctrl_X; + case CTRL_X_FUNCTION: + return c == Ctrl_U || c == Ctrl_P || c == Ctrl_N; + case CTRL_X_OMNI: + return c == Ctrl_O || c == Ctrl_P || c == Ctrl_N; + case CTRL_X_SPELL: + return c == Ctrl_S || c == Ctrl_P || c == Ctrl_N; + case CTRL_X_EVAL: + return (c == Ctrl_P || c == Ctrl_N); + } + internal_error("vim_is_ctrl_x_key()"); + return false; +} + +/// Check that character "c" is part of the item currently being +/// completed. Used to decide whether to abandon complete mode when the menu +/// is visible. +/// +/// @param c character to check +bool ins_compl_accept_char(int c) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT +{ + if (ctrl_x_mode & CTRL_X_WANT_IDENT) { + // When expanding an identifier only accept identifier chars. + return vim_isIDc(c); + } + + switch (ctrl_x_mode) { + case CTRL_X_FILES: + // When expanding file name only accept file name chars. But not + // path separators, so that "proto/<Tab>" expands files in + // "proto", not "proto/" as a whole + return vim_isfilec(c) && !vim_ispathsep(c); + + case CTRL_X_CMDLINE: + case CTRL_X_CMDLINE_CTRL_X: + case CTRL_X_OMNI: + // Command line and Omni completion can work with just about any + // printable character, but do stop at white space. + return vim_isprintc(c) && !ascii_iswhite(c); + + case CTRL_X_WHOLE_LINE: + // For while line completion a space can be part of the line. + return vim_isprintc(c); + } + return vim_iswordc(c); +} + +/// This is like ins_compl_add(), but if 'ic' and 'inf' are set, then the +/// case of the originally typed text is used, and the case of the completed +/// text is inferred, ie this tries to work out what case you probably wanted +/// the rest of the word to be in -- webb +/// +/// @param[in] cont_s_ipos next ^X<> will set initial_pos +int ins_compl_add_infercase(char_u *str_arg, int len, bool icase, char_u *fname, Direction dir, + bool cont_s_ipos) + FUNC_ATTR_NONNULL_ARG(1) +{ + char_u *str = str_arg; + int i, c; + int actual_len; // Take multi-byte characters + int actual_compl_length; // into account. + int min_len; + bool has_lower = false; + bool was_letter = false; + int flags = 0; + + if (p_ic && curbuf->b_p_inf && len > 0) { + // Infer case of completed part. + + // Find actual length of completion. + { + const char_u *p = str; + actual_len = 0; + while (*p != NUL) { + MB_PTR_ADV(p); + actual_len++; + } + } + + // Find actual length of original text. + { + const char_u *p = compl_orig_text; + actual_compl_length = 0; + while (*p != NUL) { + MB_PTR_ADV(p); + actual_compl_length++; + } + } + + // "actual_len" may be smaller than "actual_compl_length" when using + // thesaurus, only use the minimum when comparing. + min_len = actual_len < actual_compl_length + ? actual_len : actual_compl_length; + + // Allocate wide character array for the completion and fill it. + int *const wca = xmalloc((size_t)actual_len * sizeof(*wca)); + { + const char_u *p = str; + for (i = 0; i < actual_len; i++) { + wca[i] = mb_ptr2char_adv(&p); + } + } + + // Rule 1: Were any chars converted to lower? + { + const char_u *p = compl_orig_text; + for (i = 0; i < min_len; i++) { + c = mb_ptr2char_adv(&p); + if (mb_islower(c)) { + has_lower = true; + if (mb_isupper(wca[i])) { + // Rule 1 is satisfied. + for (i = actual_compl_length; i < actual_len; i++) { + wca[i] = mb_tolower(wca[i]); + } + break; + } + } + } + } + + // Rule 2: No lower case, 2nd consecutive letter converted to + // upper case. + if (!has_lower) { + const char_u *p = compl_orig_text; + for (i = 0; i < min_len; i++) { + c = mb_ptr2char_adv(&p); + if (was_letter && mb_isupper(c) && mb_islower(wca[i])) { + // Rule 2 is satisfied. + for (i = actual_compl_length; i < actual_len; i++) { + wca[i] = mb_toupper(wca[i]); + } + break; + } + was_letter = mb_islower(c) || mb_isupper(c); + } + } + + // Copy the original case of the part we typed. + { + const char_u *p = compl_orig_text; + for (i = 0; i < min_len; i++) { + c = mb_ptr2char_adv(&p); + if (mb_islower(c)) { + wca[i] = mb_tolower(wca[i]); + } else if (mb_isupper(c)) { + wca[i] = mb_toupper(wca[i]); + } + } + } + + // Generate encoding specific output from wide character array. + // Multi-byte characters can occupy up to five bytes more than + // ASCII characters, and we also need one byte for NUL, so stay + // six bytes away from the edge of IObuff. + { + char_u *p = IObuff; + i = 0; + while (i < actual_len && (p - IObuff + 6) < IOSIZE) { + p += utf_char2bytes(wca[i++], (char *)p); + } + *p = NUL; + } + + xfree(wca); + + str = IObuff; + } + if (cont_s_ipos) { + flags |= CP_CONT_S_IPOS; + } + if (icase) { + flags |= CP_ICASE; + } + + return ins_compl_add(str, len, fname, NULL, false, NULL, dir, flags, false); +} + +/// Add a match to the list of matches +/// +/// @param[in] str Match to add. +/// @param[in] len Match length, -1 to use #STRLEN. +/// @param[in] fname File name match comes from. May be NULL. +/// @param[in] cptext Extra text for popup menu. May be NULL. If not NULL, +/// must have exactly #CPT_COUNT items. +/// @param[in] cptext_allocated If true, will not copy cptext strings. +/// +/// @note Will free strings in case of error. +/// cptext itself will not be freed. +/// @param[in] cdir Completion direction. +/// @param[in] adup True if duplicate matches are to be accepted. +/// +/// @return NOTDONE if the given string is already in the list of completions, +/// otherwise it is added to the list and OK is returned. FAIL will be +/// returned in case of error. +static int ins_compl_add(char_u *const str, int len, char_u *const fname, + char_u *const *const cptext, const bool cptext_allocated, + typval_T *user_data, const Direction cdir, int flags_arg, const bool adup) + FUNC_ATTR_NONNULL_ARG(1) +{ + compl_T *match; + const Direction dir = (cdir == kDirectionNotSet ? compl_direction : cdir); + int flags = flags_arg; + + if (flags & CP_FAST) { + fast_breakcheck(); + } else { + os_breakcheck(); + } +#define FREE_CPTEXT(cptext, cptext_allocated) \ + do { \ + if ((cptext) != NULL && (cptext_allocated)) { \ + for (size_t i = 0; i < CPT_COUNT; i++) { \ + xfree((cptext)[i]); \ + } \ + } \ + } while (0) + if (got_int) { + FREE_CPTEXT(cptext, cptext_allocated); + return FAIL; + } + if (len < 0) { + len = (int)STRLEN(str); + } + + // If the same match is already present, don't add it. + if (compl_first_match != NULL && !adup) { + match = compl_first_match; + do { + if (!(match->cp_flags & CP_ORIGINAL_TEXT) + && STRNCMP(match->cp_str, str, len) == 0 + && match->cp_str[len] == NUL) { + FREE_CPTEXT(cptext, cptext_allocated); + return NOTDONE; + } + match = match->cp_next; + } while (match != NULL && match != compl_first_match); + } + + // Remove any popup menu before changing the list of matches. + ins_compl_del_pum(); + + // Allocate a new match structure. + // Copy the values to the new match structure. + match = xcalloc(1, sizeof(compl_T)); + match->cp_number = -1; + if (flags & CP_ORIGINAL_TEXT) { + match->cp_number = 0; + } + match->cp_str = vim_strnsave(str, (size_t)len); + + // match-fname is: + // - compl_curr_match->cp_fname if it is a string equal to fname. + // - a copy of fname, CP_FREE_FNAME is set to free later THE allocated mem. + // - NULL otherwise. --Acevedo + if (fname != NULL + && compl_curr_match != NULL + && compl_curr_match->cp_fname != NULL + && STRCMP(fname, compl_curr_match->cp_fname) == 0) { + match->cp_fname = compl_curr_match->cp_fname; + } else if (fname != NULL) { + match->cp_fname = vim_strsave(fname); + flags |= CP_FREE_FNAME; + } else { + match->cp_fname = NULL; + } + match->cp_flags = flags; + + if (cptext != NULL) { + int i; + + for (i = 0; i < CPT_COUNT; i++) { + if (cptext[i] == NULL) { + continue; + } + if (*cptext[i] != NUL) { + match->cp_text[i] = (cptext_allocated + ? cptext[i] + : (char_u *)xstrdup((char *)cptext[i])); + } else if (cptext_allocated) { + xfree(cptext[i]); + } + } + } + + if (user_data != NULL) { + match->cp_user_data = *user_data; + } + + // Link the new match structure in the list of matches. + if (compl_first_match == NULL) { + match->cp_next = match->cp_prev = NULL; + } else if (dir == FORWARD) { + match->cp_next = compl_curr_match->cp_next; + match->cp_prev = compl_curr_match; + } else { // BACKWARD + match->cp_next = compl_curr_match; + match->cp_prev = compl_curr_match->cp_prev; + } + if (match->cp_next) { + match->cp_next->cp_prev = match; + } + if (match->cp_prev) { + match->cp_prev->cp_next = match; + } else { // if there's nothing before, it is the first match + compl_first_match = match; + } + compl_curr_match = match; + + // Find the longest common string if still doing that. + if (compl_get_longest && (flags & CP_ORIGINAL_TEXT) == 0) { + ins_compl_longest_match(match); + } + + return OK; +} + +/// Check that "str[len]" matches with "match->cp_str", considering +/// "match->cp_flags". +/// +/// @param match completion match +/// @param str character string to check +/// @param len length of "str" +static bool ins_compl_equal(compl_T *match, char_u *str, size_t len) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL +{ + if (match->cp_flags & CP_EQUAL) { + return true; + } + if (match->cp_flags & CP_ICASE) { + return STRNICMP(match->cp_str, str, len) == 0; + } + return STRNCMP(match->cp_str, str, len) == 0; +} + +/// Reduce the longest common string for match "match". +static void ins_compl_longest_match(compl_T *match) +{ + char_u *p, *s; + int c1, c2; + int had_match; + + if (compl_leader == NULL) { + // First match, use it as a whole. + compl_leader = vim_strsave(match->cp_str); + had_match = (curwin->w_cursor.col > compl_col); + ins_compl_delete(); + ins_bytes(compl_leader + get_compl_len()); + ins_redraw(false); + + // When the match isn't there (to avoid matching itself) remove it + // again after redrawing. + if (!had_match) { + ins_compl_delete(); + } + compl_used_match = false; + } else { + // Reduce the text if this match differs from compl_leader. + p = compl_leader; + s = match->cp_str; + while (*p != NUL) { + c1 = utf_ptr2char((char *)p); + c2 = utf_ptr2char((char *)s); + + if ((match->cp_flags & CP_ICASE) + ? (mb_tolower(c1) != mb_tolower(c2)) + : (c1 != c2)) { + break; + } + MB_PTR_ADV(p); + MB_PTR_ADV(s); + } + + if (*p != NUL) { + // Leader was shortened, need to change the inserted text. + *p = NUL; + had_match = (curwin->w_cursor.col > compl_col); + ins_compl_delete(); + ins_bytes(compl_leader + get_compl_len()); + ins_redraw(false); + + // When the match isn't there (to avoid matching itself) remove it + // again after redrawing. + if (!had_match) { + ins_compl_delete(); + } + } + + compl_used_match = false; + } +} + +/// Add an array of matches to the list of matches. +/// Frees matches[]. +static void ins_compl_add_matches(int num_matches, char **matches, int icase) + FUNC_ATTR_NONNULL_ALL +{ + int add_r = OK; + Direction dir = compl_direction; + + for (int i = 0; i < num_matches && add_r != FAIL; i++) { + if ((add_r = ins_compl_add((char_u *)matches[i], -1, NULL, NULL, false, NULL, dir, + CP_FAST | (icase ? CP_ICASE : 0), + false)) == OK) { + // If dir was BACKWARD then honor it just once. + dir = FORWARD; + } + } + FreeWild(num_matches, matches); +} + +/// Make the completion list cyclic. +/// Return the number of matches (excluding the original). +static int ins_compl_make_cyclic(void) +{ + compl_T *match; + int count = 0; + + if (compl_first_match != NULL) { + // Find the end of the list. + match = compl_first_match; + // there's always an entry for the compl_orig_text, it doesn't count. + while (match->cp_next != NULL && match->cp_next != compl_first_match) { + match = match->cp_next; + count++; + } + match->cp_next = compl_first_match; + compl_first_match->cp_prev = match; + } + return count; +} + +/// Return whether there currently is a shown match. +bool ins_compl_has_shown_match(void) +{ + return compl_shown_match == NULL || compl_shown_match != compl_shown_match->cp_next; +} + +/// Return whether the shown match is long enough. +bool ins_compl_long_shown_match(void) +{ + return compl_shown_match != NULL && compl_shown_match->cp_str != NULL + && (colnr_T)STRLEN(compl_shown_match->cp_str) > curwin->w_cursor.col - compl_col; +} + +/// Set variables that store noselect and noinsert behavior from the +/// 'completeopt' value. +void completeopt_was_set(void) +{ + compl_no_insert = false; + compl_no_select = false; + if (strstr((char *)p_cot, "noselect") != NULL) { + compl_no_select = true; + } + if (strstr((char *)p_cot, "noinsert") != NULL) { + compl_no_insert = true; + } +} + +/// "compl_match_array" points the currently displayed list of entries in the +/// popup menu. It is NULL when there is no popup menu. +static pumitem_T *compl_match_array = NULL; +static int compl_match_arraysize; + +/// Remove any popup menu. +static void ins_compl_del_pum(void) +{ + if (compl_match_array != NULL) { + pum_undisplay(false); + XFREE_CLEAR(compl_match_array); + } +} + +/// Check if the popup menu should be displayed. +bool pum_wanted(void) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT +{ + // "completeopt" must contain "menu" or "menuone" + return vim_strchr((char *)p_cot, 'm') != NULL; +} + +/// Check that there are two or more matches to be shown in the popup menu. +/// One if "completopt" contains "menuone". +static bool pum_enough_matches(void) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT +{ + // Don't display the popup menu if there are no matches or there is only + // one (ignoring the original text). + compl_T *comp = compl_first_match; + int i = 0; + do { + if (comp == NULL + || ((comp->cp_flags & CP_ORIGINAL_TEXT) == 0 && ++i == 2)) { + break; + } + comp = comp->cp_next; + } while (comp != compl_first_match); + + if (strstr((char *)p_cot, "menuone") != NULL) { + return i >= 1; + } + return i >= 2; +} + +/// Convert to complete item dict +static dict_T *ins_compl_dict_alloc(compl_T *match) +{ + // { word, abbr, menu, kind, info } + dict_T *dict = tv_dict_alloc_lock(VAR_FIXED); + tv_dict_add_str(dict, S_LEN("word"), EMPTY_IF_NULL(match->cp_str)); + tv_dict_add_str(dict, S_LEN("abbr"), EMPTY_IF_NULL(match->cp_text[CPT_ABBR])); + tv_dict_add_str(dict, S_LEN("menu"), EMPTY_IF_NULL(match->cp_text[CPT_MENU])); + tv_dict_add_str(dict, S_LEN("kind"), EMPTY_IF_NULL(match->cp_text[CPT_KIND])); + tv_dict_add_str(dict, S_LEN("info"), EMPTY_IF_NULL(match->cp_text[CPT_INFO])); + if (match->cp_user_data.v_type == VAR_UNKNOWN) { + tv_dict_add_str(dict, S_LEN("user_data"), ""); + } else { + tv_dict_add_tv(dict, S_LEN("user_data"), &match->cp_user_data); + } + return dict; +} + +static void trigger_complete_changed_event(int cur) +{ + static bool recursive = false; + save_v_event_T save_v_event; + + if (recursive) { + return; + } + + dict_T *v_event = get_v_event(&save_v_event); + if (cur < 0) { + tv_dict_add_dict(v_event, S_LEN("completed_item"), tv_dict_alloc()); + } else { + dict_T *item = ins_compl_dict_alloc(compl_curr_match); + tv_dict_add_dict(v_event, S_LEN("completed_item"), item); + } + pum_set_event_info(v_event); + tv_dict_set_keys_readonly(v_event); + + recursive = true; + textlock++; + apply_autocmds(EVENT_COMPLETECHANGED, NULL, NULL, false, curbuf); + textlock--; + recursive = false; + + restore_v_event(v_event, &save_v_event); +} + +/// Show the popup menu for the list of matches. +/// Also adjusts "compl_shown_match" to an entry that is actually displayed. +void ins_compl_show_pum(void) +{ + compl_T *compl; + compl_T *shown_compl = NULL; + bool did_find_shown_match = false; + bool shown_match_ok = false; + int i; + int cur = -1; + colnr_T col; + int lead_len = 0; + bool array_changed = false; + + if (!pum_wanted() || !pum_enough_matches()) { + return; + } + + // Dirty hard-coded hack: remove any matchparen highlighting. + do_cmdline_cmd("if exists('g:loaded_matchparen')|3match none|endif"); + + // Update the screen before drawing the popup menu over it. + update_screen(0); + + if (compl_match_array == NULL) { + array_changed = true; + // Need to build the popup menu list. + compl_match_arraysize = 0; + compl = compl_first_match; + // If it's user complete function and refresh_always, + // do not use "compl_leader" as prefix filter. + if (ins_compl_need_restart()) { + XFREE_CLEAR(compl_leader); + } + if (compl_leader != NULL) { + lead_len = (int)STRLEN(compl_leader); + } + do { + if ((compl->cp_flags & CP_ORIGINAL_TEXT) == 0 + && (compl_leader == NULL + || ins_compl_equal(compl, compl_leader, (size_t)lead_len))) { + compl_match_arraysize++; + } + compl = compl->cp_next; + } while (compl != NULL && compl != compl_first_match); + if (compl_match_arraysize == 0) { + return; + } + + assert(compl_match_arraysize >= 0); + compl_match_array = xcalloc((size_t)compl_match_arraysize, sizeof(pumitem_T)); + // If the current match is the original text don't find the first + // match after it, don't highlight anything. + if (compl_shown_match->cp_flags & CP_ORIGINAL_TEXT) { + shown_match_ok = true; + } + + i = 0; + compl = compl_first_match; + do { + if ((compl->cp_flags & CP_ORIGINAL_TEXT) == 0 + && (compl_leader == NULL + || ins_compl_equal(compl, compl_leader, (size_t)lead_len))) { + if (!shown_match_ok) { + if (compl == compl_shown_match || did_find_shown_match) { + // This item is the shown match or this is the + // first displayed item after the shown match. + compl_shown_match = compl; + did_find_shown_match = true; + shown_match_ok = true; + } else { + // Remember this displayed match for when the + // shown match is just below it. + shown_compl = compl; + } + cur = i; + } + + if (compl->cp_text[CPT_ABBR] != NULL) { + compl_match_array[i].pum_text = + compl->cp_text[CPT_ABBR]; + } else { + compl_match_array[i].pum_text = compl->cp_str; + } + compl_match_array[i].pum_kind = compl->cp_text[CPT_KIND]; + compl_match_array[i].pum_info = compl->cp_text[CPT_INFO]; + if (compl->cp_text[CPT_MENU] != NULL) { + compl_match_array[i++].pum_extra = + compl->cp_text[CPT_MENU]; + } else { + compl_match_array[i++].pum_extra = compl->cp_fname; + } + } + + if (compl == compl_shown_match) { + did_find_shown_match = true; + + // When the original text is the shown match don't set + // compl_shown_match. + if (compl->cp_flags & CP_ORIGINAL_TEXT) { + shown_match_ok = true; + } + + if (!shown_match_ok && shown_compl != NULL) { + // The shown match isn't displayed, set it to the + // previously displayed match. + compl_shown_match = shown_compl; + shown_match_ok = true; + } + } + compl = compl->cp_next; + } while (compl != NULL && compl != compl_first_match); + + if (!shown_match_ok) { // no displayed match at all + cur = -1; + } + } else { + // popup menu already exists, only need to find the current item. + for (i = 0; i < compl_match_arraysize; i++) { + if (compl_match_array[i].pum_text == compl_shown_match->cp_str + || compl_match_array[i].pum_text + == compl_shown_match->cp_text[CPT_ABBR]) { + cur = i; + break; + } + } + } + + // In Replace mode when a $ is displayed at the end of the line only + // part of the screen would be updated. We do need to redraw here. + dollar_vcol = -1; + + // Compute the screen column of the start of the completed text. + // Use the cursor to get all wrapping and other settings right. + col = curwin->w_cursor.col; + curwin->w_cursor.col = compl_col; + pum_selected_item = cur; + pum_display(compl_match_array, compl_match_arraysize, cur, array_changed, 0); + curwin->w_cursor.col = col; + + if (has_event(EVENT_COMPLETECHANGED)) { + trigger_complete_changed_event(cur); + } +} + +#define DICT_FIRST (1) ///< use just first element in "dict" +#define DICT_EXACT (2) ///< "dict" is the exact name of a file + +/// Add any identifiers that match the given pattern in the list of dictionary +/// files "dict_start" to the list of completions. +/// +/// @param flags DICT_FIRST and/or DICT_EXACT +/// @param thesaurus Thesaurus completion +static void ins_compl_dictionaries(char_u *dict_start, char_u *pat, int flags, int thesaurus) +{ + char *dict = (char *)dict_start; + char_u *ptr; + char *buf; + regmatch_T regmatch; + char **files; + int count; + int save_p_scs; + Direction dir = compl_direction; + + if (*dict == NUL) { + // When 'dictionary' is empty and spell checking is enabled use + // "spell". + if (!thesaurus && curwin->w_p_spell) { + dict = "spell"; + } else { + return; + } + } + + buf = xmalloc(LSIZE); + regmatch.regprog = NULL; // so that we can goto theend + + // If 'infercase' is set, don't use 'smartcase' here + save_p_scs = p_scs; + if (curbuf->b_p_inf) { + p_scs = false; + } + + // When invoked to match whole lines for CTRL-X CTRL-L adjust the pattern + // to only match at the start of a line. Otherwise just match the + // pattern. Also need to double backslashes. + if (ctrl_x_mode_line_or_eval()) { + char_u *pat_esc = vim_strsave_escaped(pat, (char_u *)"\\"); + + size_t len = STRLEN(pat_esc) + 10; + ptr = xmalloc(len); + vim_snprintf((char *)ptr, len, "^\\s*\\zs\\V%s", pat_esc); + regmatch.regprog = vim_regcomp((char *)ptr, RE_MAGIC); + xfree(pat_esc); + xfree(ptr); + } else { + regmatch.regprog = vim_regcomp((char *)pat, p_magic ? RE_MAGIC : 0); + if (regmatch.regprog == NULL) { + goto theend; + } + } + + // ignore case depends on 'ignorecase', 'smartcase' and "pat" + regmatch.rm_ic = ignorecase(pat); + while (*dict != NUL && !got_int && !compl_interrupted) { + // copy one dictionary file name into buf + if (flags == DICT_EXACT) { + count = 1; + files = &dict; + } else { + // Expand wildcards in the dictionary name, but do not allow + // backticks (for security, the 'dict' option may have been set in + // a modeline). + copy_option_part(&dict, buf, LSIZE, ","); + if (!thesaurus && STRCMP(buf, "spell") == 0) { + count = -1; + } else if (vim_strchr(buf, '`') != NULL + || expand_wildcards(1, &buf, &count, &files, + EW_FILE|EW_SILENT) != OK) { + count = 0; + } + } + + if (count == -1) { + // Complete from active spelling. Skip "\<" in the pattern, we + // don't use it as a RE. + if (pat[0] == '\\' && pat[1] == '<') { + ptr = pat + 2; + } else { + ptr = pat; + } + spell_dump_compl(ptr, regmatch.rm_ic, &dir, 0); + } else if (count > 0) { // avoid warning for using "files" uninit + ins_compl_files(count, files, thesaurus, flags, + ®match, (char_u *)buf, &dir); + if (flags != DICT_EXACT) { + FreeWild(count, files); + } + } + if (flags != 0) { + break; + } + } + +theend: + p_scs = save_p_scs; + vim_regfree(regmatch.regprog); + xfree(buf); +} + +static void ins_compl_files(int count, char **files, int thesaurus, int flags, regmatch_T *regmatch, + char_u *buf, Direction *dir) + FUNC_ATTR_NONNULL_ARG(2, 7) +{ + char_u *ptr; + int i; + FILE *fp; + int add_r; + + for (i = 0; i < count && !got_int && !compl_interrupted; i++) { + fp = os_fopen(files[i], "r"); // open dictionary file + if (flags != DICT_EXACT) { + msg_hist_off = true; // reset in msg_trunc_attr() + vim_snprintf((char *)IObuff, IOSIZE, + _("Scanning dictionary: %s"), files[i]); + (void)msg_trunc_attr((char *)IObuff, true, HL_ATTR(HLF_R)); + } + + if (fp == NULL) { + continue; + } + + // Read dictionary file line by line. + // Check each line for a match. + while (!got_int && !compl_interrupted + && !vim_fgets(buf, LSIZE, fp)) { + ptr = buf; + while (vim_regexec(regmatch, (char *)buf, (colnr_T)(ptr - buf))) { + ptr = regmatch->startp[0]; + if (ctrl_x_mode_line_or_eval()) { + ptr = find_line_end(ptr); + } else { + ptr = find_word_end(ptr); + } + add_r = ins_compl_add_infercase(regmatch->startp[0], + (int)(ptr - regmatch->startp[0]), + p_ic, (char_u *)files[i], *dir, false); + if (thesaurus) { + char_u *wstart; + + // Add the other matches on the line + ptr = buf; + while (!got_int) { + // Find start of the next word. Skip white + // space and punctuation. + ptr = find_word_start(ptr); + if (*ptr == NUL || *ptr == NL) { + break; + } + wstart = ptr; + + // Find end of the word. + // Japanese words may have characters in + // different classes, only separate words + // with single-byte non-word characters. + while (*ptr != NUL) { + const int l = utfc_ptr2len((char *)ptr); + + if (l < 2 && !vim_iswordc(*ptr)) { + break; + } + ptr += l; + } + + // Add the word. Skip the regexp match. + if (wstart != regmatch->startp[0]) { + add_r = ins_compl_add_infercase(wstart, (int)(ptr - wstart), + p_ic, (char_u *)files[i], *dir, false); + } + } + } + if (add_r == OK) { + // if dir was BACKWARD then honor it just once + *dir = FORWARD; + } else if (add_r == FAIL) { + break; + } + // avoid expensive call to vim_regexec() when at end + // of line + if (*ptr == '\n' || got_int) { + break; + } + } + line_breakcheck(); + ins_compl_check_keys(50, false); + } + fclose(fp); + } +} + +/// Find the start of the next word. +/// Returns a pointer to the first char of the word. Also stops at a NUL. +char_u *find_word_start(char_u *ptr) + FUNC_ATTR_PURE +{ + while (*ptr != NUL && *ptr != '\n' && mb_get_class(ptr) <= 1) { + ptr += utfc_ptr2len((char *)ptr); + } + return ptr; +} + +/// Find the end of the word. Assumes it starts inside a word. +/// Returns a pointer to just after the word. +char_u *find_word_end(char_u *ptr) + FUNC_ATTR_PURE +{ + const int start_class = mb_get_class(ptr); + if (start_class > 1) { + while (*ptr != NUL) { + ptr += utfc_ptr2len((char *)ptr); + if (mb_get_class(ptr) != start_class) { + break; + } + } + } + return ptr; +} + +/// Find the end of the line, omitting CR and NL at the end. +/// Returns a pointer to just after the line. +static char_u *find_line_end(char_u *ptr) +{ + char_u *s; + + s = ptr + STRLEN(ptr); + while (s > ptr && (s[-1] == CAR || s[-1] == NL)) { + s--; + } + return s; +} + +/// Free the list of completions +static void ins_compl_free(void) +{ + compl_T *match; + + XFREE_CLEAR(compl_pattern); + XFREE_CLEAR(compl_leader); + + if (compl_first_match == NULL) { + return; + } + + ins_compl_del_pum(); + pum_clear(); + + compl_curr_match = compl_first_match; + do { + match = compl_curr_match; + compl_curr_match = compl_curr_match->cp_next; + xfree(match->cp_str); + // several entries may use the same fname, free it just once. + if (match->cp_flags & CP_FREE_FNAME) { + xfree(match->cp_fname); + } + for (int i = 0; i < CPT_COUNT; i++) { + xfree(match->cp_text[i]); + } + tv_clear(&match->cp_user_data); + xfree(match); + } while (compl_curr_match != NULL && compl_curr_match != compl_first_match); + compl_first_match = compl_curr_match = NULL; + compl_shown_match = NULL; + compl_old_match = NULL; +} + +void ins_compl_clear(void) +{ + compl_cont_status = 0; + compl_started = false; + compl_matches = 0; + XFREE_CLEAR(compl_pattern); + XFREE_CLEAR(compl_leader); + edit_submode_extra = NULL; + XFREE_CLEAR(compl_orig_text); + compl_enter_selects = false; + // clear v:completed_item + set_vim_var_dict(VV_COMPLETED_ITEM, tv_dict_alloc_lock(VAR_FIXED)); +} + +/// Check that Insert completion is active. +bool ins_compl_active(void) + FUNC_ATTR_PURE +{ + return compl_started; +} + +/// Selected one of the matches. When false the match was edited or using the +/// longest common string. +bool ins_compl_used_match(void) +{ + return compl_used_match; +} + +/// Initialize get longest common string. +void ins_compl_init_get_longest(void) +{ + compl_get_longest = false; +} + +/// Returns true when insert completion is interrupted. +bool ins_compl_interrupted(void) +{ + return compl_interrupted; +} + +/// Returns true if the <Enter> key selects a match in the completion popup +/// menu. +bool ins_compl_enter_selects(void) +{ + return compl_enter_selects; +} + +/// Return the column where the text starts that is being completed +colnr_T ins_compl_col(void) +{ + return compl_col; +} + +/// Delete one character before the cursor and show the subset of the matches +/// that match the word that is now before the cursor. +/// Returns the character to be used, NUL if the work is done and another char +/// to be got from the user. +int ins_compl_bs(void) +{ + char_u *line = get_cursor_line_ptr(); + char_u *p = line + curwin->w_cursor.col; + MB_PTR_BACK(line, p); + ptrdiff_t p_off = p - line; + + // Stop completion when the whole word was deleted. For Omni completion + // allow the word to be deleted, we won't match everything. + // Respect the 'backspace' option. + if ((int)(p - line) - (int)compl_col < 0 + || ((int)(p - line) - (int)compl_col == 0 && !ctrl_x_mode_omni()) + || ctrl_x_mode_eval() + || (!can_bs(BS_START) && (int)(p - line) - (int)compl_col + - compl_length < 0)) { + return K_BS; + } + + // Deleted more than what was used to find matches or didn't finish + // finding all matches: need to look for matches all over again. + if (curwin->w_cursor.col <= compl_col + compl_length + || ins_compl_need_restart()) { + ins_compl_restart(); + } + + // ins_compl_restart() calls update_screen(0) which may invalidate the pointer + // TODO(bfredl): get rid of random update_screen() calls deep inside completion logic + line = get_cursor_line_ptr(); + + xfree(compl_leader); + compl_leader = vim_strnsave(line + compl_col, (size_t)(p_off - (ptrdiff_t)compl_col)); + ins_compl_new_leader(); + if (compl_shown_match != NULL) { + // Make sure current match is not a hidden item. + compl_curr_match = compl_shown_match; + } + + return NUL; +} + +/// Check that we need to find matches again, ins_compl_restart() is to +/// be called. +static bool ins_compl_need_restart(void) + FUNC_ATTR_PURE +{ + // Return true if we didn't complete finding matches or when the + // "completefunc" returned "always" in the "refresh" dictionary item. + return compl_was_interrupted + || ((ctrl_x_mode_function() || ctrl_x_mode_omni()) + && compl_opt_refresh_always); +} + +/// Called after changing "compl_leader". +/// Show the popup menu with a different set of matches. +/// May also search for matches again if the previous search was interrupted. +static void ins_compl_new_leader(void) +{ + ins_compl_del_pum(); + ins_compl_delete(); + ins_bytes(compl_leader + get_compl_len()); + compl_used_match = false; + + if (compl_started) { + ins_compl_set_original_text(compl_leader); + } else { + spell_bad_len = 0; // need to redetect bad word + // Matches were cleared, need to search for them now. + // Set "compl_restarting" to avoid that the first match is inserted. + compl_restarting = true; + if (ins_complete(Ctrl_N, true) == FAIL) { + compl_cont_status = 0; + } + compl_restarting = false; + } + + compl_enter_selects = !compl_used_match; + + // Show the popup menu with a different set of matches. + ins_compl_show_pum(); + + // Don't let Enter select the original text when there is no popup menu. + // Don't let Enter select when use user function and refresh_always is set + if (compl_match_array == NULL || ins_compl_need_restart()) { + compl_enter_selects = false; + } +} + +/// Return the length of the completion, from the completion start column to +/// the cursor column. Making sure it never goes below zero. +static int get_compl_len(void) +{ + int off = (int)curwin->w_cursor.col - (int)compl_col; + + if (off < 0) { + return 0; + } + return off; +} + +/// Append one character to the match leader. May reduce the number of +/// matches. +void ins_compl_addleader(int c) +{ + int cc; + + if (stop_arrow() == FAIL) { + return; + } + if ((cc = utf_char2len(c)) > 1) { + char buf[MB_MAXBYTES + 1]; + + utf_char2bytes(c, (char *)buf); + buf[cc] = NUL; + ins_char_bytes((char_u *)buf, (size_t)cc); + } else { + ins_char(c); + } + + // If we didn't complete finding matches we must search again. + if (ins_compl_need_restart()) { + ins_compl_restart(); + } + + xfree(compl_leader); + compl_leader = vim_strnsave(get_cursor_line_ptr() + compl_col, + (size_t)(curwin->w_cursor.col - compl_col)); + ins_compl_new_leader(); +} + +/// Setup for finding completions again without leaving CTRL-X mode. Used when +/// BS or a key was typed while still searching for matches. +static void ins_compl_restart(void) +{ + // update screen before restart. + // so if complete is blocked, + // will stay to the last popup menu and reduce flicker + update_screen(0); + ins_compl_free(); + compl_started = false; + compl_matches = 0; + compl_cont_status = 0; + compl_cont_mode = 0; +} + +/// Set the first match, the original text. +static void ins_compl_set_original_text(char_u *str) + FUNC_ATTR_NONNULL_ALL +{ + // Replace the original text entry. + // The CP_ORIGINAL_TEXT flag is either at the first item or might possibly be + // at the last item for backward completion + if (compl_first_match->cp_flags & CP_ORIGINAL_TEXT) { // safety check + xfree(compl_first_match->cp_str); + compl_first_match->cp_str = vim_strsave(str); + } else if (compl_first_match->cp_prev != NULL + && (compl_first_match->cp_prev->cp_flags & CP_ORIGINAL_TEXT)) { + xfree(compl_first_match->cp_prev->cp_str); + compl_first_match->cp_prev->cp_str = vim_strsave(str); + } +} + +/// Append one character to the match leader. May reduce the number of +/// matches. +void ins_compl_addfrommatch(void) +{ + char_u *p; + int len = (int)curwin->w_cursor.col - (int)compl_col; + int c; + compl_T *cp; + assert(compl_shown_match != NULL); + p = compl_shown_match->cp_str; + if ((int)STRLEN(p) <= len) { // the match is too short + // When still at the original match use the first entry that matches + // the leader. + if (compl_shown_match->cp_flags & CP_ORIGINAL_TEXT) { + p = NULL; + for (cp = compl_shown_match->cp_next; cp != NULL + && cp != compl_first_match; cp = cp->cp_next) { + if (compl_leader == NULL + || ins_compl_equal(cp, compl_leader, STRLEN(compl_leader))) { + p = cp->cp_str; + break; + } + } + if (p == NULL || (int)STRLEN(p) <= len) { + return; + } + } else { + return; + } + } + p += len; + c = utf_ptr2char((char *)p); + ins_compl_addleader(c); +} + +/// Prepare for Insert mode completion, or stop it. +/// Called just after typing a character in Insert mode. +/// +/// @param c character that was typed +/// +/// @return true when the character is not to be inserted; +bool ins_compl_prep(int c) +{ + char_u *ptr; + bool retval = false; + const int prev_mode = ctrl_x_mode; + + // Forget any previous 'special' messages if this is actually + // a ^X mode key - bar ^R, in which case we wait to see what it gives us. + if (c != Ctrl_R && vim_is_ctrl_x_key(c)) { + edit_submode_extra = NULL; + } + + // Ignore end of Select mode mapping and mouse scroll buttons. + if (c == K_SELECT || c == K_MOUSEDOWN || c == K_MOUSEUP + || c == K_MOUSELEFT || c == K_MOUSERIGHT || c == K_EVENT + || c == K_COMMAND || c == K_LUA) { + return retval; + } + + if (ctrl_x_mode == CTRL_X_CMDLINE_CTRL_X && c != Ctrl_X) { + if (c == Ctrl_V || c == Ctrl_Q || c == Ctrl_Z || ins_compl_pum_key(c) + || !vim_is_ctrl_x_key(c)) { + // Not starting another completion mode. + ctrl_x_mode = CTRL_X_CMDLINE; + + // CTRL-X CTRL-Z should stop completion without inserting anything + if (c == Ctrl_Z) { + retval = true; + } + } else { + ctrl_x_mode = CTRL_X_CMDLINE; + + // Other CTRL-X keys first stop completion, then start another + // completion mode. + ins_compl_prep(' '); + ctrl_x_mode = CTRL_X_NOT_DEFINED_YET; + } + } + + // Set "compl_get_longest" when finding the first matches. + if (ctrl_x_mode_not_defined_yet() + || (ctrl_x_mode_normal() && !compl_started)) { + compl_get_longest = (strstr((char *)p_cot, "longest") != NULL); + compl_used_match = true; + } + + if (ctrl_x_mode_not_defined_yet()) { + // We have just typed CTRL-X and aren't quite sure which CTRL-X mode + // it will be yet. Now we decide. + switch (c) { + case Ctrl_E: + case Ctrl_Y: + ctrl_x_mode = CTRL_X_SCROLL; + if (!(State & REPLACE_FLAG)) { + edit_submode = (char_u *)_(" (insert) Scroll (^E/^Y)"); + } else { + edit_submode = (char_u *)_(" (replace) Scroll (^E/^Y)"); + } + edit_submode_pre = NULL; + showmode(); + break; + case Ctrl_L: + ctrl_x_mode = CTRL_X_WHOLE_LINE; + break; + case Ctrl_F: + ctrl_x_mode = CTRL_X_FILES; + break; + case Ctrl_K: + ctrl_x_mode = CTRL_X_DICTIONARY; + break; + case Ctrl_R: + // Simply allow ^R to happen without affecting ^X mode + break; + case Ctrl_T: + ctrl_x_mode = CTRL_X_THESAURUS; + break; + case Ctrl_U: + ctrl_x_mode = CTRL_X_FUNCTION; + break; + case Ctrl_O: + ctrl_x_mode = CTRL_X_OMNI; + break; + case 's': + case Ctrl_S: + ctrl_x_mode = CTRL_X_SPELL; + emsg_off++; // Avoid getting the E756 error twice. + spell_back_to_badword(); + emsg_off--; + break; + case Ctrl_RSB: + ctrl_x_mode = CTRL_X_TAGS; + break; + case Ctrl_I: + case K_S_TAB: + ctrl_x_mode = CTRL_X_PATH_PATTERNS; + break; + case Ctrl_D: + ctrl_x_mode = CTRL_X_PATH_DEFINES; + break; + case Ctrl_V: + case Ctrl_Q: + ctrl_x_mode = CTRL_X_CMDLINE; + break; + case Ctrl_Z: + ctrl_x_mode = CTRL_X_NORMAL; + edit_submode = NULL; + showmode(); + retval = true; + break; + case Ctrl_P: + case Ctrl_N: + // ^X^P means LOCAL expansion if nothing interrupted (eg we + // just started ^X mode, or there were enough ^X's to cancel + // the previous mode, say ^X^F^X^X^P or ^P^X^X^X^P, see below) + // do normal expansion when interrupting a different mode (say + // ^X^F^X^P or ^P^X^X^P, see below) + // nothing changes if interrupting mode 0, (eg, the flag + // doesn't change when going to ADDING mode -- Acevedo + if (!(compl_cont_status & CONT_INTRPT)) { + compl_cont_status |= CONT_LOCAL; + } else if (compl_cont_mode != 0) { + compl_cont_status &= ~CONT_LOCAL; + } + FALLTHROUGH; + default: + // If we have typed at least 2 ^X's... for modes != 0, we set + // compl_cont_status = 0 (eg, as if we had just started ^X + // mode). + // For mode 0, we set "compl_cont_mode" to an impossible + // value, in both cases ^X^X can be used to restart the same + // mode (avoiding ADDING mode). + // Undocumented feature: In a mode != 0 ^X^P and ^X^X^P start + // 'complete' and local ^P expansions respectively. + // In mode 0 an extra ^X is needed since ^X^P goes to ADDING + // mode -- Acevedo + if (c == Ctrl_X) { + if (compl_cont_mode != 0) { + compl_cont_status = 0; + } else { + compl_cont_mode = CTRL_X_NOT_DEFINED_YET; + } + } + ctrl_x_mode = CTRL_X_NORMAL; + edit_submode = NULL; + showmode(); + break; + } + } else if (ctrl_x_mode_not_default()) { + // We're already in CTRL-X mode, do we stay in it? + if (!vim_is_ctrl_x_key(c)) { + if (ctrl_x_mode_scroll()) { + ctrl_x_mode = CTRL_X_NORMAL; + } else { + ctrl_x_mode = CTRL_X_FINISHED; + } + edit_submode = NULL; + } + showmode(); + } + + if (compl_started || ctrl_x_mode == CTRL_X_FINISHED) { + // Show error message from attempted keyword completion (probably + // 'Pattern not found') until another key is hit, then go back to + // showing what mode we are in. + showmode(); + if ((ctrl_x_mode_normal() + && c != Ctrl_N + && c != Ctrl_P + && c != Ctrl_R + && !ins_compl_pum_key(c)) + || ctrl_x_mode == CTRL_X_FINISHED) { + // Get here when we have finished typing a sequence of ^N and + // ^P or other completion characters in CTRL-X mode. Free up + // memory that was used, and make sure we can redo the insert. + if (compl_curr_match != NULL || compl_leader != NULL || c == Ctrl_E) { + // If any of the original typed text has been changed, eg when + // ignorecase is set, we must add back-spaces to the redo + // buffer. We add as few as necessary to delete just the part + // of the original text that has changed. + // When using the longest match, edited the match or used + // CTRL-E then don't use the current match. + if (compl_curr_match != NULL && compl_used_match && c != Ctrl_E) { + ptr = compl_curr_match->cp_str; + } else { + ptr = NULL; + } + ins_compl_fixRedoBufForLeader(ptr); + } + + bool want_cindent = (can_cindent_get() && cindent_on()); + + // When completing whole lines: fix indent for 'cindent'. + // Otherwise, break line if it's too long. + if (compl_cont_mode == CTRL_X_WHOLE_LINE) { + // re-indent the current line + if (want_cindent) { + do_c_expr_indent(); + want_cindent = false; // don't do it again + } + } else { + int prev_col = curwin->w_cursor.col; + + // put the cursor on the last char, for 'tw' formatting + if (prev_col > 0) { + dec_cursor(); + } + + if (!arrow_used && !ins_need_undo_get() && c != Ctrl_E) { + insertchar(NUL, 0, -1); + } + + if (prev_col > 0 + && get_cursor_line_ptr()[curwin->w_cursor.col] != NUL) { + inc_cursor(); + } + } + + // If the popup menu is displayed pressing CTRL-Y means accepting + // the selection without inserting anything. When + // compl_enter_selects is set the Enter key does the same. + if ((c == Ctrl_Y || (compl_enter_selects + && (c == CAR || c == K_KENTER || c == NL))) + && pum_visible()) { + retval = true; + } + + // CTRL-E means completion is Ended, go back to the typed text. + // but only do this, if the Popup is still visible + if (c == Ctrl_E) { + ins_compl_delete(); + if (compl_leader != NULL) { + ins_bytes(compl_leader + get_compl_len()); + } else if (compl_first_match != NULL) { + ins_bytes(compl_orig_text + get_compl_len()); + } + retval = true; + } + + auto_format(false, true); + + // Trigger the CompleteDonePre event to give scripts a chance to + // act upon the completion before clearing the info, and restore + // ctrl_x_mode, so that complete_info() can be used. + ctrl_x_mode = prev_mode; + ins_apply_autocmds(EVENT_COMPLETEDONEPRE); + + ins_compl_free(); + compl_started = false; + compl_matches = 0; + if (!shortmess(SHM_COMPLETIONMENU)) { + msg_clr_cmdline(); // necessary for "noshowmode" + } + ctrl_x_mode = CTRL_X_NORMAL; + compl_enter_selects = false; + if (edit_submode != NULL) { + edit_submode = NULL; + showmode(); + } + + // Avoid the popup menu remains displayed when leaving the + // command line window. + if (c == Ctrl_C && cmdwin_type != 0) { + update_screen(0); + } + + // Indent now if a key was typed that is in 'cinkeys'. + if (want_cindent && in_cinkeys(KEY_COMPLETE, ' ', inindent(0))) { + do_c_expr_indent(); + } + // Trigger the CompleteDone event to give scripts a chance to act + // upon the end of completion. + ins_apply_autocmds(EVENT_COMPLETEDONE); + } + } else if (ctrl_x_mode == CTRL_X_LOCAL_MSG) { + // Trigger the CompleteDone event to give scripts a chance to act + // upon the (possibly failed) completion. + ins_apply_autocmds(EVENT_COMPLETEDONE); + } + + may_trigger_modechanged(); + + // reset continue_* if we left expansion-mode, if we stay they'll be + // (re)set properly in ins_complete() + if (!vim_is_ctrl_x_key(c)) { + compl_cont_status = 0; + compl_cont_mode = 0; + } + + return retval; +} + +/// Fix the redo buffer for the completion leader replacing some of the typed +/// text. This inserts backspaces and appends the changed text. +/// "ptr" is the known leader text or NUL. +static void ins_compl_fixRedoBufForLeader(char_u *ptr_arg) +{ + int len; + char_u *p; + char_u *ptr = ptr_arg; + + if (ptr == NULL) { + if (compl_leader != NULL) { + ptr = compl_leader; + } else { + return; // nothing to do + } + } + if (compl_orig_text != NULL) { + p = compl_orig_text; + for (len = 0; p[len] != NUL && p[len] == ptr[len]; len++) {} + if (len > 0) { + len -= utf_head_off(p, p + len); + } + for (p += len; *p != NUL; MB_PTR_ADV(p)) { + AppendCharToRedobuff(K_BS); + } + } else { + len = 0; + } + AppendToRedobuffLit((char *)ptr + len, -1); +} + +/// Loops through the list of windows, loaded-buffers or non-loaded-buffers +/// (depending on flag) starting from buf and looking for a non-scanned +/// buffer (other than curbuf). curbuf is special, if it is called with +/// buf=curbuf then it has to be the first call for a given flag/expansion. +/// +/// Returns the buffer to scan, if any, otherwise returns curbuf -- Acevedo +static buf_T *ins_compl_next_buf(buf_T *buf, int flag) +{ + static win_T *wp = NULL; + + if (flag == 'w') { // just windows + if (buf == curbuf || wp == NULL) { // first call for this flag/expansion + wp = curwin; + } + assert(wp); + while ((wp = (wp->w_next != NULL ? wp->w_next : firstwin)) != curwin + && wp->w_buffer->b_scanned) {} + buf = wp->w_buffer; + } else { + // 'b' (just loaded buffers), 'u' (just non-loaded buffers) or 'U' + // (unlisted buffers) + // When completing whole lines skip unloaded buffers. + while ((buf = (buf->b_next != NULL ? buf->b_next : firstbuf)) != curbuf + && ((flag == 'U' + ? buf->b_p_bl + : (!buf->b_p_bl + || (buf->b_ml.ml_mfp == NULL) != (flag == 'u'))) + || buf->b_scanned)) {} + } + return buf; +} + +/// Get the user-defined completion function name for completion 'type' +static char_u *get_complete_funcname(int type) +{ + switch (type) { + case CTRL_X_FUNCTION: + return curbuf->b_p_cfu; + case CTRL_X_OMNI: + return curbuf->b_p_ofu; + case CTRL_X_THESAURUS: + return *curbuf->b_p_tsrfu == NUL ? p_tsrfu : curbuf->b_p_tsrfu; + default: + return (char_u *)""; + } +} + +/// Execute user defined complete function 'completefunc' or 'omnifunc', and +/// get matches in "matches". +/// +/// @param type CTRL_X_OMNI or CTRL_X_FUNCTION +static void expand_by_function(int type, char_u *base) +{ + list_T *matchlist = NULL; + dict_T *matchdict = NULL; + char_u *funcname; + pos_T pos; + typval_T rettv; + const int save_State = State; + + assert(curbuf != NULL); + funcname = get_complete_funcname(type); + if (*funcname == NUL) { + return; + } + + // Call 'completefunc' to obtain the list of matches. + typval_T args[3]; + args[0].v_type = VAR_NUMBER; + args[1].v_type = VAR_STRING; + args[2].v_type = VAR_UNKNOWN; + args[0].vval.v_number = 0; + args[1].vval.v_string = base != NULL ? (char *)base : ""; + + pos = curwin->w_cursor; + // Lock the text to avoid weird things from happening. Also disallow + // switching to another window, it should not be needed and may end up in + // Insert mode in another buffer. + textlock++; + + // Call a function, which returns a list or dict. + if (call_vim_function((char *)funcname, 2, args, &rettv) == OK) { + switch (rettv.v_type) { + case VAR_LIST: + matchlist = rettv.vval.v_list; + break; + case VAR_DICT: + matchdict = rettv.vval.v_dict; + break; + case VAR_SPECIAL: + FALLTHROUGH; + default: + // TODO(brammool): Give error message? + tv_clear(&rettv); + break; + } + } + textlock--; + + curwin->w_cursor = pos; // restore the cursor position + validate_cursor(); + if (!equalpos(curwin->w_cursor, pos)) { + emsg(_(e_compldel)); + goto theend; + } + + if (matchlist != NULL) { + ins_compl_add_list(matchlist); + } else if (matchdict != NULL) { + ins_compl_add_dict(matchdict); + } + +theend: + // Restore State, it might have been changed. + State = save_State; + + if (matchdict != NULL) { + tv_dict_unref(matchdict); + } + if (matchlist != NULL) { + tv_list_unref(matchlist); + } +} + +/// Add a match to the list of matches from VimL object +/// +/// @param[in] tv Object to get matches from. +/// @param[in] dir Completion direction. +/// @param[in] fast use fast_breakcheck() instead of os_breakcheck(). +/// +/// @return NOTDONE if the given string is already in the list of completions, +/// otherwise it is added to the list and OK is returned. FAIL will be +/// returned in case of error. +static int ins_compl_add_tv(typval_T *const tv, const Direction dir, bool fast) + FUNC_ATTR_NONNULL_ALL +{ + const char *word; + bool dup = false; + bool empty = false; + int flags = fast ? CP_FAST : 0; + char *(cptext[CPT_COUNT]); + typval_T user_data; + + user_data.v_type = VAR_UNKNOWN; + if (tv->v_type == VAR_DICT && tv->vval.v_dict != NULL) { + word = tv_dict_get_string(tv->vval.v_dict, "word", false); + cptext[CPT_ABBR] = tv_dict_get_string(tv->vval.v_dict, "abbr", true); + cptext[CPT_MENU] = tv_dict_get_string(tv->vval.v_dict, "menu", true); + cptext[CPT_KIND] = tv_dict_get_string(tv->vval.v_dict, "kind", true); + cptext[CPT_INFO] = tv_dict_get_string(tv->vval.v_dict, "info", true); + tv_dict_get_tv(tv->vval.v_dict, "user_data", &user_data); + + if (tv_dict_get_number(tv->vval.v_dict, "icase")) { + flags |= CP_ICASE; + } + dup = (bool)tv_dict_get_number(tv->vval.v_dict, "dup"); + empty = (bool)tv_dict_get_number(tv->vval.v_dict, "empty"); + if (tv_dict_get_string(tv->vval.v_dict, "equal", false) != NULL + && tv_dict_get_number(tv->vval.v_dict, "equal")) { + flags |= CP_EQUAL; + } + } else { + word = tv_get_string_chk(tv); + memset(cptext, 0, sizeof(cptext)); + } + if (word == NULL || (!empty && *word == NUL)) { + for (size_t i = 0; i < CPT_COUNT; i++) { + xfree(cptext[i]); + } + return FAIL; + } + return ins_compl_add((char_u *)word, -1, NULL, + (char_u **)cptext, true, &user_data, dir, flags, dup); +} + +/// Add completions from a list. +static void ins_compl_add_list(list_T *const list) +{ + Direction dir = compl_direction; + + // Go through the List with matches and add each of them. + TV_LIST_ITER(list, li, { + if (ins_compl_add_tv(TV_LIST_ITEM_TV(li), dir, true) == OK) { + // If dir was BACKWARD then honor it just once. + dir = FORWARD; + } else if (did_emsg) { + break; + } + }); +} + +/// Add completions from a dict. +static void ins_compl_add_dict(dict_T *dict) +{ + dictitem_T *di_refresh; + dictitem_T *di_words; + + // Check for optional "refresh" item. + compl_opt_refresh_always = false; + di_refresh = tv_dict_find(dict, S_LEN("refresh")); + if (di_refresh != NULL && di_refresh->di_tv.v_type == VAR_STRING) { + const char *v = (const char *)di_refresh->di_tv.vval.v_string; + + if (v != NULL && strcmp(v, "always") == 0) { + compl_opt_refresh_always = true; + } + } + + // Add completions from a "words" list. + di_words = tv_dict_find(dict, S_LEN("words")); + if (di_words != NULL && di_words->di_tv.v_type == VAR_LIST) { + ins_compl_add_list(di_words->di_tv.vval.v_list); + } +} + +/// Start completion for the complete() function. +/// +/// @param startcol where the matched text starts (1 is first column). +/// @param list the list of matches. +static void set_completion(colnr_T startcol, list_T *list) +{ + int flags = CP_ORIGINAL_TEXT; + + // If already doing completions stop it. + if (ctrl_x_mode_not_default()) { + ins_compl_prep(' '); + } + ins_compl_clear(); + ins_compl_free(); + + compl_direction = FORWARD; + if (startcol > curwin->w_cursor.col) { + startcol = curwin->w_cursor.col; + } + compl_col = startcol; + compl_length = (int)curwin->w_cursor.col - (int)startcol; + // compl_pattern doesn't need to be set + compl_orig_text = vim_strnsave(get_cursor_line_ptr() + compl_col, + (size_t)compl_length); + if (p_ic) { + flags |= CP_ICASE; + } + if (ins_compl_add(compl_orig_text, -1, NULL, NULL, false, NULL, 0, + flags | CP_FAST, false) != OK) { + return; + } + + ctrl_x_mode = CTRL_X_EVAL; + + ins_compl_add_list(list); + compl_matches = ins_compl_make_cyclic(); + compl_started = true; + compl_used_match = true; + compl_cont_status = 0; + int save_w_wrow = curwin->w_wrow; + int save_w_leftcol = curwin->w_leftcol; + + compl_curr_match = compl_first_match; + if (compl_no_insert || compl_no_select) { + ins_complete(K_DOWN, false); + if (compl_no_select) { + ins_complete(K_UP, false); + } + } else { + ins_complete(Ctrl_N, false); + } + compl_enter_selects = compl_no_insert; + + // Lazily show the popup menu, unless we got interrupted. + if (!compl_interrupted) { + show_pum(save_w_wrow, save_w_leftcol); + } + + may_trigger_modechanged(); + ui_flush(); +} + +/// "complete()" function +void f_complete(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + if ((State & MODE_INSERT) == 0) { + emsg(_("E785: complete() can only be used in Insert mode")); + return; + } + + // Check for undo allowed here, because if something was already inserted + // the line was already saved for undo and this check isn't done. + if (!undo_allowed(curbuf)) { + return; + } + + if (argvars[1].v_type != VAR_LIST) { + emsg(_(e_invarg)); + } else { + const colnr_T startcol = (colnr_T)tv_get_number_chk(&argvars[0], NULL); + if (startcol > 0) { + set_completion(startcol - 1, argvars[1].vval.v_list); + } + } +} + +/// "complete_add()" function +void f_complete_add(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + rettv->vval.v_number = ins_compl_add_tv(&argvars[0], 0, false); +} + +/// "complete_check()" function +void f_complete_check(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + int saved = RedrawingDisabled; + + RedrawingDisabled = 0; + ins_compl_check_keys(0, true); + rettv->vval.v_number = ins_compl_interrupted(); + RedrawingDisabled = saved; +} + +/// Return Insert completion mode name string +static char_u *ins_compl_mode(void) +{ + if (ctrl_x_mode_not_defined_yet() || ctrl_x_mode_scroll() || compl_started) { + return (char_u *)ctrl_x_mode_names[ctrl_x_mode & ~CTRL_X_WANT_IDENT]; + } + return (char_u *)""; +} + +static void ins_compl_update_sequence_numbers(void) +{ + int number = 0; + compl_T *match; + + if (compl_direction == FORWARD) { + // search backwards for the first valid (!= -1) number. + // This should normally succeed already at the first loop + // cycle, so it's fast! + for (match = compl_curr_match->cp_prev; + match != NULL && match != compl_first_match; + match = match->cp_prev) { + if (match->cp_number != -1) { + number = match->cp_number; + break; + } + } + if (match != NULL) { + // go up and assign all numbers which are not assigned yet + for (match = match->cp_next; + match != NULL && match->cp_number == -1; + match = match->cp_next) { + match->cp_number = ++number; + } + } + } else { // BACKWARD + assert(compl_direction == BACKWARD); + // search forwards (upwards) for the first valid (!= -1) + // number. This should normally succeed already at the + // first loop cycle, so it's fast! + for (match = compl_curr_match->cp_next; + match != NULL && match != compl_first_match; + match = match->cp_next) { + if (match->cp_number != -1) { + number = match->cp_number; + break; + } + } + if (match != NULL) { + // go down and assign all numbers which are not + // assigned yet + for (match = match->cp_prev; + match && match->cp_number == -1; + match = match->cp_prev) { + match->cp_number = ++number; + } + } + } +} + +/// Get complete information +static void get_complete_info(list_T *what_list, dict_T *retdict) +{ +#define CI_WHAT_MODE 0x01 +#define CI_WHAT_PUM_VISIBLE 0x02 +#define CI_WHAT_ITEMS 0x04 +#define CI_WHAT_SELECTED 0x08 +#define CI_WHAT_INSERTED 0x10 +#define CI_WHAT_ALL 0xff + int what_flag; + + if (what_list == NULL) { + what_flag = CI_WHAT_ALL; + } else { + what_flag = 0; + for (listitem_T *item = tv_list_first(what_list) + ; item != NULL + ; item = TV_LIST_ITEM_NEXT(what_list, item)) { + const char *what = tv_get_string(TV_LIST_ITEM_TV(item)); + + if (STRCMP(what, "mode") == 0) { + what_flag |= CI_WHAT_MODE; + } else if (STRCMP(what, "pum_visible") == 0) { + what_flag |= CI_WHAT_PUM_VISIBLE; + } else if (STRCMP(what, "items") == 0) { + what_flag |= CI_WHAT_ITEMS; + } else if (STRCMP(what, "selected") == 0) { + what_flag |= CI_WHAT_SELECTED; + } else if (STRCMP(what, "inserted") == 0) { + what_flag |= CI_WHAT_INSERTED; + } + } + } + + int ret = OK; + if (what_flag & CI_WHAT_MODE) { + ret = tv_dict_add_str(retdict, S_LEN("mode"), + (char *)ins_compl_mode()); + } + + if (ret == OK && (what_flag & CI_WHAT_PUM_VISIBLE)) { + ret = tv_dict_add_nr(retdict, S_LEN("pum_visible"), pum_visible()); + } + + if (ret == OK && (what_flag & CI_WHAT_ITEMS)) { + list_T *li = tv_list_alloc(get_compl_len()); + + ret = tv_dict_add_list(retdict, S_LEN("items"), li); + if (ret == OK && compl_first_match != NULL) { + compl_T *match = compl_first_match; + do { + if (!(match->cp_flags & CP_ORIGINAL_TEXT)) { + dict_T *di = tv_dict_alloc(); + + tv_list_append_dict(li, di); + tv_dict_add_str(di, S_LEN("word"), EMPTY_IF_NULL(match->cp_str)); + tv_dict_add_str(di, S_LEN("abbr"), EMPTY_IF_NULL(match->cp_text[CPT_ABBR])); + tv_dict_add_str(di, S_LEN("menu"), EMPTY_IF_NULL(match->cp_text[CPT_MENU])); + tv_dict_add_str(di, S_LEN("kind"), EMPTY_IF_NULL(match->cp_text[CPT_KIND])); + tv_dict_add_str(di, S_LEN("info"), EMPTY_IF_NULL(match->cp_text[CPT_INFO])); + if (match->cp_user_data.v_type == VAR_UNKNOWN) { + tv_dict_add_str(di, S_LEN("user_data"), ""); + } else { + tv_dict_add_tv(di, S_LEN("user_data"), &match->cp_user_data); + } + } + match = match->cp_next; + } while (match != NULL && match != compl_first_match); + } + } + + if (ret == OK && (what_flag & CI_WHAT_SELECTED)) { + if (compl_curr_match != NULL && compl_curr_match->cp_number == -1) { + ins_compl_update_sequence_numbers(); + } + ret = tv_dict_add_nr(retdict, S_LEN("selected"), + (compl_curr_match != NULL) + ? compl_curr_match->cp_number - 1 : -1); + } + + (void)ret; + // TODO(vim): + // if (ret == OK && (what_flag & CI_WHAT_INSERTED)) +} + +/// "complete_info()" function +void f_complete_info(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + tv_dict_alloc_ret(rettv); + + list_T *what_list = NULL; + + if (argvars[0].v_type != VAR_UNKNOWN) { + if (argvars[0].v_type != VAR_LIST) { + emsg(_(e_listreq)); + return; + } + what_list = argvars[0].vval.v_list; + } + get_complete_info(what_list, rettv->vval.v_dict); +} + +/// Returns true when using a user-defined function for thesaurus completion. +static bool thesaurus_func_complete(int type) +{ + return type == CTRL_X_THESAURUS + && (*curbuf->b_p_tsrfu != NUL || *p_tsrfu != NUL); +} + +/// Get the next expansion(s), using "compl_pattern". +/// The search starts at position "ini" in curbuf and in the direction +/// compl_direction. +/// When "compl_started" is false start at that position, otherwise continue +/// where we stopped searching before. +/// This may return before finding all the matches. +/// Return the total number of matches or -1 if still unknown -- Acevedo +static int ins_compl_get_exp(pos_T *ini) +{ + static pos_T first_match_pos; + static pos_T last_match_pos; + static char_u *e_cpt = (char_u *)""; // curr. entry in 'complete' + static bool found_all = false; // Found all matches of a + // certain type. + static buf_T *ins_buf = NULL; // buffer being scanned + + pos_T *pos; + char **matches; + int save_p_scs; + bool save_p_ws; + int save_p_ic; + int i; + int num_matches; + int len; + int found_new_match; + int type = ctrl_x_mode; + char_u *ptr; + char_u *dict = NULL; + int dict_f = 0; + bool set_match_pos; + pos_T prev_pos = { 0, 0, 0 }; + + assert(curbuf != NULL); + + if (!compl_started) { + FOR_ALL_BUFFERS(buf) { + buf->b_scanned = false; + } + found_all = false; + ins_buf = curbuf; + e_cpt = (compl_cont_status & CONT_LOCAL) + ? (char_u *)"." : curbuf->b_p_cpt; + last_match_pos = first_match_pos = *ini; + } else if (ins_buf != curbuf && !buf_valid(ins_buf)) { + ins_buf = curbuf; // In case the buffer was wiped out. + } + assert(ins_buf != NULL); + + compl_old_match = compl_curr_match; // remember the last current match + pos = (compl_direction == FORWARD) ? &last_match_pos : &first_match_pos; + + // For ^N/^P loop over all the flags/windows/buffers in 'complete' + for (;;) { + found_new_match = FAIL; + set_match_pos = false; + + // For ^N/^P pick a new entry from e_cpt if compl_started is off, + // or if found_all says this entry is done. For ^X^L only use the + // entries from 'complete' that look in loaded buffers. + if ((ctrl_x_mode_normal() || ctrl_x_mode_line_or_eval()) + && (!compl_started || found_all)) { + found_all = false; + while (*e_cpt == ',' || *e_cpt == ' ') { + e_cpt++; + } + if (*e_cpt == '.' && !curbuf->b_scanned) { + ins_buf = curbuf; + first_match_pos = *ini; + // Move the cursor back one character so that ^N can match the + // word immediately after the cursor. + if (ctrl_x_mode_normal() && dec(&first_match_pos) < 0) { + // Move the cursor to after the last character in the + // buffer, so that word at start of buffer is found + // correctly. + first_match_pos.lnum = ins_buf->b_ml.ml_line_count; + first_match_pos.col = (colnr_T)STRLEN(ml_get(first_match_pos.lnum)); + } + last_match_pos = first_match_pos; + type = 0; + + // Remember the first match so that the loop stops when we + // wrap and come back there a second time. + set_match_pos = true; + } else if (vim_strchr("buwU", *e_cpt) != NULL + && (ins_buf = ins_compl_next_buf(ins_buf, *e_cpt)) != curbuf) { + // Scan a buffer, but not the current one. + if (ins_buf->b_ml.ml_mfp != NULL) { // loaded buffer + compl_started = true; + first_match_pos.col = last_match_pos.col = 0; + first_match_pos.lnum = ins_buf->b_ml.ml_line_count + 1; + last_match_pos.lnum = 0; + type = 0; + } else { // unloaded buffer, scan like dictionary + found_all = true; + if (ins_buf->b_fname == NULL) { + continue; + } + type = CTRL_X_DICTIONARY; + dict = (char_u *)ins_buf->b_fname; + dict_f = DICT_EXACT; + } + msg_hist_off = true; // reset in msg_trunc_attr() + vim_snprintf((char *)IObuff, IOSIZE, _("Scanning: %s"), + ins_buf->b_fname == NULL + ? buf_spname(ins_buf) + : ins_buf->b_sfname == NULL + ? ins_buf->b_fname + : ins_buf->b_sfname); + (void)msg_trunc_attr((char *)IObuff, true, HL_ATTR(HLF_R)); + } else if (*e_cpt == NUL) { + break; + } else { + if (ctrl_x_mode_line_or_eval()) { + type = -1; + } else if (*e_cpt == 'k' || *e_cpt == 's') { + if (*e_cpt == 'k') { + type = CTRL_X_DICTIONARY; + } else { + type = CTRL_X_THESAURUS; + } + if (*++e_cpt != ',' && *e_cpt != NUL) { + dict = e_cpt; + dict_f = DICT_FIRST; + } + } else if (*e_cpt == 'i') { + type = CTRL_X_PATH_PATTERNS; + } else if (*e_cpt == 'd') { + type = CTRL_X_PATH_DEFINES; + } else if (*e_cpt == ']' || *e_cpt == 't') { + msg_hist_off = true; // reset in msg_trunc_attr() + type = CTRL_X_TAGS; + vim_snprintf((char *)IObuff, IOSIZE, "%s", _("Scanning tags.")); + (void)msg_trunc_attr((char *)IObuff, true, HL_ATTR(HLF_R)); + } else { + type = -1; + } + + // in any case e_cpt is advanced to the next entry + (void)copy_option_part((char **)&e_cpt, (char *)IObuff, IOSIZE, ","); + + found_all = true; + if (type == -1) { + continue; + } + } + } + + // If complete() was called then compl_pattern has been reset. + // The following won't work then, bail out. + if (compl_pattern == NULL) { + break; + } + + switch (type) { + case -1: + break; + case CTRL_X_PATH_PATTERNS: + case CTRL_X_PATH_DEFINES: + find_pattern_in_path((char_u *)compl_pattern, compl_direction, + STRLEN(compl_pattern), false, false, + ((type == CTRL_X_PATH_DEFINES + && !(compl_cont_status & CONT_SOL)) + ? FIND_DEFINE + : FIND_ANY), + 1L, ACTION_EXPAND, 1, MAXLNUM); + break; + + case CTRL_X_DICTIONARY: + case CTRL_X_THESAURUS: + if (thesaurus_func_complete(type)) { + expand_by_function(type, (char_u *)compl_pattern); + } else { + ins_compl_dictionaries(dict != NULL ? dict + : (type == CTRL_X_THESAURUS + ? (*curbuf->b_p_tsr == NUL ? p_tsr : curbuf->b_p_tsr) + : (*curbuf->b_p_dict == + NUL ? p_dict : curbuf->b_p_dict)), + (char_u *)compl_pattern, + dict != NULL ? dict_f : 0, type == CTRL_X_THESAURUS); + } + dict = NULL; + break; + + case CTRL_X_TAGS: + // set p_ic according to p_ic, p_scs and pat for find_tags(). + save_p_ic = p_ic; + p_ic = ignorecase((char_u *)compl_pattern); + + // Find up to TAG_MANY matches. Avoids that an enormous number + // of matches is found when compl_pattern is empty + g_tag_at_cursor = true; + if (find_tags((char_u *)compl_pattern, &num_matches, &matches, + TAG_REGEXP | TAG_NAMES | TAG_NOIC | TAG_INS_COMP + | (ctrl_x_mode_not_default() ? TAG_VERBOSE : 0), + TAG_MANY, (char_u *)curbuf->b_ffname) == OK && num_matches > 0) { + ins_compl_add_matches(num_matches, matches, p_ic); + } + g_tag_at_cursor = false; + p_ic = save_p_ic; + break; + + case CTRL_X_FILES: + if (expand_wildcards(1, &compl_pattern, &num_matches, &matches, + EW_FILE|EW_DIR|EW_ADDSLASH|EW_SILENT) == OK) { + // May change home directory back to "~". + tilde_replace((char_u *)compl_pattern, num_matches, matches); +#ifdef BACKSLASH_IN_FILENAME + if (curbuf->b_p_csl[0] != NUL) { + for (int i = 0; i < num_matches; i++) { + char_u *ptr = matches[i]; + while (*ptr != NUL) { + if (curbuf->b_p_csl[0] == 's' && *ptr == '\\') { + *ptr = '/'; + } else if (curbuf->b_p_csl[0] == 'b' && *ptr == '/') { + *ptr = '\\'; + } + ptr += utfc_ptr2len(ptr); + } + } + } +#endif + ins_compl_add_matches(num_matches, matches, p_fic || p_wic); + } + break; + + case CTRL_X_CMDLINE: + case CTRL_X_CMDLINE_CTRL_X: + if (expand_cmdline(&compl_xp, (char_u *)compl_pattern, + (int)STRLEN(compl_pattern), + &num_matches, &matches) == EXPAND_OK) { + ins_compl_add_matches(num_matches, matches, false); + } + break; + + case CTRL_X_FUNCTION: + case CTRL_X_OMNI: + expand_by_function(type, (char_u *)compl_pattern); + break; + + case CTRL_X_SPELL: + num_matches = expand_spelling(first_match_pos.lnum, + (char_u *)compl_pattern, &matches); + if (num_matches > 0) { + ins_compl_add_matches(num_matches, matches, p_ic); + } + break; + + default: // normal ^P/^N and ^X^L + // If 'infercase' is set, don't use 'smartcase' here + save_p_scs = p_scs; + assert(ins_buf); + if (ins_buf->b_p_inf) { + p_scs = false; + } + + // Buffers other than curbuf are scanned from the beginning or the + // end but never from the middle, thus setting nowrapscan in this + // buffers is a good idea, on the other hand, we always set + // wrapscan for curbuf to avoid missing matches -- Acevedo,Webb + save_p_ws = p_ws; + if (ins_buf != curbuf) { + p_ws = false; + } else if (*e_cpt == '.') { + p_ws = true; + } + bool looped_around = false; + for (;;) { + bool cont_s_ipos = false; + + msg_silent++; // Don't want messages for wrapscan. + // ctrl_x_mode_line_or_eval() || word-wise search that + // has added a word that was at the beginning of the line. + if (ctrl_x_mode_line_or_eval() + || (compl_cont_status & CONT_SOL)) { + found_new_match = search_for_exact_line(ins_buf, pos, + compl_direction, + (char_u *)compl_pattern); + } else { + found_new_match = searchit(NULL, ins_buf, pos, NULL, + compl_direction, + (char_u *)compl_pattern, 1L, + SEARCH_KEEP + SEARCH_NFMSG, + RE_LAST, NULL); + } + msg_silent--; + if (!compl_started || set_match_pos) { + // set "compl_started" even on fail + compl_started = true; + first_match_pos = *pos; + last_match_pos = *pos; + set_match_pos = false; + } else if (first_match_pos.lnum == last_match_pos.lnum + && first_match_pos.col == last_match_pos.col) { + found_new_match = FAIL; + } else if ((compl_direction == FORWARD) + && (prev_pos.lnum > pos->lnum + || (prev_pos.lnum == pos->lnum + && prev_pos.col >= pos->col))) { + if (looped_around) { + found_new_match = FAIL; + } else { + looped_around = true; + } + } else if ((compl_direction != FORWARD) + && (prev_pos.lnum < pos->lnum + || (prev_pos.lnum == pos->lnum + && prev_pos.col <= pos->col))) { + if (looped_around) { + found_new_match = FAIL; + } else { + looped_around = true; + } + } + prev_pos = *pos; + if (found_new_match == FAIL) { + if (ins_buf == curbuf) { + found_all = true; + } + break; + } + + // when ADDING, the text before the cursor matches, skip it + if ((compl_cont_status & CONT_ADDING) && ins_buf == curbuf + && ini->lnum == pos->lnum + && ini->col == pos->col) { + continue; + } + ptr = ml_get_buf(ins_buf, pos->lnum, false) + pos->col; + if (ctrl_x_mode_line_or_eval()) { + if (compl_cont_status & CONT_ADDING) { + if (pos->lnum >= ins_buf->b_ml.ml_line_count) { + continue; + } + ptr = ml_get_buf(ins_buf, pos->lnum + 1, false); + if (!p_paste) { + ptr = (char_u *)skipwhite((char *)ptr); + } + } + len = (int)STRLEN(ptr); + } else { + char_u *tmp_ptr = ptr; + + if (compl_cont_status & CONT_ADDING) { + tmp_ptr += compl_length; + // Skip if already inside a word. + if (vim_iswordp(tmp_ptr)) { + continue; + } + // Find start of next word. + tmp_ptr = find_word_start(tmp_ptr); + } + // Find end of this word. + tmp_ptr = find_word_end(tmp_ptr); + len = (int)(tmp_ptr - ptr); + + if ((compl_cont_status & CONT_ADDING) + && len == compl_length) { + if (pos->lnum < ins_buf->b_ml.ml_line_count) { + // Try next line, if any. the new word will be "join" as if the + // normal command "J" was used. IOSIZE is always greater than + // compl_length, so the next STRNCPY always works -- Acevedo + STRNCPY(IObuff, ptr, len); // NOLINT(runtime/printf) + ptr = ml_get_buf(ins_buf, pos->lnum + 1, false); + tmp_ptr = ptr = (char_u *)skipwhite((char *)ptr); + // Find start of next word. + tmp_ptr = find_word_start(tmp_ptr); + // Find end of next word. + tmp_ptr = find_word_end(tmp_ptr); + if (tmp_ptr > ptr) { + if (*ptr != ')' && IObuff[len - 1] != TAB) { + if (IObuff[len - 1] != ' ') { + IObuff[len++] = ' '; + } + // IObuf =~ "\k.* ", thus len >= 2 + if (p_js + && (IObuff[len - 2] == '.' + || IObuff[len - 2] == '?' + || IObuff[len - 2] == '!')) { + IObuff[len++] = ' '; + } + } + // copy as much as possible of the new word + if (tmp_ptr - ptr >= IOSIZE - len) { + tmp_ptr = ptr + IOSIZE - len - 1; + } + STRLCPY(IObuff + len, ptr, IOSIZE - len); + len += (int)(tmp_ptr - ptr); + cont_s_ipos = true; + } + IObuff[len] = NUL; + ptr = IObuff; + } + if (len == compl_length) { + continue; + } + } + } + if (ins_compl_add_infercase(ptr, len, p_ic, + ins_buf == curbuf ? NULL : (char_u *)ins_buf->b_sfname, + 0, cont_s_ipos) != NOTDONE) { + found_new_match = OK; + break; + } + } + p_scs = save_p_scs; + p_ws = save_p_ws; + } + + // check if compl_curr_match has changed, (e.g. other type of + // expansion added something) + if (type != 0 && compl_curr_match != compl_old_match) { + found_new_match = OK; + } + + // break the loop for specialized modes (use 'complete' just for the + // generic ctrl_x_mode == CTRL_X_NORMAL) or when we've found a new match + if ((ctrl_x_mode_not_default() + && !ctrl_x_mode_line_or_eval()) + || found_new_match != FAIL) { + if (got_int) { + break; + } + // Fill the popup menu as soon as possible. + if (type != -1) { + ins_compl_check_keys(0, false); + } + + if ((ctrl_x_mode_not_default() + && !ctrl_x_mode_line_or_eval()) + || compl_interrupted) { + break; + } + compl_started = true; + } else { + // Mark a buffer scanned when it has been scanned completely + if (type == 0 || type == CTRL_X_PATH_PATTERNS) { + assert(ins_buf); + ins_buf->b_scanned = true; + } + + compl_started = false; + } + } + compl_started = true; + + if ((ctrl_x_mode_normal() + || ctrl_x_mode_line_or_eval()) + && *e_cpt == NUL) { // Got to end of 'complete' + found_new_match = FAIL; + } + + i = -1; // total of matches, unknown + if (found_new_match == FAIL + || (ctrl_x_mode_not_default() + && !ctrl_x_mode_line_or_eval())) { + i = ins_compl_make_cyclic(); + } + + if (compl_old_match != NULL) { + // If several matches were added (FORWARD) or the search failed and has + // just been made cyclic then we have to move compl_curr_match to the + // next or previous entry (if any) -- Acevedo + compl_curr_match = compl_direction == FORWARD + ? compl_old_match->cp_next + : compl_old_match->cp_prev; + if (compl_curr_match == NULL) { + compl_curr_match = compl_old_match; + } + } + may_trigger_modechanged(); + + return i; +} + +/// Delete the old text being completed. +void ins_compl_delete(void) +{ + int col; + + // In insert mode: Delete the typed part. + // In replace mode: Put the old characters back, if any. + col = compl_col + (compl_cont_status & CONT_ADDING ? compl_length : 0); + if ((int)curwin->w_cursor.col > col) { + if (stop_arrow() == FAIL) { + return; + } + backspace_until_column(col); + } + + // TODO(vim): is this sufficient for redrawing? Redrawing everything + // causes flicker, thus we can't do that. + changed_cline_bef_curs(); + // clear v:completed_item + set_vim_var_dict(VV_COMPLETED_ITEM, tv_dict_alloc_lock(VAR_FIXED)); +} + +/// Insert the new text being completed. +/// "in_compl_func" is true when called from complete_check(). +void ins_compl_insert(bool in_compl_func) +{ + ins_bytes(compl_shown_match->cp_str + get_compl_len()); + compl_used_match = !(compl_shown_match->cp_flags & CP_ORIGINAL_TEXT); + + dict_T *dict = ins_compl_dict_alloc(compl_shown_match); + set_vim_var_dict(VV_COMPLETED_ITEM, dict); + if (!in_compl_func) { + compl_curr_match = compl_shown_match; + } +} + +/// Fill in the next completion in the current direction. +/// If "allow_get_expansion" is true, then we may call ins_compl_get_exp() to +/// get more completions. If it is false, then we just do nothing when there +/// are no more completions in a given direction. The latter case is used when +/// we are still in the middle of finding completions, to allow browsing +/// through the ones found so far. +/// @return the total number of matches, or -1 if still unknown -- webb. +/// +/// compl_curr_match is currently being used by ins_compl_get_exp(), so we use +/// compl_shown_match here. +/// +/// Note that this function may be called recursively once only. First with +/// "allow_get_expansion" true, which calls ins_compl_get_exp(), which in turn +/// calls this function with "allow_get_expansion" false. +/// +/// @param count Repeat completion this many times; should be at least 1 +/// @param insert_match Insert the newly selected match +/// @param in_compl_func Called from complete_check() +static int ins_compl_next(bool allow_get_expansion, int count, bool insert_match, + bool in_compl_func) +{ + int num_matches = -1; + int todo = count; + compl_T *found_compl = NULL; + bool found_end = false; + const bool started = compl_started; + + // When user complete function return -1 for findstart which is next + // time of 'always', compl_shown_match become NULL. + if (compl_shown_match == NULL) { + return -1; + } + + if (compl_leader != NULL + && (compl_shown_match->cp_flags & CP_ORIGINAL_TEXT) == 0) { + // Set "compl_shown_match" to the actually shown match, it may differ + // when "compl_leader" is used to omit some of the matches. + while (!ins_compl_equal(compl_shown_match, + compl_leader, STRLEN(compl_leader)) + && compl_shown_match->cp_next != NULL + && compl_shown_match->cp_next != compl_first_match) { + compl_shown_match = compl_shown_match->cp_next; + } + + // If we didn't find it searching forward, and compl_shows_dir is + // backward, find the last match. + if (compl_shows_dir == BACKWARD + && !ins_compl_equal(compl_shown_match, compl_leader, STRLEN(compl_leader)) + && (compl_shown_match->cp_next == NULL + || compl_shown_match->cp_next == compl_first_match)) { + while (!ins_compl_equal(compl_shown_match, compl_leader, STRLEN(compl_leader)) + && compl_shown_match->cp_prev != NULL + && compl_shown_match->cp_prev != compl_first_match) { + compl_shown_match = compl_shown_match->cp_prev; + } + } + } + + if (allow_get_expansion && insert_match + && (!(compl_get_longest || compl_restarting) || compl_used_match)) { + // Delete old text to be replaced + ins_compl_delete(); + } + + // When finding the longest common text we stick at the original text, + // don't let CTRL-N or CTRL-P move to the first match. + bool advance = count != 1 || !allow_get_expansion || !compl_get_longest; + + // When restarting the search don't insert the first match either. + if (compl_restarting) { + advance = false; + compl_restarting = false; + } + + // Repeat this for when <PageUp> or <PageDown> is typed. But don't wrap + // around. + while (--todo >= 0) { + if (compl_shows_dir == FORWARD && compl_shown_match->cp_next != NULL) { + compl_shown_match = compl_shown_match->cp_next; + found_end = (compl_first_match != NULL + && (compl_shown_match->cp_next == compl_first_match + || compl_shown_match == compl_first_match)); + } else if (compl_shows_dir == BACKWARD + && compl_shown_match->cp_prev != NULL) { + found_end = (compl_shown_match == compl_first_match); + compl_shown_match = compl_shown_match->cp_prev; + found_end |= (compl_shown_match == compl_first_match); + } else { + if (!allow_get_expansion) { + if (advance) { + if (compl_shows_dir == BACKWARD) { + compl_pending -= todo + 1; + } else { + compl_pending += todo + 1; + } + } + return -1; + } + + if (!compl_no_select && advance) { + if (compl_shows_dir == BACKWARD) { + compl_pending--; + } else { + compl_pending++; + } + } + + // Find matches. + num_matches = ins_compl_get_exp(&compl_startpos); + + // handle any pending completions + while (compl_pending != 0 && compl_direction == compl_shows_dir + && advance) { + if (compl_pending > 0 && compl_shown_match->cp_next != NULL) { + compl_shown_match = compl_shown_match->cp_next; + compl_pending--; + } + if (compl_pending < 0 && compl_shown_match->cp_prev != NULL) { + compl_shown_match = compl_shown_match->cp_prev; + compl_pending++; + } else { + break; + } + } + found_end = false; + } + if ((compl_shown_match->cp_flags & CP_ORIGINAL_TEXT) == 0 + && compl_leader != NULL + && !ins_compl_equal(compl_shown_match, + compl_leader, STRLEN(compl_leader))) { + todo++; + } else { + // Remember a matching item. + found_compl = compl_shown_match; + } + + // Stop at the end of the list when we found a usable match. + if (found_end) { + if (found_compl != NULL) { + compl_shown_match = found_compl; + break; + } + todo = 1; // use first usable match after wrapping around + } + } + + // Insert the text of the new completion, or the compl_leader. + if (compl_no_insert && !started) { + ins_bytes(compl_orig_text + get_compl_len()); + compl_used_match = false; + } else if (insert_match) { + if (!compl_get_longest || compl_used_match) { + ins_compl_insert(in_compl_func); + } else { + ins_bytes(compl_leader + get_compl_len()); + } + } else { + compl_used_match = false; + } + + if (!allow_get_expansion) { + // redraw to show the user what was inserted + update_screen(0); + + // display the updated popup menu + ins_compl_show_pum(); + + // Delete old text to be replaced, since we're still searching and + // don't want to match ourselves! + ins_compl_delete(); + } + + // Enter will select a match when the match wasn't inserted and the popup + // menu is visible. + if (compl_no_insert && !started) { + compl_enter_selects = true; + } else { + compl_enter_selects = !insert_match && compl_match_array != NULL; + } + + // Show the file name for the match (if any) + // Truncate the file name to avoid a wait for return. + if (compl_shown_match->cp_fname != NULL) { + char *lead = _("match in file"); + int space = sc_col - vim_strsize(lead) - 2; + char *s; + char *e; + + if (space > 0) { + // We need the tail that fits. With double-byte encoding going + // back from the end is very slow, thus go from the start and keep + // the text that fits in "space" between "s" and "e". + for (s = e = (char *)compl_shown_match->cp_fname; *e != NUL; MB_PTR_ADV(e)) { + space -= ptr2cells(e); + while (space < 0) { + space += ptr2cells(s); + MB_PTR_ADV(s); + } + } + msg_hist_off = true; + vim_snprintf((char *)IObuff, IOSIZE, "%s %s%s", lead, + (char_u *)s > compl_shown_match->cp_fname ? "<" : "", s); + msg((char *)IObuff); + msg_hist_off = false; + redraw_cmdline = false; // don't overwrite! + } + } + + return num_matches; +} + +void pum_ext_select_item(int item, bool insert, bool finish) +{ + if (!pum_visible() || item < -1 || item >= compl_match_arraysize) { + return; + } + pum_want.active = true; + pum_want.item = item; + pum_want.insert = insert; + pum_want.finish = finish; +} + +/// Call this while finding completions, to check whether the user has hit a key +/// that should change the currently displayed completion, or exit completion +/// mode. Also, when compl_pending is not zero, show a completion as soon as +/// possible. -- webb +/// +/// @param frequency specifies out of how many calls we actually check. +/// @param in_compl_func true when called from complete_check(), don't set +/// compl_curr_match. +void ins_compl_check_keys(int frequency, bool in_compl_func) +{ + static int count = 0; + + // Don't check when reading keys from a script, :normal or feedkeys(). + // That would break the test scripts. But do check for keys when called + // from complete_check(). + if (!in_compl_func && (using_script() || ex_normal_busy)) { + return; + } + + // Only do this at regular intervals + if (++count < frequency) { + return; + } + count = 0; + + // Check for a typed key. Do use mappings, otherwise vim_is_ctrl_x_key() + // can't do its work correctly. + int c = vpeekc_any(); + if (c != NUL) { + if (vim_is_ctrl_x_key(c) && c != Ctrl_X && c != Ctrl_R) { + c = safe_vgetc(); // Eat the character + compl_shows_dir = ins_compl_key2dir(c); + (void)ins_compl_next(false, ins_compl_key2count(c), + c != K_UP && c != K_DOWN, in_compl_func); + } else { + // Need to get the character to have KeyTyped set. We'll put it + // back with vungetc() below. But skip K_IGNORE. + c = safe_vgetc(); + if (c != K_IGNORE) { + // Don't interrupt completion when the character wasn't typed, + // e.g., when doing @q to replay keys. + if (c != Ctrl_R && KeyTyped) { + compl_interrupted = true; + } + + vungetc(c); + } + } + } + if (compl_pending != 0 && !got_int && !compl_no_insert) { + int todo = compl_pending > 0 ? compl_pending : -compl_pending; + + compl_pending = 0; + (void)ins_compl_next(false, todo, true, in_compl_func); + } +} + +/// Decide the direction of Insert mode complete from the key typed. +/// Returns BACKWARD or FORWARD. +static int ins_compl_key2dir(int c) +{ + if (c == K_EVENT || c == K_COMMAND || c == K_LUA) { + return pum_want.item < pum_selected_item ? BACKWARD : FORWARD; + } + if (c == Ctrl_P || c == Ctrl_L + || c == K_PAGEUP || c == K_KPAGEUP + || c == K_S_UP || c == K_UP) { + return BACKWARD; + } + return FORWARD; +} + +/// Check that "c" is a valid completion key only while the popup menu is shown +/// +/// @param c character to check +static bool ins_compl_pum_key(int c) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT +{ + return pum_visible() && (c == K_PAGEUP || c == K_KPAGEUP || c == K_S_UP + || c == K_PAGEDOWN || c == K_KPAGEDOWN + || c == K_S_DOWN || c == K_UP || c == K_DOWN); +} + +/// Decide the number of completions to move forward. +/// Returns 1 for most keys, height of the popup menu for page-up/down keys. +static int ins_compl_key2count(int c) +{ + int h; + + if (c == K_EVENT || c == K_COMMAND || c == K_LUA) { + int offset = pum_want.item - pum_selected_item; + return abs(offset); + } + + if (ins_compl_pum_key(c) && c != K_UP && c != K_DOWN) { + h = pum_get_height(); + if (h > 3) { + h -= 2; // keep some context + } + return h; + } + return 1; +} + +/// Check that completion with "c" should insert the match, false if only +/// to change the currently selected completion. +/// +/// @param c character to check +static bool ins_compl_use_match(int c) + FUNC_ATTR_CONST FUNC_ATTR_WARN_UNUSED_RESULT +{ + switch (c) { + case K_UP: + case K_DOWN: + case K_PAGEDOWN: + case K_KPAGEDOWN: + case K_S_DOWN: + case K_PAGEUP: + case K_KPAGEUP: + case K_S_UP: + return false; + case K_EVENT: + case K_COMMAND: + case K_LUA: + return pum_want.active && pum_want.insert; + } + return true; +} + +/// Get the pattern, column and length for normal completion (CTRL-N CTRL-P +/// completion) +/// Sets the global variables: compl_col, compl_length and compl_pattern. +/// Uses the global variables: compl_cont_status and ctrl_x_mode +static int get_normal_compl_info(char_u *line, int startcol, colnr_T curs_col) +{ + if ((compl_cont_status & CONT_SOL) || ctrl_x_mode_path_defines()) { + if (!(compl_cont_status & CONT_ADDING)) { + while (--startcol >= 0 && vim_isIDc(line[startcol])) {} + compl_col += ++startcol; + compl_length = curs_col - startcol; + } + if (p_ic) { + compl_pattern = (char *)str_foldcase(line + compl_col, compl_length, NULL, 0); + } else { + compl_pattern = (char *)vim_strnsave(line + compl_col, (size_t)compl_length); + } + } else if (compl_cont_status & CONT_ADDING) { + char_u *prefix = (char_u *)"\\<"; + + // we need up to 2 extra chars for the prefix + compl_pattern = xmalloc(quote_meta(NULL, line + compl_col, + compl_length) + 2); + if (!vim_iswordp(line + compl_col) + || (compl_col > 0 && (vim_iswordp(mb_prevptr(line, line + compl_col))))) { + prefix = (char_u *)""; + } + STRCPY(compl_pattern, prefix); + (void)quote_meta((char_u *)compl_pattern + STRLEN(prefix), + line + compl_col, compl_length); + } else if (--startcol < 0 + || !vim_iswordp(mb_prevptr(line, line + startcol + 1))) { + // Match any word of at least two chars + compl_pattern = (char *)vim_strsave((char_u *)"\\<\\k\\k"); + compl_col += curs_col; + compl_length = 0; + } else { + // Search the point of change class of multibyte character + // or not a word single byte character backward. + startcol -= utf_head_off(line, line + startcol); + int base_class = mb_get_class(line + startcol); + while (--startcol >= 0) { + int head_off = utf_head_off(line, line + startcol); + if (base_class != mb_get_class(line + startcol - head_off)) { + break; + } + startcol -= head_off; + } + compl_col += ++startcol; + compl_length = (int)curs_col - startcol; + if (compl_length == 1) { + // Only match word with at least two chars -- webb + // there's no need to call quote_meta, + // xmalloc(7) is enough -- Acevedo + compl_pattern = xmalloc(7); + STRCPY(compl_pattern, "\\<"); + (void)quote_meta((char_u *)compl_pattern + 2, line + compl_col, 1); + STRCAT(compl_pattern, "\\k"); + } else { + compl_pattern = xmalloc(quote_meta(NULL, line + compl_col, + compl_length) + 2); + STRCPY(compl_pattern, "\\<"); + (void)quote_meta((char_u *)compl_pattern + 2, line + compl_col, compl_length); + } + } + + return OK; +} + +/// Get the pattern, column and length for whole line completion or for the +/// complete() function. +/// Sets the global variables: compl_col, compl_length and compl_pattern. +static int get_wholeline_compl_info(char_u *line, colnr_T curs_col) +{ + compl_col = (colnr_T)getwhitecols(line); + compl_length = (int)curs_col - (int)compl_col; + if (compl_length < 0) { // cursor in indent: empty pattern + compl_length = 0; + } + if (p_ic) { + compl_pattern = (char *)str_foldcase(line + compl_col, compl_length, NULL, 0); + } else { + compl_pattern = (char *)vim_strnsave(line + compl_col, (size_t)compl_length); + } + + return OK; +} + +/// Get the pattern, column and length for filename completion. +/// Sets the global variables: compl_col, compl_length and compl_pattern. +static int get_filename_compl_info(char_u *line, int startcol, colnr_T curs_col) +{ + // Go back to just before the first filename character. + if (startcol > 0) { + char_u *p = line + startcol; + + MB_PTR_BACK(line, p); + while (p > line && vim_isfilec(utf_ptr2char((char *)p))) { + MB_PTR_BACK(line, p); + } + if (p == line && vim_isfilec(utf_ptr2char((char *)p))) { + startcol = 0; + } else { + startcol = (int)(p - line) + 1; + } + } + + compl_col += startcol; + compl_length = (int)curs_col - startcol; + compl_pattern = (char *)addstar(line + compl_col, (size_t)compl_length, EXPAND_FILES); + + return OK; +} + +/// Get the pattern, column and length for command-line completion. +/// Sets the global variables: compl_col, compl_length and compl_pattern. +static int get_cmdline_compl_info(char_u *line, colnr_T curs_col) +{ + compl_pattern = (char *)vim_strnsave(line, (size_t)curs_col); + set_cmd_context(&compl_xp, (char_u *)compl_pattern, (int)STRLEN(compl_pattern), curs_col, false); + if (compl_xp.xp_context == EXPAND_UNSUCCESSFUL + || compl_xp.xp_context == EXPAND_NOTHING) { + // No completion possible, use an empty pattern to get a + // "pattern not found" message. + compl_col = curs_col; + } else { + compl_col = (int)(compl_xp.xp_pattern - compl_pattern); + } + compl_length = curs_col - compl_col; + + return OK; +} + +/// Get the pattern, column and length for user defined completion ('omnifunc', +/// 'completefunc' and 'thesaurusfunc') +/// Sets the global variables: compl_col, compl_length and compl_pattern. +/// Uses the global variable: spell_bad_len +static int get_userdefined_compl_info(colnr_T curs_col) +{ + // Call user defined function 'completefunc' with "a:findstart" + // set to 1 to obtain the length of text to use for completion. + const int save_State = State; + + // Call 'completefunc' or 'omnifunc' and get pattern length as a string + char_u *funcname = get_complete_funcname(ctrl_x_mode); + if (*funcname == NUL) { + semsg(_(e_notset), ctrl_x_mode_function() ? "completefunc" : "omnifunc"); + return FAIL; + } + + typval_T args[3]; + args[0].v_type = VAR_NUMBER; + args[1].v_type = VAR_STRING; + args[2].v_type = VAR_UNKNOWN; + args[0].vval.v_number = 1; + args[1].vval.v_string = ""; + + pos_T pos = curwin->w_cursor; + textlock++; + colnr_T col = (colnr_T)call_func_retnr((char *)funcname, 2, args); + textlock--; + + State = save_State; + curwin->w_cursor = pos; // restore the cursor position + validate_cursor(); + if (!equalpos(curwin->w_cursor, pos)) { + emsg(_(e_compldel)); + return FAIL; + } + + // Return value -2 means the user complete function wants to cancel the + // complete without an error, do the same if the function did not execute + // successfully. + if (col == -2 || aborting()) { + return FAIL; + } + // Return value -3 does the same as -2 and leaves CTRL-X mode. + if (col == -3) { + ctrl_x_mode = CTRL_X_NORMAL; + edit_submode = NULL; + if (!shortmess(SHM_COMPLETIONMENU)) { + msg_clr_cmdline(); + } + return FAIL; + } + + // Reset extended parameters of completion, when start new + // completion. + compl_opt_refresh_always = false; + + if (col < 0) { + col = curs_col; + } + compl_col = col; + if (compl_col > curs_col) { + compl_col = curs_col; + } + + // Setup variables for completion. Need to obtain "line" again, + // it may have become invalid. + char_u *line = ml_get(curwin->w_cursor.lnum); + compl_length = curs_col - compl_col; + compl_pattern = (char *)vim_strnsave(line + compl_col, (size_t)compl_length); + + return OK; +} + +/// Get the pattern, column and length for spell completion. +/// Sets the global variables: compl_col, compl_length and compl_pattern. +/// Uses the global variable: spell_bad_len +static int get_spell_compl_info(int startcol, colnr_T curs_col) +{ + if (spell_bad_len > 0) { + assert(spell_bad_len <= INT_MAX); + compl_col = curs_col - (int)spell_bad_len; + } else { + compl_col = spell_word_start(startcol); + } + if (compl_col >= (colnr_T)startcol) { + compl_length = 0; + compl_col = curs_col; + } else { + spell_expand_check_cap(compl_col); + compl_length = (int)curs_col - compl_col; + } + // Need to obtain "line" again, it may have become invalid. + char_u *line = ml_get(curwin->w_cursor.lnum); + compl_pattern = (char *)vim_strnsave(line + compl_col, (size_t)compl_length); + + return OK; +} + +/// Get the completion pattern, column and length. +static int compl_get_info(char_u *line, int startcol, colnr_T curs_col, bool *line_invalid) +{ + if (ctrl_x_mode_normal() + || ((ctrl_x_mode & CTRL_X_WANT_IDENT) + && !thesaurus_func_complete(ctrl_x_mode))) { + return get_normal_compl_info(line, startcol, curs_col); + } else if (ctrl_x_mode_line_or_eval()) { + return get_wholeline_compl_info(line, curs_col); + } else if (ctrl_x_mode_files()) { + return get_filename_compl_info(line, startcol, curs_col); + } else if (ctrl_x_mode == CTRL_X_CMDLINE) { + return get_cmdline_compl_info(line, curs_col); + } else if (ctrl_x_mode_function() || ctrl_x_mode_omni() + || thesaurus_func_complete(ctrl_x_mode)) { + if (get_userdefined_compl_info(curs_col) == FAIL) { + return FAIL; + } + *line_invalid = true; // 'line' may have become invalid + } else if (ctrl_x_mode_spell()) { + if (get_spell_compl_info(startcol, curs_col) == FAIL) { + return FAIL; + } + *line_invalid = true; // 'line' may have become invalid + } else { + internal_error("ins_complete()"); + return FAIL; + } + + return OK; +} + +/// Do Insert mode completion. +/// Called when character "c" was typed, which has a meaning for completion. +/// Returns OK if completion was done, FAIL if something failed. +int ins_complete(int c, bool enable_pum) +{ + char_u *line; + int startcol = 0; // column where searched text starts + colnr_T curs_col; // cursor column + int n; + int save_w_wrow; + int save_w_leftcol; + int insert_match; + const bool save_did_ai = did_ai; + int flags = CP_ORIGINAL_TEXT; + bool line_invalid = false; + + compl_direction = ins_compl_key2dir(c); + insert_match = ins_compl_use_match(c); + + if (!compl_started) { + // First time we hit ^N or ^P (in a row, I mean) + + did_ai = false; + did_si = false; + can_si = false; + can_si_back = false; + if (stop_arrow() == FAIL) { + return FAIL; + } + + line = ml_get(curwin->w_cursor.lnum); + curs_col = curwin->w_cursor.col; + compl_pending = 0; + + // If this same ctrl_x_mode has been interrupted use the text from + // "compl_startpos" to the cursor as a pattern to add a new word + // instead of expand the one before the cursor, in word-wise if + // "compl_startpos" is not in the same line as the cursor then fix it + // (the line has been split because it was longer than 'tw'). if SOL + // is set then skip the previous pattern, a word at the beginning of + // the line has been inserted, we'll look for that -- Acevedo. + if ((compl_cont_status & CONT_INTRPT) == CONT_INTRPT + && compl_cont_mode == ctrl_x_mode) { + // it is a continued search + compl_cont_status &= ~CONT_INTRPT; // remove INTRPT + if (ctrl_x_mode_normal() + || ctrl_x_mode_path_patterns() + || ctrl_x_mode_path_defines()) { + if (compl_startpos.lnum != curwin->w_cursor.lnum) { + // line (probably) wrapped, set compl_startpos to the + // first non_blank in the line, if it is not a wordchar + // include it to get a better pattern, but then we don't + // want the "\\<" prefix, check it below. + compl_col = (colnr_T)getwhitecols(line); + compl_startpos.col = compl_col; + compl_startpos.lnum = curwin->w_cursor.lnum; + compl_cont_status &= ~CONT_SOL; // clear SOL if present + } else { + // S_IPOS was set when we inserted a word that was at the + // beginning of the line, which means that we'll go to SOL + // mode but first we need to redefine compl_startpos + if (compl_cont_status & CONT_S_IPOS) { + compl_cont_status |= CONT_SOL; + compl_startpos.col = (colnr_T)((char_u *)skipwhite((char *)line + + compl_length + + compl_startpos.col) - line); + } + compl_col = compl_startpos.col; + } + compl_length = curwin->w_cursor.col - (int)compl_col; + // IObuff is used to add a "word from the next line" would we + // have enough space? just being paranoid +#define MIN_SPACE 75 + if (compl_length > (IOSIZE - MIN_SPACE)) { + compl_cont_status &= ~CONT_SOL; + compl_length = (IOSIZE - MIN_SPACE); + compl_col = curwin->w_cursor.col - compl_length; + } + compl_cont_status |= CONT_ADDING | CONT_N_ADDS; + if (compl_length < 1) { + compl_cont_status &= CONT_LOCAL; + } + } else if (ctrl_x_mode_line_or_eval()) { + compl_cont_status = CONT_ADDING | CONT_N_ADDS; + } else { + compl_cont_status = 0; + } + } else { + compl_cont_status &= CONT_LOCAL; + } + + if (!(compl_cont_status & CONT_ADDING)) { // normal expansion + compl_cont_mode = ctrl_x_mode; + if (ctrl_x_mode_not_default()) { + // Remove LOCAL if ctrl_x_mode != CTRL_X_NORMAL + compl_cont_status = 0; + } + compl_cont_status |= CONT_N_ADDS; + compl_startpos = curwin->w_cursor; + startcol = (int)curs_col; + compl_col = 0; + } + + // Work out completion pattern and original text -- webb + if (compl_get_info(line, startcol, curs_col, &line_invalid) == FAIL) { + if (ctrl_x_mode_function() || ctrl_x_mode_omni() + || thesaurus_func_complete(ctrl_x_mode)) { + // restore did_ai, so that adding comment leader works + did_ai = save_did_ai; + } + return FAIL; + } + // If "line" was changed while getting completion info get it again. + if (line_invalid) { + line = ml_get(curwin->w_cursor.lnum); + } + + if (compl_cont_status & CONT_ADDING) { + edit_submode_pre = (char_u *)_(" Adding"); + if (ctrl_x_mode_line_or_eval()) { + // Insert a new line, keep indentation but ignore 'comments'. + char_u *old = curbuf->b_p_com; + + curbuf->b_p_com = (char_u *)""; + compl_startpos.lnum = curwin->w_cursor.lnum; + compl_startpos.col = compl_col; + ins_eol('\r'); + curbuf->b_p_com = old; + compl_length = 0; + compl_col = curwin->w_cursor.col; + } + } else { + edit_submode_pre = NULL; + compl_startpos.col = compl_col; + } + + if (compl_cont_status & CONT_LOCAL) { + edit_submode = (char_u *)_(ctrl_x_msgs[CTRL_X_LOCAL_MSG]); + } else { + edit_submode = (char_u *)_(CTRL_X_MSG(ctrl_x_mode)); + } + + // If any of the original typed text has been changed we need to fix + // the redo buffer. + ins_compl_fixRedoBufForLeader(NULL); + + // Always add completion for the original text. + xfree(compl_orig_text); + compl_orig_text = vim_strnsave(line + compl_col, (size_t)compl_length); + if (p_ic) { + flags |= CP_ICASE; + } + if (ins_compl_add(compl_orig_text, -1, NULL, NULL, false, NULL, 0, + flags, false) != OK) { + XFREE_CLEAR(compl_pattern); + XFREE_CLEAR(compl_orig_text); + return FAIL; + } + + // showmode might reset the internal line pointers, so it must + // be called before line = ml_get(), or when this address is no + // longer needed. -- Acevedo. + edit_submode_extra = (char_u *)_("-- Searching..."); + edit_submode_highl = HLF_COUNT; + showmode(); + edit_submode_extra = NULL; + ui_flush(); + } else if (insert_match && stop_arrow() == FAIL) { + return FAIL; + } + + compl_shown_match = compl_curr_match; + compl_shows_dir = compl_direction; + + // Find next match (and following matches). + save_w_wrow = curwin->w_wrow; + save_w_leftcol = curwin->w_leftcol; + n = ins_compl_next(true, ins_compl_key2count(c), insert_match, false); + + if (n > 1) { // all matches have been found + compl_matches = n; + } + compl_curr_match = compl_shown_match; + compl_direction = compl_shows_dir; + + // Eat the ESC that vgetc() returns after a CTRL-C to avoid leaving Insert + // mode. + if (got_int && !global_busy) { + (void)vgetc(); + got_int = false; + } + + // we found no match if the list has only the "compl_orig_text"-entry + if (compl_first_match == compl_first_match->cp_next) { + edit_submode_extra = (compl_cont_status & CONT_ADDING) + && compl_length > 1 + ? (char_u *)_(e_hitend) : (char_u *)_(e_patnotf); + edit_submode_highl = HLF_E; + // remove N_ADDS flag, so next ^X<> won't try to go to ADDING mode, + // because we couldn't expand anything at first place, but if we used + // ^P, ^N, ^X^I or ^X^D we might want to add-expand a single-char-word + // (such as M in M'exico) if not tried already. -- Acevedo + if (compl_length > 1 + || (compl_cont_status & CONT_ADDING) + || (ctrl_x_mode_not_default() + && !ctrl_x_mode_path_patterns() + && !ctrl_x_mode_path_defines())) { + compl_cont_status &= ~CONT_N_ADDS; + } + } + + if (compl_curr_match->cp_flags & CP_CONT_S_IPOS) { + compl_cont_status |= CONT_S_IPOS; + } else { + compl_cont_status &= ~CONT_S_IPOS; + } + + if (edit_submode_extra == NULL) { + if (compl_curr_match->cp_flags & CP_ORIGINAL_TEXT) { + edit_submode_extra = (char_u *)_("Back at original"); + edit_submode_highl = HLF_W; + } else if (compl_cont_status & CONT_S_IPOS) { + edit_submode_extra = (char_u *)_("Word from other line"); + edit_submode_highl = HLF_COUNT; + } else if (compl_curr_match->cp_next == compl_curr_match->cp_prev) { + edit_submode_extra = (char_u *)_("The only match"); + edit_submode_highl = HLF_COUNT; + compl_curr_match->cp_number = 1; + } else { + // Update completion sequence number when needed. + if (compl_curr_match->cp_number == -1) { + ins_compl_update_sequence_numbers(); + } + + // The match should always have a sequence number now, this is + // just a safety check. + if (compl_curr_match->cp_number != -1) { + // Space for 10 text chars. + 2x10-digit no.s = 31. + // Translations may need more than twice that. + static char_u match_ref[81]; + + if (compl_matches > 0) { + vim_snprintf((char *)match_ref, sizeof(match_ref), + _("match %d of %d"), + compl_curr_match->cp_number, compl_matches); + } else { + vim_snprintf((char *)match_ref, sizeof(match_ref), + _("match %d"), + compl_curr_match->cp_number); + } + edit_submode_extra = match_ref; + edit_submode_highl = HLF_R; + if (dollar_vcol >= 0) { + curs_columns(curwin, false); + } + } + } + } + + // Show a message about what (completion) mode we're in. + showmode(); + if (!shortmess(SHM_COMPLETIONMENU)) { + if (edit_submode_extra != NULL) { + if (!p_smd) { + msg_hist_off = true; + msg_attr((const char *)edit_submode_extra, + (edit_submode_highl < HLF_COUNT + ? HL_ATTR(edit_submode_highl) : 0)); + msg_hist_off = false; + } + } else { + msg_clr_cmdline(); // necessary for "noshowmode" + } + } + + // Show the popup menu, unless we got interrupted. + if (enable_pum && !compl_interrupted) { + show_pum(save_w_wrow, save_w_leftcol); + } + compl_was_interrupted = compl_interrupted; + compl_interrupted = false; + + return OK; +} + +static void show_pum(int prev_w_wrow, int prev_w_leftcol) +{ + // RedrawingDisabled may be set when invoked through complete(). + int n = RedrawingDisabled; + RedrawingDisabled = 0; + + // If the cursor moved or the display scrolled we need to remove the pum + // first. + setcursor(); + if (prev_w_wrow != curwin->w_wrow || prev_w_leftcol != curwin->w_leftcol) { + ins_compl_del_pum(); + } + + ins_compl_show_pum(); + setcursor(); + RedrawingDisabled = n; +} + +// Looks in the first "len" chars. of "src" for search-metachars. +// If dest is not NULL the chars. are copied there quoting (with +// a backslash) the metachars, and dest would be NUL terminated. +// Returns the length (needed) of dest +static unsigned quote_meta(char_u *dest, char_u *src, int len) +{ + unsigned m = (unsigned)len + 1; // one extra for the NUL + + for (; --len >= 0; src++) { + switch (*src) { + case '.': + case '*': + case '[': + if (ctrl_x_mode_dictionary() || ctrl_x_mode_thesaurus()) { + break; + } + FALLTHROUGH; + case '~': + if (!p_magic) { // quote these only if magic is set + break; + } + FALLTHROUGH; + case '\\': + if (ctrl_x_mode_dictionary() || ctrl_x_mode_thesaurus()) { + break; + } + FALLTHROUGH; + case '^': // currently it's not needed. + case '$': + m++; + if (dest != NULL) { + *dest++ = '\\'; + } + break; + } + if (dest != NULL) { + *dest++ = *src; + } + // Copy remaining bytes of a multibyte character. + const int mb_len = utfc_ptr2len((char *)src) - 1; + if (mb_len > 0 && len >= mb_len) { + for (int i = 0; i < mb_len; i++) { + len--; + src++; + if (dest != NULL) { + *dest++ = *src; + } + } + } + } + if (dest != NULL) { + *dest = NUL; + } + + return m; +} + +#if defined(EXITFREE) +void free_insexpand_stuff(void) +{ + XFREE_CLEAR(compl_orig_text); +} +#endif + +/// Called when starting CTRL_X_SPELL mode: Move backwards to a previous badly +/// spelled word, if there is one. +static void spell_back_to_badword(void) +{ + pos_T tpos = curwin->w_cursor; + spell_bad_len = spell_move_to(curwin, BACKWARD, true, true, NULL); + if (curwin->w_cursor.col != tpos.col) { + start_arrow(&tpos); + } +} diff --git a/src/nvim/insexpand.h b/src/nvim/insexpand.h new file mode 100644 index 0000000000..8e183455ca --- /dev/null +++ b/src/nvim/insexpand.h @@ -0,0 +1,17 @@ +#ifndef NVIM_INSEXPAND_H +#define NVIM_INSEXPAND_H + +#include "nvim/vim.h" + +/// state for pum_ext_select_item. +EXTERN struct { + bool active; + int item; + bool insert; + bool finish; +} pum_want; + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "insexpand.h.generated.h" +#endif +#endif // NVIM_INSEXPAND_H diff --git a/src/nvim/keycodes.c b/src/nvim/keycodes.c index cd3c7316bf..9899f10788 100644 --- a/src/nvim/keycodes.c +++ b/src/nvim/keycodes.c @@ -8,7 +8,7 @@ #include "nvim/ascii.h" #include "nvim/charset.h" #include "nvim/edit.h" -#include "nvim/eval.h" +#include "nvim/eval/vars.h" #include "nvim/keycodes.h" #include "nvim/memory.h" #include "nvim/message.h" @@ -392,7 +392,7 @@ static struct mousetable { { (int)KE_X2DRAG, MOUSE_X2, false, true }, { (int)KE_X2RELEASE, MOUSE_X2, false, false }, // DRAG without CLICK - { (int)K_MOUSEMOVE, MOUSE_RELEASE, false, true }, + { (int)KE_MOUSEMOVE, MOUSE_RELEASE, false, true }, // RELEASE without CLICK { (int)KE_IGNORE, MOUSE_RELEASE, false, false }, { 0, 0, 0, 0 }, diff --git a/src/nvim/keycodes.h b/src/nvim/keycodes.h index 67ec092f60..943c127325 100644 --- a/src/nvim/keycodes.h +++ b/src/nvim/keycodes.h @@ -7,10 +7,8 @@ // // Any special key code sequences are replaced by these codes. -// -// For MS-DOS some keys produce codes larger than 0xff. They are split into two -// chars, the first one is K_NUL. -// +/// For MS-DOS some keys produce codes larger than 0xff. They are split into two +/// chars, the first one is K_NUL. #define K_NUL (0xce) // for MS-DOS: special key follows /// K_SPECIAL is the first byte of a special key code and is always followed by @@ -59,13 +57,13 @@ #define KS_SELECT 245 #define K_SELECT_STRING (char_u *)"\200\365X" -// Used a termcap entry that produces a normal character. +/// Used a termcap entry that produces a normal character. #define KS_KEY 242 -// Used for click in a tab pages label. +/// Used for click in a tab pages label. #define KS_TABLINE 240 -// Used for menu in a tab pages line. +/// Used for menu in a tab pages line. #define KS_TABMENU 239 /// Filler used after KS_SPECIAL and others @@ -89,18 +87,19 @@ #define TO_SPECIAL(a, b) ((a) == KS_SPECIAL ? K_SPECIAL : (a) == \ KS_ZERO ? K_ZERO : TERMCAP2KEY(a, b)) -// Codes for keys that do not have a termcap name. -// The numbers are fixed to make sure that recorded key sequences remain valid. -// Add new entries at the end, not halfway. -// -// K_SPECIAL KS_EXTRA KE_xxx -// -// Entries must be in the range 0x02-0x7f (see comment at K_SPECIAL). +/// Codes for keys that do not have a termcap name. +/// The numbers are fixed to make sure that recorded key sequences remain valid. +/// Add new entries at the end, not halfway. +/// +/// K_SPECIAL KS_EXTRA KE_xxx +/// +/// Entries must be in the range 0x02-0x7f (see comment at K_SPECIAL). enum key_extra { KE_S_UP = 4, // shift-up - KE_S_DOWN = 5, // shift-down + KE_S_DOWN = 5, // shift-down - KE_S_F1 = 6, // shifted function keys + // shifted function keys + KE_S_F1 = 6, KE_S_F2 = 7, KE_S_F3 = 8, KE_S_F4 = 9, @@ -141,7 +140,7 @@ enum key_extra { KE_S_F36 = 41, KE_S_F37 = 42, - KE_MOUSE = 43, // mouse event start + KE_MOUSE = 43, // mouse event start // Symbols for pseudo keys which are translated from the real key symbols // above. @@ -153,14 +152,14 @@ enum key_extra { KE_MIDDLERELEASE = 49, // Middle mouse button release KE_RIGHTMOUSE = 50, // Right mouse button click KE_RIGHTDRAG = 51, // Drag with right mouse button down - KE_RIGHTRELEASE = 52, // Right mouse button release + KE_RIGHTRELEASE = 52, // Right mouse button release - KE_IGNORE = 53, // Ignored mouse drag/release + KE_IGNORE = 53, // Ignored mouse drag/release KE_TAB = 54, // unshifted TAB key - KE_S_TAB_OLD = 55, // shifted TAB key (no longer used) + KE_S_TAB_OLD = 55, // shifted TAB key (no longer used) - // , KE_SNIFF_UNUSED = 56 // obsolete + // KE_SNIFF_UNUSED = 56, // obsolete KE_XF1 = 57, // extra vt100 function keys for xterm KE_XF2 = 58, KE_XF3 = 59, @@ -175,7 +174,7 @@ enum key_extra { KE_XRIGHT = 68, KE_LEFTMOUSE_NM = 69, // non-mappable Left mouse button click - KE_LEFTRELEASE_NM = 70, // non-mappable left mouse button release + KE_LEFTRELEASE_NM = 70, // non-mappable left mouse button release KE_S_XF1 = 71, // vt100 shifted function keys for xterm KE_S_XF2 = 72, @@ -188,20 +187,20 @@ enum key_extra { KE_MOUSEDOWN = 75, // scroll wheel pseudo-button Down KE_MOUSEUP = 76, // scroll wheel pseudo-button Up KE_MOUSELEFT = 77, // scroll wheel pseudo-button Left - KE_MOUSERIGHT = 78, // scroll wheel pseudo-button Right + KE_MOUSERIGHT = 78, // scroll wheel pseudo-button Right KE_KINS = 79, // keypad Insert key - KE_KDEL = 80, // keypad Delete key + KE_KDEL = 80, // keypad Delete key // KE_CSI = 81, // Nvim doesn't need escaping CSI KE_SNR = 82, // <SNR> KE_PLUG = 83, // <Plug> - KE_CMDWIN = 84, // open command-line window from Command-line Mode + KE_CMDWIN = 84, // open command-line window from Command-line Mode KE_C_LEFT = 85, // control-left KE_C_RIGHT = 86, // control-right KE_C_HOME = 87, // control-home - KE_C_END = 88, // control-end + KE_C_END = 88, // control-end KE_X1MOUSE = 89, // X1/X2 mouse-buttons KE_X1DRAG = 90, @@ -210,16 +209,16 @@ enum key_extra { KE_X2DRAG = 93, KE_X2RELEASE = 94, - KE_DROP = 95, // DnD data is available - // , KE_CURSORHOLD = 96 // CursorHold event - KE_NOP = 97, // no-op: does nothing - // , KE_FOCUSGAINED = 98 // focus gained - // , KE_FOCUSLOST = 99 // focus lost - KE_MOUSEMOVE = 100, // mouse moved with no button down - // , KE_CANCEL = 101 // return from vgetc + KE_DROP = 95, // DnD data is available + // KE_CURSORHOLD = 96, // CursorHold event + KE_NOP = 97, // no-op: does nothing + // KE_FOCUSGAINED = 98, // focus gained + // KE_FOCUSLOST = 99, // focus lost + KE_MOUSEMOVE = 100, // mouse moved with no button down + // KE_CANCEL = 101, // return from vgetc() KE_EVENT = 102, // event - KE_LUA = 103, // lua special key - KE_COMMAND = 104, // <Cmd> special key + KE_LUA = 103, // Lua special key + KE_COMMAND = 104, // <Cmd> special key }; // the three byte codes are replaced with the following int when using vgetc() @@ -259,7 +258,8 @@ enum key_extra { #define K_XLEFT TERMCAP2KEY(KS_EXTRA, KE_XLEFT) #define K_XRIGHT TERMCAP2KEY(KS_EXTRA, KE_XRIGHT) -#define K_F1 TERMCAP2KEY('k', '1') // function keys +// function keys +#define K_F1 TERMCAP2KEY('k', '1') #define K_F2 TERMCAP2KEY('k', '2') #define K_F3 TERMCAP2KEY('k', '3') #define K_F4 TERMCAP2KEY('k', '4') @@ -490,13 +490,13 @@ enum key_extra { /// Current longest is <M-C-S-T-D-A-4-ScrollWheelRight> (length includes '<' and '>'). #define MAX_KEY_NAME_LEN 32 -// Maximum length of a special key event as tokens. This includes modifiers. -// The longest event is something like <M-C-S-T-4-LeftDrag> which would be the -// following string of tokens: -// -// <K_SPECIAL> <KS_MODIFIER> bitmask <K_SPECIAL> <KS_EXTRA> <KE_LEFTDRAG>. -// -// This is a total of 6 tokens, and is currently the longest one possible. +/// Maximum length of a special key event as tokens. This includes modifiers. +/// The longest event is something like <M-C-S-T-4-LeftDrag> which would be the +/// following string of tokens: +/// +/// <K_SPECIAL> <KS_MODIFIER> bitmask <K_SPECIAL> <KS_EXTRA> <KE_LEFTDRAG>. +/// +/// This is a total of 6 tokens, and is currently the longest one possible. #define MAX_KEY_CODE_LEN 6 #define FLAG_CPO_BSLASH 0x01 @@ -504,7 +504,7 @@ enum key_extra { ? 0 \ : FLAG_CPO_BSLASH) -// Flags for replace_termcodes() +/// Flags for replace_termcodes() enum { REPTERM_FROM_PART = 1, REPTERM_DO_LT = 2, @@ -512,7 +512,7 @@ enum { REPTERM_NO_SIMPLIFY = 8, }; -// Flags for find_special_key() +/// Flags for find_special_key() enum { FSK_KEYCODE = 0x01, ///< prefer key code, e.g. K_DEL in place of DEL FSK_KEEP_X_KEY = 0x02, ///< don’t translate xHome to Home key diff --git a/src/nvim/log.c b/src/nvim/log.c index 57c7c4758b..99b17a612b 100644 --- a/src/nvim/log.c +++ b/src/nvim/log.c @@ -310,7 +310,7 @@ static bool v_do_log_to_file(FILE *log_file, int log_level, const char *context, const char *parent = path_tail(os_getenv(ENV_NVIM)); // Servername. Empty until starting=false. const char *serv = path_tail(get_vim_var_str(VV_SEND_SERVER)); - if (parent && parent[0] != NUL) { + if (parent[0] != NUL) { snprintf(name, sizeof(name), "%s/c", parent); // "/c" indicates child. } else if (serv[0] != NUL) { snprintf(name, sizeof(name), "%s", serv); diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index ad03ebd1ed..17157ccdc2 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -15,6 +15,7 @@ #include "nvim/buffer_defs.h" #include "nvim/change.h" #include "nvim/cursor.h" +#include "nvim/eval/funcs.h" #include "nvim/eval/typval.h" #include "nvim/eval/userfunc.h" #include "nvim/event/loop.h" @@ -416,9 +417,9 @@ static int nlua_wait(lua_State *lstate) LOOP_PROCESS_EVENTS_UNTIL(&main_loop, loop_events, (int)timeout, - is_function ? nlua_wait_condition(lstate, - &pcall_status, - &callback_result) : false || got_int); + got_int || (is_function ? nlua_wait_condition(lstate, + &pcall_status, + &callback_result) : false)); // Stop dummy timer time_watcher_stop(tw); @@ -1673,7 +1674,7 @@ static void nlua_add_treesitter(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL lua_setfield(lstate, -2, "_ts_get_minimum_language_version"); } -int nlua_expand_pat(expand_T *xp, char_u *pat, int *num_results, char_u ***results) +int nlua_expand_pat(expand_T *xp, char_u *pat, int *num_results, char ***results) { lua_State *const lstate = global_lstate; int ret = OK; @@ -1685,7 +1686,7 @@ int nlua_expand_pat(expand_T *xp, char_u *pat, int *num_results, char_u ***resul lua_getfield(lstate, -1, "_expand_pat"); luaL_checktype(lstate, -1, LUA_TFUNCTION); - // [ vim, vim._on_key, buf ] + // [ vim, vim._expand_pat, buf ] lua_pushlstring(lstate, (const char *)pat, STRLEN(pat)); if (nlua_pcall(lstate, 1, 2) != 0) { @@ -1838,7 +1839,7 @@ void nlua_execute_on_key(int c) // [ vim ] lua_getglobal(lstate, "vim"); - // [ vim, vim._on_key] + // [ vim, vim._on_key ] lua_getfield(lstate, -1, "_on_key"); luaL_checktype(lstate, -1, LUA_TFUNCTION); diff --git a/src/nvim/lua/stdlib.c b/src/nvim/lua/stdlib.c index 8fde85b163..6ba0056f48 100644 --- a/src/nvim/lua/stdlib.c +++ b/src/nvim/lua/stdlib.c @@ -232,7 +232,7 @@ static int nlua_str_utf_start(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL if (offset < 0 || offset > (intptr_t)s1_len) { return luaL_error(lstate, "index out of range"); } - int head_offset = mb_head_off((char_u *)s1, (char_u *)s1 + offset - 1); + int head_offset = utf_cp_head_off((char_u *)s1, (char_u *)s1 + offset - 1); lua_pushinteger(lstate, head_offset); return 1; } @@ -252,7 +252,7 @@ static int nlua_str_utf_end(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL if (offset < 0 || offset > (intptr_t)s1_len) { return luaL_error(lstate, "index out of range"); } - int tail_offset = mb_tail_off(s1, s1 + offset - 1); + int tail_offset = utf_cp_tail_off(s1, s1 + offset - 1); lua_pushinteger(lstate, tail_offset); return 1; } diff --git a/src/nvim/lua/treesitter.c b/src/nvim/lua/treesitter.c index b96193d199..f0d847e352 100644 --- a/src/nvim/lua/treesitter.c +++ b/src/nvim/lua/treesitter.c @@ -11,6 +11,7 @@ #include <lua.h> #include <lualib.h> #include <stdbool.h> +#include <stdint.h> #include <stdlib.h> #include <string.h> @@ -208,31 +209,32 @@ int tslua_inspect_lang(lua_State *L) lua_createtable(L, 0, 2); // [retval] - size_t nsymbols = (size_t)ts_language_symbol_count(lang); + uint32_t nsymbols = ts_language_symbol_count(lang); + assert(nsymbols < INT_MAX); - lua_createtable(L, nsymbols - 1, 1); // [retval, symbols] - for (size_t i = 0; i < nsymbols; i++) { - TSSymbolType t = ts_language_symbol_type(lang, i); + lua_createtable(L, (int)(nsymbols - 1), 1); // [retval, symbols] + for (uint32_t i = 0; i < nsymbols; i++) { + TSSymbolType t = ts_language_symbol_type(lang, (TSSymbol)i); if (t == TSSymbolTypeAuxiliary) { // not used by the API continue; } lua_createtable(L, 2, 0); // [retval, symbols, elem] - lua_pushstring(L, ts_language_symbol_name(lang, i)); + lua_pushstring(L, ts_language_symbol_name(lang, (TSSymbol)i)); lua_rawseti(L, -2, 1); lua_pushboolean(L, t == TSSymbolTypeRegular); lua_rawseti(L, -2, 2); // [retval, symbols, elem] - lua_rawseti(L, -2, i); // [retval, symbols] + lua_rawseti(L, -2, (int)i); // [retval, symbols] } lua_setfield(L, -2, "symbols"); // [retval] - size_t nfields = (size_t)ts_language_field_count(lang); - lua_createtable(L, nfields, 1); // [retval, fields] + uint32_t nfields = ts_language_field_count(lang); + lua_createtable(L, (int)nfields, 1); // [retval, fields] // Field IDs go from 1 to nfields inclusive (extra index 0 maps to NULL) - for (size_t i = 1; i <= nfields; i++) { - lua_pushstring(L, ts_language_field_name_for_id(lang, i)); - lua_rawseti(L, -2, i); // [retval, fields] + for (uint32_t i = 1; i <= nfields; i++) { + lua_pushstring(L, ts_language_field_name_for_id(lang, (TSFieldId)i)); + lua_rawseti(L, -2, (int)i); // [retval, fields] } lua_setfield(L, -2, "fields"); // [retval] @@ -300,7 +302,7 @@ static const char *input_cb(void *payload, uint32_t byte_index, TSPoint position *bytes_read = 0; return ""; } - char_u *line = ml_get_buf(bp, position.row + 1, false); + char *line = (char *)ml_get_buf(bp, (linenr_T)position.row + 1, false); size_t len = STRLEN(line); if (position.column > len) { *bytes_read = 0; @@ -322,9 +324,9 @@ static const char *input_cb(void *payload, uint32_t byte_index, TSPoint position #undef BUFSIZE } -static void push_ranges(lua_State *L, const TSRange *ranges, const unsigned int length) +static void push_ranges(lua_State *L, const TSRange *ranges, const size_t length) { - lua_createtable(L, length, 0); + lua_createtable(L, (int)length, 0); for (size_t i = 0; i < length; i++) { lua_createtable(L, 4, 0); lua_pushinteger(L, ranges[i].start_point.row); @@ -336,7 +338,7 @@ static void push_ranges(lua_State *L, const TSRange *ranges, const unsigned int lua_pushinteger(L, ranges[i].end_point.column); lua_rawseti(L, -2, 4); - lua_rawseti(L, -2, i + 1); + lua_rawseti(L, -2, (int)(i + 1)); } } @@ -365,15 +367,19 @@ static int parser_parse(lua_State *L) switch (lua_type(L, 3)) { case LUA_TSTRING: str = lua_tolstring(L, 3, &len); - new_tree = ts_parser_parse_string(*p, old_tree, str, len); + new_tree = ts_parser_parse_string(*p, old_tree, str, (uint32_t)len); break; case LUA_TNUMBER: bufnr = lua_tointeger(L, 3); - buf = handle_get_buffer(bufnr); + buf = handle_get_buffer((handle_T)bufnr); if (!buf) { - return luaL_error(L, "invalid buffer handle: %d", bufnr); +#define BUFSIZE 256 + char ebuf[BUFSIZE] = { 0 }; + vim_snprintf(ebuf, BUFSIZE, "invalid buffer handle: %ld", bufnr); + return luaL_argerror(L, 3, ebuf); +#undef BUFSIZE } input = (TSInput){ (void *)buf, input_cb, TSInputEncodingUTF8 }; @@ -382,7 +388,7 @@ static int parser_parse(lua_State *L) break; default: - return luaL_error(L, "invalid argument to parser:parse()"); + return luaL_argerror(L, 3, "expected either string or buffer handle"); } // Sometimes parsing fails (timeout, or wrong parser ABI) @@ -429,12 +435,12 @@ static int tree_edit(lua_State *L) return 0; } - long start_byte = lua_tointeger(L, 2); - long old_end_byte = lua_tointeger(L, 3); - long new_end_byte = lua_tointeger(L, 4); - TSPoint start_point = { lua_tointeger(L, 5), lua_tointeger(L, 6) }; - TSPoint old_end_point = { lua_tointeger(L, 7), lua_tointeger(L, 8) }; - TSPoint new_end_point = { lua_tointeger(L, 9), lua_tointeger(L, 10) }; + uint32_t start_byte = (uint32_t)luaL_checkint(L, 2); + uint32_t old_end_byte = (uint32_t)luaL_checkint(L, 3); + uint32_t new_end_byte = (uint32_t)luaL_checkint(L, 4); + TSPoint start_point = { (uint32_t)luaL_checkint(L, 5), (uint32_t)luaL_checkint(L, 6) }; + TSPoint old_end_point = { (uint32_t)luaL_checkint(L, 7), (uint32_t)luaL_checkint(L, 8) }; + TSPoint new_end_point = { (uint32_t)luaL_checkint(L, 9), (uint32_t)luaL_checkint(L, 10) }; TSInputEdit edit = { start_byte, old_end_byte, new_end_byte, start_point, old_end_point, new_end_point }; @@ -456,29 +462,28 @@ static void range_from_lua(lua_State *L, TSRange *range) goto error; } - uint32_t start_row, start_col, start_byte, end_row, end_col, end_byte; lua_rawgeti(L, -1, 1); // [ range, start_row] - start_row = luaL_checkinteger(L, -1); + uint32_t start_row = (uint32_t)luaL_checkinteger(L, -1); lua_pop(L, 1); lua_rawgeti(L, -1, 2); // [ range, start_col] - start_col = luaL_checkinteger(L, -1); + uint32_t start_col = (uint32_t)luaL_checkinteger(L, -1); lua_pop(L, 1); lua_rawgeti(L, -1, 3); // [ range, start_byte] - start_byte = luaL_checkinteger(L, -1); + uint32_t start_byte = (uint32_t)luaL_checkinteger(L, -1); lua_pop(L, 1); lua_rawgeti(L, -1, 4); // [ range, end_row] - end_row = luaL_checkinteger(L, -1); + uint32_t end_row = (uint32_t)luaL_checkinteger(L, -1); lua_pop(L, 1); lua_rawgeti(L, -1, 5); // [ range, end_col] - end_col = luaL_checkinteger(L, -1); + uint32_t end_col = (uint32_t)luaL_checkinteger(L, -1); lua_pop(L, 1); lua_rawgeti(L, -1, 6); // [ range, end_byte] - end_byte = luaL_checkinteger(L, -1); + uint32_t end_byte = (uint32_t)luaL_checkinteger(L, -1); lua_pop(L, 1); // [ range ] *range = (TSRange) { @@ -531,13 +536,13 @@ static int parser_set_ranges(lua_State *L) // [ parser, ranges ] for (size_t index = 0; index < tbl_len; index++) { - lua_rawgeti(L, 2, index + 1); // [ parser, ranges, range ] + lua_rawgeti(L, 2, (int)index + 1); // [ parser, ranges, range ] range_from_lua(L, ranges + index); lua_pop(L, 1); } // This memcpies ranges, thus we can free it afterwards - ts_parser_set_included_ranges(*p, ranges, tbl_len); + ts_parser_set_included_ranges(*p, ranges, (uint32_t)tbl_len); xfree(ranges); return 0; @@ -550,7 +555,7 @@ static int parser_get_ranges(lua_State *L) return 0; } - unsigned int len; + uint32_t len; const TSRange *ranges = ts_parser_included_ranges(*p, &len); push_ranges(L, ranges, len); @@ -793,7 +798,7 @@ static int node_field(lua_State *L) TSTreeCursor cursor = ts_tree_cursor_new(node); lua_newtable(L); // [table] - unsigned int curr_index = 0; + size_t curr_index = 0; if (ts_tree_cursor_goto_first_child(&cursor)) { do { @@ -801,7 +806,7 @@ static int node_field(lua_State *L) if (current_field != NULL && !STRCMP(field_name, current_field)) { push_node(L, ts_tree_cursor_current_node(&cursor), 1); // [table, node] - lua_rawseti(L, -2, ++curr_index); + lua_rawseti(L, -2, (int)++curr_index); } } while (ts_tree_cursor_goto_next_sibling(&cursor)); } @@ -1036,7 +1041,7 @@ static void set_match(lua_State *L, TSQueryMatch *match, int nodeidx) { for (int i = 0; i < match->capture_count; i++) { push_node(L, match->captures[i].node, nodeidx); - lua_rawseti(L, -2, match->captures[i].index + 1); + lua_rawseti(L, -2, (int)match->captures[i].index + 1); } } @@ -1049,7 +1054,7 @@ static int query_next_match(lua_State *L) TSQueryMatch match; if (ts_query_cursor_next_match(cursor, &match)) { lua_pushinteger(L, match.pattern_index + 1); // [index] - lua_createtable(L, ts_query_capture_count(query), 2); // [index, match] + lua_createtable(L, (int)ts_query_capture_count(query), 2); // [index, match] set_match(L, &match, lua_upvalueindex(2)); return 2; } @@ -1070,7 +1075,7 @@ static int query_next_capture(lua_State *L) bool active = lua_toboolean(L, -1); lua_pop(L, 1); if (!active) { - ts_query_cursor_remove_match(cursor, ud->predicated_match); + ts_query_cursor_remove_match(cursor, (uint32_t)ud->predicated_match); } ud->predicated_match = -1; } @@ -1080,6 +1085,7 @@ static int query_next_capture(lua_State *L) if (ts_query_cursor_next_capture(cursor, &match, &capture_index)) { TSQueryCapture capture = match.captures[capture_index]; + // TODO(vigoux): handle capture quantifiers here lua_pushinteger(L, capture.index + 1); // [index] push_node(L, capture.node, lua_upvalueindex(2)); // [index, node] @@ -1088,7 +1094,7 @@ static int query_next_capture(lua_State *L) ts_query_predicates_for_pattern(query, match.pattern_index, &n_pred); if (n_pred > 0 && (ud->max_match_id < (int)match.id)) { - ud->max_match_id = match.id; + ud->max_match_id = (int)match.id; lua_pushvalue(L, lua_upvalueindex(4)); // [index, node, match] set_match(L, &match, lua_upvalueindex(2)); @@ -1096,7 +1102,7 @@ static int query_next_capture(lua_State *L) lua_setfield(L, -2, "pattern"); if (match.capture_count > 1) { - ud->predicated_match = match.id; + ud->predicated_match = (int)match.id; lua_pushboolean(L, false); lua_setfield(L, -2, "active"); } @@ -1131,10 +1137,9 @@ static int node_rawquery(lua_State *L) bool captures = lua_toboolean(L, 3); if (lua_gettop(L) >= 4) { - int start = luaL_checkinteger(L, 4); - int end = lua_gettop(L) >= 5 ? luaL_checkinteger(L, 5) : MAXLNUM; - ts_query_cursor_set_point_range(cursor, - (TSPoint){ start, 0 }, (TSPoint){ end, 0 }); + uint32_t start = (uint32_t)luaL_checkinteger(L, 4); + uint32_t end = lua_gettop(L) >= 5 ? (uint32_t)luaL_checkinteger(L, 5) : MAXLNUM; + ts_query_cursor_set_point_range(cursor, (TSPoint){ start, 0 }, (TSPoint){ end, 0 }); } TSLua_cursor *ud = lua_newuserdata(L, sizeof(*ud)); // [udata] @@ -1151,7 +1156,7 @@ static int node_rawquery(lua_State *L) if (captures) { // placeholder for match state - lua_createtable(L, ts_query_capture_count(query), 2); // [u, n, q, match] + lua_createtable(L, (int)ts_query_capture_count(query), 2); // [u, n, q, match] lua_pushcclosure(L, query_next_capture, 4); // [closure] } else { lua_pushcclosure(L, query_next_match, 3); // [closure] @@ -1187,7 +1192,7 @@ int tslua_parse_query(lua_State *L) uint32_t error_offset; TSQueryError error_type; - TSQuery *query = ts_query_new(lang, src, len, &error_offset, &error_type); + TSQuery *query = ts_query_new(lang, src, (uint32_t)len, &error_offset, &error_type); if (!query) { return luaL_error(L, "query: %s at position %d", @@ -1212,6 +1217,8 @@ static const char *query_err_string(TSQueryError err) return "invalid field"; case TSQueryErrorCapture: return "invalid capture"; + case TSQueryErrorStructure: + return "invalid structure"; default: return "error"; } @@ -1249,15 +1256,14 @@ static int query_inspect(lua_State *L) uint32_t n_pat = ts_query_pattern_count(query); lua_createtable(L, 0, 2); // [retval] - lua_createtable(L, n_pat, 1); // [retval, patterns] + lua_createtable(L, (int)n_pat, 1); // [retval, patterns] for (size_t i = 0; i < n_pat; i++) { uint32_t len; - const TSQueryPredicateStep *step = ts_query_predicates_for_pattern(query, - i, &len); + const TSQueryPredicateStep *step = ts_query_predicates_for_pattern(query, (uint32_t)i, &len); if (len == 0) { continue; } - lua_createtable(L, len/4, 1); // [retval, patterns, pat] + lua_createtable(L, (int)len/4, 1); // [retval, patterns, pat] lua_createtable(L, 3, 0); // [retval, patterns, pat, pred] int nextpred = 1; int nextitem = 1; @@ -1283,17 +1289,17 @@ static int query_inspect(lua_State *L) } // last predicate should have ended with TypeDone lua_pop(L, 1); // [retval, patters, pat] - lua_rawseti(L, -2, i + 1); // [retval, patterns] + lua_rawseti(L, -2, (int)i + 1); // [retval, patterns] } lua_setfield(L, -2, "patterns"); // [retval] uint32_t n_captures = ts_query_capture_count(query); - lua_createtable(L, n_captures, 0); // [retval, captures] + lua_createtable(L, (int)n_captures, 0); // [retval, captures] for (size_t i = 0; i < n_captures; i++) { uint32_t strlen; - const char *str = ts_query_capture_name_for_id(query, i, &strlen); + const char *str = ts_query_capture_name_for_id(query, (uint32_t)i, &strlen); lua_pushlstring(L, str, strlen); // [retval, captures, capture] - lua_rawseti(L, -2, i + 1); + lua_rawseti(L, -2, (int)i + 1); } lua_setfield(L, -2, "captures"); // [retval] diff --git a/src/nvim/main.c b/src/nvim/main.c index b06b9630e2..494ff0b4af 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -28,6 +28,7 @@ #include "nvim/highlight_group.h" #include "nvim/iconv.h" #include "nvim/if_cscope.h" +#include "nvim/insexpand.h" #include "nvim/lua/executor.h" #include "nvim/main.h" #include "nvim/mapping.h" @@ -202,7 +203,6 @@ void early_init(mparm_T *paramp) set_lang_var(); // set v:lang and v:ctype init_signs(); - ui_comp_syn_init(); } #ifdef MAKE_LIB @@ -320,6 +320,7 @@ int main(int argc, char **argv) no_wait_return = true; init_highlight(true, false); // Default highlight groups. + ui_comp_syn_init(); TIME_MSG("init highlight"); // Set the break level after the terminal is initialized. diff --git a/src/nvim/mapping.c b/src/nvim/mapping.c index 5a11ac686e..1797bb0365 100644 --- a/src/nvim/mapping.c +++ b/src/nvim/mapping.c @@ -235,7 +235,7 @@ static void showmap(mapblock_T *mp, bool local) /// @param[in] orig_lhs Original mapping LHS, with characters to replace. /// @param[in] orig_lhs_len `strlen` of orig_lhs. /// @param[in] orig_rhs Original mapping RHS, with characters to replace. -/// @param[in] rhs_lua Lua reference for Lua maps. +/// @param[in] rhs_lua Lua reference for Lua mappings. /// @param[in] orig_rhs_len `strlen` of orig_rhs. /// @param[in] cpo_flags See param docs for @ref replace_termcodes. /// @param[out] mapargs MapArguments struct holding the replaced strings. @@ -428,6 +428,66 @@ static int str_to_mapargs(const char_u *strargs, bool is_unmap, MapArguments *ma return 0; } +/// @param args "rhs", "rhs_lua", "orig_rhs", "expr", "silent", "nowait", "replace_keycodes" and +/// and "desc" fields are used. +/// "rhs", "rhs_lua", "orig_rhs" fields are cleared if "simplified" is false. +/// @param sid -1 to use current_sctx +static void map_add(buf_T *buf, mapblock_T **map_table, mapblock_T **abbr_table, const char_u *keys, + MapArguments *args, int noremap, int mode, bool is_abbr, scid_T sid, + linenr_T lnum, bool simplified) +{ + mapblock_T *mp = xcalloc(1, sizeof(mapblock_T)); + + // If CTRL-C has been mapped, don't always use it for Interrupting. + if (*keys == Ctrl_C) { + if (map_table == buf->b_maphash) { + buf->b_mapped_ctrl_c |= mode; + } else { + mapped_ctrl_c |= mode; + } + } + + mp->m_keys = vim_strsave(keys); + mp->m_str = args->rhs; + mp->m_orig_str = args->orig_rhs; + mp->m_luaref = args->rhs_lua; + if (!simplified) { + args->rhs = NULL; + args->orig_rhs = NULL; + args->rhs_lua = LUA_NOREF; + } + mp->m_keylen = (int)STRLEN(mp->m_keys); + mp->m_noremap = noremap; + mp->m_nowait = args->nowait; + mp->m_silent = args->silent; + mp->m_mode = mode; + mp->m_simplified = simplified; + mp->m_expr = args->expr; + mp->m_replace_keycodes = args->replace_keycodes; + if (sid >= 0) { + mp->m_script_ctx.sc_sid = sid; + mp->m_script_ctx.sc_lnum = lnum; + } else { + mp->m_script_ctx = current_sctx; + mp->m_script_ctx.sc_lnum += sourcing_lnum; + nlua_set_sctx(&mp->m_script_ctx); + } + mp->m_desc = NULL; + if (args->desc != NULL) { + mp->m_desc = xstrdup(args->desc); + } + + // add the new entry in front of the abbrlist or maphash[] list + if (is_abbr) { + mp->m_next = *abbr_table; + *abbr_table = mp; + } else { + const int n = MAP_HASH(mp->m_mode, mp->m_keys[0]); + mp->m_next = map_table[n]; + map_table[n] = mp; + } +} + /// Sets or removes a mapping or abbreviation in buffer `buf`. /// /// @param maptype @see do_map @@ -452,7 +512,7 @@ static int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev, abbr_table = &first_abbr; // For ":noremap" don't remap, otherwise do remap. - if (maptype == 2) { + if (maptype == MAPTYPE_NOREMAP) { noremap = REMAP_NONE; } else { noremap = REMAP_YES; @@ -470,10 +530,10 @@ static int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev, const bool has_lhs = (args->lhs[0] != NUL); const bool has_rhs = args->rhs_lua != LUA_NOREF || (args->rhs[0] != NUL) || args->rhs_is_noop; - const bool do_print = !has_lhs || (maptype != 1 && !has_rhs); + const bool do_print = !has_lhs || (maptype != MAPTYPE_UNMAP && !has_rhs); // check for :unmap without argument - if (maptype == 1 && !has_lhs) { + if (maptype == MAPTYPE_UNMAP && !has_lhs) { retval = 1; goto theend; } @@ -507,13 +567,11 @@ static int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev, goto theend; } - if (is_abbrev && maptype != 1) { - // + if (is_abbrev && maptype != MAPTYPE_UNMAP) { // If an abbreviation ends in a keyword character, the // rest must be all keyword-char or all non-keyword-char. // Otherwise we won't be able to find the start of it in a // vi-compatible way. - // int same = -1; const int first = vim_iswordp(lhs); @@ -551,7 +609,8 @@ static int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev, } // Check if a new local mapping wasn't already defined globally. - if (args->unique && map_table == buf->b_maphash && has_lhs && has_rhs && maptype != 1) { + if (args->unique && map_table == buf->b_maphash && has_lhs && has_rhs + && maptype != MAPTYPE_UNMAP) { // need to loop over all global hash lists for (int hash = 0; hash < 256 && !got_int; hash++) { if (is_abbrev) { @@ -581,7 +640,7 @@ static int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev, } // When listing global mappings, also list buffer-local ones here. - if (map_table != buf->b_maphash && !has_rhs && maptype != 1) { + if (map_table != buf->b_maphash && !has_rhs && maptype != MAPTYPE_UNMAP) { // need to loop over all global hash lists for (int hash = 0; hash < 256 && !got_int; hash++) { if (is_abbrev) { @@ -616,7 +675,7 @@ static int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev, // entry with a matching 'to' part. This was done to allow ":ab foo bar" // to be unmapped by typing ":unab foo", where "foo" will be replaced by // "bar" because of the abbreviation. - for (int round = 0; (round == 0 || maptype == 1) && round <= 1 + for (int round = 0; (round == 0 || maptype == MAPTYPE_UNMAP) && round <= 1 && !did_it && !got_int; round++) { int hash_start, hash_end; if (has_lhs || is_abbrev) { @@ -650,7 +709,7 @@ static int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev, p = mp->m_keys; } if (STRNCMP(p, lhs, (size_t)(n < len ? n : len)) == 0) { - if (maptype == 1) { + if (maptype == MAPTYPE_UNMAP) { // Delete entry. // Only accept a full match. For abbreviations // we ignore trailing space when matching with @@ -715,6 +774,7 @@ static int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev, mp->m_mode = mode; mp->m_simplified = keyround1_simplified; mp->m_expr = args->expr; + mp->m_replace_keycodes = args->replace_keycodes; mp->m_script_ctx = current_sctx; mp->m_script_ctx.sc_lnum += sourcing_lnum; nlua_set_sctx(&mp->m_script_ctx); @@ -745,7 +805,7 @@ static int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev, } } - if (maptype == 1) { + if (maptype == MAPTYPE_UNMAP) { // delete entry if (!did_it) { if (!keyround1_simplified) { @@ -779,50 +839,10 @@ static int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev, } // Get here when adding a new entry to the maphash[] list or abbrlist. - mp = xmalloc(sizeof(mapblock_T)); - - // If CTRL-C has been mapped, don't always use it for Interrupting. - if (*lhs == Ctrl_C) { - if (map_table == buf->b_maphash) { - buf->b_mapped_ctrl_c |= mode; - } else { - mapped_ctrl_c |= mode; - } - } - - mp->m_keys = vim_strsave(lhs); - mp->m_str = args->rhs; - mp->m_orig_str = args->orig_rhs; - mp->m_luaref = args->rhs_lua; - if (!keyround1_simplified) { - args->rhs = NULL; - args->orig_rhs = NULL; - args->rhs_lua = LUA_NOREF; - } - mp->m_keylen = (int)STRLEN(mp->m_keys); - mp->m_noremap = noremap; - mp->m_nowait = args->nowait; - mp->m_silent = args->silent; - mp->m_mode = mode; - mp->m_simplified = keyround1_simplified; // Notice this when porting patch 8.2.0807 - mp->m_expr = args->expr; - mp->m_script_ctx = current_sctx; - mp->m_script_ctx.sc_lnum += sourcing_lnum; - nlua_set_sctx(&mp->m_script_ctx); - mp->m_desc = NULL; - if (args->desc != NULL) { - mp->m_desc = xstrdup(args->desc); - } - - // add the new entry in front of the abbrlist or maphash[] list - if (is_abbrev) { - mp->m_next = *abbr_table; - *abbr_table = mp; - } else { - n = MAP_HASH(mp->m_mode, mp->m_keys[0]); - mp->m_next = map_table[n]; - map_table[n] = mp; - } + map_add(buf, map_table, abbr_table, lhs, args, noremap, mode, is_abbrev, + -1, // sid + 0, // lnum + keyround1_simplified); } theend: @@ -861,7 +881,9 @@ theend: /// for :cabbr mode is MODE_CMDLINE /// ``` /// -/// @param maptype 0 for |:map|, 1 for |:unmap|, 2 for |noremap|. +/// @param maptype MAPTYPE_MAP for |:map| +/// MAPTYPE_UNMAP for |:unmap| +/// MAPTYPE_NOREMAP for |noremap|. /// @param arg C-string containing the arguments of the map/abbrev /// command, i.e. everything except the initial `:[X][nore]map`. /// - Cannot be a read-only string; it will be modified. @@ -878,7 +900,7 @@ theend: int do_map(int maptype, char_u *arg, int mode, bool is_abbrev) { MapArguments parsed_args; - int result = str_to_mapargs(arg, maptype == 1, &parsed_args); + int result = str_to_mapargs(arg, maptype == MAPTYPE_UNMAP, &parsed_args); switch (result) { case 0: break; @@ -1230,7 +1252,7 @@ char_u *set_context_in_map_cmd(expand_T *xp, char_u *cmd, char_u *arg, bool forc /// Find all mapping/abbreviation names that match regexp "regmatch". /// For command line expansion of ":[un]map" and ":[un]abbrev" in all modes. /// @return OK if matches found, FAIL otherwise. -int ExpandMappings(regmatch_T *regmatch, int *num_file, char_u ***file) +int ExpandMappings(regmatch_T *regmatch, int *num_file, char ***file) { mapblock_T *mp; int hash; @@ -1270,7 +1292,7 @@ int ExpandMappings(regmatch_T *regmatch, int *num_file, char_u ***file) if (round == 1) { count++; } else { - (*file)[count++] = vim_strsave(p); + (*file)[count++] = (char *)vim_strsave(p); } } } @@ -1293,7 +1315,7 @@ int ExpandMappings(regmatch_T *regmatch, int *num_file, char_u ***file) if (round == 1) { count++; } else { - (*file)[count++] = p; + (*file)[count++] = (char *)p; p = NULL; } } @@ -1307,22 +1329,18 @@ int ExpandMappings(regmatch_T *regmatch, int *num_file, char_u ***file) } if (round == 1) { - *file = (char_u **)xmalloc((size_t)count * sizeof(char_u *)); + *file = xmalloc((size_t)count * sizeof(char_u *)); } } // for (round) if (count > 1) { - char_u **ptr1; - char_u **ptr2; - char_u **ptr3; - // Sort the matches sort_strings(*file, count); // Remove multiple entries - ptr1 = *file; - ptr2 = ptr1 + 1; - ptr3 = ptr1 + count; + char **ptr1 = *file; + char **ptr2 = ptr1 + 1; + char **ptr3 = ptr1 + count; while (ptr2 < ptr3) { if (STRCMP(*ptr1, *ptr2)) { @@ -1517,12 +1535,8 @@ bool check_abbr(int c, char_u *ptr, int col, int mincol) /// @param c NUL or typed character for abbreviation char_u *eval_map_expr(mapblock_T *mp, int c) { - char_u *res; char_u *p = NULL; char_u *expr = NULL; - pos_T save_cursor; - int save_msg_col; - int save_msg_row; // Remove escaping of K_SPECIAL, because "str" is in a format to be used as // typeahead. @@ -1536,9 +1550,9 @@ char_u *eval_map_expr(mapblock_T *mp, int c) textlock++; ex_normal_lock++; set_vim_var_char(c); // set v:char to the typed character - save_cursor = curwin->w_cursor; - save_msg_col = msg_col; - save_msg_row = msg_row; + const pos_T save_cursor = curwin->w_cursor; + const int save_msg_col = msg_col; + const int save_msg_row = msg_row; if (mp->m_luaref != LUA_NOREF) { Error err = ERROR_INIT; Array args = ARRAY_DICT_INIT; @@ -1564,8 +1578,15 @@ char_u *eval_map_expr(mapblock_T *mp, int c) if (p == NULL) { return NULL; } - // Escape K_SPECIAL in the result to be able to use the string as typeahead. - res = (char_u *)vim_strsave_escape_ks((char *)p); + + char_u *res = NULL; + + if (mp->m_replace_keycodes) { + replace_termcodes((char *)p, STRLEN(p), (char **)&res, REPTERM_DO_LT, NULL, CPO_TO_CPO_FLAGS); + } else { + // Escape K_SPECIAL in the result to be able to use the string as typeahead. + res = (char_u *)vim_strsave_escape_ks((char *)p); + } xfree(p); return res; @@ -1612,7 +1633,7 @@ int makemap(FILE *fd, buf_T *buf) continue; } - // skip lua mappings and mappings that contain a <SNR> (script-local thing), + // skip Lua mappings and mappings that contain a <SNR> (script-local thing), // they probably don't work when loaded again if (mp->m_luaref != LUA_NOREF) { continue; @@ -1973,9 +1994,9 @@ void f_hasmapto(typval_T *argvars, typval_T *rettv, FunPtr fptr) /// @param mp The maphash that contains the mapping information /// @param buffer_value The "buffer" value /// @param compatible True for compatible with old maparg() dict -static void mapblock_fill_dict(dict_T *const dict, const mapblock_T *const mp, long buffer_value, - bool compatible) - FUNC_ATTR_NONNULL_ALL +static void mapblock_fill_dict(dict_T *const dict, const mapblock_T *const mp, + const char *lhsrawalt, long buffer_value, bool compatible) + FUNC_ATTR_NONNULL_ARG(1, 2) { char *const lhs = str2special_save((const char *)mp->m_keys, compatible, !compatible); @@ -2007,6 +2028,11 @@ static void mapblock_fill_dict(dict_T *const dict, const mapblock_T *const mp, l tv_dict_add_allocated_str(dict, S_LEN("desc"), xstrdup(mp->m_desc)); } tv_dict_add_allocated_str(dict, S_LEN("lhs"), lhs); + tv_dict_add_str(dict, S_LEN("lhsraw"), (const char *)mp->m_keys); + if (lhsrawalt != NULL) { + // Also add the value for the simplified entry. + tv_dict_add_str(dict, S_LEN("lhsrawalt"), lhsrawalt); + } tv_dict_add_nr(dict, S_LEN("noremap"), noremap_value); tv_dict_add_nr(dict, S_LEN("script"), mp->m_noremap == REMAP_SCRIPT ? 1 : 0); tv_dict_add_nr(dict, S_LEN("expr"), mp->m_expr ? 1 : 0); @@ -2015,23 +2041,14 @@ static void mapblock_fill_dict(dict_T *const dict, const mapblock_T *const mp, l tv_dict_add_nr(dict, S_LEN("lnum"), (varnumber_T)mp->m_script_ctx.sc_lnum); tv_dict_add_nr(dict, S_LEN("buffer"), (varnumber_T)buffer_value); tv_dict_add_nr(dict, S_LEN("nowait"), mp->m_nowait ? 1 : 0); + if (mp->m_replace_keycodes) { + tv_dict_add_nr(dict, S_LEN("replace_keycodes"), 1); + } tv_dict_add_allocated_str(dict, S_LEN("mode"), mapmode); } static void get_maparg(typval_T *argvars, typval_T *rettv, int exact) { - char *keys_buf = NULL; - char_u *alt_keys_buf = NULL; - bool did_simplify = false; - char_u *rhs; - LuaRef rhs_lua; - int mode; - bool abbr = false; - bool get_dict = false; - mapblock_T *mp; - int buffer_local; - int flags = REPTERM_FROM_PART | REPTERM_DO_LT; - // Return empty string for failure. rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; @@ -2041,8 +2058,11 @@ static void get_maparg(typval_T *argvars, typval_T *rettv, int exact) return; } - char buf[NUMBUFLEN]; const char *which; + char buf[NUMBUFLEN]; + bool abbr = false; + bool get_dict = false; + if (argvars[1].v_type != VAR_UNKNOWN) { which = tv_get_string_buf_chk(&argvars[1], buf); if (argvars[2].v_type != VAR_UNKNOWN) { @@ -2058,13 +2078,19 @@ static void get_maparg(typval_T *argvars, typval_T *rettv, int exact) return; } - mode = get_map_mode((char **)&which, 0); + char *keys_buf = NULL; + char_u *alt_keys_buf = NULL; + bool did_simplify = false; + const int flags = REPTERM_FROM_PART | REPTERM_DO_LT; + const int mode = get_map_mode((char **)&which, 0); char_u *keys_simplified - = (char_u *)replace_termcodes(keys, - STRLEN(keys), &keys_buf, flags, &did_simplify, + = (char_u *)replace_termcodes(keys, STRLEN(keys), &keys_buf, flags, &did_simplify, CPO_TO_CPO_FLAGS); - rhs = check_map(keys_simplified, mode, exact, false, abbr, &mp, &buffer_local, &rhs_lua); + mapblock_T *mp = NULL; + int buffer_local; + LuaRef rhs_lua; + char_u *rhs = check_map(keys_simplified, mode, exact, false, abbr, &mp, &buffer_local, &rhs_lua); if (did_simplify) { // When the lhs is being simplified the not-simplified keys are // preferred for printing, like in do_map(). @@ -2093,7 +2119,8 @@ static void get_maparg(typval_T *argvars, typval_T *rettv, int exact) tv_dict_alloc_ret(rettv); if (mp != NULL && (rhs != NULL || rhs_lua != LUA_NOREF)) { // Return a dictionary. - mapblock_fill_dict(rettv->vval.v_dict, mp, buffer_local, true); + mapblock_fill_dict(rettv->vval.v_dict, mp, did_simplify ? (char *)keys_simplified : NULL, + buffer_local, true); } } @@ -2101,6 +2128,74 @@ static void get_maparg(typval_T *argvars, typval_T *rettv, int exact) xfree(alt_keys_buf); } +/// "mapset()" function +void f_mapset(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + char buf[NUMBUFLEN]; + const char *which = tv_get_string_buf_chk(&argvars[0], buf); + if (which == NULL) { + return; + } + const int mode = get_map_mode((char **)&which, 0); + const bool is_abbr = tv_get_number(&argvars[1]) != 0; + + if (argvars[2].v_type != VAR_DICT) { + emsg(_(e_dictreq)); + return; + } + dict_T *d = argvars[2].vval.v_dict; + + // Get the values in the same order as above in get_maparg(). + char *lhs = tv_dict_get_string(d, "lhs", false); + char *lhsraw = tv_dict_get_string(d, "lhsraw", false); + char *lhsrawalt = tv_dict_get_string(d, "lhsrawalt", false); + char *rhs = tv_dict_get_string(d, "rhs", false); + if (lhs == NULL || lhsraw == NULL || rhs == NULL) { + emsg(_("E460: entries missing in mapset() dict argument")); + return; + } + char *orig_rhs = rhs; + char *arg_buf = NULL; + rhs = replace_termcodes(rhs, STRLEN(rhs), &arg_buf, REPTERM_DO_LT, NULL, CPO_TO_CPO_FLAGS); + + int noremap = tv_dict_get_number(d, "noremap") ? REMAP_NONE : 0; + if (tv_dict_get_number(d, "script") != 0) { + noremap = REMAP_SCRIPT; + } + MapArguments args = { // TODO(zeertzjq): support restoring "callback"? + .rhs = (char_u *)rhs, + .rhs_lua = LUA_NOREF, + .orig_rhs = vim_strsave((char_u *)orig_rhs), + .expr = tv_dict_get_number(d, "expr") != 0, + .silent = tv_dict_get_number(d, "silent") != 0, + .nowait = tv_dict_get_number(d, "nowait") != 0, + .replace_keycodes = tv_dict_get_number(d, "replace_keycodes") != 0, + .desc = tv_dict_get_string(d, "desc", false), + }; + scid_T sid = (scid_T)tv_dict_get_number(d, "sid"); + linenr_T lnum = (linenr_T)tv_dict_get_number(d, "lnum"); + bool buffer = tv_dict_get_number(d, "buffer") != 0; + // mode from the dict is not used + + mapblock_T **map_table = buffer ? curbuf->b_maphash : maphash; + mapblock_T **abbr_table = buffer ? &curbuf->b_first_abbr : &first_abbr; + + // Delete any existing mapping for this lhs and mode. + MapArguments unmap_args = MAP_ARGUMENTS_INIT; + set_maparg_lhs_rhs(lhs, strlen(lhs), rhs, strlen(rhs), LUA_NOREF, 0, &unmap_args); + unmap_args.buffer = buffer; + buf_do_map(MAPTYPE_UNMAP, &unmap_args, mode, false, curbuf); + xfree(unmap_args.rhs); + xfree(unmap_args.orig_rhs); + + if (lhsrawalt != NULL) { + map_add(curbuf, map_table, abbr_table, (char_u *)lhsrawalt, &args, noremap, mode, is_abbr, + sid, lnum, true); + } + map_add(curbuf, map_table, abbr_table, (char_u *)lhsraw, &args, noremap, mode, is_abbr, + sid, lnum, false); +} + /// "maparg()" function void f_maparg(typval_T *argvars, typval_T *rettv, FunPtr fptr) { @@ -2123,11 +2218,11 @@ void f_mapcheck(typval_T *argvars, typval_T *rettv, FunPtr fptr) /// @param buffer If true, make a buffer-local mapping for curbuf void add_map(char *lhs, char *rhs, int mode, bool buffer) { - MapArguments args = { 0 }; + MapArguments args = MAP_ARGUMENTS_INIT; set_maparg_lhs_rhs(lhs, strlen(lhs), rhs, strlen(rhs), LUA_NOREF, 0, &args); args.buffer = buffer; - buf_do_map(2, &args, mode, false, curbuf); + buf_do_map(MAPTYPE_NOREMAP, &args, mode, false, curbuf); xfree(args.rhs); xfree(args.orig_rhs); } @@ -2307,7 +2402,8 @@ static void do_exmap(exarg_T *eap, int isabbrev) char *cmdp = eap->cmd; mode = get_map_mode(&cmdp, eap->forceit || isabbrev); - switch (do_map((*cmdp == 'n') ? 2 : (*cmdp == 'u'), + switch (do_map((*cmdp == 'n') ? MAPTYPE_NOREMAP + : (*cmdp == 'u') ? MAPTYPE_UNMAP : MAPTYPE_MAP, (char_u *)eap->arg, mode, isabbrev)) { case 1: emsg(_(e_invarg)); @@ -2396,10 +2492,16 @@ void modify_keymap(uint64_t channel_id, Buffer buffer, bool is_unmap, String mod KEY_TO_BOOL(script); KEY_TO_BOOL(expr); KEY_TO_BOOL(unique); + KEY_TO_BOOL(replace_keycodes); #undef KEY_TO_BOOL } parsed_args.buffer = !global; + if (parsed_args.replace_keycodes && !parsed_args.expr) { + api_set_error(err, kErrorTypeValidation, "\"replace_keycodes\" requires \"expr\""); + goto fail_and_free; + } + if (!set_maparg_lhs_rhs(lhs.data, lhs.size, rhs.data, rhs.size, lua_funcref, CPO_TO_CPO_FLAGS, &parsed_args)) { @@ -2461,11 +2563,11 @@ void modify_keymap(uint64_t channel_id, Buffer buffer, bool is_unmap, String mod } // buf_do_map() reads noremap/unmap as its own argument. - int maptype_val = 0; + int maptype_val = MAPTYPE_MAP; if (is_unmap) { - maptype_val = 1; + maptype_val = MAPTYPE_UNMAP; } else if (is_noremap) { - maptype_val = 2; + maptype_val = MAPTYPE_NOREMAP; } switch (buf_do_map(maptype_val, &parsed_args, mode_val, 0, target_buf)) { @@ -2499,7 +2601,7 @@ fail_and_free: /// /// @param mode The abbreviation for the mode /// @param buf The buffer to get the mapping array. NULL for global -/// @param from_lua Whether it is called from internal lua api. +/// @param from_lua Whether it is called from internal Lua api. /// @returns Array of maparg()-like dictionaries describing mappings ArrayOf(Dictionary) keymap_array(String mode, buf_T *buf, bool from_lua) { @@ -2523,7 +2625,7 @@ ArrayOf(Dictionary) keymap_array(String mode, buf_T *buf, bool from_lua) } // Check for correct mode if (int_mode & current_maphash->m_mode) { - mapblock_fill_dict(dict, current_maphash, buffer_value, false); + mapblock_fill_dict(dict, current_maphash, NULL, buffer_value, false); Object api_dict = vim_to_object((typval_T[]) { { .v_type = VAR_DICT, .vval.v_dict = dict } }); if (from_lua) { diff --git a/src/nvim/mapping.h b/src/nvim/mapping.h index 4b0622ffa1..7c48c3bce2 100644 --- a/src/nvim/mapping.h +++ b/src/nvim/mapping.h @@ -2,7 +2,6 @@ #define NVIM_MAPPING_H #include "nvim/buffer_defs.h" -#include "nvim/eval/funcs.h" #include "nvim/ex_cmds_defs.h" #include "nvim/types.h" #include "nvim/vim.h" @@ -22,6 +21,7 @@ struct map_arguments { bool script; bool silent; bool unique; + bool replace_keycodes; /// The {lhs} of the mapping. /// @@ -45,9 +45,14 @@ struct map_arguments { char *desc; /// map description }; typedef struct map_arguments MapArguments; -#define MAP_ARGUMENTS_INIT { false, false, false, false, false, false, false, \ +#define MAP_ARGUMENTS_INIT { false, false, false, false, false, false, false, false, \ { 0 }, 0, { 0 }, 0, NULL, 0, LUA_NOREF, false, NULL, 0, NULL } +// Used for the first argument of do_map() +#define MAPTYPE_MAP 0 +#define MAPTYPE_UNMAP 1 +#define MAPTYPE_NOREMAP 2 + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "mapping.h.generated.h" #endif diff --git a/src/nvim/mark.c b/src/nvim/mark.c index 66855c66b5..1fe3327b29 100644 --- a/src/nvim/mark.c +++ b/src/nvim/mark.c @@ -339,11 +339,11 @@ fmark_T *mark_get(buf_T *buf, win_T *win, fmark_T *fmp, MarkGet flag, int name) fmark_T *fm = NULL; if (ASCII_ISUPPER(name) || ascii_isdigit(name)) { // Global marks - xfmark_T *xfm = mark_get_global(!(flag & kMarkAllNoResolve), name); + xfmark_T *xfm = mark_get_global(flag != kMarkAllNoResolve, name); fm = &xfm->fmark; - // Only wanted marks belonging to the buffer - if ((flag & kMarkBufLocal) && xfm->fmark.fnum != buf->handle) { - return NULL; + if (flag == kMarkBufLocal && xfm->fmark.fnum != buf->handle) { + // Only wanted marks belonging to the buffer + return pos_to_mark(buf, NULL, (pos_T){ .lnum = 0 }); } } else if (name > 0 && name < NMARK_LOCAL_MAX) { // Local Marks @@ -491,7 +491,7 @@ fmark_T *mark_get_visual(buf_T *buf, int name) mark = pos_to_mark(buf, NULL, endp); } - if (mark != NULL && buf->b_visual.vi_mode == 'V') { + if (buf->b_visual.vi_mode == 'V') { if (name == '<') { mark->mark.col = 0; } else { @@ -508,11 +508,12 @@ fmark_T *mark_get_visual(buf_T *buf, int name) /// Pass an fmp if multiple c /// @note view fields are set to 0. /// @param buf for fmark->fnum. -/// @param pos for fmrak->mark. +/// @param pos for fmark->mark. /// @param fmp pointer to save the mark. /// /// @return[static] Mark with the given information. fmark_T *pos_to_mark(buf_T *buf, fmark_T *fmp, pos_T pos) + FUNC_ATTR_NONNULL_RET { static fmark_T fms = INIT_FMARK; fmark_T *fm = fmp == NULL ? &fms : fmp; diff --git a/src/nvim/match.c b/src/nvim/match.c index e17a95569c..8c72b13bc2 100644 --- a/src/nvim/match.c +++ b/src/nvim/match.c @@ -7,6 +7,7 @@ #include "nvim/buffer_defs.h" #include "nvim/charset.h" +#include "nvim/eval/funcs.h" #include "nvim/fold.h" #include "nvim/highlight_group.h" #include "nvim/match.h" diff --git a/src/nvim/match.h b/src/nvim/match.h index fdcec0ae05..22a848bfdf 100644 --- a/src/nvim/match.h +++ b/src/nvim/match.h @@ -2,7 +2,6 @@ #define NVIM_MATCH_H #include "nvim/buffer_defs.h" -#include "nvim/eval/funcs.h" #include "nvim/ex_cmds_defs.h" #ifdef INCLUDE_GENERATED_DECLARATIONS diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c index a9792cf1b9..223b4d6845 100644 --- a/src/nvim/mbyte.c +++ b/src/nvim/mbyte.c @@ -551,7 +551,7 @@ size_t mb_string2cells(const char *str) size_t clen = 0; for (const char_u *p = (char_u *)str; *p != NUL; p += utfc_ptr2len((char *)p)) { - clen += utf_ptr2cells((char *)p); + clen += (size_t)utf_ptr2cells((char *)p); } return clen; @@ -569,8 +569,8 @@ size_t mb_string2cells_len(const char *str, size_t size) size_t clen = 0; for (const char_u *p = (char_u *)str; *p != NUL && p < (char_u *)str + size; - p += utfc_ptr2len_len(p, size + (p - (char_u *)str))) { - clen += utf_ptr2cells((char *)p); + p += utfc_ptr2len_len(p, (int)size + (int)(p - (char_u *)str))) { + clen += (size_t)utf_ptr2cells((char *)p); } return clen; @@ -994,37 +994,37 @@ int utf_char2len(const int c) int utf_char2bytes(const int c, char *const buf) { if (c < 0x80) { // 7 bits - buf[0] = c; + buf[0] = (char)c; return 1; } else if (c < 0x800) { // 11 bits - buf[0] = 0xc0 + ((unsigned)c >> 6); - buf[1] = 0x80 + (c & 0x3f); + buf[0] = (char)(0xc0 + ((unsigned)c >> 6)); + buf[1] = (char)(0x80 + ((unsigned)c & 0x3f)); return 2; } else if (c < 0x10000) { // 16 bits - buf[0] = 0xe0 + ((unsigned)c >> 12); - buf[1] = 0x80 + (((unsigned)c >> 6) & 0x3f); - buf[2] = 0x80 + (c & 0x3f); + buf[0] = (char)(0xe0 + ((unsigned)c >> 12)); + buf[1] = (char)(0x80 + (((unsigned)c >> 6) & 0x3f)); + buf[2] = (char)(0x80 + ((unsigned)c & 0x3f)); return 3; } else if (c < 0x200000) { // 21 bits - buf[0] = 0xf0 + ((unsigned)c >> 18); - buf[1] = 0x80 + (((unsigned)c >> 12) & 0x3f); - buf[2] = 0x80 + (((unsigned)c >> 6) & 0x3f); - buf[3] = 0x80 + (c & 0x3f); + buf[0] = (char)(0xf0 + ((unsigned)c >> 18)); + buf[1] = (char)(0x80 + (((unsigned)c >> 12) & 0x3f)); + buf[2] = (char)(0x80 + (((unsigned)c >> 6) & 0x3f)); + buf[3] = (char)(0x80 + ((unsigned)c & 0x3f)); return 4; } else if (c < 0x4000000) { // 26 bits - buf[0] = 0xf8 + ((unsigned)c >> 24); - buf[1] = 0x80 + (((unsigned)c >> 18) & 0x3f); - buf[2] = 0x80 + (((unsigned)c >> 12) & 0x3f); - buf[3] = 0x80 + (((unsigned)c >> 6) & 0x3f); - buf[4] = 0x80 + (c & 0x3f); + buf[0] = (char)(0xf8 + ((unsigned)c >> 24)); + buf[1] = (char)(0x80 + (((unsigned)c >> 18) & 0x3f)); + buf[2] = (char)(0x80 + (((unsigned)c >> 12) & 0x3f)); + buf[3] = (char)(0x80 + (((unsigned)c >> 6) & 0x3f)); + buf[4] = (char)(0x80 + ((unsigned)c & 0x3f)); return 5; } else { // 31 bits - buf[0] = 0xfc + ((unsigned)c >> 30); - buf[1] = 0x80 + (((unsigned)c >> 24) & 0x3f); - buf[2] = 0x80 + (((unsigned)c >> 18) & 0x3f); - buf[3] = 0x80 + (((unsigned)c >> 12) & 0x3f); - buf[4] = 0x80 + (((unsigned)c >> 6) & 0x3f); - buf[5] = 0x80 + (c & 0x3f); + buf[0] = (char)(0xfc + ((unsigned)c >> 30)); + buf[1] = (char)(0x80 + (((unsigned)c >> 24) & 0x3f)); + buf[2] = (char)(0x80 + (((unsigned)c >> 18) & 0x3f)); + buf[3] = (char)(0x80 + (((unsigned)c >> 12) & 0x3f)); + buf[4] = (char)(0x80 + (((unsigned)c >> 6) & 0x3f)); + buf[5] = (char)(0x80 + ((unsigned)c & 0x3f)); return 6; } } @@ -1251,7 +1251,7 @@ int mb_toupper(int a) #if defined(__STDC_ISO_10646__) // If towupper() is available and handles Unicode, use it. if (!(cmp_flags & CMP_INTERNAL)) { - return towupper(a); + return (int)towupper((wint_t)a); } #endif @@ -1282,7 +1282,7 @@ int mb_tolower(int a) #if defined(__STDC_ISO_10646__) // If towlower() is available and handles Unicode, use it. if (!(cmp_flags & CMP_INTERNAL)) { - return towlower(a); + return (int)towlower((wint_t)a); } #endif @@ -1347,10 +1347,10 @@ static int utf_strnicmp(const char_u *s1, const char_u *s2, size_t n1, size_t n2 // to fold just one character to determine the result of comparison. if (c1 != -1 && c2 == -1) { - n1 = utf_char2bytes(utf_fold(c1), (char *)buffer); + n1 = (size_t)utf_char2bytes(utf_fold(c1), (char *)buffer); s1 = (char_u *)buffer; } else if (c2 != -1 && c1 == -1) { - n2 = utf_char2bytes(utf_fold(c2), (char *)buffer); + n2 = (size_t)utf_char2bytes(utf_fold(c2), (char *)buffer); s2 = (char_u *)buffer; } @@ -1487,7 +1487,7 @@ void mb_utflen(const char_u *s, size_t len, size_t *codepoints, size_t *codeunit size_t count = 0, extra = 0; size_t clen; for (size_t i = 0; i < len && s[i] != NUL; i += clen) { - clen = utf_ptr2len_len(s + i, len - i); + clen = (size_t)utf_ptr2len_len(s + i, (int)(len - i)); // NB: gets the byte value of invalid sequence bytes. // we only care whether the char fits in the BMP or not int c = (clen > 1) ? utf_ptr2char((char *)s + i) : s[i]; @@ -1509,7 +1509,7 @@ ssize_t mb_utf_index_to_bytes(const char_u *s, size_t len, size_t index, bool us return 0; } for (i = 0; i < len && s[i] != NUL; i += clen) { - clen = utf_ptr2len_len(s + i, len - i); + clen = (size_t)utf_ptr2len_len(s + i, (int)(len - i)); // NB: gets the byte value of invalid sequence bytes. // we only care whether the char fits in the BMP or not int c = (clen > 1) ? utf_ptr2char((char *)s + i) : s[i]; @@ -1518,7 +1518,7 @@ ssize_t mb_utf_index_to_bytes(const char_u *s, size_t len, size_t index, bool us count++; } if (count >= index) { - return i + clen; + return (ssize_t)(i + clen); } } return -1; @@ -1837,10 +1837,10 @@ int mb_off_next(const char_u *base, const char_u *p) return i; } -/// Return the offset from "p" to the last byte of the character it points -/// into. Can start anywhere in a stream of bytes. -/// Composing characters are not included. -int mb_tail_off(const char *base, const char *p_in) +/// Return the offset from `p_in` to the last byte of the codepoint it points +/// to. Can start anywhere in a stream of bytes. +/// Note: Counts individual codepoints of composed characters separately. +int utf_cp_tail_off(const char *base, const char *p_in) { const uint8_t *p = (uint8_t *)p_in; int i; @@ -1866,15 +1866,16 @@ int mb_tail_off(const char *base, const char *p_in) return i; } -/// Return the offset from "p" to the first byte of the character it points -/// into. Can start anywhere in a stream of bytes. -/// Unlike utf_head_off() this doesn't include composing characters and returns a negative value. +/// Return the offset from "p" to the first byte of the codepoint it points +/// to. Can start anywhere in a stream of bytes. +/// Note: Unlike `utf_head_off`, this counts individual codepoints of composed characters +/// separately and returns a negative offset. /// /// @param[in] base Pointer to start of string /// @param[in] p Pointer to byte for which to return the offset to the previous codepoint // /// @return 0 if invalid sequence, else offset to previous codepoint -int mb_head_off(const char_u *base, const char_u *p) +int utf_cp_head_off(const char_u *base, const char_u *p) { int i; int j; @@ -2165,7 +2166,7 @@ char_u *enc_canonize(char_u *enc) FUNC_ATTR_NONNULL_RET if (*s == '_') { *p++ = '-'; } else { - *p++ = TOLOWER_ASC(*s); + *p++ = (char_u)TOLOWER_ASC(*s); } } *p = NUL; @@ -2269,8 +2270,8 @@ char_u *enc_locale(void) && !isalnum((int)p[4]) && p[4] != '-' && p[-3] == '_') { // Copy "XY.EUC" to "euc-XY" to buf[10]. memmove(buf, "euc-", 4); - buf[4] = (ASCII_ISALNUM(p[-2]) ? TOLOWER_ASC(p[-2]) : 0); - buf[5] = (ASCII_ISALNUM(p[-1]) ? TOLOWER_ASC(p[-1]) : 0); + buf[4] = (char)(ASCII_ISALNUM(p[-2]) ? TOLOWER_ASC(p[-2]) : 0); + buf[5] = (char)(ASCII_ISALNUM(p[-1]) ? TOLOWER_ASC(p[-1]) : 0); buf[6] = NUL; } else { s = p + 1; @@ -2282,7 +2283,7 @@ enc_locale_copy_enc: if (s[i] == '_' || s[i] == '-') { buf[i] = '-'; } else if (ASCII_ISALNUM((uint8_t)s[i])) { - buf[i] = TOLOWER_ASC(s[i]); + buf[i] = (char)TOLOWER_ASC(s[i]); } else { break; } @@ -2406,14 +2407,14 @@ static char_u *iconv_string(const vimconv_T *const vcp, char_u *str, size_t slen } l = utfc_ptr2len_len((const char_u *)from, (int)fromlen); from += l; - fromlen -= l; + fromlen -= (size_t)l; } else if (ICONV_ERRNO != ICONV_E2BIG) { // conversion failed XFREE_CLEAR(result); break; } // Not enough room or skipping illegal sequence. - done = to - (char *)result; + done = (size_t)(to - (char *)result); } if (resultlenp != NULL && result != NULL) { @@ -2550,10 +2551,10 @@ char_u *string_convert_ext(const vimconv_T *const vcp, char_u *ptr, size_t *lenp for (size_t i = 0; i < len; ++i) { c = ptr[i]; if (c < 0x80) { - *d++ = c; + *d++ = (char_u)c; } else { - *d++ = 0xc0 + ((unsigned)c >> 6); - *d++ = 0x80 + (c & 0x3f); + *d++ = (char_u)(0xc0 + (char_u)((unsigned)c >> 6)); + *d++ = (char_u)(0x80 + (c & 0x3f)); } } *d = NUL; @@ -2597,8 +2598,8 @@ char_u *string_convert_ext(const vimconv_T *const vcp, char_u *ptr, size_t *lenp case CONV_TO_LATIN9: // utf-8 to latin9 conversion retval = xmalloc(len + 1); d = retval; - for (size_t i = 0; i < len; ++i) { - l = utf_ptr2len_len(ptr + i, len - i); + for (size_t i = 0; i < len; i++) { + l = utf_ptr2len_len(ptr + i, (int)(len - i)); if (l == 0) { *d++ = NUL; } else if (l == 1) { @@ -2648,7 +2649,7 @@ char_u *string_convert_ext(const vimconv_T *const vcp, char_u *ptr, size_t *lenp } if (!utf_iscomposing(c)) { // skip composing chars if (c < 0x100) { - *d++ = c; + *d++ = (char_u)c; } else if (vcp->vc_fail) { xfree(retval); return NULL; @@ -2659,7 +2660,7 @@ char_u *string_convert_ext(const vimconv_T *const vcp, char_u *ptr, size_t *lenp } } } - i += l - 1; + i += (size_t)l - 1; } } *d = NUL; diff --git a/src/nvim/memline.c b/src/nvim/memline.c index 5f74440747..fa3a400a68 100644 --- a/src/nvim/memline.c +++ b/src/nvim/memline.c @@ -309,7 +309,7 @@ int ml_open(buf_T *buf) if (!buf->b_spell) { b0p->b0_dirty = buf->b_changed ? B0_DIRTY : 0; - b0p->b0_flags = get_fileformat(buf) + 1; + b0p->b0_flags = (uint8_t)(get_fileformat(buf) + 1); set_b0_fname(b0p, buf); (void)os_get_username((char *)b0p->b0_uname, B0_UNAME_SIZE); b0p->b0_uname[B0_UNAME_SIZE - 1] = NUL; @@ -359,7 +359,7 @@ int ml_open(buf_T *buf) dp = hp->bh_data; dp->db_index[0] = --dp->db_txt_start; // at end of block - dp->db_free -= 1 + INDEX_SIZE; + dp->db_free -= 1 + (unsigned)INDEX_SIZE; dp->db_line_count = 1; *((char_u *)dp + dp->db_txt_start) = NUL; // empty line @@ -711,7 +711,7 @@ static void set_b0_dir_flag(ZERO_BL *b0p, buf_T *buf) if (same_directory(buf->b_ml.ml_mfp->mf_fname, (char_u *)buf->b_ffname)) { b0p->b0_flags |= B0_SAME_DIR; } else { - b0p->b0_flags &= ~B0_SAME_DIR; + b0p->b0_flags &= (uint8_t) ~B0_SAME_DIR; } } @@ -723,7 +723,7 @@ static void add_b0_fenc(ZERO_BL *b0p, buf_T *buf) n = (int)STRLEN(buf->b_p_fenc); if ((int)STRLEN(b0p->b0_fname) + n + 1 > size) { - b0p->b0_flags &= ~B0_HAS_FENC; + b0p->b0_flags &= (uint8_t) ~B0_HAS_FENC; } else { memmove((char *)b0p->b0_fname + size - n, (char *)buf->b_p_fenc, (size_t)n); @@ -750,7 +750,6 @@ void ml_recover(bool checkext) DATA_BL *dp; infoptr_T *ip; blocknr_T bnum; - int page_count; int len; bool directly; linenr_T lnum; @@ -971,7 +970,7 @@ void ml_recover(bool checkext) int fnsize = B0_FNAME_SIZE_NOCRYPT; for (p = b0p->b0_fname + fnsize; p > b0p->b0_fname && p[-1] != NUL; p--) {} - b0_fenc = vim_strnsave(p, b0p->b0_fname + fnsize - p); + b0_fenc = vim_strnsave(p, (size_t)(b0p->b0_fname + fnsize - p)); } mf_put(mfp, hp, false, false); // release block 0 @@ -1004,11 +1003,11 @@ void ml_recover(bool checkext) } unchanged(curbuf, true, true); - bnum = 1; // start with block 1 - page_count = 1; // which is 1 page - lnum = 0; // append after line 0 in curbuf + bnum = 1; // start with block 1 + unsigned page_count = 1; // which is 1 page + lnum = 0; // append after line 0 in curbuf line_count = 0; - idx = 0; // start with first index in block 1 + idx = 0; // start with first index in block 1 error = 0; buf->b_ml.ml_stack_top = 0; // -V1048 buf->b_ml.ml_stack = NULL; @@ -1091,7 +1090,7 @@ void ml_recover(bool checkext) bnum = pp->pb_pointer[idx].pe_bnum; line_count = pp->pb_pointer[idx].pe_line_count; - page_count = pp->pb_pointer[idx].pe_page_count; + page_count = (unsigned)pp->pb_pointer[idx].pe_page_count; idx = 0; continue; } @@ -1267,12 +1266,12 @@ theend: int recover_names(char_u *fname, int list, int nr, char_u **fname_out) { int num_names; - char_u *(names[6]); + char *(names[6]); char_u *tail; char_u *p; int num_files; int file_count = 0; - char_u **files; + char **files; char_u *dirp; char_u *dir_name; char_u *fname_res = NULL; @@ -1309,22 +1308,22 @@ int recover_names(char_u *fname, int list, int nr, char_u **fname_out) if (dir_name[0] == '.' && dir_name[1] == NUL) { // check current dir if (fname == NULL) { - names[0] = vim_strsave((char_u *)"*.sw?"); + names[0] = xstrdup("*.sw?"); // For Unix names starting with a dot are special. MS-Windows // supports this too, on some file systems. - names[1] = vim_strsave((char_u *)".*.sw?"); - names[2] = vim_strsave((char_u *)".sw?"); + names[1] = xstrdup(".*.sw?"); + names[2] = xstrdup(".sw?"); num_names = 3; } else { num_names = recov_file_names(names, fname_res, TRUE); } } else { // check directory dir_name if (fname == NULL) { - names[0] = (char_u *)concat_fnames((char *)dir_name, "*.sw?", true); + names[0] = concat_fnames((char *)dir_name, "*.sw?", true); // For Unix names starting with a dot are special. MS-Windows // supports this too, on some file systems. - names[1] = (char_u *)concat_fnames((char *)dir_name, ".*.sw?", true); - names[2] = (char_u *)concat_fnames((char *)dir_name, ".sw?", true); + names[1] = concat_fnames((char *)dir_name, ".*.sw?", true); + names[2] = concat_fnames((char *)dir_name, ".sw?", true); num_names = 3; } else { int len = (int)STRLEN(dir_name); @@ -1361,7 +1360,7 @@ int recover_names(char_u *fname, int list, int nr, char_u **fname_out) if (swapname != NULL) { if (os_path_exists(swapname)) { files = xmalloc(sizeof(char_u *)); - files[0] = swapname; + files[0] = (char *)swapname; swapname = NULL; num_files = 1; } @@ -1377,7 +1376,7 @@ int recover_names(char_u *fname, int list, int nr, char_u **fname_out) for (int i = 0; i < num_files; i++) { // Do not expand wildcards, on Windows would try to expand // "%tmp%" in "%tmp%file" - if (path_full_compare((char *)p, (char *)files[i], true, false) & kEqualFiles) { + if (path_full_compare((char *)p, files[i], true, false) & kEqualFiles) { // Remove the name from files[i]. Move further entries // down. When the array becomes empty free it here, since // FreeWild() won't be called below. @@ -1395,7 +1394,7 @@ int recover_names(char_u *fname, int list, int nr, char_u **fname_out) if (nr > 0) { file_count += num_files; if (nr <= file_count) { - *fname_out = vim_strsave(files[nr - 1 + num_files - file_count]); + *fname_out = vim_strsave((char_u *)files[nr - 1 + num_files - file_count]); dirp = (char_u *)""; // stop searching } } else if (list) { @@ -1416,9 +1415,9 @@ int recover_names(char_u *fname, int list, int nr, char_u **fname_out) // print the swap file name msg_outnum((long)++file_count); msg_puts(". "); - msg_puts((const char *)path_tail((char *)files[i])); + msg_puts((const char *)path_tail(files[i])); msg_putchar('\n'); - (void)swapfile_info(files[i]); + (void)swapfile_info((char_u *)files[i]); } } else { msg_puts(_(" -- none --\n")); @@ -1518,7 +1517,7 @@ static time_t swapfile_info(char_u *fname) if (os_fileinfo((char *)fname, &file_info)) { #ifdef UNIX // print name of owner of the file - if (os_get_uname(file_info.stat.st_uid, uname, B0_UNAME_SIZE) == OK) { + if (os_get_uname((uv_uid_t)file_info.stat.st_uid, uname, B0_UNAME_SIZE) == OK) { msg_puts(_(" owned by: ")); msg_outtrans(uname); msg_puts(_(" dated: ")); @@ -1637,7 +1636,7 @@ static bool swapfile_unchanged(char *fname) return ret; } -static int recov_file_names(char_u **names, char_u *path, int prepend_dot) +static int recov_file_names(char **names, char_u *path, int prepend_dot) FUNC_ATTR_NONNULL_ALL { int num_names = 0; @@ -1645,7 +1644,7 @@ static int recov_file_names(char_u **names, char_u *path, int prepend_dot) // May also add the file name with a dot prepended, for swap file in same // dir as original file. if (prepend_dot) { - names[num_names] = (char_u *)modname((char *)path, ".sw?", true); + names[num_names] = modname((char *)path, ".sw?", true); if (names[num_names] == NULL) { return num_names; } @@ -1653,9 +1652,9 @@ static int recov_file_names(char_u **names, char_u *path, int prepend_dot) } // Form the normal swap file name pattern by appending ".sw?". - names[num_names] = (char_u *)concat_fnames((char *)path, ".sw?", FALSE); + names[num_names] = concat_fnames((char *)path, ".sw?", false); if (num_names >= 1) { // check if we have the same name twice - char_u *p = names[num_names - 1]; + char_u *p = (char_u *)names[num_names - 1]; int i = (int)STRLEN(names[num_names - 1]) - (int)STRLEN(names[num_names]); if (i > 0) { p += i; // file name has been expanded to full path @@ -1970,12 +1969,9 @@ static int ml_append_int(buf_T *buf, linenr_T lnum, char_u *line, colnr_T len, b int line_count; // number of indexes in current block int offset; int from, to; - int space_needed; // space needed for new line - int page_size; int page_count; int db_idx; // index for lnum in data block bhdr_T *hp; - memfile_T *mfp; DATA_BL *dp; PTR_BL *pp; infoptr_T *ip; @@ -1992,10 +1988,10 @@ static int ml_append_int(buf_T *buf, linenr_T lnum, char_u *line, colnr_T len, b if (len == 0) { len = (colnr_T)STRLEN(line) + 1; // space needed for the text } - space_needed = len + INDEX_SIZE; // space needed for text + index + int space_needed = len + (int)INDEX_SIZE; // space needed for text + index - mfp = buf->b_ml.ml_mfp; - page_size = mfp->mf_page_size; + memfile_T *mfp = buf->b_ml.ml_mfp; + int page_size = (int)mfp->mf_page_size; /* * find the data block containing the previous line @@ -2053,9 +2049,9 @@ static int ml_append_int(buf_T *buf, linenr_T lnum, char_u *line, colnr_T len, b /* * Insert new line in existing data block, or in data block allocated above. */ - dp->db_txt_start -= len; - dp->db_free -= space_needed; - ++(dp->db_line_count); + dp->db_txt_start -= (unsigned)len; + dp->db_free -= (unsigned)space_needed; + dp->db_line_count++; /* * move the text of the lines that follow to the front @@ -2067,17 +2063,17 @@ static int ml_append_int(buf_T *buf, linenr_T lnum, char_u *line, colnr_T len, b * This will become the character just after the new line. */ if (db_idx < 0) { - offset = dp->db_txt_end; + offset = (int)dp->db_txt_end; } else { offset = ((dp->db_index[db_idx]) & DB_INDEX_MASK); } memmove((char *)dp + dp->db_txt_start, (char *)dp + dp->db_txt_start + len, - (size_t)(offset - (dp->db_txt_start + len))); - for (i = line_count - 1; i > db_idx; --i) { - dp->db_index[i + 1] = dp->db_index[i] - len; + (size_t)offset - (dp->db_txt_start + (size_t)len)); + for (i = line_count - 1; i > db_idx; i--) { + dp->db_index[i + 1] = dp->db_index[i] - (unsigned)len; } - dp->db_index[db_idx + 1] = offset - len; + dp->db_index[db_idx + 1] = (unsigned)(offset - len); } else { // add line at the end dp->db_index[db_idx + 1] = dp->db_txt_start; } @@ -2107,7 +2103,7 @@ static int ml_append_int(buf_T *buf, linenr_T lnum, char_u *line, colnr_T len, b * The line counts in the pointer blocks have already been adjusted by * ml_find_line(). */ - long line_count_left, line_count_right; + int line_count_left, line_count_right; int page_count_left, page_count_right; bhdr_T *hp_left; bhdr_T *hp_right; @@ -2142,9 +2138,9 @@ static int ml_append_int(buf_T *buf, linenr_T lnum, char_u *line, colnr_T len, b in_left = false; // put new line in right block // space_needed does not change } else { - data_moved = ((dp->db_index[db_idx]) & DB_INDEX_MASK) - - dp->db_txt_start; - total_moved = data_moved + lines_moved * INDEX_SIZE; + data_moved = (int)(((dp->db_index[db_idx]) & DB_INDEX_MASK) - + dp->db_txt_start); + total_moved = data_moved + lines_moved * (int)INDEX_SIZE; if ((int)dp->db_free + total_moved >= space_needed) { in_left = true; // put new line in left block space_needed = total_moved; @@ -2155,7 +2151,7 @@ static int ml_append_int(buf_T *buf, linenr_T lnum, char_u *line, colnr_T len, b } } - page_count = ((space_needed + HEADER_SIZE) + page_size - 1) / page_size; + page_count = ((space_needed + (int)HEADER_SIZE) + page_size - 1) / page_size; hp_new = ml_new_data(mfp, newfile, page_count); if (db_idx < 0) { // left block is new hp_left = hp_new; @@ -2172,15 +2168,15 @@ static int ml_append_int(buf_T *buf, linenr_T lnum, char_u *line, colnr_T len, b dp_left = hp_left->bh_data; bnum_left = hp_left->bh_bnum; bnum_right = hp_right->bh_bnum; - page_count_left = hp_left->bh_page_count; - page_count_right = hp_right->bh_page_count; + page_count_left = (int)hp_left->bh_page_count; + page_count_right = (int)hp_right->bh_page_count; /* * May move the new line into the right/new block. */ if (!in_left) { - dp_right->db_txt_start -= len; - dp_right->db_free -= len + INDEX_SIZE; + dp_right->db_txt_start -= (unsigned)len; + dp_right->db_free -= (unsigned)len + (unsigned)INDEX_SIZE; dp_right->db_index[0] = dp_right->db_txt_start; if (mark) { dp_right->db_index[0] |= DB_MARKED; @@ -2196,21 +2192,21 @@ static int ml_append_int(buf_T *buf, linenr_T lnum, char_u *line, colnr_T len, b if (lines_moved) { /* */ - dp_right->db_txt_start -= data_moved; - dp_right->db_free -= total_moved; + dp_right->db_txt_start -= (unsigned)data_moved; + dp_right->db_free -= (unsigned)total_moved; memmove((char *)dp_right + dp_right->db_txt_start, (char *)dp_left + dp_left->db_txt_start, (size_t)data_moved); - offset = dp_right->db_txt_start - dp_left->db_txt_start; - dp_left->db_txt_start += data_moved; - dp_left->db_free += total_moved; + offset = (int)(dp_right->db_txt_start - dp_left->db_txt_start); + dp_left->db_txt_start += (unsigned)data_moved; + dp_left->db_free += (unsigned)total_moved; /* * update indexes in the new block */ for (to = line_count_right, from = db_idx + 1; - from < line_count_left; ++from, ++to) { - dp_right->db_index[to] = dp->db_index[from] + offset; + from < line_count_left; from++, to++) { + dp_right->db_index[to] = dp->db_index[from] + (unsigned)offset; } line_count_right += lines_moved; line_count_left -= lines_moved; @@ -2220,8 +2216,8 @@ static int ml_append_int(buf_T *buf, linenr_T lnum, char_u *line, colnr_T len, b * May move the new line into the left (old or new) block. */ if (in_left) { - dp_left->db_txt_start -= len; - dp_left->db_free -= len + INDEX_SIZE; + dp_left->db_txt_start -= (unsigned)len; + dp_left->db_free -= (unsigned)len + (unsigned)INDEX_SIZE; dp_left->db_index[line_count_left] = dp_left->db_txt_start; if (mark) { dp_left->db_index[line_count_left] |= DB_MARKED; @@ -2369,8 +2365,8 @@ static int ml_append_int(buf_T *buf, linenr_T lnum, char_u *line, colnr_T len, b memmove(&pp_new->pb_pointer[0], &pp->pb_pointer[pb_idx + 1], (size_t)(total_moved) * sizeof(PTR_EN)); - pp_new->pb_count = total_moved; - pp->pb_count -= total_moved - 1; + pp_new->pb_count = (uint16_t)total_moved; + pp->pb_count = (uint16_t)(pp->pb_count - (total_moved - 1)); pp->pb_pointer[pb_idx + 1].pe_bnum = bnum_right; pp->pb_pointer[pb_idx + 1].pe_line_count = line_count_right; pp->pb_pointer[pb_idx + 1].pe_page_count = page_count_right; @@ -2436,12 +2432,12 @@ void ml_add_deleted_len_buf(buf_T *buf, char_u *ptr, ssize_t len) return; } if (len == -1) { - len = STRLEN(ptr); + len = (ssize_t)STRLEN(ptr); } - curbuf->deleted_bytes += len + 1; - curbuf->deleted_bytes2 += len + 1; + curbuf->deleted_bytes += (size_t)len + 1; + curbuf->deleted_bytes2 += (size_t)len + 1; if (curbuf->update_need_codepoints) { - mb_utflen(ptr, len, &curbuf->deleted_codepoints, + mb_utflen(ptr, (size_t)len, &curbuf->deleted_codepoints, &curbuf->deleted_codeunits); curbuf->deleted_codepoints++; // NL char curbuf->deleted_codeunits++; @@ -2525,7 +2521,6 @@ static int ml_delete_int(buf_T *buf, linenr_T lnum, bool message) int count; // number of entries in block int idx; int stack_idx; - int text_start; int line_start; long line_size; int i; @@ -2568,17 +2563,16 @@ static int ml_delete_int(buf_T *buf, linenr_T lnum, bool message) dp = hp->bh_data; // compute line count before the delete - count = (long)(buf->b_ml.ml_locked_high) - - (long)(buf->b_ml.ml_locked_low) + 2; + count = buf->b_ml.ml_locked_high - buf->b_ml.ml_locked_low + 2; idx = lnum - buf->b_ml.ml_locked_low; --buf->b_ml.ml_line_count; line_start = ((dp->db_index[idx]) & DB_INDEX_MASK); if (idx == 0) { // first line in block, text at the end - line_size = dp->db_txt_end - line_start; + line_size = dp->db_txt_end - (unsigned)line_start; } else { - line_size = ((dp->db_index[idx - 1]) & DB_INDEX_MASK) - line_start; + line_size = ((dp->db_index[idx - 1]) & DB_INDEX_MASK) - (unsigned)line_start; } // Line should always have an NL char internally (represented as NUL), @@ -2636,24 +2630,20 @@ static int ml_delete_int(buf_T *buf, linenr_T lnum, bool message) } CHECK(stack_idx < 0, _("deleted block 1?")); } else { - /* - * delete the text by moving the next lines forwards - */ - text_start = dp->db_txt_start; + // delete the text by moving the next lines forwards + int text_start = (int)dp->db_txt_start; memmove((char *)dp + text_start + line_size, (char *)dp + text_start, (size_t)(line_start - text_start)); - /* - * delete the index by moving the next indexes backwards - * Adjust the indexes for the text movement. - */ - for (i = idx; i < count - 1; ++i) { - dp->db_index[i] = dp->db_index[i + 1] + line_size; + // delete the index by moving the next indexes backwards + // Adjust the indexes for the text movement. + for (i = idx; i < count - 1; i++) { + dp->db_index[i] = dp->db_index[i + 1] + (unsigned)line_size; } - dp->db_free += line_size + INDEX_SIZE; - dp->db_txt_start += line_size; - --(dp->db_line_count); + dp->db_free += (unsigned)line_size + (unsigned)INDEX_SIZE; + dp->db_txt_start += (unsigned)line_size; + dp->db_line_count--; /* * mark the block dirty and make sure it is in the file (for recovery) @@ -2823,9 +2813,9 @@ static void ml_flush_line(buf_T *buf) start = ((dp->db_index[idx]) & DB_INDEX_MASK); old_line = (char_u *)dp + start; if (idx == 0) { // line is last in block - old_len = dp->db_txt_end - start; + old_len = (int)dp->db_txt_end - start; } else { // text of previous line follows - old_len = (dp->db_index[idx - 1] & DB_INDEX_MASK) - start; + old_len = (int)(dp->db_index[idx - 1] & DB_INDEX_MASK) - start; } new_len = (colnr_T)STRLEN(new_line) + 1; extra = new_len - old_len; // negative if lines gets smaller @@ -2840,18 +2830,18 @@ static void ml_flush_line(buf_T *buf) // move text of following lines memmove((char *)dp + dp->db_txt_start - extra, (char *)dp + dp->db_txt_start, - (size_t)(start - dp->db_txt_start)); + (size_t)(start - (int)dp->db_txt_start)); // adjust pointers of this and following lines - for (i = idx + 1; i < count; ++i) { - dp->db_index[i] -= extra; + for (i = idx + 1; i < count; i++) { + dp->db_index[i] -= (unsigned)extra; } } - dp->db_index[idx] -= extra; + dp->db_index[idx] -= (unsigned)extra; // adjust free space - dp->db_free -= extra; - dp->db_txt_start -= extra; + dp->db_free -= (unsigned)extra; + dp->db_txt_start -= (unsigned)extra; // copy new line into the data block memmove(old_line - extra, new_line, (size_t)new_len); @@ -2866,7 +2856,7 @@ static void ml_flush_line(buf_T *buf) // Don't forget to copy the mark! // How about handling errors??? (void)ml_append_int(buf, lnum, new_line, new_len, false, - (dp->db_index[idx] & DB_MARKED)); + (int)(dp->db_index[idx] & DB_MARKED)); (void)ml_delete_int(buf, lnum, false); } } @@ -2886,8 +2876,8 @@ static bhdr_T *ml_new_data(memfile_T *mfp, bool negative, int page_count) bhdr_T *hp = mf_new(mfp, negative, (unsigned)page_count); DATA_BL *dp = hp->bh_data; dp->db_id = DATA_ID; - dp->db_txt_start = dp->db_txt_end = page_count * mfp->mf_page_size; - dp->db_free = dp->db_txt_start - HEADER_SIZE; + dp->db_txt_start = dp->db_txt_end = (unsigned)page_count * mfp->mf_page_size; + dp->db_free = dp->db_txt_start - (unsigned)HEADER_SIZE; dp->db_line_count = 0; return hp; @@ -2900,7 +2890,7 @@ static bhdr_T *ml_new_ptr(memfile_T *mfp) PTR_BL *pp = hp->bh_data; pp->pb_id = PTR_ID; pp->pb_count = 0; - pp->pb_count_max = (mfp->mf_page_size - sizeof(PTR_BL)) / sizeof(PTR_EN) + 1; + pp->pb_count_max = (uint16_t)((mfp->mf_page_size - sizeof(PTR_BL)) / sizeof(PTR_EN) + 1); return hp; } @@ -3000,7 +2990,7 @@ static bhdr_T *ml_find_line(buf_T *buf, linenr_T lnum, int action) * search downwards in the tree until a data block is found */ for (;;) { - if ((hp = mf_get(mfp, bnum, page_count)) == NULL) { + if ((hp = mf_get(mfp, bnum, (unsigned)page_count)) == NULL) { goto error_noblock; } @@ -3110,7 +3100,7 @@ static int ml_add_stack(buf_T *buf) CHECK(top > 0, _("Stack size increases")); // more than 5 levels??? buf->b_ml.ml_stack_size += STACK_INCR; - size_t new_size = sizeof(infoptr_T) * buf->b_ml.ml_stack_size; + size_t new_size = sizeof(infoptr_T) * (size_t)buf->b_ml.ml_stack_size; buf->b_ml.ml_stack = xrealloc(buf->b_ml.ml_stack, new_size); } @@ -3179,7 +3169,7 @@ int resolve_symlink(const char_u *fname, char_u *buf) return FAIL; } - ret = readlink((char *)tmp, (char *)buf, MAXPATHL - 1); + ret = (int)readlink((char *)tmp, (char *)buf, MAXPATHL - 1); if (ret <= 0) { if (errno == EINVAL || errno == ENOENT) { // Found non-symlink or not existing file, stop here. @@ -3294,9 +3284,9 @@ char_u *get_file_in_dir(char_u *fname, char_u *dname) } else { save_char = *tail; *tail = NUL; - t = (char_u *)concat_fnames((char *)fname, (char *)dname + 2, TRUE); - *tail = save_char; - retval = (char_u *)concat_fnames((char *)t, (char *)tail, TRUE); + t = (char_u *)concat_fnames((char *)fname, (char *)dname + 2, true); + *tail = (uint8_t)save_char; + retval = (char_u *)concat_fnames((char *)t, (char *)tail, true); xfree(t); } } else { @@ -3798,8 +3788,7 @@ void ml_setflags(buf_T *buf) if (hp->bh_bnum == 0) { b0p = hp->bh_data; b0p->b0_dirty = buf->b_changed ? B0_DIRTY : 0; - b0p->b0_flags = (b0p->b0_flags & ~B0_FF_MASK) - | (get_fileformat(buf) + 1); + b0p->b0_flags = (uint8_t)((b0p->b0_flags & ~B0_FF_MASK) | (uint8_t)(get_fileformat(buf) + 1)); add_b0_fenc(b0p, buf); hp->bh_flags |= BH_DIRTY; mf_sync(buf->b_ml.ml_mfp, MFS_ZERO); @@ -3887,7 +3876,7 @@ static void ml_updatechunk(buf_T *buf, linenr_T line, long len, int updtype) if (buf->b_ml.ml_usedchunks + 1 >= buf->b_ml.ml_numchunks) { buf->b_ml.ml_numchunks = buf->b_ml.ml_numchunks * 3 / 2; buf->b_ml.ml_chunksize = xrealloc(buf->b_ml.ml_chunksize, - sizeof(chunksize_T) * buf->b_ml.ml_numchunks); + sizeof(chunksize_T) * (size_t)buf->b_ml.ml_numchunks); } if (buf->b_ml.ml_chunksize[curix].mlcs_numlines >= MLCS_MAXL) { @@ -3898,8 +3887,7 @@ static void ml_updatechunk(buf_T *buf, linenr_T line, long len, int updtype) memmove(buf->b_ml.ml_chunksize + curix + 1, buf->b_ml.ml_chunksize + curix, - (buf->b_ml.ml_usedchunks - curix) * - sizeof(chunksize_T)); + (size_t)(buf->b_ml.ml_usedchunks - curix) * sizeof(chunksize_T)); // Compute length of first half of lines in the split chunk size = 0; linecnt = 0; @@ -3910,12 +3898,11 @@ static void ml_updatechunk(buf_T *buf, linenr_T line, long len, int updtype) return; } dp = hp->bh_data; - count = (long)(buf->b_ml.ml_locked_high) - - (long)(buf->b_ml.ml_locked_low) + 1; + count = buf->b_ml.ml_locked_high - buf->b_ml.ml_locked_low + 1; idx = curline - buf->b_ml.ml_locked_low; curline = buf->b_ml.ml_locked_high + 1; if (idx == 0) { // first line in block, text at the end - text_end = dp->db_txt_end; + text_end = (int)dp->db_txt_end; } else { text_end = ((dp->db_index[idx - 1]) & DB_INDEX_MASK); } @@ -3928,7 +3915,7 @@ static void ml_updatechunk(buf_T *buf, linenr_T line, long len, int updtype) idx = count - 1; linecnt += rest; } - size += text_end - ((dp->db_index[idx]) & DB_INDEX_MASK); + size += text_end - (int)((dp->db_index[idx]) & DB_INDEX_MASK); } buf->b_ml.ml_chunksize[curix].mlcs_numlines = linecnt; buf->b_ml.ml_chunksize[curix + 1].mlcs_numlines -= linecnt; @@ -3959,11 +3946,10 @@ static void ml_updatechunk(buf_T *buf, linenr_T line, long len, int updtype) } dp = hp->bh_data; if (dp->db_line_count == 1) { - rest = dp->db_txt_end - dp->db_txt_start; + rest = (int)(dp->db_txt_end - dp->db_txt_start); } else { - rest = - ((dp->db_index[dp->db_line_count - 2]) & DB_INDEX_MASK) - - dp->db_txt_start; + rest = (int)((dp->db_index[dp->db_line_count - 2]) & DB_INDEX_MASK) + - (int)dp->db_txt_start; } curchnk->mlcs_totalsize = rest; curchnk->mlcs_numlines = 1; @@ -3982,7 +3968,7 @@ static void ml_updatechunk(buf_T *buf, linenr_T line, long len, int updtype) } else if (curix == 0 && curchnk->mlcs_numlines <= 0) { buf->b_ml.ml_usedchunks--; memmove(buf->b_ml.ml_chunksize, buf->b_ml.ml_chunksize + 1, - buf->b_ml.ml_usedchunks * sizeof(chunksize_T)); + (size_t)buf->b_ml.ml_usedchunks * sizeof(chunksize_T)); return; } else if (curix == 0 || (curchnk->mlcs_numlines > 10 && (curchnk->mlcs_numlines + @@ -3998,8 +3984,7 @@ static void ml_updatechunk(buf_T *buf, linenr_T line, long len, int updtype) if (curix < buf->b_ml.ml_usedchunks) { memmove(buf->b_ml.ml_chunksize + curix, buf->b_ml.ml_chunksize + curix + 1, - (buf->b_ml.ml_usedchunks - curix) * - sizeof(chunksize_T)); + (size_t)(buf->b_ml.ml_usedchunks - curix) * sizeof(chunksize_T)); } return; } @@ -4049,7 +4034,7 @@ long ml_find_line_or_offset(buf_T *buf, linenr_T lnum, long *offp, bool no_ff) if (lnum == 0 || buf->b_ml.ml_line_lnum < lnum || !no_ff) { ml_flush_line(curbuf); } else if (can_cache && buf->b_ml.ml_line_offset > 0) { - return buf->b_ml.ml_line_offset; + return (long)buf->b_ml.ml_line_offset; } if (buf->b_ml.ml_usedchunks == -1 @@ -4071,7 +4056,8 @@ long ml_find_line_or_offset(buf_T *buf, linenr_T lnum, long *offp, bool no_ff) * special because it will never qualify */ curline = 1; - curix = size = 0; + curix = 0; + size = 0; while (curix < buf->b_ml.ml_usedchunks - 1 && ((lnum != 0 && lnum >= curline + buf->b_ml.ml_chunksize[curix].mlcs_numlines) @@ -4093,11 +4079,10 @@ long ml_find_line_or_offset(buf_T *buf, linenr_T lnum, long *offp, bool no_ff) return -1; } dp = hp->bh_data; - count = (long)(buf->b_ml.ml_locked_high) - - (long)(buf->b_ml.ml_locked_low) + 1; + count = buf->b_ml.ml_locked_high - buf->b_ml.ml_locked_low + 1; start_idx = idx = curline - buf->b_ml.ml_locked_low; if (idx == 0) { // first line in block, text at the end - text_end = dp->db_txt_end; + text_end = (int)dp->db_txt_end; } else { text_end = ((dp->db_index[idx - 1]) & DB_INDEX_MASK); } @@ -4123,7 +4108,7 @@ long ml_find_line_or_offset(buf_T *buf, linenr_T lnum, long *offp, bool no_ff) idx++; } } - len = text_end - ((dp->db_index[idx]) & DB_INDEX_MASK); + len = text_end - (int)((dp->db_index[idx]) & DB_INDEX_MASK); size += len; if (offset != 0 && size >= offset) { if (size + ffdos == offset) { @@ -4132,7 +4117,7 @@ long ml_find_line_or_offset(buf_T *buf, linenr_T lnum, long *offp, bool no_ff) *offp = offset - size + len; } else { *offp = offset - size + len - - (text_end - ((dp->db_index[idx - 1]) & DB_INDEX_MASK)); + - (text_end - (int)((dp->db_index[idx - 1]) & DB_INDEX_MASK)); } curline += idx - start_idx + extra; if (curline > buf->b_ml.ml_line_count) { @@ -4158,7 +4143,7 @@ long ml_find_line_or_offset(buf_T *buf, linenr_T lnum, long *offp, bool no_ff) } if (can_cache && size > 0) { - buf->b_ml.ml_line_offset = size; + buf->b_ml.ml_line_offset = (size_t)size; } return size; @@ -4168,14 +4153,13 @@ long ml_find_line_or_offset(buf_T *buf, linenr_T lnum, long *offp, bool no_ff) void goto_byte(long cnt) { long boff = cnt; - linenr_T lnum; ml_flush_line(curbuf); // cached line may be dirty setpcmark(); if (boff) { boff--; } - lnum = ml_find_line_or_offset(curbuf, (linenr_T)0, &boff, false); + linenr_T lnum = (linenr_T)ml_find_line_or_offset(curbuf, (linenr_T)0, &boff, false); if (lnum < 1) { // past the end curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count; curwin->w_curswant = MAXCOL; diff --git a/src/nvim/memory.c b/src/nvim/memory.c index 4d5cf047f9..5ae7f7bbe3 100644 --- a/src/nvim/memory.c +++ b/src/nvim/memory.c @@ -14,6 +14,7 @@ #include "nvim/eval.h" #include "nvim/highlight.h" #include "nvim/highlight_group.h" +#include "nvim/insexpand.h" #include "nvim/lua/executor.h" #include "nvim/mapping.h" #include "nvim/memfile.h" @@ -545,7 +546,7 @@ void arena_start(Arena *arena, ArenaMem *reuse_blk) /// Finnish the allocations in an arena. /// -/// This does not immedately free the memory, but leaves existing allocated +/// This does not immediately free the memory, but leaves existing allocated /// objects valid, and returns an opaque ArenaMem handle, which can be used to /// free the allocations using `arena_mem_free`, when the objects allocated /// from the arena are not needed anymore. @@ -710,6 +711,7 @@ void free_all_mem(void) free_search_patterns(); free_old_sub(); free_last_insert(); + free_insexpand_stuff(); free_prev_shellcmd(); free_regexp_stuff(); free_tag_stuff(); diff --git a/src/nvim/menu.c b/src/nvim/menu.c index 018c62d604..febb66081a 100644 --- a/src/nvim/menu.c +++ b/src/nvim/menu.c @@ -121,7 +121,7 @@ void ex_menu(exarg_T *eap) } if (ascii_iswhite(*p)) { for (i = 0; i < MENUDEPTH && !ascii_iswhite(*arg); i++) { - pri_tab[i] = getdigits_long((char_u **)&arg, false, 0); + pri_tab[i] = getdigits_long(&arg, false, 0); if (pri_tab[i] == 0) { pri_tab[i] = 500; } diff --git a/src/nvim/message.c b/src/nvim/message.c index 2c96613bb3..621a9212df 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -493,6 +493,7 @@ int smsg(const char *s, ...) va_start(arglist, s); vim_vsnprintf((char *)IObuff, IOSIZE, s, arglist); va_end(arglist); + return msg((char *)IObuff); } @@ -1389,7 +1390,7 @@ void msg_start(void) need_fileinfo = false; } - bool no_msg_area = !ui_has(kUIMessages) && p_ch < 1; + const bool no_msg_area = !ui_has_messages(); if (need_clr_eos || (no_msg_area && redrawing_cmdline)) { // Halfway an ":echo" command and getting an (error) message: clear @@ -2503,6 +2504,7 @@ static void store_sb_text(char_u **sb_str, char_u *s, int attr, int *sb_col, int if (do_clear_sb_text == SB_CLEAR_ALL || do_clear_sb_text == SB_CLEAR_CMDLINE_DONE) { clear_sb_text(do_clear_sb_text == SB_CLEAR_ALL); + msg_sb_eol(); // prevent messages from overlapping do_clear_sb_text = SB_CLEAR_NONE; } @@ -2537,14 +2539,44 @@ void may_clear_sb_text(void) do_clear_sb_text = SB_CLEAR_ALL; } -/// Starting to edit the command line, do not clear messages now. +/// Starting to edit the command line: do not clear messages now. void sb_text_start_cmdline(void) { + if (do_clear_sb_text == SB_CLEAR_CMDLINE_BUSY) { + // Invoking command line recursively: the previous-level command line + // doesn't need to be remembered as it will be redrawn when returning + // to that level. + sb_text_restart_cmdline(); + } else { + msg_sb_eol(); + do_clear_sb_text = SB_CLEAR_CMDLINE_BUSY; + } +} + +/// Redrawing the command line: clear the last unfinished line. +void sb_text_restart_cmdline(void) +{ + // Needed when returning from nested command line. do_clear_sb_text = SB_CLEAR_CMDLINE_BUSY; - msg_sb_eol(); + + if (last_msgchunk == NULL || last_msgchunk->sb_eol) { + // No unfinished line: don't clear anything. + return; + } + + msgchunk_T *tofree = msg_sb_start(last_msgchunk); + last_msgchunk = tofree->sb_prev; + if (last_msgchunk != NULL) { + last_msgchunk->sb_next = NULL; + } + while (tofree != NULL) { + msgchunk_T *tofree_next = tofree->sb_next; + xfree(tofree); + tofree = tofree_next; + } } -/// Ending to edit the command line. Clear old lines but the last one later. +/// Ending to edit the command line: clear old lines but the last one later. void sb_text_end_cmdline(void) { do_clear_sb_text = SB_CLEAR_CMDLINE_DONE; @@ -2564,7 +2596,7 @@ void clear_sb_text(int all) if (last_msgchunk == NULL) { return; } - lastp = &last_msgchunk->sb_prev; + lastp = &msg_sb_start(last_msgchunk)->sb_prev; } while (*lastp != NULL) { @@ -3081,7 +3113,7 @@ void msg_clr_eos_force(void) msg_row = msg_grid_pos; } - if (p_ch > 0) { + if (ui_has_messages()) { grid_fill(&msg_grid_adj, msg_row, msg_row + 1, msg_startcol, msg_endcol, ' ', ' ', HL_ATTR(HLF_MSG)); grid_fill(&msg_grid_adj, msg_row + 1, Rows, 0, Columns, diff --git a/src/nvim/move.c b/src/nvim/move.c index bd68ad6f97..6d4eb8ef49 100644 --- a/src/nvim/move.c +++ b/src/nvim/move.c @@ -2271,7 +2271,13 @@ void do_check_cursorbind(void) int restart_edit_save = restart_edit; restart_edit = true; check_cursor(); - validate_cursor(); + + // Avoid a scroll here for the cursor position, 'scrollbind' is + // more important. + if (!curwin->w_p_scb) { + validate_cursor(); + } + restart_edit = restart_edit_save; } // Correct cursor for multi-byte character. diff --git a/src/nvim/msgpack_rpc/channel.c b/src/nvim/msgpack_rpc/channel.c index de01443313..1ca68979f4 100644 --- a/src/nvim/msgpack_rpc/channel.c +++ b/src/nvim/msgpack_rpc/channel.c @@ -239,7 +239,14 @@ static void parse_msgpack(Channel *channel) { Unpacker *p = channel->rpc.unpacker; while (unpacker_advance(p)) { - if (p->type == kMessageTypeResponse) { + if (p->type == kMessageTypeRedrawEvent) { + if (p->grid_line_event) { + ui_client_event_raw_line(p->grid_line_event); + } else if (p->ui_handler.fn != NULL && p->result.type == kObjectTypeArray) { + p->ui_handler.fn(p->result.data.array); + arena_mem_free(arena_finish(&p->arena), &p->reuse_blk); + } + } else if (p->type == kMessageTypeResponse) { ChannelCallFrame *frame = kv_last(channel->rpc.call_stack); if (p->request_id != frame->request_id) { char buf[256]; diff --git a/src/nvim/msgpack_rpc/unpacker.c b/src/nvim/msgpack_rpc/unpacker.c index 26c1843026..c8e9fdd4c3 100644 --- a/src/nvim/msgpack_rpc/unpacker.c +++ b/src/nvim/msgpack_rpc/unpacker.c @@ -6,6 +6,7 @@ #include "nvim/memory.h" #include "nvim/msgpack_rpc/helpers.h" #include "nvim/msgpack_rpc/unpacker.h" +#include "nvim/ui_client.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "msgpack_rpc/unpacker.c.generated.h" @@ -280,10 +281,20 @@ error: // is relatively small, just ~10 bytes + the method name. Thus we can simply refuse // to advance the stream beyond the header until it can be parsed in its entirety. // -// Of course, later on, we want to specialize state 2 into sub-states depending +// Later on, we want to specialize state 2 into more sub-states depending // on the specific method. "nvim_exec_lua" should just decode direct into lua -// objects, and "redraw/grid_line" should use a hand-rolled decoder to avoid -// a blizzard of small objects for each screen cell. +// objects. For the moment "redraw/grid_line" uses a hand-rolled decoder, +// to avoid a blizzard of small objects for each screen cell. +// +// <0>[2, "redraw", <10>[{11}["method", <12>[args], <12>[args], ...], <11>[...], ...]] +// +// Where [args] gets unpacked as an Array. Note: first {11} is not saved as a state. +// +// When method is "grid_line", we furthermore decode a cell at a time like: +// +// <0>[2, "redraw", <10>[{11}["grid_line", <13>[g, r, c, [<14>[cell], <14>[cell], ...]], ...], <11>[...], ...]] +// +// where [cell] is [char, repeat, attr], where 'repeat' and 'attr' is optional bool unpacker_advance(Unpacker *p) { @@ -292,8 +303,27 @@ bool unpacker_advance(Unpacker *p) if (!unpacker_parse_header(p)) { return false; } - p->state = p->type == kMessageTypeResponse ? 1 : 2; - arena_start(&p->arena, &p->reuse_blk); + if (p->type == kMessageTypeNotification && p->handler.fn == handle_ui_client_redraw) { + p->type = kMessageTypeRedrawEvent; + p->state = 10; + } else { + p->state = p->type == kMessageTypeResponse ? 1 : 2; + arena_start(&p->arena, &p->reuse_blk); + } + } + + if (p->state >= 10 && p->state != 12) { + if (!unpacker_parse_redraw(p)) { + return false; + } + + if (p->state == 14) { + // grid_line event already unpacked + goto done; + } else { + // unpack other ui events using mpack_parse() + arena_start(&p->arena, &p->reuse_blk); + } } int result; @@ -310,13 +340,173 @@ rerun: return false; } - if (p->state == 1) { +done: + switch (p->state) { + case 1: p->error = p->result; p->state = 2; goto rerun; - } else { - assert(p->state == 2); + case 2: p->state = 0; + return true; + case 12: + case 14: + p->ncalls--; + if (p->ncalls > 0) { + p->state = (p->state == 14) ? 13 : 12; + } else if (p->nevents > 0) { + p->state = 11; + } else { + p->state = 0; + } + return true; + default: + abort(); + } +} + +bool unpacker_parse_redraw(Unpacker *p) +{ + mpack_token_t tok; + int result; + + const char *data = p->read_ptr; + size_t size = p->read_size; + GridLineEvent *g = p->grid_line_event; + +#define NEXT_TYPE(tok, typ) \ + result = mpack_rtoken(&data, &size, &tok); \ + if (result == MPACK_EOF) { \ + return false; \ + } else if (result || (tok.type != typ \ + && !(typ == MPACK_TOKEN_STR && tok.type == MPACK_TOKEN_BIN) \ + && !(typ == MPACK_TOKEN_SINT && tok.type == MPACK_TOKEN_UINT))) { \ + p->state = -1; \ + return false; \ + } + +redo: + switch (p->state) { + case 10: + NEXT_TYPE(tok, MPACK_TOKEN_ARRAY); + p->nevents = (int)tok.length; + FALLTHROUGH; + + case 11: + NEXT_TYPE(tok, MPACK_TOKEN_ARRAY); + p->ncalls = (int)tok.length; + + if (p->ncalls-- == 0) { + p->state = -1; + return false; + } + + NEXT_TYPE(tok, MPACK_TOKEN_STR); + if (tok.length > size) { + return false; + } + + p->ui_handler = ui_client_get_redraw_handler(data, tok.length, NULL); + data += tok.length; + size -= tok.length; + + p->nevents--; + p->read_ptr = data; + p->read_size = size; + if (p->ui_handler.fn != ui_client_event_grid_line) { + p->state = 12; + if (p->grid_line_event) { + arena_mem_free(arena_finish(&p->arena), &p->reuse_blk); + p->grid_line_event = NULL; + } + return true; + } else { + p->state = 13; + arena_start(&p->arena, &p->reuse_blk); + p->grid_line_event = arena_alloc(&p->arena, sizeof *p->grid_line_event, true); + g = p->grid_line_event; + } + FALLTHROUGH; + + case 13: + NEXT_TYPE(tok, MPACK_TOKEN_ARRAY); + int eventarrsize = (int)tok.length; + if (eventarrsize != 4) { + p->state = -1; + return false; + } + + for (int i = 0; i < 3; i++) { + NEXT_TYPE(tok, MPACK_TOKEN_UINT); + g->args[i] = (int)tok.data.value.lo; + } + + NEXT_TYPE(tok, MPACK_TOKEN_ARRAY); + g->ncells = (int)tok.length; + g->icell = 0; + g->coloff = 0; + g->cur_attr = -1; + + p->read_ptr = data; + p->read_size = size; + p->state = 14; + FALLTHROUGH; + + case 14: + assert(g->icell < g->ncells); + + NEXT_TYPE(tok, MPACK_TOKEN_ARRAY); + int cellarrsize = (int)tok.length; + if (cellarrsize < 1 || cellarrsize > 3) { + p->state = -1; + return false; + } + + NEXT_TYPE(tok, MPACK_TOKEN_STR); + if (tok.length > size) { + return false; + } + + const char *cellbuf = data; + size_t cellsize = tok.length; + data += cellsize; + size -= cellsize; + + if (cellarrsize >= 2) { + NEXT_TYPE(tok, MPACK_TOKEN_SINT); + g->cur_attr = (int)tok.data.value.lo; + } + + int repeat = 1; + if (cellarrsize >= 3) { + NEXT_TYPE(tok, MPACK_TOKEN_UINT); + repeat = (int)tok.data.value.lo; + } + + g->clear_width = 0; + if (g->icell == g->ncells - 1 && cellsize == 1 && cellbuf[0] == ' ' && repeat > 1) { + g->clear_width = repeat; + } else { + for (int r = 0; r < repeat; r++) { + if (g->coloff >= (int)grid_line_buf_size) { + p->state = -1; + return false; + } + memcpy(grid_line_buf_char[g->coloff], cellbuf, cellsize); + grid_line_buf_char[g->coloff][cellsize] = NUL; + grid_line_buf_attr[g->coloff++] = g->cur_attr; + } + } + + g->icell++; + p->read_ptr = data; + p->read_size = size; + if (g->icell == g->ncells) { + return true; + } + goto redo; + + default: + abort(); } - return true; } diff --git a/src/nvim/msgpack_rpc/unpacker.h b/src/nvim/msgpack_rpc/unpacker.h index e0dc6f0a68..f39439be63 100644 --- a/src/nvim/msgpack_rpc/unpacker.h +++ b/src/nvim/msgpack_rpc/unpacker.h @@ -32,8 +32,13 @@ struct Unpacker { Error unpack_error; Arena arena; - // one lenght free-list of reusable blocks + // one length free-list of reusable blocks ArenaMem reuse_blk; + + int nevents; + int ncalls; + UIClientHandler ui_handler; + GridLineEvent *grid_line_event; }; // unrecovareble error. unpack_error should be set! diff --git a/src/nvim/normal.c b/src/nvim/normal.c index b675abfb7d..2d752eade7 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -1908,6 +1908,13 @@ bool do_mouse(oparg_T *oap, int c, int dir, long count, bool fixindent) StlClickDefinition *click_defs = in_status_line ? wp->w_status_click_defs : wp->w_winbar_click_defs; + if (in_status_line && global_stl_height() > 0) { + // global statusline is displayed for the current window, + // and spans the whole screen. + click_defs = curwin->w_status_click_defs; + click_col = mouse_col; + } + if (click_defs != NULL) { switch (click_defs[click_col].type) { case kStlClickDisabled: @@ -2474,7 +2481,7 @@ size_t find_ident_at_pos(win_T *wp, linenr_T lnum, colnr_T startcol, char_u **te col = 0; // Search for point of changing multibyte character class. this_class = mb_get_class(ptr); - while (ptr[col] != NUL + while (ptr[col] != NUL // -V781 && ((i == 0 ? mb_get_class(ptr + col) == this_class : mb_get_class(ptr + col) != 0) @@ -2803,7 +2810,7 @@ void pop_showcmd(void) static void display_showcmd(void) { - if (p_ch < 1 && !ui_has(kUIMessages)) { + if (!ui_has_messages()) { return; } @@ -2915,10 +2922,11 @@ void check_scrollbind(linenr_T topline_diff, long leftcol_diff) FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { curwin = wp; curbuf = curwin->w_buffer; - // skip original window and windows with 'noscrollbind' + // skip original window and windows with 'noscrollbind' if (curwin == old_curwin || !curwin->w_p_scb) { continue; } + // do the vertical scroll if (want_ver) { if (old_curwin->w_p_diff && curwin->w_p_diff) { @@ -3478,7 +3486,8 @@ void scroll_redraw(int up, long count) redraw_later(curwin, VALID); } -/// Get the count specified after a 'z' command. +/// Get the count specified after a 'z' command. Only the 'z<CR>', 'zl', 'zh', +/// 'z<Left>', and 'z<Right>' commands accept a count after 'z'. /// @return true to process the 'z' command and false to skip it. static bool nv_z_get_count(cmdarg_T *cap, int *nchar_arg) { @@ -5094,6 +5103,7 @@ static void nv_brackets(cmdarg_T *cap) } else if (cap->nchar == '\'' || cap->nchar == '`') { // "['", "[`", "]'" and "]`": jump to next mark fmark_T *fm = pos_to_mark(curbuf, NULL, curwin->w_cursor); + assert(fm != NULL); fmark_T *prev_fm; for (n = cap->count1; n > 0; n--) { prev_fm = fm; diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 3602eb2a3e..9a44c36d44 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -928,6 +928,7 @@ int do_record(int c) { char_u *p; static int regname; + static bool changed_cmdheight = false; yankreg_T *old_y_previous; int retval; @@ -941,6 +942,15 @@ int do_record(int c) showmode(); regname = c; retval = OK; + + if (!ui_has_messages()) { + // Enable macro indicator temporarily + set_option_value("ch", 1L, NULL, 0); + update_screen(VALID); + + changed_cmdheight = true; + } + apply_autocmds(EVENT_RECORDINGENTER, NULL, NULL, false, curbuf); } } else { // stop recording @@ -986,6 +996,12 @@ int do_record(int c) y_previous = old_y_previous; } + + if (changed_cmdheight) { + // Restore cmdheight + set_option_value("ch", 0L, NULL, 0); + redraw_all_later(CLEAR); + } } return retval; } @@ -1050,7 +1066,7 @@ static int execreg_lastc = NUL; /// with a \. Lines that start with a comment "\ character are ignored. /// @returns the concatenated line. The index of the line that should be /// processed next is returned in idx. -static char_u *execreg_line_continuation(char_u **lines, size_t *idx) +static char_u *execreg_line_continuation(char **lines, size_t *idx) { size_t i = *idx; assert(i > 0); @@ -1065,7 +1081,7 @@ static char_u *execreg_line_continuation(char_u **lines, size_t *idx) // Any line not starting with \ or "\ is the start of the // command. while (--i > 0) { - p = (char_u *)skipwhite((char *)lines[i]); + p = (char_u *)skipwhite(lines[i]); if (*p != '\\' && (p[0] != '"' || p[1] != '\\' || p[2] != ' ')) { break; } @@ -1073,9 +1089,9 @@ static char_u *execreg_line_continuation(char_u **lines, size_t *idx) const size_t cmd_start = i; // join all the lines - ga_concat(&ga, (char *)lines[cmd_start]); + ga_concat(&ga, lines[cmd_start]); for (size_t j = cmd_start + 1; j <= cmd_end; j++) { - p = (char_u *)skipwhite((char *)lines[j]); + p = (char_u *)skipwhite(lines[j]); if (*p == '\\') { // Adjust the growsize to the current length to // speed up concatenating many lines. @@ -1188,7 +1204,7 @@ int do_execreg(int regname, int colon, int addcr, int silent) if (colon && i > 0) { p = (char_u *)skipwhite((char *)str); if (*p == '\\' || (p[0] == '"' && p[1] == '\\' && p[2] == ' ')) { - str = execreg_line_continuation((char_u **)reg->y_array, &i); + str = execreg_line_continuation(reg->y_array, &i); free_str = true; } } @@ -1945,7 +1961,7 @@ static void mb_adjust_opend(oparg_T *oap) { if (oap->inclusive) { char *p = (char *)ml_get(oap->end.lnum); - oap->end.col += mb_tail_off(p, p + oap->end.col); + oap->end.col += utf_cp_tail_off(p, p + oap->end.col); } } @@ -2936,7 +2952,7 @@ static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append) xfree(reg->y_array); } - if (message && (p_ch > 0 || ui_has(kUIMessages))) { // Display message about yank? + if (message) { // Display message about yank? if (yank_type == kMTCharWise && yanklines == 1) { yanklines = 0; } @@ -4480,6 +4496,9 @@ static void op_format(oparg_T *oap, int keep_cursor) if (keep_cursor) { curwin->w_cursor = saved_cursor; saved_cursor.lnum = 0; + + // formatting may have made the cursor position invalid + check_cursor(); } if (oap->is_VIsual) { @@ -5017,7 +5036,7 @@ void op_addsub(oparg_T *oap, linenr_T Prenum1, bool g_cmd) linenr_T amount = Prenum1; // do_addsub() might trigger re-evaluation of 'foldexpr' halfway, when the - // buffer is not completly updated yet. Postpone updating folds until before + // buffer is not completely updated yet. Postpone updating folds until before // the call to changed_lines(). disable_fold_update++; diff --git a/src/nvim/option.c b/src/nvim/option.c index cfd8248eb6..ba77d12a3d 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -37,6 +37,7 @@ #include "nvim/edit.h" #include "nvim/eval.h" #include "nvim/eval/typval.h" +#include "nvim/eval/vars.h" #include "nvim/ex_cmds2.h" #include "nvim/ex_docmd.h" #include "nvim/ex_getln.h" @@ -49,6 +50,7 @@ #include "nvim/highlight.h" #include "nvim/highlight_group.h" #include "nvim/indent_c.h" +#include "nvim/insexpand.h" #include "nvim/keycodes.h" #include "nvim/macros.h" #include "nvim/mapping.h" @@ -1325,7 +1327,6 @@ int do_set(char *arg, int opt_flags) char *saved_newval = NULL; unsigned newlen; int comma; - bool new_value_alloced = false; // new string option was allocated // When using ":set opt=val" for a global option // with a local value the local value will be @@ -1365,7 +1366,6 @@ int do_set(char *arg, int opt_flags) // default value was already expanded, only // required when an environment variable was set // later - new_value_alloced = true; if (newval == NULL) { newval = empty_option; } else if (!(options[opt_idx].flags & P_NO_DEF_EXP)) { @@ -1379,7 +1379,6 @@ int do_set(char *arg, int opt_flags) } } else if (nextchar == '<') { // set to global val newval = vim_strsave(*(char_u **)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL)); - new_value_alloced = true; } else { arg++; // jump to after the '=' or ':' @@ -1624,7 +1623,6 @@ int do_set(char *arg, int opt_flags) if (save_arg != NULL) { // number for 'whichwrap' arg = (char *)save_arg; } - new_value_alloced = true; } // Set the new value. @@ -1659,8 +1657,7 @@ int do_set(char *arg, int opt_flags) // for ":set" on local options. Note: when setting // 'syntax' or 'filetype' autocommands may be // triggered that can cause havoc. - errmsg = did_set_string_option(opt_idx, (char_u **)varp, - new_value_alloced, oldval, + errmsg = did_set_string_option(opt_idx, (char_u **)varp, oldval, errbuf, sizeof(errbuf), opt_flags, &value_checked); @@ -2299,9 +2296,9 @@ static char *set_string_option(const int opt_idx, const char *const value, const char *const saved_newval = xstrdup(s); int value_checked = false; - char *const r = did_set_string_option(opt_idx, (char_u **)varp, true, - (char_u *)oldval, - NULL, 0, opt_flags, &value_checked); + char *const r = did_set_string_option(opt_idx, (char_u **)varp, (char_u *)oldval, + NULL, 0, + opt_flags, &value_checked); if (r == NULL) { did_set_option(opt_idx, opt_flags, true, value_checked); } @@ -2430,19 +2427,18 @@ static char *check_mousescroll(char *string) } /// Handle string options that need some action to perform when changed. +/// The new value must be allocated. /// Returns NULL for success, or an error message for an error. /// /// @param opt_idx index in options[] table /// @param varp pointer to the option variable -/// @param new_value_alloced new value was allocated /// @param oldval previous value of the option /// @param errbuf buffer for errors, or NULL /// @param errbuflen length of errors buffer /// @param opt_flags OPT_LOCAL and/or OPT_GLOBAL /// @param value_checked value was checked to be safe, no need to set P_INSECURE -static char *did_set_string_option(int opt_idx, char_u **varp, bool new_value_alloced, - char_u *oldval, char *errbuf, size_t errbuflen, int opt_flags, - int *value_checked) +static char *did_set_string_option(int opt_idx, char_u **varp, char_u *oldval, char *errbuf, + size_t errbuflen, int opt_flags, int *value_checked) { char *errmsg = NULL; char_u *s, *p; @@ -3021,7 +3017,7 @@ ambw_end: } // add / remove window bars for 'winbar' if (gvarp == (char_u **)&p_wbr) { - set_winbar(); + set_winbar(true); } } else if (gvarp == &p_cpt) { // check if it is a valid value for 'complete' -- Acevedo @@ -3097,11 +3093,8 @@ ambw_end: (char **)&p, REPTERM_FROM_PART | REPTERM_DO_LT, NULL, CPO_TO_CPO_FLAGS); if (p != NULL) { - if (new_value_alloced) { - free_string_option(p_pt); - } + free_string_option(p_pt); p_pt = p; - new_value_alloced = true; } } } else if (varp == &p_bs) { // 'backspace' @@ -3344,9 +3337,7 @@ ambw_end: * If error detected, restore the previous value. */ if (errmsg != NULL) { - if (new_value_alloced) { - free_string_option(*varp); - } + free_string_option(*varp); *varp = oldval; /* * When resetting some values, need to act on it. @@ -3363,11 +3354,7 @@ ambw_end: if (free_oldval) { free_string_option(oldval); } - if (new_value_alloced) { - options[opt_idx].flags |= P_ALLOCED; - } else { - options[opt_idx].flags &= ~P_ALLOCED; - } + options[opt_idx].flags |= P_ALLOCED; if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0 && ((int)options[opt_idx].indir & PV_BOTH)) { @@ -5045,28 +5032,29 @@ static int findoption(const char *const arg) /// @param stringval NULL when only checking existence /// /// @returns: -/// Toggle option: 2, *numval gets value. -/// Number option: 1, *numval gets value. -/// String option: 0, *stringval gets allocated string. -/// Hidden Number or Toggle option: -1. -/// hidden String option: -2. -/// unknown option: -3. -int get_option_value(const char *name, long *numval, char **stringval, int opt_flags) +/// Number option: gov_number, *numval gets value. +/// Tottle option: gov_bool, *numval gets value. +/// String option: gov_string, *stringval gets allocated string. +/// Hidden Number option: gov_hidden_number. +/// Hidden Toggle option: gov_hidden_bool. +/// Hidden String option: gov_hidden_string. +/// Unknown option: gov_unknown. +getoption_T get_option_value(const char *name, long *numval, char **stringval, int opt_flags) { if (get_tty_option(name, stringval)) { - return 0; + return gov_string; } int opt_idx = findoption(name); - if (opt_idx < 0) { // Unknown option. - return -3; + if (opt_idx < 0) { // option not in the table + return gov_unknown; } char_u *varp = get_varp_scope(&(options[opt_idx]), opt_flags); if (options[opt_idx].flags & P_STRING) { if (varp == NULL) { // hidden option - return -2; + return gov_hidden_string; } if (stringval != NULL) { if ((char_u **)varp == &p_pt) { // 'pastetoggle' @@ -5075,26 +5063,24 @@ int get_option_value(const char *name, long *numval, char **stringval, int opt_f *stringval = xstrdup(*(char **)(varp)); } } - return 0; + return gov_string; } if (varp == NULL) { // hidden option - return -1; + return (options[opt_idx].flags & P_NUM) ? gov_hidden_number : gov_hidden_bool; } if (options[opt_idx].flags & P_NUM) { *numval = *(long *)varp; - return 1; - } - - // Special case: 'modified' is b_changed, but we also want to consider - // it set when 'ff' or 'fenc' changed. - if ((int *)varp == &curbuf->b_changed) { - *numval = curbufIsChanged(); } else { - *numval = (long)*(int *)varp; // NOLINT(whitespace/cast) + // Special case: 'modified' is b_changed, but we also want to consider + // it set when 'ff' or 'fenc' changed. + if ((int *)varp == &curbuf->b_changed) { + *numval = curbufIsChanged(); + } else { + *numval = (long)(*(int *)varp); + } } - - return 2; + return (options[opt_idx].flags & P_NUM) ? gov_number : gov_bool; } // Returns the option attributes and its value. Unlike the above function it @@ -5301,6 +5287,14 @@ char *set_option_value(const char *const name, const long number, const char *co return NULL; } +/// Return true if "name" is a string option. +/// Returns false if option "name" does not exist. +bool is_string_option(const char *name) +{ + int idx = findoption(name); + return idx >= 0 && (options[idx].flags & P_STRING); +} + // Translate a string like "t_xx", "<t_xx>" or "<S-Tab>" to a key number. // When "has_lt" is true there is a '<' before "*arg_arg". // Returns 0 when the key is not recognized. @@ -6457,6 +6451,7 @@ void didset_window_options(win_T *wp) set_chars_option(wp, &wp->w_p_lcs, true); parse_winhl_opt(wp); // sets w_hl_needs_update also for w_p_winbl check_blending(wp); + set_winbar_win(wp, false); wp->w_grid_alloc.blending = wp->w_p_winbl > 0; } @@ -6972,7 +6967,7 @@ void set_context_in_set_cmd(expand_T *xp, char_u *arg, int opt_flags) } } -int ExpandSettings(expand_T *xp, regmatch_T *regmatch, int *num_file, char_u ***file) +int ExpandSettings(expand_T *xp, regmatch_T *regmatch, int *num_file, char ***file) { int num_normal = 0; // Nr of matching non-term-code settings int match; @@ -6994,7 +6989,7 @@ int ExpandSettings(expand_T *xp, regmatch_T *regmatch, int *num_file, char_u *** if (loop == 0) { num_normal++; } else { - (*file)[count++] = vim_strsave((char_u *)names[match]); + (*file)[count++] = xstrdup(names[match]); } } } @@ -7021,7 +7016,7 @@ int ExpandSettings(expand_T *xp, regmatch_T *regmatch, int *num_file, char_u *** if (loop == 0) { num_normal++; } else { - (*file)[count++] = vim_strsave(str); + (*file)[count++] = (char *)vim_strsave(str); } } } @@ -7032,18 +7027,18 @@ int ExpandSettings(expand_T *xp, regmatch_T *regmatch, int *num_file, char_u *** } else { return OK; } - *file = (char_u **)xmalloc((size_t)(*num_file) * sizeof(char_u *)); + *file = xmalloc((size_t)(*num_file) * sizeof(char_u *)); } } return OK; } -void ExpandOldSetting(int *num_file, char_u ***file) +void ExpandOldSetting(int *num_file, char ***file) { char_u *var = NULL; *num_file = 0; - *file = (char_u **)xmalloc(sizeof(char_u *)); + *file = xmalloc(sizeof(char_u *)); /* * For a terminal key code expand_option_idx is < 0. @@ -7078,7 +7073,7 @@ void ExpandOldSetting(int *num_file, char_u ***file) } #endif - *file[0] = buf; + *file[0] = (char *)buf; *num_file = 1; } @@ -7890,16 +7885,15 @@ static bool briopt_check(win_T *wp) bool bri_sbr = false; int bri_list = 0; - char_u *p = wp->w_p_briopt; - while (*p != NUL) - { + char *p = (char *)wp->w_p_briopt; + while (*p != NUL) { if (STRNCMP(p, "shift:", 6) == 0 && ((p[6] == '-' && ascii_isdigit(p[7])) || ascii_isdigit(p[6]))) { p += 6; - bri_shift = getdigits_int((char **)&p, true, 0); + bri_shift = getdigits_int(&p, true, 0); } else if (STRNCMP(p, "min:", 4) == 0 && ascii_isdigit(p[4])) { p += 4; - bri_min = getdigits_int((char **)&p, true, 0); + bri_min = getdigits_int(&p, true, 0); } else if (STRNCMP(p, "sbr", 3) == 0) { p += 3; bri_sbr = true; diff --git a/src/nvim/option.h b/src/nvim/option.h index 9321dd5454..a5a57cc66d 100644 --- a/src/nvim/option.h +++ b/src/nvim/option.h @@ -3,6 +3,17 @@ #include "nvim/ex_cmds_defs.h" // for exarg_T +/// Returned by get_option_value(). +typedef enum { + gov_unknown, + gov_bool, + gov_number, + gov_string, + gov_hidden_bool, + gov_hidden_number, + gov_hidden_string, +} getoption_T; + // flags for buf_copy_options() #define BCO_ENTER 1 // going to enter the buffer #define BCO_ALWAYS 2 // always copy the options diff --git a/src/nvim/options.lua b/src/nvim/options.lua index 37f3770506..fd4661f9f9 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -2063,7 +2063,7 @@ return { type='string', list='onecomma', scope={'global'}, deny_duplicates=true, varname='p_ssop', - defaults={if_true="blank,buffers,curdir,folds,help,tabpages,winsize"} + defaults={if_true="blank,buffers,curdir,folds,help,tabpages,winsize,terminal"} }, { full_name='shada', abbreviation='sd', diff --git a/src/nvim/os/shell.c b/src/nvim/os/shell.c index 9283ea2e42..8f2018c1f4 100644 --- a/src/nvim/os/shell.c +++ b/src/nvim/os/shell.c @@ -47,30 +47,30 @@ typedef struct { # include "os/shell.c.generated.h" #endif -static void save_patterns(int num_pat, char_u **pat, int *num_file, char_u ***file) +static void save_patterns(int num_pat, char **pat, int *num_file, char ***file) { *file = xmalloc((size_t)num_pat * sizeof(char_u *)); for (int i = 0; i < num_pat; i++) { - char_u *s = vim_strsave(pat[i]); + char_u *s = vim_strsave((char_u *)pat[i]); // Be compatible with expand_filename(): halve the number of // backslashes. backslash_halve(s); - (*file)[i] = s; + (*file)[i] = (char *)s; } *num_file = num_pat; } -static bool have_wildcard(int num, char_u **file) +static bool have_wildcard(int num, char **file) { for (int i = 0; i < num; i++) { - if (path_has_wildcard(file[i])) { + if (path_has_wildcard((char_u *)file[i])) { return true; } } return false; } -static bool have_dollars(int num, char_u **file) +static bool have_dollars(int num, char **file) { for (int i = 0; i < num; i++) { if (vim_strchr((char *)file[i], '$') != NULL) { @@ -98,7 +98,7 @@ static bool have_dollars(int num, char_u **file) /// copied into *file. /// /// @returns OK for success or FAIL for error. -int os_expand_wildcards(int num_pat, char_u **pat, int *num_file, char_u ***file, int flags) +int os_expand_wildcards(int num_pat, char **pat, int *num_file, char ***file, int flags) FUNC_ATTR_NONNULL_ARG(3) FUNC_ATTR_NONNULL_ARG(4) { @@ -151,7 +151,7 @@ int os_expand_wildcards(int num_pat, char_u **pat, int *num_file, char_u ***file // Don't allow the use of backticks in secure. if (secure) { for (i = 0; i < num_pat; i++) { - if (vim_strchr((char *)pat[i], '`') != NULL + if (vim_strchr(pat[i], '`') != NULL && (check_secure())) { return FAIL; } @@ -297,7 +297,7 @@ int os_expand_wildcards(int num_pat, char_u **pat, int *num_file, char_u ***file } // Copy one character. - *p++ = pat[i][j]; + *p++ = (char_u)pat[i][j]; } *p = NUL; } @@ -471,7 +471,7 @@ int os_expand_wildcards(int num_pat, char_u **pat, int *num_file, char_u ***file // Isolate the individual file names. p = buffer; for (i = 0; i < *num_file; i++) { - (*file)[i] = p; + (*file)[i] = (char *)p; // Space or NL separates if (shell_style == STYLE_ECHO || shell_style == STYLE_BT || shell_style == STYLE_VIMGLOB) { @@ -496,19 +496,19 @@ int os_expand_wildcards(int num_pat, char_u **pat, int *num_file, char_u ***file // Move the file names to allocated memory. for (j = 0, i = 0; i < *num_file; i++) { // Require the files to exist. Helps when using /bin/sh - if (!(flags & EW_NOTFOUND) && !os_path_exists((*file)[i])) { + if (!(flags & EW_NOTFOUND) && !os_path_exists((char_u *)(*file)[i])) { continue; } // check if this entry should be included - dir = (os_isdir((*file)[i])); + dir = (os_isdir((char_u *)(*file)[i])); if ((dir && !(flags & EW_DIR)) || (!dir && !(flags & EW_FILE))) { continue; } // Skip files that are not executable if we check for that. if (!dir && (flags & EW_EXEC) - && !os_can_exe((char *)(*file)[i], NULL, !(flags & EW_SHELLCMD))) { + && !os_can_exe((*file)[i], NULL, !(flags & EW_SHELLCMD))) { continue; } @@ -517,7 +517,7 @@ int os_expand_wildcards(int num_pat, char_u **pat, int *num_file, char_u ***file if (dir) { add_pathsep((char *)p); // add '/' to a directory name } - (*file)[j++] = p; + (*file)[j++] = (char *)p; } xfree(buffer); *num_file = j; diff --git a/src/nvim/path.c b/src/nvim/path.c index b22c0a18bd..a0b09bcec2 100644 --- a/src/nvim/path.c +++ b/src/nvim/path.c @@ -1211,7 +1211,7 @@ static bool has_special_wildchar(char_u *p) /// If FAIL is returned, *num_file and *file are either /// unchanged or *num_file is set to 0 and *file is set /// to NULL or points to "". -int gen_expand_wildcards(int num_pat, char_u **pat, int *num_file, char_u ***file, int flags) +int gen_expand_wildcards(int num_pat, char **pat, int *num_file, char ***file, int flags) { garray_T ga; char_u *p; @@ -1240,8 +1240,8 @@ int gen_expand_wildcards(int num_pat, char_u **pat, int *num_file, char_u ***fil * For `=expr` do use the internal function. */ for (int i = 0; i < num_pat; i++) { - if (has_special_wildchar(pat[i]) - && !(vim_backtick(pat[i]) && pat[i][1] == '=')) { + if (has_special_wildchar((char_u *)pat[i]) + && !(vim_backtick((char_u *)pat[i]) && pat[i][1] == '=')) { return os_expand_wildcards(num_pat, pat, num_file, file, flags); } } @@ -1256,13 +1256,13 @@ int gen_expand_wildcards(int num_pat, char_u **pat, int *num_file, char_u ***fil for (int i = 0; i < num_pat; ++i) { add_pat = -1; - p = pat[i]; + p = (char_u *)pat[i]; if (vim_backtick(p)) { add_pat = expand_backtick(&ga, p, flags); if (add_pat == -1) { recursive = false; - FreeWild(ga.ga_len, (char_u **)ga.ga_data); + FreeWild(ga.ga_len, ga.ga_data); *num_file = 0; *file = NULL; return FAIL; @@ -1272,7 +1272,7 @@ int gen_expand_wildcards(int num_pat, char_u **pat, int *num_file, char_u ***fil if ((has_env_var(p) && !(flags & EW_NOTENV)) || *p == '~') { p = expand_env_save_opt(p, true); if (p == NULL) { - p = pat[i]; + p = (char_u *)pat[i]; } #ifdef UNIX /* @@ -1338,13 +1338,13 @@ int gen_expand_wildcards(int num_pat, char_u **pat, int *num_file, char_u ***fil if (did_expand_in_path && !GA_EMPTY(&ga) && (flags & EW_PATH)) { uniquefy_paths(&ga, p); } - if (p != pat[i]) { + if (p != (char_u *)pat[i]) { xfree(p); } } *num_file = ga.ga_len; - *file = (ga.ga_data != NULL) ? (char_u **)ga.ga_data : NULL; + *file = (ga.ga_data != NULL) ? ga.ga_data : NULL; recursive = false; @@ -1352,7 +1352,7 @@ int gen_expand_wildcards(int num_pat, char_u **pat, int *num_file, char_u ***fil } /// Free the list of files returned by expand_wildcards() or other expansion functions. -void FreeWild(int count, char_u **files) +void FreeWild(int count, char **files) { if (count <= 0 || files == NULL) { return; @@ -2123,21 +2123,20 @@ char_u *path_shorten_fname(char_u *full_path, char_u *dir_name) /// If FAIL is returned, *num_file and *file are either /// unchanged or *num_file is set to 0 and *file is set /// to NULL or points to "". -int expand_wildcards_eval(char_u **pat, int *num_file, char_u ***file, int flags) +int expand_wildcards_eval(char_u **pat, int *num_file, char ***file, int flags) { int ret = FAIL; char_u *eval_pat = NULL; - char_u *exp_pat = *pat; + char *exp_pat = (char *)(*pat); char *ignored_msg; size_t usedlen; if (*exp_pat == '%' || *exp_pat == '#' || *exp_pat == '<') { - ++emsg_off; - eval_pat = eval_vars(exp_pat, exp_pat, &usedlen, - NULL, &ignored_msg, NULL); - --emsg_off; + emsg_off++; + eval_pat = eval_vars((char_u *)exp_pat, (char_u *)exp_pat, &usedlen, NULL, &ignored_msg, NULL); + emsg_off--; if (eval_pat != NULL) { - exp_pat = concat_str(eval_pat, exp_pat + usedlen); + exp_pat = (char *)concat_str(eval_pat, (char_u *)exp_pat + usedlen); } } @@ -2167,7 +2166,7 @@ int expand_wildcards_eval(char_u **pat, int *num_file, char_u ***file, int flags /// If FAIL is returned, *num_file and *file are either /// unchanged or *num_file is set to 0 and *file is set to /// NULL or points to "". -int expand_wildcards(int num_pat, char_u **pat, int *num_files, char_u ***files, int flags) +int expand_wildcards(int num_pat, char **pat, int *num_files, char ***files, int flags) { int retval; int i, j; @@ -2190,10 +2189,10 @@ int expand_wildcards(int num_pat, char_u **pat, int *num_files, char_u ***files, // check all files in (*files)[] assert(*num_files == 0 || *files != NULL); for (i = 0; i < *num_files; i++) { - ffname = (char_u *)FullName_save((char *)(*files)[i], false); + ffname = (char_u *)FullName_save((*files)[i], false); assert((*files)[i] != NULL); assert(ffname != NULL); - if (match_file_list(p_wig, (*files)[i], ffname)) { + if (match_file_list(p_wig, (char_u *)(*files)[i], ffname)) { // remove this matching file from the list xfree((*files)[i]); for (j = i; j + 1 < *num_files; j++) { @@ -2213,15 +2212,15 @@ int expand_wildcards(int num_pat, char_u **pat, int *num_files, char_u ***files, if (*num_files > 1) { non_suf_match = 0; for (i = 0; i < *num_files; i++) { - if (!match_suffix((*files)[i])) { + if (!match_suffix((char_u *)(*files)[i])) { // // Move the name without matching suffix to the front of the list. // - p = (*files)[i]; + p = (char_u *)(*files)[i]; for (j = i; j > non_suf_match; j--) { (*files)[j] = (*files)[j - 1]; } - (*files)[non_suf_match++] = p; + (*files)[non_suf_match++] = (char *)p; } } } diff --git a/src/nvim/po/tr.po b/src/nvim/po/tr.po index ac96b8b3e4..fae2fd4967 100644 --- a/src/nvim/po/tr.po +++ b/src/nvim/po/tr.po @@ -1,44 +1,29 @@ -# Turkish translations for Vim -# Vim Türkçe çevirileri -# Copyright (C) 2021 Emir SARI <emir_sari@msn.com> +# Turkish translations for Neovim +# Neovim Türkçe çevirileri +# Copyright (C) 2019-2022 Emir SARI <emir_sari@icloud.com> # This file is distributed under the same license as the Vim package. -# Emir SARI <emir_sari@msn.com>, 2019-2021 +# Emir SARI <emir_sari@icloud.com>, 2019-2022 # msgid "" msgstr "" -"Project-Id-Version: Vim Turkish Localization Project\n" +"Project-Id-Version: Neovim Turkish Localization Project\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-07-16 17:56+0300\n" -"PO-Revision-Date: 2021-07-16 20:00+0300\n" -"Last-Translator: Emir SARI <emir_sari@msn.com>\n" -"Language-Team: Turkish <https://github.com/bitigchi/vim>\n" +"POT-Creation-Date: 2022-04-13 22:59+0300\n" +"PO-Revision-Date: 2022-07-20 05:00+0300\n" +"Last-Translator: Emir SARI <emir_sari@icloud.com>\n" +"Language-Team: Turkish <https://github.com/bitigchi/neovim>\n" "Language: tr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -msgid "E163: There is only one file to edit" -msgstr "E163: Düzenlenecek yalnızca bir dosya var" - -msgid "E164: Cannot go before first file" -msgstr "E164: İlk dosyadan öncesine gidilemez" - -msgid "E165: Cannot go beyond last file" -msgstr "E165: Son dosyadan öteye gidilemez" - -msgid "E610: No argument to delete" -msgstr "E610: Silinecek bir argüman yok" - -msgid "E249: window layout changed unexpectedly" -msgstr "E249: Pencere yerleşimi beklenmedik bir biçimde değişti" - msgid "--Deleted--" msgstr "--Silindi--" #, c-format msgid "auto-removing autocommand: %s <buffer=%d>" -msgstr "otokomut kendiliğinden kaldırılıyor: %s <buffer=%d>" +msgstr "otokomut kendiliğinden kaldırılıyor: %s <arabellek=%d>" #, c-format msgid "E367: No such group: \"%s\"" @@ -50,18 +35,6 @@ msgstr "E936: Geçerli grup silinemiyor" msgid "W19: Deleting augroup that is still in use" msgstr "W19: Kullanımda olan otokomut grubu siliniyor" -#, c-format -msgid "E215: Illegal character after *: %s" -msgstr "E215: * sonrası izin verilmeyen karakter: %s" - -#, c-format -msgid "E216: No such event: %s" -msgstr "E216: Böyle bir olay yok: %s" - -#, c-format -msgid "E216: No such group or event: %s" -msgstr "E216: Böyle bir grup veya olay yok: %s" - msgid "" "\n" "--- Autocommands ---" @@ -71,7 +44,7 @@ msgstr "" #, c-format msgid "E680: <buffer=%d>: invalid buffer number " -msgstr "E680: <buffer=%d>: Geçersiz arabellek numarası" +msgstr "E680: <arabellek=%d>: Geçersiz arabellek numarası " msgid "E217: Can't execute autocommands for ALL events" msgstr "E217: Otokomutlar TÜM olaylar için çalıştırılamıyor" @@ -94,23 +67,17 @@ msgstr "%s çalıştırılıyor" msgid "autocommand %s" msgstr "%s otokomutu" -msgid "E972: Blob value does not have the right number of bytes" -msgstr "E972: İkili geniş nesne değeri doğru bayt sayısına sahip değil" - -msgid "E831: bf_key_init() called with empty password" -msgstr "E831: bf_key_init() boş bir şifre ile çağrıldı" - -msgid "E820: sizeof(uint32_t) != 4" -msgstr "E820: sizeof(uint32_t) != 4" - -msgid "E817: Blowfish big/little endian use wrong" -msgstr "E817: Blowfish yüksek/düşük son haneli kullanımı yanlış" +#, c-format +msgid "E215: Illegal character after *: %s" +msgstr "E215: * sonrası izin verilmeyen karakter: %s" -msgid "E818: sha256 test failed" -msgstr "E818: sha256 testi başarısız oldu" +#, c-format +msgid "E216: No such event: %s" +msgstr "E216: Böyle bir olay yok: %s" -msgid "E819: Blowfish test failed" -msgstr "E819: Blowfish testi başarısız oldu" +#, c-format +msgid "E216: No such group or event: %s" +msgstr "E216: Böyle bir grup veya olay yok: %s" msgid "[Location List]" msgstr "[Konum Listesi]" @@ -127,12 +94,17 @@ msgstr "E82: Arabellek ayrılamadı, çıkılıyor..." msgid "E83: Cannot allocate buffer, using other one..." msgstr "E83: Arabellek ayrılamadı, başka bir tane kullanılıyor..." -msgid "E931: Buffer cannot be registered" -msgstr "E931: Arabellek kaydedilemedi" +msgid "E937: Attempt to delete a buffer that is in use" +msgstr "E937: Kullanımda olan bir arabellek silinmeye çalışılıyor" -#, c-format -msgid "E937: Attempt to delete a buffer that is in use: %s" -msgstr "E937: Kullanımda olan bir arabellek silinmeye çalışılıyor: %s" +msgid "E515: No buffers were unloaded" +msgstr "E515: Hiçbir arabellek bellekten kaldırılmadı" + +msgid "E516: No buffers were deleted" +msgstr "E516: Hiçbir arabellek silinmedi" + +msgid "E517: No buffers were wiped out" +msgstr "E517: Hiçbir arabellek yok edilmedi" msgid "E90: Cannot unload last buffer" msgstr "E90: Son arabellek bellekten kaldırılamıyor" @@ -150,37 +122,15 @@ msgid "E88: Cannot go before first buffer" msgstr "E88: İlk arabellekten öncesine gidilemez" #, c-format -msgid "E89: No write since last change for buffer %d (add ! to override)" +msgid "" +"E89: No write since last change for buffer %<PRId64> (add ! to override)" msgstr "" -"E89: %d numaralı arabellek son değişiklikten sonra yazılmadı (geçersiz " +"E89: %<PRId64> numaralı arabellek son değişiklikten sonra yazılmadı (geçersiz " "kılmak için ! ekleyin)" -msgid "E515: No buffers were unloaded" -msgstr "E515: Hiçbir arabellek bellekten kaldırılmadı" - -msgid "E516: No buffers were deleted" -msgstr "E516: Hiçbir arabellek silinmedi" - -msgid "E517: No buffers were wiped out" -msgstr "E517: Hiçbir arabellek yok edilmedi" - -#, c-format -msgid "%d buffer unloaded" -msgid_plural "%d buffers unloaded" -msgstr[0] "%d arabellek bellekten kaldırıldı" -msgstr[1] "%d arabellek bellekten kaldırıldı" - -#, c-format -msgid "%d buffer deleted" -msgid_plural "%d buffers deleted" -msgstr[0] "%d arabellek silindi" -msgstr[1] "%d arabellek silindi" - #, c-format -msgid "%d buffer wiped out" -msgid_plural "%d buffers wiped out" -msgstr[0] "%d arabellek yok edildi" -msgstr[1] "%d arabellek yok edildi" +msgid "E89: %s will be killed (add ! to override)" +msgstr "E89: %s sonlandırılacak (geçersiz kılmak için ! ekleyin)" msgid "E948: Job still running (add ! to end the job)" msgstr "E948: İş hâlâ sürüyor (bitirmek için ! ekleyin)" @@ -199,8 +149,8 @@ msgid "W14: Warning: List of file names overflow" msgstr "W14: Uyarı: Dosya adları taşımı" #, c-format -msgid "E92: Buffer %d not found" -msgstr "E92: %d numaralı arabellek bulunamadı" +msgid "E92: Buffer %<PRId64> not found" +msgstr "E92: %<PRId64> numaralı arabellek bulunamadı" #, c-format msgid "E93: More than one match for %s" @@ -211,8 +161,8 @@ msgid "E94: No matching buffer for %s" msgstr "E94: %s için eşleşen arabellek yok" #, c-format -msgid "line %ld" -msgstr "%ld. satır" +msgid "line %<PRId64>" +msgstr "%<PRId64>. satır" msgid "E95: Buffer with this name already exists" msgstr "E95: Aynı adda bir arabellek hâlihazırda var" @@ -233,14 +183,8 @@ msgid "[readonly]" msgstr "[saltokunur]" #, c-format -msgid "%ld line --%d%%--" -msgid_plural "%ld lines --%d%%--" -msgstr[0] "%ld. satır --%d%%" -msgstr[1] "%ld. satır --%d%%" - -#, c-format -msgid "line %ld of %ld --%d%%-- col " -msgstr "satır %ld/%ld --%d%%-- sütun " +msgid "line %<PRId64> of %<PRId64> --%d%%-- col " +msgstr "satır %<PRId64>/%<PRId64> --%d%%-- sütun " msgid "[No Name]" msgstr "[Adsız]" @@ -269,270 +213,41 @@ msgstr "E382: Yazılamıyor, 'buftype' seçeneği ayarlanmamış" msgid "[Prompt]" msgstr "[İstem]" -msgid "[Popup]" -msgstr "[Açılır pencere]" - msgid "[Scratch]" msgstr "[Geçici alan]" -msgid "WARNING: The file has been changed since reading it!!!" -msgstr "UYARI: Bu dosya açıldıktan sonra başkası tarafından değiştirilmiş!!!" - -msgid "Do you really want to write to it" -msgstr "Yine de yazmak istiyor musunuz?" - -msgid "[New]" -msgstr "[Yeni]" - -msgid "[New File]" -msgstr "[Yeni Dosya]" - -msgid "E676: No matching autocommands for acwrite buffer" -msgstr "E676: acwrite arabelleği için eşleşen bir otokomut yok" - -msgid "E203: Autocommands deleted or unloaded buffer to be written" -msgstr "E203: Otokomutlar arabelleği silmiş veya yazılması için kaldırmışlar" - -msgid "E204: Autocommand changed number of lines in unexpected way" -msgstr "E204: Otokomut satır sayısını beklenmeyen biçimde değiştirdi" - -msgid "NetBeans disallows writes of unmodified buffers" -msgstr "NetBeans değiştirilmemiş arabelleklerin yazılmasına izin vermez" - -msgid "Partial writes disallowed for NetBeans buffers" -msgstr "NetBeans arabellekleri için kısmî yazmalara izin verilmez" - -msgid "is a directory" -msgstr "bir dizin" - -msgid "is not a file or writable device" -msgstr "bir dosya veya yazılabilir aygıt değil" - -msgid "writing to device disabled with 'opendevice' option" -msgstr "aygıta yazma 'opendevice' seçeneği ile kapatılmış" - -msgid "is read-only (add ! to override)" -msgstr "saltokunur (geçersiz kılmak için ! ekleyin)" - -msgid "E506: Can't write to backup file (add ! to override)" -msgstr "E506: Yedek dosyasına yazılamıyor (geçersiz kılmak için ! ekleyin)" - -msgid "E507: Close error for backup file (add ! to override)" -msgstr "E507: Yedek dosya için kapatma hatası (geçersiz kılmak için ! ekleyin)" - -msgid "E508: Can't read file for backup (add ! to override)" -msgstr "E508: Yedek dosyası okunamıyor (geçersiz kılmak için ! ekleyin)" - -msgid "E509: Cannot create backup file (add ! to override)" -msgstr "E509: Yedek dosyası oluşturulamıyor (geçersiz kılmak için ! ekleyin)" - -msgid "E510: Can't make backup file (add ! to override)" -msgstr "E510: Yedek dosyası yapılamıyor (geçersiz kılmak için ! ekleyin) " - -msgid "E214: Can't find temp file for writing" -msgstr "E214: Yazma için geçici dosya bulunamıyor" - -msgid "E213: Cannot convert (add ! to write without conversion)" -msgstr "E213: Dönüştürülemiyor (dönüştürmeden yazmak için ! ekleyin)" - -msgid "E166: Can't open linked file for writing" -msgstr "E166: Bağlı dosya yazma için açılamıyor" - -msgid "E212: Can't open file for writing" -msgstr "E212: Dosya yazma için açılamıyor" - -msgid "E949: File changed while writing" -msgstr "E949: Dosya yazma sırasında değiştirildi" - -msgid "E512: Close failed" -msgstr "E512: Kapatma başarısız oldu" - -msgid "E513: write error, conversion failed (make 'fenc' empty to override)" -msgstr "" -"E513: Yazma hatası, dönüştürme başarısız (geçersiz kılmak için 'fenc'i boş " -"bırakın)" - -#, c-format -msgid "" -"E513: write error, conversion failed in line %ld (make 'fenc' empty to " -"override)" -msgstr "" -"E513: Yazma hatası, %ld. satırda dönüştürme başarısız (geçersiz kılmak için " -"'fenc'i boş bırakın)" - -msgid "E514: write error (file system full?)" -msgstr "E514: Yazma hatası (dosya sistemi dolu mu?)" - -msgid " CONVERSION ERROR" -msgstr " DÖNÜŞTÜRME HATASI" - -#, c-format -msgid " in line %ld;" -msgstr " %ld. satırda;" - -msgid "[NOT converted]" -msgstr "[dönüştürülmedi]" - -msgid "[converted]" -msgstr "[dönüştürüldü]" - -msgid "[Device]" -msgstr "[Aygıt]" - -msgid " [a]" -msgstr " [a]" - -msgid " appended" -msgstr " iliştirildi" - -msgid " [w]" -msgstr " [w]" - -msgid " written" -msgstr " yazıldı" - -msgid "E205: Patchmode: can't save original file" -msgstr "E205: Yama kipi: Orijinal dosya kaydedilemiyor" - -msgid "E206: patchmode: can't touch empty original file" -msgstr "E206: Yama kipi: Orijinal boş dosyaya dokunulamıyor" - -msgid "E207: Can't delete backup file" -msgstr "E207: Yedek dosyası silinemiyor" - -msgid "" -"\n" -"WARNING: Original file may be lost or damaged\n" -msgstr "" -"\n" -"UYARI: Orijinal dosya kaybolmuş veya hasar görmüş olabilir\n" - -msgid "don't quit the editor until the file is successfully written!" -msgstr "dosya başarılı bir biçimde yazılana kadar düzenleyiciden çıkmayın!" - msgid "W10: Warning: Changing a readonly file" msgstr "W10: Uyarı: Saltokunur bir dosya değiştiriliyor" -msgid "E902: Cannot connect to port" -msgstr "E902: Kapıya bağlanılamıyor" - -msgid "E898: socket() in channel_connect()" -msgstr "E898: channel_connect() içinde socket()" - -#, c-format -msgid "E901: getaddrinfo() in channel_open(): %s" -msgstr "E901: channel_open() içinde getaddrinfo(): %s" - -msgid "E901: gethostbyname() in channel_open()" -msgstr "E901: channel_open() içinde gethostbyname()" - -msgid "E903: received command with non-string argument" -msgstr "E903: Dizi olmayan argüman içeren komut alındı" - -msgid "E904: last argument for expr/call must be a number" -msgstr "E904: İfadenin/çağrının son argüman bir sayı olmalıdır" - -msgid "E904: third argument for call must be a list" -msgstr "E904: Çağrının üçüncü argümanı bir liste olmalıdır" - -#, c-format -msgid "E905: received unknown command: %s" -msgstr "E905: Bilinmeyen komut alındı: %s" - -msgid "E906: not an open channel" -msgstr "E906: Açık bir kanal değil" - -#, c-format -msgid "E630: %s(): write while not connected" -msgstr "E630: %s(): Bağlantı yokken yazım" - -#, c-format -msgid "E631: %s(): write failed" -msgstr "E631: %s(): Yazma başarısız" - -#, c-format -msgid "E917: Cannot use a callback with %s()" -msgstr "E917: %s() ile geri çağırma kullanılamıyor" +msgid "can only be opened in headless mode" +msgstr "yalnızca başsız kipte açılabilir" -msgid "E912: cannot use ch_evalexpr()/ch_sendexpr() with a raw or nl channel" -msgstr "E912: ch_evalexpr()/ch_sendexpr() raw/nl kanalları ile kullanılamaz" - -msgid "No display" -msgstr "Görüntü yok" - -msgid ": Send failed.\n" -msgstr ": Gönderme başarısız oldu.\n" - -msgid ": Send failed. Trying to execute locally\n" -msgstr ": Gönderme başarısız oldu. Yerel ortamda çalıştırma deneniyor\n" - -#, c-format -msgid "%d of %d edited" -msgstr "%d/%d düzenlendi" +msgid "channel was already open" +msgstr "kanal halihazırda açıktı" -msgid "No display: Send expression failed.\n" -msgstr "Görüntü yok: Gönderme başarısız oldu.\n" +msgid "Can't send data to closed stream" +msgstr "Kapalı akışa veri gönderilemez" -msgid ": Send expression failed.\n" -msgstr ": Gönderme başarısız oldu.\n" +msgid "Can't send raw data to rpc channel" +msgstr "rpc kanalına ham veri gönderilemez" -msgid "E240: No connection to the X server" -msgstr "E240: X sunucusuna bağlanılamadı" +msgid "E474: Failed to convert list to msgpack string buffer" +msgstr "E474: Liste, msgpack dizi arabelleğine dönüştürülemedi" -#, c-format -msgid "E241: Unable to send to %s" -msgstr "E241: Şuraya gönderilemedi: %s" - -msgid "E277: Unable to read a server reply" -msgstr "E277: Bir sunucu yanıtı okunamadı" - -msgid "E941: already started a server" -msgstr "E941: Sunucu hâlihazırda çalışıyor" - -msgid "E942: +clientserver feature not available" -msgstr "E942: +clientserver özelliği mevcut değil" - -msgid "E258: Unable to send to client" -msgstr "E258: İstemciye gönderilemiyor" - -msgid "Used CUT_BUFFER0 instead of empty selection" -msgstr "Boş seçim yerine CUT_BUFFER0 kullanıldı" - -msgid "tagname" -msgstr "etiket adı" - -msgid " kind file\n" -msgstr " dosya türü\n" - -msgid "'history' option is zero" -msgstr "'history' seçeneği sıfır" - -msgid "E821: File is encrypted with unknown method" -msgstr "E821: Dosya bilinmeyen bir yöntemle şifrelenmiş" - -msgid "Warning: Using a weak encryption method; see :help 'cm'" -msgstr "" -"Uyarı: Zayıf bir şifreleme yöntemi kullanılıyor; bilgi için: :help 'cm'" - -msgid "" -"Note: Encryption of swapfile not supported, disabling swap- and undofile" -msgstr "Takas dosyası şifrelemesi desteklenmiyor, takas ve geri al dosyası " -"devre dışı bırakılıyor" - -msgid "Enter encryption key: " -msgstr "Şifreleme anahtarı girin: " +msgid "E545: Missing colon" +msgstr "E545: İki nokta eksik" -msgid "Enter same key again: " -msgstr "Aynı anahtarı yeniden girin: " +msgid "E546: Illegal mode" +msgstr "E546: İzin verilmeyen kip" -msgid "Keys don't match!" -msgstr "Anahtarlar eşleşmiyor!" +msgid "E548: digit expected" +msgstr "E548: Basamak bekleniyordu" -msgid "[crypted]" -msgstr "[şifreli]" +msgid "E549: Illegal percentage" +msgstr "E549: İzin verilmeyen yüzde" msgid "Entering Debug mode. Type \"cont\" to continue." -msgstr "Hata Ayıklama kipine giriliyor. Sürdürmek için \"cont\" yazın." +msgstr "Hata ayıklama kipine giriliyor. Sürdürmek için \"cont\" yazın." #, c-format msgid "Oldval = \"%s\"" @@ -544,8 +259,8 @@ msgid "Newval = \"%s\"" msgstr "Yeni değer = \"%s\"" #, c-format -msgid "line %ld: %s" -msgstr "%ld. satır: %s" +msgid "line %<PRId64>: %s" +msgstr "%<PRId64>. satır: %s" #, c-format msgid "cmd: %s" @@ -559,8 +274,8 @@ msgid "frame at highest level: %d" msgstr "çerçeve en yüksek düzeyde: %d" #, c-format -msgid "Breakpoint in \"%s%s\" line %ld" -msgstr "\"%s%s\" içinde kesme noktası, %ld. satır" +msgid "Breakpoint in \"%s%s\" line %<PRId64>" +msgstr "\"%s%s\" içinde kesme noktası, %<PRId64>. satır" #, c-format msgid "E161: Breakpoint not found: %s" @@ -570,23 +285,16 @@ msgid "No breakpoints defined" msgstr "Hiçbir kesme noktası tanımlanmamış" #, c-format -msgid "%3d %s %s line %ld" -msgstr "%3d %s %s %ld. satır" +msgid "%3d %s %s line %<PRId64>" +msgstr "%3d %s %s %<PRId64>. satır" #, c-format msgid "%3d expr %s" msgstr "%3d ifade %s" -msgid "extend() argument" -msgstr "extend() argümanı" - #, c-format -msgid "E737: Key already exists: %s" -msgstr "E737: Anahtar hâlihazırda var: %s" - -#, c-format -msgid "E96: Cannot diff more than %d buffers" -msgstr "E96: %d arabellekten fazlasında karşılaştırma yapılamıyor" +msgid "E96: Cannot diff more than %<PRId64> buffers" +msgstr "E96: %<PRId64> arabellekten fazlasında karşılaştırma yapılamıyor" #, c-format msgid "Not enough memory to use internal diff for buffer \"%s\"" @@ -601,18 +309,12 @@ msgstr "E97: Karşılaştırma yapılamıyor" msgid "E960: Problem creating the internal diff" msgstr "E960: Karşılaştırma hazırlanırken sorun" -msgid "Patch file" -msgstr "Yama dosyası" - msgid "E816: Cannot read patch output" msgstr "E816: Yama çıktısı okunamıyor" msgid "E98: Cannot read diff output" msgstr "E98: Karşılaştırma çıktısı okunamıyor" -msgid "E959: Invalid diff format." -msgstr "E959: Geçersiz karşılaştırma biçimi" - msgid "E99: Current buffer is not in diff mode" msgstr "E99: Şu anki arabellek karşılaştırma kipinde değil" @@ -639,6 +341,19 @@ msgstr "E103: Arabellek \"%s\" karşılaştırma kipinde değil" msgid "E787: Buffer changed unexpectedly" msgstr "E787: Arabellek beklenmeyen bir biçimde değiştirildi" +#, c-format +msgid "E1214: Digraph must be just two characters: %s" +msgstr "E1214: İkili harf yalnızca iki karakter olmalıdır: %s" + +#, c-format +msgid "E1215: Digraph must be one character: %s" +msgstr "E1215: İkili harf tek bir karakter olmalıdır: %s" + +msgid "" +"E1216: digraph_setlist() argument must be a list of lists with two items" +msgstr "" +"E1216: digraph_setlist() argümanı iki ögeli listelerin bir listesi olmalıdır" + msgid "E104: Escape not allowed in digraph" msgstr "E104: Kaçan, ikili harflerde kullanılamaz" @@ -726,6 +441,186 @@ msgstr "E105: :loadkeymap kaynak alınmayan bir dosyada kullanılıyor" msgid "E791: Empty keymap entry" msgstr "E791: Boş düğme eşlem girdisi" +msgid " Keyword completion (^N^P)" +msgstr " Anahtar sözcük tamamlaması (^N^P)" + +msgid " ^X mode (^]^D^E^F^I^K^L^N^O^Ps^U^V^Y)" +msgstr " ^X kipi (^]^D^E^F^I^K^L^N^O^Ps^U^V^Y)" + +msgid " Whole line completion (^L^N^P)" +msgstr " Tam satır tamamlaması (^L^N^P)" + +msgid " File name completion (^F^N^P)" +msgstr " Dosya adı tamamlaması (^F^N^P)" + +msgid " Tag completion (^]^N^P)" +msgstr " Etiket tamamlaması (^]^N^P)" + +msgid " Path pattern completion (^N^P)" +msgstr " Yol dizgisi tamamlaması (^N^P)" + +msgid " Definition completion (^D^N^P)" +msgstr " Tanım tamamlaması (^D^N^P)" + +msgid " Dictionary completion (^K^N^P)" +msgstr " Sözlük tamamlaması (^K^N^P)" + +msgid " Thesaurus completion (^T^N^P)" +msgstr " Eşanlamlılar sözlüğü tamamlaması (^T^N^P)" + +msgid " Command-line completion (^V^N^P)" +msgstr " Komut satırı tamamlaması (^V^N^P)" + +msgid " User defined completion (^U^N^P)" +msgstr " Kullanıcı tanımlı tamamlamalar (^U^N^P)" + +msgid " Omni completion (^O^N^P)" +msgstr " Omni tamamlaması (^O^N^P)" + +msgid " Spelling suggestion (s^N^P)" +msgstr " Yazım önerisi (s^N^P)" + +msgid " Keyword Local completion (^N^P)" +msgstr " Dahili anahtar sözcük tamamlaması (^N^P)" + +msgid "Hit end of paragraph" +msgstr "Paragrafın sonuna varıldı" + +msgid "E839: Completion function changed window" +msgstr "E839: Tamamlama işlevi pencereyi değiştirdi" + +msgid "E840: Completion function deleted text" +msgstr "E840: Tamamlama işlevi metni sildi" + +msgid "'dictionary' option is empty" +msgstr "'dictionary' seçeneği boş" + +msgid "'thesaurus' option is empty" +msgstr "'thesaurus' seçeneği boş" + +#, c-format +msgid "Scanning dictionary: %s" +msgstr "Sözlük taranıyor: %s" + +msgid " (insert) Scroll (^E/^Y)" +msgstr " (ekle) Kaydır (^E/^Y)" + +msgid " (replace) Scroll (^E/^Y)" +msgstr " (değiştir) Kaydır (^E/^Y)" + +#, c-format +msgid "Scanning: %s" +msgstr "Taranıyor: %s" + +msgid "Scanning tags." +msgstr "Etiketler taranıyor..." + +msgid "match in file" +msgstr "dosya içinde eşleşme" + +msgid " Adding" +msgstr " Ekleniyor" + +msgid "-- Searching..." +msgstr "-- Aranıyor..." + +msgid "Back at original" +msgstr "Başlangıca geri dönüldü" + +msgid "Word from other line" +msgstr "Sözcük diğer satırdan" + +msgid "The only match" +msgstr "Tek eşleşen" + +#, c-format +msgid "match %d of %d" +msgstr "eşleşme %d/%d" + +#, c-format +msgid "match %d" +msgstr "eşleşme %d" + +msgid "E18: Unexpected characters in :let" +msgstr "E18: :let içinde beklenmeyen karakter" + +msgid "E111: Missing ']'" +msgstr "E111: ']' eksik" + +msgid "E719: Cannot use [:] with a Dictionary" +msgstr "E719: [:], bir Sözlük ile kullanılamaz" + +#, c-format +msgid "E461: Illegal variable name: %s" +msgstr "E461: İzin verilmeyen değişken adı: %s" + +msgid "E995: Cannot modify existing variable" +msgstr "E995: Mevcut değişken değiştirilemiyor" + +msgid "E274: No white space allowed before parenthesis" +msgstr "E274: Ayraçtan önce boşluğa izin verilmiyor" + +#, c-format +msgid "E940: Cannot lock or unlock variable %s" +msgstr "E940: Değişken %s kilitlenemiyor veya açılamıyor" + +#, c-format +msgid "E80: Error while writing: %s" +msgstr "E80: Yazma sırasında hata: %s" + +msgid "E1098: String, List or Blob required" +msgstr "E1098: Dizi, Liste veya İkili Nesne gerekiyor" + +#, c-format +msgid "E734: Wrong variable type for %s=" +msgstr "E734: %s= için yanlış değişken türü" + +msgid "" +"E5700: Expression from 'spellsuggest' must yield lists with exactly two " +"values" +msgstr "" +"E5700: 'spellsuggest'ten olan ifade, yalnızca iki değerli listelere izin " +"vermelidir" + +msgid "E991: cannot use =<< here" +msgstr "E991: Burada =<< kullanılamaz" + +msgid "E221: Marker cannot start with lower case letter" +msgstr "E221: İmleyici küçük harfle başlayamaz" + +msgid "E172: Missing marker" +msgstr "E172: İmleyici eksik" + +#, c-format +msgid "E990: Missing end marker '%s'" +msgstr "E990: Son imleyici '%s' eksik" + +msgid "E687: Less targets than List items" +msgstr "E687: Liste ögelerinden daha az hedef var" + +msgid "E688: More targets than List items" +msgstr "E688: Liste ögelerinden daha fazla hedef var" + +msgid "E452: Double ; in list of variables" +msgstr "E452: Değişkenler listesinde çifte ;" + +#, c-format +msgid "E738: Can't list variables for %s" +msgstr "E738: %s için değişkenler listelenemiyor" + +msgid "E996: Cannot lock an environment variable" +msgstr "E996: Ortam değişkeni kilitlenemiyor" + +msgid "E996: Cannot lock an option" +msgstr "E996: Seçenek kilitlenemiyor" + +msgid "E996: Cannot lock a register" +msgstr "E996: Yazmaç kilitlenemiyor" + +#, c-format +msgid "E121: Undefined variable: %.*s" +msgstr "E121: Tanımlanmamış değişken: %.*s" + msgid "E689: Can only index a List, Dictionary or Blob" msgstr "" "E689: Yalnızca bir liste, sözlük, veya ikili geniş nesne dizinlenebilir" @@ -733,26 +628,144 @@ msgstr "" msgid "E708: [:] must come last" msgstr "E708: [:] en son gelmelidir" +msgid "E713: Cannot use empty key after ." +msgstr "E713: . sonrası boş anahtar kullanılamaz" + msgid "E709: [:] requires a List or Blob value" msgstr "E709: [:] bir liste veya ikili geniş nesne değeri gerektirir" +msgid "E972: Blob value does not have the right number of bytes" +msgstr "E972: İkili geniş nesne değeri doğru bayt sayısına sahip değil" + msgid "E996: Cannot lock a range" msgstr "E996: Erim kilitlenemiyor" +msgid "E710: List value has more items than target" +msgstr "E710: Liste değeri hedeften daha fazla ögeye sahip" + +msgid "E711: List value has not enough items" +msgstr "E711: Liste değeri yeterli ögeye sahip değil" + msgid "E996: Cannot lock a list or dict" msgstr "E996: Bir liste veya sözlük kilitlenemiyor" +msgid "E690: Missing \"in\" after :for" +msgstr "E690: :for sonrası \"in\" eksik" + +#, c-format +msgid "E108: No such variable: \"%s\"" +msgstr "E108: Böyle bir değişken yok: \"%s\"" + +msgid "E109: Missing ':' after '?'" +msgstr "E109: '?' sonrası ':' eksik" + +msgid "E804: Cannot use '%' with Float" +msgstr "E804: Bir kayan noktalı değer ile '%' kullanılamaz" + +msgid "E973: Blob literal should have an even number of hex characters" +msgstr "" +"E973: İkili geniş nesne hazır bilgisi çift onalt. karakterlere iye olmalıdır" + +msgid "E110: Missing ')'" +msgstr "E110: ')' eksik" + msgid "E260: Missing name after ->" msgstr "E260: -> sonrası ad eksik" msgid "E695: Cannot index a Funcref" msgstr "E695: Bir Funcref dizinlenemiyor" +msgid "E909: Cannot index a special variable" +msgstr "E909: Özel bir değişken dizinlenemiyor" + +#, c-format +msgid "E112: Option name missing: %s" +msgstr "E112: Seçenek adı eksik: %s" + +#, c-format +msgid "E113: Unknown option: %s" +msgstr "E113: Bilinmeyen seçenek: %s" + +#, c-format +msgid "E114: Missing quote: %s" +msgstr "E114: Tırnak eksik: %s" + +#, c-format +msgid "E115: Missing quote: %s" +msgstr "E115: Tırnak eksik: %s" + +#, c-format +msgid "E696: Missing comma in List: %s" +msgstr "E696: Listede virgül eksik: %s" + +#, c-format +msgid "E697: Missing end of List ']': %s" +msgstr "E697: Liste sonunda ']' eksik: %s" + msgid "Not enough memory to set references, garbage collection aborted!" msgstr "Referansları ayarlamak için yetersiz bellek, atık toplama durduruldu" -msgid "E724: variable nested too deep for displaying" -msgstr "E724: Değişken çok iç içe geçtiğinden görüntülenemiyor" +#, c-format +msgid "E720: Missing colon in Dictionary: %s" +msgstr "E720: Sözlükte iki nokta eksik: %s" + +#, c-format +msgid "E721: Duplicate key in Dictionary: \"%s\"" +msgstr "E721: Sözlükte yinelenmiş anahtar: \"%s\"" + +#, c-format +msgid "E722: Missing comma in Dictionary: %s" +msgstr "E722: Sözlükte virgül eksik: %s" + +#, c-format +msgid "E723: Missing end of Dictionary '}': %s" +msgstr "E723: Sözlük sonu '}' eksik: %s" + +msgid "map() argument" +msgstr "map() argümanı" + +msgid "filter() argument" +msgstr "filter() argümanı" + +#, c-format +msgid "E700: Unknown function: %s" +msgstr "E700: Bilinmeyen işlev: %s" + +msgid "E922: expected a dict" +msgstr "E922: Bir sözlük bekleniyordu" + +msgid "E923: Second argument of function() must be a list or a dict" +msgstr "E923: function() ikinci argümanı bir liste veya sözlük olmalıdır" + +msgid "E5050: {opts} must be the only argument" +msgstr "E5050: {opts}, tek argüman olmalıdır" + +#, c-format +msgid "Executing command: \"%s\"" +msgstr "Komut çalıştırılıyor: \"%s\"" + +msgid "E921: Invalid callback argument" +msgstr "E921: Geçersiz geri çağırma argümanı" + +#, c-format +msgid "E963: setting %s to value with wrong type" +msgstr "E963: %s yanlış türe sahip değere ayarlanıyor" + +#, c-format +msgid "E794: Cannot set variable in the sandbox: \"%.*s\"" +msgstr "E794: Kum havuzunda değişken ayarlanamaz: \"%.*s\"" + +#, c-format +msgid "E795: Cannot delete variable %.*s" +msgstr "E795: %.*s değişkeni silinemiyor" + +#, c-format +msgid "E704: Funcref variable name must start with a capital: %s" +msgstr "E704: Funcref değişkeni BÜYÜK harf ile başlamalıdır: %s" + +#, c-format +msgid "E705: Variable name conflicts with existing function: %s" +msgstr "E705: Değişken adı mevcut işlevle çakışıyor: %s" msgid "E698: variable nested too deep for making a copy" msgstr "E698: Değişken kopyalama için çok iç içe geçmiş" @@ -764,127 +777,705 @@ msgstr "" "\n" "\tEn son şuradan ayarlandı: " +msgid "E5009: $VIMRUNTIME is empty or unset" +msgstr "E5009: $VIMRUNTIME boş veya ayarlanmamış" + +#, c-format +msgid "E5009: Invalid $VIMRUNTIME: %s" +msgstr "E5009: Geçersiz VIMRUNTIME: %s" + +msgid "E5009: Invalid 'runtimepath'" +msgstr "E5009: Geçersiz 'runtimepath'" + +msgid "E977: Can only compare Blob with Blob" +msgstr "" +"E977: Bir ikili geniş öğe yalnızca kendinden bir başkası ile " +"karşılaştırılabilir" + +msgid "E691: Can only compare List with List" +msgstr "E691: Bir liste yalnızca başka bir liste ile karşılaştırılabilir" + +msgid "E692: Invalid operation for List" +msgstr "E692: Geçersiz liste işlemi" + +msgid "E735: Can only compare Dictionary with Dictionary" +msgstr "E735: Bir sözlük yalnızca başka bir sözlük ile karşılaştırılabilir" + +msgid "E736: Invalid operation for Dictionary" +msgstr "E736: Geçersiz sözlük işlemi" + +msgid "E694: Invalid operation for Funcrefs" +msgstr "E694: Geçersiz Funcref işlemi" + +#, c-format +msgid "E474: Expected comma before list item: %s" +msgstr "E474: Liste ögesi öncesi virgül bekleniyordu: %s" + +#, c-format +msgid "E474: Expected colon before dictionary value: %s" +msgstr "E474: Sözlük değeri öncesi iki nokta bekleniyordu: %s" + +#, c-format +msgid "E474: Expected string key: %s" +msgstr "E474: Dizi anahtarı bekleniyordu: %s" + +#, c-format +msgid "E474: Expected comma before dictionary key: %s" +msgstr "E474: Sözlük anahtarı öncesi virgül bekleniyordu: %s" + +#, c-format +msgid "E474: Unfinished escape sequence: %.*s" +msgstr "E474: Bitirilmemiş kaçış sırası: %.*s" + +#, c-format +msgid "E474: Unfinished unicode escape sequence: %.*s" +msgstr "E474: Tamamlanmamış Unicode kaçış sıralaması: %.*s" + +#, c-format +msgid "E474: Expected four hex digits after \\u: %.*s" +msgstr "E474: \\u sonrası dört onaltılık basamak bekleniyordu: %.*s" + +#, c-format +msgid "E474: Unknown escape sequence: %.*s" +msgstr "E474: Bilinmeyen kaçış sıralaması: %.*s" + +#, c-format +msgid "E474: ASCII control characters cannot be present inside string: %.*s" +msgstr "E474: ASCII denetim karakterleri, dizi içinde var olamaz: %.*s" + +#, c-format +msgid "E474: Only UTF-8 strings allowed: %.*s" +msgstr "E474: Yalnızca UTF-8 dizilere izin verilir: %.*s" + +#, c-format +msgid "" +"E474: Only UTF-8 code points up to U+10FFFF are allowed to appear unescaped: " +"%.*s" +msgstr "" +"E474: Yalnızca U+10FFFF'e kadar olan UTF-8 kod noktalarına kaçışsız " +"var olma izni verilir: %.*s" + +#, c-format +msgid "E474: Expected string end: %.*s" +msgstr "E474: Dizi sonu bekleniyordu: %.*s" + +#, c-format +msgid "E474: Leading zeroes are not allowed: %.*s" +msgstr "E474: Öncü sıfırlara zin verilmiyor: %.*s" + +#, c-format +msgid "E474: Missing number after minus sign: %.*s" +msgstr "E474: Eksi imi sonrası sayı eksik: %.*s" + +#, c-format +msgid "E474: Missing number after decimal dot: %.*s" +msgstr "E474: Ondalık noktası sonrası sayı eksik: %.*s" + +#, c-format +msgid "E474: Missing exponent: %.*s" +msgstr "E474: Argüman eksik: %.*s" + +#, c-format +msgid "" +"E685: internal error: while converting number \"%.*s\" to float string2float " +"consumed %zu bytes in place of %zu" +msgstr "" +"E685: İçsel hata: \"%.*s\" sayısı kayan noktalı değere dönüştürülürken " +"string2float %zu bayt harcadı, %zu bayt harcaması gerekiyordu" + +#, c-format +msgid "" +"E685: internal error: while converting number \"%.*s\" to integer vim_str2nr " +"consumed %i bytes in place of %zu" +msgstr "" +"E685: İçsel hata: \"%.*s\" sayısı tamsayıya dönüştürülürken " +"vim_str2nr %i bayt harcadı, %zu bayt harcaması gerekiyordu" + +msgid "E474: Attempt to decode a blank string" +msgstr "E474: Boş bir dizinin kodu çözülmeye çalışılıyor" + +#, c-format +msgid "E474: No container to close: %.*s" +msgstr "E474: Kapatılacak kapsayıcı yok: %.*s" + +#, c-format +msgid "E474: Closing list with curly bracket: %.*s" +msgstr "E474: Liste, kıvrımlı ayraçla kapatılıyor: %.*s" + +#, c-format +msgid "E474: Closing dictionary with square bracket: %.*s" +msgstr "E474: Sözlük, bir kare ayraç ile kapatılıyor: %.*s" + +#, c-format +msgid "E474: Trailing comma: %.*s" +msgstr "E474: Sonda virgül: %.*s" + +#, c-format +msgid "E474: Expected value after colon: %.*s" +msgstr "E474: İki nokta sonrası değer bekleniyordu: %.*s" + +#, c-format +msgid "E474: Expected value: %.*s" +msgstr "E474: Değer bekleniyordu: %.*s" + +#, c-format +msgid "E474: Comma not inside container: %.*s" +msgstr "E474: Virgül, kapsayıcı içinde değil: %.*s" + +#, c-format +msgid "E474: Duplicate comma: %.*s" +msgstr "E474: Yinelenen virgül: %.*s" + +#, c-format +msgid "E474: Comma after colon: %.*s" +msgstr "E474: İki nokta sonrası virgül: %.*s" + +#, c-format +msgid "E474: Using comma in place of colon: %.*s" +msgstr "E474: İki nokta yerine virgül kullanılıyor: %.*s" + +#, c-format +msgid "E474: Leading comma: %.*s" +msgstr "E474: Öncü virgül: %.*s" + +#, c-format +msgid "E474: Colon not inside container: %.*s" +msgstr "E474: İki nokta, kapsayıcı içinde değil: %.*s" + +#, c-format +msgid "E474: Using colon not in dictionary: %.*s" +msgstr "E474: Sözlük dışında iki nokta kullanılıyor: %.*s" + +#, c-format +msgid "E474: Unexpected colon: %.*s" +msgstr "E474: Beklenmedik iki nokta: %.*s" + +#, c-format +msgid "E474: Colon after comma: %.*s" +msgstr "E474: Virgül sonrası iki nokta: %.*s" + +#, c-format +msgid "E474: Duplicate colon: %.*s" +msgstr "E474: Yinelenen iki nokta: %.*s" + +#, c-format +msgid "E474: Expected null: %.*s" +msgstr "E474: null bekleniyordu: %.*s" + +#, c-format +msgid "E474: Expected true: %.*s" +msgstr "E474: true bekleniyordu: %.*s" + +#, c-format +msgid "E474: Expected false: %.*s" +msgstr "E474: false bekleniyordu: %.*s" + +#, c-format +msgid "E474: Unidentified byte: %.*s" +msgstr "E474: Tanımlanmamış bayt: %.*s" + +#, c-format +msgid "E474: Trailing characters: %.*s" +msgstr "E474: Sonda fazladan karakterler: %.*s" + +#, c-format +msgid "E474: Unexpected end of input: %.*s" +msgstr "E474: Beklenmedik girdi sonu: %.*s" + +#, c-format +msgid "key %s" +msgstr "%s anahtarı" + +#, c-format +msgid "key %s at index %i from special map" +msgstr "özel eşlemden %s anahtarı, %i indeksinde" + +#, c-format +msgid "index %i" +msgstr "%i indeksi" + +msgid "partial" +msgstr "kısımsal" + +#, c-format +msgid "argument %i" +msgstr "%i argümanı" + +msgid "partial self dictionary" +msgstr "kısımsal öz sözlük" + +msgid "itself" +msgstr "kendisi" + +msgid "E724: unable to correctly dump variable with self-referencing container" +msgstr "E724: Özüne başvuran kapsayıcı ile değişken düzgünce dökülemiyor" + +msgid "E474: Unable to represent NaN value in JSON" +msgstr "E474: JSON içinde NaN değer düzgünce temsil edilemiyor" + +msgid "E474: Unable to represent infinity in JSON" +msgstr "E474: JSON içinde sonsuzluk temsil edilemiyor" + +#, c-format +msgid "" +"E474: String \"%.*s\" contains byte that does not start any UTF-8 character" +msgstr "E474: \"%.*s\" dizisi, herhangi bir UTF-8 karakter başlatmayan bayt " +"içeriyor" + +#, c-format +msgid "" +"E474: UTF-8 string contains code point which belongs to a surrogate pair: " +"%.*s" +msgstr "E474: UTF-8 dizisi, bir vekil çifte iye olan kod noktası içeriyor: " +"%.*s" + +msgid "E474: Unable to convert EXT string to JSON" +msgstr "E474: EXT dizisi, JSON'a dönüştürülemiyor" + +#, c-format +msgid "E474: Error while dumping %s, %s: attempt to dump function reference" +msgstr "E474: %s dökülürken hata, %s: İşlev başvurusu dökme girişimi" + +msgid "E474: Invalid key in special dictionary" +msgstr "E474: Özel sözlükte geçersiz anahtar" + +msgid "encode_tv2string() argument" +msgstr "encode_tv2string() argümanı" + +msgid ":echo argument" +msgstr ":echo argümanı" + +msgid "encode_tv2json() argument" +msgstr "encode_tv2json() argümanı" + +#, c-format +msgid "E5004: Error while dumping %s, %s: attempt to dump function reference" +msgstr "E5004: %s dökülürken hata, %s: İşlev başvurusu dökme girişimi" + +#, c-format +msgid "E5005: Unable to dump %s: container references itself in %s" +msgstr "E5005: %s dökülemiyor: Kapsayıcı, %s içinde özüne başvuruyor" + +#, c-format +msgid "E684: list index out of range: %<PRId64>" +msgstr "E684: Liste indeksi erim dışında: %<PRId64>" + +#, c-format +msgid "E899: Argument of %s must be a List or Blob" +msgstr "E899: %s argümanı bir liste veya ikili geniş nesne olmalıdır" + +msgid "E957: Invalid window number" +msgstr "E957: Geçersiz pencere numarası" + +#, c-format +msgid "E998: Reduce of an empty %s with no initial value" +msgstr "E998: Başlangıç değeri olmayan boş bir %s için reduce() yapılamıyor" + +#, c-format +msgid "Error converting the call result: %s" +msgstr "Çağrı sonuçlarını dönüştürürken hata: %s" + +msgid "add() argument" +msgstr "add() argümanı" + #, c-format msgid "E158: Invalid buffer name: %s" msgstr "E158: Geçersiz arabellek adı: %s" +#, c-format +msgid "Invalid channel stream \"%s\"" +msgstr "Geçersiz kanal akışı \"%s\"" + +msgid "E785: complete() can only be used in Insert mode" +msgstr "E785: complete() yalnızca Ekleme kipinde kullanılabilir" + msgid "&Ok" msgstr "&Tamam" -msgid "E980: lowlevel input not supported" -msgstr "E980: Alt düzey girdi desteklenmiyor" +msgid "Context stack is empty" +msgstr "Bağlam yığını boş" -#, c-format -msgid "E700: Unknown function: %s" -msgstr "E700: Bilinmeyen işlev: %s" +msgid "dictwatcheradd() argument" +msgstr "dictwatcheradd() argümanı" -msgid "E922: expected a dict" -msgstr "E922: Bir sözlük bekleniyordu" +msgid "E900: maxdepth must be non-negative number" +msgstr "E900: maxdepth negatif olmayan bir sayı olmalı" -msgid "E923: Second argument of function() must be a list or a dict" -msgstr "E923: function() ikinci argümanı bir liste veya sözlük olmalıdır" +msgid "flatten() argument" +msgstr "flatten() argümanı" -msgid "" -"&OK\n" -"&Cancel" -msgstr "" -"&Tamam\n" -"İ&ptal" +msgid "extend() argument" +msgstr "extend() argümanı" + +msgid "E5000: Cannot find tab number." +msgstr "E5000: Sekme numarası bulunamıyor." + +msgid "E5001: Higher scope cannot be -1 if lower scope is >= 0." +msgstr "E5001: Daha yüksek kapsam, düşük kapsam >= 0 ise -1 olamaz." + +msgid "E5002: Cannot find window number." +msgstr "E5002: Pencere numarası bulunamıyor." msgid "called inputrestore() more often than inputsave()" msgstr "inputrestore(), inputsave()'den daha fazla çağrıldı" +msgid "insert() argument" +msgstr "insert() argümanı" + msgid "E786: Range not allowed" msgstr "E786: Erime izin verilmiyor" +msgid "E474: Failed to convert list to string" +msgstr "E474: Liste, diziye dönüştürülemedi" + +#, c-format +msgid "E474: Failed to parse %.*s" +msgstr "E474: %.*s ayrıştırılamadı" + msgid "E701: Invalid type for len()" msgstr "E701: len() için geçersiz tür" +#, c-format +msgid "msgpackdump() argument, index %i" +msgstr "msgpackdump() argümanı, %i indeksi" + +msgid "E5070: Character number must not be less than zero" +msgstr "E5070: Karakter numarası sıfırdan küçük olmamalı" + +#, c-format +msgid "E5071: Character number must not be greater than INT_MAX (%i)" +msgstr "E5071: Karakter numarası INT_MAX (%i) değerinden büyük olmamalı" + msgid "E726: Stride is zero" msgstr "E726: Sıfır adım" msgid "E727: Start past end" msgstr "E727: Başlangıç bitişten sonra" +msgid "<empty>" +msgstr "<boş>" + +msgid "remove() argument" +msgstr "remove() argümanı" + +msgid "E655: Too many symbolic links (cycle?)" +msgstr "E655: Çok fazla sembolik bağlantı (çevrim?)" + +msgid "reverse() argument" +msgstr "reverse() argümanı" + +#, c-format +msgid "E5010: List item %d of the second argument is not a string" +msgstr "E5010: İkinci argümanın %d liste ögesi bir dizi değil" + +#, c-format +msgid "E927: Invalid action: '%s'" +msgstr "E927: Geçersiz eylem: '%s'" + #, c-format msgid "E962: Invalid action: '%s'" msgstr "E962: Geçersiz eylem: '%s'" #, c-format +msgid "connection failed: %s" +msgstr "bağlantı başarısız: %s" + +msgid "sort() argument" +msgstr "sort() argümanı" + +msgid "uniq() argument" +msgstr "uniq() argümanı" + +msgid "E702: Sort compare function failed" +msgstr "E702: Sıralayıp karşılaştırma işlevi başarısız oldu" + +msgid "E882: Uniq compare function failed" +msgstr "E882: Benzersizlik karşılaştırma işlevi başarısız oldu" + +#, c-format +msgid "E6100: \"%s\" is not a valid stdpath" +msgstr "E6100: \"%s\", geçerli bir stdpath değil" + +msgid "(Invalid)" +msgstr "(Geçersiz)" + +#, c-format msgid "E935: invalid submatch number: %d" msgstr "E935: Geçersiz alteşleşme numarası: %d" -msgid "E991: cannot use =<< here" -msgstr "E991: Burada =<< kullanılamaz" +msgid "Can only call this function in an unmodified buffer" +msgstr "Bu işlev yalnızca değiştirilmemiş bir arabellekte çağrılabilir" -msgid "E221: Marker cannot start with lower case letter" -msgstr "E221: İmleyici küçük harfle başlayamaz" - -msgid "E172: Missing marker" -msgstr "E172: İmleyici eksik" +msgid "writefile() first argument must be a List or a Blob" +msgstr "writefile() ilk argümanı bir liste veya ikili geniş nesne olmalıdır" #, c-format -msgid "E990: Missing end marker '%s'" -msgstr "E990: Son imleyici '%s' eksik" +msgid "E5060: Unknown flag: %s" +msgstr "E5060: Bilinmeyen bayrak: %s" -msgid "E985: .= is not supported with script version >= 2" -msgstr "E985: .= betiğin ikinci sürümünden itibaren desteklenmiyor" +msgid "E482: Can't open file with an empty name" +msgstr "E482: Adı boş olan bir dosya açılamaz" -msgid "E687: Less targets than List items" -msgstr "E687: Liste ögelerinden daha az hedef var" +#, c-format +msgid "E482: Can't open file %s for writing: %s" +msgstr "E482: %s dosyası yazma için açılamıyor: %s" -msgid "E688: More targets than List items" -msgstr "E688: Liste ögelerinden daha fazla hedef var" +#, c-format +msgid "E80: Error when closing file %s: %s" +msgstr "E80: %s dosyası kapatılırken hata: %s" -msgid "E452: Double ; in list of variables" -msgstr "E452: Değişkenler listesinde çifte ;" +#, c-format +msgid "E5142: Failed to open file %s: %s" +msgstr "E5142: %s dosyası açılamadı: %s" #, c-format -msgid "E738: Can't list variables for %s" -msgstr "E738: %s için değişkenler listelenemiyor" +msgid "E5143: Failed to write to file %s: %s" +msgstr "E5143: %s dosyası yazılamadı: %s" -msgid "E996: Cannot lock an environment variable" -msgstr "E996: Ortam değişkeni kilitlenemiyor" +#, c-format +msgid "E5144: Failed to close file %s: %s" +msgstr "E5144: %s dosyası kapatılamadı: %s" -msgid "E996: Cannot lock a register" -msgstr "E996: Yazmaç kilitlenemiyor" +msgid "E6000: Argument is not a function or function name" +msgstr "E6000: Argüman bir işlev veya işlev adı değil" #, c-format -msgid "E108: No such variable: \"%s\"" -msgstr "E108: Böyle bir değişken yok: \"%s\"" +msgid "E737: Key already exists: %s" +msgstr "E737: Anahtar hâlihazırda var: %s" msgid "E743: variable nested too deep for (un)lock" msgstr "E743: Değişken kilitlenemez/kilidi açılamaz, çok iç içe geçmiş" #, c-format -msgid "E963: setting %s to value with wrong type" -msgstr "E963: %s yanlış türe sahip değere ayarlanıyor" +msgid "E741: Value is locked: %.*s" +msgstr "E741: Değer kilitli: %.*s" + +#, c-format +msgid "E742: Cannot change value of %.*s" +msgstr "E742: %.*s ögesinin değeri değiştirilemiyor" + +msgid "Unknown" +msgstr "Bilinmiyor" + +msgid "E805: Expected a Number or a String, Float found" +msgstr "E805: Bir sayı veya dizi bekleniyordu, kayan noktalı değer bulundu" + +msgid "E703: Expected a Number or a String, Funcref found" +msgstr "E703: Bir sayı veya dizi bekleniyordu, Funcref bulundu" + +msgid "E745: Expected a Number or a String, List found" +msgstr "E745: Bir sayı veya dizi bekleniyordu, liste bulundu" + +msgid "E728: Expected a Number or a String, Dictionary found" +msgstr "E728: Bir sayı veya dizi bekleniyordu, sözlük bulundu" + +msgid "E974: Expected a Number or a String, Blob found" +msgstr "E974: Bir sayı veya dizi bekleniyordu, ikili geniş nesne bulundu" + +msgid "E5299: Expected a Number or a String, Boolean found" +msgstr "E5299: Bir sayı veya dizi bekleniyordu, Boole bulundu" + +msgid "E5300: Expected a Number or a String" +msgstr "E5300: Bir sayı veya dizi bekleniyordu" + +msgid "E745: Using a List as a Number" +msgstr "E745: Bir sayı olarak liste kullanılıyor" + +msgid "E728: Using a Dictionary as a Number" +msgstr "E728: Bir sayı olarak liste kullanılıyor" + +msgid "E805: Using a Float as a Number" +msgstr "E805: Bir sayı olarak kayan noktalı değer kullanılıyor" + +msgid "E974: Using a Blob as a Number" +msgstr "E974: Bir Sayı olarak ikili geniş nesne kullanılıyor" + +msgid "E685: using an invalid value as a Number" +msgstr "E685: Bir sayı olarak geçersiz bir değer kullanılıyor" + +msgid "E730: using List as a String" +msgstr "E730: Bir dizi olarak liste kullanılıyor" + +msgid "E731: using Dictionary as a String" +msgstr "E731: Bir dizi olarak sözlük kullanılıyor" + +msgid "E976: using Blob as a String" +msgstr "E976: Bir dizi olarak ikili geniş nesne kullanılıyor" + +msgid "E908: using an invalid value as a String" +msgstr "E908: Bir dizi olarak geçersiz bir değer kullanılıyor" + +msgid "E891: Using a Funcref as a Float" +msgstr "E891: Bir kayan noktalı değer olarak bir Funcref kullanılıyor" + +msgid "E892: Using a String as a Float" +msgstr "E892: Bir kayan noktalı değer olarak bir dizi kullanılıyor" + +msgid "E893: Using a List as a Float" +msgstr "E893: Bir kayan noktalı değer olarak bir liste kullanılıyor" + +msgid "E894: Using a Dictionary as a Float" +msgstr "E894: Bir kayan noktalı değer olarak bir sözlük kullanılıyor" + +msgid "E362: Using a boolean value as a Float" +msgstr "E362: Bir kayan noktalı değer olarak bir Boole kullanılıyor" + +msgid "E907: Using a special value as a Float" +msgstr "E907: Bir Kayan Noktalı Değer olarak bir özel değer kullanılıyor" + +msgid "E975: Using a Blob as a Float" +msgstr "E975: Bir kayan noktalı değer olarak bir ikili geniş nesne kullanılıyor" + +msgid "E808: Number or Float required" +msgstr "E808: Sayı veya kayan noktalı değer gerekiyor" #, c-format -msgid "E795: Cannot delete variable %s" -msgstr "E795: %s değişkeni silinemiyor" +msgid "E122: Function %s already exists, add ! to replace it" +msgstr "E122: %s işlevi hâlihazırda mevcut, değiştirmek için ! ekleyin" + +msgid "E717: Dictionary entry already exists" +msgstr "E717: Sözlük girdisi hâlihazırda mevcut" + +msgid "E718: Funcref required" +msgstr "E718: Funcref gerekiyor" #, c-format -msgid "E704: Funcref variable name must start with a capital: %s" -msgstr "E704: Funcref değişkeni BÜYÜK harf ile başlamalıdır: %s" +msgid "E130: Unknown function: %s" +msgstr "E130: Bilinmeyen işlev: %s" #, c-format -msgid "E705: Variable name conflicts with existing function: %s" -msgstr "E705: Değişken adı mevcut işlevle çakışıyor: %s" +msgid "E125: Illegal argument: %s" +msgstr "E125: İzin verilmeyen argüman: %s" #, c-format -msgid "E741: Value is locked: %s" -msgstr "E741: Değer kilitli: %s" +msgid "E853: Duplicate argument name: %s" +msgstr "E853: Yinelenen argüman adı: %s" -msgid "Unknown" -msgstr "Bilinmiyor" +msgid "E989: Non-default argument follows default argument" +msgstr "E989: Öntanımlı olmayan argüman öntanımlı argümandan sonra" #, c-format -msgid "E742: Cannot change value of %s" -msgstr "E742: %s değeri değiştirilemiyor" +msgid "E740: Too many arguments for function %s" +msgstr "E740: %s işlevi için pek fazla argüman" -msgid "E921: Invalid callback argument" -msgstr "E921: Geçersiz geri çağırma argümanı" +#, c-format +msgid "E116: Invalid arguments for function %s" +msgstr "E116: %s işlevi için geçersiz argümanlar" + +msgid "E132: Function call depth is higher than 'maxfuncdepth'" +msgstr "E132: İşlevin çağırdığı derinlik 'maxfuncdepth'ten daha yüksek" + +#, c-format +msgid "calling %s" +msgstr "%s çağrılıyor" + +#, c-format +msgid "%s aborted" +msgstr "%s durduruldu" + +#, c-format +msgid "%s returning #%<PRId64>" +msgstr "%s, #%<PRId64> döndürüyor" + +#, c-format +msgid "%s returning %s" +msgstr "%s, %s döndürüyor" + +#, c-format +msgid "continuing in %s" +msgstr "%s içinde sürdürülüyor" + +msgid "E699: Too many arguments" +msgstr "E699: Çok fazla argüman" + +#, c-format +msgid "E117: Unknown function: %s" +msgstr "E117: Bilinmeyen işlev: %s" + +#, c-format +msgid "E276: Cannot use function as a method: %s" +msgstr "E276: İşlev bir yöntem olarak kullanılamaz: %s" + +#, c-format +msgid "E933: Function was deleted: %s" +msgstr "E933: İşlev silinmiş: %s" + +#, c-format +msgid "E119: Not enough arguments for function: %s" +msgstr "E119: Şu işlev için yetersiz sayıda argüman: %s" + +#, c-format +msgid "E120: Using <SID> not in a script context: %s" +msgstr "E120: <SID> bir betik bağlamında kullanılmıyor: %s" + +#, c-format +msgid "E725: Calling dict function without Dictionary: %s" +msgstr "E725: dic işlevi bir sözlük olmadan çağrılıyor: %s" + +msgid "E129: Function name required" +msgstr "E129: İşlev adı gerekiyor" + +#, c-format +msgid "E128: Function name must start with a capital or \"s:\": %s" +msgstr "E128: İşlev adı bir BÜYÜK harfle veya \"s:\" ile başlamalı: %s" + +#, c-format +msgid "E884: Function name cannot contain a colon: %s" +msgstr "E884: İşlev adı iki nokta içeremez: %s" + +#, c-format +msgid "E123: Undefined function: %s" +msgstr "E123: Tanımlanmamış işlev: %s" + +#, c-format +msgid "E124: Missing '(': %s" +msgstr "E124: '(' eksik: %s" + +msgid "E862: Cannot use g: here" +msgstr "E862: g: burada kullanılamaz" + +#, c-format +msgid "E932: Closure function should not be at top level: %s" +msgstr "E932: Kapatma işlevi en üst düzeyde olmamalıdır: %s" + +msgid "E126: Missing :endfunction" +msgstr "E126: :endfunction eksik" + +#, c-format +msgid "W22: Text found after :endfunction: %s" +msgstr "W22: :endfunction sonrası metin bulundu: %s" + +#, c-format +msgid "E707: Function name conflicts with variable: %s" +msgstr "E707: İşlev adı şu değişken ile çakışıyor: %s" + +#, c-format +msgid "E127: Cannot redefine function %s: It is in use" +msgstr "E127: %s işlevi yeniden tanımlanamıyor: Şu an kullanımda" + +#, c-format +msgid "E746: Function name does not match script file name: %s" +msgstr "E746: İşlev adı betik dosyası adına eşleşmiyor: %s" + +#, c-format +msgid "E131: Cannot delete function %s: It is in use" +msgstr "E131: %s işlevi silinemiyor: Şu an kullanımda" + +#, c-format +msgid "Cannot delete function %s: It is being used internally" +msgstr "%s işlevi silinemiyor: Şu an program içinde kullanımda" + +msgid "E133: :return not inside a function" +msgstr "E133: :return bir işlev içinde değil" + +msgid "tcp address must be host:port" +msgstr "tcp adresi makine:kapı olmalı" + +msgid "failed to lookup host or port" +msgstr "makine veya kapı bulunamadı" + +msgid "connection refused" +msgstr "bağlantı reddedildi" #, c-format msgid "<%s>%s%s %d, Hex %02x, Oct %03o, Digr %s" @@ -914,14 +1505,12 @@ msgid "E134: Cannot move a range of lines into itself" msgstr "E134: Satırlardan oluşan erim kendi içine taşınamaz" #, c-format -msgid "%ld line moved" -msgid_plural "%ld lines moved" -msgstr[0] "%ld satır taşındı" -msgstr[1] "%ld satır taşındı" +msgid "E482: Can't create file %s" +msgstr "E482: %s dosyası oluşturulamıyor" #, c-format -msgid "%ld lines filtered" -msgstr "%ld satır süzüldü" +msgid "%<PRId64> lines filtered" +msgstr "%<PRId64> satır süzüldü" msgid "E135: *Filter* Autocommands must not change current buffer" msgstr "E135: *Süzgeç* otokomutları şu anki arabelleği değiştirmemelidir" @@ -929,13 +1518,6 @@ msgstr "E135: *Süzgeç* otokomutları şu anki arabelleği değiştirmemelidir" msgid "[No write since last change]\n" msgstr "[Son değişiklikten sonra yazılmadı]\n" -#, c-format -msgid "E503: \"%s\" is not a file or writable device" -msgstr "E503: \"%s\", bir dosya veya yazılabilir aygıt değil" - -msgid "Save As" -msgstr "Farklı Kaydet" - msgid "Write partial file?" msgstr "Dosyanın bir kısmı yazılsın mı?" @@ -955,8 +1537,8 @@ msgid "E768: Swap file exists: %s (:silent! overrides)" msgstr "E768: Takas dosyası mevcut: %s (:silent! geçersiz kılar)" #, c-format -msgid "E141: No file name for buffer %ld" -msgstr "E141: %ld numaralı arabelleğin bir adı yok" +msgid "E141: No file name for buffer %<PRId64>" +msgstr "E141: %<PRId64> numaralı arabelleğin bir adı yok" msgid "E142: File not written: Writing is disabled by 'write' option" msgstr "E142: Dosya yazılamadı: Yazma 'write' seçeneği ile kapatılmış" @@ -983,9 +1565,6 @@ msgstr "" msgid "E505: \"%s\" is read-only (add ! to override)" msgstr "E505: \"%s\" saltokunur (geçersiz kılmak için ! ekleyin)" -msgid "Edit File" -msgstr "Dosya Düzenle" - #, c-format msgid "E143: Autocommands unexpectedly deleted new buffer %s" msgstr "E143: yeni %s arabelleğini otokomutlar beklenmedik bir biçimde sildi" @@ -993,9 +1572,6 @@ msgstr "E143: yeni %s arabelleğini otokomutlar beklenmedik bir biçimde sildi" msgid "E144: non-numeric argument to :z" msgstr "E144: :z için sayısal olmayan argüman" -msgid "E145: Shell commands and some functionality not allowed in rvim" -msgstr "E145: rvim içinde kabuk komutları ve bazı işlevselliğe izin verilmez" - msgid "E146: Regular expressions can't be delimited by letters" msgstr "E146: Düzenli ifadeler harflerle sınırlandırılamaz" @@ -1006,52 +1582,74 @@ msgstr "%s ile değiştir (y/n/a/q/l/^E/^Y)?" msgid "(Interrupted) " msgstr "(Yarıda kesildi) " +msgid "E147: Cannot do :global recursive with a range" +msgstr "E147: :global özyineli olarak bir erim ile yapılamaz" + +msgid "E148: Regular expression missing from global" +msgstr "E148: Düzenli ifade :global'den eksik" + #, c-format -msgid "%ld match on %ld line" -msgid_plural "%ld matches on %ld line" -msgstr[0] "%ld eşleşme, %ld satırda" -msgstr[1] "%ld eşleşme, %ld satırda" +msgid "Pattern found in every line: %s" +msgstr "Dizginin bulunduğu her satır: %s" #, c-format -msgid "%ld substitution on %ld line" -msgid_plural "%ld substitutions on %ld line" -msgstr[0] "%ld değiştirme, %ld satırda" -msgstr[1] "%ld değiştirme, %ld satırda" +msgid "Pattern not found: %s" +msgstr "Dizgi bulunamadı: %s" + +msgid "E478: Don't panic!" +msgstr "E478: Panik yok!" #, c-format -msgid "%ld match on %ld lines" -msgid_plural "%ld matches on %ld lines" -msgstr[0] "%ld eşleşme, %ld satırda" -msgstr[1] "%ld eşleşme, %ld satırda" +msgid "E661: Sorry, no '%s' help for %s" +msgstr "E661: Üzgünüm, '%s' yardımı %s için mevcut değil" #, c-format -msgid "%ld substitution on %ld lines" -msgid_plural "%ld substitutions on %ld lines" -msgstr[0] "%ld değiştirme, %ld satırda" -msgstr[1] "%ld değiştirme, %ld satırda" +msgid "E149: Sorry, no help for %s" +msgstr "E149: Üzgünüm, %s için yardım mevcut değil" -msgid "E147: Cannot do :global recursive with a range" -msgstr "E147: :global özyineli olarak bir erim ile yapılamaz" +#, c-format +msgid "Sorry, help file \"%s\" not found" +msgstr "Üzgünüm, \"%s\" yardım dosyası bulunamadı" -msgid "E148: Regular expression missing from global" -msgstr "E148: Düzenli ifade eksik" +#, c-format +msgid "E151: No match: %s" +msgstr "E151: Eşleşme bulunamadı: %s" #, c-format -msgid "Pattern found in every line: %s" -msgstr "Dizginin bulunduğu her satır: %s" +msgid "E152: Cannot open %s for writing" +msgstr "E152: %s yazma için açılamıyor" #, c-format -msgid "Pattern not found: %s" -msgstr "Dizgi bulunamadı: %s" +msgid "E153: Unable to open %s for reading" +msgstr "E153: %s okuma için açılamıyor" + +#, c-format +msgid "E670: Mix of help file encodings within a language: %s" +msgstr "E670: Bir dilde yardım dosyası kodlamaları karıştı: %s" + +#, c-format +msgid "E154: Duplicate tag \"%s\" in file %s/%s" +msgstr "E154: Şu dosyada yinelenen \"%s\" etiketi: %s/%s" + +#, c-format +msgid "E150: Not a directory: %s" +msgstr "E150: %s, bir dizin değil" msgid "No old files" msgstr "Eski dosya yok" +msgid "E750: First use \":profile start {fname}\"" +msgstr "E750: İlk kullanım \":profile start {dosyaadı}\"" + #, c-format msgid "Save changes to \"%s\"?" msgstr "Değişiklikler şuraya kaydedilsin mi: \"%s\"?" #, c-format +msgid "Close \"%s\"?" +msgstr "\"%s\" kapatılsın mı?" + +#, c-format msgid "E947: Job still running in buffer \"%s\"" msgstr "E947: İş \"%s\" arabelleğinde hâlâ sürüyor" @@ -1062,17 +1660,102 @@ msgstr "E162: \"%s\" arabelleği son değişiklikten sonra yazılmadı" msgid "Warning: Entered other buffer unexpectedly (check autocommands)" msgstr "Uyarı: Diğer arabelleğe aniden girildi (otokomutları denetleyin)" +msgid "E163: There is only one file to edit" +msgstr "E163: Düzenlenecek yalnızca bir dosya var" + +msgid "E164: Cannot go before first file" +msgstr "E164: İlk dosyadan öncesine gidilemez" + +msgid "E165: Cannot go beyond last file" +msgstr "E165: Son dosyadan öteye gidilemez" + +msgid "E610: No argument to delete" +msgstr "E610: Silinecek bir argüman yok" + #, c-format msgid "E666: compiler not supported: %s" msgstr "E666: Derleyici desteklenmiyor: %s" #, c-format -msgid "W20: Required python version 2.x not supported, ignoring file: %s" -msgstr "W20: Gerekli 2.x Python sürümü desteklenmiyor, dosya yok sayılıyor: %s" +msgid "Cannot source a directory: \"%s\"" +msgstr "Dizin kaynak alınamıyor: \"%s\"" + +#, c-format +msgid "could not source \"%s\"" +msgstr "\"%s\" kaynak alınamadı" + +#, c-format +msgid "line %<PRId64>: could not source \"%s\"" +msgstr "%<PRId64>. satır: \"%s\" kaynak alınamadı" + +#, c-format +msgid "sourcing \"%s\"" +msgstr "\"%s\" kaynak alınıyor" + +#, c-format +msgid "line %<PRId64>: sourcing \"%s\"" +msgstr "%<PRId64>. satır: \"%s\" kaynak alınıyor" + +#, c-format +msgid "finished sourcing %s" +msgstr "%s kaynak alınması bitti" + +msgid "modeline" +msgstr "kip satırı" + +msgid "--cmd argument" +msgstr "--cmd argümanı" + +msgid "-c argument" +msgstr "-c argümanı" + +msgid "environment variable" +msgstr "ortam değişkeni" + +msgid "error handler" +msgstr "hata işleyicisi" + +msgid "changed window size" +msgstr "değiştirilen pencere boyutu" + +msgid "Lua" +msgstr "Lua" + +#, c-format +msgid "API client (channel id %<PRIu64>)" +msgstr "API istemcisi (kanal kimliği %<PRIu64>" + +msgid "anonymous :source" +msgstr "anonim :source" + +#, c-format +msgid "anonymous :source (script id %d)" +msgstr "anonim :source (betik kimliği %d)" + +msgid "W15: Warning: Wrong line separator, ^M may be missing" +msgstr "W15: Uyarı: Yanlış satır ayırıcısı, ^M eksik olabilir" + +msgid "E167: :scriptencoding used outside of a sourced file" +msgstr "E167: :scriptencoding kaynak alınmış bir dosyanın dışında kullanıldı" + +msgid "E168: :finish used outside of a sourced file" +msgstr "E168: :finish kaynak alınmış bir dosyanın dışında kullanıldı" + +#, c-format +msgid "Current %slanguage: \"%s\"" +msgstr "Şu anki %sdil: \"%s\"" #, c-format -msgid "W21: Required python version 3.x not supported, ignoring file: %s" -msgstr "W21: Gerekli Python sürümü 3.x desteklenmiyor, dosya yok sayılıyor: %s" +msgid "E197: Cannot set language to \"%s\"" +msgstr "E197: \"%s\" diline ayarlanamıyor" + +#, c-format +msgid "E184: No such user-defined command: %s" +msgstr "E184: Böyle bir kullanıcı tanımlı komut yok: %s" + +#, c-format +msgid "E1237: No such user-defined command in current buffer: %s" +msgstr "E1237: Geçerli arabellekte böyle bir kullanıcı tanımlı komut yok: %s" msgid "Entering Ex mode. Type \"visual\" to go to Normal mode." msgstr "Ex kipine giriliyor. Normal kipe geri dönmek için \"visual\" yazın." @@ -1084,6 +1767,9 @@ msgstr "E501: Dosyanın sonunda" msgid "Executing: %s" msgstr "Çalıştırılıyor: %s" +msgid "line %" +msgstr "satır %" + msgid "E169: Command too recursive" msgstr "E169: Komut çok özyineli" @@ -1097,12 +1783,12 @@ msgstr "Kaynak alınan dosyanın sonu" msgid "End of function" msgstr "İşlevin sonu" +msgid "E464: Ambiguous use of user-defined command" +msgstr "E464: Kullanıcı tanımlı komutun belirsiz kullanımı" + msgid "E492: Not an editor command" msgstr "E492: Bir düzenleyici komutu değil" -msgid "E981: Command not allowed in rvim" -msgstr "E981: Bu komuta rvim'de izin verilmiyor" - msgid "E493: Backwards range given" msgstr "E493: Geriye dönük erim verildi" @@ -1112,33 +1798,79 @@ msgstr "Geriye dönük erim verildi, takas edilebilir" msgid "E494: Use w or w>>" msgstr "E494: w veya w>> kullanın" -msgid "E943: Command table needs to be updated, run 'make cmdidxs'" -msgstr "" -"E943: Komut tablosunun güncellenmesi gerekiyor, 'make cmdidxs' çalıştırın" - msgid "" "INTERNAL: Cannot use EX_DFLALL with ADDR_NONE, ADDR_UNSIGNED or ADDR_QUICKFIX" msgstr "" "DAHİLİ: EX_DFLALL; ADDR_NONE, ADDR_UNSIGNED veya ADDR_QUICKFIX ile birlikte " "kullanılamaz" -msgid "E319: Sorry, the command is not available in this version" +msgid "E943: Command table needs to be updated, run 'make'" +msgstr "E943: Komut tablosunun güncellenmesi gerekiyor, 'make' çalıştırın" + +msgid "E319: The command is not available in this version" msgstr "E319: Üzgünüm, komut bu sürümde mevcut değil" #, c-format -msgid "%d more file to edit. Quit anyway?" -msgid_plural "%d more files to edit. Quit anyway?" -msgstr[0] "Düzenlenecek %d dosya daha var. Yine de çıkılsın mı?" -msgstr[1] "Düzenlenecek %d dosya daha var. Yine de çıkılsın mı?" +msgid "E174: Command already exists: add ! to replace it: %s" +msgstr "E174: Komut zaten mevcut: Değiştirmek için ! ekleyin: %s" + +msgid "" +"\n" +" Name Args Address Complete Definition" +msgstr "" +"\n" +" Ad Dğkl Adres Tam Tanım" + +msgid "No user-defined commands found" +msgstr "Kullanıcı tanımlı bir komut bulunamadı" + +msgid "E175: No attribute specified" +msgstr "E175: Bir öznitelik belirtilmemiş" + +msgid "E176: Invalid number of arguments" +msgstr "E176: Geçersiz argüman sayısı" + +msgid "E177: Count cannot be specified twice" +msgstr "E177: Sayım iki defa belirtilemez" + +msgid "E178: Invalid default value for count" +msgstr "E178: Sayım için geçersiz öntanımlı değer" + +msgid "E179: argument required for -complete" +msgstr "E179: -complete için argüman gerekiyor" + +msgid "E179: argument required for -addr" +msgstr "E179: -addr için argüman gerekiyor" + +#, c-format +msgid "E181: Invalid attribute: %s" +msgstr "E181: Geçersiz öznitelik: %s" + +msgid "E1208: -complete used without -nargs" +msgstr "E1208: -complete, -nargs olmadan kullanıldı" + +msgid "E182: Invalid command name" +msgstr "E182: Geçersiz komut adı" + +msgid "E183: User defined commands must start with an uppercase letter" +msgstr "E183: Kullanıcı tanımlı komutlar BÜYÜK harfle başlamalıdır" + +msgid "E841: Reserved name, cannot be used for user defined command" +msgstr "E841: Ayrılmış ad, kullanıcı tanımlı komut için kullanılamaz" + +#, c-format +msgid "E180: Invalid address type value: %s" +msgstr "E180: Geçersiz adres türü değeri: %s" #, c-format -msgid "E173: %d more file to edit" -msgid_plural "E173: %d more files to edit" -msgstr[0] "E173: Düzenlenecek %d dosya daha var" -msgstr[1] "E173: Düzenlenecek %d dosya daha var" +msgid "E180: Invalid complete value: %s" +msgstr "E180: Geçersiz tam değer: %s" + +msgid "E468: Completion argument only allowed for custom completion" +msgstr "E468: Tamamlama argümanına yalnızca özel tamamlamalarda izin verilir" -msgid "unknown" -msgstr "bilinmeyen" +msgid "E467: Custom completion requires a function argument" +msgstr "E467: Özel tamamlama bir işlev argümanı gerektirir" #, c-format msgid "E185: Cannot find color scheme '%s'" @@ -1153,57 +1885,26 @@ msgstr "E784: Son sekme sayfası kapatılamıyor" msgid "Already only one tab page" msgstr "Zaten bir sekme sayfası var" -msgid "Edit File in new tab page" -msgstr "Dosyayı yeni sekme sayfasında düzenle" - -msgid "Edit File in new window" -msgstr "Dosyayı yeni pencerede düzenle" - #, c-format msgid "Tab page %d" msgstr "Sekme sayfası %d" +msgid "E25: Nvim does not have a built-in GUI" +msgstr "E25: Nvim, bir grafik arabirim ile gelmez" + msgid "No swap file" msgstr "Takas dosyası yok" -msgid "Append File" -msgstr "Dosya iliştir" - -msgid "E747: Cannot change directory, buffer is modified (add ! to override)" -msgstr "" -"E747: Dizin değiştirilemiyor, arabellek değiştirilmiş (geçersiz kılmak " -"için ! ekleyin)" - msgid "E186: No previous directory" msgstr "E186: Öncesinde dizin yok" msgid "E187: Unknown" -msgstr "E187: Bilinmeyen" +msgstr "E187: Bilinmiyor" msgid "E465: :winsize requires two number arguments" msgstr "E465: :winsize iki adet sayı argüman gerektirir" #, c-format -msgid "Window position: X %d, Y %d" -msgstr "Pencere konumu: X %d, Y %d" - -msgid "E188: Obtaining window position not implemented for this platform" -msgstr "E188: Pencere konumunu alma özelliği bu platformda mevcut değil" - -msgid "E466: :winpos requires two number arguments" -msgstr "E466: :winpos iki adet sayı argüman gerektirir" - -msgid "E930: Cannot use :redir inside execute()" -msgstr "E930: :redir, execute() içinde kullanılamaz" - -msgid "Save Redirection" -msgstr "Yeniden yönlendirmeyi kaydet" - -#, c-format -msgid "E739: Cannot create directory: %s" -msgstr "E739: Dizin oluşturulamıyor: %s" - -#, c-format msgid "E189: \"%s\" exists (add ! to override)" msgstr "E189: \"%s\" zaten var (geçersiz kılmak için ! ekleyin)" @@ -1212,31 +1913,26 @@ msgid "E190: Cannot open \"%s\" for writing" msgstr "E190: \"%s\" yazma için açılamıyor" msgid "E191: Argument must be a letter or forward/backward quote" -msgstr "E191: Değişken bir harf veya açma/kapama tırnağı olmalıdır" +msgstr "E191: Değişken bir harf veya açma/kapatma tırnağı olmalıdır" msgid "E192: Recursive use of :normal too deep" msgstr "E192: :normal'in özyineli kullanımı çok derinde" -msgid "E809: #< is not available without the +eval feature" -msgstr "E809: #<, +eval özelliği olmadan kullanılamaz" - msgid "E194: No alternate file name to substitute for '#'" msgstr "E194: '#' yerine koymak için başka dosya adı yok" msgid "E495: no autocommand file name to substitute for \"<afile>\"" -msgstr "E495: \"<afile>\" yerine koymak için otokomut dosya adı yok" +msgstr "E495: \"<odosyası>\" yerine koymak için otokomut dosya adı yok" msgid "E496: no autocommand buffer number to substitute for \"<abuf>\"" -msgstr "E496: \"<abuf>\" yerine koymak için otokomut arabellek numarası yok" +msgstr "" +"E496: \"<oarabelleği>\" yerine koymak için otokomut arabellek numarası yok" msgid "E497: no autocommand match name to substitute for \"<amatch>\"" -msgstr "E497: \"<amatch>\" yerine koymak için otokomut eşleşme adı yok" +msgstr "E497: \"<oeşi>\" yerine koymak için otokomut eşleşme adı yok" msgid "E498: no :source file name to substitute for \"<sfile>\"" -msgstr "E498: \"<sfile>\" yerine koymak için :source dosya adı yok" - -msgid "E489: no call stack to substitute for \"<stack>\"" -msgstr "E489: \"<stack>\" yerine koymak için bir çağrı yığını yok" +msgstr "E498: \"<kdosyası>\" yerine koymak için :source dosya adı yok" msgid "E842: no line number to use for \"<slnum>\"" msgstr "E842: \"<slnum>\" kullanımı için satır numarası yok" @@ -1254,9 +1950,6 @@ msgstr "E500: Boş bir satır olarak değer biçer" msgid "Untitled" msgstr "Adsız" -msgid "E196: No digraphs in this version" -msgstr "E196: Bu sürümde ikili harfler bulunmamaktadır" - msgid "E608: Cannot :throw exceptions with 'Vim' prefix" msgstr "E608: 'Vim' öneki ile kural dışı durumlar :throw edilemez" @@ -1273,8 +1966,8 @@ msgid "Exception discarded: %s" msgstr "Kural dışı durum kenara atıldı: %s" #, c-format -msgid "%s, line %ld" -msgstr "%s, %ld. satır" +msgid "%s, line %<PRId64>" +msgstr "%s, %<PRId64>. satır" #, c-format msgid "Exception caught: %s" @@ -1307,6 +2000,15 @@ msgstr "Yarıda Kesilme" msgid "E579: :if nesting too deep" msgstr "E579: :if'ler çok iç içe geçmiş" +msgid "E580: :endif without :if" +msgstr "E580: :if olmadan :endif" + +msgid "E581: :else without :if" +msgstr "E581: :if olmadan :else" + +msgid "E582: :elseif without :if" +msgstr "E582: :if olmadan :elseif" + msgid "E583: multiple :else" msgstr "E583: Birden fazla :else" @@ -1316,26 +2018,38 @@ msgstr "E584: :else sonrası :elseif" msgid "E585: :while/:for nesting too deep" msgstr "E585: :while/:for çok iç içe geçmiş" +msgid "E586: :continue without :while or :for" +msgstr "E586: :while veya :for olmadan :continue" + +msgid "E587: :break without :while or :for" +msgstr "E587: :while veya :for olmadan :break" + msgid "E732: Using :endfor with :while" msgstr "E732: :endfor, :while ile kullanılıyor" msgid "E733: Using :endwhile with :for" msgstr "E733: :endwhile, :for ile kullanılıyor" -msgid "E579: block nesting too deep" -msgstr "E579: İç içe geçmeler çok derin" - msgid "E601: :try nesting too deep" msgstr "E601: :try çok iç içe geçmiş" +msgid "E603: :catch without :try" +msgstr "E603: :try olmadan :catch" + msgid "E604: :catch after :finally" msgstr "E604: :finally sonrası :catch" -msgid "E193: :enddef not inside a function" -msgstr "E193: :enddef bir işlev içinde değil" +msgid "E606: :finally without :try" +msgstr "E606: :try olmadan :finally" + +msgid "E607: multiple :finally" +msgstr "E607: Birden fazla :finally" + +msgid "E602: :endtry without :try" +msgstr "E602: :try olmadan :endtry" msgid "E193: :endfunction not inside a function" -msgstr "E193: :endfunction bir işlev içinde değil" +msgstr "E193: :endfunction, bir işlev içinde değil" msgid "E788: Not allowed to edit another buffer now" msgstr "E788: Şu anda başka bir arabellek düzenlenemez" @@ -1343,24 +2057,91 @@ msgstr "E788: Şu anda başka bir arabellek düzenlenemez" msgid "E811: Not allowed to change buffer information now" msgstr "E811: Şu anda arabellek bilgisi değiştirilemez" +#, c-format +msgid "E5408: Unable to get g:Nvim_color_cmdline callback: %s" +msgstr "E5408: g:Nvim_color_cmdline geri çağrısı alınamıyor: %s" + +#, c-format +msgid "E5407: Callback has thrown an exception: %s" +msgstr "E5407: Geri çağrı bir istisna döndürdü: %s" + +msgid "E5400: Callback should return list" +msgstr "E5400: Geri çağrı, liste döndürmeli" + +#, c-format +msgid "E5401: List item %i is not a List" +msgstr "E5401: Liste ögesi %i, bir Liste değil" + +#, c-format +msgid "E5402: List item %i has incorrect length: %d /= 3" +msgstr "E5402: %i liste ögesinin uzunluğu yanlış: %d /= 3" + +msgid "E5403: Chunk %i start %" +msgstr "E5403: %i parçası başlangıç %" + +msgid "E5405: Chunk %i start %" +msgstr "E5405: %i parçası başlangıç %" + +msgid "E5404: Chunk %i end %" +msgstr "E5404: %i parçası bitiş %" + +msgid "E5406: Chunk %i end %" +msgstr "E5406: %i parçası bitiş %" + +msgid "tagname" +msgstr "etiket adı" + +msgid " kind file\n" +msgstr " dosya türü\n" + +msgid "'history' option is zero" +msgstr "'history' seçeneği sıfır" + msgid "[Command Line]" msgstr "[Komut Satırı]" msgid "E199: Active window or buffer deleted" msgstr "E199: Etkin pencere veya arabellek silinmiş" +msgid "E854: path too long for completion" +msgstr "E854: Yol tamamlama için çok uzun" + +#, c-format +msgid "" +"E343: Invalid path: '**[number]' must be at the end of the path or be " +"followed by '%s'." +msgstr "" +"E343: Geçersiz yol: '**[sayı]' yolun sonunda olmalı veya sonrasında '%s' " +"gelmelidir" + +#, c-format +msgid "E344: Can't find directory \"%s\" in cdpath" +msgstr "E344: \"%s\" dizini cdpath içinde bulunamadı" + +#, c-format +msgid "E345: Can't find file \"%s\" in path" +msgstr "E345: \"%s\" dosyası yol içinde bulunamadı" + +#, c-format +msgid "E346: No more directory \"%s\" found in cdpath" +msgstr "E346: Başka bir \"%s\" dizini cdpath içinde bulunamadı" + +#, c-format +msgid "E347: No more file \"%s\" found in path" +msgstr "E347: Başka bir \"%s\" dosyası yol içinde bulunamadı" + msgid "E812: Autocommands changed buffer or buffer name" msgstr "E812: Otokomutlar arabelleği veya arabellek adını değiştirdi" +msgid "is a directory" +msgstr "bir dizin" + msgid "Illegal file name" msgstr "İzin verilmeyen dosya adı" msgid "is not a file" msgstr "bir dosya değil" -msgid "is a device (disabled with 'opendevice' option)" -msgstr "bir aygıt ('opendevice' seçeneği ile kapatılır)" - msgid "[New DIRECTORY]" msgstr "[Yeni DİZİN]" @@ -1376,17 +2157,11 @@ msgstr "E200: *ReadPre otokomutları dosyayı okunamaz hale getirdi" msgid "E201: *ReadPre autocommands must not change current buffer" msgstr "E201: *ReadPre otokomutları şu anki arabelleği değiştirmemeli" -msgid "Vim: Reading from stdin...\n" -msgstr "Vim: stdin'den okunuyor...\n" - -msgid "Reading from stdin..." -msgstr "stdin'den okunuyor..." - msgid "E202: Conversion made file unreadable!" msgstr "E202: Dönüştürme dosyayı okunamaz hale getirdi!" msgid "[fifo]" -msgstr "[fifo]" +msgstr "[ilk giren ilk çıkar]" msgid "[socket]" msgstr "[uç nokta]" @@ -1400,13 +2175,19 @@ msgstr "[Eksik CR]" msgid "[long lines split]" msgstr "[uzun satırlar bölünmüş]" +msgid "[NOT converted]" +msgstr "[dönüştürülmedi]" + +msgid "[converted]" +msgstr "[dönüştürüldü]" + #, c-format -msgid "[CONVERSION ERROR in line %ld]" -msgstr "[%ld. satırda DÖNÜŞTÜRME HATASI]" +msgid "[CONVERSION ERROR in line %<PRId64>]" +msgstr "[%<PRId64>. satırda DÖNÜŞTÜRME HATASI]" #, c-format -msgid "[ILLEGAL BYTE in line %ld]" -msgstr "[%ld. satırda GEÇERSİZ BAYT]" +msgid "[ILLEGAL BYTE in line %<PRId64>]" +msgstr "[%<PRId64>. satırda İZİN VERİLMEYEN BAYT]" msgid "[READ ERRORS]" msgstr "[OKUMA HATALARI]" @@ -1420,41 +2201,138 @@ msgstr "'charconvert' ile dönüştürme başarısız" msgid "can't read output of 'charconvert'" msgstr "'charconvert' çıktısı okunamıyor" -msgid "[dos]" -msgstr "[dos]" +msgid "[New File]" +msgstr "[Yeni Dosya]" + +msgid "[New]" +msgstr "[Yeni]" + +msgid "E676: No matching autocommands for acwrite buffer" +msgstr "E676: acwrite arabelleği için eşleşen bir otokomut yok" + +msgid "E203: Autocommands deleted or unloaded buffer to be written" +msgstr "E203: Otokomutlar arabelleği silmiş veya yazılması için kaldırmışlar" + +msgid "E204: Autocommand changed number of lines in unexpected way" +msgstr "E204: Otokomut satır sayısını beklenmeyen biçimde değiştirdi" + +msgid "is not a file or writable device" +msgstr "bir dosya veya yazılabilir aygıt değil" + +msgid "is read-only (add ! to override)" +msgstr "saltokunur (geçersiz kılmak için ! ekleyin)" + +#, c-format +msgid "E303: Unable to create directory \"%s\" for backup file: %s" +msgstr "E303: Yedek dosyası için \"%s\" dizini oluşturulamadı: %s" + +msgid "E506: Can't write to backup file (add ! to override)" +msgstr "E506: Yedek dosyasına yazılamıyor (geçersiz kılmak için ! ekleyin)" + +msgid "E509: Cannot create backup file (add ! to override)" +msgstr "E509: Yedek dosyası oluşturulamıyor (geçersiz kılmak için ! ekleyin)" + +msgid "E510: Can't make backup file (add ! to override)" +msgstr "E510: Yedek dosyası yapılamıyor (geçersiz kılmak için ! ekleyin)" + +msgid "E214: Can't find temp file for writing" +msgstr "E214: Yazma için geçici dosya bulunamıyor" + +msgid "E213: Cannot convert (add ! to write without conversion)" +msgstr "E213: Dönüştürülemiyor (dönüştürmeden yazmak için ! ekleyin)" + +msgid "E166: Can't open linked file for writing" +msgstr "E166: Bağlı dosya yazma için açılamıyor" + +#, c-format +msgid "E212: Can't open file for writing: %s" +msgstr "E212: Dosya yazma için açılamıyor: %s" + +#, c-format +msgid "E512: Close failed: %s" +msgstr "E512: Kapatma başarısız oldu: %s" + +msgid "E513: write error, conversion failed (make 'fenc' empty to override)" +msgstr "" +"E513: Yazma hatası, dönüştürme başarısız (geçersiz kılmak için 'fenc'i boş " +"bırakın)" + +msgid "E513: write error, conversion failed in line %" +msgstr "E513: Yazma hatası, şu satırda dönüştürme başarısız: %" + +msgid "E514: write error (file system full?)" +msgstr "E514: Yazma hatası (dosya sistemi dolu mu?)" + +msgid " CONVERSION ERROR" +msgstr " DÖNÜŞTÜRME HATASI" + +#, c-format +msgid " in line %<PRId64>;" +msgstr " %<PRId64>. satırda;" + +msgid "[Device]" +msgstr "[Aygıt]" + +msgid " [a]" +msgstr " [i]" + +msgid " appended" +msgstr " iliştirildi" + +msgid " [w]" +msgstr " [y]" + +msgid " written" +msgstr " yazıldı" + +msgid "E205: Patchmode: can't save original file" +msgstr "E205: Yama kipi: Orijinal dosya kaydedilemiyor" + +msgid "E206: patchmode: can't touch empty original file" +msgstr "E206: Yama kipi: Orijinal boş dosyaya dokunulamıyor" + +msgid "E207: Can't delete backup file" +msgstr "E207: Yedek dosyası silinemiyor" + +msgid "" +"\n" +"WARNING: Original file may be lost or damaged\n" +msgstr "" +"\n" +"UYARI: Orijinal dosya kaybolmuş veya hasar görmüş olabilir\n" + +msgid "don't quit the editor until the file is successfully written!" +msgstr "dosya başarılı bir biçimde yazılana kadar düzenleyiciden çıkmayın!" msgid "[dos format]" msgstr "[dos biçimi]" -msgid "[mac]" -msgstr "[mac]" +msgid "[dos]" +msgstr "[dos]" msgid "[mac format]" msgstr "[mac biçimi]" -msgid "[unix]" -msgstr "[unix]" +msgid "[mac]" +msgstr "[mac]" msgid "[unix format]" msgstr "[unix biçimi]" -#, c-format -msgid "%ld line, " -msgid_plural "%ld lines, " -msgstr[0] "%ld satır, " -msgstr[1] "%ld satır, " +msgid "[unix]" +msgstr "[unix]" -#, c-format -msgid "%lld byte" -msgid_plural "%lld bytes" -msgstr[0] "%lld bayt" -msgstr[1] "%lld bayt" +msgid "[Incomplete last line]" +msgstr "[Tamamlanmamış son satır]" msgid "[noeol]" -msgstr "[noeol]" +msgstr "[satır sonu yok]" -msgid "[Incomplete last line]" -msgstr "[Tamamlanmamış son satır]" +msgid "WARNING: The file has been changed since reading it!!!" +msgstr "UYARI: Bu dosya açıldıktan sonra başkası tarafından değiştirilmiş!!!" + +msgid "Do you really want to write to it" +msgstr "Yine de yazmak istiyor musunuz?" #, c-format msgid "E208: Error writing to \"%s\"" @@ -1479,9 +2357,7 @@ msgstr "E211: \"%s\" dosyası artık mevcut değil" msgid "" "W12: Warning: File \"%s\" has changed and the buffer was changed in Vim as " "well" -msgstr "" -"W12: Uyarı: \"%s\" dosyası Vim'deki arabellek de dahil olmak üzere " -"değiştirildi" +msgstr "W12: Uyarı: \"%s\" dosyası ve Vim'deki arabellek değişti" msgid "See \":help W12\" for more info." msgstr "Ek bilgi için \":help W12\" yazın." @@ -1510,10 +2386,12 @@ msgstr "Uyarı" msgid "" "&OK\n" -"&Load File" +"&Load File\n" +"Load File &and Options" msgstr "" "&Tamam\n" -"&Dosya Yükle" +"&Dosyayı Yükle\n" +"Dosyayı ve &Seçenekleri Yükle" #, c-format msgid "E462: Could not prepare for reloading \"%s\"" @@ -1529,386 +2407,508 @@ msgstr "E219: { eksik." msgid "E220: Missing }." msgstr "E220: } eksik." -msgid "<empty>" -msgstr "<boş>" - -msgid "E655: Too many symbolic links (cycle?)" -msgstr "E655: Çok fazla sembolik bağlantı (çevrim?)" +msgid "E490: No fold found" +msgstr "E490: Kıvırma bulunamadı" -msgid "writefile() first argument must be a List or a Blob" -msgstr "writefile() ilk argümanı bir liste veya ikili geniş nesne olmalıdır" +msgid "E350: Cannot create fold with current 'foldmethod'" +msgstr "E350: Şu anki 'foldmethod' ile kıvırma oluşturulamıyor" -msgid "Select Directory dialog" -msgstr "Dizin Seç iletişim kutusu" +msgid "E351: Cannot delete fold with current 'foldmethod'" +msgstr "E351: Şu anki 'foldmethod' ile kıvırma silinemiyor" -msgid "Save File dialog" -msgstr "Dosya Kaydet iletişim kutusu" +msgid "E222: Add to read buffer" +msgstr "E222: Okunan arabelleğe ekle" -msgid "Open File dialog" -msgstr "Dosya Aç iletişim kutusu" +msgid "E223: recursive mapping" +msgstr "E223: Özyineli eşlemleme" -msgid "E338: Sorry, no file browser in console mode" -msgstr "E338: Üzgünüm, konsol kipinde dosya tarayıcı yoktur" +#, c-format +msgid "E224: global abbreviation already exists for %s" +msgstr "E224: %s için global kısaltma hâlihazırda var" -msgid "no matches" -msgstr "eşleşme yok" +#, c-format +msgid "E225: global mapping already exists for %s" +msgstr "E225: %s için global eşlemleme hâlihazırda var" -msgid "E854: path too long for completion" -msgstr "E854: Yol tamamlama için çok uzun" +#, c-format +msgid "E226: abbreviation already exists for %s" +msgstr "E226: %s için kısaltma hâlihazırda var" #, c-format -msgid "" -"E343: Invalid path: '**[number]' must be at the end of the path or be " -"followed by '%s'." +msgid "E227: mapping already exists for %s" +msgstr "E227: %s için eşlemleme hâlihazırda var" + +msgid "No abbreviation found" +msgstr "Kısaltma bulunamadı" + +msgid "No mapping found" +msgstr "Eşlemleme bulunamadı" + +msgid "E228: makemap: Illegal mode" +msgstr "E228: makemap: İzin verilmeyen kip" + +msgid "--No lines in buffer--" +msgstr "--Arabellek içinde satır yok--" + +msgid "E470: Command aborted" +msgstr "E470: Komut durduruldu" + +msgid "E905: Cannot set this option after startup" +msgstr "E905: Bu seçenek, başlangıçtan sonra ayarlanamaz" + +msgid "E903: Could not spawn API job" +msgstr "E903: API işi ortaya çıkarılamadı" + +msgid "E471: Argument required" +msgstr "E471: Argüman gerekiyor" + +msgid "E10: \\ should be followed by /, ? or &" +msgstr "E10: \\ sonrasında /, ? veya & gelmeli" + +msgid "E11: Invalid in command-line window; <CR> executes, CTRL-C quits" +msgstr "E11: Komut satırı penceresinde geçersiz; <CR> çalıştırır, CTRL-C çıkar" + +msgid "E12: Command not allowed from exrc/vimrc in current dir or tag search" msgstr "" -"E343: Geçersiz yol: '**[sayı]' yolun sonunda olmalı veya sonrasında '%s' " -"gelmelidir" +"E12: Geçerli dizin veya etiket aramasında exrc veya vimrc'den komutlara izin " +"verilmiyor" + +msgid "E171: Missing :endif" +msgstr "E171: :endif eksik" + +msgid "E600: Missing :endtry" +msgstr "E600: :endtry eksik" + +msgid "E170: Missing :endwhile" +msgstr "E170: :endwhile eksik" + +msgid "E170: Missing :endfor" +msgstr "E170: :endfor eksik" + +msgid "E588: :endwhile without :while" +msgstr "E588: :while olmadan :endwhile" + +msgid "E588: :endfor without :for" +msgstr "E588: :for olmadan :endfor" + +msgid "E13: File exists (add ! to override)" +msgstr "E13: Dosya mevcut (geçersiz kılmak için ! ekleyin)" + +msgid "E472: Command failed" +msgstr "E472: Komut başarısız oldu" + +msgid "E473: Internal error" +msgstr "E473: İçsel hata" #, c-format -msgid "E344: Can't find directory \"%s\" in cdpath" -msgstr "E344: \"%s\" dizini cdpath içinde bulunamadı" +msgid "E685: Internal error: %s" +msgstr "E685: İçsel hata: %s" + +msgid "Interrupted" +msgstr "Yarıda kesildi" + +msgid "E474: Invalid argument" +msgstr "E474: Geçersiz argüman" #, c-format -msgid "E345: Can't find file \"%s\" in path" -msgstr "E345: \"%s\" dosyası yol içinde bulunamadı" +msgid "E475: Invalid argument: %s" +msgstr "E475: Geçersiz argüman: %s" #, c-format -msgid "E346: No more directory \"%s\" found in cdpath" -msgstr "E346: Başka bir \"%s\" dizini cdpath içinde bulunamadı" +msgid "E475: Invalid value for argument %s" +msgstr "E475: %s argümanı için geçersiz değer" #, c-format -msgid "E347: No more file \"%s\" found in path" -msgstr "E347: Başka bir \"%s\" dosyası yol içinde bulunamadı" +msgid "E475: Invalid value for argument %s: %s" +msgstr "E475: %s argümanı için geçersiz değer: %s" -msgid "E446: No file name under cursor" -msgstr "E446: İmleç altında bir dosya adı yok" +#, c-format +msgid "E983: Duplicate argument: %s" +msgstr "E983: Yinelenen argüman: %s" #, c-format -msgid "E447: Can't find file \"%s\" in path" -msgstr "E447: \"%s\" dosyası yol içinde bulunamadı" +msgid "E15: Invalid expression: %s" +msgstr "E15: Geçersiz ifade: %s" -msgid "E808: Number or Float required" -msgstr "E808: Sayı veya kayan noktalı değer gerekiyor" +msgid "E16: Invalid range" +msgstr "E16: Geçersiz erim" -msgid "E490: No fold found" -msgstr "E490: Kıvırma bulunamadı" +msgid "E476: Invalid command" +msgstr "E476: Geçersiz komut" -msgid "E350: Cannot create fold with current 'foldmethod'" -msgstr "E350: Şu anki 'foldmethod' ile kıvırma oluşturulamıyor" +#, c-format +msgid "E17: \"%s\" is a directory" +msgstr "E17: \"%s\" bir dizin" -msgid "E351: Cannot delete fold with current 'foldmethod'" -msgstr "E351: Şu anki 'foldmethod' ile kıvırma silinemiyor" +msgid "E756: Spell checking is not possible" +msgstr "E756: Yazım denetimi olanaklı değil" + +msgid "E900: Invalid channel id" +msgstr "E900: Geçersiz kanal kimliği" + +msgid "E900: Invalid channel id: not a job" +msgstr "E900: Geçersiz kanal kimliği: Bir iş değil" + +msgid "E901: Job table is full" +msgstr "E901: İş tablosu dolu" #, c-format -msgid "+--%3ld line folded " -msgid_plural "+--%3ld lines folded " -msgstr[0] "+--%3ld satır kıvrıldı " -msgstr[1] "+--%3ld satır kıvrıldı " +msgid "E903: Process failed to start: %s: \"%s\"" +msgstr "E903: Süreç başlatılamadı: %s: \"%s\"" + +msgid "E904: channel is not a pty" +msgstr "E904: Kanal bir pty değil" #, c-format -msgid "+-%s%3ld line: " -msgid_plural "+-%s%3ld lines: " -msgstr[0] "+-%s%3ld satır: " -msgstr[1] "+-%s%3ld satır: " +msgid "E905: Couldn't open stdio channel: %s" +msgstr "E905: stdio kanalı açılamadı: %s" -msgid "E222: Add to read buffer" -msgstr "E222: Okuma arabelleğine ekle" +msgid "E906: invalid stream for channel" +msgstr "E906: Kanal için geçersiz akış" -msgid "E223: recursive mapping" -msgstr "E223: Özyineli eşlemleme" +msgid "E906: invalid stream for rpc channel, use 'rpc'" +msgstr "E906: rpc kanalı için geçersiz akış, 'rpc' kullanın" -msgid "E851: Failed to create a new process for the GUI" -msgstr "E851: Grafik arabirim için yeni bir işlem yaratılamadı" +#, c-format +msgid "" +"E5210: dict key '%s' already set for buffered stream in channel %<PRIu64>" +msgstr "E5210: '%s' sözlük anahtarı, %<PRIu64> kanalında halihazırda " +"arabelleklenmiş akış için ayarlandı" -msgid "E852: The child process failed to start the GUI" -msgstr "E852: Alt işlem grafik arabirimini başlatamadı" +#, c-format +msgid "E364: Library call failed for \"%s()\"" +msgstr "E364: \"%s()\" için kitaplık çağrısı başarısız oldu" -msgid "E229: Cannot start the GUI" -msgstr "E229: Grafik arabirimi başlatılamıyor" +#, c-format +msgid "E667: Fsync failed: %s" +msgstr "E667: Fsync başarısız oldu: %s" #, c-format -msgid "E230: Cannot read from \"%s\"" -msgstr "E230: \"%s\" okunamıyor" +msgid "E739: Cannot create directory %s: %s" +msgstr "E739: Dizin oluşturulamıyor: %s: %s" + +msgid "E19: Mark has invalid line number" +msgstr "E19: İm satır numarası geçersiz" -msgid "E665: Cannot start GUI, no valid font found" -msgstr "E665: Grafik arabirim başlatılamıyor, geçerli bir font bulunamadı" +msgid "E20: Mark not set" +msgstr "E20: İm ayarlanmamış" -msgid "E231: 'guifontwide' invalid" -msgstr "E231: 'guifontwide' geçersiz" +msgid "E21: Cannot make changes, 'modifiable' is off" +msgstr "E21: Değişiklik yapılamıyor, 'modifiable' kapalı" -msgid "E599: Value of 'imactivatekey' is invalid" -msgstr "E599: 'imactivatekey' değeri geçersiz" +msgid "E22: Scripts nested too deep" +msgstr "E22: Betikler çok iç içe geçmiş" -msgid "No match at cursor, finding next" -msgstr "İmleç konumunda eşleşme bulunamadı, sonraki bulunuyor" +msgid "E23: No alternate file" +msgstr "E23: Başka bir dosya yok" -msgid "<cannot open> " -msgstr "<açılamıyor> " +msgid "E24: No such abbreviation" +msgstr "E24: Böyle bir kısaltma yok" + +msgid "E477: No ! allowed" +msgstr "E477: ! imine izin verilmiyor" #, c-format -msgid "E616: vim_SelFile: can't get font %s" -msgstr "E616: vim_SelFile: %s fontu bulunamıyor" +msgid "E28: No such highlight group name: %s" +msgstr "E28: Böyle bir vurgulama grup adı yok: %s" -msgid "E614: vim_SelFile: can't return to current directory" -msgstr "E614: vim_SelFile: Şu anki dizine dönülemiyor" +msgid "E29: No inserted text yet" +msgstr "E29: Henüz bir metin eklenmedi" -msgid "Pathname:" -msgstr "Yol adı:" +msgid "E30: No previous command line" +msgstr "E30: Öncesinde komut satırı yok" -msgid "E615: vim_SelFile: can't get current directory" -msgstr "E615: vim_SelFile: Şu anki dizin bulunamıyor" +msgid "E31: No such mapping" +msgstr "E31: Böyle bir eşlem yok" -msgid "OK" -msgstr "Tamam" +msgid "E479: No match" +msgstr "E479: Eşleşme yok" -msgid "Cancel" -msgstr "İptal" +#, c-format +msgid "E480: No match: %s" +msgstr "E480: Eşleşme yok: %s" -msgid "Scrollbar Widget: Could not get geometry of thumb pixmap." -msgstr "Kaydırma Parçacığı: Kenar piksel haritası geometrisi bulunamadı" +msgid "E32: No file name" +msgstr "E32: Dosya adı yok" -msgid "Vim dialog" -msgstr "Vim" +msgid "E33: No previous substitute regular expression" +msgstr "E33: Öncesinde yerine geçen bir düzenli ifade yok" -msgid "E232: Cannot create BalloonEval with both message and callback" -msgstr "E232: Hem ileti hem de geri çağırma ile BallonEval oluşturulamıyor" +msgid "E34: No previous command" +msgstr "E34: Öncesinde komut yok" -msgid "_Save" -msgstr "_Kaydet" +msgid "E35: No previous regular expression" +msgstr "E35: Öncesinde düzenli ifade yok" -msgid "_Open" -msgstr "_Aç" +msgid "E481: No range allowed" +msgstr "E481: Erime izin verilmiyor" -msgid "_Cancel" -msgstr "İ_ptal" +msgid "E36: Not enough room" +msgstr "E36: Yeterli alan yok" -msgid "_OK" -msgstr "_Tamam" +msgid "E483: Can't get temp file name" +msgstr "E483: Geçici dosya adı alınamıyor" -msgid "" -"&Yes\n" -"&No\n" -"&Cancel" -msgstr "" -"&Evet\n" -"&Hayır\n" -"İ&ptal" +#, c-format +msgid "E484: Can't open file %s" +msgstr "E484: %s dosyası açılamıyor" -msgid "Yes" -msgstr "Evet" +#, c-format +msgid "E484: Can't open file %s: %s" +msgstr "E484: %s dosyası açılamıyor: %s" -msgid "No" -msgstr "Hayır" +#, c-format +msgid "E485: Can't read file %s" +msgstr "E485: %s dosyası okunamıyor" -msgid "Input _Methods" -msgstr "Giriş _Yöntemleri" +msgid "E38: Null argument" +msgstr "E38: Anlamsız argüman" -msgid "VIM - Search and Replace..." -msgstr "VİM - Ara ve Değiştir..." +msgid "E39: Number expected" +msgstr "E39: Sayı bekleniyordu" -msgid "VIM - Search..." -msgstr "VİM - Ara..." +#, c-format +msgid "E40: Can't open errorfile %s" +msgstr "E40: Hata dosyası %s açılamıyor" -msgid "Find what:" -msgstr "Bulunacak nesne:" +msgid "E41: Out of memory!" +msgstr "E41: Bellek tükendi!" -msgid "Replace with:" -msgstr "Şununla değiştir:" +msgid "Pattern not found" +msgstr "Dizgi bulunamadı" -msgid "Match whole word only" -msgstr "Tam sözcükleri ara" +#, c-format +msgid "E486: Pattern not found: %s" +msgstr "E486: Dizgi bulunamadı: %s" -msgid "Match case" -msgstr "BÜYÜK/küçük harf duyarlı" +msgid "E487: Argument must be positive" +msgstr "E487: Değişken pozitif olmalı" -msgid "Direction" -msgstr "Yön" +msgid "E459: Cannot go back to previous directory" +msgstr "E459: Bir önceki dizine gidilemiyor" -msgid "Up" -msgstr "Yukarı" +msgid "E42: No Errors" +msgstr "E42: Hata yok" -msgid "Down" -msgstr "Aşağı" +msgid "E776: No location list" +msgstr "E776: Konum listesi yok" -msgid "Find Next" -msgstr "Sonrakini Bul" +msgid "E43: Damaged match string" +msgstr "E43: Hasarlı eşleşme dizisi" -msgid "Replace" -msgstr "Değiştir" +msgid "E44: Corrupted regexp program" +msgstr "E44: Bozulmuş regexp programı" -msgid "Replace All" -msgstr "Tümünü Değiştir" +msgid "E45: 'readonly' option is set (add ! to override)" +msgstr "E45: 'readonly' seçeneği ayarlanmış (geçersiz kılmak için ! ekleyin)" -msgid "_Close" -msgstr "K_apat" +#, c-format +msgid "E46: Cannot change read-only variable \"%.*s\"" +msgstr "E46: Saltokunur değişken \"%.*s\" değiştirilemiyor" -msgid "Vim: Received \"die\" request from session manager\n" -msgstr "Vim: Oturum yöneticisinden işi sonlandırma isteği geldi\n" +msgid "E928: String required" +msgstr "E928: Dizi gerekiyor" -msgid "Close tab" -msgstr "Sekmeyi kapat" +msgid "E715: Dictionary required" +msgstr "E715: Sözlük gerekiyor" -msgid "New tab" -msgstr "Yeni sekme" +#, c-format +msgid "E979: Blob index out of range: %<PRId64>" +msgstr "E979: İkili geniş nesne indeksi erimin dışında: %<PRId64>" -msgid "Open Tab..." -msgstr "Sekme Aç..." +msgid "E978: Invalid operation for Blob" +msgstr "E978: İkili geniş nesne için geçersiz işlem" -msgid "Vim: Main window unexpectedly destroyed\n" -msgstr "Vim: Ana pencere beklenmedik bir biçimde gitti\n" +#, c-format +msgid "E118: Too many arguments for function: %s" +msgstr "E118: İşlev için pek fazla argüman: %s" -msgid "&Filter" -msgstr "&Süz" +#, c-format +msgid "E716: Key not present in Dictionary: \"%s\"" +msgstr "E716: Anahtar sözlükte mevcut değil: \"%s\"" -msgid "&Cancel" -msgstr "İ&ptal" +msgid "E714: List required" +msgstr "E714: Liste gerekiyor" -msgid "Directories" -msgstr "Dizinler" +msgid "E897: List or Blob required" +msgstr "E897: Liste veya ikili geniş nesne gerekiyor" -msgid "Filter" -msgstr "Süzgeç" +#, c-format +msgid "E712: Argument of %s must be a List or Dictionary" +msgstr "E712: %s ögesinin argümanı bir liste veya sözlük olmalıdır" -msgid "&Help" -msgstr "&Yardım" +#, c-format +msgid "E896: Argument of %s must be a List, Dictionary or Blob" +msgstr "E896: %s argümanı bir liste, sözlük veya ikili geniş nesne olmalıdır" -msgid "Files" -msgstr "Dosyalar" +msgid "E47: Error while reading errorfile" +msgstr "E47: Hata dosyası okunurken hata" -msgid "&OK" -msgstr "&Tamam" +msgid "E48: Not allowed in sandbox" +msgstr "E48: Kum havuzunda izin verilmiyor" -msgid "Selection" -msgstr "Seçim" +msgid "E523: Not allowed here" +msgstr "E523: Burada izin verilmiyor" -msgid "Find &Next" -msgstr "Sonrakini &Bul" +msgid "E359: Screen mode setting not supported" +msgstr "E359: Ekran kipi ayarı desteklenmiyor" -msgid "&Replace" -msgstr "&Değiştir" +msgid "E49: Invalid scroll size" +msgstr "E49: Geçersiz kaydırma boyutu" -msgid "Replace &All" -msgstr "Tümünü D&eğiştir" +msgid "E91: 'shell' option is empty" +msgstr "E91: 'shell' seçeneği boş" -msgid "&Undo" -msgstr "&Geri al" +msgid "E255: Couldn't read in sign data!" +msgstr "E255: İşaret verisinde okunamadı!" -msgid "Open tab..." -msgstr "Sekme aç..." +msgid "E72: Close error on swap file" +msgstr "E72: Takas dosyasında kapatma hatası" -msgid "Find string" -msgstr "Dizi bul" +msgid "E73: tag stack empty" +msgstr "E73: Etiket yığını boş" -msgid "Find & Replace" -msgstr "Bul ve Değiştir" +msgid "E74: Command too complex" +msgstr "E74: Komut çok karmaşık" -msgid "Not Used" -msgstr "Kullanılmıyor" +msgid "E75: Name too long" +msgstr "E75: Ad çok uzun" -msgid "Directory\t*.nothing\n" -msgstr "Directory\t*.hiçbir şey\n" +msgid "E76: Too many [" +msgstr "E76: Çok fazla [" -#, c-format -msgid "E671: Cannot find window title \"%s\"" -msgstr "E671: Pencere başlığı \"%s\" bulunamıyor" +msgid "E77: Too many file names" +msgstr "E77: Çok fazla dosya adı" + +msgid "E488: Trailing characters" +msgstr "E488: Sonda fazladan karakterler" #, c-format -msgid "E243: Argument not supported: \"-%s\"; Use the OLE version." -msgstr "E243: \"-%s\" argümanı desteklenmiyor; OLE sürümünü kullanın." +msgid "E488: Trailing characters: %s" +msgstr "E488: Sonda fazladan karakterler: %s" -msgid "E988: GUI cannot be used. Cannot execute gvim.exe." -msgstr "E988: Grafik arabirim kullanılamaz. gvim.exe çalıştırılamadı." +msgid "E78: Unknown mark" +msgstr "E78: Bilinmeyen im" -msgid "E672: Unable to open window inside MDI application" -msgstr "E672: MDI uygulaması içinde pencere açılamıyor" +msgid "E79: Cannot expand wildcards" +msgstr "E79: Joker karakterleri genişletilemiyor" -msgid "Vim E458: Cannot allocate colormap entry, some colors may be incorrect" -msgstr "" -"Vim E458: Renk eşlemi girdisi ayrılamadı, bazı renkler hatalı görünebilir" +msgid "E591: 'winheight' cannot be smaller than 'winminheight'" +msgstr "E591: 'winheight' değeri 'winminheight' değerinden küçük olamaz" -#, c-format -msgid "E250: Fonts for the following charsets are missing in fontset %s:" -msgstr "E250: %s yazıtipi seti içinde şu karakter setleri için fontlar eksik:" +msgid "E592: 'winwidth' cannot be smaller than 'winminwidth'" +msgstr "E592: 'winwidth' değeri 'winminwidth' değerinden küçük olamaz" -#, c-format -msgid "E252: Fontset name: %s" -msgstr "E252: Yazıtipi seti adı: %s" +msgid "E80: Error while writing" +msgstr "E80: Yazma sırasında hata" -#, c-format -msgid "Font '%s' is not fixed-width" -msgstr "'%s' yazıtipi sabit aralıklı değil" +msgid "E939: Positive count required" +msgstr "E939: Pozitif sayım gerekiyor" -#, c-format -msgid "E253: Fontset name: %s" -msgstr "E253: Yazıtipi seti adı: %s" +msgid "E81: Using <SID> not in a script context" +msgstr "E81: <SID> bir betik bağlamında kullanılmıyor" #, c-format -msgid "Font0: %s" -msgstr "Yazıtipi0: %s" +msgid "E107: Missing parentheses: %s" +msgstr "E107: Ayraç eksik: %s" + +msgid "E363: pattern uses more memory than 'maxmempattern'" +msgstr "E363: Dizgi 'maxmempattern' ögesinden daha fazla bellek kullanıyor" + +msgid "E749: empty buffer" +msgstr "E749: Boş arabellek" #, c-format -msgid "Font%d: %s" -msgstr "Yazıtipi%d: %s" +msgid "E86: Buffer %<PRId64> does not exist" +msgstr "E86: Arabellek %<PRId64> mevcut değil" + +msgid "E682: Invalid search pattern or delimiter" +msgstr "E682: Geçersiz arama dizgisi veya sınırlandırıcısı" + +msgid "E139: File is loaded in another buffer" +msgstr "E139: Dosya başka bir arabellekte yüklü" #, c-format -msgid "Font%d width is not twice that of font0" -msgstr "Yazıtipi%d genişliği yazıtipi0 genişliğinin iki katı olmalıdır" +msgid "E764: Option '%s' is not set" +msgstr "E764: '%s' seçeneği ayarlanmamış" + +msgid "E850: Invalid register name" +msgstr "E850: Geçersiz yazmaç adı" #, c-format -msgid "Font0 width: %d" -msgstr "Yazıtipi0 genişliği: %d" +msgid "E919: Directory not found in '%s': \"%s\"" +msgstr "E919: '%s' içinde dizin bulunamadı: \"%s\"" + +msgid "E952: Autocommand caused recursive behavior" +msgstr "E952: Otokomut özyineli davranışa neden oldu" + +msgid "E813: Cannot close autocmd window" +msgstr "E813: Otokomut penceresi kapatılamıyor" #, c-format -msgid "Font%d width: %d" -msgstr "Yazıtipi%d genişliği: %d" +msgid "E686: Argument of %s must be a List" +msgstr "E686: %s argümanı bir liste olmalı" + +msgid "E519: Option not supported" +msgstr "E519: Özellik desteklenmiyor" -msgid "E284: Cannot set IC values" -msgstr "E284: Girdi bağlamı değerleri ayarlanamıyor" +msgid "E856: Filename too long" +msgstr "E856: Dosya adı pek uzun" -msgid "E285: Failed to create input context" -msgstr "E285: Girdi bağlamı oluşturulamadı" +msgid "E806: using Float as a String" +msgstr "E806: Kayan Noktalı Değer, bir Dizi yerine kullanılıyor" -msgid "E286: Failed to open input method" -msgstr "E286: Giriş yöntemi açılamadı" +#, c-format +msgid "E5500: autocmd has thrown an exception: %s" +msgstr "E5500: Otokomut, bir istisna attı: %s" -msgid "E287: Warning: Could not set destroy callback to IM" -msgstr "E287: Uyarı: Giriş yöntemine yok etme geri çağırması ayarlanamadı" +msgid "E5520: <Cmd> mapping must end with <CR>" +msgstr "E5520: <Cmd> eşlemlemesi <CR> ile bitmelidir" -msgid "E288: input method doesn't support any style" -msgstr "E288: Giriş yöntemi herhangi bir biçemi desteklemiyor" +msgid "E5521: <Cmd> mapping must end with <CR> before second <Cmd>" +msgstr "E5521: <Cmd> eşlemlemesi ikinci <Cmd>'den önce <CR> ile bitmelidir" -msgid "E289: input method doesn't support my preedit type" -msgstr "E289: Giriş yöntemi benim ön düzenleme türümü desteklemiyor" +#, c-format +msgid "E5522: <Cmd> mapping must not include %s key" +msgstr "E5522: <Cmd> eşlemlemesi %s anahtarını içermemelidir" -msgid "Invalid font specification" -msgstr "Geçersiz yazıtipi belirtimi" +#, c-format +msgid "E5555: API call: %s" +msgstr "E5555: API çağrısı: %s" -msgid "&Dismiss" -msgstr "So&nlandır" +#, c-format +msgid "E5560: %s must not be called in a lua loop callback" +msgstr "E5560: %s, bir lua döngü geri çağrısında çağrılmamalıdır" -msgid "no specific match" -msgstr "belirli bir eşleşme yok" +msgid "E5601: Cannot close window, only floating window would remain" +msgstr "E5601: Pencere kapatılamıyor, yalnızca yüzen pencere açık kalır" -msgid "Vim - Font Selector" -msgstr "Vim - Yazıtipi Seçicisi" +msgid "E5602: Cannot exchange or rotate float" +msgstr "E5602: Kayan noktalı değer değiştirilemiyor veya döndürülemiyor" -msgid "Name:" -msgstr "Ad:" +msgid "E1142: Non-empty string required" +msgstr "E1142: Boş olmayan dizi gerekiyor" -msgid "Show size in Points" -msgstr "Büyüklüğü puntolarla göster" +msgid "E1155: Cannot define autocommands for ALL events" +msgstr "E1155: Otokomutlar TÜM olaylar için tanımlanamıyor" -msgid "Encoding:" -msgstr "Kodlama:" +msgid "E1240: Resulting text too long" +msgstr "E1240: Ortaya çıkan metin pek uzun" -msgid "Font:" -msgstr "Yazıtipi:" +msgid "E1247: Line number out of range" +msgstr "E1247: Satır numarası erim dışında" -msgid "Style:" -msgstr "Biçem:" +msgid "E1249: Highlight group name too long" +msgstr "E1249: Vurgulama grubu adı pek uzun" -msgid "Size:" -msgstr "Büyüklük:" +msgid "search hit TOP, continuing at BOTTOM" +msgstr "Arama dosyanın BAŞINI geçti, dosyanın SONUNDAN sürüyor" + +msgid "search hit BOTTOM, continuing at TOP" +msgstr "Arama dosyanın SONUNU geçti, dosyanın BAŞINDAN sürüyor" + +msgid " line " +msgstr " satır " msgid "E550: Missing colon" msgstr "E550: İki nokta eksik" @@ -1927,8 +2927,8 @@ msgid "No text to be printed" msgstr "Yazdırılacak metin yok" #, c-format -msgid "Printing page %d (%d%%)" -msgstr "Sayfa yazdırılıyor: %d (%d%%)" +msgid "Printing page %d (%zu%%)" +msgstr "Sayfa %d yazdırılıyor (%%%zu)" #, c-format msgid " Copy %d of %d" @@ -1950,28 +2950,28 @@ msgstr "E624: \"%s\" dosyası açılamıyor" #, c-format msgid "E457: Can't read PostScript resource file \"%s\"" -msgstr "E457: PostScript kaynak dosyası \"%s\" okunamıyor" +msgstr "E457: PostScript özkaynak dosyası \"%s\" okunamıyor" #, c-format msgid "E618: file \"%s\" is not a PostScript resource file" -msgstr "E618: \"%s\" dosyası bir PostScript kaynak dosyası değil" +msgstr "E618: \"%s\" dosyası bir PostScript özkaynak dosyası değil" #, c-format msgid "E619: file \"%s\" is not a supported PostScript resource file" -msgstr "E619: \"%s\" dosyası desteklenen bir PostScript kaynak dosyası değil" +msgstr "E619: \"%s\" dosyası desteklenen bir PostScript özkaynak dosyası değil" #, c-format msgid "E621: \"%s\" resource file has wrong version" -msgstr "E621: \"%s\" kaynak dosyası sürümü hatalı" +msgstr "E621: \"%s\" özkaynak dosyası sürümü hatalı" msgid "E673: Incompatible multi-byte encoding and character set." -msgstr "E673: Uyumsuz çoklu bit kodlaması ve karakter seti." +msgstr "E673: Uyumsuz çoklu bayt kodlaması ve karakter kümesi." msgid "E674: printmbcharset cannot be empty with multi-byte encoding." -msgstr "E674: printmbcharset çoklu bit kodlamada boş olamaz" +msgstr "E674: printmbcharset çoklu bayt kodlamada boş olamaz." msgid "E675: No default font specified for multi-byte printing." -msgstr "E675: Çoklu bit yazdırma için öntanımlı yazıtipi ayarlanmamış." +msgstr "E675: Çoklu bayt yazdırma için öntanımlı yazıtipi ayarlanmamış." msgid "E324: Can't open PostScript output file" msgstr "E324: PostScript çıktı dosyası açılamıyor" @@ -1981,14 +2981,14 @@ msgid "E456: Can't open file \"%s\"" msgstr "E456: \"%s\" dosyası açılamıyor" msgid "E456: Can't find PostScript resource file \"prolog.ps\"" -msgstr "E456: PostScript kaynak dosyası \"prolog.ps\" bulunamıyor" +msgstr "E456: PostScript özkaynak dosyası \"prolog.ps\" bulunamıyor" msgid "E456: Can't find PostScript resource file \"cidfont.ps\"" -msgstr "E456: PostScript kaynak dosyası \"cidfont.ps\" bulunamıyor" +msgstr "E456: PostScript özkaynak dosyası \"cidfont.ps\" bulunamıyor" #, c-format msgid "E456: Can't find PostScript resource file \"%s.ps\"" -msgstr "E456: PostScript kaynak dosyası \"%s.ps\" bulunamıyor" +msgstr "E456: PostScript özkaynak dosyası \"%s.ps\" bulunamıyor" #, c-format msgid "E620: Unable to convert to print encoding \"%s\"" @@ -2001,49 +3001,10 @@ msgid "E365: Failed to print PostScript file" msgstr "E365: PostScript dosyası yazdırılamadı" msgid "Print job sent." -msgstr "Yazdırma işi gönderildi" - -msgid "E478: Don't panic!" -msgstr "E478: Panik yok!" - -#, c-format -msgid "E661: Sorry, no '%s' help for %s" -msgstr "E661: Üzgünüm, '%s' yardımı %s için mevcut değil" - -#, c-format -msgid "E149: Sorry, no help for %s" -msgstr "E149: Üzgünüm, %s için yardım mevcut değil" - -#, c-format -msgid "Sorry, help file \"%s\" not found" -msgstr "Üzgünüm, \"%s\" yardım dosyası bulunamadı" - -#, c-format -msgid "E151: No match: %s" -msgstr "E151: Eşleşme bulunamadı: %s" - -#, c-format -msgid "E152: Cannot open %s for writing" -msgstr "E152: %s yazma için açılamıyor" +msgstr "Yazdırma işi gönderildi." -#, c-format -msgid "E153: Unable to open %s for reading" -msgstr "E153: %s okuma için açılamıyor" - -#, c-format -msgid "E670: Mix of help file encodings within a language: %s" -msgstr "E670: Bir dilde yardım dosyası kodlamaları karıştı: %s" - -#, c-format -msgid "E154: Duplicate tag \"%s\" in file %s/%s" -msgstr "E154: Şu dosyada yinelenen \"%s\" etiketi: %s/%s" - -#, c-format -msgid "E150: Not a directory: %s" -msgstr "E150: %s bir dizin değil" - -msgid "E679: recursive loop loading syncolor.vim" -msgstr "E679: syncolor.vim yüklenirken özyineli döngü" +msgid "E424: Too many different highlighting attributes in use" +msgstr "E424: Çok fazla değişik vurgulama kuralları kullanılıyor" #, c-format msgid "E411: highlight group not found: %s" @@ -2082,24 +3043,14 @@ msgstr "E419: Bilinmeyen metin rengi" msgid "E420: BG color unknown" msgstr "E420: Bilinmeyen ardalan rengi" -msgid "E453: UL color unknown" -msgstr "E453: Bilinmeyen alt çizme rengi" - #, c-format msgid "E421: Color name or number not recognized: %s" msgstr "E421: Renk adı veya numarası tanımlanamadı: %s" #, c-format -msgid "E422: terminal code too long: %s" -msgstr "E422: Uçbirim kodu çok uzun: %s" - -#, c-format msgid "E423: Illegal argument: %s" msgstr "E423: İzin verilmeyen argüman: %s" -msgid "E424: Too many different highlighting attributes in use" -msgstr "E424: Çok fazla değişik vurgulama kuralları kullanılıyor" - msgid "E669: Unprintable character in group name" msgstr "E669: Grup adında yazdırılamayan karakter" @@ -2144,20 +3095,17 @@ msgstr "E257: cstag: Etiket bulunamadı" msgid "E563: stat(%s) error: %d" msgstr "E563: stat(%s) hatası: %d" -msgid "E563: stat error" -msgstr "E563: stat hatası" - #, c-format msgid "E564: %s is not a directory or a valid cscope database" -msgstr "E564: %s bir dizin veya geçerli bir cscope veritabanı değil" +msgstr "E564: %s, bir dizin veya geçerli bir cscope veritabanı değil" #, c-format msgid "Added cscope database %s" msgstr "cscope veritabanı %s eklendi" #, c-format -msgid "E262: error reading cscope connection %d" -msgstr "E262: cscope bağlantısı %d okunurken hata" +msgid "E262: error reading cscope connection %<PRIu64>" +msgstr "E262: cscope bağlantısı %<PRIu64> okunurken hata" msgid "E561: unknown cscope search type" msgstr "E561: Bilinmeyen cscope arama türü" @@ -2181,7 +3129,7 @@ msgid "cs_create_connection: fdopen for fr_fp failed" msgstr "cs_create_connection: fr_fp için fdopen başarısız oldu" msgid "E623: Could not spawn cscope process" -msgstr "E623: cscope işlemi ortaya çıkarılamadı" +msgstr "E623: cscope süreci ortaya çıkarılamadı" msgid "E567: no cscope connections" msgstr "E567: cscope bağlantıları yok" @@ -2224,13 +3172,6 @@ msgstr "" " s: Bu \"C\" sembolünü bul\n" " t: Bu metin dizisini bul\n" -#, c-format -msgid "E625: cannot open cscope database: %s" -msgstr "E625: cscope veritabanı açılamıyor: %s" - -msgid "E626: cannot get cscope database information" -msgstr "E626: cscope veritabanı bilgisi alınamıyor" - msgid "E568: duplicate cscope database not added" msgstr "E568: Yinelenen cscope veritabanı eklenmemiş" @@ -2272,389 +3213,109 @@ msgstr "cscope bağlantısı yok\n" msgid " # pid database name prepend path\n" msgstr " # pid veritabanı adı başlangıç yolu\n" -msgid "Lua library cannot be loaded." -msgstr "Lua kitaplığı yüklenemedi." - -msgid "cannot save undo information" -msgstr "Geri al bilgisi kaydedilemiyor" - -msgid "" -"E815: Sorry, this command is disabled, the MzScheme libraries could not be " -"loaded." -msgstr "" -"E815: Üzgünüm, bu komut etkin değil, MzScheme kitaplıkları yüklenemedi." - -msgid "" -"E895: Sorry, this command is disabled, the MzScheme's racket/base module " -"could not be loaded." +msgid "Type number and <Enter> or click with the mouse (q or empty cancels): " msgstr "" -"E895: Üzgünüm, bu komut etkin değil, MzScheme'in racket/base birimi " -"yüklenemedi." - -msgid "invalid expression" -msgstr "geçersiz ifade" - -msgid "expressions disabled at compile time" -msgstr "ifadeler derleme aşamasında kapatılmış" - -msgid "hidden option" -msgstr "gizli seçenek" - -msgid "unknown option" -msgstr "bilinmeyen seçenek" - -msgid "window index is out of range" -msgstr "pencere sırası erimin dışında" - -msgid "couldn't open buffer" -msgstr "arabellek açılamadı" - -msgid "cannot delete line" -msgstr "satır silinemiyor" - -msgid "cannot replace line" -msgstr "satır değiştirilemiyor" - -msgid "cannot insert line" -msgstr "satır eklenemiyor" - -msgid "string cannot contain newlines" -msgstr "dizi \"yeni satır\" imi içeremez" - -msgid "error converting Scheme values to Vim" -msgstr "Scheme değerlerini Vim değerlerine dönüştürürken hata" - -msgid "Vim error: ~a" -msgstr "Vim hatası: ~a" - -msgid "Vim error" -msgstr "Vim hatası" - -msgid "buffer is invalid" -msgstr "arabellek geçersiz" - -msgid "window is invalid" -msgstr "pencere geçersiz" - -msgid "linenr out of range" -msgstr "linenr erimin dışında" - -msgid "not allowed in the Vim sandbox" -msgstr "Vim kum havuzunda izin verilmiyor" +"Sayı girip <Enter>'a veya fare düğmesine basın (q veya boş iptal eder): " -msgid "E836: This Vim cannot execute :python after using :py3" -msgstr "E836: Bu Vim :py3 komutundan sonra :python komutunu çalıştıramaz" +msgid "Type number and <Enter> (q or empty cancels): " +msgstr "Sayı girip <Enter>'a basın (q veya boş iptal eder): " -msgid "" -"E263: Sorry, this command is disabled, the Python library could not be " -"loaded." -msgstr "E263: Üzgünüm, bu komut etkin değil, Python kitaplığı yüklenemedi" +#, c-format +msgid "E1502: Lua failed to grow stack to %i" +msgstr "E1502: Lua, yığını %i olarak büyütemedi" msgid "" -"E887: Sorry, this command is disabled, the Python's site module could not be " -"loaded." +"E5100: Cannot convert given lua table: table should either have a sequence " +"of positive integer keys or contain only string keys" msgstr "" -"E887: Üzgünüm, bu komut etkin değil, Python'un site birimi yüklenemedi." - -msgid "E659: Cannot invoke Python recursively" -msgstr "E659: Python özyineli olarak çalıştırılamıyor" - -msgid "E837: This Vim cannot execute :py3 after using :python" -msgstr "E837: Bu Vim :python komutundan sonra :py3 komutunu çalıştıramaz" +"E5100: Verilen lua tablosu dönüştürülemiyor: Tabloda ya bir tamsayı anahtar " +"sıramalası olmalı veya yalnızca dizi anahtarlar içermeli" -msgid "E265: $_ must be an instance of String" -msgstr "E265: $_ bir dizi örneği olmalıdır" - -msgid "" -"E266: Sorry, this command is disabled, the Ruby library could not be loaded." -msgstr "E266: Üzgünüm, bu komut etkin değil, Ruby kitaplığı yüklenemedi." - -msgid "E267: unexpected return" -msgstr "E267: Beklenmeyen dönüş" - -msgid "E268: unexpected next" -msgstr "E268: Beklenmeyen sonraki" - -msgid "E269: unexpected break" -msgstr "E269: Beklenmeyen kesme" - -msgid "E270: unexpected redo" -msgstr "E270: Beklenmeyen yinele komutu" - -msgid "E271: retry outside of rescue clause" -msgstr "E271: retry, rescue işlecinin dışında" - -msgid "E272: unhandled exception" -msgstr "E272: İşletilemeyen kural dışı durum" +msgid "E5101: Cannot convert given lua type" +msgstr "E5101: Verilen lua türü dönüştürülemiyor" #, c-format -msgid "E273: unknown longjmp status %d" -msgstr "E273: Bilinmeyen longjmp durumu %d" - -msgid "invalid buffer number" -msgstr "Geçersiz arabellek numarası" - -msgid "not implemented yet" -msgstr "henüz uygulanmadı" - -msgid "cannot set line(s)" -msgstr "satır(lar) ayarlanamıyor" - -msgid "invalid mark name" -msgstr "geçersiz im adı" - -msgid "mark not set" -msgstr "im ayarlanmamış" +msgid "E5102: Lua failed to grow stack to %i" +msgstr "E5102: Lua, yığını %i olarak büyütemedi" #, c-format -msgid "row %d column %d" -msgstr "satır %d sütun %d" - -msgid "cannot insert/append line" -msgstr "satır eklenemiyor/iliştirilemiyor" +msgid "Error executing vim.schedule lua callback: %.*s" +msgstr "vim.schedule lua geri çağrısı çalıştırılırken hata: %.*s" -msgid "line number out of range" -msgstr "satır numarası erimin dışında" +msgid "E970: Failed to initialize lua interpreter\n" +msgstr "E970: lua yorumlayıcısı ilklendirilemedi\n" -msgid "unknown flag: " -msgstr "geçersiz bayrak: " - -msgid "unknown vimOption" -msgstr "geçersiz vimOption" - -msgid "keyboard interrupt" -msgstr "klavye araya girdi" - -msgid "cannot create buffer/window command: object is being deleted" -msgstr "arabellek/pencere komutu oluşturulamadı: öge şu anda siliniyor" - -msgid "" -"cannot register callback command: buffer/window is already being deleted" -msgstr "geri çağırma komutu kaydedilemiyor: arabellek/pencere zaten siliniyor" - -msgid "" -"E280: TCL FATAL ERROR: reflist corrupt!? Please report this to vim-dev@vim." -"org" -msgstr "" -"E280: ONULMAZ TCL HATASI: Başvuru listesi hasar görmüş! Lütfen bunu vim-" -"dev@vim.org adresine bildirin" - -msgid "cannot register callback command: buffer/window reference not found" -msgstr "" -"geri çağırma komutu kaydedilemiyor: arabellek/pencere başvurusu bulunamadı" - -msgid "" -"E571: Sorry, this command is disabled: the Tcl library could not be loaded." -msgstr "E571: Üzgünüm, bu komut etkin değil: Tcl kitaplığı yüklenemedi." +msgid "E970: Failed to initialize builtin lua modules\n" +msgstr "E970: Gömülü lua modülleri ilklendirilemedi\n" #, c-format -msgid "E572: exit code %d" -msgstr "E572: %d çıkış kodu" - -msgid "cannot get line" -msgstr "satır alınamıyor" - -msgid "Unable to register a command server name" -msgstr "Bir komut sunucusu adı kaydedilemiyor" - -msgid "E248: Failed to send command to the destination program" -msgstr "E248: Hedef programa komut gönderimi başarısız oldu" +msgid "E5114: Error while converting print argument #%i: %.*s" +msgstr "E5114: Yazdırma argümanı #%i dönüştürülürken hata: %.*s" #, c-format -msgid "E573: Invalid server id used: %s" -msgstr "E573: Geçersiz sunucu kimliği kullanıldı: %s" - -msgid "E251: VIM instance registry property is badly formed. Deleted!" -msgstr "E251: VİM oturumu kayıt değeri düzgün oluşturulmamış. Silindi!" +msgid "E5115: Error while loading debug string: %.*s" +msgstr "E5115: Hata ayıklama dizisi yüklenirken hata: %.*s" #, c-format -msgid "%ld lines to indent... " -msgstr "girintilenecek %ld satır kaldı... " +msgid "E5116: Error while calling debug string: %.*s" +msgstr "E5116: Hata ayıklama dizisi çağrılırken hata: %.*s" #, c-format -msgid "%ld line indented " -msgid_plural "%ld lines indented " -msgstr[0] "%ld satır girintilendi " -msgstr[1] "%ld satır girintilendi" - -msgid " Keyword completion (^N^P)" -msgstr " Anahtar sözcük tamamlaması (^N^P)" - -msgid " ^X mode (^]^D^E^F^I^K^L^N^O^Ps^U^V^Y)" -msgstr " ^X kipi (^]^D^E^F^I^K^L^N^O^Ps^U^V^Y)" - -msgid " Whole line completion (^L^N^P)" -msgstr " Tam satır tamamlaması (^L^N^P)" - -msgid " File name completion (^F^N^P)" -msgstr " Dosya adı tamamlaması (^F^N^P)" - -msgid " Tag completion (^]^N^P)" -msgstr " Etiket tamamlaması (^]^N^P)" - -msgid " Path pattern completion (^N^P)" -msgstr " Yol dizgisi tamamlaması (^N^P)" - -msgid " Definition completion (^D^N^P)" -msgstr " Tanım tamamlaması (^D^N^P)" - -msgid " Dictionary completion (^K^N^P)" -msgstr " Sözlük tamamlaması (^K^N^P)" - -msgid " Thesaurus completion (^T^N^P)" -msgstr " Eşanlamlılar sözlüğü tamamlaması (^T^N^P)" - -msgid " Command-line completion (^V^N^P)" -msgstr " Komut satırı tamamlaması (^V^N^P)" - -msgid " User defined completion (^U^N^P)" -msgstr " Kullanıcı tanımlı tamamlamalar (^U^N^P)" - -msgid " Omni completion (^O^N^P)" -msgstr " Omni tamamlaması (^O^N^P)" - -msgid " Spelling suggestion (s^N^P)" -msgstr " Yazım önerisi (s^N^P)" - -msgid " Keyword Local completion (^N^P)" -msgstr " Dahili anahtar sözcük tamamlaması (^N^P)" - -msgid "Hit end of paragraph" -msgstr "Paragrafın sonuna varıldı" - -msgid "E840: Completion function deleted text" -msgstr "E840: Tamamlama işlevi metni sildi" - -msgid "'dictionary' option is empty" -msgstr "'dictionary' seçeneği boş" - -msgid "'thesaurus' option is empty" -msgstr "'thesaurus' seçeneği boş" +msgid "E5108: Error executing Lua function: %.*s" +msgstr "E5108: Lua işlevi çalıştırılırken hata: %.*s" #, c-format -msgid "Scanning dictionary: %s" -msgstr "Sözlük taranıyor: %s" - -msgid " (insert) Scroll (^E/^Y)" -msgstr " (ekle) Kaydır (^E/^Y)" - -msgid " (replace) Scroll (^E/^Y)" -msgstr " (değiştir) Kaydır (^E/^Y)" - -msgid "E785: complete() can only be used in Insert mode" -msgstr "E785: complete() yalnızca Ekleme kipinde kullanılabilir" +msgid "E5107: Error loading lua %.*s" +msgstr "E5107: lua %.*s yüklenirken hata" #, c-format -msgid "Scanning: %s" -msgstr "Taranıyor: %s" +msgid "E5108: Error executing lua %.*s" +msgstr "E5108: lua %.*s çalıştırılırken hata" -msgid "Scanning tags." -msgstr "Etiketler taranıyor..." - -msgid "match in file" -msgstr "dosya içinde eşleşme" - -msgid " Adding" -msgstr " Ekleniyor" - -msgid "-- Searching..." -msgstr "-- Aranıyor..." - -msgid "Back at original" -msgstr "Başlangıca geri dönüldü" - -msgid "Word from other line" -msgstr "Sözcük diğer satırdan" +#, c-format +msgid "Error executing lua callback: %.*s" +msgstr "lua geri çağrısı çalıştırılırken hata: %.*s" -msgid "The only match" -msgstr "Tek eşleşen" +msgid "cannot save undo information" +msgstr "Geri al bilgisi kaydedilemiyor" #, c-format -msgid "match %d of %d" -msgstr "eşleşme %d/%d" +msgid "E5109: Error loading lua: %.*s" +msgstr "E5109: lua yüklenirken hata: %.*s" #, c-format -msgid "match %d" -msgstr "eşleşme %d" - -msgid "E920: _io file requires _name to be set" -msgstr "E920: _io dosyası _name ayarlı olmasını gerektirir" - -msgid "E915: in_io buffer requires in_buf or in_name to be set" -msgstr "E915: in_io arabelleği in_buf veya in_name ayarlı olmasını gerektirir" +msgid "E5110: Error executing lua: %.*s" +msgstr "E5110: lua çalıştırılırken hata: %.*s" #, c-format -msgid "E918: buffer must be loaded: %s" -msgstr "E918: Arabellek yüklenmiş olmalıdır: %s" - -msgid "E916: not a valid job" -msgstr "E916: Geçerli bir iş değil" +msgid "E5111: Error calling lua: %.*s" +msgstr "E5111: lua çağrılırken hata: %.*s" #, c-format -msgid "E491: json decode error at '%s'" -msgstr "E491: '%s' konumunda json çözümü hatası" +msgid "E5112: Error while creating lua chunk: %.*s" +msgstr "E5112: lua parçası oluşturulurken hata: %.*s" #, c-format -msgid "E938: Duplicate key in JSON: \"%s\"" -msgstr "E938: JSON'da yinelenmiş anahtar: \"%s\"" +msgid "E5113: Error while calling lua chunk: %.*s" +msgstr "E5113: lua parçası çağrılırken hata: %.*s" #, c-format -msgid "E899: Argument of %s must be a List or Blob" -msgstr "E899: %s argümanı bir liste veya ikili geniş nesne olmalıdır" - -msgid "E900: maxdepth must be non-negative number" -msgstr "E900: maxdepth negatif olmayan bir sayı olmalı" - -msgid "flatten() argument" -msgstr "flatten() argümanı" +msgid "Error executing vim._expand_pat: %.*s" +msgstr "vim._expand_pat çalıştırılırken hata: %.*s" #, c-format -msgid "E696: Missing comma in List: %s" -msgstr "E696: Listede virgül eksik: %s" - -msgid "sort() argument" -msgstr "sort() argümanı" - -msgid "uniq() argument" -msgstr "uniq() argümanı" - -msgid "E702: Sort compare function failed" -msgstr "E702: Sıralayıp karşılaştırma işlevi başarısız oldu" - -msgid "E882: Uniq compare function failed" -msgstr "E882: Benzersizlik karşılaştırma işlevi başarısız oldu" - -msgid "map() argument" -msgstr "map() argümanı" - -msgid "mapnew() argument" -msgstr "mapnew() argümanı" - -msgid "filter() argument" -msgstr "filter() argümanı" - -msgid "add() argument" -msgstr "add() argümanı" - -msgid "extendnew() argument" -msgstr "extendnew() argümanı" - -msgid "insert() argument" -msgstr "insert() argümanı" - -msgid "remove() argument" -msgstr "remove() argümanı" - -msgid "reverse() argument" -msgstr "reverse() argümanı" +msgid "Error executing vim.on_key Lua callback: %.*s" +msgstr "vim.on_key Lua geri çağrısı çalıştırılırken hata: %.*s" #, c-format -msgid "Current %slanguage: \"%s\"" -msgstr "Şu anki %sdil: \"%s\"" +msgid "Error executing Lua callback: %.*s" +msgstr "Lua geri çağrısı çalıştırılırken hata: %.*s" -#, c-format -msgid "E197: Cannot set language to \"%s\"" -msgstr "E197: \"%s\" diline ayarlanamıyor" +msgid "Argument missing after" +msgstr "Şundan sonra argüman eksik:" + +msgid "Garbage after option argument" +msgstr "Seçenek argümanından sonra anlamsız veri" msgid "Unknown option argument" msgstr "Bilinmeyen seçenek argümanı" @@ -2662,440 +3323,202 @@ msgstr "Bilinmeyen seçenek argümanı" msgid "Too many edit arguments" msgstr "Çok fazla düzenleme argümanı" -msgid "Argument missing after" -msgstr "Şundan sonra argüman eksik:" - -msgid "Garbage after option argument" -msgstr "Seçenek argümanından sonra anlamsız veri" - msgid "Too many \"+command\", \"-c command\" or \"--cmd command\" arguments" msgstr "Çok fazla \"+komut\", \"-c komut\" veya \"--cmd komut\" argümanı" -msgid "Invalid argument for" -msgstr "Şunun için geçersiz argüman:" - #, c-format -msgid "%d files to edit\n" -msgstr "%d dosya düzenleniyor\n" +msgid "E5421: Failed to open stdin: %s" +msgstr "E5421: stdin açılamadı: %s" -msgid "netbeans is not supported with this GUI\n" -msgstr "NetBeans bu grafik arabirimde desteklenmiyor\n" - -msgid "'-nb' cannot be used: not enabled at compile time\n" -msgstr "'-nb' kullanılamaz: Derleme sırasında etkinleştirilmemiş\n" - -msgid "This Vim was not compiled with the diff feature." -msgstr "Bu Vim karşılaştırma özelliği ile derlenmemiş" - -msgid "Attempt to open script file again: \"" -msgstr "Betik dosyası yeniden açılmaya çalışılıyor: \"" +#, c-format +msgid "Attempt to open script file again: \"%s %s\"\n" +msgstr "Betik dosyası yeniden açılmaya çalışılıyor: \"%s %s\"\n" -msgid "Cannot open for reading: \"" -msgstr "Okuma için açılamıyor: \"" +#, c-format +msgid "Cannot open for reading: \"%s\": %s\n" +msgstr "Okuma için açılamıyor: \"%s\": %s\n" msgid "Cannot open for script output: \"" msgstr "Betik çıktısı için açılamıyor: \"" -msgid "Vim: Error: Failure to start gvim from NetBeans\n" -msgstr "Vim: Hata: gvim'i NetBeans içinden başlatma başarısız oldu\n" - -msgid "Vim: Error: This version of Vim does not run in a Cygwin terminal\n" -msgstr "Vim: Hata: Vim'in bu sürümü bir Cygwin uçbirimi içinde çalışmaz\n" - -msgid "Vim: Warning: Output is not to a terminal\n" -msgstr "Vim: Uyarı: Çıktı bir uçbirime değil\n" - -msgid "Vim: Warning: Input is not from a terminal\n" -msgstr "Vim: Uyarı: Girdi bir uçbirimden değil\n" +msgid "--embed conflicts with -es/-Es" +msgstr "--embed, -es/-Es ile çakışıyor" msgid "pre-vimrc command line" msgstr "vimrc uygulanma öncesi komut satırı" #, c-format +msgid "E5422: Conflicting configs: \"%s\" \"%s\"" +msgstr "E5422: Çakışan yapılandırmalar: \"%s\" \"%s\"" + +#, c-format msgid "E282: Cannot read from \"%s\"" msgstr "E282: Şuradan okunamıyor: \"%s\"" msgid "" "\n" -"More info with: \"vim -h\"\n" +"More info with \"" msgstr "" "\n" -"Daha fazla bilgi için: \"vim -h\"\n" - -msgid "[file ..] edit specified file(s)" -msgstr "[dosya ..] belirlenen dosyaları düzenle" - -msgid "- read text from stdin" -msgstr "- stdin'den metni oku" +"Daha fazla bilgi için: \"" -msgid "-t tag edit file where tag is defined" -msgstr "-t etiket etiket tanımlanan dosyaları düzenle" +msgid "Usage:\n" +msgstr "Kullanım:\n" -msgid "-q [errorfile] edit file with first error" -msgstr "-q [hatalıd.] hata içeren ilk dosyayı düzenle" - -msgid "" -"\n" -"\n" -"Usage:" +msgid " nvim [options] [file ...] Edit file(s)\n" msgstr "" -"\n" -"\n" -"Kullanım:" - -msgid " vim [arguments] " -msgstr " vim [argümanlar] " +"nvim [seçenekler] [dosya ...] Dosyaları düzenle\n" -msgid "" -"\n" -" or:" +msgid " nvim [options] -t <tag> Edit file where tag is defined\n" msgstr "" -"\n" -" veya:" +"nvim [seçenekler] -t <etiket> Etiketin tanımlandığı dosyayı düzenle\n" -msgid "" -"\n" -"Where case is ignored prepend / to make flag upper case" +msgid " nvim [options] -q [errorfile] Edit file with first error\n" msgstr "" -"\n" -"BÜYÜK/küçük harfin yok sayıldığı yerde bayrağı BÜYÜK harfli yapmak için " -"başına / koyun" +"nvim [seçenekler] -q [hatadosyası] İlk hatalı dosyayı düzenle\n" msgid "" "\n" -"\n" -"Arguments:\n" +"Options:\n" msgstr "" "\n" -"\n" -"Değişkenler:\n" - -msgid "--\t\t\tOnly file names after this" -msgstr "--\t\t\tBundan sonra yalnızca dosya adları" - -msgid "--literal\t\tDon't expand wildcards" -msgstr "--literal\t\tJoker karakterleri genişletme!" - -msgid "-register\t\tRegister this gvim for OLE" -msgstr "-register\t\tBu gvim'i OLE için kaydet" - -msgid "-unregister\t\tUnregister gvim for OLE" -msgstr "-unregister\t\tgvim'in OLE kaydını sil" - -msgid "-g\t\t\tRun using GUI (like \"gvim\")" -msgstr "-g\t\t\tGrafik arabirim kullanarak çalıştır (\"gvim\" gibi)" - -msgid "-f or --nofork\tForeground: Don't fork when starting GUI" -msgstr "-f veya --nofork\tÖnalan: Grafik arabirim başlatılırken çatallama!" - -msgid "-v\t\t\tVi mode (like \"vi\")" -msgstr "-v\t\t\tVi kipi (\"vi\" gibi)" - -msgid "-e\t\t\tEx mode (like \"ex\")" -msgstr "-e\t\t\tEx kipi (\"ex\" gibi)" - -msgid "-E\t\t\tImproved Ex mode" -msgstr "-E\t\t\tGeliştirilmiş Ex kipi" - -msgid "-s\t\t\tSilent (batch) mode (only for \"ex\")" -msgstr "-s\t\t\tSessiz (toplu iş) kipi (yalnızca \"ex\" için)" - -msgid "-d\t\t\tDiff mode (like \"vimdiff\")" -msgstr "-d\t\t\tKarşılaştırma kipi (like \"vimdiff\")" - -msgid "-y\t\t\tEasy mode (like \"evim\", modeless)" -msgstr "-y\t\t\tKolay kip (\"evim\" gibi, kipsiz)" - -msgid "-R\t\t\tReadonly mode (like \"view\")" -msgstr "-R\t\t\tSaltokunur kip (\"view\" gibi)" - -msgid "-Z\t\t\tRestricted mode (like \"rvim\")" -msgstr "-Z\t\t\tKısıtlanmış kip (\"rvim\" gibi)" - -msgid "-m\t\t\tModifications (writing files) not allowed" -msgstr "-m\t\t\tDeğişikliklere (dosya yazma) izin verilmez" - -msgid "-M\t\t\tModifications in text not allowed" -msgstr "-M\t\t\tMetinde değişikliklere izin verilmez" - -msgid "-b\t\t\tBinary mode" -msgstr "-b\t\t\tİkili kip" - -msgid "-l\t\t\tLisp mode" -msgstr "-l\t\t\tLisp kipi" - -msgid "-C\t\t\tCompatible with Vi: 'compatible'" -msgstr "-C\t\t\tVi ile uyumlu: 'compatible'" +"Seçenekler:\n" -msgid "-N\t\t\tNot fully Vi compatible: 'nocompatible'" -msgstr "-N\t\t\tTümüyle Vi uyumlu değil: 'nocompatible'" - -msgid "-V[N][fname]\t\tBe verbose [level N] [log messages to fname]" -msgstr "-V[N][dosya]\t\tAyrıntılı bilgi ver [N düzeyi] [iletileri dosyaya yaz]" - -msgid "-D\t\t\tDebugging mode" -msgstr "-D\t\t\tHata ayıklama kipi" - -msgid "-n\t\t\tNo swap file, use memory only" -msgstr "-n\t\t\tTakas dosyası kullanma, yalnızca belleğe yaz" - -msgid "-r\t\t\tList swap files and exit" -msgstr "-r\t\t\tTakas dosyalarını listele ve çık" - -msgid "-r (with file name)\tRecover crashed session" -msgstr "-r (dosya adı ile)\tÇöken oturumu kurtar" - -msgid "-L\t\t\tSame as -r" -msgstr "-L\t\t\t-r ile aynı" - -msgid "-f\t\t\tDon't use newcli to open window" -msgstr "-f\t\t\tPencere açmak için yeni komut satırı arabirimi kullanma" - -msgid "-dev <device>\t\tUse <device> for I/O" -msgstr "-dev <aygıt>\t\tGirdi/Çıktı için <aygıt>'ı kullan" - -msgid "-A\t\t\tStart in Arabic mode" -msgstr "-A\t\t\tArapça kipinde başla" - -msgid "-H\t\t\tStart in Hebrew mode" -msgstr "-H\t\t\tİbranca kipinde başla" - -msgid "-T <terminal>\tSet terminal type to <terminal>" -msgstr "-T <uçbirim>\t\tUçbirim türünü <uçbirim>'e ayarla" - -msgid "--not-a-term\t\tSkip warning for input/output not being a terminal" -msgstr "--not-a-term\t\tGirdi/Çıktının bir uçbirime olmadığı uyarısını atla" - -msgid "--ttyfail\t\tExit if input or output is not a terminal" -msgstr "--ttyfail\t\tGirdi veya çıktı bir uçbirime değilse çık" - -msgid "-u <vimrc>\t\tUse <vimrc> instead of any .vimrc" -msgstr "-u <vimrc>\t\tHerhangi bir .vimrc yerine <vimrc> kullan" - -msgid "-U <gvimrc>\t\tUse <gvimrc> instead of any .gvimrc" -msgstr "-U <gvimrc>\t\tHerhangi bir .gvimrc yerine <gvimrc> kullan" - -msgid "--noplugin\t\tDon't load plugin scripts" -msgstr "--noplugin\t\tEklenti betiklerini yükleme!" - -msgid "-p[N]\t\tOpen N tab pages (default: one for each file)" -msgstr "-p[N]\t\tN sekme sayfası aç (öntanımlı: her dosya için bir sekme)" - -msgid "-o[N]\t\tOpen N windows (default: one for each file)" -msgstr "-o[N]\t\tN pencere aç (öntanımlı: her dosya için bir pencere)" - -msgid "-O[N]\t\tLike -o but split vertically" -msgstr "-O[N]\t\t-o gibi, yalnızca dikey bölerek" - -msgid "+\t\t\tStart at end of file" -msgstr "+\t\t\tDosyanın sonunda başlat" - -msgid "+<lnum>\t\tStart at line <lnum>" -msgstr "+<satırno>\t\t<satırno> numaralı satırda başlat" +msgid " -- Only file names after this\n" +msgstr "" +"-- Yalnızca bundan sonraki dosya adları\n" -msgid "--cmd <command>\tExecute <command> before loading any vimrc file" -msgstr "--cmd <komut>\tHerhangi bir vimrc dosyası yüklemeden <komut> çalıştır" +msgid " + Start at end of file\n" +msgstr "" +"+ Dosyanın sonunda başlat\n" -msgid "-c <command>\t\tExecute <command> after loading the first file" -msgstr "-c <komut>\t\tİlk dosyayı yükleyip <komut> komutunu çalıştır" +msgid " --cmd <cmd> Execute <cmd> before any config\n" +msgstr "" +"--cmd <komut> Herhangi bir yapılandırma öncesi <komut> çalıştır\n" -msgid "-S <session>\t\tSource file <session> after loading the first file" -msgstr "-S <oturum>\t\tİlk dosyayı yükleyip <oturum> dosyasını kaynak al" +msgid " +<cmd>, -c <cmd> Execute <cmd> after config and first file\n" +msgstr "" +"+<komut>, -c <komut> Yapılandırma ve ilk dosya sonrası <komut> çalıştır\n" -msgid "-s <scriptin>\tRead Normal mode commands from file <scriptin>" -msgstr "-s <betikgir>\tNormal kip komutlarını <betikgir> dosyasından oku" +msgid " -b Binary mode\n" +msgstr "" +"-b İkili kip\n" -msgid "-w <scriptout>\tAppend all typed commands to file <scriptout>" -msgstr "-w <betikçık>\tGirilen tüm komutları <betikçık> dosyasına iliştir" +msgid " -d Diff mode\n" +msgstr "" +"-d Diff kipi\n" -msgid "-W <scriptout>\tWrite all typed commands to file <scriptout>" -msgstr "-W <betikçık>\tGirilen tüm komutları <betikçık> dosyasına yaz" +msgid " -e, -E Ex mode\n" +msgstr "" +"-e, -E Ex kipi\n" -msgid "-x\t\t\tEdit encrypted files" -msgstr "-x\t\t\tŞifrelenmiş dosyaları düzenle" +msgid " -es, -Es Silent (batch) mode\n" +msgstr "" +"-es, -Es Sessiz (toplu iş) kipi\n" -msgid "-display <display>\tConnect Vim to this particular X-server" -msgstr "-display <ekran>\tVim'i bu belirtilen X sunucusuna bağla" +msgid " -h, --help Print this help message\n" +msgstr "" +"-h, --help Bu yardım iletisini yazdır\n" -msgid "-X\t\t\tDo not connect to X server" -msgstr "-X\t\t\tX sunucusuna bağlama" +msgid " -i <shada> Use this shada file\n" +msgstr "" +"-i <shada> Bu shada (paylaşılan veri) dosyasını kullan\n" -msgid "--remote <files>\tEdit <files> in a Vim server if possible" -msgstr "--remote <dosya>\tOlanaklıysa bir Vim sunucusuda <dosya> düzenler" +msgid " -m Modifications (writing files) not allowed\n" +msgstr "" +"-m Değişikliklere (dosya yazmaya) izin verme\n" -msgid "--remote-silent <files> Same, don't complain if there is no server" -msgstr "--remote-silent <dosya> Aynısı, yalnızca sunucu yoksa şikayet etmez" +msgid " -M Modifications in text not allowed\n" +msgstr "" +"-M Metinde değişikliklere izin verme\n" -msgid "" -"--remote-wait <files> As --remote but wait for files to have been edited" +msgid " -n No swap file, use memory only\n" msgstr "" -"--remote-wait <dosya> ---remote gibi, yalnızca düzenlenme bitişini bekle" +"-n Takas dosyası yok, yalnızca belleği kullan\n" -msgid "" -"--remote-wait-silent <files> Same, don't complain if there is no server" +msgid " -o[N] Open N windows (default: one per file)\n" msgstr "" -"--remote-wait-silent <dosya> Aynısı, yalnızca sunucu yoksa şikayet etmez" +"-o[N] N pencere aç (öntanımlı: dosya başına bir tane)\n" msgid "" -"--remote-tab[-wait][-silent] <files> As --remote but use tab page per file" +" -O[N] Open N vertical windows (default: one per file)\n" msgstr "" -"--remote-tab[-wait][-silent] <dosya> --remote, artı sekme sayfası kullanır" +"-O[N] N dikey pencere aç (öntanımlı: dosya başına bir tane)\n" -msgid "--remote-send <keys>\tSend <keys> to a Vim server and exit" +msgid " -p[N] Open N tab pages (default: one per file)\n" msgstr "" -"--remote-send <anahtar>\tBir Vim sunucusuna <anahtar> gönderir ve çıkar" +"-p[N] N sekme sayfası aç (öntanımlı: dosya başına bir tane)\n" -msgid "--remote-expr <expr>\tEvaluate <expr> in a Vim server and print result" +msgid " -r, -L List swap files\n" msgstr "" -"--remote-expr <ifade>\t<ifade>'leri bir Vim sunucusunda değerlendirir ve " -"sonuçları yazdırır" - -msgid "--serverlist\t\tList available Vim server names and exit" -msgstr "--serverlist\t\tMevcut Vim sunucu adlarını listeler ve çıkar" - -msgid "--servername <name>\tSend to/become the Vim server <name>" -msgstr "--servername <ad>\t<ad> Vim sunucusuna gönder veya sunucu ol" +"-r, -L Takas dosyalarını listele\n" -msgid "--startuptime <file>\tWrite startup timing messages to <file>" -msgstr "--startuptime <dsy>\tBaşlangıç zamanlama iletilerini <dsy>'ya yaz" - -msgid "-i <viminfo>\t\tUse <viminfo> instead of .viminfo" -msgstr "-i <viminfo>\t\t.viminfo yerine <viminfo> kullan" - -msgid "--clean\t\t'nocompatible', Vim defaults, no plugins, no viminfo" -msgstr "--clean\t\t'nocompatible', Vim öntanımlıları, eklenti-viminfo yok" - -msgid "-h or --help\tPrint Help (this message) and exit" -msgstr "-h veya --help\tYardımı (bu iletiyi) yazdırır ve çıkar" - -msgid "--version\t\tPrint version information and exit" -msgstr "--version\t\tSürüm bilgisini yazdırır ve çıkar" - -msgid "" -"\n" -"Arguments recognised by gvim (Motif version):\n" +msgid " -r <file> Recover edit state for this file\n" msgstr "" -"\n" -"gvim tarafından tanınan argümanlar (Motif sürümü):\n" +"-r <dosya> Bu dosyanın düzenleme durumunu kurtar\n" -msgid "" -"\n" -"Arguments recognised by gvim (neXtaw version):\n" +msgid " -R Read-only mode\n" msgstr "" -"\n" -"gvim tarafından tanınan argümanlar (neXtaw sürümü):\n" +"-R Saltokunur kip\n" -msgid "" -"\n" -"Arguments recognised by gvim (Athena version):\n" +msgid " -S <session> Source <session> after loading the first file\n" msgstr "" -"\n" -"gvim tarafından tanınan argümanlar (Athena sürümü):\n" - -msgid "-display <display>\tRun Vim on <display>" -msgstr "-display <ekran>\tVim'i <ekran>'da çalıştır" +"-S <oturum> İlk dosyayı yükledikten sonra <oturum>'u kaynak al\n" -msgid "-iconic\t\tStart Vim iconified" -msgstr "-iconic\t\tVim'i simge durumunda başlat" - -msgid "-background <color>\tUse <color> for the background (also: -bg)" -msgstr "-background <renk>\tArdalanı <renk> yap (kısa: -bg)" +msgid " -s <scriptin> Read Normal mode commands from <scriptin>\n" +msgstr "" +"-s <betikgir> Normal kip komutlarını <betikgir>'den oku\n" -msgid "-foreground <color>\tUse <color> for normal text (also: -fg)" -msgstr "-foreground <renk>\tNormal metin için <renk> kullan (kısa: -fg)" +msgid " -u <config> Use this config file\n" +msgstr "" +"-u <yapılandırma> Bu yapılandırma dosyasını kullan\n" -msgid "-font <font>\t\tUse <font> for normal text (also: -fn)" -msgstr "-font <font>\t\tNormal metin için <font> yazıtipini kullan (kısa: -fn)" +msgid " -v, --version Print version information\n" +msgstr "" +"-v, --version Sürüm bilgisini yazdır\n" -msgid "-boldfont <font>\tUse <font> for bold text" -msgstr "-boldfont <font>\tKalın metin için <font> yazıtipini kullan" +msgid " -V[N][file] Verbose [level][file]\n" +msgstr "" +"-V[N][dosya] Ayrıntılı bilgi ver [düzey][dosya]\n" -msgid "-italicfont <font>\tUse <font> for italic text" -msgstr "-italicfont <font>\tEğik metin için <font> yazıtipini kullan" +msgid " --api-info Write msgpack-encoded API metadata to stdout\n" +msgstr "" +"--api-info stdout'a msgpack kodlu API üstverisi yaz\n" -msgid "-geometry <geom>\tUse <geom> for initial geometry (also: -geom)" -msgstr "-geometry <geom>\tBaşlangıç boyutları için <geom> kullan (kısa -geom)" +msgid " --embed Use stdin/stdout as a msgpack-rpc channel\n" +msgstr "" +"--embed stdin/stdout'u msgpack-rpc kanalı olarak kullan\n" -msgid "-borderwidth <width>\tUse a border width of <width> (also: -bw)" -msgstr "-borderwidth <gnşlk>\t<gnşlk> kenar genişliği kullan (kısa: -bw)" +msgid " --headless Don't start a user interface\n" +msgstr "" +"--headless Bir kullanıcı arayüzü başlatma\n" -msgid "-scrollbarwidth <width> Use a scrollbar width of <width> (also: -sw)" +msgid " --listen <address> Serve RPC API from this address\n" msgstr "" -"-scrollbarwidth <gnşlk> Kaydırma çubuğu için <gnşlk> genişlik (kısa: -sw)" +"--listen <adres> Bu adresten RPC API'si sun\n" -msgid "-menuheight <height>\tUse a menu bar height of <height> (also: -mh)" +msgid " --noplugin Don't load plugins\n" msgstr "" -"-menuheight <yükseklik>\t<yükseklik> menü çubuğu yüksekliği (kısa: -mh)" +"--noplugin Eklentileri yükleme\n" -msgid "-reverse\t\tUse reverse video (also: -rv)" -msgstr "-reverse\t\tTersine dönmüş video kullan (kısa: -rv)" +msgid " --remote[-subcommand] Execute commands remotely on a server\n" +msgstr "" +"--remote[-subcommand] Bir sunucuda komutları uzaktan çalıştır\n" -msgid "+reverse\t\tDon't use reverse video (also: +rv)" -msgstr "+reverse\t\tTersine dönmüş video kullanma (kısa: +rv)" +msgid " --server <address> Specify RPC server to send commands to\n" +msgstr "" +"--server <adres> Komut gönderilecek RPC sunucusunu belirt\n" -msgid "-xrm <resource>\tSet the specified resource" -msgstr "-xrm <kaynak>\tBelirtilen kaynağı ayarla" +msgid " --startuptime <file> Write startup timing messages to <file>\n" +msgstr "" +"--startuptime <dosya> Başlangıç zamanlama iletilerini <dosya>'ya yaz\n" msgid "" "\n" -"Arguments recognised by gvim (GTK+ version):\n" +"See \":help startup-options\" for all options.\n" msgstr "" "\n" -"gvim tarafından tanınan argümanlar (GTK+ sürümü):\n" - -msgid "-display <display>\tRun Vim on <display> (also: --display)" -msgstr "-display <ekran>\tVim'i <ekran>'da çalıştır (veya: --display)" - -msgid "--role <role>\tSet a unique role to identify the main window" -msgstr "--role <rol>\tAna pencereyi tanımlamak için eşsiz bir rol ayarla" - -msgid "--socketid <xid>\tOpen Vim inside another GTK widget" -msgstr "--socketid <xid>\tBaşka bir GTK parçacığında Vim'i aç" - -msgid "--echo-wid\t\tMake gvim echo the Window ID on stdout" -msgstr "--echo-wid\t\tgvim'in pencere kimliğini stdout'ta echo yapmasını sağla" - -msgid "-P <parent title>\tOpen Vim inside parent application" -msgstr "-P <üst başlık>\tVim'i üst uygulama içinde aç" - -msgid "--windowid <HWND>\tOpen Vim inside another win32 widget" -msgstr "--windowid <HWND>\tVim'i başka bir win32 parçacığı içinde aç" - -#, c-format -msgid "E224: global abbreviation already exists for %s" -msgstr "E224: %s için global kısaltma hâlihazırda var" - -#, c-format -msgid "E225: global mapping already exists for %s" -msgstr "E225: %s için global eşlemleme hâlihazırda var " - -#, c-format -msgid "E226: abbreviation already exists for %s" -msgstr "E226: %s için kısaltma hâlihazırda var" - -#, c-format -msgid "E227: mapping already exists for %s" -msgstr "E227: %s için eşlemleme hâlihazırda var" - -msgid "No abbreviation found" -msgstr "Kısaltma bulunamadı" - -msgid "No mapping found" -msgstr "Eşlemleme bulunamadı" - -msgid "E228: makemap: Illegal mode" -msgstr "E228: makemap: İzin verilmeyen kip" - -msgid "E460: entries missing in mapset() dict argument" -msgstr "E460: mapset() sözlük argümanında eksik girdiler" - -#, c-format -msgid "E357: 'langmap': Matching character missing for %s" -msgstr "E357: 'langmap': %s için eşleşen karakter eksik" - -#, c-format -msgid "E358: 'langmap': Extra characters after semicolon: %s" -msgstr "E358: 'langmap': Noktalı virgülden sonra ek karakterler: %s" +"Tüm seçenekler için \":help startup-options\" yazın.\n" msgid "No marks set" msgstr "İm ayarlanmamış" @@ -3126,30 +3549,44 @@ msgstr "" "dğşklk satr stn metin" #, c-format -msgid "E799: Invalid ID: %d (must be greater than or equal to 1)" -msgstr "E799: Geçersiz ID: %d (1'e eşit veya 1'den büyük olmalıdır)" +msgid "E799: Invalid ID: %<PRId64> (must be greater than or equal to 1)" +msgstr "E799: Geçersiz kimlik: %<PRId64> (1'e eşit veya 1'den büyük olmalıdır)" #, c-format -msgid "E801: ID already taken: %d" -msgstr "E801: Kullanımda olan ID: %d" +msgid "E801: ID already taken: %<PRId64>" +msgstr "E801: Kimlik halihazırda kullanımda: %<PRId64>" -msgid "E290: List or number required" -msgstr "E290: Liste veya numara gerekiyor" +#, c-format +msgid "E5030: Empty list at position %d" +msgstr "E5030: %d konumunda boş liste" #, c-format -msgid "E802: Invalid ID: %d (must be greater than or equal to 1)" -msgstr "E802: Geçersiz ID: %d (1'e eşit veya 1'den büyük olmalıdır)" +msgid "E5031: List or number required at position %d" +msgstr "E5031: %d konumunda liste veya numara gerekiyor" #, c-format -msgid "E803: ID not found: %d" -msgstr "E803: ID bulunamadı: %d" +msgid "E802: Invalid ID: %<PRId64> (must be greater than or equal to 1)" +msgstr "E802: Geçersiz kimlik: %<PRId64> (1'e eşit veya 1'den büyük olmalıdır)" #, c-format -msgid "E798: ID is reserved for \":match\": %d" -msgstr "E798: ID, \":match\" için ayrılmış: %d" +msgid "E803: ID not found: %<PRId64>" +msgstr "E803: Kimlik bulunamadı: %<PRId64>" -msgid "E543: Not a valid codepage" -msgstr "E543: Geçerli bir kod sayfası değil" +#, c-format +msgid "E474: List item %d is either not a dictionary or an empty one" +msgstr "E474: Liste ögesi %d, ya bir sözlük değil ya da boş bir sözlük" + +#, c-format +msgid "E474: List item %d is missing one of the required keys" +msgstr "E474: Liste ögesi %d, gerekli anahtarlardan birini içermiyor" + +#, c-format +msgid "E798: ID is reserved for \":match\": %<PRId64>" +msgstr "E798: Kimlik, \":match\" için ayrılmış: %<PRId64>" + +#, c-format +msgid "E798: ID is reserved for \"match\": %<PRId64>" +msgstr "E798: Kimlik, \":match\" için ayrılmış: %<PRId64>" msgid "E293: block was not locked" msgstr "E293: Blok kilitlenmemişti" @@ -3178,9 +3615,6 @@ msgstr "E298: 1 numaralı blok alınmadı mı?" msgid "E298: Didn't get block nr 2?" msgstr "E298: 2 numaralı blok alınmadı mı?" -msgid "E843: Error while updating swap file crypt" -msgstr "E843: Takas dosyası şifrelemesi güncellenirken hata" - msgid "E301: Oops, lost the swap file!!!" msgstr "E301: Hay aksi, takas dosyasını kaybettik!" @@ -3213,7 +3647,7 @@ msgid "" "Maybe no changes were made or Vim did not update the swap file." msgstr "" "\n" -"Herhangi bir değişiklik yapılmadı veya Vim takas dosyasını güncellemedi" +"Herhangi bir değişiklik yapılmadı veya Vim takas dosyasını güncellemedi." msgid " cannot be used with this version of Vim.\n" msgstr " Vim'in bu sürümüyle kullanılamaz.\n" @@ -3236,12 +3670,7 @@ msgid "" "or the file has been damaged." msgstr "" ",\n" -"veya dosya zarar görmüş" - -#, c-format -msgid "" -"E833: %s is encrypted and this version of Vim does not support encryption" -msgstr "E833: %s şifrelenmiş ve Vim'in bu sürümü şifrelemeyi desteklemiyor" +"veya dosya zarar görmüş." msgid " has been damaged (page size is smaller than minimum value).\n" msgstr " hasar görmüş (sayfa boyutu olabilecek en az değerden daha küçük).\n" @@ -3258,39 +3687,6 @@ msgid "E308: Warning: Original file may have been changed" msgstr "E308: Uyarı: Orijinal dosya değiştirilmiş olabilir" #, c-format -msgid "Swap file is encrypted: \"%s\"" -msgstr "Takas dosyası şifrelenmiş: \"%s\"" - -msgid "" -"\n" -"If you entered a new crypt key but did not write the text file," -msgstr "" -"\n" -"Yeni bir şifreleme anahtarı girmiş, fakat metin dosyasını yazmamışsanız," - -msgid "" -"\n" -"enter the new crypt key." -msgstr "" -"\n" -"yeni şifreleme anahtarını girin" - -msgid "" -"\n" -"If you wrote the text file after changing the crypt key press enter" -msgstr "" -"\n" -"Metin dosyasını şifreleme anahtarını değiştirdikten sonra yazdıysanız " -"Enter'a basın" - -msgid "" -"\n" -"to use the same key for text file and swap file" -msgstr "" -"\n" -"metin dosyası ve takas dosyası için aynı anahtarı kullanmak için" - -#, c-format msgid "E309: Unable to read block 1 from %s" msgstr "E309: Blok 1 %s içinden okunamıyor" @@ -3352,27 +3748,17 @@ msgstr "Kurtarma tamamlandı. Arabellek içeriği dosya içeriğine eşit." msgid "" "\n" -"You may want to delete the .swp file now." -msgstr "" -"\n" -"Bu .swp dosyasını silmeniz iyi olur." - -msgid "" +"You may want to delete the .swp file now.\n" "\n" -"Note: process STILL RUNNING: " msgstr "" "\n" -"Not: İşlem HÂLÂ ÇALIŞIYOR: " - -msgid "Using crypt key from swap file for the text file.\n" -msgstr "" -"Metin dosyası için takas dosyasındaki şifreleme anahtarı kullanılıyor.\n" +"Bu .swp dosyasını silmeniz iyi olur.\n" msgid "Swap files found:" msgstr "Takas dosyası bulundu:" msgid " In current directory:\n" -msgstr " Şu anki dizinde:\n" +msgstr " Geçerli dizinde:\n" msgid " Using specified name:\n" msgstr " Belirtilen şu adla:\n" @@ -3398,6 +3784,9 @@ msgstr " [Vim 3.0 sürümünden itibaren]" msgid " [does not look like a Vim swap file]" msgstr " [bir Vim takas dosyasına benzemiyor]" +msgid " [garbled strings (not nul terminated)]" +msgstr " [karışmış diziler (nul ile sonlandırılmamış)]" + msgid " file name: " msgstr " dosya adı: " @@ -3436,20 +3825,13 @@ msgid "" " process ID: " msgstr "" "\n" -" işlem kimliği: " +" süreç kimliği: " msgid " (STILL RUNNING)" msgstr " (HÅLÅ ÇALIŞIYOR)" msgid "" "\n" -" [not usable with this version of Vim]" -msgstr "" -"\n" -" [Vim'in bu sürümüyle kullanılamaz]" - -msgid "" -"\n" " [not usable on this computer]" msgstr "" "\n" @@ -3471,12 +3853,12 @@ msgid "E314: Preserve failed" msgstr "E314: Koruma başarısız oldu" #, c-format -msgid "E315: ml_get: invalid lnum: %ld" -msgstr "E315: ml_get: geçersiz satır numarası: %ld" +msgid "E315: ml_get: invalid lnum: %<PRId64>" +msgstr "E315: ml_get: Geçersiz satır numarası: %<PRId64>" #, c-format -msgid "E316: ml_get: cannot find line %ld in buffer %d %s" -msgstr "E316: ml_get: %ld. satır %d %s arabelleğinde bulunamıyor" +msgid "E316: ml_get: cannot find line %<PRId64> in buffer %d %s" +msgstr "E316: ml_get: %<PRId64>. satır %d %s arabelleğinde bulunamıyor" msgid "E317: pointer block id wrong 3" msgstr "E317: Blok 3 gösterge kimliği yanlış" @@ -3494,8 +3876,8 @@ msgid "deleted block 1?" msgstr "Blok 1 mi silindi?" #, c-format -msgid "E320: Cannot find line %ld" -msgstr "E320: %ld. satır bulunamıyor" +msgid "E320: Cannot find line %<PRId64>" +msgstr "E320: %<PRId64>. satır bulunamıyor" msgid "E317: pointer block id wrong" msgstr "E317: Gösterge blok kimliği yanlış" @@ -3504,12 +3886,12 @@ msgid "pe_line_count is zero" msgstr "pe_line_count sıfır" #, c-format -msgid "E322: line number out of range: %ld past the end" -msgstr "E322: satır numarası erimin dışında: %ld en sonuncuyu geçmiş" +msgid "E322: line number out of range: %<PRId64> past the end" +msgstr "E322: Satır numarası erimin dışında: %<PRId64> en sonuncuyu geçmiş" #, c-format -msgid "E323: line count wrong in block %ld" -msgstr "E323: %ld. blokta satır sayısı yanlış" +msgid "E323: line count wrong in block %<PRId64>" +msgstr "E323: %<PRId64>. blokta satır sayısı yanlış" msgid "Stack size increases" msgstr "Yığın boyutu artıyor" @@ -3547,7 +3929,7 @@ msgid "" " file when making changes. Quit, or continue with caution.\n" msgstr "" "\n" -"(1) Bu dosya başka bir programda da açık olabilir. Eğer öyleyse, aynı\n" +"(1) Bu dosya başka bir programda da açık olabilir. Eğer öyleyse aynı\n" " dosyanın iki ayrı örneğiyle karşılaşmamak için değişiklik yaparken\n" " lütfen dikkatli olun. Ya programdan çıkın ya da dikkatli ilerleyin.\n" @@ -3555,7 +3937,7 @@ msgid "(2) An edit session for this file crashed.\n" msgstr "(2) Bu dosya düzenleme oturumu çöktü.\n" msgid " If this is the case, use \":recover\" or \"vim -r " -msgstr " Durum buysa, \":recover\" veya \"vim -r " +msgstr " Durum buysa \":recover\" veya \"vim -r " msgid "" "\"\n" @@ -3565,7 +3947,7 @@ msgstr "" " yapıp değişiklikleri kurtarın (ek bilgi için \":help recovery\").\n" msgid " If you did this already, delete the swap file \"" -msgstr " Eğer bunu yaptıysanız, bu iletiyi bir daha görmemek için \"" +msgstr " Eğer bunu yaptıysanız bu iletiyi bir daha görmemek için \"" msgid "" "\"\n" @@ -3586,9 +3968,6 @@ msgstr "\" zaten var!" msgid "VIM - ATTENTION" msgstr "VİM - DİKKAT" -msgid "Swap file already exists!" -msgstr "Takas dosyası hâlihazırda var!" - msgid "" "&Open Read-Only\n" "&Edit anyway\n" @@ -3620,9 +3999,26 @@ msgstr "" msgid "E326: Too many swap files found" msgstr "E326: Çok fazla takas dosyası bulundu" +#, c-format +msgid "" +"E303: Unable to create directory \"%s\" for swap file, recovery impossible: " +"%s" +msgstr "E303: Takas dosyası için \"%s\" dizini oluşturulamadı, kurtarma " +"olanaksız: %s" + +msgid "Vim: Data too large to fit into virtual memory space\n" +msgstr "Vim: Veri, sanal bellek alanına sığmak için çok büyük\n" + +#, c-format +msgid "E342: Out of memory! (allocating %<PRIu64> bytes)" +msgstr "E342: Bellek tükendi!! (%<PRIu64> bayt ayrılıyor)" + msgid "E327: Part of menu-item path is not sub-menu" msgstr "E327: Menü öge yolunun bir kısmı alt menü değil" +msgid "E328: Menu only exists in another mode" +msgstr "E328: Menü yalnızca başka bir kipte mevcut" + #, c-format msgid "E329: No menu \"%s\"" msgstr "E329: Menü \"%s\" yok" @@ -3646,9 +4042,6 @@ msgstr "" "\n" "--- Menüler ---" -msgid "Tear off this menu" -msgstr "Bu menüyü dışarıya al" - #, c-format msgid "E335: Menu not defined for %s mode" msgstr "E335: Menü %s kipi için tanımlanmamış" @@ -3658,17 +4051,7 @@ msgstr "E333: Menü yolu bir menü ögesine çıkmalı" #, c-format msgid "E334: Menu not found: %s" -msgstr "E334: Menü bulunamadı %s" - -msgid "E336: Menu path must lead to a sub-menu" -msgstr "E336: Menü yolu bir alt menüye çıkmalı" - -msgid "E337: Menu not found - check menu names" -msgstr "E337: Menü bulunamadı - menü adlarını denetle" - -#, c-format -msgid "Error detected while compiling %s:" -msgstr "%s derlenirken hata tespit edildi:" +msgstr "E334: Menü bulunamadı: %s" #, c-format msgid "Error detected while processing %s:" @@ -3682,18 +4065,18 @@ msgstr "satır %4ld:" msgid "E354: Invalid register name: '%s'" msgstr "E354: Geçersiz yazmaç adı: '%s'" -msgid "Messages maintainer: Bram Moolenaar <Bram@vim.org>" -msgstr "Türkçeye çeviren: Emir SARI <bitigchi@me.com>" - msgid "Interrupt: " msgstr "Yarıda kes: " msgid "Press ENTER or type command to continue" msgstr "Sürdürmek için ENTER'a basın veya komut girin" +msgid " (Interrupted)" +msgstr " (Yarıda kesildi)" + #, c-format -msgid "%s line %ld" -msgstr "%s %ld. satır" +msgid "%s line %<PRId64>" +msgstr "%s %<PRId64>. satır" msgid "-- More --" msgstr "-- Daha fazla --" @@ -3714,6 +4097,15 @@ msgstr "" msgid "" "&Yes\n" "&No\n" +"&Cancel" +msgstr "" +"&Evet\n" +"&Hayır\n" +"İ&ptal" + +msgid "" +"&Yes\n" +"&No\n" "Save &All\n" "&Discard All\n" "&Cancel" @@ -3724,113 +4116,9 @@ msgstr "" "&Tümünü At\n" "İ&ptal" -msgid "E766: Insufficient arguments for printf()" -msgstr "E766: printf() için yetersiz argüman" - -msgid "E807: Expected Float argument for printf()" -msgstr "E807: printf() için kayan noktalı değer türünde argüman bekleniyordu" - -msgid "E767: Too many arguments to printf()" -msgstr "E767: printf() için çok fazla argüman" - -msgid "Type number and <Enter> or click with the mouse (q or empty cancels): " -msgstr "" -"Sayı girip <Enter>'a veya fare düğmesine basın (q veya boş iptal eder): " - -msgid "Type number and <Enter> (q or empty cancels): " -msgstr "Sayı girip <Enter>'a basın (q veya boş iptal eder): " - -#, c-format -msgid "%ld more line" -msgid_plural "%ld more lines" -msgstr[0] "%ld daha fazla satır" -msgstr[1] "%ld daha fazla satır" - -#, c-format -msgid "%ld line less" -msgid_plural "%ld fewer lines" -msgstr[0] "%ld daha az satır" -msgstr[1] "%ld daha az satır" - -msgid " (Interrupted)" -msgstr " (Yarıda kesildi)" - -msgid "Beep!" -msgstr "Bip!" - -msgid "E677: Error writing temp file" -msgstr "E677: Geçici dosya yazılırken hata" - -msgid "ERROR: " -msgstr "HATA: " - -#, c-format -msgid "" -"\n" -"[bytes] total alloc-freed %lu-%lu, in use %lu, peak use %lu\n" -msgstr "" -"\n" -"[bitler] toplam ayrılan/boşaltılan %lu-%lu, kullanımda %lu, doruk nokt. %lu\n" - -#, c-format -msgid "" -"[calls] total re/malloc()'s %lu, total free()'s %lu\n" -"\n" -msgstr "" -"[çağrılar] toplam re/malloc()'lar %lu, toplam free()'ler %lu\n" -"\n" - -msgid "E341: Internal error: lalloc(0, )" -msgstr "E341: İç hata: lalloc(0, )" - -#, c-format -msgid "E342: Out of memory! (allocating %lu bytes)" -msgstr "E342: Yetersiz bellek! (%lu bit ayrılıyor)" - -#, c-format -msgid "Calling shell to execute: \"%s\"" -msgstr "Çalıştırmak için çağrılan kabuk: \"%s\"" - -msgid "E545: Missing colon" -msgstr "E545: İki nokta eksik" - -msgid "E546: Illegal mode" -msgstr "E546: İzin verilmeyen kip" - -msgid "E547: Illegal mouseshape" -msgstr "E547: İzin verilmeyen fare imleci türü" - -msgid "E548: digit expected" -msgstr "E548: Basamak bekleniyordu" - -msgid "E549: Illegal percentage" -msgstr "E549: İzin verilmeyen yüzde" - -#, c-format -msgid "E668: Wrong access mode for NetBeans connection info file: \"%s\"" -msgstr "" -"E668: NetBeans bağlantı bilgisi dosyası için yanlış erişim kipi: \"%s\"" - -#, c-format -msgid "E658: NetBeans connection lost for buffer %d" -msgstr "E658: Arabellek %d için NetBeans bağlantısı koptu" - -msgid "E838: netbeans is not supported with this GUI" -msgstr "E838: NetBeans bu grafik arabirimde desteklenmiyor" - -msgid "E511: netbeans already connected" -msgstr "E511: NetBeans hâlihazırda bağlı" - -#, c-format -msgid "E505: %s is read-only (add ! to override)" -msgstr "E505: %s saltokunur (geçersiz kılmak için ! ekleyin)" - msgid "E349: No identifier under cursor" msgstr "E349: İmleç altında bir tanımlayıcı yok" -msgid "Warning: terminal cannot highlight" -msgstr "Uyarı: Uçbirim vurgulama yapamıyor" - msgid "E348: No string under cursor" msgstr "E348: İmleç altında bir dizi yok" @@ -3846,83 +4134,85 @@ msgstr "E662: Değişiklik listesinin başında" msgid "E663: At end of changelist" msgstr "E663: Değişiklik listesinin sonunda" -msgid "Type :qa! and press <Enter> to abandon all changes and exit Vim" +msgid "Type :qa! and press <Enter> to abandon all changes and exit Nvim" msgstr "" -"Değişikliklerden vazgeçip Vim'den çıkmak için :qa! yazıp <Enter>'a basın" +"Değişikliklerden vazgeçip Nvim'den çıkmak için :qa! yazıp <Enter>'a basın" -msgid "Type :qa and press <Enter> to exit Vim" -msgstr "Vim'den çıkmak için :qa yazıp <Enter>'a basın" +msgid "Type :qa and press <Enter> to exit Nvim" +msgstr "Nvim'den çıkmak için :qa yazıp <Enter>'a basın" #, c-format -msgid "%ld line %sed %d time" -msgid_plural "%ld line %sed %d times" -msgstr[0] "%ld satır, %s %d kez" -msgstr[1] "%ld satır, %s %d kez" +msgid "%<PRId64> lines to indent... " +msgstr "girintilenecek %<PRId64> satır kaldı... " -#, c-format -msgid "%ld lines %sed %d time" -msgid_plural "%ld lines %sed %d times" -msgstr[0] "%ld satır, %s %d kez" -msgstr[1] "%ld satır, %s %d kez" - -msgid "cannot yank; delete anyway" -msgstr "kopyalanamıyor, silindi" +msgid "E748: No previously used register" +msgstr "E748: Daha önce kullanılan bir yazmaç yok" #, c-format -msgid "%ld line changed" -msgid_plural "%ld lines changed" -msgstr[0] "%ld satır değiştirildi" -msgstr[1] "%ld satır değiştirildi" +msgid " into \"%c" +msgstr " \"%c" #, c-format -msgid "%d line changed" -msgid_plural "%d lines changed" -msgstr[0] "%d satır değiştirildi" -msgstr[1] "%d satır değiştirildi" +msgid "E353: Nothing in register %s" +msgstr "E353: Yazmaç %s boş" + +msgid "" +"\n" +"Type Name Content" +msgstr "" +"\n" +"Tür Ad İçerik" + +msgid "" +"E883: search pattern and expression register may not contain two or more " +"lines" +msgstr "" +"E883: Arama dizgisi ve ifade yazmacı iki veya daha fazla satır içeremez" #, c-format -msgid "%ld Cols; " -msgstr "%ld Sütun; " +msgid "%<PRId64> Cols; " +msgstr "%<PRId64> Sütun; " #, c-format -msgid "Selected %s%ld of %ld Lines; %lld of %lld Words; %lld of %lld Bytes" -msgstr "%s%ld/%ld satır; %lld/%lld sözcük; %lld/%lld bit seçildi" +msgid "" +"Selected %s%<PRId64> of %<PRId64> Lines; %<PRId64> of %<PRId64> Words; " +"%<PRId64> of %<PRId64> Bytes" +msgstr "%s%<PRId64>/%<PRId64> satır; %<PRId64>/%<PRId64> sözcük; " +"%<PRId64>/%<PRId64> bayt seçildi" #, c-format msgid "" -"Selected %s%ld of %ld Lines; %lld of %lld Words; %lld of %lld Chars; %lld of " -"%lld Bytes" +"Selected %s%<PRId64> of %<PRId64> Lines; %<PRId64> of %<PRId64> Words; " +"%<PRId64> of %<PRId64> Chars; %<PRId64> of %<PRId64> Bytes" msgstr "" -"%s%ld/%ld satır; %lld/%lld sözcük; %lld/%lld karakter; %lld/%lld bit seçildi" +"%s%<PRId64>/%<PRId64> satır; %<PRId64>/%<PRId64> sözcük; " +"%<PRId64>/%<PRId64> karakter; %<PRId64>/%<PRId64> bayt seçildi" #, c-format -msgid "Col %s of %s; Line %ld of %ld; Word %lld of %lld; Byte %lld of %lld" -msgstr "Sütun %s/%s; Satır %ld/%ld; Sözcük %lld/%lld; Bit %lld/%lld" +msgid "" +"Col %s of %s; Line %<PRId64> of %<PRId64>; Word %<PRId64> of %<PRId64>; Byte " +"%<PRId64> of %<PRId64>" +msgstr "%s/%s sütun; %<PRId64>/%<PRId64> satır; %<PRId64>/%<PRId64> sözcük; " +"%<PRId64>/%<PRId64> bayt" #, c-format msgid "" -"Col %s of %s; Line %ld of %ld; Word %lld of %lld; Char %lld of %lld; Byte " -"%lld of %lld" +"Col %s of %s; Line %<PRId64> of %<PRId64>; Word %<PRId64> of %<PRId64>; Char " +"%<PRId64> of %<PRId64>; Byte %<PRId64> of %<PRId64>" msgstr "" -"Sütun %s/%s; Satır %ld/%ld; Sözcük %lld/%lld; Karakter %lld/%lld; Bit %lld/" -"%lld" +"%s/%s sütun; %<PRId64>/%<PRId64> satır; %<PRId64>/%<PRId64> sözcük; " +"%<PRId64>/%<PRId64> karakter; %<PRId64>/%<PRId64> bayt" #, c-format -msgid "(+%lld for BOM)" -msgstr "(BOM için +%lld)" +msgid "(+%<PRId64> for BOM)" +msgstr "(BOM için +%<PRId64>)" msgid "E774: 'operatorfunc' is empty" msgstr "E774: 'operatorfunc' boş" -msgid "E775: Eval feature not available" -msgstr "E775: Eval özelliği mevcut değil" - msgid "E518: Unknown option" msgstr "E518: Bilinmeyen seçenek" -msgid "E519: Option not supported" -msgstr "E519: Özellik desteklenmiyor" - msgid "E520: Not allowed in a modeline" msgstr "E520: Bir kip satırında izin verilmiyor" @@ -3935,68 +4225,6 @@ msgstr "E846: Anahtar kodu ayarlanmamış" msgid "E521: Number required after =" msgstr "E521: = sonrası sayı gerekiyor" -msgid "E522: Not found in termcap" -msgstr "E522: termcap içinde bulunamadı" - -msgid "E946: Cannot make a terminal with running job modifiable" -msgstr "E946: Uçbirim bir iş çalışırken değiştirilebilir yapılamaz" - -msgid "E590: A preview window already exists" -msgstr "E590: Bir önizleme penceresi hâlihazırda mevcut" - -msgid "W17: Arabic requires UTF-8, do ':set encoding=utf-8'" -msgstr "W17: Arapça UTF-8 gerektirir, ':set encoding=utf-8' yapın" - -msgid "E954: 24-bit colors are not supported on this environment" -msgstr "E954: 24 bit renkler bu ortamda desteklenmiyor" - -#, c-format -msgid "E593: Need at least %d lines" -msgstr "E593: En azından %d satır gerekli" - -#, c-format -msgid "E594: Need at least %d columns" -msgstr "E594: En azından %d sütun gerekli" - -#, c-format -msgid "E355: Unknown option: %s" -msgstr "E355: Bilinmeyen seçenek: %s" - -#, c-format -msgid "E521: Number required: &%s = '%s'" -msgstr "E521: Sayı gerekiyor: &%s = '%s'" - -msgid "" -"\n" -"--- Terminal codes ---" -msgstr "" -"\n" -"--- Uçbirim kodları ---" - -msgid "" -"\n" -"--- Global option values ---" -msgstr "" -"\n" -"--- Global seçenek değerleri ---" - -msgid "" -"\n" -"--- Local option values ---" -msgstr "" -"\n" -"--- Yerel seçenek değerleri ---" - -msgid "" -"\n" -"--- Options ---" -msgstr "" -"\n" -"--- Seçenekler ---" - -msgid "E356: get_varp ERROR" -msgstr "E356: get_varp HATASI" - #, c-format msgid "E539: Illegal character <%s>" msgstr "E539: İzin verilmeyen karakter <%s>" @@ -4005,36 +4233,14 @@ msgstr "E539: İzin verilmeyen karakter <%s>" msgid "For option %s" msgstr "%s seçeneği için" -msgid "E540: Unclosed expression sequence" -msgstr "E540: Kapatılmamış ifade sıralaması" - -msgid "E542: unbalanced groups" -msgstr "E542: Dengelenmemiş gruplar" - -msgid "E529: Cannot set 'term' to empty string" -msgstr "E529: Boş dizi için 'term' ayarlanamıyor" - -msgid "E530: Cannot change term in GUI" -msgstr "E530: Grafik arabirimde uçbirim değiştirilemez" - -msgid "E531: Use \":gui\" to start the GUI" -msgstr "E531: Grafik arabirimi başlatmak için \":gui\" yazın" - msgid "E589: 'backupext' and 'patchmode' are equal" msgstr "E589: 'backupext' ve 'patchmode' birbirine eşit" -msgid "E835: Conflicts with value of 'fillchars'" -msgstr "E835: 'fillchars' değeriyle çakışmalar var" - msgid "E834: Conflicts with value of 'listchars'" msgstr "E834: 'listchars' değeriyle çakışmalar var" -msgid "E617: Cannot be changed in the GTK+ 2 GUI" -msgstr "E617: GTK+ 2 grafik arabiriminde değiştirilemez" - -#, c-format -msgid "E950: Cannot convert between %s and %s" -msgstr "E950: %s ve %s arasında dönüştürme yapılamıyor" +msgid "E835: Conflicts with value of 'fillchars'" +msgstr "E835: 'fillchars' değeriyle çakışmalar var" msgid "E524: Missing colon" msgstr "E524: İki nokta eksik" @@ -4044,7 +4250,7 @@ msgstr "E525: Sıfır uzunlukta dizi" #, c-format msgid "E526: Missing number after <%s>" -msgstr "E526: <%s> sonrasında sayı eksik" +msgstr "E526: <%s> sonrası sayı eksik" msgid "E527: Missing comma" msgstr "E527: Virgül eksik" @@ -4055,21 +4261,6 @@ msgstr "E528: Bir ' değeri belirtmeli" msgid "E595: 'showbreak' contains unprintable or wide character" msgstr "E595: 'showbreak' yazdırılamaz veya geniş karakter içeriyor" -msgid "E596: Invalid font(s)" -msgstr "E596: Geçersiz font(lar)" - -msgid "E597: can't select fontset" -msgstr "E597: Yazıtipi seti seçilemiyor" - -msgid "E598: Invalid fontset" -msgstr "E598: Geçersiz yazıtipi seti" - -msgid "E533: can't select wide font" -msgstr "E533: Geniş yazıtipi seçilemiyor" - -msgid "E534: Invalid wide font" -msgstr "E534: Geçersiz geniş yazıtipi" - #, c-format msgid "E535: Illegal character after <%c>" msgstr "E535: <%c> sonrası izin verilmeyen karakter" @@ -4081,253 +4272,101 @@ msgstr "E536: Virgül gerekiyor" msgid "E537: 'commentstring' must be empty or contain %s" msgstr "E537: 'commentstring' boş olmalı veya %s içermeli" -msgid "cannot open " -msgstr "Açılamıyor: " - -msgid "VIM: Can't open window!\n" -msgstr "VİM: Pencere açılamıyor!\n" - -msgid "Need Amigados version 2.04 or later\n" -msgstr "AmigaDOS 2.04 sürümü veya sonrası gerekli\n" - -#, c-format -msgid "Need %s version %ld\n" -msgstr "%s %ld sürümü gerekli\n" - -msgid "Cannot open NIL:\n" -msgstr "NIL açılamıyor:\n" - -msgid "Cannot create " -msgstr "Oluşturulamıyor: " - -#, c-format -msgid "Vim exiting with %d\n" -msgstr "Vim %d ile çıkıyor\n" - -msgid "cannot change console mode ?!\n" -msgstr "Konsol kipi değiştirilemiyor?!\n" - -msgid "mch_get_shellsize: not a console??\n" -msgstr "mch_get_shellsize: Bir konsol değil??\n" - -msgid "E360: Cannot execute shell with -f option" -msgstr "E360: Kabuk -f seçeneği ile çalıştırılamıyor" - -msgid "Cannot execute " -msgstr "Çalıştırılamıyor: " - -msgid "shell " -msgstr "kabuk " - -msgid " returned\n" -msgstr " döndürüldü\n" - -msgid "ANCHOR_BUF_SIZE too small." -msgstr "ANCHOR_BUF_SIZE çok küçük." - -msgid "I/O ERROR" -msgstr "GİRDİ/ÇIKTI HATASI" - -msgid "Message" -msgstr "İleti" - -msgid "E237: Printer selection failed" -msgstr "E237: Yazıcı seçimi başarısız oldu" - -#, c-format -msgid "to %s on %s" -msgstr "%s, %s üzerinde" +msgid "E540: Unclosed expression sequence" +msgstr "E540: Kapatılmamış ifade sıralaması" -#, c-format -msgid "E613: Unknown printer font: %s" -msgstr "E613: Bilinmeyen yazıcı fontu: %s" +msgid "E542: unbalanced groups" +msgstr "E542: Dengelenmemiş gruplar" -#, c-format -msgid "E238: Print error: %s" -msgstr "E238: Yazdırma hatası: %s" +msgid "E590: A preview window already exists" +msgstr "E590: Bir önizleme penceresi hâlihazırda mevcut" -#, c-format -msgid "Printing '%s'" -msgstr "'%s' yazdırılıyor" +msgid "W17: Arabic requires UTF-8, do ':set encoding=utf-8'" +msgstr "W17: Arapça, UTF-8 gerektirir; ':set encoding=utf-8' yapın" #, c-format -msgid "E244: Illegal charset name \"%s\" in font name \"%s\"" -msgstr "E244: Geçersiz karakter adı \"%s\", bulunduğu yazıtipi: \"%s\"" +msgid "E593: Need at least %d lines" +msgstr "E593: En azından %d satır gerekli" #, c-format -msgid "E244: Illegal quality name \"%s\" in font name \"%s\"" -msgstr "E244: İzin verilmeyen nitelik adı: \"%s\", bulunduğu yazıtipi: \"%s\"" +msgid "E594: Need at least %d columns" +msgstr "E594: En azından %d sütun gerekli" #, c-format -msgid "E245: Illegal char '%c' in font name \"%s\"" -msgstr "" -"E245: İzin verilmeyen karakter '%c', bulunduğu yer: \"%s\" yazıtipi adı" +msgid "E355: Unknown option: %s" +msgstr "E355: Bilinmeyen seçenek: %s" #, c-format -msgid "Opening the X display took %ld msec" -msgstr "X ekranını açma %ld milisaniye sürdü" +msgid "E521: Number required: &%s = '%s'" +msgstr "E521: Sayı gerekiyor: &%s = '%s'" msgid "" "\n" -"Vim: Got X error\n" +"--- Global option values ---" msgstr "" "\n" -"Vim: X hatası alındı\n" - -#, c-format -msgid "restoring display %s" -msgstr "%s ekranı geri getiriliyor" - -msgid "Testing the X display failed" -msgstr "X ekran testi başarısız oldu" - -msgid "Opening the X display timed out" -msgstr "X ekran açılması zaman aşımına uğradı" +"--- Global seçenek değerleri ---" msgid "" "\n" -"Could not get security context for " +"--- Local option values ---" msgstr "" "\n" -"Şunun için güvenlik bağlamı alınamadı: " +"--- Yerel seçenek değerleri ---" msgid "" "\n" -"Could not set security context for " +"--- Options ---" msgstr "" "\n" -"Şunun için güvenlik bağlamı alınamadı: " +"--- Seçenekler ---" -#, c-format -msgid "Could not set security context %s for %s" -msgstr "Güvenlik bağlamı %s %s için alınamadı" +msgid "E356: get_varp ERROR" +msgstr "E356: get_varp HATASI" #, c-format -msgid "Could not get security context %s for %s. Removing it!" -msgstr "Güvenlik bağlamı %s %s için alınamadı. Kaldırılıyor!" +msgid "E357: 'langmap': Matching character missing for %s" +msgstr "E357: 'langmap': %s için eşleşen karakter eksik" -msgid "" -"\n" -"Cannot execute shell sh\n" -msgstr "" -"\n" -"sh kabuğu çalıştırılamıyor\n" +#, c-format +msgid "E358: 'langmap': Extra characters after semicolon: %s" +msgstr "E358: 'langmap': Noktalı virgülden sonra ek karakterler: %s" -msgid "" -"\n" -"shell returned " -msgstr "" -"\n" -"Program çıktı: " +#, c-format +msgid "dlerror = \"%s\"" +msgstr "dlerror = \"%s\"" -msgid "" -"\n" -"Cannot create pipes\n" -msgstr "" -"\n" -"Veri yolları oluşturulamıyor\n" +#, c-format +msgid "E5420: Failed to write to file: %s" +msgstr "E5420: Dosyaya yazılamadı: %s" -msgid "" -"\n" -"Cannot fork\n" -msgstr "" -"\n" -"Çatallanamıyor\n" +msgid "Vim: Error reading input, exiting...\n" +msgstr "Vim: Girdi okunurken hata, çıkılıyor...\n" msgid "" "\n" -"Cannot execute shell " +"shell returned " msgstr "" "\n" -"Kabuk çalıştırılamıyor " +"Program çıktı: " msgid "" "\n" -"Command terminated\n" +"shell failed to start: " msgstr "" "\n" -"Komut sonlandırıldı\n" - -msgid "XSMP lost ICE connection" -msgstr "XSMP, ICE bağlantısını kopardı" - -#, c-format -msgid "dlerror = \"%s\"" -msgstr "dlerror = \"%s\"" - -msgid "Opening the X display failed" -msgstr "X ekran açılışı başarısız oldu" - -msgid "XSMP handling save-yourself request" -msgstr "Kendini kurtarma isteği XSMP tarafından gerçekleştiriliyor" - -msgid "XSMP opening connection" -msgstr "XSMP bağlantıyı açıyor" - -msgid "XSMP ICE connection watch failed" -msgstr "XSMP ICE bağlantı izlemesi başarısız oldu" - -#, c-format -msgid "XSMP SmcOpenConnection failed: %s" -msgstr "XSMP SmcOpenConnection başarısız oldu: %s" - -msgid "At line" -msgstr "Satırda" - -#, c-format -msgid "Vim: Caught %s event\n" -msgstr "Vim: %s olayı yakalandı\n" - -msgid "close" -msgstr "kapat" - -msgid "logoff" -msgstr "oturumu kapat" - -msgid "shutdown" -msgstr "kapat" - -msgid "E371: Command not found" -msgstr "E371: Komut bulunamadı" - -msgid "" -"VIMRUN.EXE not found in your $PATH.\n" -"External commands will not pause after completion.\n" -"See :help win32-vimrun for more information." -msgstr "" -"VIMRUN.EXE $PATH üzerinde bulunamadı.\n" -"Dış komutlar tamamlandıktan sonra duraklamayacak.\n" -"Ek bilgi için :help win32-vimrun yazın." - -msgid "Vim Warning" -msgstr "Vim - Uyarı" +"kabuk başlatılamad: " #, c-format -msgid "shell returned %d" -msgstr "Program %d numaralı kod ile çıktı" - -msgid "E861: Cannot open a second popup with a terminal" -msgstr "E861: Bir uçbirimle ikinci bir açılır pencere açılamıyor" +msgid "E5677: Error writing input to shell-command: %s" +msgstr "E5677: Girdi, kabuk komutuna yazılırken hata: %s" -msgid "E450: buffer number, text or a list required" -msgstr "E450: Arabellek numarası, metin veya liste gerekiyor" - -#, c-format -msgid "E997: Tabpage not found: %d" -msgstr "E997: Sekme bulunamadı: %d" +msgid "%a %b %d %H:%M:%S %Y" +msgstr "%a %b %d %H:%M:%S %Y" #, c-format -msgid "E993: window %d is not a popup window" -msgstr "E993: %d penceresi bir açılır pencere değil" - -msgid "E994: Not allowed in a popup window" -msgstr "E994: Açılır pencere içinde izin verilmiyor" - -msgid "E863: Not allowed for a terminal in a popup window" -msgstr "E863: Açılır pencere içinde uçbirime izin verilmiyor" - -msgid "E750: First use \":profile start {fname}\"" -msgstr "E750: İlk kullanım \":profile start {dosyaadı}\"" +msgid "E447: Can't find file \"%s\" in path" +msgstr "E447: \"%s\" dosyası yol içinde bulunamadı" msgid "E553: No more items" msgstr "E553: Öge yok" @@ -4340,7 +4379,7 @@ msgstr "E926: Geçerli konum listesi değiştirildi" #, c-format msgid "E372: Too many %%%c in format string" -msgstr "E372: Biçim dizisinde çok fazla %%%c" +msgstr "E372: Biçim dizisinde pek fazla %%%c" #, c-format msgid "E373: Unexpected %%%c in format string" @@ -4390,9 +4429,6 @@ msgstr "E381: Hızlı düzelt yığınının en tepesinde" msgid "No entries" msgstr "Girdi yok" -msgid "Error file" -msgstr "Hata dosyası" - msgid "E683: File name missing or invalid pattern" msgstr "E683: Dosya adı eksik veya geçersiz dizgi" @@ -4410,16 +4446,12 @@ msgid "E777: String or List expected" msgstr "E777: Dizi veya liste bekleniyordu" #, c-format -msgid "E927: Invalid action: '%s'" -msgstr "E927: Geçersiz eylem: '%s'" - -#, c-format msgid "E369: invalid item in %s%%[]" msgstr "E369: %s%%[] içinde geçersiz öge" #, c-format msgid "E769: Missing ] after %s[" -msgstr "E769: %s[ sonrasında ] eksik" +msgstr "E769: %s[ sonrası ] eksik" msgid "E944: Reverse range in character class" msgstr "E944: Karakter sınıfında geriye dönük erim" @@ -4457,8 +4489,8 @@ msgid "E956: Cannot use pattern recursively" msgstr "E956: Dizgi özyineli olarak kullanılamaz" #, c-format -msgid "E654: missing delimiter after search pattern: %s" -msgstr "E654: Arama dizgisi sonrasında eksik sınırlandırıcı: %s" +msgid "E1204: No Number allowed after .: '\\%%%c'" +msgstr "E1204: . sonrası Sayıya izin verilmiyor: '\\%%%c'" #, c-format msgid "E554: Syntax error in %s{...}" @@ -4466,175 +4498,40 @@ msgstr "E554: %s{...} içinde sözdizimi hatası" #, c-format msgid "E888: (NFA regexp) cannot repeat %s" -msgstr "E888: (NFA regexp) %s tekrar edemiyor" +msgstr "E888: (BSO düzenli ifadesi) %s tekrar edemiyor" msgid "" "E864: \\%#= can only be followed by 0, 1, or 2. The automatic engine will be " "used " msgstr "" -"E864: \\%#= sonrasında yalnızca 0, 1, veya 2 gelebilir. Otomatik motor " +"E864: \\%#= sonrası yalnızca 0, 1, veya 2 gelebilir. Otomatik işletke " "kullanılacak " msgid "Switching to backtracking RE engine for pattern: " msgstr "Şu dizgi için düzenli ifade iz sürme motoruna geçiliyor: " -msgid "E65: Illegal back reference" -msgstr "E65: Geçersiz dönüş başvurusu" - -msgid "E63: invalid use of \\_" -msgstr "E63: Geçersiz kullanım: \\_" - -#, c-format -msgid "E64: %s%c follows nothing" -msgstr "E64: %s%c tek başına kullanılıyor" - -msgid "E68: Invalid character after \\z" -msgstr "E68: \\z sonrası geçersiz karakter" - #, c-format -msgid "E678: Invalid character after %s%%[dxouU]" -msgstr "E678: %s%%[dxouU] sonrası geçersiz karakter" - -#, c-format -msgid "E71: Invalid character after %s%%" -msgstr "E71: %s%% sonrası geçersiz karakter" - -#, c-format -msgid "E59: invalid character after %s@" -msgstr "E59: %s@ sonrası geçersiz karakter" - -#, c-format -msgid "E60: Too many complex %s{...}s" -msgstr "E60: Çok fazla karmaşık %s{...}(lar)" - -#, c-format -msgid "E61: Nested %s*" -msgstr "E61: İç içe geçmiş %s*" - -#, c-format -msgid "E62: Nested %s%c" -msgstr "E62: İç içe geçmiş %s%c" - -msgid "E50: Too many \\z(" -msgstr "E50: Çok fazla \\z(" - -#, c-format -msgid "E51: Too many %s(" -msgstr "E51: Çok fazla %s(" - -msgid "E52: Unmatched \\z(" -msgstr "E52: Eşleşmemiş \\z(" - -msgid "E339: Pattern too long" -msgstr "E339: Dizgi çok uzun" - -msgid "External submatches:\n" -msgstr "Dış alteşleşmeler:\n" - -msgid "E865: (NFA) Regexp end encountered prematurely" -msgstr "E865: (BSO) Düzenli ifade sonu çok erken geldi" - -#, c-format -msgid "E866: (NFA regexp) Misplaced %c" -msgstr "E866: (BSO düzenli ifadesi) Yanlış yere koyulmuş %c" - -#, c-format -msgid "E877: (NFA regexp) Invalid character class: %d" -msgstr "E877: (BSO düzenli ifadesi) Geçersiz karakter sınıfı: %d" - -msgid "E951: \\% value too large" -msgstr "E951: \\% çok büyük bir değer" - -#, c-format -msgid "E867: (NFA) Unknown operator '\\z%c'" -msgstr "E867: (BSO) Bilinmeyen işleç '\\z%c'" - -#, c-format -msgid "E867: (NFA) Unknown operator '\\%%%c'" -msgstr "E867: (BSO) Bilinmeyen işleç '\\%%%c'" - -msgid "E868: Error building NFA with equivalence class!" -msgstr "E868: Eşdeğerli sınıf ile BSO inşa ederken hata!" - -#, c-format -msgid "E869: (NFA) Unknown operator '\\@%c'" -msgstr "E869: (BSO) Bilinmeyen işleç '\\@%c'" - -msgid "E870: (NFA regexp) Error reading repetition limits" -msgstr "E870: (BSO düzenli ifadesi) Yineleme sınırlarımı okurken hata" - -msgid "E871: (NFA regexp) Can't have a multi follow a multi" -msgstr "E871: (BSO düzenli ifadesi) Bir çoklunun ardından çoklu gelemez" - -msgid "E872: (NFA regexp) Too many '('" -msgstr "E872: (BSO düzenli ifadesi) Çok fazla '('" - -msgid "E879: (NFA regexp) Too many \\z(" -msgstr "E879: (BSO düzenli ifadesi) Çok fazla \\z(" - -msgid "E873: (NFA regexp) proper termination error" -msgstr "E873: (BSO düzenli ifadesi) Düzgün sonlandırma hatası" - -msgid "Could not open temporary log file for writing, displaying on stderr... " -msgstr "" -"Geçici günlük dosyası yazma için açılamıyor, stderr'de görüntüleniyor..." - -msgid "E874: (NFA) Could not pop the stack!" -msgstr "E874: (BSO) Yığın çıkartılamadı!" - -msgid "" -"E875: (NFA regexp) (While converting from postfix to NFA), too many states " -"left on stack" -msgstr "" -"E875: (BSO düzenli ifadesi) (Art takı'dan BSO'ya çevirirken), yığında çok " -"fazla durum bırakıldı" - -msgid "E876: (NFA regexp) Not enough space to store the whole NFA " -msgstr "" -"E876: (BSO düzenli ifadesi) Tüm BSO'yu depolamak için yeterli alan yok " - -msgid "E878: (NFA) Could not allocate memory for branch traversal!" -msgstr "E878: (BSO) Dal gezinmesi için bellek ayrılamadı!" - -msgid "E748: No previously used register" -msgstr "E748: Daha önce kullanılan bir yazmaç yok" +msgid "Searching for \"%s\" in \"%s\"" +msgstr "\"%s\", \"%s\" içinde aranıyor" #, c-format -msgid "freeing %ld lines" -msgstr "%ld satırlık yer açılıyor" +msgid "Searching for \"%s\"" +msgstr "\"%s\" aranıyor" #, c-format -msgid " into \"%c" -msgstr " \"%c" +msgid "not found in '%s': \"%s\"" +msgstr "'%s' içinde bulunamadı: \"%s\"" #, c-format -msgid "block of %ld line yanked%s" -msgid_plural "block of %ld lines yanked%s" -msgstr[0] "%ld satırlık blok şuraya kopyalandı:%s" -msgstr[1] "%ld satırlık blok şuraya kopyalandı:%s" +msgid "Searching for \"%s\" in runtime path" +msgstr "\"%s\", çalışma zamanı yolu içinde aranıyor" #, c-format -msgid "%ld line yanked%s" -msgid_plural "%ld lines yanked%s" -msgstr[0] "%ld satır şuraya kopyalandı:%s" -msgstr[1] "%ld satır şuraya kopyalandı:%s" +msgid "not found in runtime path: \"%s\"" +msgstr "çalışma zamanı yolu içinde bulunamadı: \"%s\"" -#, c-format -msgid "E353: Nothing in register %s" -msgstr "E353: Yazmaç %s boş" - -msgid "" -"\n" -"Type Name Content" -msgstr "" -"\n" -"Tür Ad İçerik" - -msgid "" -"E883: search pattern and expression register may not contain two or more " -"lines" -msgstr "" -"E883: Arama dizgisi ve ifade yazmacı iki veya daha fazla satır içeremez" +msgid " TERMINAL" +msgstr " UÇBİRİM" msgid " VREPLACE" msgstr " SANAL DEĞİŞTİR" @@ -4688,146 +4585,264 @@ msgid "recording" msgstr "kaydediliyor" #, c-format -msgid "Searching for \"%s\" in \"%s\"" -msgstr "\"%s\", \"%s\" içinde aranıyor" +msgid "E383: Invalid search string: %s" +msgstr "E383: Geçersiz arama dizisi: %s" #, c-format -msgid "Searching for \"%s\"" -msgstr "\"%s\" aranıyor" +msgid "E384: search hit TOP without match for: %s" +msgstr "E384: Arama dosyanın BAŞINA vardı, %s bulunamadı" #, c-format -msgid "not found in '%s': \"%s\"" -msgstr "'%s' içinde bulunamadı: \"%s\"" +msgid "E385: search hit BOTTOM without match for: %s" +msgstr "E385: Arama dosyanın SONUNA vardı, %s bulunamadı" + +msgid "E386: Expected '?' or '/' after ';'" +msgstr "E386: ';' sonrasında '?' veya '/' bekleniyordu" + +msgid " (includes previously listed match)" +msgstr " (daha önce listelenen eşleşmeyi içerir)" -msgid "Source Vim script" -msgstr "Vim betiği kaynak al" +msgid "--- Included files " +msgstr "--- İçerilen dosyalar " + +msgid "not found " +msgstr "bulunamadı " + +msgid "in path ---\n" +msgstr "yolda ---\n" + +msgid " (Already listed)" +msgstr " (Hâlihazırda listelenmiş)" + +msgid " NOT FOUND" +msgstr " BULUNAMADI" #, c-format -msgid "Cannot source a directory: \"%s\"" -msgstr "Dizin kaynak alınamıyor: \"%s\"" +msgid "Scanning included file: %s" +msgstr "İçerilen dosya taranıyor: %s" #, c-format -msgid "could not source \"%s\"" -msgstr "\"%s\" kaynak alınamadı" +msgid "Searching included file %s" +msgstr "İçerilen dosya %s aranıyor" + +msgid "E387: Match is on current line" +msgstr "E387: Eşleşme şu anda bulunulan satırda" + +msgid "All included files were found" +msgstr "Tüm içerilen dosyalar bulundu" + +msgid "No included files" +msgstr "İçerilen dosya yok" + +msgid "E388: Couldn't find definition" +msgstr "E388: Tanım bulunamadı" + +msgid "E389: Couldn't find pattern" +msgstr "E389: Dizgi bulunamadı" + +msgid "too few bytes read" +msgstr "pek az bayt okundu" #, c-format -msgid "line %ld: could not source \"%s\"" -msgstr "%ld. satır: \"%s\" kaynak alınamadı" +msgid "System error while skipping in ShaDa file: %s" +msgstr "Paylaşılan veri dosyası içinde atlanırken sistem hatası: %s" #, c-format -msgid "sourcing \"%s\"" -msgstr "\"%s\" kaynak alınıyor" +msgid "" +"Error while reading ShaDa file: last entry specified that it occupies " +"%<PRIu64> bytes, but file ended earlier" +msgstr "" +"Paylaşılan veri dosyası okunurken hata: Son girdi, %<PRIu64> bayt tuttuğunu " +"belirtti; ancak dosya daha erken sonlandı" #, c-format -msgid "line %ld: sourcing \"%s\"" -msgstr "%ld. satır: \"%s\" kaynak alınıyor" +msgid "System error while closing ShaDa file: %s" +msgstr "Paylaşılan veri dosyası kapatılırken sistem hatası: %s" #, c-format -msgid "finished sourcing %s" -msgstr "%s kaynak alınması bitti" +msgid "System error while writing ShaDa file: %s" +msgstr "Paylaşılan veri dosyası yazılırken sistem hatası: %s" #, c-format -msgid "continuing in %s" -msgstr "%s içinde sürdürülüyor" +msgid "Reading ShaDa file \"%s\"%s%s%s%s" +msgstr "Paylaşılan veri dosyası okunuyor: \"%s\"%s%s%s%s" -msgid "modeline" -msgstr "kip satırı" +msgid " info" +msgstr " bilgiler-" -msgid "--cmd argument" -msgstr "--cmd argümanı" +msgid " marks" +msgstr " imler-" -msgid "-c argument" -msgstr "-c argümanı" +msgid " oldfiles" +msgstr " düzenleme geçmişi" -msgid "environment variable" -msgstr "ortam değişkeni" +msgid " FAILED" +msgstr " BAŞARISIZ" -msgid "error handler" -msgstr "hata işleyicisi" +#, c-format +msgid "System error while opening ShaDa file %s for reading: %s" +msgstr "%s paylaşılan veri dosyası açılırken sistem hatası: %s" -msgid "changed window size" -msgstr "değiştirilen pencere boyutu" +msgid "additional elements of ShaDa " +msgstr "paylaşılan verinin ek ögeleri " -msgid "W15: Warning: Wrong line separator, ^M may be missing" -msgstr "W15: Uyarı: Yanlış satır ayırıcısı, ^M eksik olabilir" +msgid "additional data of ShaDa " +msgstr "paylaşılan verinin ek verisi" -msgid "E167: :scriptencoding used outside of a sourced file" -msgstr "E167: :scriptencoding kaynak alınmış bir dosyanın dışında kullanıldı" +#, c-format +msgid "Failed to write variable %s" +msgstr "%s değişkeni yazılamadı" -msgid "E984: :scriptversion used outside of a sourced file" -msgstr "E984: :scriptversion kaynak alınmış bir dosyanın dışında kullanıldı" +#, c-format +msgid "" +"Failed to parse ShaDa file due to a msgpack parser error at position " +"%<PRIu64>" +msgstr "" +"%<PRIu64> konumundaki bir msgpack ayrıştırıcı hatası nedeniyle paylaşılan " +"veri dosyası ayrıştırılamadı" #, c-format -msgid "E999: scriptversion not supported: %d" -msgstr "E999: desteklenmeyen scriptversion: %d" +msgid "" +"Failed to parse ShaDa file: incomplete msgpack string at position %<PRIu64>" +msgstr "" +"Paylaşılan veri dosyası ayrıştırılamadı: %<PRIu64> konumunda tam olmayan " +"msgpack dizisi" -msgid "E168: :finish used outside of a sourced file" -msgstr "E168: :finish kaynak alınmış bir dosyanın dışında kullanıldı" +#, c-format +msgid "" +"Failed to parse ShaDa file: extra bytes in msgpack string at position " +"%<PRIu64>" +msgstr "" +"Paylaşılan veri dosyası ayrıştırılamadı: %<PRIu64> konumundaki msgpack " +"dizisinde ek baytlar" #, c-format -msgid "E383: Invalid search string: %s" -msgstr "E383: Geçersiz arama dizisi: %s" +msgid "" +"System error while opening ShaDa file %s for reading to merge before writing " +"it: %s" +msgstr "" +"%s paylaşılan veri dosyasını yazmadan önce birleştirmek için okumak için " +"açarken sistem hatası: %s" #, c-format -msgid "E384: search hit TOP without match for: %s" -msgstr "E384: Arama dosyanın BAŞINA vardı, %s bulunamadı" +msgid "E138: All %s.tmp.X files exist, cannot write ShaDa file!" +msgstr "E138: Tüm %s.tmp.X dosyaları var, paylaşılan veri dosyası yazılamıyor!" #, c-format -msgid "E385: search hit BOTTOM without match for: %s" -msgstr "E385: Arama dosyanın SONUNA vardı, %s bulunamadı" +msgid "System error while opening temporary ShaDa file %s for writing: %s" +msgstr "" +"%s geçici paylaşılan veri dosyası yazma için açılırken sistem hatası: %s" -msgid "E386: Expected '?' or '/' after ';'" -msgstr "E386: ';' sonrasında '?' veya '/' bekleniyordu" +#, c-format +msgid "Failed to create directory %s for writing ShaDa file: %s" +msgstr "Paylaşılan veri dosyasını yazma için %s dizini oluşturulamadı: %s" -msgid " (includes previously listed match)" -msgstr " (daha önce listelenen eşleşmeyi içerir)" +#, c-format +msgid "System error while opening ShaDa file %s for writing: %s" +msgstr "%s paylaşılan veri dosyasını yazma için açarken sistem hatası: %s" -msgid "--- Included files " -msgstr "--- İçerilen dosyalar " +#, c-format +msgid "Writing ShaDa file \"%s\"" +msgstr "Paylaşılan veri dosyası \"%s\" yazılıyor" -msgid "not found " -msgstr "bulunamadı " +#, c-format +msgid "Failed setting uid and gid for file %s: %s" +msgstr "%s dosyası için uid ve gid ayarlanamadı: %s" -msgid "in path ---\n" -msgstr "yolda ---\n" +#, c-format +msgid "E137: ShaDa file is not writable: %s" +msgstr "E137: Paylaşılan veri dosyası yazılabilir değil: %s" -msgid " (Already listed)" -msgstr " (Hâlihazırda listelenmiş)" +#, c-format +msgid "Can't rename ShaDa file from %s to %s!" +msgstr "Paylaşılan veri dosyası %s -> %s olarak yeniden adlandırılamıyor!" -msgid " NOT FOUND" -msgstr " BULUNAMADI" +#, c-format +msgid "Did not rename %s because %s does not look like a ShaDa file" +msgstr "" +"%s yeniden adlandırılmadı; çünkü %s bir paylaşılan veri dosyasına benzemiyor" #, c-format -msgid "Scanning included file: %s" -msgstr "İçerilen dosya taranıyor: %s" +msgid "Did not rename %s to %s because there were errors during writing it" +msgstr "%s dosyasını %s olarak yeniden adlandırmanızın nedeni yazma sırasında " +"hatalar olması mı?" #, c-format -msgid "Searching included file %s" -msgstr "İçerilen dosya %s aranıyor" +msgid "Do not forget to remove %s or rename it manually to %s." +msgstr "%s dosyasını kaldırmayı veya el ile %s olarak yeniden adlandırmayı " +"unutmayın." -msgid "E387: Match is on current line" -msgstr "E387: Eşleşme şu anda bulunulan satırda" +#, c-format +msgid "System error while reading ShaDa file: %s" +msgstr "Paylaşılan veri dosyası okunurken sistem hatası: %s" -msgid "All included files were found" -msgstr "Tüm içerilen dosyalar bulundu" +#, c-format +msgid "System error while reading integer from ShaDa file: %s" +msgstr "Paylaşılan veri dosyasından tamsayı okunurken sistem hatası: %s" -msgid "No included files" -msgstr "İçerilen dosya yok" +#, c-format +msgid "" +"Error while reading ShaDa file: expected positive integer at position " +"%<PRIu64>, but got nothing" +msgstr "" +"Paylaşılan veri dosyası okunurken hata: %<PRIu64> konumunda pozitif tamsayı " +"bekleniyordu; ancak hiçbir şey alınmadı" -msgid "E388: Couldn't find definition" -msgstr "E388: Tanım bulunamadı" +#, c-format +msgid "" +"Error while reading ShaDa file: expected positive integer at position " +"%<PRIu64>" +msgstr "" +"Paylaşılan veri dosyası okunurken hata: %<PRIu64> konumunda pozitif tamsayı " +"bekleniyordu" -msgid "E389: Couldn't find pattern" -msgstr "E389: Dizgi bulunamadı" +#, c-format +msgid "" +"Error while reading ShaDa file: there is an item at position %<PRIu64> that " +"is stated to be too long" +msgstr "" +"Paylaşılan veri dosyası okunurken hata: %<PRIu64> konumunda bir öge var; ancak" +" pek uzun olduğu belirtildi" -msgid "Save View" -msgstr "Görünümü Kaydet" +#, c-format +msgid "" +"Error while reading ShaDa file: there is an item at position %<PRIu64> that " +"must not be there: Missing items are for internal uses only" +msgstr "" +"Paylaşılan veri dosyası okunurken hata: %<PRIu64> konumunda orada olmaması " +"gereken bir öge var: Eksik ögeler yalnızca iç kullanım içindir" + +#, c-format +msgid "" +"Error while reading ShaDa file: buffer list at position %<PRIu64> contains " +"entry that is not a dictionary" +msgstr "" +"Paylaşılan veri dosyası okunurken hata: %<PRIu64> konumundaki arabellek " +"listesi bir sözlük olmayan bir girdi içeriyor" + +#, c-format +msgid "" +"Error while reading ShaDa file: buffer list at position %<PRIu64> contains " +"entry with invalid line number" +msgstr "" +"Paylaşılan veri dosyası okunurken hata: %<PRIu64> konumundaki arabellek " +"listesi geçersiz satır numaralı bir girdi içeriyor" -msgid "Save Session" -msgstr "Oturumu Kaydet" +#, c-format +msgid "" +"Error while reading ShaDa file: buffer list at position %<PRIu64> contains " +"entry with invalid column number" +msgstr "" +"Paylaşılan veri dosyası okunurken hata: %<PRIu64> konumundaki arabellek " +"listesi geçersiz sütun numaralı bir girdi içeriyor" -msgid "Save Setup" -msgstr "Kurulumu Kaydet" +#, c-format +msgid "" +"Error while reading ShaDa file: buffer list at position %<PRIu64> contains " +"entry that does not have a file name" +msgstr "" +"Paylaşılan veri dosyası okunurken hata: %<PRIu64> konumundaki arabellek " +"listesi bir dosya adına iye olmayan bir girdi içeriyor" msgid "[Deleted]" msgstr "[Silindi]" @@ -4845,7 +4860,7 @@ msgstr "%s için işaretler:" #, c-format msgid " group=%s" -msgstr " grup=%s" +msgstr " grup=%s" #, c-format msgid " line=%ld id=%d%s name=%s priority=%d" @@ -4870,8 +4885,8 @@ msgid "E159: Missing sign number" msgstr "E159: İşaret numarası eksik" #, c-format -msgid "E157: Invalid sign ID: %d" -msgstr "E157: Geçersiz işaret kimliği: %d" +msgid "E157: Invalid sign ID: %<PRId64>" +msgstr "E157: Geçersiz işaret kimliği: %<PRId64>" msgid "E934: Cannot jump to a buffer that does not have a name" msgstr "E934: Adı olmayan bir arabelleğe atlamak olanaklı değil" @@ -4883,15 +4898,11 @@ msgstr "E160: Bilinmeyen işaret komutu: %s" msgid "E156: Missing sign name" msgstr "E156: İşaret adı eksik" -msgid " (NOT FOUND)" -msgstr " (BULUNAMADI)" - msgid " (not supported)" msgstr " (desteklenmiyor)" -#, c-format -msgid "Warning: Cannot find word list \"%s_%s.spl\" or \"%s_ascii.spl\"" -msgstr "Uyarı: Sözcük listesi \"%s_%s.spl\" veya \"%s_ascii.spl\" bulunamıyor" +msgid "E759: Format error in spell file" +msgstr "E759: Takas dosyasında biçim hatası" #, c-format msgid "Warning: Cannot find word list \"%s.%s.spl\" or \"%s.ascii.spl\"" @@ -4904,6 +4915,21 @@ msgstr "E797: SpellFileMissing otokomutu arabelleği sildi" msgid "Warning: region %s not supported" msgstr "Uyarı: %s bölgesi desteklenmiyor" +msgid "Sorry, no suggestions" +msgstr "Üzgünüm, şu an için bir önerim yok" + +#, c-format +msgid "Sorry, only %<PRId64> suggestions" +msgstr "Üzgünüm, yalnızca %<PRId64> öneri" + +#, c-format +msgid "Change \"%.*s\" to:" +msgstr "\"%.*s\" şuna değiştirilecek:" + +#, c-format +msgid " < \"%.*s\"" +msgstr " < \"%.*s\"" + msgid "E752: No previous spell replacement" msgstr "E752: Öncesinde düzeltilmiş bir yazım yok" @@ -4922,12 +4948,6 @@ msgstr "%s içinde %d. satır ucunda fazladan metin: %s" msgid "Affix name too long in %s line %d: %s" msgstr "%s içinde %d. satırda ek adı çok uzun: %s" -msgid "E761: Format error in affix file FOL, LOW or UPP" -msgstr "E761: Ekler dosyası FOL, LOW veya UPP içinde biçimlendirme hatası" - -msgid "E762: Character in FOL, LOW or UPP is out of range" -msgstr "E762: FOL, LOW veya UPP içindeki karakterler erimin dışında" - msgid "Compressing word tree..." msgstr "Sözcük soyağacı sıkıştırılıyor..." @@ -4938,8 +4958,12 @@ msgstr "Yazım dosyası \"%s\" okunuyor" msgid "E757: This does not look like a spell file" msgstr "E757: Bu bir yazım dosyasına benzemiyor" +#, c-format +msgid "E5042: Failed to read spell file %s: %s" +msgstr "E5042: %s yazım dosyası okunamadı: %s" + msgid "E771: Old spell file, needs to be updated" -msgstr "E771: Eski yazım dosyası, güncellenmesi gerekiyor" +msgstr "E771: Eski yazım dosyası; güncellenmesi gerekiyor" msgid "E772: Spell file is for newer version of Vim" msgstr "E772: Yazım dosyası Vim'in daha yeni bir sürümü için" @@ -5034,7 +5058,7 @@ msgstr "%s içinde %d. satırda yinelenen ek: %s" #, c-format msgid "" -"Affix also used for BAD/RARE/KEEPCASE/NEEDAFFIX/NEEDCOMPOUND/NOSUGGEST in %s " +"Affix also used for BAD/RARE/KEEPCASE/NEEDAFFIX/NEEDCOMPOUND/NOSUGGESTin %s " "line %d: %s" msgstr "" "Ek aynı zamanda %s içinde %d. satırda BAD/RARE/KEEPCASE/NEEDAFFIX/" @@ -5064,10 +5088,6 @@ msgstr "%s içinde %d. satırda MAP içinde yinelenen karakter" msgid "Unrecognized or duplicate item in %s line %d: %s" msgstr "%s içinde %d. satırda yinelenen veya tanınmayan öge: %s" -#, c-format -msgid "Missing FOL/LOW/UPP line in %s" -msgstr "%s içinde FOL/LOW/UPP satırı eksik" - msgid "COMPOUNDSYLMAX used without SYLLABLE" msgstr "COMPOUNDSYLMAX, SYLLABLE olmadan kullanılmış" @@ -5151,7 +5171,7 @@ msgstr "%s içinde %ld. satırda yinelenen /regions= satırı yok sayıldı: %s" #, c-format msgid "Too many regions in %s line %ld: %s" -msgstr "%s içinde %ld. satırda çok fazla bölge: %s" +msgstr "%s içinde %ld. satırda pek fazla bölge: %s" #, c-format msgid "/ line ignored in %s line %ld: %s" @@ -5169,12 +5189,9 @@ msgstr "%s içinde %ld. satırda tanınmayan bayraklar: %s" msgid "Ignored %d words with non-ASCII characters" msgstr "ASCII olmayan karakter içeren %d sözcük yok sayıldı" -msgid "E845: Insufficient memory, word list will be incomplete" -msgstr "E845: Yetersiz bellek, sözcük listesi tam olmayacak" - #, c-format -msgid "Compressed %s: %ld of %ld nodes; %ld (%ld%%) remaining" -msgstr "%s sıkıştırılıyor: %ld/%ld uç sıkıştırıldı; %ld (%%%ld) kalan" +msgid "Compressed %s of %ld nodes; %ld (%ld%%) remaining" +msgstr "%s/%ld uç sıkıştırıldı; %ld (%%%ld) kalan" msgid "Reading back spell file..." msgstr "Yazım dosyası yeniden okunuyor..." @@ -5183,8 +5200,8 @@ msgid "Performing soundfolding..." msgstr "Sesler evriştiriliyor..." #, c-format -msgid "Number of words after soundfolding: %ld" -msgstr "Ses evriştirme sonrası sözcük sayısı: %ld" +msgid "Number of words after soundfolding: %<PRId64>" +msgstr "Ses evriştirme sonrası sözcük sayısı: %<PRId64>" #, c-format msgid "Total number of words: %d" @@ -5196,7 +5213,7 @@ msgstr "Öneriler dosyası %s yazılıyor..." #, c-format msgid "Estimated runtime memory use: %d bytes" -msgstr "Tahmini çalıştırılan bellek kullanımı: %d bit" +msgstr "Tahmini çalışma bellek kullanımı: %d bayt" msgid "E751: Output file name must not have region name" msgstr "E751: Çıktı dosyası bir bölge adı içermemelidir" @@ -5220,8 +5237,8 @@ msgid "Done!" msgstr "Yapıldı!" #, c-format -msgid "E765: 'spellfile' does not have %d entries" -msgstr "E765: 'spellfile' içinde %d adet girdi yok" +msgid "E765: 'spellfile' does not have %<PRId64> entries" +msgstr "E765: 'spellfile' içinde %<PRId64> adet girdi yok" #, c-format msgid "Word '%.*s' removed from %s" @@ -5240,20 +5257,14 @@ msgstr "E763: Sözcük karakterleri yazım dosyaları arasında ayrım gösteriy msgid "E783: duplicate char in MAP entry" msgstr "E783: MAP girdisinde yinelenen karakter" -msgid "Sorry, no suggestions" -msgstr "Üzgünüm, şu an için bir önerim yok" - -#, c-format -msgid "Sorry, only %ld suggestions" -msgstr "Üzgünüm, yalnızca %ld öneri" +msgid "E766: Insufficient arguments for printf()" +msgstr "E766: printf() için yetersiz argüman" -#, c-format -msgid "Change \"%.*s\" to:" -msgstr "\"%.*s\" şuna değiştirilecek:" +msgid "E807: Expected Float argument for printf()" +msgstr "E807: printf() için kayan noktalı değer türünde argüman bekleniyordu" -#, c-format -msgid " < \"%.*s\"" -msgstr " < \"%.*s\"" +msgid "E767: Too many arguments to printf()" +msgstr "E767: printf() için pek fazla argüman" #, c-format msgid "E390: Illegal argument: %s" @@ -5265,36 +5276,6 @@ msgstr "Bu arabellek için sözdizim ögeleri tanımlanmamış" msgid "'redrawtime' exceeded, syntax highlighting disabled" msgstr "'redrawtime' aşıldı, sözdizim vurgulaması kapatıldı" -msgid "syntax conceal on" -msgstr "sözdizim gizlemesi açık" - -msgid "syntax conceal off" -msgstr "sözdizim gizlemesi kapalı" - -msgid "syntax case ignore" -msgstr "sözdizim BÜYÜK/küçük harf yok say" - -msgid "syntax case match" -msgstr "sözdizim BÜYÜK/küçük harfe duyarlı" - -msgid "syntax foldlevel start" -msgstr "sözdizim kıvırma düzeyi başlangıcı" - -msgid "syntax foldlevel minimum" -msgstr "sözdizim kıvırma düzeyi an az" - -msgid "syntax spell toplevel" -msgstr "bir sözdizim içinde olmayan metinde yazım denetimi yap" - -msgid "syntax spell notoplevel" -msgstr "bir sözdizim içinde olmayan metinde yazım denetimi yapma" - -msgid "syntax spell default" -msgstr "@Spell kümesi varsa yazım denetimi yapma (öntanımlı)" - -msgid "syntax iskeyword " -msgstr "sözdizim anahtar sözcük" - msgid "syntax iskeyword not set" msgstr "sözdizim anahtar sözcük ayarlanmamış" @@ -5479,7 +5460,7 @@ msgid " or more" msgstr " veya daha fazla" msgid " Using tag with different case!" -msgstr " Etiket değişik bir durumla kullanılıyor!" +msgstr " Etiket değişik bir durumla kullanılıyor!" #, c-format msgid "E429: File \"%s\" does not exist" @@ -5503,16 +5484,12 @@ msgid "Searching tags file %s" msgstr "Etiket dosyası %s aranıyor" #, c-format -msgid "E430: Tag file path truncated for %s\n" -msgstr "E430: %s için etiket dosyası yolu kırpıldı\n" - -#, c-format msgid "E431: Format error in tags file \"%s\"" msgstr "E431: Etiket dosyası \"%s\" içinde biçim hatası" #, c-format -msgid "Before byte %ld" -msgstr "%ld bitinden önce" +msgid "Before byte %<PRId64>" +msgstr "%<PRId64> baytından önce" #, c-format msgid "E432: Tags file not sorted: %s" @@ -5521,9 +5498,6 @@ msgstr "E432: Etiket dosyası sıralanmadı: %s" msgid "E433: No tags file" msgstr "E433: Etiket dosyası yok" -msgid "Ignoring long line in tags file" -msgstr "Etiket dosyasındaki uzun satır yok sayılıyor" - msgid "E434: Can't find tag pattern" msgstr "E434: Etiket dizgisi bulunamıyor" @@ -5534,237 +5508,21 @@ msgstr "E435: Etiket bulunamadı, tahmin ediliyor!" msgid "Duplicate field name: %s" msgstr "Yinelenen alan adı: %s" -msgid "' not known. Available builtin terminals are:" -msgstr "' bilinmiyor. Kullanılabilir uçbirimler şunlar:" - -msgid "defaulting to '" -msgstr "öntanımlı olarak '" - -msgid "E557: Cannot open termcap file" -msgstr "E557: termcap dosyası açılamıyor" - -msgid "E558: Terminal entry not found in terminfo" -msgstr "E558: terminfo içinde uçbirim girdisi bulunamadı" - -msgid "E559: Terminal entry not found in termcap" -msgstr "E559: termcap içinde uçbirim bilgisi bulunamadı" - -#, c-format -msgid "E436: No \"%s\" entry in termcap" -msgstr "E436: termcap içinde \"%s\" girdisi yok" - -msgid "E437: terminal capability \"cm\" required" -msgstr "E437: \"cm\" uçbirim yeteneği gerekiyor" - -msgid "" -"\n" -"--- Terminal keys ---" -msgstr "" -"\n" -"--- Uçbirim düğmeleri ---" - -#, c-format -msgid "E181: Invalid attribute: %s" -msgstr "E181: Geçersiz öznitelik: %s" - -msgid "E279: Sorry, ++shell is not supported on this system" -msgstr "E279: Üzgünüm, ++shell bu sistemde desteklenmiyor" - -#, c-format -msgid "Kill job in \"%s\"?" -msgstr "\"%s\" içindeki iş sonlandırılsın mı?" - -msgid "Terminal" -msgstr "Uçbirim" - -msgid "Terminal-finished" -msgstr "Uçbirim-bitti" - -msgid "active" -msgstr "etkin" - -msgid "running" -msgstr "çalışıyor" - -msgid "finished" -msgstr "bitti" - -msgid "E958: Job already finished" -msgstr "E958: İş bitti bile" - -#, c-format -msgid "E953: File exists: %s" -msgstr "E953: Dosya mevcut: %s" - -msgid "E955: Not a terminal buffer" -msgstr "E955: Bir uçbirim arabelleği değil" - -msgid "E982: ConPTY is not available" -msgstr "E982: ConPTY mevcut değil" - -#, c-format -msgid "E971: Property type %s does not exist" -msgstr "E971: Özellik türü %s mevcut değil" - -#, c-format -msgid "E964: Invalid column number: %ld" -msgstr "E964: Geçersiz sütun numarası: %ld" - -#, c-format -msgid "E966: Invalid line number: %ld" -msgstr "E966: Geçersiz satır numarası: %ld" - -msgid "E965: missing property type name" -msgstr "E965: Özellik tür adı eksik" - -msgid "E275: Cannot add text property to unloaded buffer" -msgstr "E275: Bellekten kaldırılmış arabelleğe metin özelliği eklenemiyor" - -msgid "E967: text property info corrupted" -msgstr "E967: Metin özellik bilgisi hasarlı" - -msgid "E968: Need at least one of 'id' or 'type'" -msgstr "E968: En azından bir 'id' veya 'type' gerekli" - -msgid "E860: Need 'id' and 'type' with 'both'" -msgstr "E860: 'both' ile 'id' ve 'type' gerekli" - -#, c-format -msgid "E969: Property type %s already defined" -msgstr "E969: Özellik türü %s hâlihazırda tanımlanmış" - -#, c-format -msgid "E970: Unknown highlight group name: '%s'" -msgstr "E970: Bilinmeyen vurgulama grup adı: '%s'" - -msgid "(Invalid)" -msgstr "(Geçersiz)" - -msgid "%a %b %d %H:%M:%S %Y" -msgstr "%a %b %d %H:%M:%S %Y" - -#, c-format -msgid "%ld second ago" -msgid_plural "%ld seconds ago" -msgstr[0] "%ld saniye önce" -msgstr[1] "%ld saniye önce" - -msgid "E805: Using a Float as a Number" -msgstr "E805: Bir Kayan Noktalı Değer, Sayı yerine kullanılıyor" - -msgid "E703: Using a Funcref as a Number" -msgstr "E703: Bir Funcref, Sayı yerine kullanılıyor" - -msgid "E745: Using a List as a Number" -msgstr "E745: Bir Liste, Sayı yerine kullanılıyor" - -msgid "E728: Using a Dictionary as a Number" -msgstr "E728: Bir Sözlük, Sayı yerine kullanılıyor" - -msgid "E611: Using a Special as a Number" -msgstr "E611: Bir Özel, Sayı yerine kullanılıyor" - -msgid "E910: Using a Job as a Number" -msgstr "E910: Bir İş, Sayı yerine kullanılıyor" - -msgid "E913: Using a Channel as a Number" -msgstr "E913: Bir Kanal, Sayı yerine kullanılıyor" - -msgid "E974: Using a Blob as a Number" -msgstr "E974: Bir İkili Geniş Nesne, Sayı yerine kullanılıyor" - -msgid "E891: Using a Funcref as a Float" -msgstr "E891: Bir Funcref, Kayan Noktalı Değer yerine kullanılıyor" - -msgid "E892: Using a String as a Float" -msgstr "E892: Bir Dizi, Kayan Noktalı Değer yerine kullanılıyor" - -msgid "E893: Using a List as a Float" -msgstr "E893: Bir Liste, Kayan Noktalı Değer yerine kullanılıyor" - -msgid "E894: Using a Dictionary as a Float" -msgstr "E894: Bir Sözlük, Kayan Noktalı Değer yerine kullanılıyor" - -msgid "E362: Using a boolean value as a Float" -msgstr "E362: Bir Boole Değeri, Kayan Noktalı Değer yerine kullanılıyor" - -msgid "E907: Using a special value as a Float" -msgstr "E907: Bir Özel Değer, Kayan Noktalı Değer yerine kullanılıyor" - -msgid "E911: Using a Job as a Float" -msgstr "E911: Bir İş, Kayan Noktalı Değer yerine kullanılıyor" - -msgid "E914: Using a Channel as a Float" -msgstr "E914: Bir Kanal, Kayan Noktalı Değer yerine kullanılıyor" - -msgid "E975: Using a Blob as a Float" -msgstr "E975: Bir İkili Geniş Nesne, Kayan Noktalı Değer yerine kullanılıyor" - -msgid "E729: Using a Funcref as a String" -msgstr "E729: Funcref bir Dizi yerine kullanılıyor" - -msgid "E730: Using a List as a String" -msgstr "E730: Liste bir Dizi yerine kullanılıyor" - -msgid "E731: Using a Dictionary as a String" -msgstr "E731: Sözlük bir Dizi yerine kullanılıyor" - -msgid "E976: Using a Blob as a String" -msgstr "E976: İkili Geniş Nesne bir Dizi yerine kullanılıyor" - -msgid "E977: Can only compare Blob with Blob" -msgstr "" -"E977: Bir ikili geniş öğe yalnızca kendinden bir başkası ile " -"karşılaştırılabilir" - -msgid "E691: Can only compare List with List" -msgstr "E691: Bir liste yalnızca başka bir liste ile karşılaştırılabilir" - -msgid "E692: Invalid operation for List" -msgstr "E692: Geçersiz liste işlemi" - -msgid "E735: Can only compare Dictionary with Dictionary" -msgstr "E735: Bir sözlük yalnızca başka bir sözlük ile karşılaştırılabilir" - -msgid "E736: Invalid operation for Dictionary" -msgstr "E736: Geçersiz sözlük işlemi" - -msgid "E694: Invalid operation for Funcrefs" -msgstr "E694: Geçersiz Funcref işlemi" - -#, c-format -msgid "E112: Option name missing: %s" -msgstr "E112: Seçenek adı eksik: %s" - -msgid "E973: Blob literal should have an even number of hex characters" -msgstr "" -"E973: İkili geniş nesne hazır bilgisi çift onalt. karakterlere iye olmalıdır" - -#, c-format -msgid "E114: Missing quote: %s" -msgstr "E114: Tırnak imi eksik: %s" - -#, c-format -msgid "E115: Missing quote: %s" -msgstr "E115: Tırnak imi eksik: %s" - -msgid "new shell started\n" -msgstr "yeni kabuk başlatıldı\n" - -msgid "Vim: Error reading input, exiting...\n" -msgstr "Vim: Girdi okunurken hata, çıkılıyor...\n" +msgid "Beep!" +msgstr "Bip!" msgid "E881: Line count changed unexpectedly" msgstr "E881: Satır sayısı beklenmeyen bir biçimde değişti" -msgid "No undo possible; continue anyway" -msgstr "Geri alma olanaklı değil; bekleme yapma" - #, c-format msgid "E828: Cannot open undo file for writing: %s" msgstr "E828: Geri al dosyası yazma için açılamıyor: %s" #, c-format +msgid "E5003: Unable to create directory \"%s\" for undo file: %s" +msgstr "E5003: \"%s\" dizini, geri al dosyası için oluşturulamadı: %s" + +#, c-format msgid "E825: Corrupted undo file (%s): %s" msgstr "E825: Hasarlı geri al dosyası (%s): %s" @@ -5807,18 +5565,6 @@ msgid "E823: Not an undo file: %s" msgstr "E823: Bir geri al dosyası değil: %s" #, c-format -msgid "E832: Non-encrypted file has encrypted undo file: %s" -msgstr "E832: Şifrelenmemiş dosyanın şifrelenmiş bir geri al dosyası var: %s" - -#, c-format -msgid "E826: Undo file decryption failed: %s" -msgstr "E826: Geri al dosyası şifre çözümü başarısız oldu: %s" - -#, c-format -msgid "E827: Undo file is encrypted: %s" -msgstr "E827: Geri al dosyası şifrelenmiş: %s" - -#, c-format msgid "E824: Incompatible undo file: %s" msgstr "E824: Uyumsuz geri al dosyası: %s" @@ -5836,8 +5582,8 @@ msgid "Already at newest change" msgstr "Hâlihazırda en yeni değişiklik üzerinde" #, c-format -msgid "E830: Undo number %ld not found" -msgstr "E830: %ld numaralı geri alma bulunamadı" +msgid "E830: Undo number %<PRId64> not found" +msgstr "E830: %<PRId64> numaralı geri alma bulunamadı" msgid "E438: u_undo: line numbers wrong" msgstr "E438: u_undo: Satır numaraları yanlış" @@ -5861,15 +5607,15 @@ msgid "changes" msgstr "değişiklik" #, c-format -msgid "%ld %s; %s #%ld %s" -msgstr "%ld %s; %s #%ld %s" - -msgid "before" -msgstr "şundan önce:" +msgid "%<PRId64> %s; %s #%<PRId64> %s" +msgstr "%<PRId64> %s; %s #%<PRId64> %s" msgid "after" msgstr "şundan sonra:" +msgid "before" +msgstr "şundan önce:" + msgid "Nothing to undo" msgstr "Geri alınacak bir şey yok" @@ -5887,275 +5633,12 @@ msgstr "E440: Geri al satırı eksik" msgid "" "\n" -" Name Args Address Complete Definition" -msgstr "" "\n" -" Ad Dğkl Adres Tam Tanım" - -msgid "No user-defined commands found" -msgstr "Kullanıcı tanımlı bir komut bulunamadı" - -#, c-format -msgid "E180: Invalid address type value: %s" -msgstr "E180: Geçersiz adres türü değeri: %s" - -#, c-format -msgid "E180: Invalid complete value: %s" -msgstr "E180: Geçersiz tam değer: %s" - -msgid "E468: Completion argument only allowed for custom completion" -msgstr "E468: Tamamlama argümanına yalnızca özel tamamlamalarda izin verilir" - -msgid "E467: Custom completion requires a function argument" -msgstr "E467: Özel tamamlama bir işlev argümanı gerektirir" - -msgid "E175: No attribute specified" -msgstr "E175: Bir öznitelik belirtilmemiş" - -msgid "E176: Invalid number of arguments" -msgstr "E176: Geçersiz argüman sayısı" - -msgid "E177: Count cannot be specified twice" -msgstr "E177: Sayım iki defa belirtilemez" - -msgid "E178: Invalid default value for count" -msgstr "E178: Sayım için geçersiz öntanımlı değer" - -msgid "E179: argument required for -complete" -msgstr "E179: -complete için argüman gerekiyor" - -msgid "E179: argument required for -addr" -msgstr "E179: -addr için argüman gerekiyor" - -#, c-format -msgid "E174: Command already exists: add ! to replace it: %s" -msgstr "E174: Komut zaten mevcut: değiştirmek için ! ekleyin: %s" - -msgid "E182: Invalid command name" -msgstr "E182: Geçersiz komut adı" - -msgid "E183: User defined commands must start with an uppercase letter" -msgstr "E183: Kullanıcı tanımlı komutlar BÜYÜK harfle başlamalıdır" - -msgid "E841: Reserved name, cannot be used for user defined command" -msgstr "E841: Ayrılmış ad, kullanıcı tanımlı komut için kullanılamaz" - -#, c-format -msgid "E184: No such user-defined command: %s" -msgstr "E184: Böyle bir kullanıcı tanımlı komut yok: %s" - -#, c-format -msgid "E122: Function %s already exists, add ! to replace it" -msgstr "E122: %s işlevi hâlihazırda mevcut, değiştirmek için ! ekleyin" - -msgid "E717: Dictionary entry already exists" -msgstr "E717: Sözlük girdisi hâlihazırda mevcut" - -msgid "E718: Funcref required" -msgstr "E718: Funcref gerekiyor" - -#, c-format -msgid "E130: Unknown function: %s" -msgstr "E130: Bilinmeyen işlev: %s" - -#, c-format -msgid "E125: Illegal argument: %s" -msgstr "E125: İzin verilmeyen argüman: %s" - -#, c-format -msgid "E853: Duplicate argument name: %s" -msgstr "E853: Yinelenen argüman adı: %s" - -msgid "E989: Non-default argument follows default argument" -msgstr "E989: Öntanımlı olmayan argüman öntanımlı argümandan sonra" - -msgid "E126: Missing :endfunction" -msgstr "E126: :endfunction eksik" - -#, c-format -msgid "W22: Text found after :endfunction: %s" -msgstr "W22: :endfunction sonrası metin bulundu: %s" - -#, c-format -msgid "E451: Expected }: %s" -msgstr "E451: } bekleniyordu: %s" - -#, c-format -msgid "E740: Too many arguments for function %s" -msgstr "E740: %s işlevi için çok fazla argüman" - -#, c-format -msgid "E116: Invalid arguments for function %s" -msgstr "E116: %s işlevi için geçersiz argümanlar" - -msgid "E132: Function call depth is higher than 'maxfuncdepth'" -msgstr "E132: İşlevin çağırdığı derinlik 'maxfuncdepth'ten daha yüksek" - -#, c-format -msgid "calling %s" -msgstr "%s çağrılıyor" - -#, c-format -msgid "%s aborted" -msgstr "%s durduruldu" - -#, c-format -msgid "%s returning #%ld" -msgstr "%s, #%ld döndürüyor" - -#, c-format -msgid "%s returning %s" -msgstr "%s, %s döndürüyor" - -msgid "E699: Too many arguments" -msgstr "E699: Çok fazla argüman" - -#, c-format -msgid "E276: Cannot use function as a method: %s" -msgstr "E276: İşlev bir yöntem olarak kullanılamaz: %s" - -#, c-format -msgid "E120: Using <SID> not in a script context: %s" -msgstr "E120: <SID> bir betik bağlamında kullanılmıyor: %s" - -#, c-format -msgid "E725: Calling dict function without Dictionary: %s" -msgstr "E725: dic işlevi bir sözlük olmadan çağrılıyor: %s" - -msgid "E129: Function name required" -msgstr "E129: İşlev adı gerekiyor" - -#, c-format -msgid "E128: Function name must start with a capital or \"s:\": %s" -msgstr "E128: İşlev adı bir BÜYÜK harfle veya \"s:\" ile başlamalı: %s" - -#, c-format -msgid "E884: Function name cannot contain a colon: %s" -msgstr "E884: İşlev adı iki nokta içeremez: %s" - -msgid "E454: function list was modified" -msgstr "E454: İşlev listesi değiştirilmiş" - -#, c-format -msgid "E123: Undefined function: %s" -msgstr "E123: Tanımlanmamış işlev: %s" - -#, c-format -msgid "E124: Missing '(': %s" -msgstr "E124: '(' eksik: %s" - -msgid "E862: Cannot use g: here" -msgstr "E862: g: burada kullanılamaz" - -#, c-format -msgid "E932: Closure function should not be at top level: %s" -msgstr "E932: Kapatma işlevi en üst düzeyde olmamalıdır: %s" - -#, c-format -msgid "E707: Function name conflicts with variable: %s" -msgstr "E707: İşlev adı şu değişken ile çakışıyor: %s" - -#, c-format -msgid "E127: Cannot redefine function %s: It is in use" -msgstr "E127: %s işlevi yeniden tanımlanamıyor: Şu an kullanımda" - -#, c-format -msgid "E746: Function name does not match script file name: %s" -msgstr "E746: İşlev adı betik dosyası adına eşleşmiyor: %s" - -#, c-format -msgid "E131: Cannot delete function %s: It is in use" -msgstr "E131: %s işlevi silinemiyor: Şu an kullanımda" - -msgid "E133: :return not inside a function" -msgstr "E133: :return bir işlev içinde değil" - -#, c-format -msgid "%s (%s, compiled %s)" -msgstr "%s (%s, %s tarihinde derlendi)" - -msgid "" -"\n" -"MS-Windows 64-bit GUI/console version" -msgstr "" -"\n" -"MS-Windows 64-bit grafik arabirim/konsol sürümü" - -msgid "" -"\n" -"MS-Windows 32-bit GUI/console version" -msgstr "" -"\n" -"MS-Windows 32-bit grafik arabirim/konsol sürümü" - -msgid "" -"\n" -"MS-Windows 64-bit GUI version" -msgstr "" -"\n" -"MS-Windows 64-bit grafik arabirim sürümü" - -msgid "" -"\n" -"MS-Windows 32-bit GUI version" -msgstr "" -"\n" -"MS-Windows 32-bit grafik arabirim sürümü" - -msgid " with OLE support" -msgstr ", OLE desteği ile" - -msgid "" -"\n" -"MS-Windows 64-bit console version" -msgstr "" -"\n" -"MS-Windows 64-bit konsol sürümü" - -msgid "" -"\n" -"MS-Windows 32-bit console version" -msgstr "" -"\n" -"MS-Windows 32-bit konsol sürümü" - -msgid "" -"\n" -"macOS version" -msgstr "" -"\n" -"macOS sürümü" - -msgid "" -"\n" -"macOS version w/o darwin feat." +"Features: " msgstr "" "\n" -"Darwin özellikleri olmayan macOS sürümü" - -msgid "" -"\n" -"OpenVMS version" -msgstr "" "\n" -"OpenVMS sürümü" - -msgid "" -"\n" -"Included patches: " -msgstr "" -"\n" -"İçerilen yamalar: " - -msgid "" -"\n" -"Extra patches: " -msgstr "" -"\n" -"Ek yamalar: " - -msgid "Modified by " -msgstr "Değiştirme: " +"Özellikler: " msgid "" "\n" @@ -6167,181 +5650,38 @@ msgstr "" msgid "by " msgstr " " -msgid "" -"\n" -"Huge version " -msgstr "" -"\n" -"Dev sürüm " - -msgid "" -"\n" -"Big version " -msgstr "" -"\n" -"Büyük sürüm " - -msgid "" -"\n" -"Normal version " -msgstr "" -"\n" -"Orta boy sürüm " - -msgid "" -"\n" -"Small version " -msgstr "" -"\n" -"Küçük sürüm " - -msgid "" -"\n" -"Tiny version " -msgstr "" -"\n" -"Ufak sürüm " - -msgid "without GUI." -msgstr "(grafik arabirim içermez)." - -msgid "with GTK3 GUI." -msgstr "(GTK3 grafik arabirim ile)." - -msgid "with GTK2-GNOME GUI." -msgstr "(GTK2-GNOME grafik arabirim ile)." - -msgid "with GTK2 GUI." -msgstr "(GTK2 grafik arabirim ile)." - -msgid "with X11-Motif GUI." -msgstr "(X11-Motif grafik arabirim ile)." - -msgid "with X11-neXtaw GUI." -msgstr "(X11-neXtaw grafik arabirim ile)." - -msgid "with X11-Athena GUI." -msgstr "(X11-Athena grafik arabirim ile)." - -msgid "with Haiku GUI." -msgstr "(Haiku grafik arabirimi ile)." - -msgid "with Photon GUI." -msgstr "(Photon grafik arabirim ile)." - -msgid "with GUI." -msgstr "(grafik arabirim ile)." - -msgid " Features included (+) or not (-):\n" -msgstr " İçerilen özellikler (+), içerilmeyenler (-) ile gösterilir:\n" - msgid " system vimrc file: \"" msgstr " sistem vimrc dosyası: \"" -msgid " user vimrc file: \"" -msgstr " kullanıcı vimrc dosyası: \"" - -msgid " 2nd user vimrc file: \"" -msgstr " kullanıcı 2. vimrc dosyası: \"" - -msgid " 3rd user vimrc file: \"" -msgstr " kullanıcı 3. vimrc dosyası: \"" - -msgid " user exrc file: \"" -msgstr " kullanıcı exrc dosyası: \"" - -msgid " 2nd user exrc file: \"" -msgstr " kullanıcı 2. exrc dosyası: \"" - -msgid " system gvimrc file: \"" -msgstr " sistem gvimrc dosyası: \"" - -msgid " user gvimrc file: \"" -msgstr " kullanıcı gvimrc dosyası: \"" - -msgid "2nd user gvimrc file: \"" -msgstr " kullanıcı 2. gvimrc dosyası: \"" - -msgid "3rd user gvimrc file: \"" -msgstr " kullanıcı 3. gvimrc dosyası: \"" - -msgid " defaults file: \"" -msgstr " öntanımlılar dosyası: \"" - -msgid " system menu file: \"" -msgstr " sistem menü dosyaları: \"" - msgid " fall-back for $VIM: \"" msgstr " $VIM öntanımlı konumu: \"" msgid " f-b for $VIMRUNTIME: \"" msgstr "$VIMRUNTIME öntanımlı konumu: \"" -msgid "Compilation: " -msgstr "Derleme: " - -msgid "Compiler: " -msgstr "Derleyici: " - -msgid "Linking: " -msgstr "Bağlama: " +msgid "Nvim is open source and freely distributable" +msgstr "Nvim açık kaynaklıdır ve özgürce dağıtılabilir" -msgid " DEBUG BUILD" -msgstr " HATA AYIKLAMA AMAÇLI SÜRÜM" +msgid "https://neovim.io/#chat" +msgstr "https://neovim.io/#chat" -msgid "VIM - Vi IMproved" -msgstr "VİM - Vi IMproved" +msgid "type :help nvim<Enter> if you are new! " +msgstr "eğer yeniyseniz :help nvim<Enter> " -msgid "version " -msgstr "sürüm: " +msgid "type :checkhealth<Enter> to optimize Nvim" +msgstr "Nvim'i eniyilemek için :help checkhealth<Enter>" -msgid "by Bram Moolenaar et al." -msgstr "geliştirme: Bram Moolenaar ve diğerleri" +msgid "type :q<Enter> to exit " +msgstr "çıkmak için :q<Enter> " -msgid "Vim is open source and freely distributable" -msgstr "Vim açık kaynaklıdır ve özgürce dağıtılabilir" +msgid "type :help<Enter> for help " +msgstr "yardım için :help<Enter> " msgid "Help poor children in Uganda!" msgstr "Uganda'daki yoksul çocuklara yardım edin!" msgid "type :help iccf<Enter> for information " -msgstr "ek bilgi için :help iccf<Enter> " - -msgid "type :q<Enter> to exit " -msgstr "çıkmak için :q<Enter> " - -msgid "type :help<Enter> or <F1> for on-line help" -msgstr "yardım belgeleri için :help<Enter> veya <F1> " - -msgid "type :help version8<Enter> for version info" -msgstr "sürüm bilgisi için :help version8<Enter> " - -msgid "Running in Vi compatible mode" -msgstr "Vi uyumlu kipte çalışıyor" - -msgid "type :set nocp<Enter> for Vim defaults" -msgstr "öntanımlı ayarlar için :set nocp<Enter> " - -msgid "type :help cp-default<Enter> for info on this" -msgstr "ek bilgi için :help cp-default<Enter>" - -msgid "menu Help->Orphans for information " -msgstr "bilgi için menü -> Yardım -> Yetimler" - -msgid "Running modeless, typed text is inserted" -msgstr "Kipsiz çalışıyor, girilen metin doğrudan eklenir" - -msgid "menu Edit->Global Settings->Toggle Insert Mode " -msgstr "menü -> Düzen -> Genel Ayarlar -> Ekleme Kipine Geç" - -msgid " for two modes " -msgstr " iki kip için " - -msgid "menu Edit->Global Settings->Toggle Vi Compatible" -msgstr "menü -> Düzen -> Genel Ayarlar -> Vi Uyumlu Kipi Aç/Kapat" - -msgid " for Vim defaults " -msgstr " Vim öntanımlıları için " +msgstr "ek bilgi için :help iccf<Enter> " msgid "Sponsor Vim development!" msgstr "Vim'in geliştirilmesine sponsor olun!" @@ -6350,3303 +5690,200 @@ msgid "Become a registered Vim user!" msgstr "Kayıtlı bir Vim kullanıcısı olun!" msgid "type :help sponsor<Enter> for information " -msgstr "bilgi için :help sponsor<Enter> " +msgstr "bilgi için :help sponsor<Enter> " msgid "type :help register<Enter> for information " -msgstr "bilgi için :help register<Enter> " +msgstr "bilgi için :help register<Enter> " msgid "menu Help->Sponsor/Register for information " -msgstr "bilgi için Yardım -> Sponsorluk/Kayıt" - -msgid "[end of lines]" -msgstr "[satırların sonu]" - -msgid "global" -msgstr "global" - -msgid "buffer" -msgstr "arabellek" - -msgid "window" -msgstr "pencere" - -msgid "tab" -msgstr "sekme" - -msgid "" -"\n" -"# Buffer list:\n" -msgstr "" -"\n" -"# Arabellek listesi:\n" - -#, c-format -msgid "" -"\n" -"# %s History (newest to oldest):\n" -msgstr "" -"\n" -"# %s Geçmişi (yeniden eskiye):\n" - -msgid "Command Line" -msgstr "Komut Satırı" - -msgid "Search String" -msgstr "Arama Dizisi" - -msgid "Expression" -msgstr "İfade" - -msgid "Input Line" -msgstr "Girdi Satırı" - -msgid "Debug Line" -msgstr "Hata Ayıklama Satırı" - -msgid "" -"\n" -"# Bar lines, copied verbatim:\n" -msgstr "" -"\n" -"# Tam sureti kopyalanan | satırları:\n" - -#, c-format -msgid "%sviminfo: %s in line: " -msgstr "%sviminfo: satırdaki %s: " - -msgid "E136: viminfo: Too many errors, skipping rest of file" -msgstr "E136: viminfo: Çok fazla hata, dosyanın geri kalanı atlanıyor" - -msgid "" -"\n" -"# global variables:\n" -msgstr "" -"\n" -"# global değişkenler:\n" - -msgid "" -"\n" -"# Last Substitute String:\n" -"$" -msgstr "" -"\n" -"# Son Değiştirilen Dizi:\n" -"$" - -#, c-format -msgid "" -"\n" -"# Last %sSearch Pattern:\n" -"~" -msgstr "" -"\n" -"# Son %sArama Dizgileri:\n" -"~" - -msgid "Substitute " -msgstr "Şunu değiştir: " - -msgid "Illegal register name" -msgstr "İzin verilmeyen yazmaç adı" - -msgid "" -"\n" -"# Registers:\n" -msgstr "" -"\n" -"# Yazmaçlar:\n" - -#, c-format -msgid "E574: Unknown register type %d" -msgstr "E574: Bilinmeyen yazmaç türü %d" - -msgid "" -"\n" -"# History of marks within files (newest to oldest):\n" -msgstr "" -"\n" -"# Dosyalardaki imlerin geçmişi (yeniden eskiye):\n" - -msgid "" -"\n" -"# File marks:\n" -msgstr "" -"\n" -"# Dosya imleri:\n" - -msgid "" -"\n" -"# Jumplist (newest first):\n" -msgstr "" -"\n" -"# Atlama listesi (önce en yeniler):\n" - -msgid "Missing '>'" -msgstr "'>' eksik" - -msgid "Illegal starting char" -msgstr "İzin verilmeyen başlangıç karakteri" - -#, c-format -msgid "# This viminfo file was generated by Vim %s.\n" -msgstr "# Bu viminfo dosyası Vim %s tarafından oluşturulmuştur.\n" - -msgid "" -"# You may edit it if you're careful!\n" -"\n" -msgstr "" -"# Yapabileceğinizi düşünüyorsanız bu dosyayı düzenleyebilirsiniz!\n" -"\n" - -msgid "# Value of 'encoding' when this file was written\n" -msgstr "# Bu dosya yazıldığı sırada mevcut 'encoding'in değeri\n" - -#, c-format -msgid "Reading viminfo file \"%s\"%s%s%s%s" -msgstr "\"%s\" viminfo dosyası okunuyor...%s%s%s%s" - -msgid " info" -msgstr " bilgiler-" - -msgid " marks" -msgstr " imler-" - -msgid " oldfiles" -msgstr " düzenleme geçmişi" - -msgid " FAILED" -msgstr " BAŞARISIZ" - -#, c-format -msgid "E137: Viminfo file is not writable: %s" -msgstr "E137: Viminfo dosyası yazılabilir değil: %s" - -#, c-format -msgid "E929: Too many viminfo temp files, like %s!" -msgstr "E929: Çok fazla viminfo geçici dosyası, örneğin %s!" - -#, c-format -msgid "E138: Can't write viminfo file %s!" -msgstr "E138: viminfo dosyası %s yazılamıyor!" - -#, c-format -msgid "Writing viminfo file \"%s\"" -msgstr "viminfo dosyası \"%s\" yazılıyor" - -#, c-format -msgid "E886: Can't rename viminfo file to %s!" -msgstr "E886: viminfo dosyasının adı %s olarak değiştirilemiyor!" - -msgid "E195: Cannot open viminfo file for reading" -msgstr "E195: viminfo dosyası okuma için açılamıyor" - -msgid "Already only one window" -msgstr "Zaten tek pencere" - -#, c-format -msgid "E92: Buffer %ld not found" -msgstr "E92: Arabellek %ld bulunamadı" - -msgid "E441: There is no preview window" -msgstr "E441: Önizleme penceresi yok" - -msgid "E242: Can't split a window while closing another" -msgstr "E242: Bir başka pencere kapatılırken pencere bölünemez" - -msgid "E442: Can't split topleft and botright at the same time" -msgstr "E442: Üst sol ve alt sağ pencereler aynı anda bölünemez" - -msgid "E443: Cannot rotate when another window is split" -msgstr "E443: Başka bir pencere bölünmüşken döndürme yapılamaz" - -msgid "E444: Cannot close last window" -msgstr "E444: Son pencere kapatılamıyor" - -msgid "E814: Cannot close window, only autocmd window would remain" -msgstr "E814: Pencere kapatılamıyor, yalnızca otokomut penceresi açık kalır" - -msgid "E445: Other window contains changes" -msgstr "E445: Diğer pencerede değişiklikler var" - -msgid "E366: Not allowed to enter a popup window" -msgstr "E366: Bir açılır pencereye girişe izin verilmiyor" - -#, c-format -msgid "E370: Could not load library %s" -msgstr "E370: %s kitaplığı yüklenemedi" - -msgid "Sorry, this command is disabled: the Perl library could not be loaded." -msgstr "Üzgünüm, bu komut etkin değil: Perl kitaplığı yüklenemedi." - -msgid "E299: Perl evaluation forbidden in sandbox without the Safe module" -msgstr "" -"E299: Güvenli modül olmadan kum havuzu içinde Perl değerlendirmesine izin " -"verilmiyor" - -msgid "Edit with &multiple Vims" -msgstr "Birden &fazla Vim ile düzenle" - -msgid "Edit with single &Vim" -msgstr "Tek bir &Vim ile düzenle" - -msgid "Diff with Vim" -msgstr "Vim kullanarak karşılaştır" - -msgid "Edit with &Vim" -msgstr "&Vim ile düzenle" - -msgid "Edit with existing Vim" -msgstr "Mevcut Vim ile düzenle" - -msgid "Edit with existing Vim - " -msgstr "Mevcut Vim ile düzenle - " - -msgid "Edits the selected file(s) with Vim" -msgstr "Seçili dosyaları Vim ile düzenler" - -msgid "Error creating process: Check if gvim is in your path!" -msgstr "İşlem oluşturulurken hata: gvim'in yol üzerinde olduğundan emin olun!" - -msgid "gvimext.dll error" -msgstr "gvimext.dll hatası" - -msgid "Path length too long!" -msgstr "Yol çok uzun!" - -msgid "E10: \\ should be followed by /, ? or &" -msgstr "E10: \\ sonrasında /, ? veya & gelmeli" - -msgid "E11: Invalid in command-line window; <CR> executes, CTRL-C quits" -msgstr "E11: Komut satırı penceresinde geçersiz; <CR> çalıştırır, CTRL-C çıkar" - -msgid "E12: Command not allowed from exrc/vimrc in current dir or tag search" -msgstr "" -"E12: Geçerli dizin veya etiket aramasında exrc veya vimrc'den komutlara izin " -"verilmiyor" - -msgid "E13: File exists (add ! to override)" -msgstr "E13: Dosya mevcut (geçersiz kılmak için ! ekleyin)" - -#, c-format -msgid "E15: Invalid expression: \"%s\"" -msgstr "E15: Geçersiz ifade: \"%s\"" - -msgid "E16: Invalid range" -msgstr "E16: Geçersiz erim" - -#, c-format -msgid "E17: \"%s\" is a directory" -msgstr "E17: \"%s\" bir dizin" - -msgid "E18: Unexpected characters in :let" -msgstr "E18: :let içinde beklenmeyen karakter" - -msgid "E18: Unexpected characters in assignment" -msgstr "E18: Atama içerisinde beklenmedik karakterler" - -msgid "E19: Mark has invalid line number" -msgstr "E19: İm satır numarası geçersiz" - -msgid "E20: Mark not set" -msgstr "E20: İm ayarlanmamış" - -msgid "E21: Cannot make changes, 'modifiable' is off" -msgstr "E21: Değişiklik yapılamıyor, 'modifiable' kapalı" - -msgid "E22: Scripts nested too deep" -msgstr "E22: Betikler çok iç içe geçmiş" - -msgid "E23: No alternate file" -msgstr "E23: Başka bir dosya yok" - -msgid "E24: No such abbreviation" -msgstr "E24: Böyle bir kısaltma yok" - -#, c-format -msgid "E121: Undefined variable: %s" -msgstr "E121: Tanımlanmamış değişken: %s" - -#, c-format -msgid "E121: Undefined variable: %c:%s" -msgstr "E121: Tanımlanmamış değişken: %c:%s" - -msgid "E464: Ambiguous use of user-defined command" -msgstr "E464: Kullanıcı tanımlı komutun belirsiz kullanımı" - -msgid "E476: Invalid command" -msgstr "E476: Geçersiz komut" - -#, c-format -msgid "E476: Invalid command: %s" -msgstr "E476: Geçersiz komut: %s" - -msgid "E710: List value has more items than targets" -msgstr "E710: Liste değeri hedeften daha fazla ögeye sahip" - -msgid "E711: List value does not have enough items" -msgstr "E711: Liste değeri yeterli ögeye sahip değil" - -msgid "E719: Cannot slice a Dictionary" -msgstr "E719: Bir Sözlük dilimlenemiyor" - -msgid "" -"E856: \"assert_fails()\" second argument must be a string or a list with one " -"or two strings" -msgstr "" -"E856: \"assert_fails()\" ikinci argüman bir dizi veya bir veya iki dizili " -"bir liste olmalıdır" - -#, c-format -msgid "E908: using an invalid value as a String: %s" -msgstr "E908: Geçersiz bir değer bir Dizi yerine kullanılıyor: %s" - -msgid "E909: Cannot index a special variable" -msgstr "E909: Özel bir değişken dizinlenemiyor" - -#, c-format -msgid "E1100: Command not supported in Vim9 script (missing :var?): %s" -msgstr "E1100: Komut Vim9 betiğinde desteklenmiyor (:var? eksik): %s" - -#, c-format -msgid "E1001: Variable not found: %s" -msgstr "E1001: Değişken bulunamadı: %s" - -#, c-format -msgid "E1002: Syntax error at %s" -msgstr "E1002: %s konumunda sözdizim hatası" - -msgid "E1003: Missing return value" -msgstr "E1003: Dönüş değeri eksik" - -#, c-format -msgid "E1004: White space required before and after '%s' at \"%s\"" -msgstr "E1004: Şu konumda '%s' öncesinde ve sonrasında boşluk gerekiyor: \"%s\"" - -msgid "E1005: Too many argument types" -msgstr "E1005: Çok fazla argüman türü" - -#, c-format -msgid "E1006: %s is used as an argument" -msgstr "E1006: %s bir argüman olarak kullanılıyor" - -msgid "E1007: Mandatory argument after optional argument" -msgstr "E1007: İsteğe bağlı argüman sonrasında zorunlu argüman" - -msgid "E1008: Missing <type>" -msgstr "E1008: <tür> eksik" - -msgid "E1009: Missing > after type" -msgstr "E1009: Tür sonrasında > eksik" - -#, c-format -msgid "E1010: Type not recognized: %s" -msgstr "E1010: Tür tanımlanamadı: %s" - -#, c-format -msgid "E1011: Name too long: %s" -msgstr "E1011: Ad çok uzun: %s" - -#, c-format -msgid "E1012: Type mismatch; expected %s but got %s" -msgstr "E1012: Tür uyumsuzluğu, %s bekleniyordu, ancak %s alındı" - -#, c-format -msgid "E1013: Argument %d: type mismatch, expected %s but got %s" -msgstr "E1013: %d argümanı: Tür uyumsuzluğu, %s bekleniyordu, ancak %s alındı" - -#, c-format -msgid "E1014: Invalid key: %s" -msgstr "E1014: Geçersiz anahtar: %s" - -#, c-format -msgid "E1015: Name expected: %s" -msgstr "E1015: Ad bekleniyordu: %s" - -#, c-format -msgid "E1016: Cannot declare a %s variable: %s" -msgstr "E1016: Bir %s değişkeni tanımlanamıyor: %s" - -#, c-format -msgid "E1016: Cannot declare an environment variable: %s" -msgstr "E1016: Bir ortam değişkeni tanımlanamıyor: %s" - -#, c-format -msgid "E1017: Variable already declared: %s" -msgstr "E1017: Değişken halihazırda tanımlanmış: %s" - -#, c-format -msgid "E1018: Cannot assign to a constant: %s" -msgstr "E1018: Bir sabite atanamıyor: %s" - -msgid "E1019: Can only concatenate to string" -msgstr "E1019: Yalnızca bir diziye birleştirilebilir" +msgstr "bilgi için Yardım -> Sponsorluk/Kayıt" #, c-format -msgid "E1020: Cannot use an operator on a new variable: %s" -msgstr "E1020: Yeni bir değişken üzerinde bir işleç kullanılamaz: %s" - -msgid "E1021: Const requires a value" -msgstr "E1021: Sabit, bir değer gerektirir" - -msgid "E1022: Type or initialization required" -msgstr "E1022: Tür veya ilklendirme gerekiyor" +msgid "E15: Invalid control character present in input: %.*s" +msgstr "E15: Girdide geçersiz denetim karakteri var: %.*s" #, c-format -msgid "E1023: Using a Number as a Bool: %lld" -msgstr "E1023: Bir Sayı, bir Boole yerine kullanılıyor: %lld" - -msgid "E1024: Using a Number as a String" -msgstr "E1024: Bir Sayı, bir Dizi yerine kullanılıyor" - -msgid "E1025: Using } outside of a block scope" -msgstr "E1025: } bir blok kapsamı dışında kullanılıyor" - -msgid "E1026: Missing }" -msgstr "E1026: } eksik" - -msgid "E1027: Missing return statement" -msgstr "E1027: Dönüş ifadesi eksik" - -msgid "E1028: Compiling :def function failed" -msgstr "E1028: :def işlevi derleme başarısız" +msgid "E112: Option name missing: %.*s" +msgstr "E112: Seçenek adı eksik: %.*s" #, c-format -msgid "E1029: Expected %s but got %s" -msgstr "E1029: %s bekleniyordu ancak %s alındı" +msgid "E15: Unexpected EOC character: %.*s" +msgstr "E15: Beklenmedik EOC karakteri: %.*s" #, c-format -msgid "E1030: Using a String as a Number: \"%s\"" -msgstr "E1030: Bir Dizi, bir Sayı yerine kullanılıyor: \"%s\"" - -msgid "E1031: Cannot use void value" -msgstr "E1031: Boş değer kullanılamaz" - -msgid "E1032: Missing :catch or :finally" -msgstr "E1032: :catch veya :finally eksik" - -msgid "E1033: Catch unreachable after catch-all" -msgstr "E1033: catch-all sonrası catch ulaşılamıyor" +msgid "E15: Unidentified character: %.*s" +msgstr "E15: Tanımlanmamış karakter: %.*s" #, c-format -msgid "E1034: Cannot use reserved name %s" -msgstr "E1034: Ayrılmış ad %s kullanılamaz" - -msgid "E1035: % requires number arguments" -msgstr "E1035: %, sayı argümanları gerektirir" +msgid "E15: Operator is not associative: %.*s" +msgstr "E15: İşleç, çağrışımsal değil: %.*s" #, c-format -msgid "E1036: %c requires number or float arguments" -msgstr "E1036: %c, sayı veya kayan noktalı değer argümanları gerektirir" +msgid "E15: Missing operator: %.*s" +msgstr "E15: İşleç eksik: %.*s" #, c-format -msgid "E1037: Cannot use \"%s\" with %s" -msgstr "E1037: \"%s\", %s ile birlikte kullanılamaz" - -msgid "E1038: \"vim9script\" can only be used in a script" -msgstr "E1038: \"vim9script\" yalnızca bir betikte kullanılabilir" - -msgid "E1039: \"vim9script\" must be the first command in a script" -msgstr "E1039: \"vim9script\" bir betikteki ilk komut olmalıdır" - -msgid "E1040: Cannot use :scriptversion after :vim9script" -msgstr "E1040: :vim9script sonrası :scriptversion kullanılamaz" +msgid "E15: Expected lambda arguments list or arrow: %.*s" +msgstr "E15: Lambda argümanlar listesi veya ok bekleniyordu: %.*s" #, c-format -msgid "E1041: Redefining script item %s" -msgstr "E1041: Betik ögesi %s yeniden tanımlanıyor" - -msgid "E1042: Export can only be used in vim9script" -msgstr "E1042: Dışa aktarım yalnızca vim9script içinde kullanılabilir" - -msgid "E1043: Invalid command after :export" -msgstr "E1043: :export sonrası geçersiz komut" - -msgid "E1044: Export with invalid argument" -msgstr "E1044: Geçersiz argümanla dışa aktarım" - -msgid "E1045: Missing \"as\" after *" -msgstr "E1045: * sonrası \"as\" eksik" - -msgid "E1046: Missing comma in import" -msgstr "E1046: İçe aktarımda virgül eksik" - -msgid "E1047: Syntax error in import" -msgstr "E1047: İçe aktarımda sözdizim hatası" - -#, c-format -msgid "E1048: Item not found in script: %s" -msgstr "E1048: Betikte öge bulunamadı: %s" - -#, c-format -msgid "E1049: Item not exported in script: %s" -msgstr "E1049: Betikte öge dışa aktarılmadı: %s" - -#, c-format -msgid "E1050: Colon required before a range: %s" -msgstr "E1050: Bir erim öncesi iki nokta gerekiyor: %s" - -msgid "E1051: Wrong argument type for +" -msgstr "E1051: + için hatalı argüman türü" - -#, c-format -msgid "E1052: Cannot declare an option: %s" -msgstr "E1052: Bir seçenek tanımlanamıyor: %s" - -#, c-format -msgid "E1053: Could not import \"%s\"" -msgstr "E1053: \"%s\" içe aktarılamadı" - -#, c-format -msgid "E1054: Variable already declared in the script: %s" -msgstr "E1054: Betikte değişken halihazırda tanımlanmış: %s" - -msgid "E1055: Missing name after ..." -msgstr "E1055: ... sonraki ad eksik" - -#, c-format -msgid "E1056: Expected a type: %s" -msgstr "E1056: Bir tür bekleniyordu: %s" - -msgid "E1057: Missing :enddef" -msgstr "E1057: :enddef eksik" - -msgid "E1058: Function nesting too deep" -msgstr "E1058: İşlev çok iç içe geçmiş" +msgid "E15: Expected value part of assignment lvalue: %.*s" +msgstr "E15: lvalue ataması değer kısmı bekleniyordu: %.*s" #, c-format -msgid "E1059: No white space allowed before colon: %s" -msgstr "E1059: İki nokta öncesinde boşluğa izin verilmiyor: %s" +msgid "E15: Expected assignment operator or subscript: %.*s" +msgstr "E15: Atama işleci veya alt simgesi bekleniyordu: %.*s" -#, c-format -msgid "E1060: Expected dot after name: %s" -msgstr "E1060: Ad sonrası nokta bekleniyordu: %s" +msgid "E15: Unexpected " +msgstr "E15: Beklenmedik " #, c-format -msgid "E1061: Cannot find function %s" -msgstr "E1061: %s işlevi bulunamıyor" - -msgid "E1062: Cannot index a Number" -msgstr "E1062: Bir Sayı dizinlenemiyor" +msgid "E15: Unexpected multiplication-like operator: %.*s" +msgstr "E15: Beklenmedik çarpma benzeri işleç: %.*s" -msgid "E1063: Type mismatch for v: variable" -msgstr "E1063: v: değişkeni için tür uyumsuzluğu" +msgid "E15: Environment variable name missing" +msgstr "E15: Çevre değişkeni adı eksik" #, c-format -msgid "E1066: Cannot declare a register: %s" -msgstr "E1066: Bir yazmaç tanımlanamıyor: %s" +msgid "E15: Expected value, got comparison operator: %.*s" +msgstr "E15: Değer bekleniyordu, karşılaştırma işleci alındı: %.*s" #, c-format -msgid "E1067: Separator mismatch: %s" -msgstr "E1067: Ayırıcı uyumsuzluğu: %s" +msgid "E15: Expected value, got comma: %.*s" +msgstr "E15: Değer bekleniyordu, virgül alındı: %.*s" #, c-format -msgid "E1068: No white space allowed before '%s': %s" -msgstr "E1068: '%s' önce boşluğa izin verilmiyor: %s" +msgid "E15: Comma outside of call, lambda or literal: %.*s" +msgstr "E15: Çağrı, lambda veya düz veri dışı virgül: %.*s" #, c-format -msgid "E1069: White space required after '%s': %s" -msgstr "E1069: '%s' sonrası boşluk gerekiyor: %s" - -msgid "E1070: Missing \"from\"" -msgstr "E1070: \"from\" eksik" - -msgid "E1071: Invalid string after \"from\"" -msgstr "E1071: \"from\" sonrası geçersiz dizi" +msgid "E15: Colon outside of dictionary or ternary operator: %.*s" +msgstr "E15: Sözlük veya üç terimli işlec dışı iki nokta: %.*s" #, c-format -msgid "E1072: Cannot compare %s with %s" -msgstr "E1072: %s, %s ile karşılaştırılamıyor" +msgid "E15: Expected value, got closing bracket: %.*s" +msgstr "E15: Değer bekleniyordu, kapatma ayracı alındı: %.*s" #, c-format -msgid "E1073: Name already defined: %s" -msgstr "E1073: Ad halihazırda tanımlanmış: %s" - -msgid "E1074: No white space allowed after dot" -msgstr "E1074: Nokta sonrası boşluğa izin verilmiyor" +msgid "E475: Unable to assign to empty list: %.*s" +msgstr "E475: Boş listeye atanamadı: %.*s" #, c-format -msgid "E1075: Namespace not supported: %s" -msgstr "E1075: Ad alanı desteklenmiyor: %s" - -msgid "E1076: This Vim is not compiled with float support" -msgstr "E1076: Bu Vim kayan noktalı değer desteği ile derlenmemiş" +msgid "E15: Unexpected closing figure brace: %.*s" +msgstr "E15: Beklenmedik kapatma kıvrımlı ayracı: %.*s" #, c-format -msgid "E1077: Missing argument type for %s" -msgstr "E1077: %s için argüman türü eksik" +msgid "E475: Nested lists not allowed when assigning: %.*s" +msgstr "E475: Atama sırasında iç içe geçmiş listelere izin verilmez: %.*s" #, c-format -msgid "E1081: Cannot unlet %s" -msgstr "E1081: %s sabitten değişkene çevrilemiyor" +msgid "E15: Expected value, got closing figure brace: %.*s" +msgstr "E15: Değer bekleniyordu, kapatma kıvrımlı ayracı alındı: %.*s" #, c-format -msgid "E1082: Cannot use a namespaced variable: %s" -msgstr "E1082: Ad alanına alınmış bir değişken kullanılamaz: %s" - -msgid "E1083: Missing backtick" -msgstr "E1083: Ters eğik kesme imi eksik" +msgid "E15: Don't know what figure brace means: %.*s" +msgstr "E15: Kıvrımlı ayracın ne anlama geldiği bilinmiyor: %.*s" #, c-format -msgid "E1084: Cannot delete Vim9 script function %s" -msgstr "E1084: Vim9 betik işlevi %s silinemiyor" +msgid "E15: Unexpected arrow: %.*s" +msgstr "E15: Beklenmedik ok: %.*s" #, c-format -msgid "E1085: Not a callable type: %s" -msgstr "E1085: Çağrılabilir bir tür değil: %s" - -msgid "E1086: Cannot use :function inside :def" -msgstr "E1086: :def içinde :function kullanılamaz" - -msgid "E1087: Cannot use an index when declaring a variable" -msgstr "E1087: Bir değişken tanımlarken dizinleme kullanılamaz" +msgid "E15: Arrow outside of lambda: %.*s" +msgstr "E15: Ok, lambda dışında: %.*s" #, c-format -msgid "E1089: Unknown variable: %s" -msgstr "E1089: Bilinmeyen değişken: %s" +msgid "E15: Unexpected dot: %.*s" +msgstr "E15: Beklenmedik nokta: %.*s" #, c-format -msgid "E1090: Cannot assign to argument %s" -msgstr "E1090: %s argümanına atanamıyor" +msgid "E15: Cannot concatenate in assignments: %.*s" +msgstr "E15: Atamalarda uç uca eklenemiyor: %.*s" #, c-format -msgid "E1091: Function is not compiled: %s" -msgstr "E1091: İşlev derlenmemiş: %s" - -msgid "E1092: Cannot use a list for a declaration" -msgstr "E1092: Tanımlama için bir liste kullanılamaz" +msgid "E15: Expected value, got parenthesis: %.*s" +msgstr "E15: Değer bekleniyordu, ayraç alındı: %.*s" #, c-format -msgid "E1093: Expected %d items but got %d" -msgstr "E1093: %d öge bekleniyordu, ancak %d alındı" - -msgid "E1094: Import can only be used in a script" -msgstr "E1094: İçe aktarım yalnızca bir betikte kullanılabilir" - -msgid "E1095: Unreachable code after :return" -msgstr "E1095: :return sonrası ulaşılamayan kod" - -msgid "E1096: Returning a value in a function without a return type" -msgstr "E1096: Dönüş türü olmayan bir işlevde bir değer döndürülüyor" - -msgid "E1097: Line incomplete" -msgstr "E1097: Satır tamamlanmamış" +msgid "E15: Unexpected closing parenthesis: %.*s" +msgstr "E15: Beklenmedik kapatma ayracı: %.*s" #, c-format -msgid "E1099: Unknown error while executing %s" -msgstr "E1099: %s çalıştırılırken bilinmeyen hata" +msgid "E15: Expected value, got question mark: %.*s" +msgstr "E15: Değer bekleniyordu, soru imi alındı: %.*s" #, c-format -msgid "E1101: Cannot declare a script variable in a function: %s" -msgstr "E1101: Bir işlevde bir betik değişkeni tanımlanamıyor: %s" +msgid "E114: Missing double quote: %.*s" +msgstr "E114: Çift tırnak eksik: %.*s" #, c-format -msgid "E1102: Lambda function not found: %s" -msgstr "E1102: Lambda işlevi bulunamadı: %s" - -msgid "E1103: Dictionary not set" -msgstr "E1103: Sözlük ayarlanmamış" - -msgid "E1104: Missing >" -msgstr "E1104: > eksik" +msgid "E115: Missing single quote: %.*s" +msgstr "E115: Tek tırnak eksik: %.*s" #, c-format -msgid "E1105: Cannot convert %s to string" -msgstr "E1105: %s bir diziye dönüştürülemiyor" - -msgid "E1106: One argument too many" -msgstr "E1106: Bir argüman fazladan" +msgid "E475: Expected closing bracket to end list assignment lvalue: %.*s" +msgstr "E475: lvalue liste sonu atamasına kapatma ayracı bekleniyordu: %.*s" #, c-format -msgid "E1106: %d arguments too many" -msgstr "E1106: %d argüman fazladan" - -msgid "E1107: String, List, Dict or Blob required" -msgstr "E1107: Dizi, Liste, Sözlük veya İkili Nesne gerekiyor" +msgid "E15: Misplaced assignment: %.*s" +msgstr "E15: Yanlış konumlandırılmış atama: %.*s" #, c-format -msgid "E1108: Item not found: %s" -msgstr "E1108: Öge bulunamadı: %s" +msgid "E15: Unexpected assignment: %.*s" +msgstr "E15: Beklenmedik atama: %.*s" #, c-format -msgid "E1109: List item %d is not a List" -msgstr "E1109: Liste ögesi %d bir Liste değil" +msgid "E15: Expected value, got EOC: %.*s" +msgstr "E15: Değer bekleniyordu, EOC alındı: %.*s" #, c-format -msgid "E1110: List item %d does not contain 3 numbers" -msgstr "E1110: Liste ögesi %d 3 sayı içermiyor" +msgid "E116: Missing closing parenthesis for function call: %.*s" +msgstr "E116: İşlev çağrısı için geçersiz kapatma ayracı: %.*s" #, c-format -msgid "E1111: List item %d range invalid" -msgstr "E1111: Liste ögesi %d erimi geçersiz" +msgid "E110: Missing closing parenthesis for nested expression: %.*s" +msgstr "E110: İç içe geçmiş ifade için kapatma ayracı eksik: %.*s" #, c-format -msgid "E1112: List item %d cell width invalid" -msgstr "E1112: Liste ögesi %d hücre genişliği geçersiz" +msgid "E697: Missing end of List ']': %.*s" +msgstr "E697: Liste sonunda ']' eksik: %.*s" #, c-format -msgid "E1113: Overlapping ranges for 0x%lx" -msgstr "E1113: 0x%lx için üst üste binen erimler" - -msgid "E1114: Only values of 0x100 and higher supported" -msgstr "E1114: Yalnızca 0x100 ve daha yüksek değerler destekleniyor" - -msgid "E1115: \"assert_fails()\" fourth argument must be a number" -msgstr "E1115: \"assert_fails()\" dördüncü argüman bir sayı olmalıdır" - -msgid "E1116: \"assert_fails()\" fifth argument must be a string" -msgstr "E1116: \"assert_fails()\" beşinci argüman bir dizi olmalıdır" - -msgid "E1117: Cannot use ! with nested :def" -msgstr "E1117: !, iç içe geçmiş :def ile kullanılamaz" - -msgid "E1118: Cannot change list" -msgstr "E1118: Liste değiştirilemez" - -msgid "E1119: Cannot change list item" -msgstr "E1119: Liste ögesi değiştirilemez" - -msgid "E1120: Cannot change dict" -msgstr "E1120: Sözlük değiştirilemez" - -msgid "E1121: Cannot change dict item" -msgstr "E1121: Sözlük ögesi değiştirilemez" +msgid "E723: Missing end of Dictionary '}': %.*s" +msgstr "E723: Sözlük sonu '}' eksik: %.*s" #, c-format -msgid "E1122: Variable is locked: %s" -msgstr "E1122: Değişken kilitli: %s" +msgid "E15: Missing closing figure brace: %.*s" +msgstr "E15: Kapatma kıvrımlı ayracı eksik: %.*s" #, c-format -msgid "E1123: Missing comma before argument: %s" -msgstr "E1123: Değişken öncesi virgül eksik: %s" +msgid "E15: Missing closing figure brace for lambda: %.*s" +msgstr "E15: Lambda için kapatma kıvrımlı ayracı eksik: %.*s" #, c-format -msgid "E1124: \"%s\" cannot be used in legacy Vim script" -msgstr "E1124: \"%s\" yalnızca eski Vim betiklerinde kullanılabilir" - -msgid "E1125: Final requires a value" -msgstr "E1125: Final, bir değer gerektirir" - -msgid "E1126: Cannot use :let in Vim9 script" -msgstr "E1126: :let, Vim9 betiğinde kullanılamaz" - -msgid "E1127: Missing name after dot" -msgstr "E1127: Nokta sonrası ad eksik" - -msgid "E1128: } without {" -msgstr "E1128: { olmadan }" - -msgid "E1129: Throw with empty string" -msgstr "E1129: Boş dizi ile \"Throw\"" - -msgid "E1130: Cannot add to null list" -msgstr "E1130: Null listesine bir öge eklenemez" - -msgid "E1131: Cannot add to null blob" -msgstr "E1131: Null ikili geniş nesnesine ekleme yapılamaz" - -msgid "E1132: Missing function argument" -msgstr "E1132: İşlev argümanı eksik" - -msgid "E1133: Cannot extend a null dict" -msgstr "E1133: Bir null sözlük genişletilemez" - -msgid "E1134: Cannot extend a null list" -msgstr "E1134: Bir null listesi genişletilemez" +msgid "E109: Missing ':' after '?': %.*s" +msgstr "E109: '?' sonrası ':' eksik: %.*s" -#, c-format -msgid "E1135: Using a String as a Bool: \"%s\"" -msgstr "E1135: Bir Dizi, bir Boole yerine kullanılıyor: \"%s\"" - -msgid "E1135: <Cmd> mapping must end with <CR>" -msgstr "E1135: <Cmd> eşlemlemesi <CR> ile bitmelidir" - -msgid "E1136: <Cmd> mapping must end with <CR> before second <Cmd>" -msgstr "E1136: <Cmd> eşlemlemesi ikinci <Cmd>'den önce <CR> ile bitmelidir" - -#, c-format -msgid "E1137: <Cmd> mapping must not include %s key" -msgstr "E1137: <Cmd> eşlemlemesi %s anahtarını içermemelidir" - -msgid "E1138: Using a Bool as a Number" -msgstr "E1138: Bir Boole, Sayı yerine kullanılıyor" - -msgid "E1139: Missing matching bracket after dict key" -msgstr "E1139: Sözlük anahtarı sonrası eşleşen ayraç eksik" - -msgid "E1140: :for argument must be a sequence of lists" -msgstr "E1140: :for argümanı listelerin bir sıralaması olmalıdır" - -msgid "E1141: Indexable type required" -msgstr "E1141: İndekslenebilir tür gerekiyor" - -msgid "E1142: Non-empty string required" -msgstr "E1142: Boş olmayan dizi gerekiyor" - -#, c-format -msgid "E1143: Empty expression: \"%s\"" -msgstr "E1143: Boş ifade: \"%s\"" - -#, c-format -msgid "E1144: Command \"%s\" is not followed by white space: %s" -msgstr "E1144: \"%s\" komutu sonrasında boşluk gelmiyor: %s" - -#, c-format -msgid "E1145: Missing heredoc end marker: %s" -msgstr "E1145: Son imleyicisi eksik: %s" - -#, c-format -msgid "E1146: Command not recognized: %s" -msgstr "E1146: Komut tanınamadı: %s" - -msgid "E1147: List not set" -msgstr "E1147: Liste ayarlanmamış" - -#, c-format -msgid "E1148: Cannot index a %s" -msgstr "E1148: Bir %s dizinlenemiyor" - -#, c-format -msgid "E1149: Script variable is invalid after reload in function %s" -msgstr "E1149: %s işlevindeki yeniden yüklemeden sonra betik değişkeni geçersiz" - -msgid "E1150: Script variable type changed" -msgstr "E1150: Betik değişkeni türü değiştirildi" - -msgid "E1151: Mismatched endfunction" -msgstr "E1151: Eşleşmeyen endfunction" - -msgid "E1152: Mismatched enddef" -msgstr "E1152: Eşleşmeyen enddef" - -msgid "E1153: Invalid operation for bool" -msgstr "E1153: Boole için geçersiz işlem" - -msgid "E1154: Divide by zero" -msgstr "E1154: Sıfır ile bölüm" - -msgid "E1155: Cannot define autocommands for ALL events" -msgstr "E1155: Otokomutlar TÜM olaylar için tanımlanamıyor" - -msgid "E1156: Cannot change the argument list recursively" -msgstr "E1156: Değişken listesi özyineli olarak değiştirilemiyor" - -msgid "E1157: Missing return type" -msgstr "E1157: Dönüş türü eksik" - -msgid "E1158: Cannot use flatten() in Vim9 script" -msgstr "E1158: flatten(), Vim9 betiğinde kullanılamaz" - -msgid "E1159: Cannot split a window when closing the buffer" -msgstr "E1159: Arabellek kapatılırken bir pencere bölünemez" - -msgid "E1160: Cannot use a default for variable arguments" -msgstr "E1160: Değişken argümanları için bir öntanımlı kullanılamaz" - -#, c-format -msgid "E1161: Cannot json encode a %s" -msgstr "E1161: Bir %s JSON olarak kodlanamıyor" - -#, c-format -msgid "E1162: Register name must be one character: %s" -msgstr "E1162: Yazmaç adı tek bir karakter olmalıdır: %s" - -#, c-format -msgid "E1163: Variable %d: type mismatch, expected %s but got %s" -msgstr "E1163: %d değişkeni: Tür uyumsuzluğu, %s bekleniyordu, ancak %s alındı" - -msgid "E1164: vim9cmd must be followed by a command" -msgstr "E1164: vim9cmd sonrasında bir komut gelmelidir" - -#, c-format -msgid "E1165: Cannot use a range with an assignment: %s" -msgstr "E1165: Bir atama ile bir erim kullanılamıyor: %s" - -msgid "E1166: Cannot use a range with a dictionary" -msgstr "E1166: Bir sözlük ile bir erim kullanılamıyor" - -#, c-format -msgid "E1167: Argument name shadows existing variable: %s" -msgstr "E1167: Argüman adı var olan değişkeni gölgeliyor: %s" - -#, c-format -msgid "E1168: Argument already declared in the script: %s" -msgstr "E1168: Betikte argüman halihazırda tanımlanmış: %s" - -msgid "E1169: 'import * as {name}' not supported here" -msgstr "E1169: 'import * as {name} burada desteklenmiyor" - -msgid "E1170: Cannot use #{ to start a comment" -msgstr "E1170: Bir yorum başlatmak için #{ kullanılamaz" - -msgid "E1171: Missing } after inline function" -msgstr "E1171: Satıriçi işlevden sonra } eksik" - -msgid "E1172: Cannot use default values in a lambda" -msgstr "E1172: Bir lambda içerisinde öntanımlı değerler kullanılamıyor" - -#, c-format -msgid "E1173: Text found after enddef: %s" -msgstr "E1173: :enddef sonrası metin bulundu: %s" - -#, c-format -msgid "E1174: String required for argument %d" -msgstr "E1174: %d argümanı için dizi gerekiyor" - -#, c-format -msgid "E1175: Non-empty string required for argument %d" -msgstr "E1175: %d argümanı için boş olmayan dizi gerekiyor" - -msgid "E1176: Misplaced command modifier" -msgstr "E1176: Yanlış yere konulmuş komut değiştiricisi" - -#, c-format -msgid "E1177: For loop on %s not supported" -msgstr "E1177: %s üzerinde for döngüsü desteklenmiyor" - -msgid "E1178: Cannot lock or unlock a local variable" -msgstr "E1178: Bir yerel değişken kilitlenemiyor/kilidi açılamıyor" - -#, c-format -msgid "" -"E1179: Failed to extract PWD from %s, check your shell's config related to " -"OSC 7" -msgstr "" -"E1179: %s içinden PWD çıkarılamadı, kabuğunuzun OSC 7 ile ilgili " -"yapılandırmasını denetleyin" - -#, c-format -msgid "E1180: Variable arguments type must be a list: %s" -msgstr "E1180: Değişken argümanları türü bir liste olmalıdır: %s" - -msgid "E1181: Cannot use an underscore here" -msgstr "E1181: Alt çizgi burada kullanılamaz" - -msgid "E1182: Blob required" -msgstr "E1182: İkili geniş nesne gerekiyor" - -#, c-format -msgid "E1183: Cannot use a range with an assignment operator: %s" -msgstr "E1183: Bir atama işleci ile bir erim kullanılamıyor: %s" - -msgid "E1184: Blob not set" -msgstr "E1184: İkili geniş nesne ayarlanmamış" - -msgid "E1185: Cannot nest :redir" -msgstr "E1185: :redir içe geçirilemiyor" - -msgid "E1185: Missing :redir END" -msgstr "E1185: :redir END eksik" - -#, c-format -msgid "E1186: Expression does not result in a value: %s" -msgstr "E1186: İfade bir değer sonucu vermiyor: %s" - -msgid "E1187: Failed to source defaults.vim" -msgstr "E1187: defaults.vim kaynaklanamadı" - -msgid "E1188: Cannot open a terminal from the command line window" -msgstr "E1188: Komut satırı penceresinden bir uçbirim açılamıyor" - -#, c-format -msgid "E1189: Cannot use :legacy with this command: %s" -msgstr "E1189: :legacy, bu komut ile kullanılamıyor: %s" - -msgid "E1190: One argument too few" -msgstr "E1190: Bir argüman daha gerekiyor" - -#, c-format -msgid "E1190: %d arguments too few" -msgstr "E1190: %d argüman daha gerekiyor" - -#, c-format -msgid "E1191: Call to function that failed to compile: %s" -msgstr "E1191: Derlenemeyen işlev çağrısı: %s" - -msgid "E1192: Empty function name" -msgstr "E1192: Boş işlev adı" - -msgid "E1193: cryptmethod xchacha20 not built into this Vim" -msgstr "E1193: cryptmethod xchacha20 bu Vim ile kullanılamıyor" - -msgid "E1194: Cannot encrypt header, not enough space" -msgstr "E1194: Üstbilgi şifrelenemiyor, yetersiz alan" - -msgid "E1195: Cannot encrypt buffer, not enough space" -msgstr "E1195: Arabellek şifrelenemiyor, yetersiz alan" - -msgid "E1196: Cannot decrypt header, not enough space" -msgstr "E1196: Üstbilgi şifresi çözülemiyor, yetersiz alan" - -msgid "E1197: Cannot allocate_buffer for encryption" -msgstr "E1197: Şifreleme için allocate_buffer yapılamıyor" - -msgid "E1198: Decryption failed: Header incomplete!" -msgstr "E1198: Şifre çözümü başarısız: Üstbilgi tam değil!" - -msgid "E1199: Cannot decrypt buffer, not enough space" -msgstr "E1199: Arabellek şifresi çözülemiyor, yetersiz alan" - -msgid "E1200: Decryption failed!" -msgstr "E1200: Şifre çözümü başarısız!" - -msgid "E1201: Decryption failed: pre-mature end of file!" -msgstr "E1201: Şifre çözümü başarısız: Beklenmedik dosya sonu!" - -#, c-format -msgid "E1202: No white space allowed after '%s': %s" -msgstr "E1202: '%s' sonrası boşluğa izin verilmiyor: %s" - -#, c-format -msgid "E1203: Dot can only be used on a dictionary: %s" -msgstr "E1203: Nokta yalnızca bir sözlükte kullanılabilir: %s" - -#, c-format -msgid "E1204: No Number allowed after .: '\\%%%c'" -msgstr "E1204: . sonrası Sayıya izin verilmiyor: '\\%%%c'" - -msgid "E1205: No white space allowed between option and" -msgstr "E1205: and seçeneği arasında boşluğa izin verilmiyor" - -#, c-format -msgid "E1206: Dictionary required for argument %d" -msgstr "E1206: %d argümanı için sözlük gerekiyor" - -#, c-format -msgid "E1207: Expression without an effect: %s" -msgstr "E1207: Bir efekt olmadan ifade: %s" - -msgid "E1208: -complete used without -nargs" -msgstr "E1208: -complete, -nargs olmadan kullanıldı" - -#, c-format -msgid "E1209: Invalid value for a line number: \"%s\"" -msgstr "E1209: Satır numarası için geçersiz değer: \"%s\"" - -#, c-format -msgid "E1210: Number required for argument %d" -msgstr "E1210: %d argümanı için sayı gerekiyor" - -msgid "--No lines in buffer--" -msgstr "--Arabellek içinde satır yok--" - -msgid "E470: Command aborted" -msgstr "E470: Komut durduruldu" - -msgid "E471: Argument required" -msgstr "E471: Değişken gerekiyor" - -msgid "E171: Missing :endif" -msgstr "E171: :endif eksik" - -msgid "E603: :catch without :try" -msgstr "E603: :try olmadan :catch" - -msgid "E606: :finally without :try" -msgstr "E606: :try olmadan :finally" - -msgid "E607: multiple :finally" -msgstr "E607: Birden fazla :finally" - -msgid "E600: Missing :endtry" -msgstr "E600: :endtry eksik" - -msgid "E602: :endtry without :try" -msgstr "E602: :try olmadan :endtry" - -msgid "E170: Missing :endwhile" -msgstr "E170: :endwhile eksik" - -msgid "E170: Missing :endfor" -msgstr "E170: :endfor eksik" - -msgid "E588: :endwhile without :while" -msgstr "E588: :while olmadan :endwhile" - -msgid "E588: :endfor without :for" -msgstr "E588: :for olmadan :endfor" - -msgid "E472: Command failed" -msgstr "E472: Komut başarısız oldu" - -#, c-format -msgid "E234: Unknown fontset: %s" -msgstr "E234: Bilinmeyen yazıtipi seti: %s" - -#, c-format -msgid "E235: Unknown font: %s" -msgstr "E235: Bilinmeyen yazıtipi: %s" - -#, c-format -msgid "E236: Font \"%s\" is not fixed-width" -msgstr "E236: \"%s\" yazıtipi sabit genişlikli değil" - -msgid "E473: Internal error" -msgstr "E473: İç hata" - -#, c-format -msgid "E685: Internal error: %s" -msgstr "E685: İç hata: %s" - -msgid "Interrupted" -msgstr "Yarıda kesildi" - -msgid "E474: Invalid argument" -msgstr "E474: Geçersiz argüman" - -#, c-format -msgid "E475: Invalid argument: %s" -msgstr "E475: Geçersiz argüman: %s" - -#, c-format -msgid "E983: Duplicate argument: %s" -msgstr "E983: Yinelenen argüman: %s" - -#, c-format -msgid "E475: Invalid value for argument %s" -msgstr "E475: %s argümanı için geçersiz değer" - -#, c-format -msgid "E475: Invalid value for argument %s: %s" -msgstr "E475: %s argümanı için geçersiz değer: %s" - -msgid "E756: Spell checking is not possible" -msgstr "E756: Yazım denetimi olanaklı değil" - -#, c-format -msgid "E364: Library call failed for \"%s()\"" -msgstr "E364: \"%s()\" için kitaplık çağrısı başarısız oldu" - -msgid "E667: Fsync failed" -msgstr "E667: Fsync başarısız oldu" - -#, c-format -msgid "E448: Could not load library function %s" -msgstr "E448: %s kitaplık işlevi yüklenemedi" - -msgid "E477: No ! allowed" -msgstr "E477: ! imine izin verilmiyor" - -msgid "E25: GUI cannot be used: Not enabled at compile time" -msgstr "E25: Grafik arabirim kullanılamaz: Derlenirken etkinleştirilmemiş" - -msgid "E26: Hebrew cannot be used: Not enabled at compile time\n" -msgstr "E26: İbranca kullanılamaz: Derlenirken etkinleştirilmemiş\n" - -msgid "E27: Farsi support has been removed\n" -msgstr "E27: Farsça desteği kaldırıldı\n" - -msgid "E800: Arabic cannot be used: Not enabled at compile time\n" -msgstr "E800: Arapça kullanılamaz: Derlenirken etkinleştirilmemiş\n" - -#, c-format -msgid "E28: No such highlight group name: %s" -msgstr "E28: Böyle bir vurgulama grup adı yok: %s" - -msgid "E29: No inserted text yet" -msgstr "E29: Henüz bir metin eklenmedi" - -msgid "E30: No previous command line" -msgstr "E30: Öncesinde komut satırı yok" - -msgid "E31: No such mapping" -msgstr "E31: Böyle bir eşlem yok" - -msgid "E479: No match" -msgstr "E479: Eşleşme yok" - -#, c-format -msgid "E480: No match: %s" -msgstr "E480: Eşleşme yok: %s" - -msgid "E32: No file name" -msgstr "E32: Dosya adı yok" - -msgid "E33: No previous substitute regular expression" -msgstr "E33: Öncesinde yerine geçen bir düzenli ifade yok" - -msgid "E34: No previous command" -msgstr "E34: Öncesinde komut yok" - -msgid "E35: No previous regular expression" -msgstr "E35: Öncesinde düzenli ifade yok" - -msgid "E481: No range allowed" -msgstr "E481: Erime izin verilmiyor" - -msgid "E36: Not enough room" -msgstr "E36: Yeterli alan yok" - -#, c-format -msgid "E247: no registered server named \"%s\"" -msgstr "E247: \"%s\" adlı kayıtlı bir sunucu yok" - -#, c-format -msgid "E482: Can't create file %s" -msgstr "E482: %s dosyası oluşturulamıyor" - -msgid "E483: Can't get temp file name" -msgstr "E483: Geçici dosya adı alınamıyor" - -#, c-format -msgid "E484: Can't open file %s" -msgstr "E484: %s dosyası açılamıyor" - -#, c-format -msgid "E485: Can't read file %s" -msgstr "E485: %s dosyası okunamıyor" - -msgid "E38: Null argument" -msgstr "E38: Anlamsız argüman" - -msgid "E39: Number expected" -msgstr "E39: Sayı bekleniyordu" - -#, c-format -msgid "E40: Can't open errorfile %s" -msgstr "E40: Hata dosyası %s açılamıyor" - -msgid "E233: cannot open display" -msgstr "E233: Görüntü açılamıyor" - -msgid "E41: Out of memory!" -msgstr "E41: Bellek yetersiz!" - -msgid "Pattern not found" -msgstr "Dizgi bulunamadı" - -#, c-format -msgid "E486: Pattern not found: %s" -msgstr "E486: Dizgi bulunamadı: %s" - -msgid "E487: Argument must be positive" -msgstr "E487: Değişken pozitif olmalı" - -msgid "E459: Cannot go back to previous directory" -msgstr "E459: Bir önceki dizine gidilemiyor" - -msgid "E42: No Errors" -msgstr "E42: Hata yok" - -msgid "E776: No location list" -msgstr "E776: Konum listesi yok" - -msgid "E43: Damaged match string" -msgstr "E43: Hasarlı eşleşme dizisi" - -msgid "E44: Corrupted regexp program" -msgstr "E44: Bozulmuş regexp programı" - -msgid "E45: 'readonly' option is set (add ! to override)" -msgstr "E45: 'readonly' seçeneği ayarlanmış (geçersiz kılmak için ! ekleyin)" - -#, c-format -msgid "E734: Wrong variable type for %s=" -msgstr "E734: %s= için yanlış değişken türü" - -#, c-format -msgid "E461: Illegal variable name: %s" -msgstr "E461: İzin verilmeyen değişken adı: %s" - -msgid "E995: Cannot modify existing variable" -msgstr "E995: Mevcut değişken değiştirilemiyor" - -#, c-format -msgid "E46: Cannot change read-only variable \"%s\"" -msgstr "E46: Salt okunur değişken \"%s\" değiştirilemiyor" - -#, c-format -msgid "E794: Cannot set variable in the sandbox: \"%s\"" -msgstr "E794: Değişken kum havuzunda ayarlanamıyor: \"%s\"" - -msgid "E928: String required" -msgstr "E928: Dizi gerekiyor" - -msgid "E889: Number required" -msgstr "E889: Sayı gerekiyor" - -msgid "E713: Cannot use empty key for Dictionary" -msgstr "E713: Sözlük için boş anahtar kullanılamaz" - -msgid "E715: Dictionary required" -msgstr "E715: Sözlük gerekiyor" - -#, c-format -msgid "E684: list index out of range: %ld" -msgstr "E684: Liste sırası erimin dışında: %ld" - -#, c-format -msgid "E979: Blob index out of range: %ld" -msgstr "E979: İkili geniş nesne sırası erimin dışında: %ld" - -msgid "E978: Invalid operation for Blob" -msgstr "E978: İkili geniş nesne için geçersiz işlem" - -#, c-format -msgid "E118: Too many arguments for function: %s" -msgstr "E118: İşlev için çok fazla argüman: %s" - -#, c-format -msgid "E119: Not enough arguments for function: %s" -msgstr "E119: Şu işlev için yetersiz sayıda argüman: %s" - -#, c-format -msgid "E933: Function was deleted: %s" -msgstr "E933: İşlev silinmiş: %s" - -#, c-format -msgid "E716: Key not present in Dictionary: \"%s\"" -msgstr "E716: Anahtar sözlükte mevcut değil: \"%s\"" - -msgid "E714: List required" -msgstr "E714: Liste gerekiyor" - -msgid "E897: List or Blob required" -msgstr "E897: Liste veya ikili geniş nesne gerekiyor" - -#, c-format -msgid "E697: Missing end of List ']': %s" -msgstr "E697: Liste sonunda ']' eksik: %s" - -#, c-format -msgid "E712: Argument of %s must be a List or Dictionary" -msgstr "E712: %s ögesinin argümanı bir liste veya sözlük olmalıdır" - -#, c-format -msgid "E896: Argument of %s must be a List, Dictionary or Blob" -msgstr "E896: %s argümanı bir liste, sözlük veya ikili geniş nesne olmalıdır" - -msgid "E804: Cannot use '%' with Float" -msgstr "E804: Bir kayan noktalı değer ile '%' kullanılamaz" - -msgid "E996: Cannot lock an option" -msgstr "E996: Seçenek kilitlenemiyor" - -#, c-format -msgid "E113: Unknown option: %s" -msgstr "E113: Bilinmeyen seçenek: %s" - -#, c-format -msgid "E998: Reduce of an empty %s with no initial value" -msgstr "E998: Başlangıç değeri olmayan boş bir %s için reduce() yapılamıyor" - -#, c-format -msgid "E857: Dictionary key \"%s\" required" -msgstr "E857: Sözlük anahtarı \"%s\" gerekiyor" - -msgid "E47: Error while reading errorfile" -msgstr "E47: Hata dosyası okunurken hata" - -msgid "E48: Not allowed in sandbox" -msgstr "E48: Kum havuzunda izin verilmiyor" - -msgid "E523: Not allowed here" -msgstr "E523: Burada izin verilmiyor" - -msgid "E578: Not allowed to change text here" -msgstr "E578: Burada metin değişikliğine izin verilmiyor" - -msgid "E565: Not allowed to change text or change window" -msgstr "E565: Pencere veya metin değişikliğine izin verilmiyor" - -msgid "E359: Screen mode setting not supported" -msgstr "E359: Ekran kipi ayarı desteklenmiyor" - -msgid "E49: Invalid scroll size" -msgstr "E49: Geçersiz kaydırma boyutu" - -msgid "E91: 'shell' option is empty" -msgstr "E91: 'shell' seçeneği boş" - -msgid "E255: Couldn't read in sign data!" -msgstr "E255: İşaret verisi okunamadı!" - -msgid "E72: Close error on swap file" -msgstr "E72: Takas dosyasında kapama hatası" - -msgid "E73: tag stack empty" -msgstr "E73: Etiket yığını boş" - -msgid "E74: Command too complex" -msgstr "E74: Komut çok karmaşık" - -msgid "E75: Name too long" -msgstr "E75: Ad çok uzun" - -msgid "E76: Too many [" -msgstr "E76: Çok fazla [" - -msgid "E77: Too many file names" -msgstr "E77: Çok fazla dosya adı" - -msgid "E488: Trailing characters" -msgstr "E488: Sonda fazladan karakterler" - -#, c-format -msgid "E488: Trailing characters: %s" -msgstr "E488: Sonda fazladan karakterler: %s" - -msgid "E78: Unknown mark" -msgstr "E78: Bilinmeyen im" - -msgid "E79: Cannot expand wildcards" -msgstr "E79: Joker karakterleri genişletilemiyor" - -msgid "E591: 'winheight' cannot be smaller than 'winminheight'" -msgstr "E591: 'winheight' değeri 'winminheight' değerinden küçük olamaz" - -msgid "E592: 'winwidth' cannot be smaller than 'winminwidth'" -msgstr "E592: 'winwidth' değeri 'winminwidth' değerinden küçük olamaz" - -msgid "E80: Error while writing" -msgstr "E80: Yazma sırasında hata" - -msgid "E939: Positive count required" -msgstr "E939: Pozitif sayım gerekiyor" - -msgid "E81: Using <SID> not in a script context" -msgstr "E81: <SID> bir betik bağlamında kullanılmıyor" - -#, c-format -msgid "E107: Missing parentheses: %s" -msgstr "E107: Ayraç eksik: %s" - -msgid "E110: Missing ')'" -msgstr "E110: ')' eksik" - -#, c-format -msgid "E720: Missing colon in Dictionary: %s" -msgstr "E720: Sözlükte iki nokta eksik: %s" - -#, c-format -msgid "E721: Duplicate key in Dictionary: \"%s\"" -msgstr "E721: Sözlükte yinelenmiş anahtar: \"%s\"" - -#, c-format -msgid "E722: Missing comma in Dictionary: %s" -msgstr "E722: Sözlükte virgül eksik: %s" - -#, c-format -msgid "E723: Missing end of Dictionary '}': %s" -msgstr "E723: Sözlük sonu '}' eksik: %s" - -msgid "E449: Invalid expression received" -msgstr "E449: Geçersiz ifade alındı" - -msgid "E463: Region is guarded, cannot modify" -msgstr "E463: Bölge korunuyor, değiştirilemez" - -msgid "E744: NetBeans does not allow changes in read-only files" -msgstr "E744: NetBeans salt okunur dosyalarda değişikliklere izin vermiyor" - -msgid "E363: pattern uses more memory than 'maxmempattern'" -msgstr "E363: Dizgi 'maxmempattern' ögesinden daha fazla bellek kullanıyor" - -msgid "E749: empty buffer" -msgstr "E749: Boş arabellek" - -#, c-format -msgid "E86: Buffer %ld does not exist" -msgstr "E86: Arabellek %ld mevcut değil" - -msgid "E682: Invalid search pattern or delimiter" -msgstr "E682: Geçersiz arama dizgisi veya sınırlandırıcısı" - -msgid "E139: File is loaded in another buffer" -msgstr "E139: Dosya başka bir arabellekte yüklü" - -#, c-format -msgid "E764: Option '%s' is not set" -msgstr "E764: '%s' seçeneği ayarlanmamış" - -msgid "E850: Invalid register name" -msgstr "E850: Geçersiz yazmaç adı" - -msgid "E806: using Float as a String" -msgstr "E806: Kayan Noktalı Değer, bir Dizi yerine kullanılıyor" - -#, c-format -msgid "E919: Directory not found in '%s': \"%s\"" -msgstr "E919: '%s' içinde dizin bulunamadı: \"%s\"" - -msgid "E952: Autocommand caused recursive behavior" -msgstr "E952: Otokomut özyineli davranışa neden oldu" - -msgid "E813: Cannot close autocmd or popup window" -msgstr "E813: Otokomut veya açılır pencere kapatılamıyor" - -msgid "E328: Menu only exists in another mode" -msgstr "E328: Menü yalnızca başka bir kipte mevcut" - -msgid "E957: Invalid window number" -msgstr "E957: Geçersiz pencere numarası" - -#, c-format -msgid "E686: Argument of %s must be a List" -msgstr "E686: %s argümanı bir liste olmalı" - -msgid "E109: Missing ':' after '?'" -msgstr "E109: '?' sonrası ':' eksik" - -msgid "E690: Missing \"in\" after :for" -msgstr "E690: :for sonrası \"in\" eksik" - -#, c-format -msgid "E117: Unknown function: %s" -msgstr "E117: Bilinmeyen işlev: %s" - -msgid "E111: Missing ']'" -msgstr "E111: ']' eksik" - -msgid "E581: :else without :if" -msgstr "E581: :if olmadan :else" - -msgid "E582: :elseif without :if" -msgstr "E582: :if olmadan :elseif" - -msgid "E580: :endif without :if" -msgstr "E580: :if olmadan :endif" - -msgid "E586: :continue without :while or :for" -msgstr "E586: :while veya :for olmadan :continue" - -msgid "E587: :break without :while or :for" -msgstr "E587: :while veya :for olmadan :break" - -msgid "E274: No white space allowed before parenthesis" -msgstr "E274: Ayraçtan önce boşluğa izin verilmiyor" - -#, c-format -msgid "E940: Cannot lock or unlock variable %s" -msgstr "E940: Değişken %s kilitlenemiyor veya açılamıyor" - -#, c-format -msgid "E254: Cannot allocate color %s" -msgstr "E254: %s rengi ayrılamıyor" - -msgid "search hit TOP, continuing at BOTTOM" -msgstr "Arama dosyanın BAŞINI geçti, dosyanın SONUNDAN sürüyor" - -msgid "search hit BOTTOM, continuing at TOP" -msgstr "Arama dosyanın SONUNU geçti, dosyanın BAŞINDAN sürüyor" - -msgid " line " -msgstr " satır " - -#, c-format -msgid "Need encryption key for \"%s\"" -msgstr "\"%s\" için şifreleme anahtarı gerekli" - -msgid "empty keys are not allowed" -msgstr "boş anahtarlara izin verilmiyor" - -msgid "dictionary is locked" -msgstr "sözlük kilitli" - -msgid "list is locked" -msgstr "liste kilitli" - -#, c-format -msgid "failed to add key '%s' to dictionary" -msgstr "'%s' anahtarı sözlüğe eklenemedi" - -#, c-format -msgid "index must be int or slice, not %s" -msgstr "sıra bir tamsayı veya dilim olmalıdır, %s olamaz" - -#, c-format -msgid "expected str() or unicode() instance, but got %s" -msgstr "str() veya unicode() örneği bekleniyordu, %s geldi" - -#, c-format -msgid "expected bytes() or str() instance, but got %s" -msgstr "bytes() veya str() örneği bekleniyordu, %s geldi" - -#, c-format -msgid "" -"expected int(), long() or something supporting coercing to long(), but got %s" -msgstr "" -"int(), long() veya long()'a baskıyı destekleyen bir şey bekleniyordu, %s " -"geldi" - -#, c-format -msgid "expected int() or something supporting coercing to int(), but got %s" -msgstr "int() veya int()'e baskıyı destekleyen bir şey bekleniyordu, %s geldi" - -msgid "value is too large to fit into C int type" -msgstr "değer C tamsayı türüne sığmak için çok büyük" - -msgid "value is too small to fit into C int type" -msgstr "değer C tamsayı türüne sığmak için çok küçük" - -msgid "number must be greater than zero" -msgstr "sayı sıfırdan büyük olmalı" - -msgid "number must be greater or equal to zero" -msgstr "sayı sıfıra eşit veya sıfırdan büyük olmalı" - -msgid "can't delete OutputObject attributes" -msgstr "OutputObject öznitelikleri silinemiyor" - -#, c-format -msgid "invalid attribute: %s" -msgstr "geçersiz öznitelik: %s" - -msgid "E264: Python: Error initialising I/O objects" -msgstr "E264: Python: Girdi/Çıktı nesneleri başlatılırken hata" - -msgid "failed to change directory" -msgstr "dizin değiştirilemedi" - -#, c-format -msgid "expected 3-tuple as imp.find_module() result, but got %s" -msgstr "imp.find_module() sonucu olarak 3 çoklu öge bekleniyordu, %s geldi" - -#, c-format -msgid "expected 3-tuple as imp.find_module() result, but got tuple of size %d" -msgstr "" -"imp.find_module() sonucu olarak 3 tuple bekleniyordu, %d boyutlu çok öge " -"geldi" - -msgid "internal error: imp.find_module returned tuple with NULL" -msgstr "iç hata: imp.find_module BOŞ bir çoklu öge döndürdü" - -msgid "cannot delete vim.Dictionary attributes" -msgstr "vim.Dictionary öznitelikleri silinemiyor" - -msgid "cannot modify fixed dictionary" -msgstr "sabit sözlük değiştirilemiyor" - -#, c-format -msgid "cannot set attribute %s" -msgstr "%s özniteliği ayarlanamıyor" - -msgid "hashtab changed during iteration" -msgstr "Sağlama tablosu dürüm sırasında değişti" - -#, c-format -msgid "expected sequence element of size 2, but got sequence of size %d" -msgstr "2 boyut bir sıralama bekleniyordu, ancak %d boyut bir sıralama geldi" - -msgid "list constructor does not accept keyword arguments" -msgstr "liste yapıcısı anahtar sözcük argümanları kabul etmez" - -msgid "list index out of range" -msgstr "liste sırası erimin dışında" - -#, c-format -msgid "internal error: failed to get Vim list item %d" -msgstr "iç hata: %d vim liste ögesi alınamadı" - -msgid "slice step cannot be zero" -msgstr "dilim adımı sıfır olamaz" - -#, c-format -msgid "attempt to assign sequence of size greater than %d to extended slice" -msgstr "genişletilmiş dilime %d boyuttan büyük bir sıralamayı atama denemesi" - -#, c-format -msgid "internal error: no Vim list item %d" -msgstr "iç hata: %d vim liste ögesi yok" - -msgid "internal error: not enough list items" -msgstr "iç hata: yeterli liste ögesi yok" - -msgid "internal error: failed to add item to list" -msgstr "iç hata: öge listeye eklenemedi" - -#, c-format -msgid "attempt to assign sequence of size %d to extended slice of size %d" -msgstr "%d boyut sıralamayı %d boyut genişletilmiş dizine atama denemesi" - -msgid "failed to add item to list" -msgstr "öge listeye eklenemedi" - -msgid "cannot delete vim.List attributes" -msgstr "vim.List öznitelikleri silinemiyor" - -msgid "cannot modify fixed list" -msgstr "sabit liste değiştirilemiyor" - -#, c-format -msgid "unnamed function %s does not exist" -msgstr "adsız %s işlevi mevcut değil" - -#, c-format -msgid "function %s does not exist" -msgstr "%s işlevi mevcut değil" - -#, c-format -msgid "failed to run function %s" -msgstr "%s işlevi çalıştırılamadı" - -msgid "unable to get option value" -msgstr "seçenek değeri alınamadı" - -msgid "internal error: unknown option type" -msgstr "iç hata: bilinmeyen seçenek türü" - -msgid "problem while switching windows" -msgstr "pencereler arasında gezinirken hata" - -#, c-format -msgid "unable to unset global option %s" -msgstr "%s global seçenek ayarı kapatılamıyor" - -#, c-format -msgid "unable to unset option %s which does not have global value" -msgstr "global değeri olmayan %s seçenek ayarı kapatılamıyor" - -msgid "attempt to refer to deleted tab page" -msgstr "silinmiş sekme sayfasına başvurma denemesi" - -msgid "no such tab page" -msgstr "böyle bir sekme sayfası yok" - -msgid "attempt to refer to deleted window" -msgstr "silinmiş pencereye başvurma denemesi" - -msgid "readonly attribute: buffer" -msgstr "saltokunur öznitelik: arabellek" - -msgid "cursor position outside buffer" -msgstr "imleç konumu arabelleğin dışında" - -msgid "no such window" -msgstr "böyle bir pencere yok" - -msgid "attempt to refer to deleted buffer" -msgstr "silinmiş arabelleğe başvurma denemesi" - -msgid "failed to rename buffer" -msgstr "arabellek adı değiştirilemedi" - -msgid "mark name must be a single character" -msgstr "im adı tek bir karakterden olmalıdır" - -#, c-format -msgid "expected vim.Buffer object, but got %s" -msgstr "vim.Buffer nesnesi bekleniyordu, %s geldi" - -#, c-format -msgid "failed to switch to buffer %d" -msgstr "%d arabelleğine geçilemedi" - -#, c-format -msgid "expected vim.Window object, but got %s" -msgstr "vim.Window nesnesi bekleniyordu, %s geldi" - -msgid "failed to find window in the current tab page" -msgstr "mevcut sekme sayfasında pencere bulunamadı" - -msgid "did not switch to the specified window" -msgstr "belirtilen pencereye geçilemedi" - -#, c-format -msgid "expected vim.TabPage object, but got %s" -msgstr "vim.TabPage nesnesi bekleniyordu, %s geldi" - -msgid "did not switch to the specified tab page" -msgstr "belirtilen sekme sayfasına geçilemedi" - -msgid "failed to run the code" -msgstr "kod çalıştırılamadı" - -msgid "E858: Eval did not return a valid python object" -msgstr "E858: Eval geçerli bir python nesnesi döndürmedi" - -msgid "E859: Failed to convert returned python object to a Vim value" -msgstr "E859: Döndürülen python nesnesi vim değerine dönüştürülemedi" - -#, c-format -msgid "unable to convert %s to a Vim dictionary" -msgstr "%s vim sözlüğüne dönüştürülemedi" - -#, c-format -msgid "unable to convert %s to a Vim list" -msgstr "%s vim listesine dönüştürülemedi" - -#, c-format -msgid "unable to convert %s to a Vim structure" -msgstr "%s vim yapısına dönüştürülemedi" - -msgid "internal error: NULL reference passed" -msgstr "iç hata: BOŞ başvuru geçirildi" - -msgid "internal error: invalid value type" -msgstr "iç hata: geçersiz değer türü" - -msgid "" -"Failed to set path hook: sys.path_hooks is not a list\n" -"You should now do the following:\n" -"- append vim.path_hook to sys.path_hooks\n" -"- append vim.VIM_SPECIAL_PATH to sys.path\n" -msgstr "" -"Yol kancası ayarlanamadı: sys.path_hooks bir liste değil\n" -"Şimdi şunları yapmanız gerekiyor:\n" -"- vim.path_hook'u sys.path_hooks'a iliştirmek\n" -"- vim.VIM_SPECIAL_PATH'i sys.path'e iliştirmek\n" - -msgid "" -"Failed to set path: sys.path is not a list\n" -"You should now append vim.VIM_SPECIAL_PATH to sys.path" -msgstr "" -"Yol ayarlanamadı: sys.path bir liste değil\n" -"Şimdi vim.VIM_SPECIAL_PATH'i sys.path'e iliştirmelisiniz" - -msgid "" -"Vim macro files (*.vim)\t*.vim\n" -"All Files (*.*)\t*.*\n" -msgstr "" -"Vim makro dosyaları (*.vim)\t*.vim\n" -"Tüm Dosyalar (*.*)\t*.*\n" - -msgid "All Files (*.*)\t*.*\n" -msgstr "Tüm Dosyalar (*.*)\t*.*\n" - -msgid "" -"All Files (*.*)\t*.*\n" -"C source (*.c, *.h)\t*.c;*.h\n" -"C++ source (*.cpp, *.hpp)\t*.cpp;*.hpp\n" -"VB code (*.bas, *.frm)\t*.bas;*.frm\n" -"Vim files (*.vim, _vimrc, _gvimrc)\t*.vim;_vimrc;_gvimrc\n" -msgstr "" -"Tüm Dosyalar (*.*)\t*.*\n" -"C kaynak dosyaları (*.c, *.h)\t*.c;*.h\n" -"C++ kaynak dosyaları (*.cpp, *.hpp)\t*.cpp;*.hpp\n" -"VB kodu (*.bas, *.frm)\t*.bas;*.frm\n" -"Vim dosyaları (*.vim, _vimrc, _gvimrc)\t*.vim;_vimrc;_gvimrc\n" - -msgid "" -"Vim macro files (*.vim)\t*.vim\n" -"All Files (*)\t*\n" -msgstr "" -"Vim makro dosyaları (*.vim)\t*.vim\n" -"Tüm Dosyalar (*)\t*\n" - -msgid "All Files (*)\t*\n" -msgstr "Tüm Dosyalar (*)\t*\n" - -msgid "" -"All Files (*)\t*\n" -"C source (*.c, *.h)\t*.c;*.h\n" -"C++ source (*.cpp, *.hpp)\t*.cpp;*.hpp\n" -"Vim files (*.vim, _vimrc, _gvimrc)\t*.vim;_vimrc;_gvimrc\n" -msgstr "" -"Tüm Dosyalar (*)\t*\n" -"C kaynak dosyaları (*.c, *.h)\t*.c;*.h\n" -"C++ kaynak dosyaları (*.cpp, *.hpp)\t*.cpp;*.hpp\n" -"Vim dosyaları (*.vim, _vimrc, _gvimrc)\t*.vim;_vimrc;_gvimrc\n" - -msgid "GVim" -msgstr "GVim" - -msgid "Text Editor" -msgstr "Metin Düzenleyici" - -msgid "Edit text files" -msgstr "Metin dosyaları düzenleyin" - -msgid "Text;editor;" -msgstr "Metin;düzenleyici;" - -msgid "Vim" -msgstr "Vim" - -msgid "(local to window)" -msgstr "(pencereye yerel)" - -msgid "(local to buffer)" -msgstr "(arabelleğe yerel)" - -msgid "(global or local to buffer)" -msgstr "(arabelleğe global veya yerel)" - -msgid "" -"\" Each \"set\" line shows the current value of an option (on the left)." -msgstr "\" Her \"set\" satırı bir seçeneğin geçerli değerini gösterir (solda)." - -msgid "\" Hit <Enter> on a \"set\" line to execute it." -msgstr "\" Değiştirmek için bir \"set\" satırında <Enter>'a basın." - -msgid "\" A boolean option will be toggled." -msgstr "\" Bir Boole değeri işletilecektir." - -msgid "" -"\" For other options you can edit the value before hitting " -"<Enter>." -msgstr "" -"\" Diğer seçenekler için <Enter>'a basmadan önce değeri " -"düzenleyebilirsiniz." - -msgid "\" Hit <Enter> on a help line to open a help window on this option." -msgstr "\" Yardım penceresini açmak için seçenek adı üzerinde <Enter>'a basın." - -msgid "\" Hit <Enter> on an index line to jump there." -msgstr "" -"\" Bir seçeneğe atlamak için indeks satırının üzerinde <Enter>'a basın." - -msgid "\" Hit <Space> on a \"set\" line to refresh it." -msgstr "" -"\" Bir seçeneği yenilemek için bir \"set\" satırının üzerinde <Boşluk>'a " -"basın." - -msgid "important" -msgstr "önemli" - -msgid "behave very Vi compatible (not advisable)" -msgstr "olabildiğince Vi uyumlu biçimde davran (önerilmez)" - -msgid "list of flags to specify Vi compatibility" -msgstr "Vi uyumluluğu bayrakları listesi" - -msgid "use Insert mode as the default mode" -msgstr "Ekleme kipini öntanımlı kip olarak kullan" - -msgid "paste mode, insert typed text literally" -msgstr "yapıştır kipi, girilen metni doğrudan ekle" - -msgid "key sequence to toggle paste mode" -msgstr "yapıştır kipini açıp/kapatmak için düğme sıralaması" - -msgid "list of directories used for runtime files and plugins" -msgstr "çalışma zamanı dosyaları ve eklentileri için kullanılan dizinler" - -msgid "list of directories used for plugin packages" -msgstr "eklenti paketleri için kullanılan dizinlerin listesi" - -msgid "name of the main help file" -msgstr "ana yardım dosyasının adı" - -msgid "moving around, searching and patterns" -msgstr "dolaşma, arama ve dizgeler" - -msgid "list of flags specifying which commands wrap to another line" -msgstr "" -"hangi komutların diğer satıra kaydırıldığını belirleyen bayraklar\n" -"listesi" - -msgid "" -"many jump commands move the cursor to the first non-blank\n" -"character of a line" -msgstr "" -"çoğu atlama komutu, imleci satırın boş olmayan ilk\n" -"karakterine taşır" - -msgid "nroff macro names that separate paragraphs" -msgstr "paragrafları ayıran nroff makro adları" - -msgid "nroff macro names that separate sections" -msgstr "bölümleri ayıran nroff makro adları" - -msgid "list of directory names used for file searching" -msgstr "dosya arama için kullanılan dizin adları listesi" - -msgid "list of directory names used for :cd" -msgstr ":cd için kullanılan dizin adları listesi" - -msgid "change to directory of file in buffer" -msgstr "arabellekteki dosyanın olduğu dizine değiştir" - -msgid "change to pwd of shell in terminal buffer" -msgstr "uçbirim arabelleğindeki kabuğun pwd'sine geç" - -msgid "search commands wrap around the end of the buffer" -msgstr "arama komutları, arabelleğin sonunda kaydırılır" - -msgid "show match for partly typed search command" -msgstr "bir kısmı yazılmış arama komutu ile eşleşeni göster" - -msgid "change the way backslashes are used in search patterns" -msgstr "arama dizgilerinde ters eğik çizginin kullanımını değiştir" - -msgid "select the default regexp engine used" -msgstr "öntanımlı kullanılan düzenli ifade motorunu seç" - -msgid "ignore case when using a search pattern" -msgstr "bir arama dizgisinde BÜYÜK/küçük harf ayrımını yok say" - -msgid "override 'ignorecase' when pattern has upper case characters" -msgstr "dizgide BÜYÜK harf varsa 'ignorecase'i geçersiz kıl" - -msgid "what method to use for changing case of letters" -msgstr "BÜYÜK/küçük harf değiştirirken hangi yöntemin kullanılacağı" - -msgid "maximum amount of memory in Kbyte used for pattern matching" -msgstr "dizgi eşleşme için kullanılabilecek en çok bellek miktarı (KiB)" - -msgid "pattern for a macro definition line" -msgstr "bir makro tanım satırı için dizgi" - -msgid "pattern for an include-file line" -msgstr "bir 'include-file' satırı için dizgi" - -msgid "expression used to transform an include line to a file name" -msgstr "bir 'include' satırını dosya adına dönüştürmede kullanılan ifade" - -msgid "tags" -msgstr "etiketler" - -msgid "use binary searching in tags files" -msgstr "etiketler dosyasında ikili arama kullan" - -msgid "number of significant characters in a tag name or zero" -msgstr "bir etiket adındaki belirgin karakterlerin sayısı veya sıfır" - -msgid "list of file names to search for tags" -msgstr "etiketlerin aranacağı dosyaların listesi" - -msgid "" -"how to handle case when searching in tags files:\n" -"\"followic\" to follow 'ignorecase', \"ignore\" or \"match\"" -msgstr "" -"etiket dosyalarında arama yaparken BÜYÜK/küçük harf kullanımı:\n" -"'ignorecase', \"ignore\" veya \"match\"den sonra \"followic\" gelir" - -msgid "file names in a tags file are relative to the tags file" -msgstr "bir etiket dosyasındaki dosya adları etiket dosyasına görelidir" - -msgid "a :tag command will use the tagstack" -msgstr "bir :tag komutu etiket yığınını kullanır" - -msgid "when completing tags in Insert mode show more info" -msgstr "Ekleme kipinde etiketleri tamamlarken daha çok bilgi göster" - -msgid "a function to be used to perform tag searches" -msgstr "etiket aramaları gerçekleştirmek için bir işlev kullanılır" - -msgid "command for executing cscope" -msgstr "cscope çalıştırmak için kullanılacak komut" - -msgid "use cscope for tag commands" -msgstr "etiket komutları için cscope kullan" - -msgid "0 or 1; the order in which \":cstag\" performs a search" -msgstr "0 veya 1; \":cstag\"in arama yaparken kullanacağı sıra" - -msgid "give messages when adding a cscope database" -msgstr "bir cscope veritabanı eklerken iletiler göster" - -msgid "how many components of the path to show" -msgstr "yolun kaç tane bileşeninin gösterileceği" - -msgid "when to open a quickfix window for cscope" -msgstr "cscope için ne zaman bir hızlı düzelt penceresinin açılacağı" - -msgid "file names in a cscope file are relative to that file" -msgstr "bir cscope dosyasındaki o dosyaya göreli olan dosya adları" - -msgid "displaying text" -msgstr "metin görüntüleme" - -msgid "number of lines to scroll for CTRL-U and CTRL-D" -msgstr "CTRL-U ve CTRL-D için kaydırılacak satır sayısı" - -msgid "number of screen lines to show around the cursor" -msgstr "imleç etrafında gösterilecek ekran satırları sayısı" - -msgid "long lines wrap" -msgstr "uzun satırları kaydır" - -msgid "wrap long lines at a character in 'breakat'" -msgstr "'breakat' içindeki bir karakterde uzun satırları kaydır" - -msgid "preserve indentation in wrapped text" -msgstr "kaydırılmış metindeki girintilemeyi koru" - -msgid "adjust breakindent behaviour" -msgstr "'breakindent' davranışını ayarla" - -msgid "which characters might cause a line break" -msgstr "hangi karakterler bir satır sonuna neden olabilir" - -msgid "string to put before wrapped screen lines" -msgstr "kaydırılmış ekran satırlarından önce konumlanacak dizi" - -msgid "minimal number of columns to scroll horizontally" -msgstr "en az yatay kaydırma sütunu sayısı" - -msgid "minimal number of columns to keep left and right of the cursor" -msgstr "imlecin sağında ve solunda bırakılacak en az sütun sayısı" - -msgid "" -"include \"lastline\" to show the last line even if it doesn't fit\n" -"include \"uhex\" to show unprintable characters as a hex number" -msgstr "" -"eğer sığmasa bile son satırı göstermek için \"lastline\"ı içer\n" -"yazdırılamayan karakterleri onaltılık olarak göstermek için\n" -"\"uhex\" içer" - -msgid "characters to use for the status line, folds and filler lines" -msgstr "durum satırı, kıvırma ve doldurucular için kullanılan karakterler" - -msgid "number of lines used for the command-line" -msgstr "komut satırı için kullanılan satırların sayısı" - -msgid "width of the display" -msgstr "ekranın genişliği" - -msgid "number of lines in the display" -msgstr "ekrandaki satırların sayısı" - -msgid "number of lines to scroll for CTRL-F and CTRL-B" -msgstr "CTRL-F ve CTRL-B için kaydırılacak satır sayısı" - -msgid "don't redraw while executing macros" -msgstr "makroları çalıştırırken yenileme yapma" - -msgid "timeout for 'hlsearch' and :match highlighting in msec" -msgstr "'hlsearch' ve : match vurgulaması için zaman aşımı (milisaniye)" - -msgid "" -"delay in msec for each char written to the display\n" -"(for debugging)" -msgstr "" -"ekrana yazılan her karakter için gecikme süresi (milisaniye)\n" -"(hata ayıklama için)" - -msgid "show <Tab> as ^I and end-of-line as $" -msgstr "<Tab>'ı ^I ve satır sonunu $ olarak göster" - -msgid "list of strings used for list mode" -msgstr "liste kipi için kullanılan diziler listesi" - -msgid "show the line number for each line" -msgstr "her satır için satır numarasını göster" - -msgid "show the relative line number for each line" -msgstr "her satır için göreli satır numarasını göster" - -msgid "number of columns to use for the line number" -msgstr "satır numarası için kullanılacak sütün sayısı" - -msgid "controls whether concealable text is hidden" -msgstr "gizlenebilir metnin saklı olup olmadığını denetler" - -msgid "modes in which text in the cursor line can be concealed" -msgstr "imleç satırındaki metnin gizlenebileceği kipler" - -msgid "syntax, highlighting and spelling" -msgstr "sözdizim, vurgulama ve yazım denetimi" - -msgid "\"dark\" or \"light\"; the background color brightness" -msgstr "\"dark\" veya \"light\"; arka plan renk parlaklığı" - -msgid "type of file; triggers the FileType event when set" -msgstr "dosya türü; ayarlandığında FileType olayını tetikler" - -msgid "name of syntax highlighting used" -msgstr "kullanılan sözdizim vurgulamanın adı" - -msgid "maximum column to look for syntax items" -msgstr "sözdizim ögeleri için bakılacak en çok sütun sayısı" - -msgid "which highlighting to use for various occasions" -msgstr "çeşitli durumlarda hangi vurgulamanın kullanılacağı" - -msgid "highlight all matches for the last used search pattern" -msgstr "son kullanılan arama dizgisi için tüm eşleşmeleri vurgula" - -msgid "highlight group to use for the window" -msgstr "pencere için kullanılacak vurgulama grubu" - -msgid "use GUI colors for the terminal" -msgstr "uçbirim için grafik arabirim renklerini kullan" - -msgid "highlight the screen column of the cursor" -msgstr "imlecin ekrandaki sütununu vurgula" - -msgid "highlight the screen line of the cursor" -msgstr "imlecin ekrandaki satırını vurgula" - -msgid "specifies which area 'cursorline' highlights" -msgstr "'cursorline'ın hangi alanı vurgulayacağı" - -msgid "columns to highlight" -msgstr "vurgulanacak sütunlar" - -msgid "highlight spelling mistakes" -msgstr "yazım yanlışlarını vurgula" - -msgid "list of accepted languages" -msgstr "kabul edilen dillerin listesi" - -msgid "file that \"zg\" adds good words to" -msgstr "\"zg\" komutunun düzgün sözcükleri ekleyeceği dosya" - -msgid "pattern to locate the end of a sentence" -msgstr "bir tümcenin sonunu bulmak için kullanılan dizgi" - -msgid "flags to change how spell checking works" -msgstr "yazım denetiminin nice çalıştığını değiştirmek için bayraklar" - -msgid "methods used to suggest corrections" -msgstr "düzeltmeleri önermek için yöntemler" - -msgid "amount of memory used by :mkspell before compressing" -msgstr "sıkıştırma öncesi :mkspell tarafından kullanılan bellek" - -msgid "multiple windows" -msgstr "çoklu pencereler" - -msgid "0, 1 or 2; when to use a status line for the last window" -msgstr "" -"0, 1 veya 2; son pencere için ne zaman bir durum satırı\n" -"kullanılacağı" - -msgid "alternate format to be used for a status line" -msgstr "durum satırı için kullanılabilecek alternatif biçim" - -msgid "make all windows the same size when adding/removing windows" -msgstr "pencere eklerken/kaldırırken tüm pencereleri aynı boyuta getir" - -msgid "in which direction 'equalalways' works: \"ver\", \"hor\" or \"both\"" -msgstr "'equalalways'in hangi yönde çalıştığı: \"ver\", \"hor\" veya \"both\"" - -msgid "minimal number of lines used for the current window" -msgstr "geçerli pencere için kullanılan en az satır sayısı" - -msgid "minimal number of lines used for any window" -msgstr "herhangi bir pencere için kullanılan en az satır sayısı" - -msgid "keep the height of the window" -msgstr "pencerenin yüksekliğini tut" - -msgid "keep the width of the window" -msgstr "pencerenin genişliğini tut" - -msgid "minimal number of columns used for the current window" -msgstr "geçerli pencere için kullanılan en az sütun sayısı" - -msgid "minimal number of columns used for any window" -msgstr "herhangi bir pencere için kullanılan en az sütun sayısı" - -msgid "initial height of the help window" -msgstr "yardım penceresinin başlangıç yüksekliği" - -msgid "use a popup window for preview" -msgstr "önizleme için bir açılır pencere kullan" - -msgid "default height for the preview window" -msgstr "önizleme penceresi için öntanımlı yükseklik" - -msgid "identifies the preview window" -msgstr "önizleme penceresini tanımlar" - -msgid "don't unload a buffer when no longer shown in a window" -msgstr "arabellek artık pencerede görüntülenmiyorsa bellekten kaldırma" - -msgid "" -"\"useopen\" and/or \"split\"; which window to use when jumping\n" -"to a buffer" -msgstr "" -"\"useopen\" ve/veya \"split\"; bir belleğe atlarken hangi\n" -"pencerenin kullanılacağı" - -msgid "a new window is put below the current one" -msgstr "geçerli pencerenin altına yeni bir pencere koyulur" - -msgid "a new window is put right of the current one" -msgstr "yeni bir pencere geçerli pencerenin sağına koyulur" - -msgid "this window scrolls together with other bound windows" -msgstr "bu pencere, bağlı diğer pencerelerle birlikte kayar" - -msgid "\"ver\", \"hor\" and/or \"jump\"; list of options for 'scrollbind'" -msgstr "" -"\"ver\", \"hor\" ve/veya \"jump\"; 'scrollbind' için seçenekler listesi" - -msgid "this window's cursor moves together with other bound windows" -msgstr "bu pencerenin imleci bağlı diğer pencerelerle birlikte kayar" - -msgid "size of a terminal window" -msgstr "bir uçbirim penceresinin boyutu" - -msgid "key that precedes Vim commands in a terminal window" -msgstr "bir uçbirim penceresinde Vim komutlarından önce gelen düğme" - -msgid "max number of lines to keep for scrollback in a terminal window" -msgstr "" -"bir uçbirim penceresinde geri kaydırma için kullanılacak\n" -"en çok satır sayısı" - -msgid "type of pty to use for a terminal window" -msgstr "bir uçbirim penceresi için kullanılacak pty türü" - -msgid "name of the winpty dynamic library" -msgstr "winpty devingen kitaplığının adı" - -msgid "multiple tab pages" -msgstr "çoklu sekme sayfaları" - -msgid "0, 1 or 2; when to use a tab pages line" -msgstr "0, 1 veya 2; ne zaman bir sekme sayfası satırının kullanılacağı" - -msgid "maximum number of tab pages to open for -p and \"tab all\"" -msgstr "-p ve \"tab all\"un açacağı en çok sekme sayfası sayısı" - -msgid "custom tab pages line" -msgstr "özelleştirilmiş sekme sayfası satırı" - -msgid "custom tab page label for the GUI" -msgstr "grafik arabirim için özelleştirilmiş sekme sayfası etiketi" - -msgid "custom tab page tooltip for the GUI" -msgstr "grafik arabirim için özelleştirilmiş sekme sayfası bilgi kutusu" - -msgid "terminal" -msgstr "uçbirim" - -msgid "name of the used terminal" -msgstr "kullanılan uçbirimin adı" - -msgid "alias for 'term'" -msgstr "'term' için arma" - -msgid "check built-in termcaps first" -msgstr "önce iç termcaps'i denetle" - -msgid "terminal connection is fast" -msgstr "uçbirim bağlantısı hızlı" - -msgid "terminal that requires extra redrawing" -msgstr "ek yenileme gerektiren uçbirim" - -msgid "recognize keys that start with <Esc> in Insert mode" -msgstr "Ekleme kipinde <Esc> ile başlayan düğmeleri tanı" - -msgid "minimal number of lines to scroll at a time" -msgstr "herhangi bir zamanda kaydırılacak en az satır sayısı" - -msgid "maximum number of lines to use scrolling instead of redrawing" -msgstr "yenileme yerine kaydırma kullanacak en çok satır sayısı" - -msgid "specifies what the cursor looks like in different modes" -msgstr "imlecin farklı kiplerde nice göründüğünü belirler" - -msgid "show info in the window title" -msgstr "pencere başlığında bilgi görüntüle" - -msgid "percentage of 'columns' used for the window title" -msgstr "pencere başlığı için kullanılacak 'columns' yüzdesi" - -msgid "when not empty, string to be used for the window title" -msgstr "boş değilken, pencere başlığı yerine kullanılacak dizi" - -msgid "string to restore the title to when exiting Vim" -msgstr "Vim'den çıkarken başlığın döndürüleceği dizi" - -msgid "set the text of the icon for this window" -msgstr "bu pencere için simgenin metnini ayarla" - -msgid "when not empty, text for the icon of this window" -msgstr "boş değilken, bu pencerenin simgesi için metin" - -msgid "restore the screen contents when exiting Vim" -msgstr "Vim'den çıkarken ekran içeriğini eski haline getir" - -msgid "using the mouse" -msgstr "fare kullanımı" - -msgid "list of flags for using the mouse" -msgstr "fare kullanımı için bayraklar listesi" - -msgid "the window with the mouse pointer becomes the current one" -msgstr "fare imlecinin olduğu pencere geçerli pencere olur" - -msgid "the window with the mouse pointer scrolls with the mouse wheel" -msgstr "fare imlecinin olduğu pencere fare tekerleği ile kaydırılabilir" - -msgid "hide the mouse pointer while typing" -msgstr "yazı yazarken fare imlecini gizle" - -msgid "" -"\"extend\", \"popup\" or \"popup_setpos\"; what the right\n" -"mouse button is used for" -msgstr "" -"\"extend\", \"popup\" veya \"popup_setpos\"; sağ fare düğmesinin\"\n" -"ne için kullanıldığı" - -msgid "maximum time in msec to recognize a double-click" -msgstr "bir çif tıklamayı tanımak için en çok süre (milisaniye)" - -msgid "\"xterm\", \"xterm2\", \"sgr\", etc.; type of mouse" -msgstr "\"xterm\", \"xterm2\", \"sgr\" vb.; fare türü" - -msgid "what the mouse pointer looks like in different modes" -msgstr "farklı kiplerde fare imlecinin nice göründüğü" - -msgid "GUI" -msgstr "grafik arabirim" - -msgid "list of font names to be used in the GUI" -msgstr "grafik arabirimde kullanılacak yazıtiplerinin listesi" - -msgid "pair of fonts to be used, for multibyte editing" -msgstr "çoklu bayt düzenlemede kullanılacak yazıtipi eşleşmeleri" - -msgid "list of font names to be used for double-wide characters" -msgstr "çift genişlikli karakterler için kullanılacak yazıtiplerinin listesi" - -msgid "use smooth, antialiased fonts" -msgstr "düzletilmiş yazıtipleri kullan" - -msgid "list of flags that specify how the GUI works" -msgstr "grafik arabirimin nice çalıştığını belirleyen bayraklar listesi" - -msgid "\"icons\", \"text\" and/or \"tooltips\"; how to show the toolbar" -msgstr "\"icons\", \"text\" ve/veya \"tooltips\"; araç çubuğu kipleri " - -msgid "size of toolbar icons" -msgstr "araç çubuğu simgelerinin boyutu" - -msgid "room (in pixels) left above/below the window" -msgstr "pencerenin altında/üstünde bırakılan alan (piksel)" - -msgid "options for text rendering" -msgstr "metin dokuması için seçenekler" - -msgid "use a pseudo-tty for I/O to external commands" -msgstr "dış komutlara, girdi çıktı için yalancı-tty kullan" - -msgid "" -"\"last\", \"buffer\" or \"current\": which directory used for the file " -"browser" -msgstr "" -"\"last\", \"buffer\" veya \"current\"; dosya tarayıcısı için hangi dizinin " -"kullanıldığı" - -msgid "language to be used for the menus" -msgstr "menüler için kullanılan dil" - -msgid "maximum number of items in one menu" -msgstr "bir menüdeki en çok öge sayısı" - -msgid "\"no\", \"yes\" or \"menu\"; how to use the ALT key" -msgstr "\"no\", \"yes\" veya \"menu\"; ALT düğmesinin nice kullanılacağı" - -msgid "number of pixel lines to use between characters" -msgstr "karakterler arasında kullanılacak piksel satırları sayısı" - -msgid "delay in milliseconds before a balloon may pop up" -msgstr "bir balonun patlamadan önceki gecikme (milisaniye)" - -msgid "use balloon evaluation in the GUI" -msgstr "grafik arabirimde balon değerlendirme kullan" - -msgid "use balloon evaluation in the terminal" -msgstr "uçbirimde balon değerlendirme kullan" - -msgid "expression to show in balloon eval" -msgstr "balon değerlendirmesinde gösterilecek ifade" - -msgid "printing" -msgstr "yazdırma" - -msgid "list of items that control the format of :hardcopy output" -msgstr ":hardcopy çıktısının biçimini denetleyen ögelerin listesi" - -msgid "name of the printer to be used for :hardcopy" -msgstr ":hardcopy için kullanılan yazıcının adı" - -msgid "expression used to print the PostScript file for :hardcopy" -msgstr ":hardcopy için PostScript dosyasını yazdırmada kullanılan ifade" - -msgid "name of the font to be used for :hardcopy" -msgstr ":hardcopy için kullanılan yazıtipinin adı" - -msgid "format of the header used for :hardcopy" -msgstr ":hardcopy için kullanılan üstbilginin biçimi" - -msgid "encoding used to print the PostScript file for :hardcopy" -msgstr ":hardcopy için Postscript dosyasını yazdırmada kullanılan kodlama" - -msgid "the CJK character set to be used for CJK output from :hardcopy" -msgstr ":hardcopy'deki ÇJK çıktısında kullanılan ÇJK karakter seti" - -msgid "list of font names to be used for CJK output from :hardcopy" -msgstr ":hardcopy'deki ÇJK çıktısında kullanılan ÇJK yazıtipi" - -msgid "messages and info" -msgstr "iletiler ve bilgi" - -msgid "add 's' flag in 'shortmess' (don't show search message)" -msgstr "'shortness'daki 's' bayrağını ekle (arama iletisini gösterme)" - -msgid "list of flags to make messages shorter" -msgstr "iletileri kısalaştırmak için kullanılan bayraklar listesi" - -msgid "show (partial) command keys in the status line" -msgstr "durum satırında (kısmi) komut düğmelerini göster" - -msgid "display the current mode in the status line" -msgstr "durum satırında geçerli kipi görüntüle" - -msgid "show cursor position below each window" -msgstr "her pencerenin altında imleç konumunu göster" - -msgid "alternate format to be used for the ruler" -msgstr "cetvel için kullanılan alternatif biçim" - -msgid "threshold for reporting number of changed lines" -msgstr "değiştirilmiş satırların sayısını raporlama eşiği" - -msgid "the higher the more messages are given" -msgstr "ne kadar yüksek olursa o kadar çok ileti olur" - -msgid "file to write messages in" -msgstr "iletilerin içine yazılacağı dosya" - -msgid "pause listings when the screen is full" -msgstr "ekran doluyken listelemeleri duraklat" - -msgid "start a dialog when a command fails" -msgstr "bir komut başarısız olursa iletişim kutusu göster" - -msgid "ring the bell for error messages" -msgstr "hata iletilerinde zili çal" - -msgid "use a visual bell instead of beeping" -msgstr "bipleme yerine görsel zil kullan" - -msgid "do not ring the bell for these reasons" -msgstr "bu nedenlerle zili çalma" - -msgid "list of preferred languages for finding help" -msgstr "yardım için yeğlenen diller listesi" - -msgid "selecting text" -msgstr "metin seçme" - -msgid "\"old\", \"inclusive\" or \"exclusive\"; how selecting text behaves" -msgstr "\"old\", \"inclusive\" veya \"exclusive\"; metin seçim davranışı" - -msgid "" -"\"mouse\", \"key\" and/or \"cmd\"; when to start Select mode\n" -"instead of Visual mode" -msgstr "" -"\"mouse\", \"key\", ve/veya \"cmd\"; Görsel kip yerine Seçim\n" -"kipinin başlatılacağı zaman" - -msgid "" -"\"unnamed\" to use the * register like unnamed register\n" -"\"autoselect\" to always put selected text on the clipboard" -msgstr "" -"\"unnamed\": * yazmacını adsız yazmaç gibi kullan\n" -"\"autoselect\": seçili metni her zaman panoya koy" - -msgid "\"startsel\" and/or \"stopsel\"; what special keys can do" -msgstr "\"startsel\" ve/veya \"stopsel\"; özel düğmelerin işlevleri" - -msgid "editing text" -msgstr "metin düzenleme" - -msgid "maximum number of changes that can be undone" -msgstr "geri alınabilecek en çok değişiklik sayısı" - -msgid "automatically save and restore undo history" -msgstr "geri al geçmişini kendiliğinden kaydet ve eski haline getir" - -msgid "list of directories for undo files" -msgstr "geri al dosyaları için dizinler listesi" - -msgid "maximum number lines to save for undo on a buffer reload" -msgstr "" -"arabellek yeniden yüklemesinde geri al için kaydedilecek\n" -"en çok satır sayısı" - -msgid "changes have been made and not written to a file" -msgstr "yapılan; ancak bir dosyaya yazılmayan değişiklikler" - -msgid "buffer is not to be written" -msgstr "arabellek, yazım için değil" - -msgid "changes to the text are possible" -msgstr "metne değişiklik yapımı olanaklı" - -msgid "line length above which to break a line" -msgstr "sonrasında yeni satır yapılacak satır uzunluğu" - -msgid "margin from the right in which to break a line" -msgstr "sonrasında yeni satır yapılacak sağ kenar boşluğu" - -msgid "specifies what <BS>, CTRL-W, etc. can do in Insert mode" -msgstr "<BS>, CTRL-W, vb. Ekleme kipinde ne yapabileceğini belirtir" - -msgid "definition of what comment lines look like" -msgstr "yorum satırlarının nice görüneceğinin tanımı" - -msgid "list of flags that tell how automatic formatting works" -msgstr "" -"kendiliğinden biçimlendirmenin nice çalıştığını anlatan\n" -"bayraklar listesi" - -msgid "pattern to recognize a numbered list" -msgstr "numaralandırılmış bir listeyi tanımak için dizgi" - -msgid "expression used for \"gq\" to format lines" -msgstr "satırları biçimlendirmek için \"gq\" için kullanılan ifade" - -msgid "specifies how Insert mode completion works for CTRL-N and CTRL-P" -msgstr "" -"Ekleme kipi tamamlamasının CTRL-N ve CTRL-P için nice çalıştığını\n" -"belirler" - -msgid "whether to use a popup menu for Insert mode completion" -msgstr "Ekleme kipi tamamlaması için açılır menü kullanımı" - -msgid "options for the Insert mode completion info popup" -msgstr "Ekleme kipi tamamlama açılır penceresi için seçenekler" - -msgid "maximum height of the popup menu" -msgstr "açılır menünün en çok yüksekliği" - -msgid "minimum width of the popup menu" -msgstr "açılır menünün en çok genişliği" - -msgid "user defined function for Insert mode completion" -msgstr "Ekleme kipi tamamlaması için kullanıcı tanımlı işlev" - -msgid "function for filetype-specific Insert mode completion" -msgstr "dosya türüne özel Ekleme kipi tamamlaması için işlev" - -msgid "list of dictionary files for keyword completion" -msgstr "anahtar sözcük tamamlaması için sözlük dosyaları listesi" - -msgid "list of thesaurus files for keyword completion" -msgstr "" -"anahtar sözcük tamamlaması için eşanlamlılar sözlüğü dosyaları\n" -"listesi" - -msgid "adjust case of a keyword completion match" -msgstr "anahtar sözcük tamamlama eşleşmesinin BÜYÜK/küçük harfini ayarla" - -msgid "enable entering digraphs with c1 <BS> c2" -msgstr "c1 <BS> c2 ile ikili harflerin girilmesini etkinleştir" - -msgid "the \"~\" command behaves like an operator" -msgstr "\"~\" komutu bir işleç gibi davranır" - -msgid "function called for the \"g@\" operator" -msgstr "\"g@\" işleci için çağrılan işlev" - -msgid "when inserting a bracket, briefly jump to its match" -msgstr "bir ayraç eklendiğinde hemen eşine atla" - -msgid "tenth of a second to show a match for 'showmatch'" -msgstr "bir 'showmatch' eşleşmesini göstermek için saniyenin onda biri" - -msgid "list of pairs that match for the \"%\" command" -msgstr "\"%\" komutu için eşleşen eşleşmelerin listesi" - -msgid "use two spaces after '.' when joining a line" -msgstr "bir satırı birleştirirken '.' sonrası iki boşluk kullan" - -msgid "" -"\"alpha\", \"octal\", \"hex\", \"bin\" and/or \"unsigned\"; number formats\n" -"recognized for CTRL-A and CTRL-X commands" -msgstr "" -"\"alpha\", \"octal\", \"hex\", \"bin\" ve/veya \"unsigned\"; CTRL-A ve\n" -"CTRL-X komutları için tanınan sayı biçimleri" - -msgid "tabs and indenting" -msgstr "sekmeler ve girintileme" - -msgid "number of spaces a <Tab> in the text stands for" -msgstr "metinde bir <Tab>'ın denk olduğu boşluk sayısı" - -msgid "number of spaces used for each step of (auto)indent" -msgstr "her bir kendiliğinden girintileme için kullanılan boşluk sayısı" - -msgid "list of number of spaces a tab counts for" -msgstr "bir sekmenin denk olduğu boşlukların sayısının listesi" - -msgid "list of number of spaces a soft tabsstop counts for" -msgstr "bir yumuşak sekmedurağının denk olduğu boşlukların sayısı listesi" - -msgid "a <Tab> in an indent inserts 'shiftwidth' spaces" -msgstr "bir girintideki <Tab>, 'shiftwidth' kadar boşluk ekler" - -msgid "if non-zero, number of spaces to insert for a <Tab>" -msgstr "eğer sıfırdan farklıysa, bir <Tab> için eklenecek boşluk sayısı" - -msgid "round to 'shiftwidth' for \"<<\" and \">>\"" -msgstr "\"<<\" ve \">>\" için 'shiftwidth'e yuvarla" - -msgid "expand <Tab> to spaces in Insert mode" -msgstr "Ekleme kipinde <Tab>'ı boşluklara genişlet" - -msgid "automatically set the indent of a new line" -msgstr "yeni bir satırın girintisini kendiliğinden ayarla" - -msgid "do clever autoindenting" -msgstr "akıllı kendiliğinden girintileme yap" - -msgid "enable specific indenting for C code" -msgstr "C kodu için özel girintilemeyi etkinleştir" - -msgid "options for C-indenting" -msgstr "C girintilemesi için seçenekler" - -msgid "keys that trigger C-indenting in Insert mode" -msgstr "Ekleme kipinde C girintilemesini tetikleyen düğmeler" - -msgid "list of words that cause more C-indent" -msgstr "daha çok C girintilemesine neden olan sözcüklerin listesi" - -msgid "expression used to obtain the indent of a line" -msgstr "bir satırın girintisini elde etmek için kullanılan ifade" - -msgid "keys that trigger indenting with 'indentexpr' in Insert mode" -msgstr "Ekleme kipinde 'indentexpr' ile girintilemeyi tetikleyen düğmeler" - -msgid "copy whitespace for indenting from previous line" -msgstr "bir önceki satırdan girintileme için boşlukları kopyala" - -msgid "preserve kind of whitespace when changing indent" -msgstr "girintilemeyi değiştirirken boşluk türünü koru" - -msgid "enable lisp mode" -msgstr "lisp kipini etkinleştir" - -msgid "words that change how lisp indenting works" -msgstr "lisp girintilemesinin nice çalıştığını değiştiren sözcükler" - -msgid "folding" -msgstr "kıvırma" - -msgid "unset to display all folds open" -msgstr "tüm kıvırmaları açık görüntülemek için ayarı kaldır" - -msgid "folds with a level higher than this number will be closed" -msgstr "bu sayıdan daha yüksek düzeyli kıvırmalar kapatılacak" - -msgid "value for 'foldlevel' when starting to edit a file" -msgstr "bir dosyayı düzenlemeye başlarkenki 'foldlevel' değeri" - -msgid "width of the column used to indicate folds" -msgstr "kıvırmaları belirtmek için kullanılan sütunun genişliği" - -msgid "expression used to display the text of a closed fold" -msgstr "kapalı bir kıvırmanın metnini görüntülemek için kullanılan ifade" - -msgid "set to \"all\" to close a fold when the cursor leaves it" -msgstr "imleç ayrıldığında kıvırmayı kapatmak için \"all\" olarak ayarlayın" - -msgid "specifies for which commands a fold will be opened" -msgstr "hangi komutlarda bir kıvırmanın açılacağını belirler" - -msgid "minimum number of screen lines for a fold to be closed" -msgstr "bir kıvırmanın kapatılması için en az ekran satırı sayısı" - -msgid "template for comments; used to put the marker in" -msgstr "yorumlar için şablon; imleyiciyi içine koymak için kullanılır" - -msgid "" -"folding type: \"manual\", \"indent\", \"expr\", \"marker\",\n" -"\"syntax\" or \"diff\"" -msgstr "" -"kıvırma türü: \"manual\", \"indent\", \"expr\", \"marker\",\n" -"\"syntax\" veya \"diff\"" - -msgid "expression used when 'foldmethod' is \"expr\"" -msgstr "'foldmethod' \"expr\" olduğundan kullanılacak ifade" - -msgid "used to ignore lines when 'foldmethod' is \"indent\"" -msgstr "'foldmethod' \"indent\" olduğunda satırları yok saymada kullanılır" - -msgid "markers used when 'foldmethod' is \"marker\"" -msgstr "'foldmethod' \"marker\" olduğunda kullanılan imleyiciler" - -msgid "maximum fold depth for when 'foldmethod' is \"indent\" or \"syntax\"" -msgstr "" -"'foldmethod' \"indent\" veya \"syntax\" olduğunda kullanılan en çok\n" -"kıvırma derinliği" - -msgid "diff mode" -msgstr "diff kipi" - -msgid "use diff mode for the current window" -msgstr "geçerli pencere için diff kipi kullan" - -msgid "options for using diff mode" -msgstr "diff kipi kullanımı için seçenekler" - -msgid "expression used to obtain a diff file" -msgstr "bir diff dosyası elde etmek için kullanılan ifade" - -msgid "expression used to patch a file" -msgstr "bir dosyayı yamalamak için kullanılan ifade" - -msgid "mapping" -msgstr "eşlemleme" - -msgid "maximum depth of mapping" -msgstr "en çok eşlemleme derinliği" - -msgid "recognize mappings in mapped keys" -msgstr "eşlemlenmiş düğmelerdeki eşlemlemeleri tanımla" - -msgid "allow timing out halfway into a mapping" -msgstr "bir eşlemlemenin yarısında zaman aşımına izin ver" - -msgid "allow timing out halfway into a key code" -msgstr "bir düğme kodunun yarısında zaman aşımına izin ver" - -msgid "time in msec for 'timeout'" -msgstr "'timeout' için süre (milisaniye)" - -msgid "time in msec for 'ttimeout'" -msgstr "'ttimeout' için süre (milisaniye)" - -msgid "reading and writing files" -msgstr "dosyaları okuma ve yazma" - -msgid "enable using settings from modelines when reading a file" -msgstr "dosya okurken ayarları kip satırından kullanımı etkinleştir" - -msgid "allow setting expression options from a modeline" -msgstr "ifade seçeneklerini bir kip satırından ayarlamaya izin ver" - -msgid "number of lines to check for modelines" -msgstr "kip satırlarını denetlemede kullanılacak satırların sayısı" - -msgid "binary file editing" -msgstr "ikili dosya düzenleme" - -msgid "last line in the file has an end-of-line" -msgstr "dosyanın son satırında bir satırsonu var" - -msgid "fixes missing end-of-line at end of text file" -msgstr "bir metin dosyasının sonundaki eksik satırsonlarını onarır" - -msgid "prepend a Byte Order Mark to the file" -msgstr "dosyanın önüne bir Bayt Sıralama İmi ekle" - -msgid "end-of-line format: \"dos\", \"unix\" or \"mac\"" -msgstr "satırsonu biçimi: \"dos\", \"unix\" veya \"mac\"" - -msgid "list of file formats to look for when editing a file" -msgstr "bir dosyayı düzenlerken bakılacak dosya biçimler listesi" - -msgid "obsolete, use 'fileformat'" -msgstr "eskimiş, yerine 'fileformat' kullanın" - -msgid "obsolete, use 'fileformats'" -msgstr "eskimiş, yerine 'fileformats' kullanın" - -msgid "writing files is allowed" -msgstr "dosya yazımına izin verilir" - -msgid "write a backup file before overwriting a file" -msgstr "bir dosyanın üzerine yazmadan önce bir yedek dosyası yaz" - -msgid "keep a backup after overwriting a file" -msgstr "bir dosyanın üzerine yazdıktan sonra bir yedek tut" - -msgid "patterns that specify for which files a backup is not made" -msgstr "bir yedeği yapılmayan dosyaları belirleyen dizgi" - -msgid "whether to make the backup as a copy or rename the existing file" -msgstr "yedeğin kopya olarak mı yoksa ad değişikliği ile mi yapılacağı" - -msgid "list of directories to put backup files in" -msgstr "yedek dosyalarının koyulacağı dizinlerin listesi" - -msgid "file name extension for the backup file" -msgstr "yedek dosyası için dosya adı uzantısı" - -msgid "automatically write a file when leaving a modified buffer" -msgstr "değiştirilmiş arabellekten çıkarken dosyayı kendiliğinden yaz" - -msgid "as 'autowrite', but works with more commands" -msgstr "'autowrite' gibi, ancak daha çok komutla çalışır" - -msgid "always write without asking for confirmation" -msgstr "onay beklemeden her zaman yaz" - -msgid "automatically read a file when it was modified outside of Vim" -msgstr "Vim dışında değiştirildiğinde dosyayı kendiliğinden oku" - -msgid "keep oldest version of a file; specifies file name extension" -msgstr "bir dosyanın en eski sürümünü tut; dosya adı uzantısı belirler" - -msgid "forcibly sync the file to disk after writing it" -msgstr "yazımdan sonra dosyayı zorla diske eşitle" - -msgid "use 8.3 file names" -msgstr "8.3 dosya adlarını kullan" - -msgid "encryption method for file writing: zip, blowfish or blowfish2" -msgstr "dosya yazımı için şifreleme yöntemi: zip, blowfish veya blowfish2" - -msgid "the swap file" -msgstr "takas dosyası" - -msgid "list of directories for the swap file" -msgstr "takas dosyası için dizinler listesi" - -msgid "use a swap file for this buffer" -msgstr "bu arabellek için bir takas dosyası kullan" - -msgid "\"sync\", \"fsync\" or empty; how to flush a swap file to disk" -msgstr "" -"\"sync\", \"fsync\", veya boş; bir takas dosyasının diske\n" -"nice floşlanacağı" - -msgid "number of characters typed to cause a swap file update" -msgstr "takas dosyası güncellemesi için yazılması gereken karakter sayısı" - -msgid "time in msec after which the swap file will be updated" -msgstr "takas dosyasının güncelleneceği süre dilimi (milisaniye)" - -msgid "maximum amount of memory in Kbyte used for one buffer" -msgstr "bir arabellek için kullanılacak en çok bellek miktarı (KiB)" - -msgid "maximum amount of memory in Kbyte used for all buffers" -msgstr "tüm arabellekler için kullanılacak en çok bellek miktarı (KiB)" - -msgid "command line editing" -msgstr "komut satırı düzenleme" - -msgid "how many command lines are remembered" -msgstr "kaç tane komut satırının hatırlandığı" - -msgid "key that triggers command-line expansion" -msgstr "komut satırı ifadesi tetikleyen düğme" - -msgid "like 'wildchar' but can also be used in a mapping" -msgstr "'wildchar' gibi; ancak bir eşlemleme içinde kullanılabilir" - -msgid "specifies how command line completion works" -msgstr "komut satırı tamamlamasının nasıl çalıştığını belirtir" - -msgid "empty or \"tagfile\" to list file name of matching tags" -msgstr "eşleşen etiketlerin dosya adını listelemek için boş veya \"tagfile\"" - -msgid "list of file name extensions that have a lower priority" -msgstr "düşük öncelikli dosya adı uzantılarının listesi" - -msgid "list of file name extensions added when searching for a file" -msgstr "bir dosya ararken eklenen dosya adı uzantılarının listesi" - -msgid "list of patterns to ignore files for file name completion" -msgstr "dosya adı tamamlaması için yok sayılacak dizgelerin listesi" - -msgid "ignore case when using file names" -msgstr "dosya adları kullanırken BÜYÜK/küçük harf yok say" - -msgid "ignore case when completing file names" -msgstr "dosya adları tamamlarken BÜYÜK/küçük harf yok say" - -msgid "command-line completion shows a list of matches" -msgstr "komut satırı tamamlaması, eşleşmelerin bir listesini gösterir" - -msgid "key used to open the command-line window" -msgstr "komut satırı penceresini açmak için kullanılan düğme" - -msgid "height of the command-line window" -msgstr "komut satırı penceresinin yüksekliği" - -msgid "executing external commands" -msgstr "dış komutları çalıştırma" - -msgid "name of the shell program used for external commands" -msgstr "dış komutlar için kullanılan kabuk programının adı" - -msgid "when to use the shell or directly execute a command" -msgstr "ne zaman kabuğu kullanmalı veya doğrudan bir komut çalıştırmalı" - -msgid "character(s) to enclose a shell command in" -msgstr "bir kabuk komutunu çevreleyen karakter(ler)" - -msgid "like 'shellquote' but include the redirection" -msgstr "'shellquote' gibi; ancak yeniden yönlendirmeyi içer" - -msgid "characters to escape when 'shellxquote' is (" -msgstr "'shellxquote' ( iken kaçırılacak karakterler" - -msgid "argument for 'shell' to execute a command" -msgstr "bir komut çalıştırmak için 'shell' için argüman" - -msgid "used to redirect command output to a file" -msgstr "komut çıktısını bir dosyaya yeniden yönlendirmek için kullanılır" - -msgid "use a temp file for shell commands instead of using a pipe" -msgstr "" -"bir veri yolu kullanımı yerine kabuk komutları için geçici\n" -"bir dosya kullan" - -msgid "program used for \"=\" command" -msgstr "\"=\" komutu için kullanılan program" - -msgid "program used to format lines with \"gq\" command" -msgstr "\"gq\" komutu ile satır biçimlemek için kullanılan program" - -msgid "program used for the \"K\" command" -msgstr "\"K\" komutu için kullanılan program" - -msgid "warn when using a shell command and a buffer has changes" -msgstr "" -"bir kabuk komutu kullanılıyorsa ve arabellekte değişiklikler\n" -"varsa uyar" - -msgid "running make and jumping to errors (quickfix)" -msgstr "make çalıştırma ve hatalara atlama (hızlı düzelt)" - -msgid "name of the file that contains error messages" -msgstr "hata iletileri içeren dosyanın adı" - -msgid "list of formats for error messages" -msgstr "hata iletileri için biçim listesi" - -msgid "program used for the \":make\" command" -msgstr "\":make\" komutu için kullanılan program" - -msgid "string used to put the output of \":make\" in the error file" -msgstr "" -"\":make\" komutunun çıktısını hata dosyasına koymak için\n" -"kullanılan dizi" - -msgid "name of the errorfile for the 'makeprg' command" -msgstr "'makeprg' komutu için hata dosyası adı" - -msgid "program used for the \":grep\" command" -msgstr "\":grep\" komutu için kullanılan program" - -msgid "list of formats for output of 'grepprg'" -msgstr "'grepprg' çıktısı için kullanılan biçimlerin listesi" - -msgid "encoding of the \":make\" and \":grep\" output" -msgstr "\":make\" ve \":grep\" çıktılarının kodlaması" - -msgid "function to display text in the quickfix window" -msgstr "hızlı düzelt içinde metin düzenlemek için işlev" - -msgid "system specific" -msgstr "sisteme özel" - -msgid "use forward slashes in file names; for Unix-like shells" -msgstr "dosya adlarında eğik çizgi kullan; Unix tarzı kabuklar için" - -msgid "specifies slash/backslash used for completion" -msgstr "tamamlama için kullanılan eğik/ters eğik çizgiyi belirler" - -msgid "language specific" -msgstr "dile özel ayarlar" - -msgid "specifies the characters in a file name" -msgstr "bir dosya adındaki karakterleri belirtir" - -msgid "specifies the characters in an identifier" -msgstr "bir tanımlayıcıdaki karakterleri belirler" - -msgid "specifies the characters in a keyword" -msgstr "bir anahtar sözcükteki karakterleri belirler" - -msgid "specifies printable characters" -msgstr "yazdırılabilir karakterleri belirler" - -msgid "specifies escape characters in a string" -msgstr "bir dizideki kaçış karakterlerini belirler" - -msgid "display the buffer right-to-left" -msgstr "arabelleği sağdan sola görüntüle" - -msgid "when to edit the command-line right-to-left" -msgstr "komut satırının ne zaman sağdan sola düzenleneceği" - -msgid "insert characters backwards" -msgstr "karakterleri geriye doğru ekle" - -msgid "allow CTRL-_ in Insert and Command-line mode to toggle 'revins'" -msgstr "" -"'revins' açıp kapatmak için Ekleme ve Komut Satırı kipinde\n" -"CTRL-_ izin ver" - -msgid "the ASCII code for the first letter of the Hebrew alphabet" -msgstr "İbran abecesinin ilk harfinin ASCII kodu" - -msgid "use Hebrew keyboard mapping" -msgstr "İbranca klavye eşlemlemesini kullan" - -msgid "use phonetic Hebrew keyboard mapping" -msgstr "fonetik İbranca klavye eşlemlemesini kullan" - -msgid "prepare for editing Arabic text" -msgstr "Arapça metni düzenleme için hazırlan" - -msgid "perform shaping of Arabic characters" -msgstr "Arapça karakterlerin şekillendirmesini gerçekleştir" - -msgid "terminal will perform bidi handling" -msgstr "sağdan sola yazımı uçbirim gerçekleştirecek" - -msgid "name of a keyboard mapping" -msgstr "bir klavye eşlemlemesinin adı" - -msgid "list of characters that are translated in Normal mode" -msgstr "Normal kipte çevrilen karakterlerin listesi" - -msgid "apply 'langmap' to mapped characters" -msgstr "eşlemlenen karakterlere 'langmap' uygula" - -msgid "when set never use IM; overrules following IM options" -msgstr "" -"ayarlandığında hiçbir zaman IM kullanma; aşağıdaki IM seçeneklerini geçersiz " -"kılar" - -msgid "in Insert mode: 1: use :lmap; 2: use IM; 0: neither" -msgstr "Ekleme kipinde: 1: :lmap kullan; 2; IM kullan; 0: hiçbiri" - -msgid "input method style, 0: on-the-spot, 1: over-the-spot" -msgstr "girdi yöntemi stili, 0: on-the-spot, 1: over-the-spot" - -msgid "entering a search pattern: 1: use :lmap; 2: use IM; 0: neither" -msgstr "bir arama dizgisi gir: 1: :lmap kullan; 2: IM kullan; 0: hiçbiri" - -msgid "when set always use IM when starting to edit a command line" -msgstr "" -"ayarlandığında, bir komut satırı düzenlemeye başlarken her zaman IM kullan" - -msgid "function to obtain IME status" -msgstr "IME durumunu elde etmek için işlev" - -msgid "function to enable/disable IME" -msgstr "IME'yi etkinleştirmek/devre dışı bırakmak için işlev" - -msgid "multi-byte characters" -msgstr "çoklu bayt karakterler" - -msgid "" -"character encoding used in Vim: \"latin1\", \"utf-8\",\n" -"\"euc-jp\", \"big5\", etc." -msgstr "" -"Vim'de kullanılan karakter kodlamaları: \"latin1\", \"utf-8\",\n" -"\"euc-jp\", \"big5\" gibi" - -msgid "character encoding for the current file" -msgstr "geçerli dosya için karakter kodlaması" - -msgid "automatically detected character encodings" -msgstr "karakter kodlamasını kendiliğinden algıla" - -msgid "character encoding used by the terminal" -msgstr "uçbirim tarafından kullanılan karakter kodlaması" - -msgid "expression used for character encoding conversion" -msgstr "karakter kodlaması dönüşümü için kullanılan ifade" - -msgid "delete combining (composing) characters on their own" -msgstr "birleştiren (oluşturucu) karakterleri kendi başına kullan" - -msgid "maximum number of combining (composing) characters displayed" -msgstr "en çok görüntülenen birleştiren (oluşturucu) karakterlerin sayısı" - -msgid "key that activates the X input method" -msgstr "X girdi yöntemini etkinleştiren düğme" - -msgid "width of ambiguous width characters" -msgstr "belirsiz genişlikli karakterlerin genişliği" - -msgid "emoji characters are full width" -msgstr "emoji karakterleri tam genişliklidir" - -msgid "various" -msgstr "çeşitli" - -msgid "" -"when to use virtual editing: \"block\", \"insert\", \"all\"\n" -"and/or \"onemore\"" -msgstr "" -"ne zaman sanal düzenleme kullanmalı: \"block\", \"insert\",\n" -"\"all\" ve/veya \"onemore\"" - -msgid "list of autocommand events which are to be ignored" -msgstr "yok sayılacak otokomut olayları" - -msgid "load plugin scripts when starting up" -msgstr "başlarken eklenti betiklerini yükle" - -msgid "enable reading .vimrc/.exrc/.gvimrc in the current directory" -msgstr "geçerli dizinde .vimrc/.exrc/.gvimrc okumayı etkinleştir" - -msgid "safer working with script files in the current directory" -msgstr "geçerli dizinde betik dosyalarıyla daha güvenli çalışma" - -msgid "use the 'g' flag for \":substitute\"" -msgstr "\":substitute\" için 'g' bayrağını kullan" - -msgid "'g' and 'c' flags of \":substitute\" toggle" -msgstr "\":substitute\" açma/kapama düğmesinin 'g' ve 'c' bayrakları" - -msgid "allow reading/writing devices" -msgstr "aygıtları okumaya/yazmaya izin ver" - -msgid "maximum depth of function calls" -msgstr "işlev çağrılarının en çok derinliği" - -msgid "list of words that specifies what to put in a session file" -msgstr "bir oturum dosyasına ne koyulacağını belirleyen sözcükler listesi" - -msgid "list of words that specifies what to save for :mkview" -msgstr ":mkview için neyin kaydedileceğini belirleyen sözcükler listesi" - -msgid "directory where to store files with :mkview" -msgstr ":mkview ile dosyaların depolanacağı dizin" - -msgid "list that specifies what to write in the viminfo file" -msgstr "viminfo dosyasına nelerin yazılacağını belirleyen liste" - -msgid "file name used for the viminfo file" -msgstr "viminfo dosyası için kullanılan dosya adı" - -msgid "what happens with a buffer when it's no longer in a window" -msgstr "bir arabellek artık bir pencerede değilken ne olacağı" - -msgid "empty, \"nofile\", \"nowrite\", \"quickfix\", etc.: type of buffer" -msgstr "boş, \"nofile\", \"nowrite\", \"quickfix\" vb.: arabellek türü" - -msgid "whether the buffer shows up in the buffer list" -msgstr "arabelleğin, arabellek listesinde görünüp görünmeyeceği" - -msgid "set to \"msg\" to see all error messages" -msgstr "tüm hata iletilerini görmek için \"msg\" olarak ayarlayın" - -msgid "whether to show the signcolumn" -msgstr "işaret sütununun görünüp görünmeyeceği" - -msgid "interval in milliseconds between polls for MzScheme threads" -msgstr "MzScheme iş parçacıkları için anketler arasındaki süre (milisaniye)" - -msgid "name of the Lua dynamic library" -msgstr "Lua devingen kitaplığının adı" - -msgid "name of the Perl dynamic library" -msgstr "Perl devingen kitaplığının adı" - -msgid "whether to use Python 2 or 3" -msgstr "Python 2 veya 3 mü kullanılacağı" - -msgid "name of the Python 2 dynamic library" -msgstr "Python 2 devingen kitaplığının adı" +msgid "Already only one window" +msgstr "Zaten tek pencere" -msgid "name of the Python 2 home directory" -msgstr "Python 2 ev dizininin adı" +msgid "E441: There is no preview window" +msgstr "E441: Önizleme penceresi yok" -msgid "name of the Python 3 dynamic library" -msgstr "Python 3 devingen kitaplığının adı" +msgid "E442: Can't split topleft and botright at the same time" +msgstr "E442: Üst sol ve alt sağ pencereler aynı anda bölünemez" -msgid "name of the Python 3 home directory" -msgstr "Python 3 ev dizininin adı" +msgid "E443: Cannot rotate when another window is split" +msgstr "E443: Başka bir pencere bölünmüşken döndürme yapılamaz" -msgid "name of the Ruby dynamic library" -msgstr "Ruby devingen kitaplığının adı" +msgid "E444: Cannot close last window" +msgstr "E444: Son pencere kapatılamıyor" -msgid "name of the Tcl dynamic library" -msgstr "Tcl devingen kitaplığının adı" +msgid "E814: Cannot close window, only autocmd window would remain" +msgstr "E814: Pencere kapatılamıyor, yalnızca otokomut penceresi açık kalır" -msgid "name of the MzScheme dynamic library" -msgstr "MzScheme devingen kitaplığının adı" +msgid "E445: Other window contains changes" +msgstr "E445: Diğer pencerede değişiklikler var" -msgid "name of the MzScheme GC dynamic library" -msgstr "MzScheme GC devingen kitaplığının adı" +msgid "E446: No file name under cursor" +msgstr "E446: İmleç altında bir dosya adı yok" diff --git a/src/nvim/po/zh_CN.UTF-8.po b/src/nvim/po/zh_CN.UTF-8.po index 9a8cd38f5e..70c1389d7f 100644 --- a/src/nvim/po/zh_CN.UTF-8.po +++ b/src/nvim/po/zh_CN.UTF-8.po @@ -32,7 +32,7 @@ msgstr "选项参数后的内容无效" #: ../api/private/helpers.c:204 msgid "internal error: unknown option type" -msgstr "" +msgstr "内部错误:未知的选项类型" #: ../buffer.c:92 msgid "[Location List]" @@ -44,7 +44,7 @@ msgstr "[Quickfix 列表]" #: ../buffer.c:94 msgid "E855: Autocommands caused command to abort" -msgstr "" +msgstr "E855: 自动命令导致命令被停止" #: ../buffer.c:135 msgid "E82: Cannot allocate any buffer, exiting..." @@ -336,7 +336,7 @@ msgstr "E105: 不是在脚本文件中使用 :loadkeymap " #: ../digraph.c:1821 msgid "E791: Empty keymap entry" -msgstr "" +msgstr "E791: 空的键位映射项" #: ../edit.c:82 msgid " Keyword completion (^N^P)" @@ -401,11 +401,11 @@ msgstr "已到段落结尾" #: ../edit.c:101 msgid "E839: Completion function changed window" -msgstr "" +msgstr "E839: 补全函数更改了窗口" #: ../edit.c:102 msgid "E840: Completion function deleted text" -msgstr "" +msgstr "E840: 补全函数删除了文本" #: ../edit.c:1847 msgid "'dictionary' option is empty" @@ -1209,6 +1209,9 @@ msgid "" "It may still be possible to write it.\n" "Do you wish to try?" msgstr "" +"此文件权限 \"%s\" 是只读的。\n" +"它仍然有可能被写入。\n" +"你想继续尝试吗?" #: ../ex_cmds.c:2451 #, fuzzy, c-format @@ -1519,7 +1522,7 @@ msgstr "环境变量" #: ../ex_cmds2.c:2773 msgid "error handler" -msgstr "" +msgstr "错误的处理程序" #: ../ex_cmds2.c:3020 msgid "W15: Warning: Wrong line separator, ^M may be missing" @@ -1836,7 +1839,7 @@ msgstr "捕获异常: %s" #: ../ex_eval.c:676 #, c-format msgid "%s made pending" -msgstr "" +msgstr "%s 待定" #: ../ex_eval.c:679 #, fuzzy, c-format @@ -1846,7 +1849,7 @@ msgstr " 已返回\n" #: ../ex_eval.c:683 #, c-format msgid "%s discarded" -msgstr "" +msgstr "%s 舍弃" #: ../ex_eval.c:708 msgid "Exception" @@ -2005,7 +2008,7 @@ msgstr "E199: 活动窗口或缓冲区已被删除" #: ../file_search.c:203 msgid "E854: path too long for completion" -msgstr "" +msgstr "E854: 补全用的路径太长" #: ../file_search.c:446 #, c-format @@ -2432,7 +2435,7 @@ msgstr "--已删除--" #: ../fileio.c:5732 #, c-format msgid "auto-removing autocommand: %s <buffer=%d>" -msgstr "" +msgstr "自动删除自动命令: %s <buffer=%d>" #. the group doesn't exist #: ../fileio.c:5772 @@ -2671,7 +2674,7 @@ msgstr "E49: 无效的滚动大小" #: ../globals.h:1021 msgid "E901: Job table is full" -msgstr "" +msgstr "E901: 任务表已经满" #: ../globals.h:1024 #, c-format @@ -3215,6 +3218,7 @@ msgstr "%-5s: %-30s (用法: %s)" #: ../if_cscope.c:1155 msgid "" "\n" +" a: Find assignments to this symbol\n" " c: Find functions calling this function\n" " d: Find functions called by this function\n" " e: Find this egrep pattern\n" @@ -3224,6 +3228,16 @@ msgid "" " s: Find this C symbol\n" " t: Find this text string\n" msgstr "" +"\n" +" a: 搜索对此符号的赋值\n" +" c: 搜索调用此函数的函数\n" +" d: 搜索此函数调用的函数\n" +" e: 搜索此 egrep 模式\n" +" f: 搜索此文件\n" +" g: 搜索此定义\n" +" i: 搜索包含此文件的文件\n" +" s: 搜索此 C 符号\n" +" t: 搜索此文本字符串\n" #: ../if_cscope.c:1226 msgid "E568: duplicate cscope database not added" @@ -3455,7 +3469,7 @@ msgstr "-N\t\t\t不完全兼容传统的 Vi: 'nocompatible'" #: ../main.c:2215 msgid "-V[N][fname]\t\tBe verbose [level N] [log messages to fname]" -msgstr "" +msgstr "-V[N][fname]\t\t详细 [level N] [log messages to fname]" #: ../main.c:2216 msgid "-D\t\t\tDebugging mode" @@ -3547,7 +3561,7 @@ msgstr "-W <scriptout>\t将所有输入的命令写入到文件 <scriptout>" #: ../main.c:2240 msgid "--startuptime <file>\tWrite startup timing messages to <file>" -msgstr "" +msgstr "--startuptime <file>\t将启动时间信息写入到文件 <file>" #: ../main.c:2242 msgid "-i <viminfo>\t\tUse <viminfo> instead of .viminfo" @@ -3738,7 +3752,7 @@ msgstr "" #: ../memline.c:945 msgid " has been damaged (page size is smaller than minimum value).\n" -msgstr "" +msgstr "已损坏(页面大小小于最小值)。\n" #: ../memline.c:974 #, c-format @@ -3844,7 +3858,7 @@ msgstr "再运行 diff 与原文件比较以检查是否有改变)\n" #: ../memline.c:1254 msgid "Recovery completed. Buffer contents equals file contents." -msgstr "" +msgstr "恢复完成。缓冲区内容与文件内容相同。" #: ../memline.c:1255 #, fuzzy @@ -4553,7 +4567,7 @@ msgstr "" #: ../option.c:1238 msgid "%<%f%h%m%=Page %N" -msgstr "" +msgstr "%<%f%h%m%=页 %N" #: ../option.c:1574 msgid "Thanks for flying Vim" @@ -4574,7 +4588,7 @@ msgstr "E520: 不允许在 modeline 中使用" #: ../option.c:2815 msgid "E846: Key code not set" -msgstr "" +msgstr "E846: 未设置键位代码" #: ../option.c:2924 msgid "E521: Number required after =" @@ -4599,11 +4613,11 @@ msgstr "E589: 'backupext' 和 'patchmode' 相等" #: ../option.c:3964 msgid "E834: Conflicts with value of 'listchars'" -msgstr "" +msgstr "E834: 与'listchars'中的值发生冲突" #: ../option.c:3966 msgid "E835: Conflicts with value of 'fillchars'" -msgstr "" +msgstr "E835: 与'fillchars'中的值冲突" #: ../option.c:4163 msgid "E524: Missing colon" @@ -4978,49 +4992,50 @@ msgid "" "E864: \\%#= can only be followed by 0, 1, or 2. The automatic engine will be " "used " msgstr "" +"E864: \\%#= 后面只能是0,1,或者2。自动引擎将会被使用" #: ../regexp_nfa.c:239 msgid "E865: (NFA) Regexp end encountered prematurely" -msgstr "" +msgstr "E865: (NFA) 过早地遇到了正则表达式的结尾" #: ../regexp_nfa.c:240 #, c-format msgid "E866: (NFA regexp) Misplaced %c" -msgstr "" +msgstr "E866: (NFA regexp) %c 放错了位置" #: ../regexp_nfa.c:242 #, c-format msgid "E877: (NFA regexp) Invalid character class: %<PRId64>" -msgstr "" +msgstr "E877: (NFA regexp) 不可用的字符类: %<PRId64>" #: ../regexp_nfa.c:1261 #, c-format msgid "E867: (NFA) Unknown operator '\\z%c'" -msgstr "" +msgstr "E867: (NFA) 未知的操作符 '\\z%c'" #: ../regexp_nfa.c:1387 #, c-format msgid "E867: (NFA) Unknown operator '\\%%%c'" -msgstr "" +msgstr "E867: (NFA) 未知的操作符 '\\%%%c'" #: ../regexp_nfa.c:1802 #, c-format msgid "E869: (NFA) Unknown operator '\\@%c'" -msgstr "" +msgstr "E869: (NFA) 未知的操作符 '\\@%c'" #: ../regexp_nfa.c:1831 msgid "E870: (NFA regexp) Error reading repetition limits" -msgstr "" +msgstr "E870: (NFA regexp) 读取重复限制时出错" #. Can't have a multi follow a multi. #: ../regexp_nfa.c:1895 msgid "E871: (NFA regexp) Can't have a multi follow a multi !" -msgstr "" +msgstr "E871: (NFA regexp) 不能多个跟多个!" #. Too many `(' #: ../regexp_nfa.c:2037 msgid "E872: (NFA regexp) Too many '('" -msgstr "" +msgstr "E872: (NFA regexp) 太多 '('" #: ../regexp_nfa.c:2042 #, fuzzy @@ -5029,31 +5044,32 @@ msgstr "E50: 太多 \\z(" #: ../regexp_nfa.c:2066 msgid "E873: (NFA regexp) proper termination error" -msgstr "" +msgstr "E873: (NFA regexp) 未适当终止" #: ../regexp_nfa.c:2599 msgid "E874: (NFA) Could not pop the stack !" -msgstr "" +msgstr "E874: (NFA) 无法出栈!" #: ../regexp_nfa.c:3298 msgid "" "E875: (NFA regexp) (While converting from postfix to NFA), too many states " "left on stack" -msgstr "" +msgstr "E875: (NFA regexp) (从后缀转换到 NFA 时),栈上遗留了太多状态" #: ../regexp_nfa.c:3302 msgid "E876: (NFA regexp) Not enough space to store the whole NFA " -msgstr "" +msgstr "E876: (NFA regexp) 没有足够的空间存储整个NFA " #: ../regexp_nfa.c:4571 ../regexp_nfa.c:4869 msgid "" "Could not open temporary log file for writing, displaying on stderr ... " msgstr "" +"无法打开临时日志文件进行写入,显示在 stderr 中..." #: ../regexp_nfa.c:4840 #, c-format msgid "(NFA) COULD NOT OPEN %s !" -msgstr "" +msgstr "(NFA) 不能打开 %s !" #: ../regexp_nfa.c:6049 #, fuzzy @@ -5218,6 +5234,9 @@ msgid "" "# Last %sSearch Pattern:\n" "~" msgstr "" +"\n" +"# 最后 %s搜索模式:\n" +"~" #: ../spell.c:951 msgid "E759: Format error in spell file" @@ -5314,14 +5333,16 @@ msgstr "%s 第 %d 行,在使用标志后出现 FLAG: %s" msgid "" "Defining COMPOUNDFORBIDFLAG after PFX item may give wrong results in %s line " "%d" -msgstr "" +msgstr "在 PFX 项之后定义 COMPOUNDFORBIDFLAG (%s 第%d行)可能会给出错误的结果" +"%d" #: ../spell.c:4731 #, c-format msgid "" "Defining COMPOUNDPERMITFLAG after PFX item may give wrong results in %s line " "%d" -msgstr "" +msgstr "在 PFX 项之后定义 COMPOUNDPERMITFLAG (%s 第%d行)可能会给出错误的结果" +"%d" #: ../spell.c:4747 #, fuzzy, c-format @@ -5486,7 +5507,7 @@ msgstr "读取单词文件 %s ……" #: ../spell.c:6155 #, c-format msgid "Duplicate /encoding= line ignored in %s line %d: %s" -msgstr "" +msgstr "%s 第 %ld 行,重复的 /encoding= 行已被忽略: %s" #: ../spell.c:6159 #, c-format @@ -5641,17 +5662,17 @@ msgstr "E778: 看起来不像是 .sug 文件: %s" #: ../spell.c:9282 #, c-format msgid "E779: Old .sug file, needs to be updated: %s" -msgstr "" +msgstr "E779: 旧的.sug 文件,需要更新: %s" #: ../spell.c:9286 #, c-format msgid "E780: .sug file is for newer version of Vim: %s" -msgstr "" +msgstr "E780: .sug 文件适用于较新的vim 版本: %s" #: ../spell.c:9295 #, c-format msgid "E781: .sug file doesn't match .spl file: %s" -msgstr "" +msgstr "E781: .sug 文件不能匹配 .spl 文件: %s" #: ../spell.c:9305 #, fuzzy, c-format @@ -5662,7 +5683,7 @@ msgstr "E47: 读取错误文件失败" #. file. #: ../spell.c:11575 msgid "E783: duplicate char in MAP entry" -msgstr "" +msgstr "E783: MAP 条目中有重复的字符" #: ../syntax.c:266 msgid "No Syntax items defined for this buffer" @@ -5927,7 +5948,7 @@ msgstr "W18: 组名中含有无效字符" #: ../syntax.c:7448 msgid "E849: Too many highlight and syntax groups" -msgstr "" +msgstr "E849: 高亮和语法组过多" #: ../tag.c:104 msgid "E555: at bottom of tag stack" @@ -6002,7 +6023,7 @@ msgstr "查找 tag 文件 %s" #: ../tag.c:1545 msgid "Ignoring long line in tags file" -msgstr "" +msgstr "忽略较长的行在 tags 文件中" #: ../tag.c:1915 #, c-format @@ -6094,25 +6115,25 @@ msgstr "E212: 无法打开并写入文件" #: ../undo.c:717 #, c-format msgid "E825: Corrupted undo file (%s): %s" -msgstr "" +msgstr "E825: 已损坏的撤销文件 (%s): %s" #: ../undo.c:1039 msgid "Cannot write undo file in any directory in 'undodir'" -msgstr "" +msgstr "不能写入撤销文件到 'undodir' 中的任何文件夹" #: ../undo.c:1074 #, c-format msgid "Will not overwrite with undo file, cannot read: %s" -msgstr "" +msgstr "不能重写撤销文件, 不可读取: %s" #: ../undo.c:1092 #, c-format msgid "Will not overwrite, this is not an undo file: %s" -msgstr "" +msgstr "这个文件: %s 不是撤销文件,不可以重写" #: ../undo.c:1108 msgid "Skipping undo file write, nothing to undo" -msgstr "" +msgstr "跳过写入撤销文件,没有任何撤销" #: ../undo.c:1121 #, fuzzy, c-format @@ -6127,7 +6148,7 @@ msgstr "E297: 交换文件写入错误" #: ../undo.c:1280 #, c-format msgid "Not reading undo file, owner differs: %s" -msgstr "" +msgstr "不能读取撤销文件, 所有者不同: %s" #: ../undo.c:1292 #, fuzzy, c-format @@ -6151,7 +6172,7 @@ msgstr "E484: 无法打开文件 %s" #: ../undo.c:1328 msgid "File contents changed, cannot use undo info" -msgstr "" +msgstr "文件内容已改变,不能使用撤销信息" #: ../undo.c:1497 #, fuzzy, c-format @@ -6218,7 +6239,7 @@ msgstr "无可撤销" #: ../undo.c:2330 msgid "number changes when saved" -msgstr "" +msgstr " 编号 变更 时间 保存" #: ../undo.c:2360 #, fuzzy, c-format diff --git a/src/nvim/popupmnu.c b/src/nvim/popupmnu.c index 625fd15886..4accddfce0 100644 --- a/src/nvim/popupmnu.c +++ b/src/nvim/popupmnu.c @@ -19,6 +19,7 @@ #include "nvim/eval/typval.h" #include "nvim/ex_cmds.h" #include "nvim/highlight.h" +#include "nvim/insexpand.h" #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/menu.h" @@ -1105,7 +1106,10 @@ void pum_show_popupmenu(vimmenu_T *menu) ui_flush(); int c = vgetc(); - if (c == ESC || c == Ctrl_C) { + + // Bail out when typing Esc, CTRL-C or some callback or <expr> mapping + // closed the popup menu. + if (c == ESC || c == Ctrl_C || pum_array == NULL) { break; } else if (c == CAR || c == NL) { // enter: select current item, if any, and close diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index 2138437b29..9b46fad67a 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -270,10 +270,8 @@ static qf_delq_T *qf_delq_head = NULL; static int qf_init_process_nextline(qf_list_T *qfl, efm_T *fmt_first, qfstate_T *state, qffields_T *fields) { - int status; - // Get the next line from a file/buffer/list/string - status = qf_get_nextline(state); + int status = qf_get_nextline(state); if (status != QF_OK) { return status; } @@ -547,9 +545,7 @@ static void free_efm_list(efm_T **efm_first) /// a regular expression pattern. static size_t efm_regpat_bufsz(char *efm) { - size_t sz; - - sz = (FMT_PATTERNS * 3) + (STRLEN(efm) << 2); + size_t sz = (FMT_PATTERNS * 3) + (STRLEN(efm) << 2); for (int i = FMT_PATTERNS - 1; i >= 0;) { sz += STRLEN(fmt_pat[i--].pattern); } @@ -581,10 +577,8 @@ static int efm_option_part_len(char *efm) /// the parsed 'errorformat' option. static efm_T *parse_efm_option(char *efm) { - efm_T *fmt_ptr = NULL; efm_T *fmt_first = NULL; efm_T *fmt_last = NULL; - int len; // Get some space to modify the format string into. size_t sz = efm_regpat_bufsz(efm); @@ -592,7 +586,7 @@ static efm_T *parse_efm_option(char *efm) while (efm[0] != NUL) { // Allocate a new eformat structure and put it at the end of the list - fmt_ptr = (efm_T *)xcalloc(1, sizeof(efm_T)); + efm_T *fmt_ptr = (efm_T *)xcalloc(1, sizeof(efm_T)); if (fmt_first == NULL) { // first one fmt_first = fmt_ptr; } else { @@ -601,7 +595,7 @@ static efm_T *parse_efm_option(char *efm) fmt_last = fmt_ptr; // Isolate one part in the 'errorformat' option - len = efm_option_part_len(efm); + int len = efm_option_part_len(efm); if (efm_to_regpat(efm, len, fmt_ptr, fmtstr) == FAIL) { goto parse_efm_error; @@ -649,19 +643,13 @@ static int qf_get_next_str_line(qfstate_T *state) { // Get the next line from the supplied string char *p_str = state->p_str; - char *p; - size_t len; if (*p_str == NUL) { // Reached the end of the string return QF_END_OF_INPUT; } - p = vim_strchr(p_str, '\n'); - if (p != NULL) { - len = (size_t)(p - p_str) + 1; - } else { - len = STRLEN(p_str); - } + char *p = vim_strchr(p_str, '\n'); + size_t len = (p != NULL) ? (size_t)(p - p_str) + 1 : STRLEN(p_str); if (len > IOSIZE - 2) { state->linebuf = qf_grow_linebuf(state, len); @@ -684,7 +672,6 @@ static int qf_get_next_str_line(qfstate_T *state) static int qf_get_next_list_line(qfstate_T *state) { listitem_T *p_li = state->p_li; - size_t len; // Get the next line from the supplied list while (p_li != NULL @@ -698,7 +685,7 @@ static int qf_get_next_list_line(qfstate_T *state) return QF_END_OF_INPUT; } - len = STRLEN(TV_LIST_ITEM_TV(p_li)->vval.v_string); + size_t len = STRLEN(TV_LIST_ITEM_TV(p_li)->vval.v_string); if (len > IOSIZE - 2) { state->linebuf = qf_grow_linebuf(state, len); } else { @@ -716,17 +703,14 @@ static int qf_get_next_list_line(qfstate_T *state) /// Get the next string from state->buf. static int qf_get_next_buf_line(qfstate_T *state) { - char *p_buf = NULL; - size_t len; - // Get the next line from the supplied buffer if (state->buflnum > state->lnumlast) { return QF_END_OF_INPUT; } - p_buf = (char *)ml_get_buf(state->buf, state->buflnum, false); + char *p_buf = (char *)ml_get_buf(state->buf, state->buflnum, false); state->buflnum += 1; - len = STRLEN(p_buf); + size_t len = STRLEN(p_buf); if (len > IOSIZE - 2) { state->linebuf = qf_grow_linebuf(state, len); } else { @@ -741,8 +725,6 @@ static int qf_get_next_buf_line(qfstate_T *state) /// Get the next string from file state->fd. static int qf_get_next_file_line(qfstate_T *state) { - size_t growbuflen; - retry: errno = 0; if (fgets((char *)IObuff, IOSIZE, state->fd) == NULL) { @@ -765,7 +747,7 @@ retry: // Copy the read part of the line, excluding null-terminator memcpy(state->growbuf, IObuff, IOSIZE - 1); - growbuflen = state->linelen; + size_t growbuflen = state->linelen; for (;;) { errno = 0; @@ -1071,16 +1053,11 @@ static int qf_init_ext(qf_info_T *qi, int qf_idx, const char *restrict efile, bu linenr_T lnumlast, const char *restrict qf_title, char *restrict enc) FUNC_ATTR_NONNULL_ARG(1) { - qf_list_T *qfl; qfstate_T state = { 0 }; qffields_T fields = { 0 }; - qfline_T *old_last = NULL; - bool adding = false; static efm_T *fmt_first = NULL; - char *efm; static char *last_efm = NULL; int retval = -1; // default: return error flag - int status; // Do not used the cached buffer, it may have been wiped out. XFREE_CLEAR(qf_last_bufname); @@ -1090,6 +1067,9 @@ static int qf_init_ext(qf_info_T *qi, int qf_idx, const char *restrict efile, bu goto qf_init_end; } + qf_list_T *qfl; + qfline_T *old_last = NULL; + bool adding = false; if (newlist || qf_idx == qi->qf_listcount) { // make place for a new list qf_new_list(qi, qf_title); @@ -1104,6 +1084,8 @@ static int qf_init_ext(qf_info_T *qi, int qf_idx, const char *restrict efile, bu } } + char *efm; + // Use the local value of 'errorformat' if it's set. if (errorformat == p_efm && tv == NULL && buf && *buf->b_p_efm != NUL) { efm = (char *)buf->b_p_efm; @@ -1136,7 +1118,7 @@ static int qf_init_ext(qf_info_T *qi, int qf_idx, const char *restrict efile, bu // Read the lines in the error file one by one. // Try to recognize one of the error formats in each line. while (!got_int) { - status = qf_init_process_nextline(qfl, fmt_first, &state, &fields); + int status = qf_init_process_nextline(qfl, fmt_first, &state, &fields); if (status == QF_END_OF_INPUT) { // end of input break; } @@ -1223,9 +1205,6 @@ static qf_list_T *qf_get_curlist(qf_info_T *qi) /// the new list is added. static void qf_new_list(qf_info_T *qi, const char *qf_title) { - int i; - qf_list_T *qfl; - // If the current entry is not the last entry, delete entries beyond // the current entry. This makes it possible to browse in a tree-like // way with ":grep". @@ -1237,14 +1216,14 @@ static void qf_new_list(qf_info_T *qi, const char *qf_title) // Otherwise, add a new entry. if (qi->qf_listcount == LISTCOUNT) { qf_free(&qi->qf_lists[0]); - for (i = 1; i < LISTCOUNT; i++) { + for (int i = 1; i < LISTCOUNT; i++) { qi->qf_lists[i - 1] = qi->qf_lists[i]; } qi->qf_curlist = LISTCOUNT - 1; } else { qi->qf_curlist = qi->qf_listcount++; } - qfl = qf_get_curlist(qi); + qf_list_T *qfl = qf_get_curlist(qi); memset(qfl, 0, sizeof(qf_list_T)); qf_store_title(qfl, qf_title); qfl->qfl_type = qi->qfl_type; @@ -1255,14 +1234,12 @@ static void qf_new_list(qf_info_T *qi, const char *qf_title) /// Return the matched value in "fields->namebuf". static int qf_parse_fmt_f(regmatch_T *rmp, int midx, qffields_T *fields, int prefix) { - char c; - if (rmp->startp[midx] == NULL || rmp->endp[midx] == NULL) { return QF_FAIL; } // Expand ~/file and $HOME/file to full path. - c = (char)(*rmp->endp[midx]); + char c = (char)(*rmp->endp[midx]); *rmp->endp[midx] = NUL; expand_env(rmp->startp[midx], (char_u *)fields->namebuf, CMDBUFFSIZE); *rmp->endp[midx] = (char_u)c; @@ -1360,12 +1337,10 @@ static void qf_parse_fmt_plus(const char *linebuf, size_t linelen, qffields_T *f /// Return the matched value in "fields->errmsg". static int qf_parse_fmt_m(regmatch_T *rmp, int midx, qffields_T *fields) { - size_t len; - if (rmp->startp[midx] == NULL || rmp->endp[midx] == NULL) { return QF_FAIL; } - len = (size_t)(rmp->endp[midx] - rmp->startp[midx]); + size_t len = (size_t)(rmp->endp[midx] - rmp->startp[midx]); if (len >= fields->errmsglen) { // len + null terminator fields->errmsg = xrealloc(fields->errmsg, len + 1); @@ -1390,13 +1365,11 @@ static int qf_parse_fmt_r(regmatch_T *rmp, int midx, char **tail) /// Return the matched value in "fields->col". static int qf_parse_fmt_p(regmatch_T *rmp, int midx, qffields_T *fields) { - char *match_ptr; - if (rmp->startp[midx] == NULL || rmp->endp[midx] == NULL) { return QF_FAIL; } fields->col = 0; - for (match_ptr = (char *)rmp->startp[midx]; (char_u *)match_ptr != rmp->endp[midx]; + for (char *match_ptr = (char *)rmp->startp[midx]; (char_u *)match_ptr != rmp->endp[midx]; match_ptr++) { fields->col++; if (*match_ptr == TAB) { @@ -1425,12 +1398,10 @@ static int qf_parse_fmt_v(regmatch_T *rmp, int midx, qffields_T *fields) /// Return the matched value in "fields->pattern". static int qf_parse_fmt_s(regmatch_T *rmp, int midx, qffields_T *fields) { - size_t len; - if (rmp->startp[midx] == NULL || rmp->endp[midx] == NULL) { return QF_FAIL; } - len = (size_t)(rmp->endp[midx] - rmp->startp[midx]); + size_t len = (size_t)(rmp->endp[midx] - rmp->startp[midx]); if (len > CMDBUFFSIZE - 5) { len = CMDBUFFSIZE - 5; } @@ -1446,14 +1417,11 @@ static int qf_parse_fmt_s(regmatch_T *rmp, int midx, qffields_T *fields) /// Return the matched value in "fields->module". static int qf_parse_fmt_o(regmatch_T *rmp, int midx, qffields_T *fields) { - size_t len; - size_t dsize; - if (rmp->startp[midx] == NULL || rmp->endp[midx] == NULL) { return QF_FAIL; } - len = (size_t)(rmp->endp[midx] - rmp->startp[midx]); - dsize = STRLEN(fields->module) + len + 1; + size_t len = (size_t)(rmp->endp[midx] - rmp->startp[midx]); + size_t dsize = STRLEN(fields->module) + len + 1; if (dsize > CMDBUFFSIZE) { dsize = CMDBUFFSIZE; } @@ -1489,9 +1457,6 @@ static int qf_parse_match(char *linebuf, size_t linelen, efm_T *fmt_ptr, regmatc qffields_T *fields, int qf_multiline, int qf_multiscan, char **tail) { char idx = fmt_ptr->prefix; - int i; - int midx; - int status; if ((idx == 'C' || idx == 'Z') && !qf_multiline) { return QF_FAIL; @@ -1505,9 +1470,9 @@ static int qf_parse_match(char *linebuf, size_t linelen, efm_T *fmt_ptr, regmatc // Extract error message data from matched line. // We check for an actual submatch, because "\[" and "\]" in // the 'errorformat' may cause the wrong submatch to be used. - for (i = 0; i < FMT_PATTERNS; i++) { - status = QF_OK; - midx = (int)fmt_ptr->addr[i]; + for (int i = 0; i < FMT_PATTERNS; i++) { + int status = QF_OK; + int midx = (int)fmt_ptr->addr[i]; if (i == 0 && midx > 0) { // %f status = qf_parse_fmt_f(regmatch, midx, fields, idx); } else if (i == FMT_PATTERN_M) { @@ -1537,10 +1502,6 @@ static int qf_parse_match(char *linebuf, size_t linelen, efm_T *fmt_ptr, regmatc static int qf_parse_get_fields(char *linebuf, size_t linelen, efm_T *fmt_ptr, qffields_T *fields, int qf_multiline, int qf_multiscan, char **tail) { - regmatch_T regmatch; - int status = QF_FAIL; - int r; - if (qf_multiscan && vim_strchr("OPQ", fmt_ptr->prefix) == NULL) { return QF_FAIL; } @@ -1560,11 +1521,13 @@ static int qf_parse_get_fields(char *linebuf, size_t linelen, efm_T *fmt_ptr, qf fields->type = 0; *tail = NULL; + regmatch_T regmatch; // Always ignore case when looking for a matching error. regmatch.rm_ic = true; regmatch.regprog = fmt_ptr->prog; - r = vim_regexec(®match, linebuf, (colnr_T)0); + int r = vim_regexec(®match, linebuf, (colnr_T)0); fmt_ptr->prog = regmatch.regprog; + int status = QF_FAIL; if (r) { status = qf_parse_match(linebuf, linelen, fmt_ptr, ®match, fields, qf_multiline, qf_multiscan, tail); @@ -1689,9 +1652,7 @@ static int qf_parse_multiline_pfx(int idx, qf_list_T *qfl, qffields_T *fields) /// Queue location list stack delete request. static void locstack_queue_delreq(qf_info_T *qi) { - qf_delq_T *q; - - q = xmalloc(sizeof(qf_delq_T)); + qf_delq_T *q = xmalloc(sizeof(qf_delq_T)); q->qi = qi; q->next = qf_delq_head; qf_delq_head = q; @@ -1722,10 +1683,7 @@ static void wipe_qf_buffer(qf_info_T *qi) /// Free a location list stack static void ll_free_all(qf_info_T **pqi) { - int i; - qf_info_T *qi; - - qi = *pqi; + qf_info_T *qi = *pqi; if (qi == NULL) { return; } @@ -1744,7 +1702,7 @@ static void ll_free_all(qf_info_T **pqi) // If the quickfix window buffer is loaded, then wipe it wipe_qf_buffer(qi); - for (i = 0; i < qi->qf_listcount; i++) { + for (int i = 0; i < qi->qf_listcount; i++) { qf_free(qf_get_list(qi, i)); } xfree(qi); @@ -1754,16 +1712,14 @@ static void ll_free_all(qf_info_T **pqi) /// Free all the quickfix/location lists in the stack. void qf_free_all(win_T *wp) { - int i; - qf_info_T *qi = &ql_info; - if (wp != NULL) { // location list ll_free_all(&wp->w_llist); ll_free_all(&wp->w_llist_ref); } else { // quickfix list - for (i = 0; i < qi->qf_listcount; i++) { + qf_info_T *qi = &ql_info; + for (int i = 0; i < qi->qf_listcount; i++) { qf_free(qf_get_list(qi, i)); } } @@ -1837,7 +1793,6 @@ static int qf_add_entry(qf_list_T *qfl, char *dir, char *fname, char *module, in char vis_col, char *pattern, int nr, char type, char valid) { qfline_T *qfp = xmalloc(sizeof(qfline_T)); - qfline_T **lastp; // pointer to qf_last or NULL if (bufnum != 0) { buf_T *buf = buflist_findnr(bufnum); @@ -1873,7 +1828,7 @@ static int qf_add_entry(qf_list_T *qfl, char *dir, char *fname, char *module, in qfp->qf_type = type; qfp->qf_valid = valid; - lastp = &qfl->qf_last; + qfline_T **lastp = &qfl->qf_last; if (qf_list_empty(qfl)) { // first element in the list qfl->qf_start = qfp; @@ -2060,16 +2015,10 @@ static int copy_loclist(qf_list_T *from_qfl, qf_list_T *to_qfl) void copy_loclist_stack(win_T *from, win_T *to) FUNC_ATTR_NONNULL_ALL { - qf_info_T *qi; - // When copying from a location list window, copy the referenced // location list. For other windows, copy the location list for // that window. - if (IS_LL_WINDOW(from)) { - qi = from->w_llist_ref; - } else { - qi = from->w_llist; - } + qf_info_T *qi = IS_LL_WINDOW(from) ? from->w_llist_ref : from->w_llist; if (qi == NULL) { // no location list to copy return; @@ -2213,14 +2162,12 @@ static char *qf_push_dir(char *dirbuf, struct dir_stack_T **stackptr, bool is_fi // stack is empty static char *qf_pop_dir(struct dir_stack_T **stackptr) { - struct dir_stack_T *ds_ptr; - // TODO(vim): Should we check if dirbuf is the directory on top of the stack? // What to do if it isn't? // pop top element and free it if (*stackptr != NULL) { - ds_ptr = *stackptr; + struct dir_stack_T *ds_ptr = *stackptr; *stackptr = (*stackptr)->next; xfree(ds_ptr->dirname); xfree(ds_ptr); @@ -2262,17 +2209,13 @@ static void qf_clean_dir_stack(struct dir_stack_T **stackptr) /// qf_guess_filepath will return NULL. static char *qf_guess_filepath(qf_list_T *qfl, char *filename) { - struct dir_stack_T *ds_ptr; - struct dir_stack_T *ds_tmp; - char *fullname; - // no dirs on the stack - there's nothing we can do if (qfl->qf_dir_stack == NULL) { return NULL; } - ds_ptr = qfl->qf_dir_stack->next; - fullname = NULL; + struct dir_stack_T *ds_ptr = qfl->qf_dir_stack->next; + char *fullname = NULL; while (ds_ptr) { xfree(fullname); fullname = concat_fnames(ds_ptr->dirname, filename, true); @@ -2288,7 +2231,7 @@ static char *qf_guess_filepath(qf_list_T *qfl, char *filename) // clean up all dirs we already left while (qfl->qf_dir_stack->next != ds_ptr) { - ds_tmp = qfl->qf_dir_stack->next; + struct dir_stack_T *ds_tmp = qfl->qf_dir_stack->next; qfl->qf_dir_stack->next = qfl->qf_dir_stack->next->next; xfree(ds_tmp->dirname); xfree(ds_tmp); @@ -2392,13 +2335,11 @@ static qfline_T *get_nth_valid_entry(qf_list_T *qfl, int errornr, int dir, int * { qfline_T *qf_ptr = qfl->qf_ptr; int qf_idx = qfl->qf_index; - qfline_T *prev_qf_ptr; - int prev_index; char *err = e_no_more_items; while (errornr--) { - prev_qf_ptr = qf_ptr; - prev_index = qf_idx; + qfline_T *prev_qf_ptr = qf_ptr; + int prev_index = qf_idx; if (dir == FORWARD || dir == FORWARD_FILE) { qf_ptr = get_next_valid_entry(qfl, qf_ptr, &qf_idx, dir); @@ -2802,11 +2743,9 @@ static int qf_jump_edit_buffer(qf_info_T *qi, qfline_T *qf_ptr, int forceit, int /// a search pattern. static void qf_jump_goto_line(linenr_T qf_lnum, int qf_col, char qf_viscol, char *qf_pattern) { - linenr_T i; - if (qf_pattern == NULL) { // Go to line with error, unless qf_lnum is 0. - i = qf_lnum; + linenr_T i = qf_lnum; if (i > 0) { if (i > curbuf->b_ml.ml_line_count) { i = curbuf->b_ml.ml_line_count; @@ -2933,14 +2872,11 @@ static int qf_jump_open_window(qf_info_T *qi, qfline_T *qf_ptr, bool newwin, int static int qf_jump_to_buffer(qf_info_T *qi, int qf_index, qfline_T *qf_ptr, int forceit, int prev_winid, int *opened_window, int openfold, int print_message) { - buf_T *old_curbuf; - linenr_T old_lnum; - int retval = OK; - // If there is a file name, read the wanted file if needed, and check // autowrite etc. - old_curbuf = curbuf; - old_lnum = curwin->w_cursor.lnum; + buf_T *old_curbuf = curbuf; + linenr_T old_lnum = curwin->w_cursor.lnum; + int retval = OK; if (qf_ptr->qf_fnum != 0) { retval = qf_jump_edit_buffer(qi, qf_ptr, forceit, prev_winid, @@ -2984,18 +2920,9 @@ void qf_jump(qf_info_T *qi, int dir, int errornr, int forceit) // If 'newwin' is true, then open the file in a new window. static void qf_jump_newwin(qf_info_T *qi, int dir, int errornr, int forceit, bool newwin) { - qf_list_T *qfl; - qfline_T *qf_ptr; - qfline_T *old_qf_ptr; - int qf_index; - int old_qf_index; char *old_swb = (char *)p_swb; unsigned old_swb_flags = swb_flags; - int prev_winid; - int opened_window = false; - int print_message = true; const bool old_KeyTyped = KeyTyped; // getting file may reset it - int retval = OK; if (qi == NULL) { qi = &ql_info; @@ -3008,12 +2935,12 @@ static void qf_jump_newwin(qf_info_T *qi, int dir, int errornr, int forceit, boo incr_quickfix_busy(); - qfl = qf_get_curlist(qi); + qf_list_T *qfl = qf_get_curlist(qi); - qf_ptr = qfl->qf_ptr; - old_qf_ptr = qf_ptr; - qf_index = qfl->qf_index; - old_qf_index = qf_index; + qfline_T *qf_ptr = qfl->qf_ptr; + qfline_T *old_qf_ptr = qf_ptr; + int qf_index = qfl->qf_index; + int old_qf_index = qf_index; qf_ptr = qf_get_entry(qfl, errornr, dir, &qf_index); if (qf_ptr == NULL) { @@ -3024,15 +2951,14 @@ static void qf_jump_newwin(qf_info_T *qi, int dir, int errornr, int forceit, boo qfl->qf_index = qf_index; qfl->qf_ptr = qf_ptr; - if (qf_win_pos_update(qi, old_qf_index)) { - // No need to print the error message if it's visible in the error - // window - print_message = false; - } - prev_winid = curwin->handle; + // No need to print the error message if it's visible in the error window + bool print_message = !qf_win_pos_update(qi, old_qf_index); + + int prev_winid = curwin->handle; - retval = qf_jump_open_window(qi, qf_ptr, newwin, &opened_window); + int opened_window = false; + int retval = qf_jump_open_window(qi, qf_ptr, newwin, &opened_window); if (retval == FAIL) { goto failed; } @@ -3085,13 +3011,11 @@ static int qfLineAttr; /// quickfix list. static void qf_list_entry(qfline_T *qfp, int qf_idx, bool cursel) { - char *fname; - buf_T *buf; - - fname = NULL; + char *fname = NULL; if (qfp->qf_module != NULL && *qfp->qf_module != NUL) { vim_snprintf((char *)IObuff, IOSIZE, "%2d %s", qf_idx, qfp->qf_module); } else { + buf_T *buf; if (qfp->qf_fnum != 0 && (buf = buflist_findnr(qfp->qf_fnum)) != NULL) { fname = buf->b_fname; @@ -3147,13 +3071,27 @@ static void qf_list_entry(qfline_T *qfp, int qf_idx, bool cursel) } msg_puts(" "); + char_u *tbuf = IObuff; + size_t tbuflen = IOSIZE; + size_t len = STRLEN(qfp->qf_text) + 3; + + if (len > IOSIZE) { + tbuf = xmalloc(len); + tbuflen = len; + } + // Remove newlines and leading whitespace from the text. For an // unrecognized line keep the indent, the compiler may mark a word - // with ^^^^. */ + // with ^^^^. qf_fmt_text((fname != NULL || qfp->qf_lnum != 0) ? skipwhite(qfp->qf_text) : qfp->qf_text, - (char *)IObuff, IOSIZE); - msg_prt_line(IObuff, false); + (char *)tbuf, (int)tbuflen); + msg_prt_line(tbuf, false); + + if (tbuf != IObuff) { + xfree(tbuf); + } + ui_flush(); // show one line at a time } @@ -3161,17 +3099,11 @@ static void qf_list_entry(qfline_T *qfp, int qf_idx, bool cursel) // ":llist": list all locations void qf_list(exarg_T *eap) { - qf_list_T *qfl; - qfline_T *qfp; - int i; - int idx1 = 1; - int idx2 = -1; char *arg = eap->arg; - int all = eap->forceit; // if not :cl!, only show - // recognised errors - qf_info_T *qi; + int all = eap->forceit; // if not :cl!, only show recognised errors + qf_info_T *qi = qf_cmd_get_stack(eap, true); - if ((qi = qf_cmd_get_stack(eap, true)) == NULL) { + if (qi == NULL) { return; } @@ -3185,11 +3117,14 @@ void qf_list(exarg_T *eap) arg++; plus = true; } + int idx1 = 1; + int idx2 = -1; if (!get_list_range((char_u **)&arg, &idx1, &idx2) || *arg != NUL) { emsg(_(e_trailing)); return; } - qfl = qf_get_curlist(qi); + qf_list_T *qfl = qf_get_curlist(qi); + int i; if (plus) { i = qfl->qf_index; idx2 = i + idx1; @@ -3225,6 +3160,7 @@ void qf_list(exarg_T *eap) if (qfl->qf_nonevalid) { all = true; } + qfline_T *qfp; FOR_ALL_QFL_ITEMS(qfl, qfp, i) { if ((qfp->qf_valid || all) && idx1 <= i && i <= idx2) { qf_list_entry(qfp, i, i == qfl->qf_index); @@ -3312,17 +3248,12 @@ static void qf_msg(qf_info_T *qi, int which, char *lead) void qf_age(exarg_T *eap) { qf_info_T *qi; - int count; if ((qi = qf_cmd_get_stack(eap, true)) == NULL) { return; } - if (eap->addr_count != 0) { - count = (int)eap->line2; - } else { - count = 1; - } + int count = (eap->addr_count != 0) ? (int)eap->line2 : 1; while (count--) { if (eap->cmdidx == CMD_colder || eap->cmdidx == CMD_lolder) { if (qi->qf_curlist == 0) { @@ -3346,7 +3277,6 @@ void qf_age(exarg_T *eap) void qf_history(exarg_T *eap) { qf_info_T *qi = qf_cmd_get_stack(eap, false); - int i; if (eap->addr_count > 0) { if (qi == NULL) { @@ -3369,7 +3299,7 @@ void qf_history(exarg_T *eap) if (qf_stack_empty(qi)) { msg(_("No entries")); } else { - for (i = 0; i < qi->qf_listcount; i++) { + for (int i = 0; i < qi->qf_listcount; i++) { qf_msg(qi, i, i == qi->qf_curlist ? "> " : " "); } } @@ -3379,13 +3309,11 @@ void qf_history(exarg_T *eap) /// associated with the list like context and title are not freed. static void qf_free_items(qf_list_T *qfl) { - qfline_T *qfp; - qfline_T *qfpnext; bool stop = false; while (qfl->qf_count && qfl->qf_start != NULL) { - qfp = qfl->qf_start; - qfpnext = qfp->qf_next; + qfline_T *qfp = qfl->qf_start; + qfline_T *qfpnext = qfp->qf_next; if (!stop) { xfree(qfp->qf_module); xfree(qfp->qf_text); @@ -3438,11 +3366,7 @@ static void qf_free(qf_list_T *qfl) bool qf_mark_adjust(win_T *wp, linenr_T line1, linenr_T line2, linenr_T amount, linenr_T amount_after) { - int i; - qfline_T *qfp; - int idx; qf_info_T *qi = &ql_info; - bool found_one = false; int buf_has_flag = wp == NULL ? BUF_HAS_QF_ENTRY : BUF_HAS_LL_ENTRY; if (!(curbuf->b_has_qf_entry & buf_has_flag)) { @@ -3455,7 +3379,10 @@ bool qf_mark_adjust(win_T *wp, linenr_T line1, linenr_T line2, linenr_T amount, qi = wp->w_llist; } - for (idx = 0; idx < qi->qf_listcount; idx++) { + int i; + qfline_T *qfp; + bool found_one = false; + for (int idx = 0; idx < qi->qf_listcount; idx++) { qf_list_T *qfl = qf_get_list(qi, idx); if (!qf_list_empty(qfl)) { FOR_ALL_QFL_ITEMS(qfl, qfp, i) { @@ -3495,7 +3422,6 @@ bool qf_mark_adjust(win_T *wp, linenr_T line1, linenr_T line2, linenr_T amount, // 1 x "" :helpgrep static char *qf_types(int c, int nr) { - static char buf[20]; static char cc[3]; char *p; @@ -3520,6 +3446,7 @@ static char *qf_types(int c, int nr) return p; } + static char buf[20]; snprintf((char *)buf, sizeof(buf), "%s %3d", p, nr); return buf; } @@ -3558,17 +3485,15 @@ void qf_view_result(bool split) void ex_cwindow(exarg_T *eap) { qf_info_T *qi; - qf_list_T *qfl; - win_T *win; if ((qi = qf_cmd_get_stack(eap, true)) == NULL) { return; } - qfl = qf_get_curlist(qi); + qf_list_T *qfl = qf_get_curlist(qi); // Look for an existing quickfix window. - win = qf_find_win(qi); + win_T *win = qf_find_win(qi); // If a quickfix window is open but we have no errors to display, // close the window. If a quickfix window is not open, then open @@ -3588,7 +3513,6 @@ void ex_cwindow(exarg_T *eap) // ":lclose": close the window showing the location list void ex_cclose(exarg_T *eap) { - win_T *win = NULL; qf_info_T *qi; if ((qi = qf_cmd_get_stack(eap, false)) == NULL) { @@ -3596,7 +3520,7 @@ void ex_cclose(exarg_T *eap) } // Find existing quickfix window and close it. - win = qf_find_win(qi); + win_T *win = qf_find_win(qi); if (win != NULL) { win_close(win, false, false); } @@ -3726,10 +3650,6 @@ static void qf_set_title_var(qf_list_T *qfl) void ex_copen(exarg_T *eap) { qf_info_T *qi; - qf_list_T *qfl; - int height; - int status = FAIL; - int lnum; if ((qi = qf_cmd_get_stack(eap, true)) == NULL) { return; @@ -3737,6 +3657,7 @@ void ex_copen(exarg_T *eap) incr_quickfix_busy(); + int height; if (eap->addr_count != 0) { height = (int)eap->line2; } else { @@ -3745,6 +3666,7 @@ void ex_copen(exarg_T *eap) reset_VIsual_and_resel(); // stop Visual mode // Find an existing quickfix window, or open a new one. + int status = FAIL; if (cmdmod.cmod_tab == 0) { status = qf_goto_cwindow(qi, eap->addr_count != 0, height, cmdmod.cmod_split & WSP_VERT); @@ -3756,11 +3678,11 @@ void ex_copen(exarg_T *eap) } } - qfl = qf_get_curlist(qi); + qf_list_T *qfl = qf_get_curlist(qi); qf_set_title_var(qfl); // Save the current index here, as updating the quickfix buffer may free // the quickfix list - lnum = qfl->qf_index; + int lnum = qfl->qf_index; // Fill the buffer with the quickfix list. qf_fill_buffer(qfl, curbuf, NULL, curwin->handle); @@ -3825,14 +3747,13 @@ linenr_T qf_current_entry(win_T *wp) /// Return TRUE if there is a quickfix window. /// /// @param old_qf_index previous qf_index or zero -static int qf_win_pos_update(qf_info_T *qi, int old_qf_index) +static bool qf_win_pos_update(qf_info_T *qi, int old_qf_index) { - win_T *win; int qf_index = qf_get_curlist(qi)->qf_index; // Put the cursor on the current error in the quickfix window, so that // it's viewable. - win = qf_find_win(qi); + win_T *win = qf_find_win(qi); if (win != NULL && qf_index <= win->w_buffer->b_ml.ml_line_count && old_qf_index != qf_index) { @@ -3909,14 +3830,12 @@ static buf_T *qf_find_buf(qf_info_T *qi) // Process the 'quickfixtextfunc' option value. bool qf_process_qftf_option(void) { - typval_T *tv; - Callback cb; - if (p_qftf == NULL || *p_qftf == NUL) { callback_free(&qftf_cb); return true; } + typval_T *tv; if (*p_qftf == '{') { // Lambda expression tv = eval_expr((char *)p_qftf); @@ -3930,6 +3849,7 @@ bool qf_process_qftf_option(void) tv->vval.v_string = (char *)vim_strsave(p_qftf); } + Callback cb; if (!callback_from_typval(&cb, tv)) { tv_free(tv); return false; @@ -3961,16 +3881,13 @@ static void qf_update_win_titlevar(qf_info_T *qi) // Find the quickfix buffer. If it exists, update the contents. static void qf_update_buffer(qf_info_T *qi, qfline_T *old_last) { - buf_T *buf; - win_T *win; - aco_save_T aco; - // Check if a buffer for the quickfix list exists. Update it. - buf = qf_find_buf(qi); + buf_T *buf = qf_find_buf(qi); if (buf != NULL) { linenr_T old_line_count = buf->b_ml.ml_line_count; int qf_winid = 0; + win_T *win; if (IS_LL_STACK(qi)) { if (curwin->w_llist == qi) { win = curwin; @@ -3983,6 +3900,8 @@ static void qf_update_buffer(qf_info_T *qi, qfline_T *old_last) qf_winid = (int)win->handle; } + aco_save_T aco; + if (old_last == NULL) { // set curwin/curbuf to buf and save a few things aucmd_prepbuf(&aco, buf); @@ -4013,14 +3932,13 @@ static int qf_buf_add_line(qf_list_T *qfl, buf_T *buf, linenr_T lnum, const qfli char *dirname, char *qftf_str, bool first_bufline) FUNC_ATTR_NONNULL_ARG(1, 2, 4, 5) { - int len; - buf_T *errbuf; - // If the 'quickfixtextfunc' function returned a non-empty custom string // for this entry, then use it. if (qftf_str != NULL && *qftf_str != NUL) { STRLCPY(IObuff, qftf_str, IOSIZE); } else { + buf_T *errbuf; + int len; if (qfp->qf_module != NULL) { STRLCPY(IObuff, qfp->qf_module, IOSIZE); len = (int)STRLEN(IObuff); @@ -4109,8 +4027,6 @@ static list_T *call_qftf_func(qf_list_T *qfl, int qf_winid, long start_idx, long args[0].v_type = VAR_DICT; args[0].vval.v_dict = dict; - qftf_list = NULL; - if (callback_call(cb, 1, args, &rettv)) { if (rettv.v_type == VAR_LIST) { qftf_list = rettv.vval.v_list; @@ -4132,11 +4048,7 @@ static list_T *call_qftf_func(qf_list_T *qfl, int qf_winid, long start_idx, long static void qf_fill_buffer(qf_list_T *qfl, buf_T *buf, qfline_T *old_last, int qf_winid) FUNC_ATTR_NONNULL_ARG(2) { - linenr_T lnum; - qfline_T *qfp; const bool old_KeyTyped = KeyTyped; - list_T *qftf_list = NULL; - listitem_T *qftf_li = NULL; if (old_last == NULL) { if (buf != curbuf) { @@ -4153,11 +4065,12 @@ static void qf_fill_buffer(qf_list_T *qfl, buf_T *buf, qfline_T *old_last, int q // Check if there is anything to display if (qfl != NULL) { char dirname[MAXPATHL]; - int prev_bufnr = -1; - bool invalid_val = false; *dirname = NUL; + linenr_T lnum; + qfline_T *qfp; + // Add one line for each error if (old_last == NULL) { qfp = qfl->qf_start; @@ -4171,8 +4084,11 @@ static void qf_fill_buffer(qf_list_T *qfl, buf_T *buf, qfline_T *old_last, int q lnum = buf->b_ml.ml_line_count; } - qftf_list = call_qftf_func(qfl, qf_winid, lnum + 1, (long)qfl->qf_count); - qftf_li = tv_list_first(qftf_list); + list_T *qftf_list = call_qftf_func(qfl, qf_winid, lnum + 1, (long)qfl->qf_count); + listitem_T *qftf_li = tv_list_first(qftf_list); + + int prev_bufnr = -1; + bool invalid_val = false; while (lnum < qfl->qf_count) { char *qftf_str = NULL; @@ -4349,10 +4265,6 @@ static char *make_get_fullcmd(const char *makecmd, const char *fname) // Used for ":make", ":lmake", ":grep", ":lgrep", ":grepadd", and ":lgrepadd" void ex_make(exarg_T *eap) { - char *fname; - win_T *wp = NULL; - qf_info_T *qi = &ql_info; - int res; char *enc = (*curbuf->b_p_menc != NUL) ? (char *)curbuf->b_p_menc : (char *)p_menc; // Redirect ":grep" to ":vimgrep" if 'grepprg' is "internal". @@ -4369,12 +4281,13 @@ void ex_make(exarg_T *eap) } } + win_T *wp = NULL; if (is_loclist_cmd(eap->cmdidx)) { wp = curwin; } autowrite_all(); - fname = get_mef_name(); + char *fname = get_mef_name(); if (fname == NULL) { return; } @@ -4386,10 +4299,12 @@ void ex_make(exarg_T *eap) incr_quickfix_busy(); - res = qf_init(wp, fname, (eap->cmdidx != CMD_make - && eap->cmdidx != CMD_lmake) ? p_gefm : p_efm, - (eap->cmdidx != CMD_grepadd && eap->cmdidx != CMD_lgrepadd), - qf_cmdtitle(*eap->cmdlinep), enc); + int res = qf_init(wp, fname, (eap->cmdidx != CMD_make + && eap->cmdidx != CMD_lmake) ? p_gefm : p_efm, + (eap->cmdidx != CMD_grepadd && eap->cmdidx != CMD_lgrepadd), + qf_cmdtitle(*eap->cmdlinep), enc); + + qf_info_T *qi = &ql_info; if (wp != NULL) { qi = GET_LOC_LIST(wp); if (qi == NULL) { @@ -4423,7 +4338,6 @@ cleanup: // Returns NULL for error. static char *get_mef_name(void) { - char *p; char *name; static int start = -1; static int off = 0; @@ -4436,6 +4350,8 @@ static char *get_mef_name(void) return name; } + char *p; + for (p = p_mef; *p; ++p) { if (p[0] == '#' && p[1] == '#') { break; @@ -4484,7 +4400,6 @@ size_t qf_get_size(exarg_T *eap) size_t qf_get_valid_size(exarg_T *eap) { qf_info_T *qi; - qf_list_T *qfl; if ((qi = qf_cmd_get_stack(eap, false)) == NULL) { return 0; @@ -4495,7 +4410,7 @@ size_t qf_get_valid_size(exarg_T *eap) qfline_T *qfp; int i; assert(qf_get_curlist(qi)->qf_count >= 0); - qfl = qf_get_curlist(qi); + qf_list_T *qfl = qf_get_curlist(qi); FOR_ALL_QFL_ITEMS(qfl, qfp, i) { if (!qfp->qf_valid) { continue; @@ -4894,12 +4809,10 @@ static qfline_T *qf_find_closest_entry(qf_list_T *qfl, int bnr, const pos_T *pos bool linewise, int *errornr) FUNC_ATTR_NONNULL_ALL { - qfline_T *qfp; - *errornr = 0; // Find the first entry in this file - qfp = qf_find_first_entry_in_buf(qfl, bnr, errornr); + qfline_T *qfp = qf_find_first_entry_in_buf(qfl, bnr, errornr); if (qfp == NULL) { return NULL; // no entry in this file } @@ -4997,48 +4910,39 @@ static int qf_find_nth_adj_entry(qf_list_T *qfl, int bnr, pos_T *pos, linenr_T n /// ":lafter" and ":lbefore" commands void ex_cbelow(exarg_T *eap) { - qf_info_T *qi; - qf_list_T *qfl; - int dir; - int buf_has_flag; - if (eap->addr_count > 0 && eap->line2 <= 0) { emsg(_(e_invrange)); return; } // Check whether the current buffer has any quickfix entries - if (eap->cmdidx == CMD_cabove || eap->cmdidx == CMD_cbelow - || eap->cmdidx == CMD_cbefore || eap->cmdidx == CMD_cafter) { - buf_has_flag = BUF_HAS_QF_ENTRY; - } else { - buf_has_flag = BUF_HAS_LL_ENTRY; - } + int buf_has_flag = (eap->cmdidx == CMD_cabove + || eap->cmdidx == CMD_cbelow + || eap->cmdidx == CMD_cbefore + || eap->cmdidx == CMD_cafter) ? BUF_HAS_QF_ENTRY : BUF_HAS_LL_ENTRY; + if (!(curbuf->b_has_qf_entry & buf_has_flag)) { emsg(_(e_quickfix)); return; } + qf_info_T *qi; if ((qi = qf_cmd_get_stack(eap, true)) == NULL) { return; } - qfl = qf_get_curlist(qi); + qf_list_T *qfl = qf_get_curlist(qi); // check if the list has valid errors if (!qf_list_has_valid_entries(qfl)) { emsg(_(e_quickfix)); return; } - if (eap->cmdidx == CMD_cbelow - || eap->cmdidx == CMD_lbelow - || eap->cmdidx == CMD_cafter - || eap->cmdidx == CMD_lafter) { - // Forward motion commands - dir = FORWARD; - } else { - dir = BACKWARD; - } + // Forward motion commands + int dir = (eap->cmdidx == CMD_cbelow + || eap->cmdidx == CMD_lbelow + || eap->cmdidx == CMD_cafter + || eap->cmdidx == CMD_lafter) ? FORWARD : BACKWARD; pos_T pos = curwin->w_cursor; // A quickfix entry column number is 1 based whereas cursor column @@ -5425,7 +5329,7 @@ static int vgr_process_args(exarg_T *eap, vgr_args_T *args) } // Parse the list of arguments, wildcards have already been expanded. - if (get_arglist_exp((char_u *)p, &args->fcount, (char_u ***)&args->fnames, true) == FAIL) { + if (get_arglist_exp((char_u *)p, &args->fcount, &args->fnames, true) == FAIL) { return FAIL; } if (args->fcount == 0) { @@ -5601,12 +5505,12 @@ void ex_vimgrep(exarg_T *eap) int status = vgr_process_files(wp, qi, &args, &redraw_for_dummy, &first_match_buf, &target_dir); if (status != OK) { - FreeWild(args.fcount, (char_u **)args.fnames); + FreeWild(args.fcount, args.fnames); decr_quickfix_busy(); goto theend; } - FreeWild(args.fcount, (char_u **)args.fnames); + FreeWild(args.fcount, args.fnames); qf_list_T *qfl = qf_get_curlist(qi); qfl->qf_nonevalid = false; @@ -5689,18 +5593,14 @@ static void restore_start_dir(char *dirname_start) /// @return NULL if it fails. static buf_T *load_dummy_buffer(char *fname, char *dirname_start, char *resulting_dir) { - buf_T *newbuf; - bufref_T newbufref; - bufref_T newbuf_to_wipe; - int failed = true; - aco_save_T aco; - int readfile_result; - // Allocate a buffer without putting it in the buffer list. - newbuf = buflist_new(NULL, NULL, (linenr_T)1, BLN_DUMMY); + buf_T *newbuf = buflist_new(NULL, NULL, (linenr_T)1, BLN_DUMMY); if (newbuf == NULL) { return NULL; } + + int failed = true; + bufref_T newbufref; set_bufref(&newbufref, newbuf); // Init the options. @@ -5711,6 +5611,7 @@ static buf_T *load_dummy_buffer(char *fname, char *dirname_start, char *resultin // Make sure this buffer isn't wiped out by autocommands. newbuf->b_locked++; // set curwin/curbuf to buf and save a few things + aco_save_T aco; aucmd_prepbuf(&aco, newbuf); // Need to set the filename for autocommands. @@ -5723,10 +5624,11 @@ static buf_T *load_dummy_buffer(char *fname, char *dirname_start, char *resultin // work. curbuf->b_flags &= ~BF_DUMMY; + bufref_T newbuf_to_wipe; newbuf_to_wipe.br_buf = NULL; - readfile_result = readfile(fname, NULL, (linenr_T)0, (linenr_T)0, - (linenr_T)MAXLNUM, NULL, - READ_NEW | READ_DUMMY, false); + int readfile_result = readfile(fname, NULL, (linenr_T)0, (linenr_T)0, + (linenr_T)MAXLNUM, NULL, + READ_NEW | READ_DUMMY, false); newbuf->b_locked--; if (readfile_result == OK && !got_int @@ -5830,11 +5732,8 @@ static void unload_dummy_buffer(buf_T *buf, char *dirname_start) /// to 'list'. Returns OK on success. static int get_qfline_items(qfline_T *qfp, list_T *list) { - char buf[2]; - int bufnum; - // Handle entries with a non-existing buffer number. - bufnum = qfp->qf_fnum; + int bufnum = qfp->qf_fnum; if (bufnum != 0 && (buflist_findnr(bufnum) == NULL)) { bufnum = 0; } @@ -5842,6 +5741,7 @@ static int get_qfline_items(qfline_T *qfp, list_T *list) dict_T *const dict = tv_dict_alloc(); tv_list_append_dict(list, dict); + char buf[2]; buf[0] = qfp->qf_type; buf[1] = NUL; if (tv_dict_add_nr(dict, S_LEN("bufnr"), (varnumber_T)bufnum) == FAIL @@ -5882,9 +5782,6 @@ static int get_qfline_items(qfline_T *qfp, list_T *list) int get_errorlist(qf_info_T *qi_arg, win_T *wp, int qf_idx, int eidx, list_T *list) { qf_info_T *qi = qi_arg; - qf_list_T *qfl; - qfline_T *qfp; - int i; if (qi == NULL) { qi = &ql_info; @@ -5908,11 +5805,13 @@ int get_errorlist(qf_info_T *qi_arg, win_T *wp, int qf_idx, int eidx, list_T *li return FAIL; } - qfl = qf_get_list(qi, qf_idx); + qf_list_T *qfl = qf_get_list(qi, qf_idx); if (qf_list_empty(qfl)) { return FAIL; } + qfline_T *qfp; + int i; FOR_ALL_QFL_ITEMS(qfl, qfp, i) { if (eidx > 0) { if (eidx == i) { @@ -5949,11 +5848,11 @@ enum { static int qf_get_list_from_lines(dict_T *what, dictitem_T *di, dict_T *retdict) { int status = FAIL; - char *errorformat = p_efm; - dictitem_T *efm_di; // Only a List value is supported if (di->di_tv.v_type == VAR_LIST && di->di_tv.vval.v_list != NULL) { + char *errorformat = p_efm; + dictitem_T *efm_di; // If errorformat is supplied then use it, otherwise use the 'efm' // option setting if ((efm_di = tv_dict_find(what, S_LEN("efm"))) != NULL) { @@ -6253,11 +6152,9 @@ static int qf_getprop_qftf(qf_list_T *qfl, dict_T *retdict) int qf_get_properties(win_T *wp, dict_T *what, dict_T *retdict) { qf_info_T *qi = &ql_info; - qf_list_T *qfl; dictitem_T *di = NULL; int status = OK; int qf_idx = INVALID_QFIDX; - int eidx = 0; if ((di = tv_dict_find(what, S_LEN("lines"))) != NULL) { return qf_get_list_from_lines(what, di, retdict); @@ -6278,7 +6175,8 @@ int qf_get_properties(win_T *wp, dict_T *what, dict_T *retdict) return qf_getprop_defaults(qi, flags, wp != NULL, retdict); } - qfl = qf_get_list(qi, qf_idx); + qf_list_T *qfl = qf_get_list(qi, qf_idx); + int eidx = 0; // If an entry index is specified, use that if ((di = tv_dict_find(what, S_LEN("idx"))) != NULL) { @@ -6665,9 +6563,6 @@ static int qf_setprop_curidx(qf_info_T *qi, qf_list_T *qfl, const dictitem_T *di static int qf_set_properties(qf_info_T *qi, const dict_T *what, int action, char *title) FUNC_ATTR_NONNULL_ALL { - qf_list_T *qfl; - dictitem_T *di; - int retval = FAIL; bool newlist = action == ' ' || qf_stack_empty(qi); int qf_idx = qf_setprop_get_qfidx(qi, what, action, &newlist); if (qf_idx == INVALID_QFIDX) { // List not found @@ -6680,7 +6575,9 @@ static int qf_set_properties(qf_info_T *qi, const dict_T *what, int action, char qf_idx = qi->qf_curlist; } - qfl = qf_get_list(qi, qf_idx); + qf_list_T *qfl = qf_get_list(qi, qf_idx); + dictitem_T *di; + int retval = FAIL; if ((di = tv_dict_find(what, S_LEN("title"))) != NULL) { retval = qf_setprop_title(qi, qf_idx, what, di); } @@ -6761,7 +6658,6 @@ static void qf_free_stack(win_T *wp, qf_info_T *qi) int set_errorlist(win_T *wp, list_T *list, int action, char *title, dict_T *what) { qf_info_T *qi = &ql_info; - int retval = OK; if (wp != NULL) { qi = ll_get_or_alloc_list(wp); @@ -6781,6 +6677,7 @@ int set_errorlist(win_T *wp, list_T *list, int action, char *title, dict_T *what incr_quickfix_busy(); + int retval = OK; if (what != NULL) { retval = qf_set_properties(qi, what, action, title); } else { @@ -6911,14 +6808,7 @@ static int cbuffer_process_args(exarg_T *eap, buf_T **bufp, linenr_T *line1, lin // ":[range]lgetbuffer [bufnr]" command. void ex_cbuffer(exarg_T *eap) { - buf_T *buf = NULL; - char *au_name = NULL; - win_T *wp = NULL; - char *qf_title; - linenr_T line1; - linenr_T line2; - - au_name = cbuffer_get_auname(eap->cmdidx); + char *au_name = cbuffer_get_auname(eap->cmdidx); if (au_name != NULL && apply_autocmds(EVENT_QUICKFIXCMDPRE, au_name, curbuf->b_fname, true, curbuf)) { if (aborting()) { @@ -6927,13 +6817,17 @@ void ex_cbuffer(exarg_T *eap) } // Must come after autocommands. + win_T *wp = NULL; qf_info_T *qi = qf_cmd_get_or_alloc_stack(eap, &wp); + buf_T *buf = NULL; + linenr_T line1; + linenr_T line2; if (cbuffer_process_args(eap, &buf, &line1, &line2) == FAIL) { return; } - qf_title = qf_cmdtitle(*eap->cmdlinep); + char *qf_title = qf_cmdtitle(*eap->cmdlinep); if (buf->b_sfname) { vim_snprintf((char *)IObuff, IOSIZE, "%s (%s)", qf_title, buf->b_sfname); @@ -7001,10 +6895,7 @@ static char *cexpr_get_auname(cmdidx_T cmdidx) /// ":lexpr {expr}", ":lgetexpr {expr}", ":laddexpr {expr}" command. void ex_cexpr(exarg_T *eap) { - char *au_name = NULL; - win_T *wp = NULL; - - au_name = cexpr_get_auname(eap->cmdidx); + char *au_name = cexpr_get_auname(eap->cmdidx); if (au_name != NULL && apply_autocmds(EVENT_QUICKFIXCMDPRE, au_name, curbuf->b_fname, true, curbuf)) { if (aborting()) { @@ -7012,6 +6903,7 @@ void ex_cexpr(exarg_T *eap) } } + win_T *wp = NULL; qf_info_T *qi = qf_cmd_get_or_alloc_stack(eap, &wp); // Evaluate the expression. When the result is a string or a list we can @@ -7060,22 +6952,10 @@ cleanup: static qf_info_T *hgr_get_ll(bool *new_ll) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET { - win_T *wp; - qf_info_T *qi; - - // If the current window is a help window, then use it - if (bt_help(curwin->w_buffer)) { - wp = curwin; - } else { - // Find an existing help window - wp = qf_find_help_win(); - } + // If the current window is a help window, then use it, else find an existing help window + win_T *wp = bt_help(curwin->w_buffer) ? curwin : qf_find_help_win(); - if (wp == NULL) { // Help window not found - qi = NULL; - } else { - qi = wp->w_llist; - } + qf_info_T *qi = wp == NULL ? NULL : wp->w_llist; if (qi == NULL) { // Allocate a new location list for help text matches qi = qf_alloc_stack(QFLT_LOCATION); @@ -7151,8 +7031,7 @@ static void hgr_search_files_in_dir(qf_list_T *qfl, char *dirname, regmatch_T *p // Find all "*.txt" and "*.??x" files in the "doc" directory. add_pathsep(dirname); STRCAT(dirname, "doc/*.\\(txt\\|??x\\)"); // NOLINT - if (gen_expand_wildcards(1, (char_u **)&dirname, &fcount, - (char_u ***)&fnames, EW_FILE|EW_SILENT) == OK + if (gen_expand_wildcards(1, &dirname, &fcount, &fnames, EW_FILE|EW_SILENT) == OK && fcount > 0) { for (int fi = 0; fi < fcount && !got_int; fi++) { // Skip files for a different language. @@ -7166,7 +7045,7 @@ static void hgr_search_files_in_dir(qf_list_T *qfl, char *dirname, regmatch_T *p hgr_search_file(qfl, fnames[fi], p_regmatch); } - FreeWild(fcount, (char_u **)fnames); + FreeWild(fcount, fnames); } } @@ -7190,7 +7069,6 @@ static void hgr_search_in_rtp(qf_list_T *qfl, regmatch_T *p_regmatch, const char void ex_helpgrep(exarg_T *eap) { qf_info_T *qi = &ql_info; - bool new_qi = false; char *au_name = NULL; switch (eap->cmdidx) { @@ -7212,6 +7090,7 @@ void ex_helpgrep(exarg_T *eap) char *const save_cpo = p_cpo; p_cpo = (char *)empty_option; + bool new_qi = false; if (is_loclist_cmd(eap->cmdidx)) { qi = hgr_get_ll(&new_qi); } diff --git a/src/nvim/rbuffer.c b/src/nvim/rbuffer.c index 6407ac172e..2f718e9c2e 100644 --- a/src/nvim/rbuffer.c +++ b/src/nvim/rbuffer.c @@ -156,7 +156,7 @@ void rbuffer_consumed(RBuffer *buf, size_t count) /// Use instead of rbuffer_consumed to use rbuffer in a linear, non-cyclic fashion. /// -/// This is generally usefull if we can guarantee to parse all input +/// This is generally useful if we can guarantee to parse all input /// except some small incomplete token, like when parsing msgpack. void rbuffer_consumed_compact(RBuffer *buf, size_t count) FUNC_ATTR_NONNULL_ALL diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c index 4c49d30819..fbbf904f8b 100644 --- a/src/nvim/regexp.c +++ b/src/nvim/regexp.c @@ -176,12 +176,10 @@ static int backslash_trans(int c) return c; } -/* - * Check for a character class name "[:name:]". "pp" points to the '['. - * Returns one of the CLASS_ items. CLASS_NONE means that no item was - * recognized. Otherwise "pp" is advanced to after the item. - */ -static int get_char_class(char_u **pp) +/// Check for a character class name "[:name:]". "pp" points to the '['. +/// Returns one of the CLASS_ items. CLASS_NONE means that no item was +/// recognized. Otherwise "pp" is advanced to after the item. +static int get_char_class(char **pp) { static const char *(class_names[]) = { @@ -306,7 +304,7 @@ static void init_class_tab(void) // Global work variables for vim_regcomp(). -static char_u *regparse; ///< Input-scan pointer. +static char *regparse; ///< Input-scan pointer. static int regnpar; ///< () count. static bool wants_nfa; ///< regex should use NFA engine static int regnzpar; ///< \z() count. @@ -395,11 +393,11 @@ int re_multiline(const regprog_T *prog) * Returns a character representing the class. Zero means that no item was * recognized. Otherwise "pp" is advanced to after the item. */ -static int get_equi_class(char_u **pp) +static int get_equi_class(char **pp) { int c; int l = 1; - char_u *p = *pp; + char_u *p = (char_u *)(*pp); if (p[1] == '=' && p[2] != NUL) { l = utfc_ptr2len((char *)p + 2); @@ -418,11 +416,11 @@ static int get_equi_class(char_u **pp) * "pp" is advanced to after the item. * Currently only single characters are recognized! */ -static int get_coll_element(char_u **pp) +static int get_coll_element(char **pp) { int c; int l = 1; - char_u *p = *pp; + char_u *p = (char_u *)(*pp); if (p[0] != NUL && p[1] == '.' && p[2] != NUL) { l = utfc_ptr2len((char *)p + 2); @@ -442,12 +440,10 @@ static void get_cpo_flags(void) reg_cpo_lit = vim_strchr(p_cpo, CPO_LITERAL) != NULL; } -/* - * Skip over a "[]" range. - * "p" must point to the character after the '['. - * The returned pointer is on the matching ']', or the terminating NUL. - */ -static char_u *skip_anyof(char_u *p) +/// Skip over a "[]" range. +/// "p" must point to the character after the '['. +/// The returned pointer is on the matching ']', or the terminating NUL. +static char_u *skip_anyof(char *p) { int l; @@ -458,7 +454,7 @@ static char_u *skip_anyof(char_u *p) p++; } while (*p != NUL && *p != ']') { - if ((l = utfc_ptr2len((char *)p)) > 1) { + if ((l = utfc_ptr2len(p)) > 1) { p += l; } else if (*p == '-') { p++; @@ -482,7 +478,7 @@ static char_u *skip_anyof(char_u *p) } } - return p; + return (char_u *)p; } /* @@ -512,7 +508,7 @@ char_u *skip_regexp(char_u *startp, int dirc, int magic, char_u **newp) } if ((p[0] == '[' && mymagic >= MAGIC_ON) || (p[0] == '\\' && p[1] == '[' && mymagic <= MAGIC_OFF)) { - p = skip_anyof(p + 1); + p = skip_anyof((char *)p + 1); if (p[0] == NUL) { break; } @@ -547,7 +543,7 @@ static int prev_at_start; // True when on the second character */ static void initchr(char_u *str) { - regparse = str; + regparse = (char *)str; prevchr_len = 0; curchr = prevprevchr = prevchr = nextchr = -1; at_start = true; @@ -560,7 +556,7 @@ static void initchr(char_u *str) */ static void save_parse_state(parse_state_T *ps) { - ps->regparse = regparse; + ps->regparse = (char_u *)regparse; ps->prevchr_len = prevchr_len; ps->curchr = curchr; ps->prevchr = prevchr; @@ -576,7 +572,7 @@ static void save_parse_state(parse_state_T *ps) */ static void restore_parse_state(parse_state_T *ps) { - regparse = ps->regparse; + regparse = (char *)ps->regparse; prevchr_len = ps->prevchr_len; curchr = ps->curchr; prevchr = ps->prevchr; @@ -598,7 +594,7 @@ static int peekchr(void) return curchr; } - switch (curchr = regparse[0]) { + switch (curchr = (uint8_t)regparse[0]) { case '.': case '[': case '~': @@ -669,7 +665,7 @@ static int peekchr(void) // '$' is only magic as the very last char and if it's in front of // either "\|", "\)", "\&", or "\n" if (reg_magic >= MAGIC_OFF) { - char_u *p = regparse + 1; + char_u *p = (char_u *)regparse + 1; bool is_magic_all = (reg_magic == MAGIC_ALL); // ignore \c \C \m \M \v \V and \Z after '$' @@ -696,7 +692,7 @@ static int peekchr(void) } break; case '\\': { - int c = regparse[1]; + int c = (uint8_t)regparse[1]; if (c == NUL) { curchr = '\\'; // trailing '\' @@ -725,13 +721,13 @@ static int peekchr(void) } else { // Next character can never be (made) magic? // Then backslashing it won't do anything. - curchr = utf_ptr2char((char *)regparse + 1); + curchr = utf_ptr2char(regparse + 1); } break; } default: - curchr = utf_ptr2char((char *)regparse); + curchr = utf_ptr2char(regparse); } return curchr; @@ -750,7 +746,7 @@ static void skipchr(void) } if (regparse[prevchr_len] != NUL) { // Exclude composing chars that utfc_ptr2len does include. - prevchr_len += utf_ptr2len((char *)regparse + prevchr_len); + prevchr_len += utf_ptr2len(regparse + prevchr_len); } regparse += prevchr_len; prev_at_start = at_start; @@ -820,8 +816,8 @@ static int64_t gethexchrs(int maxinputlen) int c; int i; - for (i = 0; i < maxinputlen; ++i) { - c = regparse[0]; + for (i = 0; i < maxinputlen; i++) { + c = (uint8_t)regparse[0]; if (!ascii_isxdigit(c)) { break; } @@ -846,8 +842,8 @@ static int64_t getdecchrs(void) int c; int i; - for (i = 0;; ++i) { - c = regparse[0]; + for (i = 0;; i++) { + c = (uint8_t)regparse[0]; if (c < '0' || c > '9') { break; } @@ -878,7 +874,7 @@ static int64_t getoctchrs(void) int i; for (i = 0; i < 3 && nr < 040; i++) { // -V536 - c = regparse[0]; + c = (uint8_t)regparse[0]; if (c < '0' || c > '7') { break; } @@ -910,7 +906,7 @@ static int read_limits(long *minval, long *maxval) regparse++; reverse = true; } - first_char = regparse; + first_char = (char_u *)regparse; *minval = getdigits_long(®parse, false, 0); if (*regparse == ',') { // There is a comma. if (ascii_isdigit(*++regparse)) { @@ -1270,8 +1266,8 @@ static int match_with_backref(linenr_T start_lnum, colnr_T start_col, linenr_T e if (reg_tofree == NULL || len >= (int)reg_tofreelen) { len += 50; // get some extra xfree(reg_tofree); - reg_tofree = xmalloc(len); - reg_tofreelen = len; + reg_tofree = xmalloc((size_t)len); + reg_tofreelen = (unsigned)len; } STRCPY(reg_tofree, rex.line); rex.input = reg_tofree + (rex.input - rex.line); @@ -1554,7 +1550,7 @@ char_u *regtilde(char_u *source, int magic, bool preview) if (reg_prev_sub != NULL) { // length = len(newsub) - 1 + len(prev_sub) + 1 prevlen = (int)STRLEN(reg_prev_sub); - tmpsub = xmalloc(STRLEN(newsub) + prevlen); + tmpsub = xmalloc(STRLEN(newsub) + (size_t)prevlen); // copy prefix len = (int)(p - newsub); // not including ~ memmove(tmpsub, newsub, (size_t)len); @@ -1587,12 +1583,10 @@ char_u *regtilde(char_u *source, int magic, bool preview) // Only change reg_prev_sub when not previewing. if (!preview) { + // Store a copy of newsub in reg_prev_sub. It is always allocated, + // because recursive calls may make the returned string invalid. xfree(reg_prev_sub); - if (newsub != source) { // newsub was allocated, just keep it - reg_prev_sub = newsub; - } else { // no ~ found, need to save newsub - reg_prev_sub = vim_strsave(newsub); - } + reg_prev_sub = vim_strsave(newsub); } return newsub; @@ -1635,7 +1629,7 @@ static int fill_submatch_list(int argc FUNC_ATTR_UNUSED, typval_T *argv, int arg if (s == NULL || rsm.sm_match->endp[i] == NULL) { s = NULL; } else { - s = vim_strnsave(s, rsm.sm_match->endp[i] - s); + s = vim_strnsave(s, (size_t)(rsm.sm_match->endp[i] - s)); } TV_LIST_ITEM_TV(li)->v_type = VAR_STRING; TV_LIST_ITEM_TV(li)->vval.v_string = (char *)s; @@ -1655,7 +1649,7 @@ static void clear_submatch_list(staticList10_T *sl) /// vim_regexec_multi() match. /// /// If "flags" has REGSUB_COPY really copy into "dest[destlen]". -/// Oterwise nothing is copied, only compue the length of the result. +/// Otherwise nothing is copied, only compute the length of the result. /// /// If "flags" has REGSUB_MAGIC then behave like 'magic' is set. /// @@ -1922,7 +1916,7 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, int des iemsg("vim_regsub_both(): not enough space"); return 0; } - *dst++ = c; + *dst++ = (char_u)c; *dst++ = *src++; *dst++ = *src++; } else { @@ -2197,7 +2191,7 @@ char_u *reg_submatch(int no) if (round == 2) { STRCPY(retval + len, s); } - len += STRLEN(s); + len += (ssize_t)STRLEN(s); if (round == 2) { retval[len] = '\n'; } @@ -2215,7 +2209,7 @@ char_u *reg_submatch(int no) } if (retval == NULL) { - retval = xmalloc(len); + retval = xmalloc((size_t)len); } } } else { @@ -2223,7 +2217,7 @@ char_u *reg_submatch(int no) if (s == NULL || rsm.sm_match->endp[no] == NULL) { retval = NULL; } else { - retval = vim_strnsave(s, rsm.sm_match->endp[no] - s); + retval = vim_strnsave(s, (size_t)(rsm.sm_match->endp[no] - s)); } } @@ -2328,7 +2322,7 @@ regprog_T *vim_regcomp(char *expr_arg, int re_flags) regprog_T *prog = NULL; char_u *expr = (char_u *)expr_arg; - regexp_engine = p_re; + regexp_engine = (int)p_re; // Check for prefix "\%#=", that sets the regexp engine if (STRNCMP(expr, "\\%#=", 4) == 0) { @@ -2396,8 +2390,8 @@ regprog_T *vim_regcomp(char *expr_arg, int re_flags) if (prog != NULL) { // Store the info needed to call regcomp() again when the engine turns out // to be very slow when executing it. - prog->re_engine = regexp_engine; - prog->re_flags = re_flags; + prog->re_engine = (unsigned)regexp_engine; + prog->re_flags = (unsigned)re_flags; } return prog; @@ -2475,8 +2469,8 @@ static bool vim_regexec_string(regmatch_T *rmp, char_u *line, colnr_T col, bool // NFA engine aborted because it's very slow, use backtracking engine instead. if (rmp->regprog->re_engine == AUTOMATIC_ENGINE && result == NFA_TOO_EXPENSIVE) { - int save_p_re = p_re; - int re_flags = rmp->regprog->re_flags; + int save_p_re = (int)p_re; + int re_flags = (int)rmp->regprog->re_flags; char_u *pat = vim_strsave(((nfa_regprog_T *)rmp->regprog)->pattern); p_re = BACKTRACKING_ENGINE; @@ -2560,15 +2554,14 @@ long vim_regexec_multi(regmmatch_T *rmp, win_T *win, buf_T *buf, linenr_T lnum, } rex_in_use = true; - int result = rmp->regprog->engine->regexec_multi(rmp, win, buf, lnum, col, - tm, timed_out); + long result = rmp->regprog->engine->regexec_multi(rmp, win, buf, lnum, col, tm, timed_out); rmp->regprog->re_in_use = false; // NFA engine aborted because it's very slow, use backtracking engine instead. if (rmp->regprog->re_engine == AUTOMATIC_ENGINE && result == NFA_TOO_EXPENSIVE) { - int save_p_re = p_re; - int re_flags = rmp->regprog->re_flags; + int save_p_re = (int)p_re; + int re_flags = (int)rmp->regprog->re_flags; char_u *pat = vim_strsave(((nfa_regprog_T *)rmp->regprog)->pattern); p_re = BACKTRACKING_ENGINE; @@ -2589,8 +2582,7 @@ long vim_regexec_multi(regmmatch_T *rmp, win_T *win, buf_T *buf, linenr_T lnum, vim_regfree(prev_prog); rmp->regprog->re_in_use = true; - result = rmp->regprog->engine->regexec_multi(rmp, win, buf, lnum, col, - tm, timed_out); + result = rmp->regprog->engine->regexec_multi(rmp, win, buf, lnum, col, tm, timed_out); rmp->regprog->re_in_use = false; } diff --git a/src/nvim/regexp_bt.c b/src/nvim/regexp_bt.c index 272429bb91..fff5ae9b7f 100644 --- a/src/nvim/regexp_bt.c +++ b/src/nvim/regexp_bt.c @@ -492,7 +492,7 @@ static void regc(int b) if (regcode == JUST_CALC_SIZE) { regsize++; } else { - *regcode++ = b; + *regcode++ = (char_u)b; } } @@ -1493,7 +1493,7 @@ static char_u *regnode(int op) if (ret == JUST_CALC_SIZE) { regsize += 3; } else { - *regcode++ = op; + *regcode++ = (char_u)op; *regcode++ = NUL; // Null "next" pointer. *regcode++ = NUL; } @@ -1610,7 +1610,7 @@ static void reginsert(int op, char_u *opnd) } place = opnd; // Op node, where operand used to be. - *place++ = op; + *place++ = (char_u)op; *place++ = NUL; *place = NUL; } @@ -1637,7 +1637,7 @@ static void reginsert_nr(int op, long val, char_u *opnd) } place = opnd; // Op node, where operand used to be. - *place++ = op; + *place++ = (char_u)op; *place++ = NUL; *place++ = NUL; assert(val >= 0 && (uintmax_t)val <= UINT32_MAX); @@ -1668,7 +1668,7 @@ static void reginsert_limits(int op, long minval, long maxval, char_u *opnd) } place = opnd; // Op node, where operand used to be. - *place++ = op; + *place++ = (char_u)op; *place++ = NUL; *place++ = NUL; assert(minval >= 0 && (uintmax_t)minval <= UINT32_MAX); @@ -1689,7 +1689,7 @@ static int seen_endbrace(int refnum) // Trick: check if "@<=" or "@<!" follows, in which case // the \1 can appear before the referenced match. - for (p = regparse; *p != NUL; p++) { + for (p = (char_u *)regparse; *p != NUL; p++) { if (p[0] == '@' && p[1] == '<' && (p[2] == '!' || p[2] == '=')) { break; } @@ -2071,7 +2071,7 @@ static char_u *regatom(int *flagp) EMSG2_RET_NULL(_("E678: Invalid character after %s%%[dxouU]"), reg_magic == MAGIC_ALL); } - if (use_multibytecode(i)) { + if (use_multibytecode((int)i)) { ret = regnode(MULTIBYTECODE); } else { ret = regnode(EXACTLY); @@ -2079,7 +2079,7 @@ static char_u *regatom(int *flagp) if (i == 0) { regc(0x0a); } else { - regmbc(i); + regmbc((int)i); } regc(NUL); *flagp |= HASWIDTH; @@ -2111,8 +2111,8 @@ static char_u *regatom(int *flagp) if (ret == JUST_CALC_SIZE) { regsize += 2; } else { - *regcode++ = c; - *regcode++ = cmp; + *regcode++ = (char_u)c; + *regcode++ = (char_u)cmp; } break; } else if (c == 'l' || c == 'c' || c == 'v') { @@ -2123,7 +2123,7 @@ static char_u *regatom(int *flagp) } if (c == 'l') { if (cur) { - n = curwin->w_cursor.lnum; + n = (uint32_t)curwin->w_cursor.lnum; } ret = regnode(RE_LNUM); if (save_prev_at_start) { @@ -2131,7 +2131,7 @@ static char_u *regatom(int *flagp) } } else if (c == 'c') { if (cur) { - n = curwin->w_cursor.col; + n = (uint32_t)curwin->w_cursor.col; n++; } ret = regnode(RE_COL); @@ -2139,7 +2139,7 @@ static char_u *regatom(int *flagp) if (cur) { colnr_T vcol = 0; getvvcol(curwin, &curwin->w_cursor, NULL, NULL, &vcol); - n = ++vcol; + n = (uint32_t)(++vcol); } ret = regnode(RE_VCOL); } @@ -2149,7 +2149,7 @@ static char_u *regatom(int *flagp) // put the number and the optional // comparator after the opcode regcode = re_put_uint32(regcode, n); - *regcode++ = cmp; + *regcode++ = (char_u)cmp; } break; } @@ -2469,7 +2469,7 @@ do_multibyte: // Need to get composing character too. for (;;) { l = utf_ptr2len((char *)regparse); - if (!utf_composinglike(regparse, regparse + l)) { + if (!utf_composinglike((char_u *)regparse, (char_u *)regparse + l)) { break; } regmbc(utf_ptr2char((char *)regparse)); @@ -2910,7 +2910,7 @@ static regprog_T *bt_regcomp(char_u *expr, int re_flags) } // Allocate space. - bt_regprog_T *r = xmalloc(sizeof(bt_regprog_T) + regsize); + bt_regprog_T *r = xmalloc(sizeof(bt_regprog_T) + (size_t)regsize); r->re_in_use = false; // Second pass: emit code. @@ -2938,7 +2938,7 @@ static regprog_T *bt_regcomp(char_u *expr, int re_flags) r->regflags |= RF_LOOKBH; } // Remember whether this pattern has any \z specials in it. - r->reghasz = re_has_z; + r->reghasz = (char_u)re_has_z; scan = r->program + 1; // First BRANCH. if (OP(regnext(scan)) == END) { // Only one top-level choice. scan = OPERAND(scan); @@ -3027,7 +3027,7 @@ static int coll_get_char(void) regparse--; nr = '\\'; } - return nr; + return (int)nr; } /* @@ -3505,7 +3505,7 @@ static regitem_T *regstack_push(regstate_T state, char_u *scan) rp->rs_state = state; rp->rs_scan = scan; - regstack.ga_len += sizeof(regitem_T); + regstack.ga_len += (int)sizeof(regitem_T); return rp; } @@ -3519,7 +3519,7 @@ static void regstack_pop(char_u **scan) rp = (regitem_T *)((char *)regstack.ga_data + regstack.ga_len) - 1; *scan = rp->rs_scan; - regstack.ga_len -= sizeof(regitem_T); + regstack.ga_len -= (int)sizeof(regitem_T); } // Save the current subexpr to "bp", so that they can be restored @@ -3704,9 +3704,7 @@ static bool regmatch(char_u *scan, proftime_T *tm, int *timed_out) int mark = OPERAND(scan)[0]; int cmp = OPERAND(scan)[1]; pos_T *pos; - size_t col = REG_MULTI ? rex.input - rex.line : 0; - - // fm will be NULL if the mark is not set in reg_buf + size_t col = REG_MULTI ? (size_t)(rex.input - rex.line) : 0; fmark_T *fm = mark_get(rex.reg_buf, curwin, NULL, kMarkBufLocal, mark); // Line may have been freed, get it again. @@ -4170,7 +4168,7 @@ static bool regmatch(char_u *scan, proftime_T *tm, int *timed_out) if (rp == NULL) { status = RA_FAIL; } else { - rp->rs_no = no; + rp->rs_no = (int16_t)no; save_se(&rp->rs_un.sesave, &rex.reg_startpos[no], &rex.reg_startp[no]); // We simply continue and handle the result when done. @@ -4200,7 +4198,7 @@ static bool regmatch(char_u *scan, proftime_T *tm, int *timed_out) if (rp == NULL) { status = RA_FAIL; } else { - rp->rs_no = no; + rp->rs_no = (int16_t)no; save_se(&rp->rs_un.sesave, ®_startzpos[no], ®_startzp[no]); // We simply continue and handle the result when done. @@ -4223,7 +4221,7 @@ static bool regmatch(char_u *scan, proftime_T *tm, int *timed_out) if (rp == NULL) { status = RA_FAIL; } else { - rp->rs_no = no; + rp->rs_no = (int16_t)no; save_se(&rp->rs_un.sesave, &rex.reg_endpos[no], &rex.reg_endp[no]); // We simply continue and handle the result when done. } @@ -4244,7 +4242,7 @@ static bool regmatch(char_u *scan, proftime_T *tm, int *timed_out) if (rp == NULL) { status = RA_FAIL; } else { - rp->rs_no = no; + rp->rs_no = (int16_t)no; save_se(&rp->rs_un.sesave, ®_endzpos[no], ®_endzp[no]); // We simply continue and handle the result when done. @@ -4380,7 +4378,7 @@ static bool regmatch(char_u *scan, proftime_T *tm, int *timed_out) if (rp == NULL) { status = RA_FAIL; } else { - rp->rs_no = no; + rp->rs_no = (int16_t)no; reg_save(&rp->rs_un.regsave, &backpos); next = OPERAND(scan); // We continue and handle the result when done. @@ -4396,7 +4394,7 @@ static bool regmatch(char_u *scan, proftime_T *tm, int *timed_out) if (rp == NULL) { status = RA_FAIL; } else { - rp->rs_no = no; + rp->rs_no = (int16_t)no; reg_save(&rp->rs_un.regsave, &backpos); next = OPERAND(scan); // We continue and handle the result when done. @@ -4465,7 +4463,7 @@ static bool regmatch(char_u *scan, proftime_T *tm, int *timed_out) status = RA_FAIL; } else { ga_grow(®stack, sizeof(regstar_T)); - regstack.ga_len += sizeof(regstar_T); + regstack.ga_len += (int)sizeof(regstar_T); rp = regstack_push(rst.minval <= rst.maxval ? RS_STAR_LONG : RS_STAR_SHORT, scan); if (rp == NULL) { status = RA_FAIL; @@ -4487,7 +4485,7 @@ static bool regmatch(char_u *scan, proftime_T *tm, int *timed_out) if (rp == NULL) { status = RA_FAIL; } else { - rp->rs_no = op; + rp->rs_no = (int16_t)op; reg_save(&rp->rs_un.regsave, &backpos); next = OPERAND(scan); // We continue and handle the result when done. @@ -4502,7 +4500,7 @@ static bool regmatch(char_u *scan, proftime_T *tm, int *timed_out) status = RA_FAIL; } else { ga_grow(®stack, sizeof(regbehind_T)); - regstack.ga_len += sizeof(regbehind_T); + regstack.ga_len += (int)sizeof(regbehind_T); rp = regstack_push(RS_BEHIND1, scan); if (rp == NULL) { status = RA_FAIL; @@ -4511,7 +4509,7 @@ static bool regmatch(char_u *scan, proftime_T *tm, int *timed_out) // when there is a match but we don't use it. save_subexpr(((regbehind_T *)rp) - 1); - rp->rs_no = op; + rp->rs_no = (int16_t)op; reg_save(&rp->rs_un.regsave, &backpos); // First try if what follows matches. If it does then we // check the behind match by looping. @@ -4691,7 +4689,7 @@ static bool regmatch(char_u *scan, proftime_T *tm, int *timed_out) case RS_BEHIND1: if (status == RA_NOMATCH) { regstack_pop(&scan); - regstack.ga_len -= sizeof(regbehind_T); + regstack.ga_len -= (int)sizeof(regbehind_T); } else { // The stuff after BEHIND/NOBEHIND matches. Now try if // the behind part does (not) match before the current @@ -4734,7 +4732,7 @@ static bool regmatch(char_u *scan, proftime_T *tm, int *timed_out) restore_subexpr(((regbehind_T *)rp) - 1); } regstack_pop(&scan); - regstack.ga_len -= sizeof(regbehind_T); + regstack.ga_len -= (int)sizeof(regbehind_T); } else { long limit; @@ -4808,7 +4806,7 @@ static bool regmatch(char_u *scan, proftime_T *tm, int *timed_out) } } regstack_pop(&scan); - regstack.ga_len -= sizeof(regbehind_T); + regstack.ga_len -= (int)sizeof(regbehind_T); } } break; @@ -4819,7 +4817,7 @@ static bool regmatch(char_u *scan, proftime_T *tm, int *timed_out) if (status == RA_MATCH) { regstack_pop(&scan); - regstack.ga_len -= sizeof(regstar_T); + regstack.ga_len -= (int)sizeof(regstar_T); break; } @@ -4885,7 +4883,7 @@ static bool regmatch(char_u *scan, proftime_T *tm, int *timed_out) if (status != RA_CONT) { // Failed. regstack_pop(&scan); - regstack.ga_len -= sizeof(regstar_T); + regstack.ga_len -= (int)sizeof(regstar_T); status = RA_NOMATCH; } } @@ -4976,15 +4974,13 @@ static long regtry(bt_regprog_T *prog, colnr_T col, proftime_T *tm, int *timed_o && reg_endzpos[i].lnum == reg_startzpos[i].lnum && reg_endzpos[i].col >= reg_startzpos[i].col) { re_extmatch_out->matches[i] = - vim_strnsave(reg_getline(reg_startzpos[i].lnum) - + reg_startzpos[i].col, - reg_endzpos[i].col - - reg_startzpos[i].col); + vim_strnsave(reg_getline(reg_startzpos[i].lnum) + reg_startzpos[i].col, + (size_t)(reg_endzpos[i].col - reg_startzpos[i].col)); } } else { if (reg_startzp[i] != NULL && reg_endzp[i] != NULL) { re_extmatch_out->matches[i] = - vim_strnsave(reg_startzp[i], reg_endzp[i] - reg_startzp[i]); + vim_strnsave(reg_startzp[i], (size_t)(reg_endzp[i] - reg_startzp[i])); } } } diff --git a/src/nvim/regexp_nfa.c b/src/nvim/regexp_nfa.c index 870af3eafc..1a5c250664 100644 --- a/src/nvim/regexp_nfa.c +++ b/src/nvim/regexp_nfa.c @@ -545,7 +545,7 @@ static char_u *nfa_get_match_text(nfa_state_T *start) return NULL; } - ret = xmalloc(len); + ret = xmalloc((size_t)len); p = start->out->out; // skip first char, it goes into regstart s = ret; while (p->c > 0) { @@ -565,7 +565,7 @@ static void realloc_post_list(void) { // For weird patterns the number of states can be very high. Increasing by // 50% seems a reasonable compromise between memory use and speed. - const size_t new_max = (post_end - post_start) * 3 / 2; + const size_t new_max = (size_t)(post_end - post_start) * 3 / 2; int *new_start = xrealloc(post_start, new_max * sizeof(int)); post_ptr = new_start + (post_ptr - post_start); post_end = new_start + new_max; @@ -1807,7 +1807,7 @@ static int nfa_regatom(void) int got_coll_char; char_u *p; char_u *endp; - char_u *old_regparse = regparse; + char_u *old_regparse = (char_u *)regparse; int extra = 0; int emit_range; int negated; @@ -1905,7 +1905,7 @@ static int nfa_regatom(void) // When '.' is followed by a composing char ignore the dot, so that // the composing char is matched here. if (c == Magic('.') && utf_iscomposing(peekchr())) { - old_regparse = regparse; + old_regparse = (char_u *)regparse; c = getchr(); goto nfa_do_multibyte; } @@ -2079,7 +2079,7 @@ static int nfa_regatom(void) } // A NUL is stored in the text as NL // TODO(vim): what if a composing character follows? - EMIT(nr == 0 ? 0x0a : nr); + EMIT(nr == 0 ? 0x0a : (int)nr); } break; @@ -2226,16 +2226,15 @@ collection: * - ranges, two characters followed by NFA_RANGE. */ - p = regparse; - endp = skip_anyof(p); + p = (char_u *)regparse; + endp = skip_anyof((char *)p); if (*endp == ']') { /* * Try to reverse engineer character classes. For example, * recognize that [0-9] stands for \d and [A-Za-z_] for \h, * and perform the necessary substitutions in the NFA. */ - int result = nfa_recognize_char_class(regparse, endp, - extra == NFA_ADD_NL); + int result = nfa_recognize_char_class((char_u *)regparse, endp, extra == NFA_ADD_NL); if (result != FAIL) { if (result >= NFA_FIRST_NL && result <= NFA_LAST_NL) { EMIT(result - NFA_ADD_NL); @@ -2244,7 +2243,7 @@ collection: } else { EMIT(result); } - regparse = endp; + regparse = (char *)endp; MB_PTR_ADV(regparse); return OK; } @@ -2269,7 +2268,7 @@ collection: } // Emit the OR branches for each character in the [] emit_range = false; - while (regparse < endp) { + while ((char_u *)regparse < endp) { int oldstartc = startc; startc = -1; got_coll_char = false; @@ -2376,7 +2375,7 @@ collection: // accepts "\t", "\e", etc., but only when the 'l' flag in // 'cpoptions' is not included. if (*regparse == '\\' - && regparse + 1 <= endp + && (char_u *)regparse + 1 <= endp && (vim_strchr(REGEXP_INRANGE, regparse[1]) != NULL || (!reg_cpo_lit && vim_strchr(REGEXP_ABBR, regparse[1]) @@ -2479,7 +2478,7 @@ collection: } // skip the trailing ] - regparse = endp; + regparse = (char *)endp; MB_PTR_ADV(regparse); // Mark end of the collection. @@ -2531,7 +2530,7 @@ nfa_do_multibyte: c = utf_ptr2char((char *)old_regparse + i); } EMIT(NFA_COMPOSING); - regparse = old_regparse + plen; + regparse = (char *)old_regparse + plen; } else { c = no_Magic(c); EMIT(c); @@ -2646,7 +2645,7 @@ static int nfa_regpiece(void) EMIT(i); if (i == NFA_PREV_ATOM_JUST_BEFORE || i == NFA_PREV_ATOM_JUST_BEFORE_NEG) { - EMIT(c2); + EMIT((int)c2); } break; @@ -3850,7 +3849,7 @@ static nfa_state_T *post2nfa(int *postfix, int *end, int nfa_calc_size) if (nfa_calc_size == false) { // Allocate space for the stack. Max states on the stack: "nstate". - stack = xmalloc((nstate + 1) * sizeof(Frag_T)); + stack = xmalloc((size_t)(nstate + 1) * sizeof(Frag_T)); stackp = stack; stack_end = stack + (nstate + 1); } @@ -4472,10 +4471,9 @@ static void clear_sub(regsub_T *sub) { if (REG_MULTI) { // Use 0xff to set lnum to -1 - memset(sub->list.multi, 0xff, - sizeof(struct multipos) * rex.nfa_nsubexpr); + memset(sub->list.multi, 0xff, sizeof(struct multipos) * (size_t)rex.nfa_nsubexpr); } else { - memset(sub->list.line, 0, sizeof(struct linepos) * rex.nfa_nsubexpr); + memset(sub->list.line, 0, sizeof(struct linepos) * (size_t)rex.nfa_nsubexpr); } sub->in_use = 0; } @@ -4489,13 +4487,11 @@ static void copy_sub(regsub_T *to, regsub_T *from) if (from->in_use > 0) { // Copy the match start and end positions. if (REG_MULTI) { - memmove(&to->list.multi[0], - &from->list.multi[0], - sizeof(struct multipos) * from->in_use); + memmove(&to->list.multi[0], &from->list.multi[0], + sizeof(struct multipos) * (size_t)from->in_use); } else { - memmove(&to->list.line[0], - &from->list.line[0], - sizeof(struct linepos) * from->in_use); + memmove(&to->list.line[0], &from->list.line[0], + sizeof(struct linepos) * (size_t)from->in_use); } } } @@ -4511,13 +4507,11 @@ static void copy_sub_off(regsub_T *to, regsub_T *from) if (from->in_use > 1) { // Copy the match start and end positions. if (REG_MULTI) { - memmove(&to->list.multi[1], - &from->list.multi[1], - sizeof(struct multipos) * (from->in_use - 1)); + memmove(&to->list.multi[1], &from->list.multi[1], + sizeof(struct multipos) * (size_t)(from->in_use - 1)); } else { - memmove(&to->list.line[1], - &from->list.line[1], - sizeof(struct linepos) * (from->in_use - 1)); + memmove(&to->list.line[1], &from->list.line[1], + sizeof(struct linepos) * (size_t)(from->in_use - 1)); } } } @@ -4964,7 +4958,7 @@ skip_add: // be (a lot) bigger than anticipated. if (l->n == l->len) { const int newlen = l->len * 3 / 2 + 50; - const size_t newsize = newlen * sizeof(nfa_thread_T); + const size_t newsize = (size_t)newlen * sizeof(nfa_thread_T); if ((long)(newsize >> 10) >= p_mmp) { emsg(_(e_maxmempat)); @@ -5255,7 +5249,7 @@ static regsubs_T *addstate_here(nfa_list_T *l, nfa_state_T *state, regsubs_T *su // not enough space to move the new states, reallocate the list // and move the states to the right position const int newlen = l->len * 3 / 2 + 50; - const size_t newsize = newlen * sizeof(nfa_thread_T); + const size_t newsize = (size_t)newlen * sizeof(nfa_thread_T); if ((long)(newsize >> 10) >= p_mmp) { emsg(_(e_maxmempat)); @@ -5265,13 +5259,13 @@ static regsubs_T *addstate_here(nfa_list_T *l, nfa_state_T *state, regsubs_T *su l->len = newlen; memmove(&(newl[0]), &(l->t[0]), - sizeof(nfa_thread_T) * listidx); + sizeof(nfa_thread_T) * (size_t)listidx); memmove(&(newl[listidx]), &(l->t[l->n - count]), - sizeof(nfa_thread_T) * count); + sizeof(nfa_thread_T) * (size_t)count); memmove(&(newl[listidx + count]), &(l->t[listidx + 1]), - sizeof(nfa_thread_T) * (l->n - count - listidx - 1)); + sizeof(nfa_thread_T) * (size_t)(l->n - count - listidx - 1)); xfree(l->t); l->t = newl; } else { @@ -5279,10 +5273,10 @@ static regsubs_T *addstate_here(nfa_list_T *l, nfa_state_T *state, regsubs_T *su // end to the current position memmove(&(l->t[listidx + count]), &(l->t[listidx + 1]), - sizeof(nfa_thread_T) * (l->n - listidx - 1)); + sizeof(nfa_thread_T) * (size_t)(l->n - listidx - 1)); memmove(&(l->t[listidx]), &(l->t[l->n - 1]), - sizeof(nfa_thread_T) * count); + sizeof(nfa_thread_T) * (size_t)count); } } --l->n; @@ -5621,7 +5615,7 @@ static int recursive_regmatch(nfa_state_T *state, nfa_pim_T *pim, nfa_regprog_T // values and clear them. if (*listids == NULL || *listids_len < prog->nstate) { xfree(*listids); - *listids = xmalloc(sizeof(**listids) * prog->nstate); + *listids = xmalloc(sizeof(**listids) * (size_t)prog->nstate); *listids_len = prog->nstate; } nfa_save_listids(prog, *listids); @@ -5888,7 +5882,7 @@ static long find_match_text(colnr_T startcol, int regstart, char_u *match_text) rex.reg_startpos[0].lnum = rex.lnum; rex.reg_startpos[0].col = col; rex.reg_endpos[0].lnum = rex.lnum; - rex.reg_endpos[0].col = s2 - rex.line; + rex.reg_endpos[0].col = (colnr_T)(s2 - rex.line); } else { rex.reg_startp[0] = rex.line + col; rex.reg_endp[0] = s2; @@ -5969,7 +5963,7 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm nfa_match = false; // Allocate memory for the lists of nodes. - size_t size = (prog->nstate + 1) * sizeof(nfa_thread_T); + size_t size = (size_t)(prog->nstate + 1) * sizeof(nfa_thread_T); list[0].t = xmalloc(size); list[0].len = prog->nstate + 1; list[1].t = xmalloc(size); @@ -6930,10 +6924,8 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm case NFA_MARK: case NFA_MARK_GT: case NFA_MARK_LT: { - fmark_T *fm; - size_t col = REG_MULTI ? rex.input - rex.line : 0; - // fm will be NULL if the mark is not set, doesn't belong to reg_buf - fm = mark_get(rex.reg_buf, curwin, NULL, kMarkBufLocal, t->state->val); + size_t col = REG_MULTI ? (size_t)(rex.input - rex.line) : 0; + fmark_T *fm = mark_get(rex.reg_buf, curwin, NULL, kMarkBufLocal, t->state->val); // Line may have been freed, get it again. if (REG_MULTI) { @@ -7371,14 +7363,14 @@ static long nfa_regtry(nfa_regprog_T *prog, colnr_T col, proftime_T *tm, int *ti && mpos->end_col >= mpos->start_col) { re_extmatch_out->matches[i] = vim_strnsave(reg_getline(mpos->start_lnum) + mpos->start_col, - mpos->end_col - mpos->start_col); + (size_t)(mpos->end_col - mpos->start_col)); } } else { struct linepos *lpos = &subs.synt.list.line[i]; if (lpos->start != NULL && lpos->end != NULL) { re_extmatch_out->matches[i] = - vim_strnsave(lpos->start, lpos->end - lpos->start); + vim_strnsave(lpos->start, (size_t)(lpos->end - lpos->start)); } } } @@ -7569,7 +7561,7 @@ static regprog_T *nfa_regcomp(char_u *expr, int re_flags) post2nfa(postfix, post_ptr, true); // allocate the regprog with space for the compiled regexp - size_t prog_size = sizeof(nfa_regprog_T) + sizeof(nfa_state_T) * (nstate - 1); + size_t prog_size = sizeof(nfa_regprog_T) + sizeof(nfa_state_T) * (size_t)(nstate - 1); prog = xmalloc(prog_size); state_ptr = prog->state; prog->re_in_use = false; @@ -7653,7 +7645,7 @@ static int nfa_regexec_nl(regmatch_T *rmp, char_u *line, colnr_T col, bool line_ rex.reg_ic = rmp->rm_ic; rex.reg_icombine = false; rex.reg_maxcol = 0; - return nfa_regexec_both(line, col, NULL, NULL); + return (int)nfa_regexec_both(line, col, NULL, NULL); } /// Matches a regexp against multiple lines. diff --git a/src/nvim/runtime.c b/src/nvim/runtime.c index 045cee2439..5d28d624fe 100644 --- a/src/nvim/runtime.c +++ b/src/nvim/runtime.c @@ -74,14 +74,14 @@ int do_in_path(char_u *path, char *name, int flags, DoInRuntimepathCB callback, { char_u *tail; int num_files; - char_u **files; + char **files; int i; bool did_one = false; // Make a copy of 'runtimepath'. Invoking the callback may change the // value. char_u *rtp_copy = vim_strsave(path); - char_u *buf = xmallocz(MAXPATHL); + char *buf = xmallocz(MAXPATHL); { if (p_verbose > 10 && name != NULL) { verbose_enter(); @@ -93,7 +93,7 @@ int do_in_path(char_u *path, char *name, int flags, DoInRuntimepathCB callback, char_u *rtp = rtp_copy; while (*rtp != NUL && ((flags & DIP_ALL) || !did_one)) { // Copy the path from 'runtimepath' to buf[]. - copy_option_part((char **)&rtp, (char *)buf, MAXPATHL, ","); + copy_option_part((char **)&rtp, buf, MAXPATHL, ","); size_t buflen = STRLEN(buf); // Skip after or non-after directories. @@ -107,18 +107,19 @@ int do_in_path(char_u *path, char *name, int flags, DoInRuntimepathCB callback, } if (name == NULL) { - (*callback)((char *)buf, cookie); + (*callback)(buf, cookie); did_one = true; } else if (buflen + STRLEN(name) + 2 < MAXPATHL) { - add_pathsep((char *)buf); - tail = buf + STRLEN(buf); + add_pathsep(buf); + tail = (char_u *)buf + STRLEN(buf); // Loop over all patterns in "name" char_u *np = (char_u *)name; while (*np != NUL && ((flags & DIP_ALL) || !did_one)) { // Append the pattern from "name" to buf[]. - assert(MAXPATHL >= (tail - buf)); - copy_option_part((char **)&np, (char *)tail, (size_t)(MAXPATHL - (tail - buf)), "\t "); + assert(MAXPATHL >= (tail - (char_u *)buf)); + copy_option_part((char **)&np, (char *)tail, (size_t)(MAXPATHL - (tail - (char_u *)buf)), + "\t "); if (p_verbose > 10) { verbose_enter(); @@ -132,7 +133,7 @@ int do_in_path(char_u *path, char *name, int flags, DoInRuntimepathCB callback, // Expand wildcards, invoke the callback for each match. if (gen_expand_wildcards(1, &buf, &num_files, &files, ew_flags) == OK) { for (i = 0; i < num_files; i++) { - (*callback)((char *)files[i], cookie); + (*callback)(files[i], cookie); did_one = true; if (!(flags & DIP_ALL)) { break; @@ -211,7 +212,7 @@ int do_in_cached_path(char_u *name, int flags, DoInRuntimepathCB callback, void { char_u *tail; int num_files; - char_u **files; + char **files; int i; bool did_one = false; @@ -263,10 +264,10 @@ int do_in_cached_path(char_u *name, int flags, DoInRuntimepathCB callback, void | (flags & DIP_DIRFILE) ? (EW_DIR|EW_FILE) : 0; // Expand wildcards, invoke the callback for each match. - char_u *(pat[]) = { buf }; + char *(pat[]) = { (char *)buf }; if (gen_expand_wildcards(1, pat, &num_files, &files, ew_flags) == OK) { for (i = 0; i < num_files; i++) { - (*callback)((char *)files[i], cookie); + (*callback)(files[i], cookie); did_one = true; if (!(flags & DIP_ALL)) { break; @@ -458,11 +459,11 @@ static void expand_rtp_entry(RuntimeSearchPath *search_path, Map(String, handle_ } int num_files; - char_u **files; - char_u *(pat[]) = { (char_u *)entry }; + char **files; + char *(pat[]) = { entry }; if (gen_expand_wildcards(1, pat, &num_files, &files, EW_DIR) == OK) { for (int i = 0; i < num_files; i++) { - push_path(search_path, rtp_used, (char *)files[i], after); + push_path(search_path, rtp_used, files[i], after); } FreeWild(num_files, files); } @@ -488,7 +489,7 @@ static void expand_pack_entry(RuntimeSearchPath *search_path, Map(String, handle } } -static bool path_is_after(char_u *buf, size_t buflen) +static bool path_is_after(char *buf, size_t buflen) { // NOTE: we only consider dirs exactly matching "after" to be an AFTER dir. // vim8 considers all dirs like "foo/bar_after", "Xafter" etc, as an @@ -525,7 +526,7 @@ RuntimeSearchPath runtime_search_path_build(void) copy_option_part(&rtp_entry, (char *)buf, MAXPATHL, ","); size_t buflen = STRLEN(buf); - if (path_is_after(buf, buflen)) { + if (path_is_after((char *)buf, buflen)) { rtp_entry = cur_entry; break; } @@ -557,7 +558,7 @@ RuntimeSearchPath runtime_search_path_build(void) // "after" dirs in rtp for (; *rtp_entry != NUL;) { copy_option_part(&rtp_entry, (char *)buf, MAXPATHL, ","); - expand_rtp_entry(&search_path, &rtp_used, (char *)buf, path_is_after(buf, STRLEN(buf))); + expand_rtp_entry(&search_path, &rtp_used, (char *)buf, path_is_after((char *)buf, STRLEN(buf))); } // strings are not owned @@ -640,14 +641,14 @@ int source_in_path(char_u *path, char_u *name, int flags) // Expand wildcards in "pat" and invoke do_source()/nlua_exec_file() // for each match. -static void source_all_matches(char_u *pat) +static void source_all_matches(char *pat) { int num_files; - char_u **files; + char **files; if (gen_expand_wildcards(1, &pat, &num_files, &files, EW_FILE) == OK) { for (int i = 0; i < num_files; i++) { - (void)do_source((char *)files[i], false, DOSO_NONE); + (void)do_source(files[i], false, DOSO_NONE); } FreeWild(num_files, files); } @@ -811,9 +812,9 @@ static int load_pack_plugin(bool opt, char_u *fname) char_u *pat = xmallocz(len); vim_snprintf((char *)pat, len, "%s/plugin/**/*.vim", ffname); // NOLINT - source_all_matches(pat); + source_all_matches((char *)pat); vim_snprintf((char *)pat, len, "%s/plugin/**/*.lua", ffname); // NOLINT - source_all_matches(pat); + source_all_matches((char *)pat); char_u *cmd = vim_strsave((char_u *)"g:did_load_filetypes"); @@ -822,9 +823,9 @@ static int load_pack_plugin(bool opt, char_u *fname) if (opt && eval_to_number((char *)cmd) > 0) { do_cmdline_cmd("augroup filetypedetect"); vim_snprintf((char *)pat, len, ftpat, ffname); - source_all_matches(pat); + source_all_matches((char *)pat); vim_snprintf((char *)pat, len, "%s/ftdetect/*.lua", ffname); // NOLINT - source_all_matches(pat); + source_all_matches((char *)pat); do_cmdline_cmd("augroup END"); } xfree(cmd); @@ -886,8 +887,8 @@ void add_pack_start_dirs(void) static bool pack_has_entries(char_u *buf) { int num_files; - char_u **files; - char_u *(pat[]) = { buf }; + char **files; + char *(pat[]) = { (char *)buf }; if (gen_expand_wildcards(1, pat, &num_files, &files, EW_DIR) == OK) { FreeWild(num_files, files); } diff --git a/src/nvim/screen.c b/src/nvim/screen.c index e99f9b9153..0b7b58dc40 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -92,6 +92,7 @@ #include "nvim/highlight.h" #include "nvim/highlight_group.h" #include "nvim/indent.h" +#include "nvim/insexpand.h" #include "nvim/lib/kvec.h" #include "nvim/log.h" #include "nvim/lua/executor.h" @@ -406,7 +407,7 @@ int update_screen(int type) curwin->w_redr_status = true; } } - msg_grid_set_pos(Rows - p_ch, false); + msg_grid_set_pos(Rows - (int)p_ch, false); msg_grid_invalid = false; } else if (msg_scrolled > Rows - 5) { // clearing is faster type = CLEAR; @@ -470,7 +471,7 @@ int update_screen(int type) // After disabling msgsep the grid might not have been deallocated yet, // hence we also need to check msg_grid.chars if (type == NOT_VALID && (msg_use_grid() || msg_grid.chars)) { - grid_fill(&default_grid, Rows - p_ch, Rows, 0, Columns, ' ', ' ', 0); + grid_fill(&default_grid, Rows - (int)p_ch, Rows, 0, Columns, ' ', ' ', 0); } ui_comp_set_screen_valid(true); @@ -904,7 +905,7 @@ win_update_start: for (i = 0; i < wp->w_lines_valid; ++i) { j += wp->w_lines[i].wl_size; if (j >= wp->w_upd_rows) { - top_end = j; + top_end = (int)j; break; } } @@ -977,7 +978,7 @@ win_update_start: // Move the entries that were scrolled, disable // the entries for the lines to be redrawn. - if ((wp->w_lines_valid += j) > wp->w_grid.rows) { + if ((wp->w_lines_valid += (linenr_T)j) > wp->w_grid.rows) { wp->w_lines_valid = wp->w_grid.rows; } for (idx = wp->w_lines_valid; idx - j >= 0; idx--) { @@ -1063,8 +1064,8 @@ win_update_start: // Correct the first entry for filler lines at the top // when it won't get updated below. if (win_may_fill(wp) && bot_start > 0) { - wp->w_lines[0].wl_size = (plines_win_nofill(wp, wp->w_topline, true) - + wp->w_topfill); + wp->w_lines[0].wl_size = (uint16_t)(plines_win_nofill(wp, wp->w_topline, true) + + wp->w_topfill); } } } @@ -1183,7 +1184,7 @@ win_update_start: pos.lnum += cursor_above ? 1 : -1) { colnr_T t; - pos.col = STRLEN(ml_get_buf(wp->w_buffer, pos.lnum, false)); + pos.col = (colnr_T)STRLEN(ml_get_buf(wp->w_buffer, pos.lnum, false)); getvvcol(wp, &pos, NULL, NULL, &t); if (toc < t) { toc = t; @@ -1284,7 +1285,7 @@ win_update_start: } if (VIsual_active && buf == curwin->w_buffer) { - wp->w_old_visual_mode = VIsual_mode; + wp->w_old_visual_mode = (char)VIsual_mode; wp->w_old_cursor_lnum = curwin->w_cursor.lnum; wp->w_old_visual_lnum = VIsual.lnum; wp->w_old_visual_col = VIsual.col; @@ -1480,14 +1481,14 @@ win_update_start: for (;;) { // stop at last valid entry in w_lines[] if (i >= wp->w_lines_valid) { - wp->w_lines_valid = j; + wp->w_lines_valid = (int)j; break; } wp->w_lines[j] = wp->w_lines[i]; // stop at a line that won't fit if (x + (int)wp->w_lines[j].wl_size > wp->w_grid.rows) { - wp->w_lines_valid = j + 1; + wp->w_lines_valid = (int)j + 1; break; } x += wp->w_lines[j++].wl_size; @@ -1499,7 +1500,7 @@ win_update_start: } else { // j > i // move entries in w_lines[] downwards j -= i; - wp->w_lines_valid += j; + wp->w_lines_valid += (linenr_T)j; if (wp->w_lines_valid > wp->w_grid.rows) { wp->w_lines_valid = wp->w_grid.rows; } @@ -1569,13 +1570,13 @@ win_update_start: if (row > wp->w_grid.rows) { // past end of grid // we may need the size of that too long line later on if (dollar_vcol == -1) { - wp->w_lines[idx].wl_size = plines_win(wp, lnum, true); + wp->w_lines[idx].wl_size = (uint16_t)plines_win(wp, lnum, true); } idx++; break; } if (dollar_vcol == -1) { - wp->w_lines[idx].wl_size = row - srow; + wp->w_lines[idx].wl_size = (uint16_t)(row - srow); } idx++; lnum += foldinfo.fi_lines + 1; @@ -1702,7 +1703,7 @@ win_update_start: // Send win_extmarks if needed for (size_t n = 0; n < kv_size(win_extmark_arr); n++) { ui_call_win_extmark(wp->w_grid_alloc.handle, wp->handle, - kv_A(win_extmark_arr, n).ns_id, kv_A(win_extmark_arr, n).mark_id, + kv_A(win_extmark_arr, n).ns_id, (Integer)kv_A(win_extmark_arr, n).mark_id, kv_A(win_extmark_arr, n).win_row, kv_A(win_extmark_arr, n).win_col); } @@ -1807,7 +1808,7 @@ static void win_draw_end(win_T *wp, int c1, int c2, bool draw_margin, int row, i } } - int attr = hl_combine_attr(wp->w_hl_attr_normal, win_hl_attr(wp, hl)); + int attr = hl_combine_attr(wp->w_hl_attr_normal, win_hl_attr(wp, (int)hl)); if (wp->w_p_rl) { grid_fill(&wp->w_grid, row, endrow, wp->w_wincol, W_ENDCOL(wp) - 1 - n, @@ -1837,7 +1838,7 @@ static bool advance_color_col(int vcol, int **color_cols) static int compute_foldcolumn(win_T *wp, int col) { int fdc = win_fdccol_count(wp); - int wmw = wp == curwin && p_wmw == 0 ? 1 : p_wmw; + int wmw = wp == curwin && p_wmw == 0 ? 1 : (int)p_wmw; int wwidth = wp->w_grid.cols; if (fdc > wwidth - (col + wmw)) { @@ -1866,7 +1867,7 @@ static int line_putchar(buf_T *buf, LineState *s, schar_T *dest, int maxcells, b } goto done; } else if (*p < 0x80 && u8cc[0] == 0) { - schar_from_ascii(dest[0], *p); + schar_from_ascii(dest[0], (char)(*p)); s->prev_c = u8c; } else { if (p_arshape && !p_tbidi && ARABIC_CHAR(u8c)) { @@ -1923,7 +1924,7 @@ static size_t fill_foldcolumn(char_u *p, win_T *wp, foldinfo_T foldinfo, linenr_ int len = 0; bool closed = foldinfo.fi_lines > 0; // Init to all spaces. - memset(p, ' ', MAX_MCO * fdc + 1); + memset(p, ' ', MAX_MCO * (size_t)fdc + 1); level = foldinfo.fi_level; @@ -1947,7 +1948,7 @@ static size_t fill_foldcolumn(char_u *p, win_T *wp, foldinfo_T foldinfo, linenr_ } len = utf_char2bytes(symbol, (char *)&p[char_counter]); - char_counter += len; + char_counter += (size_t)len; if (first_level + i >= level) { i++; break; @@ -1957,14 +1958,14 @@ static size_t fill_foldcolumn(char_u *p, win_T *wp, foldinfo_T foldinfo, linenr_ if (closed) { if (symbol != 0) { // rollback previous write - char_counter -= len; - memset(&p[char_counter], ' ', len); + char_counter -= (size_t)len; + memset(&p[char_counter], ' ', (size_t)len); } len = utf_char2bytes(wp->w_p_fcs_chars.foldclosed, (char *)&p[char_counter]); - char_counter += len; + char_counter += (size_t)len; } - return MAX(char_counter + (fdc - i), (size_t)fdc); + return MAX(char_counter + (size_t)(fdc - i), (size_t)fdc); } static inline void provider_err_virt_text(linenr_T lnum, char *err) @@ -1974,7 +1975,7 @@ static inline void provider_err_virt_text(linenr_T lnum, char *err) kv_push(err_decor.virt_text, ((VirtTextChunk){ .text = provider_err, .hl_id = hl_err })); - err_decor.virt_text_width = mb_string2cells(err); + err_decor.virt_text_width = (int)mb_string2cells(err); decor_add_ephemeral(lnum - 1, 0, lnum - 1, 0, &err_decor, 0, 0); } @@ -2118,7 +2119,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc bool has_decor = false; // this buffer has decoration int win_col_offset = 0; // offset for window columns - char_u buf_fold[FOLD_TEXT_LEN + 1]; // Hold value returned by get_foldtext + char_u buf_fold[FOLD_TEXT_LEN]; // Hold value returned by get_foldtext bool area_active = false; @@ -2211,7 +2212,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc // Check for columns to display for 'colorcolumn'. color_cols = wp->w_buffer->terminal ? NULL : wp->w_p_cc_cols; if (color_cols != NULL) { - draw_color_col = advance_color_col(VCOL_HLC, &color_cols); + draw_color_col = advance_color_col((int)VCOL_HLC, &color_cols); } if (wp->w_p_spell @@ -2447,10 +2448,10 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc nextlinecol = 0; memmove(nextline, line, (size_t)v); STRMOVE(nextline + v, nextline + SPWORDLEN); - nextline_idx = v + 1; + nextline_idx = (int)v + 1; } else { // Long line, use only the last SPWORDLEN bytes. - nextlinecol = v - SPWORDLEN; + nextlinecol = (int)v - SPWORDLEN; memmove(nextline, line + nextlinecol, SPWORDLEN); // -V512 nextline_idx = SPWORDLEN + 1; } @@ -2529,7 +2530,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc // If the character fits on the screen, don't need to skip it. // Except for a TAB. if (utf_ptr2cells((char *)ptr) >= c || *ptr == TAB) { - n_skip = v - vcol; + n_skip = (int)(v - vcol); } } @@ -2540,7 +2541,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc if (tocol <= vcol) { fromcol = 0; } else if (fromcol >= 0 && fromcol < vcol) { - fromcol = vcol; + fromcol = (int)vcol; } // When w_skipcol is non-zero, first line needs 'showbreak' @@ -2616,7 +2617,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc ptr = line + v; // "line" may have been updated } - unsigned off = 0; // Offset relative start of line + int off = 0; // Offset relative start of line int col = 0; // Visual column on screen. if (wp->w_p_rl) { // Rightleft window: process the text in the normal direction, but put @@ -2668,7 +2669,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc // Draw the 'foldcolumn'. Allocate a buffer, "extra" may // already be in use. xfree(p_extra_free); - p_extra_free = xmalloc(MAX_MCO * fdc + 1); + p_extra_free = xmalloc(MAX_MCO * (size_t)fdc + 1); n_extra = (int)fill_foldcolumn(p_extra_free, wp, foldinfo, lnum); p_extra_free[n_extra] = NUL; p_extra = p_extra_free; @@ -2731,7 +2732,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc char_u *p2 = (char_u *)skipwhite((char *)extra); p2 = skiptowhite(p2) - 1; for (char_u *p1 = (char_u *)skipwhite((char *)extra); p1 < p2; p1++, p2--) { - const int t = *p1; + const char_u t = *p1; *p1 = *p2; *p2 = t; } @@ -2770,7 +2771,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc char_attr = 0; if (diff_hlf != (hlf_T)0) { - char_attr = win_hl_attr(wp, diff_hlf); + char_attr = win_hl_attr(wp, (int)diff_hlf); } p_extra = NULL; c_extra = ' '; @@ -2853,7 +2854,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc if (has_decor && row == startrow + filler_lines) { // hide virt_text on text hidden by 'nowrap' - decor_redraw_col(wp->w_buffer, vcol, off, true, &decor_state); + decor_redraw_col(wp->w_buffer, (int)vcol, off, true, &decor_state); } if (saved_n_extra) { @@ -2908,7 +2909,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc if (draw_state == WL_LINE && has_fold - && vcol == 0 + && col == win_col_offset && n_extra == 0 && row == startrow) { char_attr = win_hl_attr(wp, HLF_FL); @@ -2916,7 +2917,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc linenr_T lnume = lnum + foldinfo.fi_lines - 1; memset(buf_fold, ' ', FOLD_TEXT_LEN); p_extra = get_foldtext(wp, lnum, lnume, foldinfo, buf_fold); - n_extra = STRLEN(p_extra); + n_extra = (int)STRLEN(p_extra); if (p_extra != buf_fold) { xfree(p_extra_free); @@ -2992,7 +2993,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc && n_extra == 0) { diff_hlf = HLF_CHD; // changed line } - line_attr = win_hl_attr(wp, diff_hlf); + line_attr = win_hl_attr(wp, (int)diff_hlf); // Overlay CursorLine onto diff-mode highlight. if (cul_attr) { line_attr = 0 != line_attr_lowprio // Low-priority CursorLine @@ -3299,7 +3300,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc size_t tmplen = spell_check(wp, p, &spell_hlf, &cap_col, nochange); assert(tmplen <= INT_MAX); len = (int)tmplen; - word_end = v + len; + word_end = (int)v + len; /* In Insert mode only highlight a word that * doesn't touch the cursor. */ @@ -3390,7 +3391,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc } if (c == TAB && n_extra + col > grid->cols) { - n_extra = tabstop_padding(vcol, wp->w_buffer->b_p_ts, + n_extra = tabstop_padding((colnr_T)vcol, wp->w_buffer->b_p_ts, wp->w_buffer->b_p_vts_array) - 1; } c_extra = mb_off > 0 ? MB_FILLER_CHAR : ' '; @@ -3494,7 +3495,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc vcol_adjusted = vcol - mb_charlen(sbr); } // tab amount depends on current column - tab_len = tabstop_padding(vcol_adjusted, + tab_len = tabstop_padding((colnr_T)vcol_adjusted, wp->w_buffer->b_p_ts, wp->w_buffer->b_p_vts_array) - 1; @@ -3526,8 +3527,8 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc len += n_extra - tab_len; } c = wp->w_p_lcs_chars.tab1; - p = xmalloc(len + 1); - memset(p, ' ', len); + p = xmalloc((size_t)len + 1); + memset(p, ' ', (size_t)len); p[len] = NUL; xfree(p_extra_free); p_extra_free = p; @@ -3657,9 +3658,9 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc char_u *p; c = *p_extra; - p = xmalloc(n_extra + 1); - memset(p, ' ', n_extra); - STRNCPY(p, p_extra + 1, STRLEN(p_extra) - 1); + p = xmalloc((size_t)n_extra + 1); + memset(p, ' ', (size_t)n_extra); + STRNCPY(p, p_extra + 1, STRLEN(p_extra) - 1); // NOLINT(runtime/printf) p[n_extra] = NUL; xfree(p_extra_free); p_extra_free = p_extra = p; @@ -3835,8 +3836,8 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc } } if (n != 0) { - /* At the window boundary, highlight the last character - * instead (better than nothing). */ + // At the window boundary, highlight the last character + // instead (better than nothing). off += n; col += n; } else { @@ -3881,7 +3882,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc // boguscols = 0; // Disabled because value never read after this if (draw_color_col) { - draw_color_col = advance_color_col(VCOL_HLC, &color_cols); + draw_color_col = advance_color_col((int)VCOL_HLC, &color_cols); } bool has_virttext = false; @@ -3926,7 +3927,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc diff_hlf = HLF_CHD; } if (diff_hlf != 0) { - diff_attr = win_hl_attr(wp, diff_hlf); + diff_attr = win_hl_attr(wp, (int)diff_hlf); } int base_attr = hl_combine_attr(line_attr_lowprio, diff_attr); @@ -3940,7 +3941,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc schar_from_ascii(linebuf_char[off], ' '); col += col_stride; if (draw_color_col) { - draw_color_col = advance_color_col(VCOL_HLC, &color_cols); + draw_color_col = advance_color_col((int)VCOL_HLC, &color_cols); } int col_attr = base_attr; @@ -4026,7 +4027,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc // advance to the next 'colorcolumn' if (draw_color_col) { - draw_color_col = advance_color_col(VCOL_HLC, &color_cols); + draw_color_col = advance_color_col((int)VCOL_HLC, &color_cols); } // Highlight the cursor column if 'cursorcolumn' is set. But don't @@ -4073,7 +4074,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc if (mb_utf8) { schar_from_cc(linebuf_char[off], mb_c, u8cc); } else { - schar_from_ascii(linebuf_char[off], c); + schar_from_ascii(linebuf_char[off], (char)c); } if (multi_attr) { linebuf_attr[off] = multi_attr; @@ -4217,7 +4218,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc if (filler_todo > 0) { int index = filler_todo - (filler_lines - n_virt_lines); if (index > 0) { - int i = kv_size(virt_lines) - index; + int i = (int)kv_size(virt_lines) - index; assert(i >= 0); int offset = kv_A(virt_lines, i).left_col ? 0 : win_col_offset; draw_virt_text_item(buf, offset, kv_A(virt_lines, i).line, @@ -4331,7 +4332,7 @@ void draw_virt_text(win_T *wp, buf_T *buf, int col_off, int *end_col, int max_co if (item->decor.ui_watched) { // send mark position to UI col = item->win_col; - WinExtmark m = { item->ns_id, item->mark_id, win_row, col }; + WinExtmark m = { (NS)item->ns_id, item->mark_id, win_row, col }; kv_push(win_extmark_arr, m); } if (kv_size(item->decor.virt_text)) { @@ -4505,12 +4506,12 @@ static void get_sign_display_info(bool nrcol, win_T *wp, linenr_T lnum, sign_att // full cell width? assert((size_t)win_signcol_width(wp) >= mb_string2cells((char *)(*pp_extra))); // symbol(s) bytes + (filling spaces) (one byte each) - *n_extrap = symbol_blen + - (win_signcol_width(wp) - mb_string2cells((char *)(*pp_extra))); + *n_extrap = symbol_blen + win_signcol_width(wp) - + (int)mb_string2cells((char *)(*pp_extra)); assert(extra_size > (size_t)symbol_blen); memset(extra, ' ', extra_size); - memcpy(extra, *pp_extra, symbol_blen); + memcpy(extra, *pp_extra, (size_t)symbol_blen); *pp_extra = extra; (*pp_extra)[*n_extrap] = NUL; @@ -4533,7 +4534,7 @@ static void get_sign_display_info(bool nrcol, win_T *wp, linenr_T lnum, sign_att void rl_mirror(char_u *str) { char_u *p1, *p2; - int t; + char_u t; for (p1 = str, p2 = str + STRLEN(str) - 1; p1 < p2; ++p1, --p2) { t = *p1; @@ -4763,8 +4764,7 @@ static int skip_status_match_char(expand_T *xp, char_u *s) /// If inversion is possible we use it. Else '=' characters are used. /// /// @param matches list of matches -void win_redr_status_matches(expand_T *xp, int num_matches, char_u **matches, int match, - int showtail) +void win_redr_status_matches(expand_T *xp, int num_matches, char **matches, int match, int showtail) { #define L_MATCH(m) (showtail ? sm_gettail(matches[m], false) : matches[m]) int row; @@ -4788,14 +4788,14 @@ void win_redr_status_matches(expand_T *xp, int num_matches, char_u **matches, in return; } - buf = xmalloc(Columns * MB_MAXBYTES + 1); + buf = xmalloc((size_t)Columns * MB_MAXBYTES + 1); if (match == -1) { // don't show match but original text match = 0; highlight = false; } // count 1 for the ending ">" - clen = status_match_len(xp, L_MATCH(match)) + 3; + clen = status_match_len(xp, (char_u *)L_MATCH(match)) + 3; if (match == 0) { first_match = 0; } else if (match < first_match) { @@ -4804,8 +4804,8 @@ void win_redr_status_matches(expand_T *xp, int num_matches, char_u **matches, in add_left = true; } else { // check if match fits on the screen - for (i = first_match; i < match; ++i) { - clen += status_match_len(xp, L_MATCH(i)) + 2; + for (i = first_match; i < match; i++) { + clen += status_match_len(xp, (char_u *)L_MATCH(i)) + 2; } if (first_match > 0) { clen += 2; @@ -4815,8 +4815,8 @@ void win_redr_status_matches(expand_T *xp, int num_matches, char_u **matches, in first_match = match; // if showing the last match, we can add some on the left clen = 2; - for (i = match; i < num_matches; ++i) { - clen += status_match_len(xp, L_MATCH(i)) + 2; + for (i = match; i < num_matches; i++) { + clen += status_match_len(xp, (char_u *)L_MATCH(i)) + 2; if ((long)clen >= Columns) { break; } @@ -4828,7 +4828,7 @@ void win_redr_status_matches(expand_T *xp, int num_matches, char_u **matches, in } if (add_left) { while (first_match > 0) { - clen += status_match_len(xp, L_MATCH(first_match - 1)) + 2; + clen += status_match_len(xp, (char_u *)L_MATCH(first_match - 1)) + 2; if ((long)clen >= Columns) { break; } @@ -4848,13 +4848,13 @@ void win_redr_status_matches(expand_T *xp, int num_matches, char_u **matches, in clen = len; i = first_match; - while (clen + status_match_len(xp, L_MATCH(i)) + 2 < Columns) { + while (clen + status_match_len(xp, (char_u *)L_MATCH(i)) + 2 < Columns) { if (i == match) { selstart = buf + len; selstart_col = clen; } - s = L_MATCH(i); + s = (char_u *)L_MATCH(i); // Check for menu separators - replace with '|' emenu = (xp->xp_context == EXPAND_MENUS || xp->xp_context == EXPAND_MENUNAMES); @@ -4915,8 +4915,8 @@ void win_redr_status_matches(expand_T *xp, int num_matches, char_u **matches, in // Set 'winminheight' to zero to avoid that the window is // resized. if (lastwin->w_status_height == 0 && global_stl_height() == 0) { - save_p_ls = p_ls; - save_p_wmh = p_wmh; + save_p_ls = (int)p_ls; + save_p_wmh = (int)p_wmh; p_ls = 2; p_wmh = 0; last_status(false); @@ -4995,19 +4995,19 @@ static void win_redr_status(win_T *wp) *(p + len++) = ' '; } if (bt_help(wp->w_buffer)) { - snprintf((char *)p + len, MAXPATHL - len, "%s", _("[Help]")); + snprintf((char *)p + len, MAXPATHL - (size_t)len, "%s", _("[Help]")); len += (int)STRLEN(p + len); } if (wp->w_p_pvw) { - snprintf((char *)p + len, MAXPATHL - len, "%s", _("[Preview]")); + snprintf((char *)p + len, MAXPATHL - (size_t)len, "%s", _("[Preview]")); len += (int)STRLEN(p + len); } if (bufIsChanged(wp->w_buffer)) { - snprintf((char *)p + len, MAXPATHL - len, "%s", "[+]"); + snprintf((char *)p + len, MAXPATHL - (size_t)len, "%s", "[+]"); len += (int)STRLEN(p + len); } if (wp->w_buffer->b_p_ro) { - snprintf((char *)p + len, MAXPATHL - len, "%s", _("[RO]")); + snprintf((char *)p + len, MAXPATHL - (size_t)len, "%s", _("[RO]")); // len += (int)STRLEN(p + len); // dead assignment } @@ -5038,7 +5038,7 @@ static void win_redr_status(win_T *wp) } } - row = is_stl_global ? (Rows - p_ch - 1) : W_ENDROW(wp); + row = is_stl_global ? (Rows - (int)p_ch - 1) : W_ENDROW(wp); col = is_stl_global ? 0 : wp->w_wincol; grid_puts(&default_grid, p, row, col, attr); grid_fill(&default_grid, row, row + 1, len + col, @@ -5047,7 +5047,7 @@ static void win_redr_status(win_T *wp) if (get_keymap_str(wp, "<%s>", (char *)NameBuff, MAXPATHL) && this_ru_col - len > (int)(STRLEN(NameBuff) + 1)) { grid_puts(&default_grid, NameBuff, row, - (int)(this_ru_col - STRLEN(NameBuff) - 1), attr); + (int)((size_t)this_ru_col - STRLEN(NameBuff) - 1), attr); } win_redr_ruler(wp, true); @@ -5256,7 +5256,7 @@ bool get_keymap_str(win_T *wp, char *fmt, char *buf, int len) p = "lang"; } } - if (vim_snprintf(buf, len, fmt, p) > len - 1) { + if (vim_snprintf(buf, (size_t)len, fmt, p) > len - 1) { buf[0] = NUL; } xfree(s); @@ -5323,7 +5323,7 @@ static void win_redr_custom(win_T *wp, bool draw_winbar, bool draw_ruler) maxwidth = wp->w_width_inner; use_sandbox = was_set_insecurely(wp, "winbar", 0); - stl_clear_click_defs(wp->w_winbar_click_defs, wp->w_winbar_click_defs_size); + stl_clear_click_defs(wp->w_winbar_click_defs, (long)wp->w_winbar_click_defs_size); // Allocate / resize the click definitions array for winbar if needed. if (wp->w_winbar_height && wp->w_winbar_click_defs_size < (size_t)maxwidth) { xfree(wp->w_winbar_click_defs); @@ -5331,15 +5331,15 @@ static void win_redr_custom(win_T *wp, bool draw_winbar, bool draw_ruler) wp->w_winbar_click_defs = xcalloc(wp->w_winbar_click_defs_size, sizeof(StlClickRecord)); } } else { - row = is_stl_global ? (Rows - p_ch - 1) : W_ENDROW(wp); + row = is_stl_global ? (Rows - (int)p_ch - 1) : W_ENDROW(wp); fillchar = fillchar_status(&attr, wp); maxwidth = is_stl_global ? Columns : wp->w_width; - stl_clear_click_defs(wp->w_status_click_defs, wp->w_status_click_defs_size); + stl_clear_click_defs(wp->w_status_click_defs, (long)wp->w_status_click_defs_size); // Allocate / resize the click definitions array for statusline if needed. if (wp->w_status_click_defs_size < (size_t)maxwidth) { xfree(wp->w_status_click_defs); - wp->w_status_click_defs_size = maxwidth; + wp->w_status_click_defs_size = (size_t)maxwidth; wp->w_status_click_defs = xcalloc(wp->w_status_click_defs_size, sizeof(StlClickRecord)); } @@ -5406,7 +5406,7 @@ static void win_redr_custom(win_T *wp, bool draw_winbar, bool draw_ruler) // Make all characters printable. p = transstr(buf, true); - len = STRLCPY(buf, p, sizeof(buf)); + len = (int)STRLCPY(buf, p, sizeof(buf)); len = (size_t)len < sizeof(buf) ? len : (int)sizeof(buf) - 1; xfree(p); @@ -5799,13 +5799,11 @@ void screenclear(void) /// Copy part of a grid line for vertically split window. static void linecopy(ScreenGrid *grid, int to, int from, int col, int width) { - unsigned off_to = grid->line_offset[to] + col; - unsigned off_from = grid->line_offset[from] + col; + unsigned off_to = (unsigned)(grid->line_offset[to] + (size_t)col); + unsigned off_from = (unsigned)(grid->line_offset[from] + (size_t)col); - memmove(grid->chars + off_to, grid->chars + off_from, - width * sizeof(schar_T)); - memmove(grid->attrs + off_to, grid->attrs + off_from, - width * sizeof(sattr_T)); + memmove(grid->chars + off_to, grid->chars + off_from, (size_t)width * sizeof(schar_T)); + memmove(grid->attrs + off_to, grid->attrs + off_from, (size_t)width * sizeof(sattr_T)); } /// Set cursor to its position in the current window. @@ -5902,11 +5900,11 @@ void grid_ins_lines(ScreenGrid *grid, int row, int line_count, int end, int col, linecopy(grid, j + line_count, j, col, width); } j += line_count; - grid_clear_line(grid, grid->line_offset[j] + col, width, false); + grid_clear_line(grid, grid->line_offset[j] + (size_t)col, width, false); grid->line_wraps[j] = false; } else { j = end - 1 - i; - temp = grid->line_offset[j]; + temp = (unsigned)grid->line_offset[j]; while ((j -= line_count) >= row) { grid->line_offset[j + line_count] = grid->line_offset[j]; grid->line_wraps[j + line_count] = grid->line_wraps[j]; @@ -5951,12 +5949,12 @@ void grid_del_lines(ScreenGrid *grid, int row, int line_count, int end, int col, linecopy(grid, j - line_count, j, col, width); } j -= line_count; - grid_clear_line(grid, grid->line_offset[j] + col, width, false); + grid_clear_line(grid, grid->line_offset[j] + (size_t)col, width, false); grid->line_wraps[j] = false; } else { // whole width, moving the line pointers is faster j = row + i; - temp = grid->line_offset[j]; + temp = (unsigned)grid->line_offset[j]; while ((j += line_count) <= end - 1) { grid->line_offset[j - line_count] = grid->line_offset[j]; grid->line_wraps[j - line_count] = grid->line_wraps[j]; @@ -6058,7 +6056,7 @@ int showmode(void) if (edit_submode_extra != NULL) { msg_puts_attr(" ", attr); // Add a space in between. if ((int)edit_submode_highl < HLF_COUNT) { - sub_attr = win_hl_attr(curwin, edit_submode_highl); + sub_attr = win_hl_attr(curwin, (int)edit_submode_highl); } else { sub_attr = attr; } @@ -6079,7 +6077,11 @@ int showmode(void) msg_puts_attr(_(" INSERT"), attr); } else if (restart_edit == 'I' || restart_edit == 'i' || restart_edit == 'a' || restart_edit == 'A') { - msg_puts_attr(_(" (insert)"), attr); + if (curbuf->terminal) { + msg_puts_attr(_(" (terminal)"), attr); + } else { + msg_puts_attr(_(" (insert)"), attr); + } } else if (restart_edit == 'R') { msg_puts_attr(_(" (replace)"), attr); } else if (restart_edit == 'V') { @@ -6195,10 +6197,6 @@ void unshowmode(bool force) // Clear the mode message. void clearmode(void) { - if (p_ch <= 0 && !ui_has(kUIMessages)) { - return; - } - const int save_msg_row = msg_row; const int save_msg_col = msg_col; @@ -6216,10 +6214,6 @@ void clearmode(void) static void recording_mode(int attr) { - if (p_ch <= 0 && !ui_has(kUIMessages)) { - return; - } - msg_puts_attr(_("recording"), attr); if (!shortmess(SHM_RECORDING)) { char s[4]; @@ -6524,8 +6518,7 @@ int redrawing(void) */ int messaging(void) { - return !(p_lz && char_avail() && !KeyTyped) - && (p_ch > 0 || ui_has(kUIMessages)); + return !(p_lz && char_avail() && !KeyTyped) && ui_has_messages(); } /// Show current status info in ruler and various other places @@ -6562,7 +6555,7 @@ static void win_redr_ruler(win_T *wp, bool always) bool is_stl_global = global_stl_height() > 0; static bool did_show_ext_ruler = false; - // If 'ruler' off or redrawing disabled, don't do anything + // If 'ruler' off, don't do anything if (!p_ru) { return; } @@ -6626,7 +6619,7 @@ static void win_redr_ruler(win_T *wp, bool always) width = wp->w_width; part_of_status = true; } else if (is_stl_global) { - row = Rows - p_ch - 1; + row = Rows - (int)p_ch - 1; fillchar = fillchar_status(&attr, wp); off = 0; width = Columns; @@ -6639,7 +6632,7 @@ static void win_redr_ruler(win_T *wp, bool always) off = 0; } - if (!part_of_status && p_ch < 1 && !ui_has(kUIMessages)) { + if (!part_of_status && !ui_has_messages()) { return; } @@ -6727,7 +6720,7 @@ static void win_redr_ruler(win_T *wp, bool always) wp->w_ru_cursor = wp->w_cursor; wp->w_ru_virtcol = wp->w_virtcol; - wp->w_ru_empty = empty_line; + wp->w_ru_empty = (char)empty_line; wp->w_ru_topline = wp->w_topline; wp->w_ru_line_count = wp->w_buffer->b_ml.ml_line_count; wp->w_ru_topfill = wp->w_topfill; @@ -6765,7 +6758,7 @@ int number_width(win_T *wp) // 'numberwidth' gives the minimal width plus one if (n < wp->w_p_nuw - 1) { - n = wp->w_p_nuw - 1; + n = (int)wp->w_p_nuw - 1; } // If 'signcolumn' is set to 'number' and there is a sign to display, then diff --git a/src/nvim/search.c b/src/nvim/search.c index f3061b4dc4..403e2f3aa4 100644 --- a/src/nvim/search.c +++ b/src/nvim/search.c @@ -18,6 +18,7 @@ #include "nvim/cursor.h" #include "nvim/edit.h" #include "nvim/eval.h" +#include "nvim/eval/funcs.h" #include "nvim/ex_cmds.h" #include "nvim/ex_cmds2.h" #include "nvim/ex_getln.h" @@ -27,6 +28,7 @@ #include "nvim/getchar.h" #include "nvim/indent.h" #include "nvim/indent_c.h" +#include "nvim/insexpand.h" #include "nvim/main.h" #include "nvim/mark.h" #include "nvim/mbyte.h" @@ -424,10 +426,10 @@ int last_csearch_until(void) void set_last_csearch(int c, char_u *s, int len) { - *lastc = c; + *lastc = (char_u)c; lastc_bytelen = len; if (len) { - memcpy(lastc_bytes, s, len); + memcpy(lastc_bytes, s, (size_t)len); } else { memset(lastc_bytes, 0, sizeof(lastc_bytes)); } @@ -973,7 +975,7 @@ int searchit(win_T *win, buf_T *buf, pos_T *pos, pos_T *end_pos, Direction dir, void set_search_direction(int cdir) { - spats[0].off.dir = cdir; + spats[0].off.dir = (char)cdir; } static void set_vv_searchforward(void) @@ -1062,7 +1064,7 @@ int do_search(oparg_T *oap, int dirc, int search_delim, char_u *pat, long count, if (dirc == 0) { dirc = (char_u)spats[0].off.dir; } else { - spats[0].off.dir = dirc; + spats[0].off.dir = (char)dirc; set_vv_searchforward(); } if (options & SEARCH_REV) { @@ -1183,7 +1185,7 @@ int do_search(oparg_T *oap, int dirc, int search_delim, char_u *pat, long count, if (!cmd_silent && (spats[0].off.line || spats[0].off.end || spats[0].off.off)) { p = off_buf; // -V507 - *p++ = dirc; + *p++ = (char_u)dirc; if (spats[0].off.end) { *p++ = 'e'; } else if (!spats[0].off.line) { @@ -1194,7 +1196,7 @@ int do_search(oparg_T *oap, int dirc, int search_delim, char_u *pat, long count, } *p = NUL; if (spats[0].off.off != 0 || spats[0].off.line) { - snprintf((char *)p, sizeof(off_buf) - 1 - (p - off_buf), + snprintf((char *)p, sizeof(off_buf) - 1 - (size_t)(p - off_buf), "%" PRId64, spats[0].off.off); } off_len = STRLEN(off_buf); @@ -1215,10 +1217,10 @@ int do_search(oparg_T *oap, int dirc, int search_delim, char_u *pat, long count, len = 0; // adjusted below } else if (msg_scrolled != 0 && !cmd_silent) { // Use all the columns. - len = (Rows - msg_row) * Columns - 1; + len = (size_t)((Rows - msg_row) * Columns - 1); } else { // Use up to 'showcmd' column. - len = (Rows - msg_row - 1) * Columns + sc_col - 1; + len = (size_t)((Rows - msg_row - 1) * Columns + sc_col - 1); } if (len < STRLEN(p) + off_len + SEARCH_STAT_BUF_LEN + 3) { len = STRLEN(p) + off_len + SEARCH_STAT_BUF_LEN + 3; @@ -1236,7 +1238,7 @@ int do_search(oparg_T *oap, int dirc, int search_delim, char_u *pat, long count, // do not fill the msgbuf buffer, if cmd_silent is set, leave it // empty for the search_stat feature. if (!cmd_silent) { - msgbuf[0] = dirc; + msgbuf[0] = (char_u)dirc; if (utf_iscomposing(utf_ptr2char((char *)p))) { // Use a space to draw the composing char on. msgbuf[1] = ' '; @@ -1266,13 +1268,13 @@ int do_search(oparg_T *oap, int dirc, int search_delim, char_u *pat, long count, while (*r == ' ') { r++; } - size_t pat_len = msgbuf + STRLEN(msgbuf) - r; + size_t pat_len = (size_t)(msgbuf + STRLEN(msgbuf) - r); memmove(msgbuf, r, pat_len); // overwrite old text if ((size_t)(r - msgbuf) >= pat_len) { memset(r, ' ', pat_len); } else { - memset(msgbuf + pat_len, ' ', r - msgbuf); + memset(msgbuf + pat_len, ' ', (size_t)(r - msgbuf)); } } msg_outtrans((char *)msgbuf); @@ -1326,7 +1328,7 @@ int do_search(oparg_T *oap, int dirc, int search_delim, char_u *pat, long count, RE_LAST, sia); if (dircp != NULL) { - *dircp = search_delim; // restore second '/' or '?' for normal_cmd() + *dircp = (char_u)search_delim; // restore second '/' or '?' for normal_cmd() } if (!shortmess(SHM_SEARCH) @@ -1361,7 +1363,7 @@ int do_search(oparg_T *oap, int dirc, int search_delim, char_u *pat, long count, } else if (c > curbuf->b_ml.ml_line_count) { pos.lnum = curbuf->b_ml.ml_line_count; } else { - pos.lnum = c; + pos.lnum = (linenr_T)c; } pos.col = 0; @@ -1535,7 +1537,7 @@ int searchc(cmdarg_T *cap, int t_cmd) if (c != NUL) { // normal search: remember args for repeat if (!KeyStuffed) { // don't remember when redoing - *lastc = c; + *lastc = (char_u)c; set_csearch_direction(dir); set_csearch_until(t_cmd); lastc_bytelen = utf_char2bytes(c, (char *)lastc_bytes); @@ -1657,14 +1659,12 @@ static bool check_prevcol(char_u *linep, int col, int ch, int *prevcol) static bool find_rawstring_end(char_u *linep, pos_T *startpos, pos_T *endpos) { char_u *p; - char_u *delim_copy; - size_t delim_len; linenr_T lnum; for (p = linep + startpos->col + 1; *p && *p != '('; p++) {} - delim_len = (p - linep) - startpos->col - 1; - delim_copy = vim_strnsave(linep + startpos->col + 1, delim_len); + size_t delim_len = (size_t)((p - linep) - startpos->col - 1); + char_u *delim_copy = vim_strnsave(linep + startpos->col + 1, delim_len); bool found = false; for (lnum = startpos->lnum; lnum <= endpos->lnum; lnum++) { char_u *line = ml_get(lnum); @@ -2434,9 +2434,9 @@ void showmatch(int c) * available. */ if (vim_strchr(p_cpo, CPO_SHOWMATCH) != NULL) { - os_delay(p_mat * 100L + 8, true); + os_delay((uint64_t)p_mat * 100L + 8, true); } else if (!char_avail()) { - os_delay(p_mat * 100L + 9, false); + os_delay((uint64_t)p_mat * 100L + 9, false); } curwin->w_cursor = save_cursor; // restore cursor position *so = save_so; @@ -3665,16 +3665,15 @@ again: curwin->w_cursor = old_pos; goto theend; } - const size_t spat_len = len + 39; + const size_t spat_len = (size_t)len + 39; char *const spat = xmalloc(spat_len); - const size_t epat_len = len + 9; + const size_t epat_len = (size_t)len + 9; char *const epat = xmalloc(epat_len); snprintf(spat, spat_len, "<%.*s\\>\\%%(\\_s\\_[^>]\\{-}\\_[^/]>\\|\\_s\\?>\\)\\c", len, p); snprintf(epat, epat_len, "</%.*s>\\c", len, p); - const int r = do_searchpair(spat, "", epat, FORWARD, NULL, - 0, NULL, (linenr_T)0, 0L); + const int r = (int)do_searchpair(spat, "", epat, FORWARD, NULL, 0, NULL, (linenr_T)0, 0L); xfree(spat); xfree(epat); @@ -3795,7 +3794,7 @@ extend: } else { dir = FORWARD; } - for (i = count; --i >= 0;) { + for (i = (int)count; --i >= 0;) { if (start_lnum == (dir == BACKWARD ? 1 : curbuf->b_ml.ml_line_count)) { retval = FAIL; @@ -3863,8 +3862,8 @@ extend: ++end_lnum; } - --end_lnum; - i = count; + end_lnum--; + i = (int)count; if (!include && white_in_front) { --i; } @@ -4435,9 +4434,9 @@ static int is_zero_width(char_u *pattern, int move, pos_T *cur, Direction direct // start and end are in the same position. do { regmatch.startpos[0].col++; - nmatched = vim_regexec_multi(®match, curwin, curbuf, - pos.lnum, regmatch.startpos[0].col, - NULL, NULL); + nmatched = (int)vim_regexec_multi(®match, curwin, curbuf, + pos.lnum, regmatch.startpos[0].col, + NULL, NULL); if (nmatched != 0) { break; } @@ -4624,7 +4623,7 @@ static void update_search_stat(int dirc, pos_T *pos, pos_T *cursor_pos, searchst if (done_search) { xfree(lastpat); lastpat = vim_strsave(spats[last_idx].pat); - chgtick = buf_get_changedtick(curbuf); + chgtick = (int)buf_get_changedtick(curbuf); lbuf = curbuf; lastpos = p; } @@ -4704,21 +4703,21 @@ void f_searchcount(typval_T *argvars, typval_T *rettv, FunPtr fptr) } li = tv_list_find(di->di_tv.vval.v_list, 0L); if (li != NULL) { - pos.lnum = tv_get_number_chk(TV_LIST_ITEM_TV(li), &error); + pos.lnum = (linenr_T)tv_get_number_chk(TV_LIST_ITEM_TV(li), &error); if (error) { return; } } li = tv_list_find(di->di_tv.vval.v_list, 1L); if (li != NULL) { - pos.col = tv_get_number_chk(TV_LIST_ITEM_TV(li), &error) - 1; + pos.col = (colnr_T)tv_get_number_chk(TV_LIST_ITEM_TV(li), &error) - 1; if (error) { return; } } li = tv_list_find(di->di_tv.vval.v_list, 2L); if (li != NULL) { - pos.coladd = tv_get_number_chk(TV_LIST_ITEM_TV(li), &error); + pos.coladd = (colnr_T)tv_get_number_chk(TV_LIST_ITEM_TV(li), &error); if (error) { return; } @@ -4841,7 +4840,7 @@ static int fuzzy_match_compute_score(const char_u *const str, const int strSz, int score = 100; // Apply leading letter penalty - int penalty = LEADING_LETTER_PENALTY * matches[0]; + int penalty = LEADING_LETTER_PENALTY * (int)matches[0]; if (penalty < MAX_LEADING_LETTER_PENALTY) { penalty = MAX_LEADING_LETTER_PENALTY; } @@ -4862,7 +4861,7 @@ static int fuzzy_match_compute_score(const char_u *const str, const int strSz, if (currIdx == prevIdx + 1) { score += SEQUENTIAL_BONUS; } else { - score += GAP_PENALTY * (currIdx - prevIdx); + score += GAP_PENALTY * (int)(currIdx - prevIdx); } } @@ -4936,7 +4935,7 @@ static int fuzzy_match_recursive(const char_u *fuzpat, const char_u *str, uint32 // "Copy-on-Write" srcMatches into matches if (first_match && srcMatches != NULL) { - memcpy(matches, srcMatches, nextMatch * sizeof(srcMatches[0])); + memcpy(matches, srcMatches, (size_t)nextMatch * sizeof(srcMatches[0])); first_match = false; } @@ -4976,7 +4975,7 @@ static int fuzzy_match_recursive(const char_u *fuzpat, const char_u *str, uint32 // Return best result if (recursiveMatch && (!matched || bestRecursiveScore > *outScore)) { // Recursive score is better than "this" - memcpy(matches, bestRecursiveMatches, maxMatches * sizeof(matches[0])); + memcpy(matches, bestRecursiveMatches, (size_t)maxMatches * sizeof(matches[0])); *outScore = bestRecursiveScore; return nextMatch; } else if (matched) { @@ -5095,7 +5094,7 @@ static void fuzzy_match_in_list(list_T *const l, char_u *const str, const bool m len = max_matches; } - fuzzyItem_T *const items = xcalloc(len, sizeof(fuzzyItem_T)); + fuzzyItem_T *const items = xcalloc((size_t)len, sizeof(fuzzyItem_T)); long match_count = 0; uint32_t matches[MAX_FUZZY_MATCHES]; @@ -5136,7 +5135,7 @@ static void fuzzy_match_in_list(list_T *const l, char_u *const str, const bool m int score; if (itemstr != NULL && fuzzy_match(itemstr, str, matchseq, &score, matches, MAX_FUZZY_MATCHES)) { - items[match_count].idx = match_count; + items[match_count].idx = (int)match_count; items[match_count].item = li; items[match_count].score = score; @@ -5161,7 +5160,7 @@ static void fuzzy_match_in_list(list_T *const l, char_u *const str, const bool m if (match_count > 0) { // Sort the list by the descending order of the match score - qsort(items, match_count, sizeof(fuzzyItem_T), fuzzy_match_item_compare); + qsort(items, (size_t)match_count, sizeof(fuzzyItem_T), fuzzy_match_item_compare); // For matchfuzzy(), return a list of matched strings. // ['str1', 'str2', 'str3'] @@ -5394,7 +5393,7 @@ void find_pattern_in_path(char_u *ptr, Direction dir, size_t len, bool whole, bo } def_regmatch.rm_ic = FALSE; // don't ignore case in define pat. } - files = xcalloc(max_path_depth, sizeof(SearchedFile)); + files = xcalloc((size_t)max_path_depth, sizeof(SearchedFile)); old_files = max_path_depth; depth = depth_displayed = -1; @@ -5537,7 +5536,7 @@ void find_pattern_in_path(char_u *ptr, Direction dir, size_t len, bool whole, bo if (new_fname != NULL) { // Push the new file onto the file stack if (depth + 1 == old_files) { - bigger = xmalloc(max_path_depth * 2 * sizeof(SearchedFile)); + bigger = xmalloc((size_t)max_path_depth * 2 * sizeof(SearchedFile)); for (i = 0; i <= depth; i++) { bigger[i] = files[i]; } @@ -5849,7 +5848,7 @@ exit_matched: if (action == ACTION_EXPAND) { ins_compl_check_keys(30, false); } - if (got_int || compl_interrupted) { + if (got_int || ins_compl_interrupted()) { break; } @@ -5911,7 +5910,7 @@ exit_matched: } } else if (!found && action != ACTION_EXPAND) { - if (got_int || compl_interrupted) { + if (got_int || ins_compl_interrupted()) { emsg(_(e_interr)); } else if (type == FIND_DEFINE) { emsg(_("E388: Couldn't find definition")); diff --git a/src/nvim/search.h b/src/nvim/search.h index 53059cc1ea..ff843bb59e 100644 --- a/src/nvim/search.h +++ b/src/nvim/search.h @@ -5,7 +5,6 @@ #include <stdint.h> #include "nvim/buffer_defs.h" -#include "nvim/eval/funcs.h" #include "nvim/eval/typval.h" #include "nvim/normal.h" #include "nvim/os/time.h" diff --git a/src/nvim/sign.c b/src/nvim/sign.c index 9a4b304d6c..1640d0167e 100644 --- a/src/nvim/sign.c +++ b/src/nvim/sign.c @@ -10,6 +10,7 @@ #include "nvim/charset.h" #include "nvim/cursor.h" #include "nvim/edit.h" +#include "nvim/eval/funcs.h" #include "nvim/ex_docmd.h" #include "nvim/fold.h" #include "nvim/highlight_group.h" diff --git a/src/nvim/sign.h b/src/nvim/sign.h index c61e5d20ef..ba84cd71a4 100644 --- a/src/nvim/sign.h +++ b/src/nvim/sign.h @@ -4,7 +4,6 @@ #include <stdbool.h> #include "nvim/buffer_defs.h" -#include "nvim/eval/funcs.h" #include "nvim/ex_cmds_defs.h" #include "nvim/sign_defs.h" diff --git a/src/nvim/spell.c b/src/nvim/spell.c index 2aadc2258e..ceb35af4b8 100644 --- a/src/nvim/spell.c +++ b/src/nvim/spell.c @@ -95,6 +95,7 @@ #include "nvim/getchar.h" #include "nvim/hashtab.h" #include "nvim/input.h" +#include "nvim/insexpand.h" #include "nvim/mark.h" #include "nvim/mbyte.h" #include "nvim/memline.h" @@ -291,7 +292,7 @@ int did_set_spelltab; // structure used to store soundfolded words that add_sound_suggest() has // handled already. typedef struct { - short sft_score; // lowest score used + int16_t sft_score; // lowest score used char_u sft_word[1]; // soundfolded word, actually longer } sftword_T; @@ -743,9 +744,8 @@ static void find_word(matchinf_T *mip, int mode) // prefix ID. // Repeat this if there are more flags/region alternatives until there // is a match. - for (len = byts[arridx - 1]; len > 0 && byts[arridx] == 0; - --len, ++arridx) { - uint32_t flags = idxs[arridx]; + for (len = byts[arridx - 1]; len > 0 && byts[arridx] == 0; len--, arridx++) { + uint32_t flags = (uint32_t)idxs[arridx]; // For the fold-case tree check that the case of the checked word // matches with what the word in the tree requires. @@ -760,7 +760,7 @@ static void find_word(matchinf_T *mip, int mode) } if (mip->mi_capflags == WF_KEEPCAP - || !spell_valid_case(mip->mi_capflags, flags)) { + || !spell_valid_case(mip->mi_capflags, (int)flags)) { continue; } } @@ -769,7 +769,7 @@ static void find_word(matchinf_T *mip, int mode) // mip->mi_prefarridx that find_prefix() filled. else if (mode == FIND_PREFIX && !prefix_found) { c = valid_word_prefix(mip->mi_prefcnt, mip->mi_prefarridx, - flags, + (int)flags, mip->mi_word + mip->mi_cprefixlen, slang, false); if (c == 0) { @@ -828,10 +828,8 @@ static void find_word(matchinf_T *mip, int mode) } // Quickly check if compounding is possible with this flag. - if (!byte_in_str(mip->mi_complen == 0 - ? slang->sl_compstartflags - : slang->sl_compallflags, - ((unsigned)flags >> 24))) { + if (!byte_in_str(mip->mi_complen == 0 ? slang->sl_compstartflags : slang->sl_compallflags, + (int)((unsigned)flags >> 24))) { continue; } @@ -879,7 +877,7 @@ static void find_word(matchinf_T *mip, int mode) // If the word ends the sequence of compound flags of the // words must match with one of the COMPOUNDRULE items and // the number of syllables must not be too large. - mip->mi_compflags[mip->mi_complen] = ((unsigned)flags >> 24); + mip->mi_compflags[mip->mi_complen] = (char_u)((unsigned)flags >> 24); mip->mi_compflags[mip->mi_complen + 1] = NUL; if (word_ends) { char_u fword[MAXWLEN] = { 0 }; @@ -1005,7 +1003,7 @@ static void find_word(matchinf_T *mip, int mode) res = SP_BANNED; } else if (flags & WF_REGION) { // Check region. - if ((mip->mi_lp->lp_region & (flags >> 16)) != 0) { + if (((unsigned)mip->mi_lp->lp_region & (flags >> 16)) != 0) { res = SP_OK; } else { res = SP_LOCAL; @@ -1119,7 +1117,7 @@ static bool can_be_compound(trystate_T *sp, slang_T *slang, char_u *compflags, i // possibly can form a match with COMPOUNDRULE patterns. This only // makes sense when we have two or more words. if (slang->sl_comprules != NULL && sp->ts_complen > sp->ts_compsplit) { - compflags[sp->ts_complen] = flag; + compflags[sp->ts_complen] = (char_u)flag; compflags[sp->ts_complen + 1] = NUL; bool v = match_compoundrule(slang, compflags + sp->ts_compsplit); compflags[sp->ts_complen] = NUL; @@ -1195,10 +1193,9 @@ static int valid_word_prefix(int totprefcnt, int arridx, int flags, char_u *word { int prefcnt; int pidx; - int prefid; - prefid = (unsigned)flags >> 24; - for (prefcnt = totprefcnt - 1; prefcnt >= 0; --prefcnt) { + int prefid = (int)((unsigned)flags >> 24); + for (prefcnt = totprefcnt - 1; prefcnt >= 0; prefcnt--) { pidx = slang->sl_pidxs[arridx + prefcnt]; // Check the prefix ID. @@ -1646,7 +1643,7 @@ void spell_cat_line(char_u *buf, char_u *line, int maxlen) // concatenate. n = (int)(p - line) + 1; if (n < maxlen - 1) { - memset(buf, ' ', n); + memset(buf, ' ', (size_t)n); STRLCPY(buf + n, p, maxlen - n); } } @@ -1872,7 +1869,7 @@ static void spell_load_cb(char *fname, void *cookie) /// @param[in] word added to common words hashtable /// @param[in] len length of word or -1 for NUL terminated /// @param[in] count 1 to count once, 10 to init -void count_common_word(slang_T *lp, char_u *word, int len, int count) +void count_common_word(slang_T *lp, char_u *word, int len, uint8_t count) { hash_T hash; hashitem_T *hi; @@ -1899,7 +1896,8 @@ void count_common_word(slang_T *lp, char_u *word, int len, int count) hash_add_item(&lp->sl_wordcount, hi, wc->wc_word, hash); } else { wc = HI2WC(hi); - if ((wc->wc_count += count) < (unsigned)count) { // check for overflow + wc->wc_count = (uint16_t)(wc->wc_count + count); + if (wc->wc_count < count) { // check for overflow wc->wc_count = MAXWORDCOUNT; } } @@ -2108,7 +2106,7 @@ char *did_set_spelllang(win_T *wp) if (p != NULL && ASCII_ISALPHA(p[1]) && ASCII_ISALPHA(p[2]) && !ASCII_ISALPHA(p[3])) { STRLCPY(region_cp, p + 1, 3); - memmove(p, p + 3, len - (p - lang) - 2); + memmove(p, p + 3, (size_t)(len - (p - lang) - 2)); region = region_cp; } else { dont_use_region = true; @@ -2360,11 +2358,11 @@ static void use_midword(slang_T *lp, win_T *wp) wp->w_s->b_spell_ismw[c] = true; } else if (wp->w_s->b_spell_ismw_mb == NULL) { // First multi-byte char in "b_spell_ismw_mb". - wp->w_s->b_spell_ismw_mb = vim_strnsave(p, l); + wp->w_s->b_spell_ismw_mb = vim_strnsave(p, (size_t)l); } else { // Append multi-byte chars to "b_spell_ismw_mb". const int n = (int)STRLEN(wp->w_s->b_spell_ismw_mb); - char_u *bp = vim_strnsave(wp->w_s->b_spell_ismw_mb, n + l); + char_u *bp = vim_strnsave(wp->w_s->b_spell_ismw_mb, (size_t)n + (size_t)l); xfree(wp->w_s->b_spell_ismw_mb); wp->w_s->b_spell_ismw_mb = bp; STRLCPY(bp + n, p, l + 1); @@ -2616,9 +2614,9 @@ void clear_spell_chartab(spelltab_T *sp) memset(sp->st_isw, false, sizeof(sp->st_isw)); memset(sp->st_isu, false, sizeof(sp->st_isu)); - for (i = 0; i < 256; ++i) { - sp->st_fold[i] = i; - sp->st_upper[i] = i; + for (i = 0; i < 256; i++) { + sp->st_fold[i] = (char_u)i; + sp->st_upper[i] = (char_u)i; } // We include digits. A word shouldn't start with a digit, but handling @@ -2629,11 +2627,11 @@ void clear_spell_chartab(spelltab_T *sp) for (i = 'A'; i <= 'Z'; ++i) { sp->st_isw[i] = true; sp->st_isu[i] = true; - sp->st_fold[i] = i + 0x20; + sp->st_fold[i] = (char_u)(i + 0x20); } for (i = 'a'; i <= 'z'; ++i) { sp->st_isw[i] = true; - sp->st_upper[i] = i - 0x20; + sp->st_upper[i] = (char_u)(i - 0x20); } } @@ -2656,8 +2654,8 @@ void init_spell_chartab(void) // The folded/upper-cased value is different between latin1 and // utf8 for 0xb5, causing E763 for no good reason. Use the latin1 // value for utf-8 to avoid this. - spelltab.st_fold[i] = (f < 256) ? f : i; - spelltab.st_upper[i] = (u < 256) ? u : i; + spelltab.st_fold[i] = (f < 256) ? (char_u)f : (char_u)i; + spelltab.st_upper[i] = (u < 256) ? (char_u)u : (char_u)i; } } @@ -2967,12 +2965,11 @@ void spell_suggest(int count) stp = &SUG(sug.su_ga, i); // The suggested word may replace only part of the bad word, add - // the not replaced part. + // the not replaced part. But only when it's not getting too long. STRLCPY(wcopy, stp->st_word, MAXWLEN + 1); - if (sug.su_badlen > stp->st_orglen) { - STRLCPY(wcopy + stp->st_wordlen, - sug.su_badptr + stp->st_orglen, - sug.su_badlen - stp->st_orglen + 1); + int el = sug.su_badlen - stp->st_orglen; + if (el > 0 && stp->st_wordlen + el <= MAXWLEN) { + STRLCPY(wcopy + stp->st_wordlen, sug.su_badptr + stp->st_orglen, el + 1); } vim_snprintf((char *)IObuff, IOSIZE, "%2d", i + 1); if (cmdmsg_rl) { @@ -3036,21 +3033,21 @@ void spell_suggest(int count) if (sug.su_badlen > stp->st_orglen) { // Replacing less than "su_badlen", append the remainder to // repl_to. - repl_from = vim_strnsave(sug.su_badptr, sug.su_badlen); + repl_from = vim_strnsave(sug.su_badptr, (size_t)sug.su_badlen); vim_snprintf((char *)IObuff, IOSIZE, "%s%.*s", stp->st_word, sug.su_badlen - stp->st_orglen, sug.su_badptr + stp->st_orglen); repl_to = vim_strsave(IObuff); } else { // Replacing su_badlen or more, use the whole word. - repl_from = vim_strnsave(sug.su_badptr, stp->st_orglen); + repl_from = vim_strnsave(sug.su_badptr, (size_t)stp->st_orglen); repl_to = vim_strsave(stp->st_word); } // Replace the word. - p = xmalloc(STRLEN(line) - stp->st_orglen + stp->st_wordlen + 1); + p = xmalloc(STRLEN(line) - (size_t)stp->st_orglen + (size_t)stp->st_wordlen + 1); c = (int)(sug.su_badptr - line); - memmove(p, line, c); + memmove(p, line, (size_t)c); STRCPY(p + c, stp->st_word); STRCAT(p, sug.su_badptr + stp->st_orglen); @@ -3171,8 +3168,8 @@ void ex_spellrepall(exarg_T *eap) line = get_cursor_line_ptr(); if (addlen <= 0 || STRNCMP(line + curwin->w_cursor.col, repl_to, STRLEN(repl_to)) != 0) { - p = xmalloc(STRLEN(line) + addlen + 1); - memmove(p, line, curwin->w_cursor.col); + p = xmalloc(STRLEN(line) + (size_t)addlen + 1); + memmove(p, line, (size_t)curwin->w_cursor.col); STRCPY(p + curwin->w_cursor.col, repl_to); STRCAT(p, line + curwin->w_cursor.col + STRLEN(repl_from)); ml_replace(curwin->w_cursor.lnum, (char *)p, false); @@ -3219,8 +3216,7 @@ void spell_suggest_list(garray_T *gap, char_u *word, int maxcount, bool need_cap // The suggested word may replace only part of "word", add the not // replaced part. - wcopy = xmalloc(stp->st_wordlen - + STRLEN(sug.su_badptr + stp->st_orglen) + 1); + wcopy = xmalloc((size_t)stp->st_wordlen + STRLEN(sug.su_badptr + stp->st_orglen) + 1); STRCPY(wcopy, stp->st_word); STRCPY(wcopy + stp->st_wordlen, sug.su_badptr + stp->st_orglen); ((char_u **)gap->ga_data)[gap->ga_len++] = wcopy; @@ -3562,7 +3558,7 @@ static void allcap_copy(char_u *word, char_u *wcopy) if (d - wcopy >= MAXWLEN - 1) { break; } - *d++ = c; + *d++ = (char_u)c; } else { c = SPELL_TOUPPER(c); } @@ -3578,14 +3574,12 @@ static void allcap_copy(char_u *word, char_u *wcopy) // Try finding suggestions by recognizing specific situations. static void suggest_try_special(suginfo_T *su) { - char_u *p; - size_t len; int c; char_u word[MAXWLEN]; // Recognize a word that is repeated: "the the". - p = skiptowhite(su->su_fbadword); - len = p - su->su_fbadword; + char_u *p = skiptowhite(su->su_fbadword); + size_t len = (size_t)(p - su->su_fbadword); p = (char_u *)skipwhite((char *)p); if (STRLEN(p) == len && STRNCMP(su->su_fbadword, p, len) == 0) { // Include badflags: if the badword is onecap or allcap @@ -3593,7 +3587,7 @@ static void suggest_try_special(suginfo_T *su) c = su->su_fbadword[len]; su->su_fbadword[len] = NUL; make_case_word(su->su_fbadword, word, su->su_badflags); - su->su_fbadword[len] = c; + su->su_fbadword[len] = (char_u)c; // Give a soundalike score of 0, compute the score as if deleting one // character. @@ -3820,13 +3814,13 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so if (sp->ts_prefixdepth == PFD_PREFIXTREE) { // Skip over the NUL bytes, we use them later. for (n = 0; n < len && byts[arridx + n] == 0; n++) {} - sp->ts_curi += n; + sp->ts_curi = (int16_t)(sp->ts_curi + n); // Always past NUL bytes now. n = (int)sp->ts_state; PROF_STORE(sp->ts_state) sp->ts_state = STATE_ENDNUL; - sp->ts_save_badflags = su->su_badflags; + sp->ts_save_badflags = (char_u)su->su_badflags; // At end of a prefix or at start of prefixtree: check for // following word. @@ -3843,7 +3837,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so go_deeper(stack, depth, 0); ++depth; sp = &stack[depth]; - sp->ts_prefixdepth = depth - 1; + sp->ts_prefixdepth = (char_u)(depth - 1); byts = fbyts; idxs = fidxs; sp->ts_arridx = 0; @@ -3863,7 +3857,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so // Past bytes in node and/or past NUL bytes. PROF_STORE(sp->ts_state) sp->ts_state = STATE_ENDNUL; - sp->ts_save_badflags = su->su_badflags; + sp->ts_save_badflags = (char_u)su->su_badflags; break; } @@ -3966,7 +3960,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so break; } - compflags[sp->ts_complen] = ((unsigned)flags >> 24); + compflags[sp->ts_complen] = (char_u)((unsigned)flags >> 24); compflags[sp->ts_complen + 1] = NUL; STRLCPY(preword + sp->ts_prewordlen, tword + sp->ts_splitoff, @@ -4048,7 +4042,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so newscore = 0; if (!soundfold) { // soundfold words don't have flags if ((flags & WF_REGION) - && (((unsigned)flags >> 16) & lp->lp_region) == 0) { + && (((unsigned)flags >> 16) & (unsigned)lp->lp_region) == 0) { newscore += SCORE_REGION; } if (flags & WF_RARE) { @@ -4166,10 +4160,9 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so && (slang->sl_compsylmax < MAXWLEN || sp->ts_complen + 1 - sp->ts_compsplit < slang->sl_compmax) - && (can_be_compound(sp, slang, - compflags, ((unsigned)flags >> 24)))) { + && (can_be_compound(sp, slang, compflags, (int)((unsigned)flags >> 24)))) { try_compound = true; - compflags[sp->ts_complen] = ((unsigned)flags >> 24); + compflags[sp->ts_complen] = (char_u)((unsigned)flags >> 24); compflags[sp->ts_complen + 1] = NUL; } @@ -4188,7 +4181,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so --sp->ts_curi; // do the same NUL again compflags[sp->ts_complen] = NUL; } else { - sp->ts_flags &= ~TSF_DIDSPLIT; + sp->ts_flags &= (char_u) ~TSF_DIDSPLIT; } if (try_split || try_compound) { @@ -4234,7 +4227,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so } #endif // Save things to be restored at STATE_SPLITUNDO. - sp->ts_save_badflags = su->su_badflags; + sp->ts_save_badflags = (char_u)su->su_badflags; PROF_STORE(sp->ts_state) sp->ts_state = STATE_SPLITUNDO; @@ -4265,14 +4258,13 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so l = utfc_ptr2len((char *)fword + sp->ts_fidx); if (fword_ends) { // Copy the skipped character to preword. - memmove(preword + sp->ts_prewordlen, - fword + sp->ts_fidx, l); - sp->ts_prewordlen += l; + memmove(preword + sp->ts_prewordlen, fword + sp->ts_fidx, (size_t)l); + sp->ts_prewordlen = (char_u)(sp->ts_prewordlen + l); preword[sp->ts_prewordlen] = NUL; } else { sp->ts_score -= SCORE_SPLIT - SCORE_SUBST; } - sp->ts_fidx += l; + sp->ts_fidx = (char_u)(sp->ts_fidx + l); } // When compounding include compound flag in @@ -4385,7 +4377,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so if (fword[sp->ts_fidx] != NUL) { sp->ts_fidx++; } - tword[sp->ts_twordlen++] = c; + tword[sp->ts_twordlen++] = (char_u)c; sp->ts_arridx = idxs[arridx]; if (newscore == SCORE_SUBST) { sp->ts_isdiff = DIFF_YES; @@ -4397,7 +4389,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so // First byte. sp->ts_tcharidx = 0; sp->ts_tcharlen = MB_BYTE2LEN(c); - sp->ts_fcharstart = sp->ts_fidx - 1; + sp->ts_fcharstart = (char_u)(sp->ts_fidx - 1); sp->ts_isdiff = (newscore != 0) ? DIFF_YES : DIFF_NONE; } else if (sp->ts_isdiff == DIFF_INSERT && sp->ts_fidx > 0) { @@ -4410,8 +4402,8 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so if (sp->ts_isdiff == DIFF_YES) { // Correct ts_fidx for the byte length of the // character (we didn't check that before). - sp->ts_fidx = sp->ts_fcharstart - + utfc_ptr2len((char *)fword + sp->ts_fcharstart); + sp->ts_fidx = (char_u)(sp->ts_fcharstart + + utfc_ptr2len((char *)fword + sp->ts_fcharstart)); // For changing a composing character adjust // the score from SCORE_SUBST to @@ -4498,7 +4490,8 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so // a bit illogical for soundfold tree but it does give better // results. c = utf_ptr2char((char *)fword + sp->ts_fidx); - stack[depth].ts_fidx += utfc_ptr2len((char *)fword + sp->ts_fidx); + stack[depth].ts_fidx = + (char_u)(stack[depth].ts_fidx + utfc_ptr2len((char *)fword + sp->ts_fidx)); if (utf_iscomposing(c)) { stack[depth].ts_score -= SCORE_DEL - SCORE_DELCOMP; } else if (c == utf_ptr2char((char *)fword + stack[depth].ts_fidx)) { @@ -4571,14 +4564,14 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so #endif ++depth; sp = &stack[depth]; - tword[sp->ts_twordlen++] = c; + tword[sp->ts_twordlen++] = (char_u)c; sp->ts_arridx = idxs[n]; fl = MB_BYTE2LEN(c); if (fl > 1) { // There are following bytes for the same character. // We must find all bytes before trying // delete/insert/swap/etc. - sp->ts_tcharlen = fl; + sp->ts_tcharlen = (char_u)fl; sp->ts_tcharidx = 1; sp->ts_isdiff = DIFF_INSERT; } @@ -4652,9 +4645,9 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so sp->ts_state = STATE_UNSWAP; depth++; fl = utf_char2len(c2); - memmove(p, p + n, fl); + memmove(p, p + n, (size_t)fl); utf_char2bytes(c, (char *)p + fl); - stack[depth].ts_fidxtry = sp->ts_fidx + n + fl; + stack[depth].ts_fidxtry = (char_u)(sp->ts_fidx + n + fl); } else { // If this swap doesn't work then SWAP3 won't either. PROF_STORE(sp->ts_state) @@ -4667,7 +4660,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so p = fword + sp->ts_fidx; n = utfc_ptr2len((char *)p); c = utf_ptr2char((char *)p + n); - memmove(p + utfc_ptr2len((char *)p + n), p, n); + memmove(p + utfc_ptr2len((char *)p + n), p, (size_t)n); utf_char2bytes(c, (char *)p); FALLTHROUGH; @@ -4708,10 +4701,10 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so sp->ts_state = STATE_UNSWAP3; depth++; tl = utf_char2len(c3); - memmove(p, p + n + fl, tl); + memmove(p, p + n + fl, (size_t)tl); utf_char2bytes(c2, (char *)p + tl); utf_char2bytes(c, (char *)p + fl + tl); - stack[depth].ts_fidxtry = sp->ts_fidx + n + fl + tl; + stack[depth].ts_fidxtry = (char_u)(sp->ts_fidx + n + fl + tl); } else { PROF_STORE(sp->ts_state) sp->ts_state = STATE_REP_INI; @@ -4726,7 +4719,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so fl = utfc_ptr2len((char *)p + n); c = utf_ptr2char((char *)p + n + fl); tl = utfc_ptr2len((char *)p + n + fl); - memmove(p + fl + tl, p, n); + memmove(p + fl + tl, p, (size_t)n); utf_char2bytes(c, (char *)p); utf_char2bytes(c2, (char *)p + tl); p = p + tl; @@ -4757,9 +4750,9 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so c = utf_ptr2char((char *)p); fl = utf_ptr2len((char *)p + n); fl += utf_ptr2len((char *)p + n + fl); - memmove(p, p + n, fl); + memmove(p, p + n, (size_t)fl); utf_char2bytes(c, (char *)p + fl); - stack[depth].ts_fidxtry = sp->ts_fidx + n + fl; + stack[depth].ts_fidxtry = (char_u)(sp->ts_fidx + n + fl); } else { PROF_STORE(sp->ts_state) sp->ts_state = STATE_REP_INI; @@ -4773,7 +4766,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so n += utfc_ptr2len((char *)p + n); c = utf_ptr2char((char *)p + n); tl = utfc_ptr2len((char *)p + n); - memmove(p + tl, p, n); + memmove(p + tl, p, (size_t)n); utf_char2bytes(c, (char *)p); // Rotate three bytes right: "123" -> "312". We change "fword" @@ -4794,9 +4787,9 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so n += utf_ptr2len((char *)p + n); c = utf_ptr2char((char *)p + n); tl = utf_ptr2len((char *)p + n); - memmove(p + tl, p, n); + memmove(p + tl, p, (size_t)n); utf_char2bytes(c, (char *)p); - stack[depth].ts_fidxtry = sp->ts_fidx + n + tl; + stack[depth].ts_fidxtry = (char_u)(sp->ts_fidx + n + tl); } else { PROF_STORE(sp->ts_state) sp->ts_state = STATE_REP_INI; @@ -4810,7 +4803,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so tl = utfc_ptr2len((char *)p); n = utfc_ptr2len((char *)p + tl); n += utfc_ptr2len((char *)p + tl + n); - memmove(p, p + tl, n); + memmove(p, p + tl, (size_t)n); utf_char2bytes(c, (char *)p + n); FALLTHROUGH; @@ -4862,7 +4855,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so ftp = (fromto_T *)gap->ga_data + sp->ts_curi++; if (*ftp->ft_from != *p) { // past possible matching entries - sp->ts_curi = gap->ga_len; + sp->ts_curi = (char_u)gap->ga_len; break; } if (STRNCMP(ftp->ft_from, p, STRLEN(ftp->ft_from)) == 0 @@ -4885,8 +4878,8 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so STRMOVE(p + tl, p + fl); repextra += tl - fl; } - memmove(p, ftp->ft_to, tl); - stack[depth].ts_fidxtry = sp->ts_fidx + tl; + memmove(p, ftp->ft_to, (size_t)tl); + stack[depth].ts_fidxtry = (char_u)(sp->ts_fidx + tl); stack[depth].ts_tcharlen = 0; break; } @@ -4915,7 +4908,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so STRMOVE(p + fl, p + tl); repextra -= tl - fl; } - memmove(p, ftp->ft_from, fl); + memmove(p, ftp->ft_from, (size_t)fl); PROF_STORE(sp->ts_state) sp->ts_state = STATE_REP; break; @@ -5410,7 +5403,7 @@ static void add_sound_suggest(suginfo_T *su, char_u *goodword, int score, langp_ hash); if (HASHITEM_EMPTY(hi)) { sft = xmalloc(sizeof(sftword_T) + goodword_len); - sft->sft_score = score; + sft->sft_score = (int16_t)score; memcpy(sft->sft_word, goodword, goodword_len + 1); hash_add_item(&slang->sl_sounddone, hi, sft->sft_word, hash); } else { @@ -5418,7 +5411,7 @@ static void add_sound_suggest(suginfo_T *su, char_u *goodword, int score, langp_ if (score >= sft->sft_score) { return; } - sft->sft_score = score; + sft->sft_score = (int16_t)score; } // Find the word nr in the soundfold tree. @@ -5511,7 +5504,7 @@ badword: } else { // Add a penalty for words in another region. if ((flags & WF_REGION) - && (((unsigned)flags >> 16) & lp->lp_region) == 0) { + && (((unsigned)flags >> 16) & (unsigned)lp->lp_region) == 0) { goodscore = SCORE_REGION; } else { goodscore = 0; @@ -5781,7 +5774,7 @@ static void add_suggestion(suginfo_T *su, garray_T *gap, const char_u *goodword, if (i < 0) { // Add a suggestion. stp = GA_APPEND_VIA_PTR(suggest_T, gap); - stp->st_word = vim_strnsave(goodword, goodlen); + stp->st_word = vim_strnsave(goodword, (size_t)goodlen); stp->st_wordlen = goodlen; stp->st_score = score; stp->st_altscore = altscore; @@ -5831,8 +5824,7 @@ static void check_suggestions(suginfo_T *su, garray_T *gap) xfree(stp[i].st_word); --gap->ga_len; if (i < gap->ga_len) { - memmove(stp + i, stp + i + 1, - sizeof(suggest_T) * (gap->ga_len - i)); + memmove(stp + i, stp + i + 1, sizeof(suggest_T) * (size_t)(gap->ga_len - i)); } } } @@ -6281,8 +6273,7 @@ static void spell_soundfold_wsal(slang_T *slang, char_u *inword, char_u *res) } } if (k > k0) { - memmove(word + i + k0, word + i + k, - sizeof(int) * (wordlen - (i + k) + 1)); + memmove(word + i + k0, word + i + k, sizeof(int) * (size_t)(wordlen - (i + k) + 1)); } // new "actual letter" @@ -6310,8 +6301,7 @@ static void spell_soundfold_wsal(slang_T *slang, char_u *inword, char_u *res) if (c != NUL) { wres[reslen++] = c; } - memmove(word, word + i + 1, - sizeof(int) * (wordlen - (i + 1) + 1)); + memmove(word, word + i + 1, sizeof(int) * (size_t)(wordlen - (i + 1) + 1)); i = 0; z0 = 1; } @@ -6610,7 +6600,7 @@ static int spell_edit_score(slang_T *slang, char_u *badword, char_u *goodword) // We use "cnt" as an array: CNT(badword_idx, goodword_idx). #define CNT(a, b) cnt[(a) + (b) * (badlen + 1)] - cnt = xmalloc(sizeof(int) * (badlen + 1) * (goodlen + 1)); + cnt = xmalloc(sizeof(int) * ((size_t)badlen + 1) * ((size_t)goodlen + 1)); CNT(0, 0) = 0; for (j = 1; j <= goodlen; ++j) { @@ -6879,7 +6869,7 @@ void ex_spelldump(exarg_T *eap) if (no_spell_checking(curwin)) { return; } - get_option_value("spl", &dummy, &spl, OPT_LOCAL); + (void)get_option_value("spl", &dummy, &spl, OPT_LOCAL); // Create a new empty buffer in a new window. do_cmdline_cmd("new"); @@ -7012,7 +7002,7 @@ void spell_dump_compl(char_u *pat, int ic, Direction *dir, int dumpflags_arg) arridx[0] = 0; curi[0] = 1; while (depth >= 0 && !got_int - && (pat == NULL || !compl_interrupted)) { + && (pat == NULL || !ins_compl_interrupted())) { if (curi[depth] > byts[arridx[depth]]) { // Done all bytes at this node, go up one level. --depth; @@ -7035,7 +7025,7 @@ void spell_dump_compl(char_u *pat, int ic, Direction *dir, int dumpflags_arg) && (do_region || (flags & WF_REGION) == 0 || (((unsigned)flags >> 16) - & lp->lp_region) != 0)) { + & (unsigned)lp->lp_region) != 0)) { word[depth] = NUL; if (!do_region) { flags &= ~WF_REGION; @@ -7043,7 +7033,7 @@ void spell_dump_compl(char_u *pat, int ic, Direction *dir, int dumpflags_arg) // Dump the basic word if there is no prefix or // when it's the first one. - c = (unsigned)flags >> 24; + c = (int)((unsigned)flags >> 24); if (c == 0 || curi[depth] == 2) { dump_word(slang, word, pat, dir, dumpflags, flags, lnum); @@ -7060,7 +7050,7 @@ void spell_dump_compl(char_u *pat, int ic, Direction *dir, int dumpflags_arg) } } else { // Normal char, go one level deeper. - word[depth++] = c; + word[depth++] = (char_u)c; arridx[depth] = idxs[n]; curi[depth] = 1; @@ -7256,7 +7246,7 @@ static linenr_T dump_prefixes(slang_T *slang, char_u *word, char_u *pat, Directi } } else { // Normal char, go one level deeper. - prefix[depth++] = c; + prefix[depth++] = (char_u)c; arridx[depth] = idxs[n]; curi[depth] = 1; } @@ -7329,7 +7319,7 @@ void spell_expand_check_cap(colnr_T col) // Used for Insert mode completion CTRL-X ?. // Returns the number of matches. The matches are in "matchp[]", array of // allocated strings. -int expand_spelling(linenr_T lnum, char_u *pat, char_u ***matchp) +int expand_spelling(linenr_T lnum, char_u *pat, char ***matchp) { garray_T ga; diff --git a/src/nvim/spell_defs.h b/src/nvim/spell_defs.h index 7e85b5bf03..222d103f5d 100644 --- a/src/nvim/spell_defs.h +++ b/src/nvim/spell_defs.h @@ -237,7 +237,7 @@ typedef struct trystate_S { state_T ts_state; // state at this level, STATE_ int ts_score; // score idx_T ts_arridx; // index in tree array, start of node - short ts_curi; // index in list of child nodes + int16_t ts_curi; // index in list of child nodes char_u ts_fidx; // index in fword[], case-folded bad word char_u ts_fidxtry; // ts_fidx at which bytes may be changed char_u ts_twordlen; // valid length of tword[] diff --git a/src/nvim/spellfile.c b/src/nvim/spellfile.c index 9d2fd2637d..9f21e24d4c 100644 --- a/src/nvim/spellfile.c +++ b/src/nvim/spellfile.c @@ -903,8 +903,8 @@ void suggest_load_files(void) } // <SUGHEADER>: <fileID> <versionnr> <timestamp> - for (i = 0; i < VIMSUGMAGICL; ++i) { - buf[i] = getc(fd); // <fileID> + for (i = 0; i < VIMSUGMAGICL; i++) { + buf[i] = (char_u)getc(fd); // <fileID> } if (STRNCMP(buf, VIMSUGMAGIC, VIMSUGMAGICL) != 0) { semsg(_("E778: This does not look like a .sug file: %s"), @@ -965,7 +965,7 @@ someerror: if (c < 0) { goto someerror; } - GA_APPEND(char_u, &ga, c); + GA_APPEND(char_u, &ga, (char_u)c); if (c == NUL) { break; } @@ -1009,7 +1009,7 @@ static char_u *read_cnt_string(FILE *fd, int cnt_bytes, int *cntp) *cntp = SP_TRUNCERROR; return NULL; } - cnt = (cnt << 8) + (unsigned)c; + cnt = (int)(((unsigned)cnt << 8) + (unsigned)c); } *cntp = cnt; if (cnt == 0) { @@ -1081,7 +1081,7 @@ static int read_prefcond_section(FILE *fd, slang_T *lp) return SP_FORMERROR; } - lp->sl_prefprog = xcalloc(cnt, sizeof(regprog_T *)); + lp->sl_prefprog = xcalloc((size_t)cnt, sizeof(regprog_T *)); lp->sl_prefixcnt = cnt; for (int i = 0; i < cnt; i++) { @@ -1146,7 +1146,7 @@ static int read_rep_section(FILE *fd, garray_T *gap, int16_t *first) for (int i = 0; i < gap->ga_len; ++i) { ftp = &((fromto_T *)gap->ga_data)[i]; if (first[*ftp->ft_from] == -1) { - first[*ftp->ft_from] = i; + first[*ftp->ft_from] = (int16_t)i; } } return 0; @@ -1193,7 +1193,7 @@ static int read_sal_section(FILE *fd, slang_T *slang) if (ccnt < 0) { return SP_TRUNCERROR; } - p = xmalloc(ccnt + 2); + p = xmalloc((size_t)ccnt + 2); smp->sm_lead = p; // Read up to the first special char into sm_lead. @@ -1203,7 +1203,7 @@ static int read_sal_section(FILE *fd, slang_T *slang) if (vim_strchr("0123456789(-<^$", c) != NULL) { break; } - *p++ = c; + *p++ = (char_u)c; } smp->sm_leadlen = (int)(p - smp->sm_lead); *p++ = NUL; @@ -1216,7 +1216,7 @@ static int read_sal_section(FILE *fd, slang_T *slang) if (c == ')') { break; } - *p++ = c; + *p++ = (char_u)c; } *p++ = NUL; if (++i < ccnt) { @@ -1230,7 +1230,7 @@ static int read_sal_section(FILE *fd, slang_T *slang) smp->sm_rules = p; if (i < ccnt) { // store the char we got while checking for end of sm_lead - *p++ = c; + *p++ = (char_u)c; } i++; if (i < ccnt) { @@ -1302,7 +1302,7 @@ static int read_words_section(FILE *fd, slang_T *lp, int len) if (c == EOF) { return SP_TRUNCERROR; } - word[i] = c; + word[i] = (char_u)c; if (word[i] == NUL) { break; } @@ -1363,11 +1363,6 @@ static int read_compound(FILE *fd, slang_T *slang, int len) int todo = len; int c; int atstart; - char_u *pat; - char_u *pp; - char_u *cp; - char_u *ap; - char_u *crp; int cnt; garray_T *gap; @@ -1432,25 +1427,25 @@ static int read_compound(FILE *fd, slang_T *slang, int len) // Conversion to utf-8 may double the size. c = todo * 2 + 7; c += todo * 2; - pat = xmalloc(c); + char_u *pat = xmalloc((size_t)c); // We also need a list of all flags that can appear at the start and one // for all flags. - cp = xmalloc(todo + 1); + char_u *cp = xmalloc((size_t)todo + 1); slang->sl_compstartflags = cp; *cp = NUL; - ap = xmalloc(todo + 1); + char_u *ap = xmalloc((size_t)todo + 1); slang->sl_compallflags = ap; *ap = NUL; // And a list of all patterns in their original form, for checking whether // compounding may work in match_compoundrule(). This is freed when we // encounter a wildcard, the check doesn't work then. - crp = xmalloc(todo + 1); + char_u *crp = xmalloc((size_t)todo + 1); slang->sl_comprules = crp; - pp = pat; + char_u *pp = pat; *pp++ = '^'; *pp++ = '\\'; *pp++ = '('; @@ -1466,7 +1461,7 @@ static int read_compound(FILE *fd, slang_T *slang, int len) // Add all flags to "sl_compallflags". if (vim_strchr("?*+[]/", c) == NULL && !byte_in_str(slang->sl_compallflags, c)) { - *ap++ = c; + *ap++ = (char_u)c; *ap = NUL; } @@ -1479,7 +1474,7 @@ static int read_compound(FILE *fd, slang_T *slang, int len) atstart = 0; } else { if (!byte_in_str(slang->sl_compstartflags, c)) { - *cp++ = c; + *cp++ = (char_u)c; *cp = NUL; } if (atstart == 1) { @@ -1494,7 +1489,7 @@ static int read_compound(FILE *fd, slang_T *slang, int len) XFREE_CLEAR(slang->sl_comprules); crp = NULL; } else { - *crp++ = c; + *crp++ = (char_u)c; } } @@ -1561,7 +1556,7 @@ static int set_sofo(slang_T *lp, char_u *from, char_u *to) // Allocate the lists. for (int i = 0; i < 256; i++) { if (lp->sl_sal_first[i] > 0) { - p = xmalloc(sizeof(int) * (lp->sl_sal_first[i] * 2 + 1)); + p = xmalloc(sizeof(int) * (size_t)(lp->sl_sal_first[i] * 2 + 1)); ((int **)gap->ga_data)[i] = (int *)p; *(int *)p = 0; } @@ -1631,7 +1626,7 @@ static void set_sal_first(slang_T *lp) i++; n--; tsal = smp[i + n]; - memmove(smp + i + 1, smp + i, sizeof(salitem_T) * n); + memmove(smp + i + 1, smp + i, sizeof(salitem_T) * (size_t)n); smp[i] = tsal; } } @@ -1645,7 +1640,7 @@ static int *mb_str2wide(char_u *s) { int i = 0; - int *res = xmalloc((mb_charlen(s) + 1) * sizeof(int)); + int *res = xmalloc(((size_t)mb_charlen(s) + 1) * sizeof(int)); for (char_u *p = s; *p != NUL;) { res[i++] = mb_ptr2char_adv((const char_u **)&p); } @@ -1682,18 +1677,18 @@ static int spell_read_tree(FILE *fd, char_u **bytsp, long *bytsp_len, idx_T **id } if (len > 0) { // Allocate the byte array. - bp = xmalloc(len); + bp = xmalloc((size_t)len); *bytsp = bp; if (bytsp_len != NULL) { *bytsp_len = len; } // Allocate the index array. - ip = xcalloc(len, sizeof(*ip)); + ip = xcalloc((size_t)len, sizeof(*ip)); *idxsp = ip; // Recursively read the tree and store it in the array. - idx = read_tree_node(fd, bp, ip, len, 0, prefixtree, prefixcnt); + idx = read_tree_node(fd, bp, ip, (int)len, 0, prefixtree, prefixcnt); if (idx < 0) { return idx; } @@ -1733,7 +1728,7 @@ static idx_T read_tree_node(FILE *fd, char_u *byts, idx_T *idxs, int maxidx, idx if (startidx + len >= maxidx) { return SP_FORMERROR; } - byts[idx++] = len; + byts[idx++] = (char_u)len; // Read the byte values, flag/region bytes and shared indexes. for (i = 1; i <= len; ++i) { @@ -1793,7 +1788,7 @@ static idx_T read_tree_node(FILE *fd, char_u *byts, idx_T *idxs, int maxidx, idx c = getc(fd); // <xbyte> } } - byts[idx++] = c; + byts[idx++] = (char_u)c; } // Recursively read the children for non-shared siblings. @@ -1867,7 +1862,7 @@ static long compress_added = 500000; // word count // Sets "sps_flags". int spell_check_msm(void) { - char_u *p = p_msm; + char *p = (char *)p_msm; long start = 0; long incr = 0; long added = 0; @@ -2257,7 +2252,7 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname) if (compflags != NULL) { l += (int)STRLEN(compflags) + 1; } - p = getroom(spin, l, false); + p = getroom(spin, (size_t)l, false); if (compflags != NULL) { STRCPY(p, compflags); STRCAT(p, "/"); @@ -2885,7 +2880,7 @@ static unsigned get_affitem(int flagtype, char_u **pp) res = mb_ptr2char_adv((const char_u **)pp) + (res << 16); } } - return res; + return (unsigned)res; } // Process the "compflags" string used in an affix file and append it to @@ -2911,7 +2906,7 @@ static void process_compflags(spellinfo_T *spin, afffile_T *aff, char_u *compfla if (spin->si_compflags != NULL) { len += (int)STRLEN(spin->si_compflags) + 1; } - p = getroom(spin, len, false); + p = getroom(spin, (size_t)len, false); if (spin->si_compflags != NULL) { STRCPY(p, spin->si_compflags); STRCAT(p, "/"); @@ -2947,7 +2942,7 @@ static void process_compflags(spellinfo_T *spin, afffile_T *aff, char_u *compfla ci->ci_newID = id; hash_add(&aff->af_comp, ci->ci_key); } - *tp++ = id; + *tp++ = (char_u)id; } if (aff->af_flagtype == AFT_NUM && *p == ',') { ++p; @@ -2978,15 +2973,15 @@ static bool flag_in_afflist(int flagtype, char_u *afflist, unsigned flag) switch (flagtype) { case AFT_CHAR: - return vim_strchr((char *)afflist, flag) != NULL; + return vim_strchr((char *)afflist, (int)flag) != NULL; case AFT_CAPLONG: case AFT_LONG: for (p = afflist; *p != NUL;) { - n = mb_ptr2char_adv((const char_u **)&p); + n = (unsigned)mb_ptr2char_adv((const char_u **)&p); if ((flagtype == AFT_LONG || (n >= 'A' && n <= 'Z')) && *p != NUL) { - n = mb_ptr2char_adv((const char_u **)&p) + (n << 16); + n = (unsigned)mb_ptr2char_adv((const char_u **)&p) + (n << 16); } if (n == flag) { return true; @@ -3362,7 +3357,7 @@ static int get_pfxlist(afffile_T *affile, char_u *afflist, char_u *store_afflist if (!HASHITEM_EMPTY(hi)) { id = HI2AH(hi)->ah_newID; if (id != 0) { - store_afflist[cnt++] = id; + store_afflist[cnt++] = (char_u)id; } } } @@ -3393,7 +3388,7 @@ static void get_compflags(afffile_T *affile, char_u *afflist, char_u *store_affl STRLCPY(key, prevp, p - prevp + 1); hi = hash_find(&affile->af_comp, (char *)key); if (!HASHITEM_EMPTY(hi)) { - store_afflist[cnt++] = HI2CI(hi)->ci_newID; + store_afflist[cnt++] = (char_u)HI2CI(hi)->ci_newID; } } if (affile->af_flagtype == AFT_NUM && *p == ',') { @@ -3856,11 +3851,10 @@ static void *getroom(spellinfo_T *spin, size_t len, bool align) if (align && bl != NULL) { // Round size up for alignment. On some systems structures need to be // aligned to the size of a pointer (e.g., SPARC). - bl->sb_used = (bl->sb_used + sizeof(char *) - 1) - & ~(sizeof(char *) - 1); + bl->sb_used = (int)(((size_t)bl->sb_used + sizeof(char *) - 1) & ~(sizeof(char *) - 1)); } - if (bl == NULL || bl->sb_used + len > SBLOCKSIZE) { + if (bl == NULL || (size_t)bl->sb_used + len > SBLOCKSIZE) { // Allocate a block of memory. It is not freed until much later. bl = xcalloc(1, (sizeof(sblock_T) + SBLOCKSIZE)); bl->sb_next = spin->si_blocks; @@ -4072,9 +4066,9 @@ static int tree_add_word(spellinfo_T *spin, char_u *word, wordnode_T *root, int } if (word[i] == NUL) { - node->wn_flags = flags; - node->wn_region |= region; - node->wn_affixID = affixID; + node->wn_flags = (uint16_t)flags; + node->wn_region |= (int16_t)region; + node->wn_affixID = (char_u)affixID; break; } prev = &node->wn_child; @@ -4298,12 +4292,12 @@ static long node_compress(spellinfo_T *spin, wordnode_T *node, hashtab_T *ht, lo // Make a hash key for the node and its siblings, so that we can quickly // find a lookalike node. This must be done after compressing the sibling // list, otherwise the hash key would become invalid by the compression. - node->wn_u1.hashkey[0] = len; + node->wn_u1.hashkey[0] = (char_u)len; nr = 0; for (np = node; np != NULL; np = np->wn_sibling) { if (np->wn_byte == NUL) { // end node: use wn_flags, wn_region and wn_affixID - n = np->wn_flags + (np->wn_region << 8) + (np->wn_affixID << 16); + n = (unsigned)(np->wn_flags + (np->wn_region << 8) + (np->wn_affixID << 16)); } else { // byte node: use the byte value and the child pointer n = (unsigned)(np->wn_byte + ((uintptr_t)np->wn_child << 8)); @@ -4313,13 +4307,13 @@ static long node_compress(spellinfo_T *spin, wordnode_T *node, hashtab_T *ht, lo // Avoid NUL bytes, it terminates the hash key. n = nr & 0xff; - node->wn_u1.hashkey[1] = n == 0 ? 1 : n; + node->wn_u1.hashkey[1] = n == 0 ? 1 : (char_u)n; n = (nr >> 8) & 0xff; - node->wn_u1.hashkey[2] = n == 0 ? 1 : n; + node->wn_u1.hashkey[2] = n == 0 ? 1 : (char_u)n; n = (nr >> 16) & 0xff; - node->wn_u1.hashkey[3] = n == 0 ? 1 : n; + node->wn_u1.hashkey[3] = n == 0 ? 1 : (char_u)n; n = (nr >> 24) & 0xff; - node->wn_u1.hashkey[4] = n == 0 ? 1 : n; + node->wn_u1.hashkey[4] = n == 0 ? 1 : (char_u)n; node->wn_u1.hashkey[5] = NUL; // Check for CTRL-C pressed now and then. @@ -4885,7 +4879,7 @@ static int put_node(FILE *fd, wordnode_T *node, int idx, int regionmask, bool pr void ex_mkspell(exarg_T *eap) { int fcount; - char_u **fnames; + char **fnames; char_u *arg = (char_u *)eap->arg; bool ascii = false; @@ -5034,7 +5028,7 @@ static int sug_filltree(spellinfo_T *spin, slang_T *slang) // We use the "flags" field for the MSB of the wordnr, // "region" for the LSB of the wordnr. if (tree_add_word(spin, tsalword, spin->si_foldroot, - words_done >> 16, words_done & 0xffff, + (int)(words_done >> 16), words_done & 0xffff, 0) == FAIL) { return FAIL; } @@ -5054,7 +5048,7 @@ static int sug_filltree(spellinfo_T *spin, slang_T *slang) } } else { // Normal char, go one level deeper. - tword[depth++] = c; + tword[depth++] = (char_u)c; arridx[depth] = idxs[n]; curi[depth] = 1; wordcount[depth] = 0; @@ -5170,25 +5164,25 @@ static int offset2bytes(int nr, char_u *buf) b4 = rem / 255 + 1; if (b4 > 1 || b3 > 0x1f) { // 4 bytes - buf[0] = 0xe0 + b4; - buf[1] = b3; - buf[2] = b2; - buf[3] = b1; + buf[0] = (char_u)(0xe0 + b4); + buf[1] = (char_u)b3; + buf[2] = (char_u)b2; + buf[3] = (char_u)b1; return 4; } if (b3 > 1 || b2 > 0x3f) { // 3 bytes - buf[0] = 0xc0 + b3; - buf[1] = b2; - buf[2] = b1; + buf[0] = (char_u)(0xc0 + b3); + buf[1] = (char_u)b2; + buf[2] = (char_u)b1; return 3; } if (b2 > 1 || b1 > 0x7f) { // 2 bytes - buf[0] = 0x80 + b2; - buf[1] = b1; + buf[0] = (char_u)(0x80 + b2); + buf[1] = (char_u)b1; return 2; } // 1 byte - buf[0] = b1; + buf[0] = (char_u)b1; return 1; } @@ -5276,11 +5270,11 @@ theend: /// @param ascii -ascii argument given /// @param over_write overwrite existing output file /// @param added_word invoked through "zg" -static void mkspell(int fcount, char_u **fnames, bool ascii, bool over_write, bool added_word) +static void mkspell(int fcount, char **fnames, bool ascii, bool over_write, bool added_word) { char_u *fname = NULL; char_u *wfname; - char_u **innames; + char **innames; int incount; afffile_T *(afile[MAXREGIONS]); int i; @@ -5373,9 +5367,8 @@ static void mkspell(int fcount, char_u **fnames, bool ascii, bool over_write, bo semsg(_("E755: Invalid region in %s"), innames[i]); goto theend; } - spin.si_region_name[i * 2] = TOLOWER_ASC(innames[i][len - 2]); - spin.si_region_name[i * 2 + 1] = - TOLOWER_ASC(innames[i][len - 1]); + spin.si_region_name[i * 2] = (char_u)TOLOWER_ASC(innames[i][len - 2]); + spin.si_region_name[i * 2 + 1] = (char_u)TOLOWER_ASC(innames[i][len - 1]); } } spin.si_region_count = incount; @@ -5418,7 +5411,7 @@ static void mkspell(int fcount, char_u **fnames, bool ascii, bool over_write, bo } else { // No .aff file, try reading the file as a word list. Store // the words in the trees. - if (spell_read_wordfile(&spin, innames[i]) == FAIL) { + if (spell_read_wordfile(&spin, (char_u *)innames[i]) == FAIL) { error = true; } } @@ -5529,7 +5522,7 @@ void spell_add_word(char_u *word, int len, SpellAddType what, int idx, bool undo FILE *fd = NULL; buf_T *buf = NULL; bool new_spf = false; - char_u *fname; + char *fname; char_u *fnamebuf = NULL; char_u line[MAXWLEN * 2]; long fpos, fpos_next = 0; @@ -5548,7 +5541,7 @@ void spell_add_word(char_u *word, int len, SpellAddType what, int idx, bool undo return; } } - fname = int_wordlist; + fname = (char *)int_wordlist; } else { // If 'spellfile' isn't set figure out a good default value. if (*curwin->w_s->b_p_spf == NUL) { @@ -5585,13 +5578,13 @@ void spell_add_word(char_u *word, int len, SpellAddType what, int idx, bool undo return; } - fname = fnamebuf; + fname = (char *)fnamebuf; } if (what == SPELL_ADD_BAD || undo) { // When the word appears as good word we need to remove that one, // since its flags sort before the one with WF_BANNED. - fd = os_fopen((char *)fname, "r"); + fd = os_fopen(fname, "r"); if (fd != NULL) { while (!vim_fgets(line, MAXWLEN * 2, fd)) { fpos = fpos_next; @@ -5605,14 +5598,14 @@ void spell_add_word(char_u *word, int len, SpellAddType what, int idx, bool undo // the start of the line. Mixing reading and writing // doesn't work for all systems, close the file first. fclose(fd); - fd = os_fopen((char *)fname, "r+"); + fd = os_fopen(fname, "r+"); if (fd == NULL) { break; } if (fseek(fd, fpos, SEEK_SET) == 0) { fputc('#', fd); if (undo) { - home_replace(NULL, (char *)fname, (char *)NameBuff, MAXPATHL, true); + home_replace(NULL, fname, (char *)NameBuff, MAXPATHL, true); smsg(_("Word '%.*s' removed from %s"), len, word, NameBuff); } } @@ -5629,7 +5622,7 @@ void spell_add_word(char_u *word, int len, SpellAddType what, int idx, bool undo } if (!undo) { - fd = os_fopen((char *)fname, "a"); + fd = os_fopen(fname, "a"); if (fd == NULL && new_spf) { char_u *p; @@ -5637,16 +5630,16 @@ void spell_add_word(char_u *word, int len, SpellAddType what, int idx, bool undo // file. We may need to create the "spell" directory first. We // already checked the runtime directory is writable in // init_spellfile(). - if (!dir_of_file_exists(fname) - && (p = (char_u *)path_tail_with_sep((char *)fname)) != fname) { + if (!dir_of_file_exists((char_u *)fname) + && (p = (char_u *)path_tail_with_sep(fname)) != (char_u *)fname) { int c = *p; // The directory doesn't exist. Try creating it and opening // the file again. *p = NUL; - os_mkdir((char *)fname, 0755); - *p = c; - fd = os_fopen((char *)fname, "a"); + os_mkdir(fname, 0755); + *p = (char_u)c; + fd = os_fopen(fname, "a"); } } @@ -5662,7 +5655,7 @@ void spell_add_word(char_u *word, int len, SpellAddType what, int idx, bool undo } fclose(fd); - home_replace(NULL, (char *)fname, (char *)NameBuff, MAXPATHL, true); + home_replace(NULL, fname, (char *)NameBuff, MAXPATHL, true); smsg(_("Word '%.*s' added to %s"), len, word, NameBuff); } } @@ -5727,19 +5720,19 @@ static void init_spellfile(void) } else { // Create the "spell" directory if it doesn't exist yet. l = (int)STRLEN(buf); - vim_snprintf((char *)buf + l, MAXPATHL - l, "/spell"); + vim_snprintf((char *)buf + l, MAXPATHL - (size_t)l, "/spell"); if (os_file_is_writable((char *)buf) != 2) { os_mkdir((char *)buf, 0755); } l = (int)STRLEN(buf); - vim_snprintf((char *)buf + l, MAXPATHL - l, + vim_snprintf((char *)buf + l, MAXPATHL - (size_t)l, "/%.*s", (int)(lend - lstart), lstart); } l = (int)STRLEN(buf); fname = LANGP_ENTRY(curwin->w_s->b_langp, 0) ->lp_slang->sl_fname; - vim_snprintf((char *)buf + l, MAXPATHL - l, ".%s.add", + vim_snprintf((char *)buf + l, MAXPATHL - (size_t)l, ".%s.add", ((fname != NULL && strstr(path_tail((char *)fname), ".ascii.") != NULL) ? "ascii" @@ -5776,9 +5769,9 @@ static void set_spell_charflags(char_u *flags, int cnt, char_u *fol) if (*p != NUL) { c = mb_ptr2char_adv((const char_u **)&p); - new_st.st_fold[i + 128] = c; + new_st.st_fold[i + 128] = (char_u)c; if (i + 128 != c && new_st.st_isu[i + 128] && c < 256) { - new_st.st_upper[c] = i + 128; + new_st.st_upper[c] = (char_u)(i + 128); } } } @@ -5878,11 +5871,10 @@ static void set_map_str(slang_T *lp, char_u *map) if (c >= 256) { int cl = utf_char2len(c); int headcl = utf_char2len(headc); - char *b; hash_T hash; hashitem_T *hi; - b = xmalloc(cl + headcl + 2); + char *b = xmalloc((size_t)(cl + headcl) + 2); utf_char2bytes(c, b); b[cl] = NUL; utf_char2bytes(headc, b + cl + 1); diff --git a/src/nvim/state.c b/src/nvim/state.c index 6475105192..d6cca71ad8 100644 --- a/src/nvim/state.c +++ b/src/nvim/state.c @@ -5,10 +5,10 @@ #include "nvim/ascii.h" #include "nvim/autocmd.h" -#include "nvim/edit.h" #include "nvim/eval.h" #include "nvim/ex_docmd.h" #include "nvim/getchar.h" +#include "nvim/insexpand.h" #include "nvim/lib/kvec.h" #include "nvim/log.h" #include "nvim/main.h" @@ -211,12 +211,15 @@ void get_mode(char *buf) buf[i++] = 'o'; // to be able to detect force-linewise/blockwise/charwise operations buf[i++] = (char)motion_force; + } else if (curbuf->terminal) { + buf[i++] = 't'; + if (restart_edit == 'I') { + buf[i++] = 'T'; + } } else if (restart_edit == 'I' || restart_edit == 'R' || restart_edit == 'V') { buf[i++] = 'i'; buf[i++] = (char)restart_edit; - } else if (curbuf->terminal) { - buf[i++] = 't'; } } diff --git a/src/nvim/strings.c b/src/nvim/strings.c index 5c2721536d..22effaade0 100644 --- a/src/nvim/strings.c +++ b/src/nvim/strings.c @@ -501,7 +501,7 @@ static int sort_compare(const void *s1, const void *s2) return STRCMP(*(char **)s1, *(char **)s2); } -void sort_strings(char_u **files, int count) +void sort_strings(char **files, int count) { qsort((void *)files, (size_t)count, sizeof(char_u *), sort_compare); } @@ -1528,3 +1528,45 @@ char_u *reverse_text(char_u *s) return rev; } + +/// Replace all occurrences of "what" with "rep" in "src". If no replacement happens then NULL is +/// returned otherwise return a newly allocated string. +/// +/// @param[in] src Source text +/// @param[in] what Substring to replace +/// @param[in] rep Substring to replace with +/// +/// @return [allocated] Copy of the string. +char *strrep(const char *src, const char *what, const char *rep) +{ + char *pos = (char *)src; + size_t whatlen = STRLEN(what); + + // Count occurrences + size_t count = 0; + while ((pos = strstr(pos, what)) != NULL) { + count++; + pos += whatlen; + } + + if (count == 0) { + return NULL; + } + + size_t replen = STRLEN(rep); + char *ret = xmalloc(STRLEN(src) + count * (replen - whatlen) + 1); + char *ptr = ret; + while ((pos = strstr(src, what)) != NULL) { + size_t idx = (size_t)(pos - src); + memcpy(ptr, src, idx); + ptr += idx; + STRCPY(ptr, rep); + ptr += replen; + src = pos + whatlen; + } + + // Copy remaining + STRCPY(ptr, src); + + return ret; +} diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index 43dbeccf01..4ec4a57d68 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -18,6 +18,7 @@ #include "nvim/charset.h" #include "nvim/cursor_shape.h" #include "nvim/eval.h" +#include "nvim/eval/vars.h" #include "nvim/ex_cmds2.h" #include "nvim/ex_docmd.h" #include "nvim/fileio.h" @@ -226,12 +227,10 @@ static int current_sub_char = 0; #define MAX_SYN_INC_TAG 999 // maximum before the above overflow #define MAX_CLUSTER_ID (32767 - SYNID_CLUSTER) -/* - * Annoying Hack(TM): ":syn include" needs this pointer to pass to - * expand_filename(). Most of the other syntax commands don't need it, so - * instead of passing it to them, we stow it here. - */ -static char_u **syn_cmdlinep; +// Annoying Hack(TM): ":syn include" needs this pointer to pass to +// expand_filename(). Most of the other syntax commands don't need it, so +// instead of passing it to them, we stow it here. +static char **syn_cmdlinep; /* * Another Annoying Hack(TM): To prevent rules from other ":syn include"'d @@ -343,7 +342,7 @@ void syntax_start(win_T *wp, linenr_T lnum) linenr_T parsed_lnum; linenr_T first_stored; int dist; - static int changedtick = 0; // remember the last change ID + static varnumber_T changedtick = 0; // remember the last change ID current_sub_char = NUL; @@ -993,11 +992,10 @@ void syn_stack_free_all(synblock_T *block) */ static void syn_stack_alloc(void) { - long len; synstate_T *to, *from; synstate_T *sstp; - len = syn_buf->b_ml.ml_line_count / SST_DIST + Rows * 2; + int len = syn_buf->b_ml.ml_line_count / SST_DIST + Rows * 2; if (len < SST_MIN_ENTRIES) { len = SST_MIN_ENTRIES; } else if (len > SST_MAX_ENTRIES) { @@ -1024,7 +1022,7 @@ static void syn_stack_alloc(void) } assert(len >= 0); - sstp = xcalloc(len, sizeof(synstate_T)); + sstp = xcalloc((size_t)len, sizeof(synstate_T)); to = sstp - 1; if (syn_block->b_sst_array != NULL) { @@ -1304,7 +1302,7 @@ static synstate_T *store_current_state(void) } for (i = 0; i < sp->sst_stacksize; ++i) { bp[i].bs_idx = CUR_STATE(i).si_idx; - bp[i].bs_flags = CUR_STATE(i).si_flags; + bp[i].bs_flags = (int)CUR_STATE(i).si_flags; bp[i].bs_seqnr = CUR_STATE(i).si_seqnr; bp[i].bs_cchar = CUR_STATE(i).si_cchar; bp[i].bs_extmatch = ref_extmatch(CUR_STATE(i).si_extmatch); @@ -1392,24 +1390,21 @@ static bool syn_stack_equal(synstate_T *sp) if (bp[i].bs_extmatch == CUR_STATE(i).si_extmatch) { continue; } - // When the extmatch pointers are different, the strings in - // them can still be the same. Check if the extmatch - // references are equal. + // When the extmatch pointers are different, the strings in them can + // still be the same. Check if the extmatch references are equal. bsx = bp[i].bs_extmatch; six = CUR_STATE(i).si_extmatch; - // If one of the extmatch pointers is NULL the states are - // different. + // If one of the extmatch pointers is NULL the states are different. if (bsx == NULL || six == NULL) { break; } int j; for (j = 0; j < NSUBEXP; j++) { - // Check each referenced match string. They must all be - // equal. + // Check each referenced match string. They must all be equal. if (bsx->matches[j] != six->matches[j]) { - // If the pointer is different it can still be the - // same text. Compare the strings, ignore case when - // the start item has the sp_ic flag set. + // If the pointer is different it can still be the same text. + // Compare the strings, ignore case when the start item has the + // sp_ic flag set. if (bsx->matches[j] == NULL || six->matches[j] == NULL) { break; } @@ -1424,11 +1419,7 @@ static bool syn_stack_equal(synstate_T *sp) break; } } - if (i < 0) { - return true; - } - - return false; + return i < 0 ? true : false; } /* @@ -2043,7 +2034,7 @@ static int syn_current_attr(const bool syncing, const bool displaying, bool *con current_attr = sip->si_attr; current_id = sip->si_id; current_trans_id = sip->si_trans_id; - current_flags = sip->si_flags; + current_flags = (int)sip->si_flags; current_seqnr = sip->si_seqnr; current_sub_char = sip->si_cchar; break; @@ -2065,7 +2056,7 @@ static int syn_current_attr(const bool syncing, const bool displaying, bool *con *can_spell = (syn_block->b_syn_spell != SYNSPL_NOTOP); } else { sps.inc_tag = 0; - sps.id = syn_block->b_nospell_cluster_id; + sps.id = (int16_t)syn_block->b_nospell_cluster_id; sps.cont_in_list = NULL; *can_spell = !in_id_list(sip, sip->si_cont_list, &sps, 0); } @@ -2078,12 +2069,12 @@ static int syn_current_attr(const bool syncing, const bool displaying, bool *con *can_spell = (syn_block->b_syn_spell == SYNSPL_TOP); } else { sps.inc_tag = 0; - sps.id = syn_block->b_spell_cluster_id; + sps.id = (int16_t)syn_block->b_spell_cluster_id; sps.cont_in_list = NULL; *can_spell = in_id_list(sip, sip->si_cont_list, &sps, 0); if (syn_block->b_nospell_cluster_id != 0) { - sps.id = syn_block->b_nospell_cluster_id; + sps.id = (int16_t)syn_block->b_nospell_cluster_id; if (in_id_list(sip, sip->si_cont_list, &sps, 0)) { *can_spell = false; } @@ -2289,7 +2280,7 @@ static void check_state_ends(void) // handle next_list, unless at end of line and no "skipnl" or // "skipempty" current_next_list = cur_si->si_next_list; - current_next_flags = cur_si->si_flags; + current_next_flags = (int)cur_si->si_flags; if (!(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY)) && syn_getcurline()[current_col] == NUL) { current_next_list = NULL; @@ -2899,7 +2890,6 @@ static char_u *syn_getcurline(void) */ static int syn_regexec(regmmatch_T *rmp, linenr_T lnum, colnr_T col, syn_time_T *st) { - int r; int timed_out = 0; proftime_T pt; const bool l_syn_time_on = syn_time_on; @@ -2915,9 +2905,8 @@ static int syn_regexec(regmmatch_T *rmp, linenr_T lnum, colnr_T col, syn_time_T return false; } - rmp->rmm_maxcol = syn_buf->b_p_smc; - r = vim_regexec_multi(rmp, syn_win, syn_buf, lnum, col, - syn_tm, &timed_out); + rmp->rmm_maxcol = (colnr_T)syn_buf->b_p_smc; + long r = vim_regexec_multi(rmp, syn_win, syn_buf, lnum, col, syn_tm, &timed_out); if (l_syn_time_on) { pt = profile_end(pt); @@ -3294,9 +3283,8 @@ static void syn_remove_pattern(synblock_T *block, int idx) --block->b_syn_folditems; } syn_clear_pattern(block, idx); - memmove(spp, spp + 1, - sizeof(synpat_T) * (block->b_syn_patterns.ga_len - idx - 1)); - --block->b_syn_patterns.ga_len; + memmove(spp, spp + 1, sizeof(synpat_T) * (size_t)(block->b_syn_patterns.ga_len - idx - 1)); + block->b_syn_patterns.ga_len--; } /* @@ -3382,7 +3370,7 @@ static void syn_cmd_clear(exarg_T *eap, int syncing) XFREE_CLEAR(SYN_CLSTR(curwin->w_s)[scl_id].scl_list); } } else { - id = syn_name2id_len((char *)arg, (int)(arg_end - arg)); + id = syn_name2id_len((char *)arg, (size_t)(arg_end - arg)); if (id == 0) { semsg(_(e_nogroup), arg); break; @@ -3553,7 +3541,7 @@ static void syn_cmd_list(exarg_T *eap, int syncing) syn_list_cluster(id - SYNID_CLUSTER); } } else { - int id = syn_name2id_len((char *)arg, (int)(arg_end - arg)); + int id = syn_name2id_len((char *)arg, (size_t)(arg_end - arg)); if (id == 0) { semsg(_(e_nogroup), arg); } else { @@ -4010,7 +3998,7 @@ static void add_keyword(char_u *const name, const int id, const int flags, keyentry_T *const kp = xmalloc(sizeof(keyentry_T) + STRLEN(name_ic)); STRCPY(kp->keyword, name_ic); - kp->k_syn.id = id; + kp->k_syn.id = (int16_t)id; kp->k_syn.inc_tag = current_syn_inc_tag; kp->flags = flags; kp->k_char = conceal_char; @@ -4193,7 +4181,7 @@ static char_u *get_syn_options(char_u *arg, syn_opt_arg_T *opt, int *conceal_cha if (gname_start == arg) { return NULL; } - gname = vim_strnsave(gname_start, arg - gname_start); + gname = vim_strnsave(gname_start, (size_t)(arg - gname_start)); if (STRCMP(gname, "NONE") == 0) { *opt->sync_idx = NONE_IDX; } else { @@ -4242,7 +4230,7 @@ static void syn_incl_toplevel(int id, int *flagsp) int16_t *grp_list = xmalloc(2 * sizeof(*grp_list)); int tlg_id = curwin->w_s->b_syn_topgrp - SYNID_CLUSTER; - grp_list[0] = id; + grp_list[0] = (int16_t)id; grp_list[1] = 0; syn_combine_list(&SYN_CLSTR(curwin->w_s)[tlg_id].scl_list, &grp_list, CLUSTER_ADD); @@ -4345,7 +4333,7 @@ static void syn_cmd_keyword(exarg_T *eap, int syncing) if (eap->skip) { syn_id = -1; } else { - syn_id = syn_check_group((char *)arg, (int)(group_name_end - arg)); + syn_id = syn_check_group((char *)arg, (size_t)(group_name_end - arg)); } if (syn_id != 0) { // Allocate a buffer, for removing backslashes in the keyword. @@ -4411,7 +4399,7 @@ static void syn_cmd_keyword(exarg_T *eap, int syncing) } const int l = utfc_ptr2len((char *)p + 1); - memmove(p, p + 1, l); + memmove(p, p + 1, (size_t)l); p += l; } } @@ -4482,7 +4470,7 @@ static void syn_cmd_match(exarg_T *eap, int syncing) if (!ends_excmd(*rest) || eap->skip) { rest = NULL; } else { - if ((syn_id = syn_check_group((char *)arg, (int)(group_name_end - arg))) != 0) { + if ((syn_id = syn_check_group((char *)arg, (size_t)(group_name_end - arg))) != 0) { syn_incl_toplevel(syn_id, &syn_opt_arg.flags); /* * Store the pattern in the syn_items list @@ -4492,7 +4480,7 @@ static void syn_cmd_match(exarg_T *eap, int syncing) *spp = item; spp->sp_syncing = syncing; spp->sp_type = SPTYPE_MATCH; - spp->sp_syn.id = syn_id; + spp->sp_syn.id = (int16_t)syn_id; spp->sp_syn.inc_tag = current_syn_inc_tag; spp->sp_flags = syn_opt_arg.flags; spp->sp_sync_idx = sync_idx; @@ -4598,7 +4586,7 @@ static void syn_cmd_region(exarg_T *eap, int syncing) ++key_end; } xfree(key); - key = vim_strnsave_up(rest, key_end - rest); + key = vim_strnsave_up(rest, (size_t)(key_end - rest)); if (STRCMP(key, "MATCHGROUP") == 0) { item = ITEM_MATCHGROUP; } else if (STRCMP(key, "START") == 0) { @@ -4631,7 +4619,7 @@ static void syn_cmd_region(exarg_T *eap, int syncing) if ((p - rest == 4 && STRNCMP(rest, "NONE", 4) == 0) || eap->skip) { matchgroup_id = 0; } else { - matchgroup_id = syn_check_group((char *)rest, (int)(p - rest)); + matchgroup_id = syn_check_group((char *)rest, (size_t)(p - rest)); if (matchgroup_id == 0) { illegal = true; break; @@ -4690,7 +4678,7 @@ static void syn_cmd_region(exarg_T *eap, int syncing) rest = NULL; } else { ga_grow(&(curwin->w_s->b_syn_patterns), pat_count); - if ((syn_id = syn_check_group((char *)arg, (int)(group_name_end - arg))) != 0) { + if ((syn_id = syn_check_group((char *)arg, (size_t)(group_name_end - arg))) != 0) { syn_incl_toplevel(syn_id, &syn_opt_arg.flags); /* * Store the start/skip/end in the syn_items list @@ -4704,11 +4692,10 @@ static void syn_cmd_region(exarg_T *eap, int syncing) (item == ITEM_START) ? SPTYPE_START : (item == ITEM_SKIP) ? SPTYPE_SKIP : SPTYPE_END; SYN_ITEMS(curwin->w_s)[idx].sp_flags |= syn_opt_arg.flags; - SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id; + SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = (int16_t)syn_id; SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag = current_syn_inc_tag; - SYN_ITEMS(curwin->w_s)[idx].sp_syn_match_id = - ppp->pp_matchgroup_id; + SYN_ITEMS(curwin->w_s)[idx].sp_syn_match_id = (int16_t)ppp->pp_matchgroup_id; SYN_ITEMS(curwin->w_s)[idx].sp_cchar = conceal_char; if (item == ITEM_START) { SYN_ITEMS(curwin->w_s)[idx].sp_cont_list = @@ -4878,7 +4865,7 @@ static void syn_combine_list(int16_t **const clstr1, int16_t **const clstr2, con clstr = NULL; break; } - clstr = xmalloc((count + 1) * sizeof(*clstr)); + clstr = xmalloc(((size_t)count + 1) * sizeof(*clstr)); clstr[count] = 0; } } @@ -4913,7 +4900,7 @@ static int syn_scl_name2id(char_u *name) */ static int syn_scl_namen2id(char_u *linep, int len) { - char_u *name = vim_strnsave(linep, len); + char_u *name = vim_strnsave(linep, (size_t)len); int id = syn_scl_name2id(name); xfree(name); @@ -4926,12 +4913,8 @@ static int syn_scl_namen2id(char_u *linep, int len) // Return 0 for failure. static int syn_check_cluster(char_u *pp, int len) { - int id; - char_u *name; - - name = vim_strnsave(pp, len); - - id = syn_scl_name2id(name); + char_u *name = vim_strnsave(pp, (size_t)len); + int id = syn_scl_name2id(name); if (id == 0) { // doesn't exist yet id = syn_add_cluster(name); } else { @@ -5081,7 +5064,7 @@ static char_u *get_syn_pattern(char_u *arg, synpat_T *ci) return NULL; } // store the pattern and compiled regexp program - ci->sp_pattern = vim_strnsave(arg + 1, end - arg - 1); + ci->sp_pattern = vim_strnsave(arg + 1, (size_t)(end - arg) - 1); // Make 'cpoptions' empty, to avoid the 'l' flag cpo_save = p_cpo; @@ -5120,7 +5103,7 @@ static char_u *get_syn_pattern(char_u *arg, synpat_T *ci) } } if (idx >= 0) { - ci->sp_off_flags |= (1 << idx); + ci->sp_off_flags |= (int16_t)(1 << idx); if (idx == SPO_LC_OFF) { // lc=99 end += 3; *p = getdigits_int((char **)&end, true, 0); @@ -5164,9 +5147,8 @@ static void syn_cmd_sync(exarg_T *eap, int syncing) char_u *arg_end; char_u *key = NULL; char_u *next_arg; - int illegal = FALSE; - int finished = FALSE; - long n; + int illegal = false; + int finished = false; char *cpo_save; if (ends_excmd(*arg_start)) { @@ -5178,7 +5160,7 @@ static void syn_cmd_sync(exarg_T *eap, int syncing) arg_end = skiptowhite(arg_start); next_arg = (char_u *)skipwhite((char *)arg_end); xfree(key); - key = vim_strnsave_up(arg_start, arg_end - arg_start); + key = vim_strnsave_up(arg_start, (size_t)(arg_end - arg_start)); if (STRCMP(key, "CCOMMENT") == 0) { if (!eap->skip) { curwin->w_s->b_syn_sync_flags |= SF_CCOMMENT; @@ -5186,11 +5168,12 @@ static void syn_cmd_sync(exarg_T *eap, int syncing) if (!ends_excmd(*next_arg)) { arg_end = skiptowhite(next_arg); if (!eap->skip) { - curwin->w_s->b_syn_sync_id = syn_check_group((char *)next_arg, (int)(arg_end - next_arg)); + curwin->w_s->b_syn_sync_id = + (int16_t)syn_check_group((char *)next_arg, (size_t)(arg_end - next_arg)); } next_arg = (char_u *)skipwhite((char *)arg_end); } else if (!eap->skip) { - curwin->w_s->b_syn_sync_id = syn_name2id("Comment"); + curwin->w_s->b_syn_sync_id = (int16_t)syn_name2id("Comment"); } } else if (STRNCMP(key, "LINES", 5) == 0 || STRNCMP(key, "MINLINES", 8) == 0 @@ -5207,7 +5190,7 @@ static void syn_cmd_sync(exarg_T *eap, int syncing) illegal = TRUE; break; } - n = getdigits_long(&arg_end, false, 0); + linenr_T n = getdigits_int32((char **)&arg_end, false, 0); if (!eap->skip) { if (key[4] == 'B') { curwin->w_s->b_syn_sync_linebreaks = n; @@ -5241,7 +5224,7 @@ static void syn_cmd_sync(exarg_T *eap, int syncing) if (!eap->skip) { // store the pattern and compiled regexp program curwin->w_s->b_syn_linecont_pat = - vim_strnsave(next_arg + 1, arg_end - next_arg - 1); + vim_strnsave(next_arg + 1, (size_t)(arg_end - next_arg) - 1); curwin->w_s->b_syn_linecont_ic = curwin->w_s->b_syn_ic; // Make 'cpoptions' empty, to avoid the 'l' flag @@ -5326,7 +5309,7 @@ static int get_id_list(char_u **const arg, const int keylen, int16_t **const lis int count = 0; do { for (end = p; *end && !ascii_iswhite(*end) && *end != ','; end++) {} - char *const name = xmalloc(end - p + 3); // leave room for "^$" + char *const name = xmalloc((size_t)(end - p) + 3); // leave room for "^$" STRLCPY(name + 1, p, end - p + 1); if (STRCMP(name + 1, "ALLBUT") == 0 || STRCMP(name + 1, "ALL") == 0 @@ -5367,7 +5350,7 @@ static int get_id_list(char_u **const arg, const int keylen, int16_t **const lis * Handle full group name. */ if (strpbrk(name + 1, "\\.*^$~[") == NULL) { - id = syn_check_group((name + 1), (int)(end - p)); + id = syn_check_group((name + 1), (size_t)(end - p)); } else { // Handle match of regexp with group names. *name = '^'; @@ -5392,7 +5375,7 @@ static int get_id_list(char_u **const arg, const int keylen, int16_t **const lis xfree(retval); round = 1; } else { - retval[count] = i + 1; // -V522 + retval[count] = (int16_t)(i + 1); // -V522 } } count++; @@ -5415,7 +5398,7 @@ static int get_id_list(char_u **const arg, const int keylen, int16_t **const lis xfree(retval); round = 1; } else { - retval[count] = id; + retval[count] = (int16_t)id; } } ++count; @@ -5430,7 +5413,7 @@ static int get_id_list(char_u **const arg, const int keylen, int16_t **const lis break; } if (round == 1) { - retval = xmalloc((count + 1) * sizeof(*retval)); + retval = xmalloc(((size_t)count + 1) * sizeof(*retval)); retval[count] = 0; // zero means end of the list total_count = count; } @@ -5461,7 +5444,7 @@ static int16_t *copy_id_list(const int16_t *const list) int count; for (count = 0; list[count]; count++) {} - const size_t len = (count + 1) * sizeof(int16_t); + const size_t len = ((size_t)count + 1) * sizeof(int16_t); int16_t *const retval = xmalloc(len); memmove(retval, list, len); @@ -5610,11 +5593,11 @@ void ex_syntax(exarg_T *eap) char_u *arg = (char_u *)eap->arg; char_u *subcmd_end; - syn_cmdlinep = (char_u **)eap->cmdlinep; + syn_cmdlinep = eap->cmdlinep; // isolate subcommand name for (subcmd_end = arg; ASCII_ISALPHA(*subcmd_end); subcmd_end++) {} - char_u *const subcmd_name = vim_strnsave(arg, subcmd_end - arg); + char_u *const subcmd_name = vim_strnsave(arg, (size_t)(subcmd_end - arg)); if (eap->skip) { // skip error messages for all subcommands emsg_skip++; } @@ -5785,7 +5768,7 @@ char *get_syntax_name(expand_T *xp, int idx) /// @param trans remove transparency /// @param spellp return: can do spell checking /// @param keep_state keep state of char at "col" -int syn_get_id(win_T *wp, long lnum, colnr_T col, int trans, bool *spellp, int keep_state) +int syn_get_id(win_T *wp, linenr_T lnum, colnr_T col, int trans, bool *spellp, int keep_state) { // When the position is not after the current position and in the same // line of the same window with the same buffer, need to restart parsing. @@ -5867,10 +5850,8 @@ static int syn_cur_foldlevel(void) return level; } -/* - * Function called to get folding level for line "lnum" in window "wp". - */ -int syn_get_foldlevel(win_T *wp, long lnum) +/// Function called to get folding level for line "lnum" in window "wp". +int syn_get_foldlevel(win_T *wp, linenr_T lnum) { int level = 0; @@ -5900,7 +5881,7 @@ int syn_get_foldlevel(win_T *wp, long lnum) } } if (level > wp->w_p_fdn) { - level = wp->w_p_fdn; + level = (int)wp->w_p_fdn; if (level < 0) { level = 0; } @@ -6000,11 +5981,11 @@ static void syntime_report(void) p = GA_APPEND_VIA_PTR(time_entry_T, &ga); p->total = spp->sp_time.total; total_total = profile_add(total_total, spp->sp_time.total); - p->count = spp->sp_time.count; - p->match = spp->sp_time.match; - total_count += spp->sp_time.count; + p->count = (int)spp->sp_time.count; + p->match = (int)spp->sp_time.match; + total_count += (int)spp->sp_time.count; p->slowest = spp->sp_time.slowest; - proftime_T tm = profile_divide(spp->sp_time.total, spp->sp_time.count); + proftime_T tm = profile_divide(spp->sp_time.total, (int)spp->sp_time.count); p->average = tm; p->id = spp->sp_syn.id; p->pattern = spp->sp_pattern; diff --git a/src/nvim/tag.c b/src/nvim/tag.c index 28b3b6c1ef..5b799be381 100644 --- a/src/nvim/tag.c +++ b/src/nvim/tag.c @@ -26,6 +26,7 @@ #include "nvim/garray.h" #include "nvim/if_cscope.h" #include "nvim/input.h" +#include "nvim/insexpand.h" #include "nvim/mark.h" #include "nvim/mbyte.h" #include "nvim/memory.h" @@ -164,7 +165,7 @@ bool do_tag(char_u *tag, int type, int count, int forceit, int verbose) fmark_T saved_fmark; bool jumped_to_tag = false; int new_num_matches; - char_u **new_matches; + char **new_matches; int use_tagstack; int skip_msg = false; char_u *buf_ffname = (char_u *)curbuf->b_ffname; // name for priority computation @@ -173,7 +174,7 @@ bool do_tag(char_u *tag, int type, int count, int forceit, int verbose) // remember the matches for the last used tag static int num_matches = 0; static int max_num_matches = 0; // limit used for match search - static char_u **matches = NULL; + static char **matches = NULL; static int flags; if (tfu_in_use) { @@ -497,15 +498,15 @@ bool do_tag(char_u *tag, int type, int count, int forceit, int verbose) // Find the position of each old match in the new list. Need // to use parse_match() to find the tag line. for (j = 0; j < num_matches; j++) { - parse_match(matches[j], &tagp); - for (i = idx; i < new_num_matches; ++i) { - parse_match(new_matches[i], &tagp2); + parse_match((char_u *)matches[j], &tagp); + for (i = idx; i < new_num_matches; i++) { + parse_match((char_u *)new_matches[i], &tagp2); if (STRCMP(tagp.tagname, tagp2.tagname) == 0) { - char_u *p = new_matches[i]; + char_u *p = (char_u *)new_matches[i]; for (k = i; k > idx; k--) { new_matches[k] = new_matches[k - 1]; } - new_matches[idx++] = p; + new_matches[idx++] = (char *)p; break; } } @@ -580,7 +581,7 @@ bool do_tag(char_u *tag, int type, int count, int forceit, int verbose) tagstack[tagstackidx].cur_fnum = cur_fnum; // store user-provided data originating from tagfunc - if (use_tfu && parse_match(matches[cur_match], &tagp2) == OK + if (use_tfu && parse_match((char_u *)matches[cur_match], &tagp2) == OK && tagp2.user_data) { XFREE_CLEAR(tagstack[tagstackidx].user_data); tagstack[tagstackidx].user_data = vim_strnsave(tagp2.user_data, @@ -639,7 +640,7 @@ bool do_tag(char_u *tag, int type, int count, int forceit, int verbose) /* * Jump to the desired match. */ - i = jumpto_tag(matches[cur_match], forceit, type != DT_CSCOPE); + i = jumpto_tag((char_u *)matches[cur_match], forceit, type != DT_CSCOPE); set_vim_var_string(VV_SWAPCOMMAND, NULL, -1); @@ -686,10 +687,8 @@ end_do_tag: return jumped_to_tag; } -// // List all the matching tags. -// -static void print_tag_list(int new_tag, int use_tagstack, int num_matches, char_u **matches) +static void print_tag_list(int new_tag, int use_tagstack, int num_matches, char **matches) { taggy_T *tagstack = curwin->w_tagstack; int tagstackidx = curwin->w_tagstackidx; @@ -702,7 +701,7 @@ static void print_tag_list(int new_tag, int use_tagstack, int num_matches, char_ // Assume that the first match indicates how long the tags can // be, and align the file names to that. - parse_match(matches[0], &tagp); + parse_match((char_u *)matches[0], &tagp); taglen = (int)(tagp.tagname_end - tagp.tagname + 2); if (taglen < 18) { taglen = 18; @@ -720,7 +719,7 @@ static void print_tag_list(int new_tag, int use_tagstack, int num_matches, char_ msg_puts_attr(_("file\n"), HL_ATTR(HLF_T)); for (i = 0; i < num_matches && !got_int; i++) { - parse_match(matches[i], &tagp); + parse_match((char_u *)matches[i], &tagp); if (!new_tag && ( (g_do_tagpreview != 0 && i == ptag_entry.cur_match) @@ -873,11 +872,9 @@ static void print_tag_list(int new_tag, int use_tagstack, int num_matches, char_ } } -// -// Add the matching tags to the location list for the current -// window. -// -static int add_llist_tags(char_u *tag, int num_matches, char_u **matches) +/// Add the matching tags to the location list for the current +/// window. +static int add_llist_tags(char_u *tag, int num_matches, char **matches) { list_T *list; char_u tag_name[128 + 1]; @@ -896,7 +893,7 @@ static int add_llist_tags(char_u *tag, int num_matches, char_u **matches) long lnum; dict_T *dict; - parse_match(matches[i], &tagp); + parse_match((char_u *)matches[i], &tagp); // Save the tag name len = (int)(tagp.tagname_end - tagp.tagname); @@ -1365,7 +1362,7 @@ static int find_tagfunc_tags(char_u *pat, garray_T *ga, int *match_count, int fl /// @param matchesp return: array of matches found /// @param mincount MAXCOL: find all matches other: minimal number of matches */ /// @param buf_ffname name of buffer for priority -int find_tags(char_u *pat, int *num_matches, char_u ***matchesp, int flags, int mincount, +int find_tags(char_u *pat, int *num_matches, char ***matchesp, int flags, int mincount, char_u *buf_ffname) { FILE *fp; @@ -1419,7 +1416,7 @@ int find_tags(char_u *pat, int *num_matches, char_u ***matchesp, int flags, int hashtab_T ht_match[MT_COUNT]; // stores matches by key hash_T hash = 0; int match_count = 0; // number of matches found - char_u **matches; + char **matches; int mtt; int help_save; int help_pri = 0; @@ -1648,7 +1645,7 @@ int find_tags(char_u *pat, int *num_matches, char_u ***matchesp, int flags, int if ((flags & TAG_INS_COMP)) { // Double brackets for gcc ins_compl_check_keys(30, false); } - if (got_int || compl_interrupted) { + if (got_int || ins_compl_interrupted()) { stop_searching = true; break; } @@ -2283,7 +2280,7 @@ findtag_end: } } } - matches[match_count++] = mfp; + matches[match_count++] = (char *)mfp; } } @@ -3093,7 +3090,7 @@ static void tagstack_clear_entry(taggy_T *item) } /// @param tagnames expand tag names -int expand_tags(int tagnames, char_u *pat, int *num_file, char_u ***file) +int expand_tags(int tagnames, char_u *pat, int *num_file, char ***file) { int i; int extra_flag; @@ -3124,7 +3121,7 @@ int expand_tags(int tagnames, char_u *pat, int *num_file, char_u ***file) for (i = 0; i < *num_file; i++) { size_t len; - parse_match((*file)[i], &t_p); + parse_match((char_u *)(*file)[i], &t_p); len = (size_t)(t_p.tagname_end - t_p.tagname); if (len > name_buf_size - 3) { char_u *buf; @@ -3195,7 +3192,7 @@ static int add_tag_field(dict_T *dict, const char *field_name, const char_u *sta int get_tags(list_T *list, char_u *pat, char_u *buf_fname) { int num_matches, i, ret; - char_u **matches; + char **matches; char_u *full_fname; dict_T *dict; tagptrs_T tp; @@ -3204,8 +3201,8 @@ int get_tags(list_T *list, char_u *pat, char_u *buf_fname) ret = find_tags(pat, &num_matches, &matches, TAG_REGEXP | TAG_NOIC, MAXCOL, buf_fname); if (ret == OK && num_matches > 0) { - for (i = 0; i < num_matches; ++i) { - int parse_result = parse_match(matches[i], &tp); + for (i = 0; i < num_matches; i++) { + int parse_result = parse_match((char_u *)matches[i], &tp); // Avoid an unused variable warning in release builds. (void)parse_result; diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c index be49048aec..c23aff00cb 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -82,6 +82,7 @@ typedef struct terminal_state { int save_rd; // saved value of RedrawingDisabled bool close; bool got_bsl; // if the last input was <C-\> + bool got_bsl_o; // if left terminal mode with <c-\><c-o> } TerminalState; #ifdef INCLUDE_GENERATED_DECLARATIONS @@ -388,12 +389,11 @@ void terminal_check_size(Terminal *term) } /// Implements MODE_TERMINAL state. :help Terminal-mode -void terminal_enter(void) +bool terminal_enter(void) { buf_T *buf = curbuf; assert(buf->terminal); // Should only be called when curbuf has a terminal. - TerminalState state, *s = &state; - memset(s, 0, sizeof(TerminalState)); + TerminalState s[1] = { 0 }; s->term = buf->terminal; stop_insert_mode = false; @@ -443,7 +443,9 @@ void terminal_enter(void) s->state.check = terminal_check; state_enter(&s->state); - restart_edit = 0; + if (!s->got_bsl_o) { + restart_edit = 0; + } State = save_state; RedrawingDisabled = s->save_rd; apply_autocmds(EVENT_TERMLEAVE, NULL, NULL, false, curbuf); @@ -451,7 +453,7 @@ void terminal_enter(void) if (save_curwin == curwin->handle) { // Else: window was closed. curwin->w_p_cul = save_w_p_cul; if (save_w_p_culopt) { - xfree(curwin->w_p_culopt); + free_string_option(curwin->w_p_culopt); curwin->w_p_culopt = save_w_p_culopt; } curwin->w_p_culopt_flags = save_w_p_culopt_flags; @@ -459,7 +461,7 @@ void terminal_enter(void) curwin->w_p_so = save_w_p_so; curwin->w_p_siso = save_w_p_siso; } else if (save_w_p_culopt) { - xfree(save_w_p_culopt); + free_string_option(save_w_p_culopt); } // draw the unfocused cursor @@ -467,7 +469,11 @@ void terminal_enter(void) if (curbuf->terminal == s->term && !s->close) { terminal_check_cursor(); } - unshowmode(true); + if (restart_edit) { + showmode(); + } else { + unshowmode(true); + } ui_busy_stop(); if (s->close) { bool wipe = s->term->buf_handle != 0; @@ -477,6 +483,8 @@ void terminal_enter(void) do_cmdline_cmd("bwipeout!"); } } + + return s->got_bsl_o; } static void terminal_check_cursor(void) @@ -564,6 +572,14 @@ static int terminal_execute(VimState *state, int key) } FALLTHROUGH; + case Ctrl_O: + if (s->got_bsl) { + s->got_bsl_o = true; + restart_edit = 'I'; + return 0; + } + FALLTHROUGH; + default: if (key == Ctrl_BSL && !s->got_bsl) { s->got_bsl = true; @@ -1384,7 +1400,7 @@ static void fetch_row(Terminal *term, int row, int end_col) fetch_cell(term, row, col, &cell); if (cell.chars[0]) { int cell_len = 0; - for (int i = 0; cell.chars[i]; i++) { + for (int i = 0; i < VTERM_MAX_CHARS_PER_CELL && cell.chars[i]; i++) { cell_len += utf_char2bytes((int)cell.chars[i], ptr + cell_len); } ptr += cell_len; diff --git a/src/nvim/testdir/README.txt b/src/nvim/testdir/README.txt new file mode 100644 index 0000000000..b8bc52f1e6 --- /dev/null +++ b/src/nvim/testdir/README.txt @@ -0,0 +1,121 @@ +This directory contains tests for various Vim features. +For testing an indent script see runtime/indent/testdir/README.txt. + +If it makes sense, add a new test method to an already existing file. You may +want to separate it from other tests with comment lines. + +TO ADD A NEW STYLE TEST: + +1) Create a test_<subject>.vim file. +2) Add test_<subject>.res to NEW_TESTS_RES in Make_all.mak in alphabetical + order. +3) Also add an entry "test_<subject>" to NEW_TESTS in Make_all.mak. +4) Use make test_<subject> to run a single test. + +At 2), instead of running the test separately, it can be included in +"test_alot". Do this for quick tests without side effects. The test runs a +bit faster, because Vim doesn't have to be started, one Vim instance runs many +tests. + +At 4), to run a test in GUI, add "GUI_FLAG=-g" to the make command. + + +What you can use (see test_assert.vim for an example): + +- Call assert_equal(), assert_true(), assert_false(), etc. + +- Use assert_fails() to check for expected errors. + +- Use try/catch to avoid an exception aborts the test. + +- Use test_alloc_fail() to have memory allocation fail. This makes it possible + to check memory allocation failures are handled gracefully. You need to + change the source code to add an ID to the allocation. Add a new one to + alloc_id_T, before aid_last. + +- Use test_override() to make Vim behave differently, e.g. if char_avail() + must return FALSE for a while. E.g. to trigger the CursorMovedI autocommand + event. See test_cursor_func.vim for an example. + +- If the bug that is being tested isn't fixed yet, you can throw an exception + with "Skipped" so that it's clear this still needs work. E.g.: throw + "Skipped: Bug with <c-e> and popupmenu not fixed yet" + +- The following environment variables are recognized and can be set to + influence the behavior of the test suite (see runtest.vim for details) + + - $TEST_MAY_FAIL=Test_channel_one - ignore those failing tests + - $TEST_FILTER=Test_channel - only run test that match this pattern + - $TEST_SKIP_PAT=Test_channel - skip tests that match this pattern + - $TEST_NO_RETRY=yes - do not try to re-run failing tests + You can also set them in Vim: + :let $TEST_MAY_FAIL = 'Test_channel_one' + :let $TEST_FILTER = '_set_mode' + :let $TEST_SKIP_PAT = 'Test_loop_forever' + :let $TEST_NO_RETRY = 'yes' + Use an empty string to revert, e.g.: + :let $TEST_FILTER = '' + +- See the start of runtest.vim for more help. + + +TO ADD A SCREEN DUMP TEST: + +Mostly the same as writing a new style test. Additionally, see help on +"terminal-dumptest". Put the reference dump in "dumps/Test_func_name.dump". + + +OLD STYLE TESTS: + +There are a few tests that are used when Vim was built without the +eval +feature. These cannot use the "assert" functions, therefore they consist of a +.in file that contains Normal mode commands between STARTTEST and ENDTEST. +They modify the file and the result gets written in the test.out file. This +is then compared with the .ok file. If they are equal the test passed. If +they differ the test failed. + + +RUNNING THE TESTS: + +To run a single test from the src directory: + + $ make test_<name> + +The below commands should be run from the src/testdir directory. + +To run a single test: + + $ make test_<name>.res + +The file 'messages' contains the messages generated by the test script. If a +test fails, then the test.log file contains the error messages. If all the +tests are successful, then this file will be an empty file. + +- To run a single test function from a test script: + + $ ../vim -u NONE -S runtest.vim <test_file>.vim <function_name> + +- To execute only specific test functions, add a second argument: + + $ ../vim -u NONE -S runtest.vim test_channel.vim open_delay + + +- To run all the tests: + + $ make + +- To run the test on MS-Windows using the MSVC nmake: + + > nmake -f Make_dos.mak + +- To run the tests with GUI Vim: + + $ make GUI_FLAG=-g + + or + + $ make VIMPROG=../gvim + +- To cleanup the temporary files after running the tests: + + $ make clean diff --git a/src/nvim/testdir/setup.vim b/src/nvim/testdir/setup.vim index e6c0762729..9d6fc5e526 100644 --- a/src/nvim/testdir/setup.vim +++ b/src/nvim/testdir/setup.vim @@ -4,6 +4,7 @@ if exists('s:did_load') set complete=.,w,b,u,t,i set directory& set directory^=. + set display= set fillchars=vert:\|,fold:- set formatoptions=tcq set fsync diff --git a/src/nvim/testdir/term_util.vim b/src/nvim/testdir/term_util.vim index 3a838a3a1f..5ff09e25b4 100644 --- a/src/nvim/testdir/term_util.vim +++ b/src/nvim/testdir/term_util.vim @@ -9,3 +9,5 @@ func CanRunVimInTerminal() " Nvim: always false, we use Lua screen-tests instead. return 0 endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_alot.vim b/src/nvim/testdir/test_alot.vim index 43a519bc84..966b8ef571 100644 --- a/src/nvim/testdir/test_alot.vim +++ b/src/nvim/testdir/test_alot.vim @@ -10,7 +10,6 @@ source test_ex_z.vim source test_ex_mode.vim source test_expand.vim source test_expand_func.vim -source test_feedkeys.vim source test_file_perm.vim source test_fnamemodify.vim source test_ga.vim diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim index 438851a0ad..1c2f86a584 100644 --- a/src/nvim/testdir/test_autocmd.vim +++ b/src/nvim/testdir/test_autocmd.vim @@ -2170,6 +2170,57 @@ func Test_autocmd_nested() call assert_fails('au WinNew * nested nested echo bad', 'E983:') endfunc +func Test_autocmd_nested_cursor_invalid() + set laststatus=0 + copen + cclose + call setline(1, ['foo', 'bar', 'baz']) + 3 + augroup nested_inv + autocmd User foo ++nested copen + autocmd BufAdd * let &laststatus = 2 - &laststatus + augroup END + doautocmd User foo + + augroup nested_inv + au! + augroup END + set laststatus& + cclose + bwipe! +endfunc + +func Test_autocmd_nested_keeps_cursor_pos() + enew + call setline(1, 'foo') + autocmd User foo ++nested normal! $a + autocmd InsertLeave * : + doautocmd User foo + call assert_equal([0, 1, 3, 0], getpos('.')) + + bwipe! +endfunc + +func Test_autocmd_nested_switch_window() + " run this in a separate Vim so that SafeState works + CheckRunVimInTerminal + + let lines =<< trim END + vim9script + ['()']->writefile('Xautofile') + autocmd VimEnter * ++nested edit Xautofile | split + autocmd BufReadPost * autocmd SafeState * ++once foldclosed('.') + autocmd WinEnter * matchadd('ErrorMsg', 'pat') + END + call writefile(lines, 'Xautoscript') + let buf = RunVimInTerminal('-S Xautoscript', {'rows': 10}) + call VerifyScreenDump(buf, 'Test_autocmd_nested_switch', {}) + + call StopVimInTerminal(buf) + call delete('Xautofile') + call delete('Xautoscript') +endfunc + func Test_autocmd_once() " Without ++once WinNew triggers twice let g:did_split = 0 @@ -2851,6 +2902,110 @@ func Test_v_event_readonly() au! TextYankPost endfunc +" Test for ModeChanged pattern +func Test_mode_changes() + let g:index = 0 + let g:mode_seq = ['n', 'i', 'n', 'v', 'V', 'i', 'ix', 'i', 'ic', 'i', 'n', 'no', 'n', 'V', 'v', 's', 'n'] + func! TestMode() + call assert_equal(g:mode_seq[g:index], get(v:event, "old_mode")) + call assert_equal(g:mode_seq[g:index + 1], get(v:event, "new_mode")) + call assert_equal(mode(1), get(v:event, "new_mode")) + let g:index += 1 + endfunc + + au ModeChanged * :call TestMode() + let g:n_to_any = 0 + au ModeChanged n:* let g:n_to_any += 1 + call feedkeys("i\<esc>vVca\<CR>\<C-X>\<C-L>\<esc>ggdG", 'tnix') + + let g:V_to_v = 0 + au ModeChanged V:v let g:V_to_v += 1 + call feedkeys("Vv\<C-G>\<esc>", 'tnix') + call assert_equal(len(filter(g:mode_seq[1:], {idx, val -> val == 'n'})), g:n_to_any) + call assert_equal(1, g:V_to_v) + call assert_equal(len(g:mode_seq) - 1, g:index) + + let g:n_to_i = 0 + au ModeChanged n:i let g:n_to_i += 1 + let g:n_to_niI = 0 + au ModeChanged i:niI let g:n_to_niI += 1 + let g:niI_to_i = 0 + au ModeChanged niI:i let g:niI_to_i += 1 + let g:nany_to_i = 0 + au ModeChanged n*:i let g:nany_to_i += 1 + let g:i_to_n = 0 + au ModeChanged i:n let g:i_to_n += 1 + let g:nori_to_any = 0 + au ModeChanged [ni]:* let g:nori_to_any += 1 + let g:i_to_any = 0 + au ModeChanged i:* let g:i_to_any += 1 + let g:index = 0 + let g:mode_seq = ['n', 'i', 'niI', 'i', 'n'] + call feedkeys("a\<C-O>l\<esc>", 'tnix') + call assert_equal(len(g:mode_seq) - 1, g:index) + call assert_equal(1, g:n_to_i) + call assert_equal(1, g:n_to_niI) + call assert_equal(1, g:niI_to_i) + call assert_equal(2, g:nany_to_i) + call assert_equal(1, g:i_to_n) + call assert_equal(2, g:i_to_any) + call assert_equal(3, g:nori_to_any) + + if has('terminal') + let g:mode_seq += ['c', 'n', 't', 'nt', 'c', 'nt', 'n'] + call feedkeys(":term\<CR>\<C-W>N:bd!\<CR>", 'tnix') + call assert_equal(len(g:mode_seq) - 1, g:index) + call assert_equal(1, g:n_to_i) + call assert_equal(1, g:n_to_niI) + call assert_equal(1, g:niI_to_i) + call assert_equal(2, g:nany_to_i) + call assert_equal(1, g:i_to_n) + call assert_equal(2, g:i_to_any) + call assert_equal(5, g:nori_to_any) + endif + + if has('cmdwin') + let g:n_to_c = 0 + au ModeChanged n:c let g:n_to_c += 1 + let g:c_to_n = 0 + au ModeChanged c:n let g:c_to_n += 1 + let g:mode_seq += ['c', 'n', 'c', 'n'] + call feedkeys("q:\<C-C>\<Esc>", 'tnix') + call assert_equal(len(g:mode_seq) - 1, g:index) + call assert_equal(2, g:n_to_c) + call assert_equal(2, g:c_to_n) + unlet g:n_to_c + unlet g:c_to_n + endif + + au! ModeChanged + delfunc TestMode + unlet! g:mode_seq + unlet! g:index + unlet! g:n_to_any + unlet! g:V_to_v + unlet! g:n_to_i + unlet! g:n_to_niI + unlet! g:niI_to_i + unlet! g:nany_to_i + unlet! g:i_to_n + unlet! g:nori_to_any + unlet! g:i_to_any +endfunc + +func Test_recursive_ModeChanged() + au! ModeChanged * norm 0u + sil! norm + au! ModeChanged +endfunc + +func Test_ModeChanged_starts_visual() + " This was triggering ModeChanged before setting VIsual, causing a crash. + au! ModeChanged * norm 0u + sil! norm + + au! ModeChanged +endfunc func Test_noname_autocmd() augroup test_noname_autocmd_group diff --git a/src/nvim/testdir/test_changelist.vim b/src/nvim/testdir/test_changelist.vim index 3741f32e69..3bb22a89b8 100644 --- a/src/nvim/testdir/test_changelist.vim +++ b/src/nvim/testdir/test_changelist.vim @@ -1,12 +1,65 @@ " Tests for the changelist functionality +" When splitting a window the changelist position is wrong. +" Test the changelist position after splitting a window. +" Test for the bug fixed by 7.4.386 +func Test_changelist() + let save_ul = &ul + enew! + call append('$', ['1', '2']) + exe "normal i\<C-G>u" + exe "normal Gkylpa\<C-G>u" + set ul=100 + exe "normal Gylpa\<C-G>u" + set ul=100 + normal gg + vsplit + normal g; + call assert_equal([3, 2], [line('.'), col('.')]) + normal g; + call assert_equal([2, 2], [line('.'), col('.')]) + call assert_fails('normal g;', 'E662:') + new + call assert_fails('normal g;', 'E664:') + %bwipe! + let &ul = save_ul +endfunc + +" Moving a split should not change its changelist index. +func Test_changelist_index_move_split() + exe "norm! iabc\<C-G>u\ndef\<C-G>u\nghi" + vsplit + normal 99g; + call assert_equal(0, getchangelist('%')[1]) + wincmd L + call assert_equal(0, getchangelist('%')[1]) +endfunc + " Tests for the getchangelist() function -func Test_getchangelist() - if !has("jumplist") - return - endif +func Test_changelist_index() + edit Xfile1.txt + exe "normal iabc\<C-G>u\ndef\<C-G>u\nghi" + call assert_equal(3, getchangelist('%')[1]) + " Move one step back in the changelist. + normal 2g; + + hide edit Xfile2.txt + exe "normal iabcd\<C-G>u\ndefg\<C-G>u\nghij" + call assert_equal(3, getchangelist('%')[1]) + " Move to the beginning of the changelist. + normal 99g; + + " Check the changelist indices. + call assert_equal(0, getchangelist('%')[1]) + call assert_equal(1, getchangelist('#')[1]) bwipe! + call delete('Xfile1.txt') + call delete('Xfile2.txt') +endfunc + +func Test_getchangelist() + bwipe! enew call assert_equal([], 10->getchangelist()) call assert_equal([[], 0], getchangelist()) @@ -15,6 +68,7 @@ func Test_getchangelist() call writefile(['line1', 'line2', 'line3'], 'Xfile2.txt') edit Xfile1.txt + let buf_1 = bufnr() exe "normal 1Goline\<C-G>u1.1" exe "normal 3Goline\<C-G>u2.1" exe "normal 5Goline\<C-G>u3.1" @@ -26,6 +80,7 @@ func Test_getchangelist() \ getchangelist('%')) hide edit Xfile2.txt + let buf_2 = bufnr() exe "normal 1GOline\<C-G>u1.0" exe "normal 2Goline\<C-G>u2.0" call assert_equal([[ @@ -37,10 +92,12 @@ func Test_getchangelist() call assert_equal([[ \ {'lnum' : 2, 'col' : 4, 'coladd' : 0}, \ {'lnum' : 4, 'col' : 4, 'coladd' : 0}, - \ {'lnum' : 6, 'col' : 4, 'coladd' : 0}], 3], getchangelist(2)) + \ {'lnum' : 6, 'col' : 4, 'coladd' : 0}], 2], + \ getchangelist(buf_1)) call assert_equal([[ \ {'lnum' : 1, 'col' : 6, 'coladd' : 0}, - \ {'lnum' : 3, 'col' : 6, 'coladd' : 0}], 2], getchangelist(3)) + \ {'lnum' : 3, 'col' : 6, 'coladd' : 0}], 2], + \ getchangelist(buf_2)) bwipe! call delete('Xfile1.txt') diff --git a/src/nvim/testdir/test_clientserver.vim b/src/nvim/testdir/test_clientserver.vim index db62fe5fa6..edf36b413b 100644 --- a/src/nvim/testdir/test_clientserver.vim +++ b/src/nvim/testdir/test_clientserver.vim @@ -1,16 +1,12 @@ " Tests for the +clientserver feature. -if !has('job') || !has('clientserver') - throw 'Skipped: job and/or clientserver feature missing' -endif +source check.vim +CheckFeature job +CheckFeature clientserver source shared.vim -func Test_client_server() - let cmd = GetVimCommand() - if cmd == '' - return - endif +func Check_X11_Connection() if has('x11') if empty($DISPLAY) throw 'Skipped: $DISPLAY is not set' @@ -19,11 +15,19 @@ func Test_client_server() call remote_send('xxx', '') catch if v:exception =~ 'E240:' - throw 'Skipped: no connection to the X server' + throw 'Skipped: no connection to the X server' endif " ignore other errors endtry endif +endfunc + +func Test_client_server() + let cmd = GetVimCommand() + if cmd == '' + return + endif + call Check_X11_Connection() let name = 'XVIMTEST' let cmd .= ' --servername ' . name @@ -34,6 +38,14 @@ func Test_client_server() " When using valgrind it takes much longer. call WaitForAssert({-> assert_match(name, serverlist())}) + if !has('win32') + if RunVim([], [], '--serverlist >Xtest_serverlist') + let lines = readfile('Xtest_serverlist') + call assert_true(index(lines, 'XVIMTEST') >= 0) + endif + call delete('Xtest_serverlist') + endif + eval name->remote_foreground() call remote_send(name, ":let testvar = 'yes'\<CR>") @@ -81,6 +93,10 @@ func Test_client_server() endif let g:testvar = 'myself' call assert_equal('myself', remote_expr(v:servername, 'testvar')) + call remote_send(v:servername, ":let g:testvar2 = 75\<CR>") + call feedkeys('', 'x') + call assert_equal(75, g:testvar2) + call assert_fails('let v = remote_expr(v:servername, "/2")', 'E449:') call remote_send(name, ":call server2client(expand('<client>'), 'got it')\<CR>", 'g:myserverid') call assert_equal('got it', g:myserverid->remote_read(2)) @@ -101,6 +117,55 @@ func Test_client_server() call assert_equal('another', g:peek_result) call assert_equal('another', remote_read(g:myserverid, 2)) + if !has('gui_running') + " In GUI vim, the following tests display a dialog box + + let cmd = GetVimProg() .. ' --servername ' .. name + + " Run a separate instance to send a command to the server + call remote_expr(name, 'execute("only")') + call system(cmd .. ' --remote-send ":new Xfile<CR>"') + call assert_equal('2', remote_expr(name, 'winnr("$")')) + call assert_equal('Xfile', remote_expr(name, 'winbufnr(1)->bufname()')) + call remote_expr(name, 'execute("only")') + + " Invoke a remote-expr. On MS-Windows, the returned value has a carriage + " return. + let l = system(cmd .. ' --remote-expr "2 + 2"') + call assert_equal(['4'], split(l, "\n")) + + " Edit multiple files using --remote + call system(cmd .. ' --remote Xfile1 Xfile2 Xfile3') + call assert_equal("Xfile1\nXfile2\nXfile3\n", remote_expr(name, 'argv()')) + eval name->remote_send(":%bw!\<CR>") + + " Edit files in separate tab pages + call system(cmd .. ' --remote-tab Xfile1 Xfile2 Xfile3') + call assert_equal('3', remote_expr(name, 'tabpagenr("$")')) + call assert_equal('Xfile2', remote_expr(name, 'bufname(tabpagebuflist(2)[0])')) + eval name->remote_send(":%bw!\<CR>") + + " Edit a file using --remote-wait + eval name->remote_send(":source $VIMRUNTIME/plugin/rrhelper.vim\<CR>") + call system(cmd .. ' --remote-wait +enew Xfile1') + call assert_equal("Xfile1", remote_expr(name, 'bufname("#")')) + eval name->remote_send(":%bw!\<CR>") + + " Edit files using --remote-tab-wait + call system(cmd .. ' --remote-tabwait +tabonly\|enew Xfile1 Xfile2') + call assert_equal('1', remote_expr(name, 'tabpagenr("$")')) + eval name->remote_send(":%bw!\<CR>") + + " Error cases + if v:lang == "C" || v:lang =~ '^[Ee]n' + let l = split(system(cmd .. ' --remote +pwd'), "\n") + call assert_equal("Argument missing after: \"+pwd\"", l[1]) + endif + let l = system(cmd .. ' --remote-expr "abcd"') + call assert_match('^E449: ', l) + endif + + eval name->remote_send(":%bw!\<CR>") eval name->remote_send(":qa!\<CR>") try call WaitForAssert({-> assert_equal("dead", job_status(job))}) @@ -111,8 +176,8 @@ func Test_client_server() endif endtry - call assert_fails("let x=remote_peek([])", 'E730:') - call assert_fails("let x=remote_read('vim10')", 'E277:') + call assert_fails("let x = remote_peek([])", 'E730:') + call assert_fails("let x = remote_read('vim10')", 'E277:') endfunc " Uncomment this line to get a debugging log diff --git a/src/nvim/testdir/test_cmdline.vim b/src/nvim/testdir/test_cmdline.vim index 276bb7fb71..7aac731709 100644 --- a/src/nvim/testdir/test_cmdline.vim +++ b/src/nvim/testdir/test_cmdline.vim @@ -642,6 +642,9 @@ func Test_cmdline_remove_char() call feedkeys(":abc def\<S-Left>\<C-U>\<C-B>\"\<CR>", 'tx') call assert_equal('"def', @:, e) + + " This was going before the start in latin1. + call feedkeys(": \<C-W>\<CR>", 'tx') endfor let &encoding = encoding_save @@ -704,6 +707,16 @@ func Test_cmdline_complete_user_cmd() delcommand Foo endfunc +func Test_complete_user_cmd() + command FooBar echo 'global' + command -buffer FooBar echo 'local' + call feedkeys(":Foo\<C-A>\<Home>\"\<CR>", 'tx') + call assert_equal('"FooBar', @:) + + delcommand -buffer FooBar + delcommand FooBar +endfunc + func s:ScriptLocalFunction() echo 'yes' endfunc @@ -1846,4 +1859,20 @@ func Test_long_error_message() silent! norm Q00000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 endfunc +func Test_cmdline_redraw_tabline() + CheckRunVimInTerminal + + let lines =<< trim END + set showtabline=2 + autocmd CmdlineEnter * set tabline=foo + END + call writefile(lines, 'Xcmdline_redraw_tabline') + let buf = RunVimInTerminal('-S Xcmdline_redraw_tabline', #{rows: 6}) + call term_sendkeys(buf, ':') + call WaitForAssert({-> assert_match('^foo', term_getline(buf, 1))}) + + call StopVimInTerminal(buf) + call delete('Xcmdline_redraw_tabline') +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_diffmode.vim b/src/nvim/testdir/test_diffmode.vim index 8c20c647f1..1dbbe578c5 100644 --- a/src/nvim/testdir/test_diffmode.vim +++ b/src/nvim/testdir/test_diffmode.vim @@ -33,7 +33,8 @@ func Test_diff_fold_sync() call win_gotoid(winone) call assert_equal(23, getcurpos()[1]) - call assert_equal(1, g:update_count) + " depending on how redraw is done DiffUpdated may be triggered once or twice + call assert_inrange(1, 2, g:update_count) au! DiffUpdated windo diffoff @@ -1332,4 +1333,157 @@ func Test_diff_binary() set diffopt&vim endfunc +" Test for using the 'zi' command to invert 'foldenable' in diff windows (test +" for the issue fixed by patch 6.2.317) +func Test_diff_foldinvert() + %bw! + edit Xfile1 + new Xfile2 + new Xfile3 + windo diffthis + " open a non-diff window + botright new + 1wincmd w + call assert_true(getwinvar(1, '&foldenable')) + call assert_true(getwinvar(2, '&foldenable')) + call assert_true(getwinvar(3, '&foldenable')) + normal zi + call assert_false(getwinvar(1, '&foldenable')) + call assert_false(getwinvar(2, '&foldenable')) + call assert_false(getwinvar(3, '&foldenable')) + normal zi + call assert_true(getwinvar(1, '&foldenable')) + call assert_true(getwinvar(2, '&foldenable')) + call assert_true(getwinvar(3, '&foldenable')) + + " If the current window has 'noscrollbind', then 'zi' should not change + " 'foldenable' in other windows. + 1wincmd w + set noscrollbind + normal zi + call assert_false(getwinvar(1, '&foldenable')) + call assert_true(getwinvar(2, '&foldenable')) + call assert_true(getwinvar(3, '&foldenable')) + + " 'zi' should not change the 'foldenable' for windows with 'noscrollbind' + 1wincmd w + set scrollbind + normal zi + call setwinvar(2, '&scrollbind', v:false) + normal zi + call assert_false(getwinvar(1, '&foldenable')) + call assert_true(getwinvar(2, '&foldenable')) + call assert_false(getwinvar(3, '&foldenable')) + + %bw! + set scrollbind& +endfunc + +" This was scrolling for 'cursorbind' but 'scrollbind' is more important +func Test_diff_scroll() + CheckScreendump + + let left =<< trim END + line 1 + line 2 + line 3 + line 4 + + // Common block + // one + // containing + // four lines + + // Common block + // two + // containing + // four lines + END + call writefile(left, 'Xleft') + let right =<< trim END + line 1 + line 2 + line 3 + line 4 + + Lorem + ipsum + dolor + sit + amet, + consectetur + adipiscing + elit. + Etiam + luctus + lectus + sodales, + dictum + + // Common block + // one + // containing + // four lines + + Vestibulum + tincidunt + aliquet + nulla. + + // Common block + // two + // containing + // four lines + END + call writefile(right, 'Xright') + let buf = RunVimInTerminal('-d Xleft Xright', {'rows': 12}) + call term_sendkeys(buf, "\<C-W>\<C-W>jjjj") + call VerifyScreenDump(buf, 'Test_diff_scroll_1', {}) + call term_sendkeys(buf, "j") + call VerifyScreenDump(buf, 'Test_diff_scroll_2', {}) + + call StopVimInTerminal(buf) + call delete('Xleft') + call delete('Xright') +endfunc + +" This was trying to update diffs for a buffer being closed +func Test_diff_only() + silent! lfile + set diff + lopen + norm o + silent! norm o + + set nodiff + %bwipe! +endfunc + +" This was causing invalid diff block values +" FIXME: somehow this causes a valgrind error when run directly but not when +" run as a test. +func Test_diff_manipulations() + set diff + split 0 + sil! norm R
doobdeuR
doobdeuR
doobdeu + + set nodiff + %bwipe! +endfunc + +" This was causing the line number in the diff block to go below one. +" FIXME: somehow this causes a valgrind error when run directly but not when +" run as a test. +func Test_diff_put_and_undo() + set diff + next 0 + split 00 + sil! norm o0gguudpo0ggJuudp + + bwipe! + bwipe! + set nodiff +endfunc + + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_edit.vim b/src/nvim/testdir/test_edit.vim index 42c77518e4..a09346a595 100644 --- a/src/nvim/testdir/test_edit.vim +++ b/src/nvim/testdir/test_edit.vim @@ -1811,95 +1811,4 @@ func Test_read_invalid() set encoding=utf-8 endfunc -" Test for ModeChanged pattern -func Test_mode_changes() - let g:index = 0 - let g:mode_seq = ['n', 'i', 'n', 'v', 'V', 'i', 'ix', 'i', 'ic', 'i', 'n', 'no', 'n', 'V', 'v', 's', 'n'] - func! TestMode() - call assert_equal(g:mode_seq[g:index], get(v:event, "old_mode")) - call assert_equal(g:mode_seq[g:index + 1], get(v:event, "new_mode")) - call assert_equal(mode(1), get(v:event, "new_mode")) - let g:index += 1 - endfunc - - au ModeChanged * :call TestMode() - let g:n_to_any = 0 - au ModeChanged n:* let g:n_to_any += 1 - call feedkeys("i\<esc>vVca\<CR>\<C-X>\<C-L>\<esc>ggdG", 'tnix') - - let g:V_to_v = 0 - au ModeChanged V:v let g:V_to_v += 1 - call feedkeys("Vv\<C-G>\<esc>", 'tnix') - call assert_equal(len(filter(g:mode_seq[1:], {idx, val -> val == 'n'})), g:n_to_any) - call assert_equal(1, g:V_to_v) - call assert_equal(len(g:mode_seq) - 1, g:index) - - let g:n_to_i = 0 - au ModeChanged n:i let g:n_to_i += 1 - let g:n_to_niI = 0 - au ModeChanged i:niI let g:n_to_niI += 1 - let g:niI_to_i = 0 - au ModeChanged niI:i let g:niI_to_i += 1 - let g:nany_to_i = 0 - au ModeChanged n*:i let g:nany_to_i += 1 - let g:i_to_n = 0 - au ModeChanged i:n let g:i_to_n += 1 - let g:nori_to_any = 0 - au ModeChanged [ni]:* let g:nori_to_any += 1 - let g:i_to_any = 0 - au ModeChanged i:* let g:i_to_any += 1 - let g:index = 0 - let g:mode_seq = ['n', 'i', 'niI', 'i', 'n'] - call feedkeys("a\<C-O>l\<esc>", 'tnix') - call assert_equal(len(g:mode_seq) - 1, g:index) - call assert_equal(1, g:n_to_i) - call assert_equal(1, g:n_to_niI) - call assert_equal(1, g:niI_to_i) - call assert_equal(2, g:nany_to_i) - call assert_equal(1, g:i_to_n) - call assert_equal(2, g:i_to_any) - call assert_equal(3, g:nori_to_any) - - if has('terminal') - let g:mode_seq += ['c', 'n', 't', 'nt', 'c', 'nt', 'n'] - call feedkeys(":term\<CR>\<C-W>N:bd!\<CR>", 'tnix') - call assert_equal(len(g:mode_seq) - 1, g:index) - call assert_equal(1, g:n_to_i) - call assert_equal(1, g:n_to_niI) - call assert_equal(1, g:niI_to_i) - call assert_equal(2, g:nany_to_i) - call assert_equal(1, g:i_to_n) - call assert_equal(2, g:i_to_any) - call assert_equal(5, g:nori_to_any) - endif - - au! ModeChanged - delfunc TestMode - unlet! g:mode_seq - unlet! g:index - unlet! g:n_to_any - unlet! g:V_to_v - unlet! g:n_to_i - unlet! g:n_to_niI - unlet! g:niI_to_i - unlet! g:nany_to_i - unlet! g:i_to_n - unlet! g:nori_to_any - unlet! g:i_to_any -endfunc - -func Test_recursive_ModeChanged() - au! ModeChanged * norm 0u - sil! norm - au! -endfunc - -func Test_ModeChanged_starts_visual() - " This was triggering ModeChanged before setting VIsual, causing a crash. - au! ModeChanged * norm 0u - sil! norm - - au! ModeChanged -endfunc - " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_ex_mode.vim b/src/nvim/testdir/test_ex_mode.vim index 122572f32a..2f734cba26 100644 --- a/src/nvim/testdir/test_ex_mode.vim +++ b/src/nvim/testdir/test_ex_mode.vim @@ -135,6 +135,30 @@ func Test_Ex_global() bwipe! endfunc +" Test for pressing Ctrl-C in :append inside a loop in Ex mode +" This used to hang Vim +func Test_Ex_append_in_loop() + CheckRunVimInTerminal + let buf = RunVimInTerminal('', {'rows': 6}) + + call term_sendkeys(buf, "gQ") + call term_sendkeys(buf, "for i in range(1)\<CR>") + call term_sendkeys(buf, "append\<CR>") + call WaitForAssert({-> assert_match(': append', term_getline(buf, 5))}, 1000) + call term_sendkeys(buf, "\<C-C>") + " Wait for input to be flushed + call term_wait(buf) + call term_sendkeys(buf, "foo\<CR>") + call WaitForAssert({-> assert_match('foo', term_getline(buf, 5))}, 1000) + call term_sendkeys(buf, ".\<CR>") + call WaitForAssert({-> assert_match('.', term_getline(buf, 5))}, 1000) + call term_sendkeys(buf, "endfor\<CR>") + call term_sendkeys(buf, "vi\<CR>") + call WaitForAssert({-> assert_match('foo', term_getline(buf, 1))}, 1000) + + call StopVimInTerminal(buf) +endfunc + " In Ex-mode, a backslash escapes a newline func Test_Ex_escape_enter() call feedkeys("gQlet l = \"a\\\<kEnter>b\"\<cr>vi\<cr>", 'xt') @@ -155,9 +179,7 @@ endfunc func Test_Ex_echo_backslash() throw 'Skipped: Nvim only supports Vim Ex mode' " This test works only when the language is English - if v:lang != "C" && v:lang !~ '^[Ee]n' - return - endif + CheckEnglish let bsl = '\\\\' let bsl2 = '\\\' call assert_fails('call feedkeys("Qecho " .. bsl .. "\nvisual\n", "xt")', diff --git a/src/nvim/testdir/test_excmd.vim b/src/nvim/testdir/test_excmd.vim index 7d581d5efd..dac7a6989d 100644 --- a/src/nvim/testdir/test_excmd.vim +++ b/src/nvim/testdir/test_excmd.vim @@ -242,49 +242,58 @@ func Test_confirm_cmd() CheckNotGui CheckRunVimInTerminal - call writefile(['foo1'], 'foo') - call writefile(['bar1'], 'bar') + call writefile(['foo1'], 'Xfoo') + call writefile(['bar1'], 'Xbar') " Test for saving all the modified buffers - let buf = RunVimInTerminal('', {'rows': 20}) - call term_sendkeys(buf, ":set nomore\n") - call term_sendkeys(buf, ":new foo\n") - call term_sendkeys(buf, ":call setline(1, 'foo2')\n") - call term_sendkeys(buf, ":new bar\n") - call term_sendkeys(buf, ":call setline(1, 'bar2')\n") - call term_sendkeys(buf, ":wincmd b\n") + let lines =<< trim END + set nomore + new Xfoo + call setline(1, 'foo2') + new Xbar + call setline(1, 'bar2') + wincmd b + END + call writefile(lines, 'Xscript') + let buf = RunVimInTerminal('-S Xscript', {'rows': 20}) call term_sendkeys(buf, ":confirm qall\n") call WaitForAssert({-> assert_match('\[Y\]es, (N)o, Save (A)ll, (D)iscard All, (C)ancel: ', term_getline(buf, 20))}, 1000) call term_sendkeys(buf, "A") call StopVimInTerminal(buf) - call assert_equal(['foo2'], readfile('foo')) - call assert_equal(['bar2'], readfile('bar')) + call assert_equal(['foo2'], readfile('Xfoo')) + call assert_equal(['bar2'], readfile('Xbar')) " Test for discarding all the changes to modified buffers - let buf = RunVimInTerminal('', {'rows': 20}) - call term_sendkeys(buf, ":set nomore\n") - call term_sendkeys(buf, ":new foo\n") - call term_sendkeys(buf, ":call setline(1, 'foo3')\n") - call term_sendkeys(buf, ":new bar\n") - call term_sendkeys(buf, ":call setline(1, 'bar3')\n") - call term_sendkeys(buf, ":wincmd b\n") + let lines =<< trim END + set nomore + new Xfoo + call setline(1, 'foo3') + new Xbar + call setline(1, 'bar3') + wincmd b + END + call writefile(lines, 'Xscript') + let buf = RunVimInTerminal('-S Xscript', {'rows': 20}) call term_sendkeys(buf, ":confirm qall\n") call WaitForAssert({-> assert_match('\[Y\]es, (N)o, Save (A)ll, (D)iscard All, (C)ancel: ', term_getline(buf, 20))}, 1000) call term_sendkeys(buf, "D") call StopVimInTerminal(buf) - call assert_equal(['foo2'], readfile('foo')) - call assert_equal(['bar2'], readfile('bar')) + call assert_equal(['foo2'], readfile('Xfoo')) + call assert_equal(['bar2'], readfile('Xbar')) " Test for saving and discarding changes to some buffers - let buf = RunVimInTerminal('', {'rows': 20}) - call term_sendkeys(buf, ":set nomore\n") - call term_sendkeys(buf, ":new foo\n") - call term_sendkeys(buf, ":call setline(1, 'foo4')\n") - call term_sendkeys(buf, ":new bar\n") - call term_sendkeys(buf, ":call setline(1, 'bar4')\n") - call term_sendkeys(buf, ":wincmd b\n") + let lines =<< trim END + set nomore + new Xfoo + call setline(1, 'foo4') + new Xbar + call setline(1, 'bar4') + wincmd b + END + call writefile(lines, 'Xscript') + let buf = RunVimInTerminal('-S Xscript', {'rows': 20}) call term_sendkeys(buf, ":confirm qall\n") call WaitForAssert({-> assert_match('\[Y\]es, (N)o, Save (A)ll, (D)iscard All, (C)ancel: ', term_getline(buf, 20))}, 1000) call term_sendkeys(buf, "N") @@ -292,11 +301,12 @@ func Test_confirm_cmd() call term_sendkeys(buf, "Y") call StopVimInTerminal(buf) - call assert_equal(['foo4'], readfile('foo')) - call assert_equal(['bar2'], readfile('bar')) + call assert_equal(['foo4'], readfile('Xfoo')) + call assert_equal(['bar2'], readfile('Xbar')) - call delete('foo') - call delete('bar') + call delete('Xscript') + call delete('Xfoo') + call delete('Xbar') endfunc func Test_confirm_cmd_cancel() @@ -304,10 +314,13 @@ func Test_confirm_cmd_cancel() CheckRunVimInTerminal " Test for closing a window with a modified buffer - let buf = RunVimInTerminal('', {'rows': 20}) - call term_sendkeys(buf, ":set nomore\n") - call term_sendkeys(buf, ":new\n") - call term_sendkeys(buf, ":call setline(1, 'abc')\n") + let lines =<< trim END + set nomore + new + call setline(1, 'abc') + END + call writefile(lines, 'Xscript') + let buf = RunVimInTerminal('-S Xscript', {'rows': 20}) call term_sendkeys(buf, ":confirm close\n") call WaitForAssert({-> assert_match('^\[Y\]es, (N)o, (C)ancel: *$', \ term_getline(buf, 20))}, 1000) @@ -320,6 +333,43 @@ func Test_confirm_cmd_cancel() call WaitForAssert({-> assert_match('^ *0,0-1 All$', \ term_getline(buf, 20))}, 1000) call StopVimInTerminal(buf) + call delete('Xscript') +endfunc + +" The ":confirm" prompt was sometimes used with the terminal in cooked mode. +" This test verifies that a "\<CR>" character is NOT required to respond to a +" prompt from the ":conf q" and ":conf wq" commands. +func Test_confirm_q_wq() + CheckNotGui + CheckRunVimInTerminal + + call writefile(['foo'], 'Xfoo') + + let lines =<< trim END + set hidden nomore + call setline(1, 'abc') + edit Xfoo + END + call writefile(lines, 'Xscript') + let buf = RunVimInTerminal('-S Xscript', {'rows': 20}) + call term_sendkeys(buf, ":confirm q\n") + call WaitForAssert({-> assert_match('^\[Y\]es, (N)o, (C)ancel: *$', + \ term_getline(buf, 20))}, 1000) + call term_sendkeys(buf, 'C') + call WaitForAssert({-> assert_notmatch('^\[Y\]es, (N)o, (C)ancel: C*$', + \ term_getline(buf, 20))}, 1000) + + call term_sendkeys(buf, ":edit Xfoo\n") + call term_sendkeys(buf, ":confirm wq\n") + call WaitForAssert({-> assert_match('^\[Y\]es, (N)o, (C)ancel: *$', + \ term_getline(buf, 20))}, 1000) + call term_sendkeys(buf, 'C') + call WaitForAssert({-> assert_notmatch('^\[Y\]es, (N)o, (C)ancel: C*$', + \ term_getline(buf, 20))}, 1000) + call StopVimInTerminal(buf) + + call delete('Xscript') + call delete('Xfoo') endfunc func Test_confirm_write_ro() @@ -646,5 +696,21 @@ func Test_using_zero_in_range() bwipe! endfunc +" Test :write after changing name with :file and loading it with :edit +func Test_write_after_rename() + call writefile(['text'], 'Xfile') + + enew + file Xfile + call assert_fails('write', 'E13: File exists (add ! to override)') + + " works OK after ":edit" + edit + write + + call delete('Xfile') + bwipe! +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_expand.vim b/src/nvim/testdir/test_expand.vim index d86fea4f45..383921bb82 100644 --- a/src/nvim/testdir/test_expand.vim +++ b/src/nvim/testdir/test_expand.vim @@ -84,6 +84,15 @@ func Test_expandcmd() let $FOO="blue\tsky" call setline(1, "$FOO") call assert_equal("grep pat blue\tsky", expandcmd('grep pat <cfile>')) + + " Test for expression expansion `= + let $FOO= "blue" + call assert_equal("blue sky", expandcmd("`=$FOO .. ' sky'`")) + + " Test for env variable with spaces + let $FOO= "foo bar baz" + call assert_equal("e foo bar baz", expandcmd("e $FOO")) + unlet $FOO close! endfunc diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim index 59b272d563..eedad15e9e 100644 --- a/src/nvim/testdir/test_filetype.vim +++ b/src/nvim/testdir/test_filetype.vim @@ -43,7 +43,9 @@ func Test_other_type() endfunc " Filetypes detected just from matching the file name. +" First one is checking that these files have no filetype. let s:filename_checks = { + \ 'none': ['bsd', 'some-bsd'], \ '8th': ['file.8th'], \ 'a2ps': ['/etc/a2ps.cfg', '/etc/a2ps/file.cfg', 'a2psrc', '.a2psrc', 'any/etc/a2ps.cfg', 'any/etc/a2ps/file.cfg'], \ 'a65': ['file.a65'], @@ -85,7 +87,7 @@ let s:filename_checks = { \ 'bindzone': ['named.root', '/bind/db.file', '/named/db.file', 'any/bind/db.file', 'any/named/db.file'], \ 'bitbake': ['file.bb', 'file.bbappend', 'file.bbclass', 'build/conf/local.conf', 'meta/conf/layer.conf', 'build/conf/bbappend.conf', 'meta-layer/conf/distro/foo.conf'], \ 'blank': ['file.bl'], - \ 'bsdl': ['file.bsd', 'file.bsdl', 'bsd', 'some-bsd'], + \ 'bsdl': ['file.bsd', 'file.bsdl'], \ 'bst': ['file.bst'], \ 'bzl': ['file.bazel', 'file.bzl', 'WORKSPACE'], \ 'bzr': ['bzr_log.any', 'bzr_log.file'], @@ -532,6 +534,7 @@ let s:filename_checks = { \ 'svelte': ['file.svelte'], \ 'svg': ['file.svg'], \ 'svn': ['svn-commitfile.tmp', 'svn-commit-file.tmp', 'svn-commit.tmp'], + \ 'swayconfig': ['/home/user/.sway/config', '/home/user/.config/sway/config', '/etc/sway/config', '/etc/xdg/sway/config'], \ 'swift': ['file.swift'], \ 'swiftgyb': ['file.swift.gyb'], \ 'sysctl': ['/etc/sysctl.conf', '/etc/sysctl.d/file.conf', 'any/etc/sysctl.conf', 'any/etc/sysctl.d/file.conf'], @@ -546,7 +549,7 @@ let s:filename_checks = { \ 'template': ['file.tmpl'], \ 'teraterm': ['file.ttl'], \ 'terminfo': ['file.ti'], - \ 'terraform': ['file.tfvars'], + \ 'terraform-vars': ['file.tfvars'], \ 'tex': ['file.latex', 'file.sty', 'file.dtx', 'file.ltx', 'file.bbl'], \ 'texinfo': ['file.texinfo', 'file.texi', 'file.txi'], \ 'texmf': ['texmf.cnf'], @@ -647,7 +650,8 @@ func CheckItems(checks) if &filetype == '' && &readonly " File exists but not able to edit it (permission denied) else - call assert_equal(ft, &filetype, 'with file name: ' . names[i]) + let expected = ft == 'none' ? '' : ft + call assert_equal(expected, &filetype, 'with file name: ' . names[i]) endif bwipe! endfor @@ -1858,6 +1862,31 @@ func Test_inc_file() call assert_equal('bitbake', &filetype) bwipe! + call writefile(['S = "${WORKDIR}"'], 'Xfile.inc') + split Xfile.inc + call assert_equal('bitbake', &filetype) + bwipe! + + call writefile(['DEPENDS:append = " somedep"'], 'Xfile.inc') + split Xfile.inc + call assert_equal('bitbake', &filetype) + bwipe! + + call writefile(['MACHINE ??= "qemu"'], 'Xfile.inc') + split Xfile.inc + call assert_equal('bitbake', &filetype) + bwipe! + + call writefile(['PROVIDES := "test"'], 'Xfile.inc') + split Xfile.inc + call assert_equal('bitbake', &filetype) + bwipe! + + call writefile(['RDEPENDS_${PN} += "bar"'], 'Xfile.inc') + split Xfile.inc + call assert_equal('bitbake', &filetype) + bwipe! + " asm call writefile(['asmsyntax=bar'], 'Xfile.inc') split Xfile.inc diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim index 9b8d740efb..c11e7b4fea 100644 --- a/src/nvim/testdir/test_functions.vim +++ b/src/nvim/testdir/test_functions.vim @@ -547,6 +547,7 @@ func Save_mode() return '' endfunc +" Test for the mode() function func Test_mode() new call append(0, ["Blue Ball Black", "Brown Band Bowl", ""]) @@ -717,6 +718,8 @@ func Test_mode() call assert_equal('c-c', g:current_modes) call feedkeys("gQecho \<C-R>=Save_mode()\<CR>\<CR>vi\<CR>", 'xt') call assert_equal('c-cv', g:current_modes) + " call feedkeys("Qcall Save_mode()\<CR>vi\<CR>", 'xt') + " call assert_equal('c-ce', g:current_modes) " How to test Ex mode? " Test mode in operatorfunc (it used to be Operator-pending). @@ -1262,6 +1265,23 @@ func Test_inputlist() call feedkeys(":let c = inputlist(['Select color:', '1. red', '2. green', '3. blue'])\<cr>5q", 'tx') call assert_equal(0, c) + " Use backspace to delete characters in the prompt + call feedkeys(":let c = inputlist(['Select color:', '1. red', '2. green', '3. blue'])\<cr>1\<BS>3\<BS>2\<cr>", 'tx') + call assert_equal(2, c) + + " Use mouse to make a selection + " call test_setmouse(&lines - 3, 2) + call nvim_input_mouse('left', 'press', '', 0, &lines - 4, 1) " set mouse position + call getchar() " discard mouse event but keep mouse position + call feedkeys(":let c = inputlist(['Select color:', '1. red', '2. green', '3. blue'])\<cr>\<LeftMouse>", 'tx') + call assert_equal(1, c) + " Mouse click outside of the list + " call test_setmouse(&lines - 6, 2) + call nvim_input_mouse('left', 'press', '', 0, &lines - 7, 1) " set mouse position + call getchar() " discard mouse event but keep mouse position + call feedkeys(":let c = inputlist(['Select color:', '1. red', '2. green', '3. blue'])\<cr>\<LeftMouse>", 'tx') + call assert_equal(-2, c) + call assert_fails('call inputlist("")', 'E686:') endfunc diff --git a/src/nvim/testdir/test_gf.vim b/src/nvim/testdir/test_gf.vim index 1569177d66..feae44e5ee 100644 --- a/src/nvim/testdir/test_gf.vim +++ b/src/nvim/testdir/test_gf.vim @@ -138,6 +138,22 @@ func Test_gf_visual() call assert_equal('Xtest_gf_visual', bufname('%')) call assert_equal(3, getcurpos()[1]) + " do not include the NUL at the end + call writefile(['x'], 'X') + let save_enc = &enc + " for enc in ['latin1', 'utf-8'] + for enc in ['utf-8'] + exe "set enc=" .. enc + new + call setline(1, 'X') + set nomodified + exe "normal \<C-V>$gf" + call assert_equal('X', bufname()) + bwipe! + endfor + let &enc = save_enc + call delete('X') + " line number in visual area is used for file name if has('unix') bwipe! @@ -194,4 +210,27 @@ func Test_gf_includeexpr() delfunc IncFunc endfunc +" Check that expanding directories can handle more than 255 entries. +func Test_gf_subdirs_wildcard() + let cwd = getcwd() + let dir = 'Xtestgf_dir' + call mkdir(dir) + call chdir(dir) + for i in range(300) + call mkdir(i) + call writefile([], i .. '/' .. i, 'S') + endfor + set path=./** + + new | only + call setline(1, '99') + w! Xtest1 + normal gf + call assert_equal('99', fnamemodify(bufname(''), ":t")) + + call chdir(cwd) + call delete(dir, 'rf') + set path& +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_global.vim b/src/nvim/testdir/test_global.vim index feddf85346..947f7efc7c 100644 --- a/src/nvim/testdir/test_global.vim +++ b/src/nvim/testdir/test_global.vim @@ -1,4 +1,7 @@ +" Test for :global and :vglobal + source check.vim +source term_util.vim func Test_yank_put_clipboard() new @@ -82,4 +85,31 @@ func Test_wrong_delimiter() call assert_fails('g x^bxd', 'E146:') endfunc +" Test for interrupting :global using Ctrl-C +func Test_interrupt_global() + CheckRunVimInTerminal + let lines =<< trim END + cnoremap ; <Cmd>sleep 10<CR> + call setline(1, repeat(['foo'], 5)) + END + call writefile(lines, 'Xtest_interrupt_global') + let buf = RunVimInTerminal('-S Xtest_interrupt_global', {'rows': 6}) + + call term_sendkeys(buf, ":g/foo/norm :\<C-V>;\<CR>") + " Wait for :sleep to start + call term_wait(buf) + call term_sendkeys(buf, "\<C-C>") + call WaitForAssert({-> assert_match('Interrupted', term_getline(buf, 6))}, 1000) + + " Also test in Ex mode + call term_sendkeys(buf, "gQg/foo/norm :\<C-V>;\<CR>") + " Wait for :sleep to start + call term_wait(buf) + call term_sendkeys(buf, "\<C-C>") + call WaitForAssert({-> assert_match('Interrupted', term_getline(buf, 5))}, 1000) + + call StopVimInTerminal(buf) + call delete('Xtest_interrupt_global') +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_feedkeys.vim b/src/nvim/testdir/test_input.vim index fb64711863..3b1e2eb2df 100644 --- a/src/nvim/testdir/test_feedkeys.vim +++ b/src/nvim/testdir/test_input.vim @@ -1,4 +1,4 @@ -" Test feedkeys() function. +" Tests for character input and feedkeys() function. func Test_feedkeys_x_with_empty_string() new @@ -34,4 +34,28 @@ func Test_feedkeys_escape_special() nunmap … endfunc +func Test_input_simplify_ctrl_at() + new + " feeding unsimplified CTRL-@ should still trigger i_CTRL-@ + call feedkeys("ifoo\<Esc>A\<*C-@>x", 'xt') + call assert_equal('foofo', getline(1)) + bw! +endfunc + +func Test_input_simplify_noremap() + call feedkeys("i\<*C-M>", 'nx') + call assert_equal('', getline(1)) + call assert_equal([0, 2, 1, 0, 1], getcurpos()) + bw! +endfunc + +func Test_input_simplify_timedout() + inoremap <C-M>a b + call feedkeys("i\<*C-M>", 'xt') + call assert_equal('', getline(1)) + call assert_equal([0, 2, 1, 0, 1], getcurpos()) + iunmap <C-M>a + bw! +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_ins_complete.vim b/src/nvim/testdir/test_ins_complete.vim index 93ab17955d..179218e48a 100644 --- a/src/nvim/testdir/test_ins_complete.vim +++ b/src/nvim/testdir/test_ins_complete.vim @@ -100,6 +100,74 @@ func Test_ins_complete() call delete('Xdir', 'rf') endfunc +func Test_omni_dash() + func Omni(findstart, base) + if a:findstart + return 5 + else + echom a:base + return ['-help', '-v'] + endif + endfunc + set omnifunc=Omni + new + exe "normal Gofind -\<C-x>\<C-o>" + call assert_equal("find -help", getline('$')) + + bwipe! + delfunc Omni + set omnifunc= +endfunc + +func Test_omni_throw() + let g:CallCount = 0 + func Omni(findstart, base) + let g:CallCount += 1 + if a:findstart + throw "he he he" + endif + endfunc + set omnifunc=Omni + new + try + exe "normal ifoo\<C-x>\<C-o>" + call assert_false(v:true, 'command should have failed') + catch + call assert_exception('he he he') + call assert_equal(1, g:CallCount) + endtry + + bwipe! + delfunc Omni + unlet g:CallCount + set omnifunc= +endfunc + +func Test_completefunc_args() + let s:args = [] + func! CompleteFunc(findstart, base) + let s:args += [[a:findstart, empty(a:base)]] + endfunc + new + + set completefunc=CompleteFunc + call feedkeys("i\<C-X>\<C-U>\<Esc>", 'x') + call assert_equal([1, 1], s:args[0]) + call assert_equal(0, s:args[1][0]) + set completefunc= + + let s:args = [] + set omnifunc=CompleteFunc + call feedkeys("i\<C-X>\<C-O>\<Esc>", 'x') + call assert_equal([1, 1], s:args[0]) + call assert_equal(0, s:args[1][0]) + set omnifunc= + + bwipe! + unlet s:args + delfunc CompleteFunc +endfunc + func s:CompleteDone_CompleteFuncNone( findstart, base ) throw 'skipped: Nvim does not support v:none' if a:findstart @@ -179,19 +247,6 @@ func Test_CompleteDoneDict() au! CompleteDone endfunc -func Test_CompleteDone_undo() - au CompleteDone * call append(0, "prepend1") - new - call setline(1, ["line1", "line2"]) - call feedkeys("Go\<C-X>\<C-N>\<CR>\<ESC>", "tx") - call assert_equal(["prepend1", "line1", "line2", "line1", ""], - \ getline(1, '$')) - undo - call assert_equal(["line1", "line2"], getline(1, '$')) - bwipe! - au! CompleteDone -endfunc - func s:CompleteDone_CompleteFuncDictNoUserData(findstart, base) if a:findstart return 0 @@ -268,72 +323,30 @@ func Test_CompleteDoneList() au! CompleteDone endfunc -func Test_omni_dash() - func Omni(findstart, base) - if a:findstart - return 5 - else - echom a:base - return ['-help', '-v'] - endif - endfunc - set omnifunc=Omni - new - exe "normal Gofind -\<C-x>\<C-o>" - call assert_equal("find -help", getline('$')) - - bwipe! - delfunc Omni - set omnifunc= -endfunc - -func Test_omni_throw() - let g:CallCount = 0 - func Omni(findstart, base) - let g:CallCount += 1 - if a:findstart - throw "he he he" - endif - endfunc - set omnifunc=Omni +func Test_CompleteDone_undo() + au CompleteDone * call append(0, "prepend1") new - try - exe "normal ifoo\<C-x>\<C-o>" - call assert_false(v:true, 'command should have failed') - catch - call assert_exception('he he he') - call assert_equal(1, g:CallCount) - endtry - + call setline(1, ["line1", "line2"]) + call feedkeys("Go\<C-X>\<C-N>\<CR>\<ESC>", "tx") + call assert_equal(["prepend1", "line1", "line2", "line1", ""], + \ getline(1, '$')) + undo + call assert_equal(["line1", "line2"], getline(1, '$')) bwipe! - delfunc Omni - unlet g:CallCount - set omnifunc= + au! CompleteDone endfunc -func Test_completefunc_args() - let s:args = [] - func! CompleteFunc(findstart, base) - let s:args += [[a:findstart, empty(a:base)]] - endfunc - new - - set completefunc=CompleteFunc - call feedkeys("i\<C-X>\<C-U>\<Esc>", 'x') - call assert_equal([1, 1], s:args[0]) - call assert_equal(0, s:args[1][0]) - set completefunc= - - let s:args = [] - set omnifunc=CompleteFunc - call feedkeys("i\<C-X>\<C-O>\<Esc>", 'x') - call assert_equal([1, 1], s:args[0]) - call assert_equal(0, s:args[1][0]) - set omnifunc= - - bwipe! - unlet s:args - delfunc CompleteFunc +func Test_CompleteDone_modify() + let value = { + \ 'word': '', + \ 'abbr': '', + \ 'menu': '', + \ 'info': '', + \ 'kind': '', + \ 'user_data': '', + \ } + let v:completed_item = value + call assert_equal(value, v:completed_item) endfunc func CompleteTest(findstart, query) @@ -505,19 +518,6 @@ func Test_ins_completeslash() set completeslash= endfunc -func Test_issue_7021() - CheckMSWindows - - let orig_shellslash = &shellslash - set noshellslash - - set completeslash=slash - call assert_false(expand('~') =~ '/') - - let &shellslash = orig_shellslash - set completeslash= -endfunc - func Test_pum_stopped_by_timer() CheckScreendump @@ -829,6 +829,19 @@ func Test_complete_stop() close! endfunc +func Test_issue_7021() + CheckMSWindows + + let orig_shellslash = &shellslash + set noshellslash + + set completeslash=slash + call assert_false(expand('~') =~ '/') + + let &shellslash = orig_shellslash + set completeslash= +endfunc + " Test to ensure 'Scanning...' messages are not recorded in messages history func Test_z1_complete_no_history() new @@ -838,7 +851,18 @@ func Test_z1_complete_no_history() exe "normal owh\<C-X>\<C-K>" exe "normal owh\<C-N>" call assert_equal(currmess, execute('messages')) - close! + bwipe! +endfunc + +" A mapping is not used for the key after CTRL-X. +func Test_no_mapping_for_ctrl_x_key() + new + inoremap <C-K> <Cmd>let was_mapped = 'yes'<CR> + setlocal dictionary=README.txt + call feedkeys("aexam\<C-X>\<C-K> ", 'xt') + call assert_equal('example ', getline(1)) + call assert_false(exists('was_mapped')) + bwipe! endfunc func FooBarComplete(findstart, base) diff --git a/src/nvim/testdir/test_maparg.vim b/src/nvim/testdir/test_maparg.vim index f9429a8020..dad4c81a7b 100644 --- a/src/nvim/testdir/test_maparg.vim +++ b/src/nvim/testdir/test_maparg.vim @@ -1,12 +1,12 @@ -" Tests for maparg(). +" Tests for maparg(), mapcheck() and mapset(). " Also test utf8 map with a 0x80 byte. " Also test mapcheck() -function s:SID() +func s:SID() return str2nr(matchstr(expand('<sfile>'), '<SNR>\zs\d\+\ze_SID$')) -endfun +endfunc -function Test_maparg() +func Test_maparg() new set cpo-=< set encoding=utf8 @@ -17,24 +17,28 @@ function Test_maparg() vnoremap <script> <buffer> <expr> <silent> bar isbar call assert_equal("is<F4>foo", maparg('foo<C-V>')) call assert_equal({'silent': 0, 'noremap': 0, 'script': 0, 'lhs': 'foo<C-V>', - \ 'mode': ' ', 'nowait': 0, 'expr': 0, 'sid': sid, 'lnum': lnum + 1, - \ 'rhs': 'is<F4>foo', 'buffer': 0}, - \ maparg('foo<C-V>', '', 0, 1)) - call assert_equal({'silent': 1, 'noremap': 1, 'script': 1, 'lhs': 'bar', 'mode': 'v', + \ 'lhsraw': "foo\x80\xfc\x04V", 'lhsrawalt': "foo\x16", + \ 'mode': ' ', 'nowait': 0, 'expr': 0, 'sid': sid, 'lnum': lnum + 1, + \ 'rhs': 'is<F4>foo', 'buffer': 0}, + \ maparg('foo<C-V>', '', 0, 1)) + call assert_equal({'silent': 1, 'noremap': 1, 'script': 1, 'lhs': 'bar', + \ 'lhsraw': 'bar', 'mode': 'v', \ 'nowait': 0, 'expr': 1, 'sid': sid, 'lnum': lnum + 2, - \ 'rhs': 'isbar', 'buffer': 1}, + \ 'rhs': 'isbar', 'buffer': 1}, \ 'bar'->maparg('', 0, 1)) let lnum = expand('<sflnum>') map <buffer> <nowait> foo bar - call assert_equal({'silent': 0, 'noremap': 0, 'script': 0, 'lhs': 'foo', 'mode': ' ', + call assert_equal({'silent': 0, 'noremap': 0, 'script': 0, 'lhs': 'foo', + \ 'lhsraw': 'foo', 'mode': ' ', \ 'nowait': 1, 'expr': 0, 'sid': sid, 'lnum': lnum + 1, 'rhs': 'bar', - \ 'buffer': 1}, + \ 'buffer': 1}, \ maparg('foo', '', 0, 1)) let lnum = expand('<sflnum>') tmap baz foo - call assert_equal({'silent': 0, 'noremap': 0, 'script': 0, 'lhs': 'baz', 'mode': 't', + call assert_equal({'silent': 0, 'noremap': 0, 'script': 0, 'lhs': 'baz', + \ 'lhsraw': 'baz', 'mode': 't', \ 'nowait': 0, 'expr': 0, 'sid': sid, 'lnum': lnum + 1, 'rhs': 'foo', - \ 'buffer': 0}, + \ 'buffer': 0}, \ maparg('baz', 't', 0, 1)) map abc x<char-114>x @@ -81,6 +85,12 @@ function Test_maparg() call assert_equal(['{', 'w', 's'], [d.lhs, d.rhs, d.mode]) sunmap { + map <C-I> foo + unmap <Tab> + " This used to cause a segfault + call maparg('<C-I>', '', 0, 1) + unmap <C-I> + map abc <Nop> call assert_equal("<Nop>", maparg('abc')) unmap abc @@ -89,7 +99,8 @@ function Test_maparg() let d = maparg('esc', 'i', 1, 1) call assert_equal(['esc', "\<C-V>\<C-V>\<Esc>", '!'], [d.lhs, d.rhs, d.mode]) abclear -endfunction + unlet d +endfunc func Test_mapcheck() call assert_equal('', mapcheck('a')) @@ -130,7 +141,7 @@ func Test_mapcheck() unabbr ab endfunc -function Test_range_map() +func Test_range_map() new " Outside of the range, minimum inoremap <Char-0x1040> a @@ -145,6 +156,147 @@ function Test_range_map() inoremap <Char-0xf040> d execute "normal a\uf040\<Esc>" call assert_equal("abcd", getline(1)) -endfunction +endfunc + +func One_mapset_test(keys) + exe 'nnoremap ' .. a:keys .. ' original<CR>' + let orig = maparg(a:keys, 'n', 0, 1) + call assert_equal(a:keys, orig.lhs) + call assert_equal('original<CR>', orig.rhs) + call assert_equal('n', orig.mode) + + exe 'nunmap ' .. a:keys + let d = maparg(a:keys, 'n', 0, 1) + call assert_equal({}, d) + + call mapset('n', 0, orig) + let d = maparg(a:keys, 'n', 0, 1) + call assert_equal(a:keys, d.lhs) + call assert_equal('original<CR>', d.rhs) + call assert_equal('n', d.mode) + + exe 'nunmap ' .. a:keys +endfunc + +func Test_mapset() + call One_mapset_test('K') + call One_mapset_test('<F3>') + + " Check <> key conversion + new + inoremap K one<Left>x + call feedkeys("iK\<Esc>", 'xt') + call assert_equal('onxe', getline(1)) + + let orig = maparg('K', 'i', 0, 1) + call assert_equal('K', orig.lhs) + call assert_equal('one<Left>x', orig.rhs) + call assert_equal('i', orig.mode) + + iunmap K + let d = maparg('K', 'i', 0, 1) + call assert_equal({}, d) + + call mapset('i', 0, orig) + call feedkeys("SK\<Esc>", 'xt') + call assert_equal('onxe', getline(1)) + + iunmap K + + " Test literal <CR> using a backslash + let cpo_save = &cpo + set cpo-=B + inoremap K one\<CR>two + call feedkeys("SK\<Esc>", 'xt') + call assert_equal('one<CR>two', getline(1)) + + let orig = maparg('K', 'i', 0, 1) + call assert_equal('K', orig.lhs) + call assert_equal('one\<CR>two', orig.rhs) + call assert_equal('i', orig.mode) + + iunmap K + let d = maparg('K', 'i', 0, 1) + call assert_equal({}, d) + + call mapset('i', 0, orig) + call feedkeys("SK\<Esc>", 'xt') + call assert_equal('one<CR>two', getline(1)) + + iunmap K + + " Test literal <CR> using CTRL-V + inoremap K one<CR>two + call feedkeys("SK\<Esc>", 'xt') + call assert_equal('one<CR>two', getline(1)) + + let orig = maparg('K', 'i', 0, 1) + call assert_equal('K', orig.lhs) + call assert_equal("one\x16<CR>two", orig.rhs) + call assert_equal('i', orig.mode) + + iunmap K + let d = maparg('K', 'i', 0, 1) + call assert_equal({}, d) + + call mapset('i', 0, orig) + call feedkeys("SK\<Esc>", 'xt') + call assert_equal('one<CR>two', getline(1)) + + iunmap K + let &cpo = cpo_save + bwipe! + + call assert_fails('call mapset([], v:false, {})', 'E730:') +endfunc + +func Check_ctrlb_map(d, check_alt) + call assert_equal('<C-B>', a:d.lhs) + if a:check_alt + call assert_equal("\x80\xfc\x04B", a:d.lhsraw) + call assert_equal("\x02", a:d.lhsrawalt) + else + call assert_equal("\x02", a:d.lhsraw) + endif +endfunc + +func Test_map_local() + nmap a global + nmap <buffer>a local + + let prev_map_list = split(execute('nmap a'), "\n") + call assert_match('n\s*a\s*@local', prev_map_list[0]) + call assert_match('n\s*a\s*global', prev_map_list[1]) + + let mapping = maparg('a', 'n', 0, 1) + call assert_equal(1, mapping.buffer) + let mapping.rhs = 'new_local' + call mapset('n', 0, mapping) + + " Check that the global mapping is left untouched. + let map_list = split(execute('nmap a'), "\n") + call assert_match('n\s*a\s*@new_local', map_list[0]) + call assert_match('n\s*a\s*global', map_list[1]) + + nunmap a +endfunc + +func Test_map_restore() + " Test restoring map with alternate keycode + nmap <C-B> back + let d = maparg('<C-B>', 'n', 0, 1) + call Check_ctrlb_map(d, 1) + let dsimp = maparg("\x02", 'n', 0, 1) + call Check_ctrlb_map(dsimp, 0) + nunmap <C-B> + call mapset('n', 0, d) + let d = maparg('<C-B>', 'n', 0, 1) + call Check_ctrlb_map(d, 1) + let dsimp = maparg("\x02", 'n', 0, 1) + call Check_ctrlb_map(dsimp, 0) + + nunmap <C-B> + +endfunc " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_messages.vim b/src/nvim/testdir/test_messages.vim index 5670368936..a02d23b409 100644 --- a/src/nvim/testdir/test_messages.vim +++ b/src/nvim/testdir/test_messages.vim @@ -118,7 +118,9 @@ func Test_message_more() let buf = RunVimInTerminal('', {'rows': 6}) call term_sendkeys(buf, ":call setline(1, range(1, 100))\n") - call term_sendkeys(buf, ":%p#\n") + call term_sendkeys(buf, ":%pfoo\<C-H>\<C-H>\<C-H>#") + call WaitForAssert({-> assert_equal(':%p#', term_getline(buf, 6))}) + call term_sendkeys(buf, "\n") call WaitForAssert({-> assert_equal(' 5 5', term_getline(buf, 5))}) call WaitForAssert({-> assert_equal('-- More --', term_getline(buf, 6))}) @@ -175,7 +177,8 @@ func Test_message_more() " Up all the way with 'g'. call term_sendkeys(buf, 'g') - call WaitForAssert({-> assert_equal(' 5 5', term_getline(buf, 5))}) + call WaitForAssert({-> assert_equal(' 4 4', term_getline(buf, 5))}) + call WaitForAssert({-> assert_equal(':%p#', term_getline(buf, 1))}) call WaitForAssert({-> assert_equal('-- More --', term_getline(buf, 6))}) " All the way down. Pressing f should do nothing but pressing @@ -193,6 +196,13 @@ func Test_message_more() call WaitForAssert({-> assert_equal('100 100', term_getline(buf, 5))}) call WaitForAssert({-> assert_equal('Press ENTER or type command to continue', term_getline(buf, 6))}) + " A command line that doesn't print text is appended to scrollback, + " even if it invokes a nested command line. + call term_sendkeys(buf, ":\<C-R>=':'\<CR>:\<CR>g<") + call WaitForAssert({-> assert_equal('100 100', term_getline(buf, 4))}) + call WaitForAssert({-> assert_equal(':::', term_getline(buf, 5))}) + call WaitForAssert({-> assert_equal('Press ENTER or type command to continue', term_getline(buf, 6))}) + call term_sendkeys(buf, ":%p#\n") call WaitForAssert({-> assert_equal(' 5 5', term_getline(buf, 5))}) call WaitForAssert({-> assert_equal('-- More --', term_getline(buf, 6))}) @@ -306,4 +316,60 @@ func Test_fileinfo_after_echo() call delete('b.txt') endfunc +func Test_cmdheight_zero() + set cmdheight=0 + set showcmd + redraw! + + echo 'test echo' + call assert_equal(116, screenchar(&lines, 1)) + redraw! + + echomsg 'test echomsg' + call assert_equal(116, screenchar(&lines, 1)) + redraw! + + call feedkeys(":ls\<CR>", "xt") + call assert_equal(':ls', Screenline(&lines - 1)) + redraw! + + let char = getchar(0) + call assert_match(char, 0) + + " Check change/restore cmdheight when macro + call feedkeys("qa", "xt") + call assert_equal(1, &cmdheight) + call feedkeys("q", "xt") + call assert_equal(0, &cmdheight) + + call setline(1, 'somestring') + call feedkeys("y", "n") + %s/somestring/otherstring/gc + call assert_equal('otherstring', getline(1)) + + call feedkeys("g\<C-g>", "xt") + call assert_match( + \ 'Col 1 of 11; Line 1 of 1; Word 1 of 1', + \ Screenline(&lines)) + + " Check split behavior + for i in range(1, 10) + split + endfor + only + call assert_equal(0, &cmdheight) + + " Check that pressing ":" should not scroll a window + " Check for what patch 9.0.0115 fixes + botright 10new + call setline(1, range(12)) + 7 + call feedkeys(":\"\<C-R>=line('w0')\<CR>\<CR>", "xt") + call assert_equal('"1', @:) + bwipe! + + set cmdheight& + set showcmd& +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_normal.vim b/src/nvim/testdir/test_normal.vim index 9fbd1f774a..f18ddb274c 100644 --- a/src/nvim/testdir/test_normal.vim +++ b/src/nvim/testdir/test_normal.vim @@ -580,6 +580,7 @@ endfunc func Test_normal_z_error() call assert_beeps('normal! z2p') call assert_beeps('normal! zq') + call assert_beeps('normal! cz1') endfunc func Test_normal15_z_scroll_vert() @@ -619,7 +620,7 @@ func Test_normal15_z_scroll_vert() call assert_equal(10, winheight(0)) exe "norm! z12\<cr>" call assert_equal(12, winheight(0)) - exe "norm! z10\<cr>" + exe "norm! z15\<Del>0\<cr>" call assert_equal(10, winheight(0)) " Test for z. @@ -2849,35 +2850,10 @@ func Test_gr_command() enew! endfunc -" When splitting a window the changelist position is wrong. -" Test the changelist position after splitting a window. -" Test for the bug fixed by 7.4.386 -func Test_changelist() - let save_ul = &ul - enew! - call append('$', ['1', '2']) - exe "normal i\<C-G>u" - exe "normal Gkylpa\<C-G>u" - set ul=100 - exe "normal Gylpa\<C-G>u" - set ul=100 - normal gg - vsplit - normal g; - call assert_equal([3, 2], [line('.'), col('.')]) - normal g; - call assert_equal([2, 2], [line('.'), col('.')]) - call assert_fails('normal g;', 'E662:') - new - call assert_fails('normal g;', 'E664:') - %bwipe! - let &ul = save_ul -endfunc - func Test_nv_hat_count() %bwipeout! let l:nr = bufnr('%') + 1 - call assert_fails(':execute "normal! ' . l:nr . '\<C-^>"', 'E92') + call assert_fails(':execute "normal! ' . l:nr . '\<C-^>"', 'E92:') edit Xfoo let l:foo_nr = bufnr('Xfoo') diff --git a/src/nvim/testdir/test_options.vim b/src/nvim/testdir/test_options.vim index 1f003041e6..c5b1266689 100644 --- a/src/nvim/testdir/test_options.vim +++ b/src/nvim/testdir/test_options.vim @@ -1,6 +1,7 @@ " Test for options source check.vim +source view_util.vim func Test_whichwrap() set whichwrap=b,s @@ -48,7 +49,9 @@ func Test_pastetoggle() let &pastetoggle = str call assert_equal(str, &pastetoggle) call assert_equal("\n pastetoggle=" .. strtrans(str), execute('set pastetoggle?')) + unlet str + set pastetoggle& endfunc func Test_wildchar() @@ -782,7 +785,6 @@ endfunc func Test_rightleftcmd() CheckFeature rightleft set rightleft - set rightleftcmd let g:l = [] func AddPos() @@ -791,6 +793,13 @@ func Test_rightleftcmd() endfunc cmap <expr> <F2> AddPos() + set rightleftcmd= + call feedkeys("/\<F2>abc\<Right>\<F2>\<Left>\<Left>\<F2>" .. + \ "\<Right>\<F2>\<Esc>", 'xt') + call assert_equal([2, 5, 3, 4], g:l) + + let g:l = [] + set rightleftcmd=search call feedkeys("/\<F2>abc\<Left>\<F2>\<Right>\<Right>\<F2>" .. \ "\<Left>\<F2>\<Esc>", 'xt') call assert_equal([&co - 1, &co - 4, &co - 2, &co - 3], g:l) @@ -801,6 +810,14 @@ func Test_rightleftcmd() set rightleft& endfunc +" Test for the "debug" option +func Test_debug_option() + set debug=beep + exe "normal \<C-c>" + call assert_equal('Beep!', Screenline(&lines)) + set debug& +endfunc + " Test for setting option values using v:false and v:true func Test_opt_boolean() set number& diff --git a/src/nvim/testdir/test_popup.vim b/src/nvim/testdir/test_popup.vim index a5e4be49f4..0486ed83ad 100644 --- a/src/nvim/testdir/test_popup.vim +++ b/src/nvim/testdir/test_popup.vim @@ -955,6 +955,25 @@ func Test_menu_only_exists_in_terminal() endtry endfunc +" This used to crash before patch 8.1.1424 +func Test_popup_delete_when_shown() + CheckFeature menu + CheckNotGui + + func Func() + popup Foo + return "\<Ignore>" + endfunc + + nmenu Foo.Bar : + nnoremap <expr> <F2> Func() + call feedkeys("\<F2>\<F2>\<Esc>", 'xt') + + delfunc Func + nunmenu Foo.Bar + nunmap <F2> +endfunc + func Test_popup_complete_info_01() new inoremap <buffer><F5> <C-R>=complete_info().mode<CR> diff --git a/src/nvim/testdir/test_quickfix.vim b/src/nvim/testdir/test_quickfix.vim index ddd4229f17..f6d573d76b 100644 --- a/src/nvim/testdir/test_quickfix.vim +++ b/src/nvim/testdir/test_quickfix.vim @@ -172,6 +172,14 @@ func XlistTests(cchar) \ ' 2 Data.Text:20 col 10 warning 22: ModuleWarning', \ ' 3 Data/Text.hs:30 col 15 warning 33: FileWarning'], l) + " Very long line should be displayed. + let text = 'Line' .. repeat('1234567890', 130) + let lines = ['Xtestfile9:2:9:' .. text] + Xgetexpr lines + + let l = split(execute('Xlist', ''), "\n") + call assert_equal([' 1 Xtestfile9:2 col 9: ' .. text] , l) + " For help entries in the quickfix list, only the filename without directory " should be displayed Xhelpgrep setqflist() diff --git a/src/nvim/testdir/test_regexp_latin.vim b/src/nvim/testdir/test_regexp_latin.vim index 82d250e8b3..d08a980787 100644 --- a/src/nvim/testdir/test_regexp_latin.vim +++ b/src/nvim/testdir/test_regexp_latin.vim @@ -1027,4 +1027,15 @@ func Test_using_invalid_visual_position() bwipe! endfunc +func Test_recursive_substitute_expr() + new + func Repl() + s + endfunc + silent! s/\%')/~\=Repl() + + bwipe! + delfunc Repl +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_spell_utf8.vim b/src/nvim/testdir/test_spell_utf8.vim index b7e3da37cb..7c588d736a 100644 --- a/src/nvim/testdir/test_spell_utf8.vim +++ b/src/nvim/testdir/test_spell_utf8.vim @@ -820,5 +820,13 @@ func Test_check_empty_line() bwipe! endfunc +func Test_spell_suggest_too_long() + " this was creating a word longer than MAXWLEN + new + call setline(1, 'a' .. repeat("\u0333", 150)) + norm! z= + bwipe! +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_startup.vim b/src/nvim/testdir/test_startup.vim index d830f5216d..39fafbf7b4 100644 --- a/src/nvim/testdir/test_startup.vim +++ b/src/nvim/testdir/test_startup.vim @@ -12,6 +12,9 @@ func Test_startup_script() source $VIMRUNTIME/defaults.vim call assert_equal(0, &compatible) + " Restore some options, so that the following tests doesn't break + set nomore + set noshowmode endfunc " Verify the order in which plugins are loaded: @@ -814,6 +817,25 @@ func Test_v_argv() call assert_equal(['arg1', '--cmd', 'echo v:argv', '--cmd', 'q'']'], list[idx:]) endfunc +" Test for the "-r" recovery mode option +func Test_r_arg() + throw 'Skipped: Nvim has different directories' + " Can't catch the output of gvim. + CheckNotGui + CheckUnix + CheckEnglish + let cmd = GetVimCommand() + " There can be swap files anywhere, only check for the headers. + let expected =<< trim END + Swap files found:.* + In current directory:.* + In directory \~/tmp:.* + In directory /var/tmp:.* + In directory /tmp:.* + END + call assert_match(join(expected, ""), system(cmd .. " -r")->substitute("[\r\n]\\+", '', '')) +endfunc + " Test for the '-t' option to jump to a tag func Test_t_arg() let before =<< trim [CODE] @@ -824,6 +846,7 @@ func Test_t_arg() call writefile([s], "Xtestout") qall [CODE] + call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//", \ "first\tXfile1\t/^ \\zsfirst$/", \ "second\tXfile1\t/^ \\zssecond$/", @@ -890,6 +913,138 @@ func Test_x_arg() call delete('Xtest_x_arg') endfunc +" Test for entering the insert mode on startup +func Test_start_insertmode() + throw "Skipped: Nvim does not support setting 'insertmode'" + let before =<< trim [CODE] + set insertmode + [CODE] + let after =<< trim [CODE] + call writefile(['insertmode=' .. &insertmode], 'Xtestout') + qall + [CODE] + if RunVim(before, after, '') + call assert_equal(['insertmode=1'], readfile('Xtestout')) + call delete('Xtestout') + endif +endfunc + +" Test for enabling the binary mode on startup +func Test_b_arg() + let after =<< trim [CODE] + call writefile(['binary=' .. &binary], 'Xtestout') + qall + [CODE] + if RunVim([], after, '-b') + call assert_equal(['binary=1'], readfile('Xtestout')) + call delete('Xtestout') + endif +endfunc + +" Test for enabling the lisp mode on startup +func Test_l_arg() + let after =<< trim [CODE] + let s = 'lisp=' .. &lisp .. ', showmatch=' .. &showmatch + call writefile([s], 'Xtestout') + qall + [CODE] + if RunVim([], after, '-l') + call assert_equal(['lisp=1, showmatch=1'], readfile('Xtestout')) + call delete('Xtestout') + endif +endfunc + +" Test for specifying a non-existing vimrc file using "-u" +func Test_missing_vimrc() + if !CanRunVimInTerminal() + throw 'Skipped: cannot run vim in terminal' + endif + let after =<< trim [CODE] + call assert_match('^E282:', v:errmsg) + call writefile(v:errors, 'Xtestout') + [CODE] + call writefile(after, 'Xafter') + + let cmd = GetVimCommandCleanTerm() . ' -u Xvimrc_missing -S Xafter' + let buf = term_start(cmd, {'term_rows' : 10}) + call WaitForAssert({-> assert_equal("running", term_getstatus(buf))}) + call term_wait(buf) + call term_sendkeys(buf, "\n:") + call term_wait(buf) + call WaitForAssert({-> assert_match(':', term_getline(buf, 10))}) + call StopVimInTerminal(buf) + call assert_equal([], readfile('Xtestout')) + call delete('Xafter') + call delete('Xtestout') +endfunc + +" Test for using the $VIMINIT environment variable +func Test_VIMINIT() + let after =<< trim [CODE] + call assert_equal(1, exists('viminit_found')) + call assert_equal('yes', viminit_found) + call writefile(v:errors, 'Xtestout') + qall + [CODE] + call writefile(after, 'Xafter') + " let cmd = GetVimProg() . ' --not-a-term -S Xafter --cmd "set enc=utf8"' + let cmd = GetVimProg() . ' -S Xafter --cmd "set enc=utf8"' + call setenv('VIMINIT', 'let viminit_found="yes"') + exe "silent !" . cmd + call assert_equal([], readfile('Xtestout')) + call delete('Xtestout') + call delete('Xafter') +endfunc + +" Test for using the $EXINIT environment variable +func Test_EXINIT() + let after =<< trim [CODE] + call assert_equal(1, exists('exinit_found')) + call assert_equal('yes', exinit_found) + call writefile(v:errors, 'Xtestout') + qall + [CODE] + call writefile(after, 'Xafter') + " let cmd = GetVimProg() . ' --not-a-term -S Xafter --cmd "set enc=utf8"' + let cmd = GetVimProg() . ' -S Xafter --cmd "set enc=utf8"' + call setenv('EXINIT', 'let exinit_found="yes"') + exe "silent !" . cmd + call assert_equal([], readfile('Xtestout')) + call delete('Xtestout') + call delete('Xafter') +endfunc + +" Test for using the 'exrc' option +func Test_exrc() + let after =<< trim [CODE] + call assert_equal(1, &exrc) + call assert_equal(1, &secure) + call assert_equal(37, exrc_found) + call writefile(v:errors, 'Xtestout') + qall + [CODE] + call mkdir('Xdir') + call writefile(['let exrc_found=37'], 'Xdir/.exrc') + call writefile(after, 'Xdir/Xafter') + " let cmd = GetVimProg() . ' --not-a-term -S Xafter --cmd "cd Xdir" --cmd "set enc=utf8 exrc secure"' + let cmd = GetVimProg() . ' -S Xafter --cmd "cd Xdir" --cmd "set enc=utf8 exrc secure"' + exe "silent !" . cmd + call assert_equal([], readfile('Xdir/Xtestout')) + call delete('Xdir', 'rf') +endfunc + +" Test for starting Vim with a non-terminal as input/output +func Test_io_not_a_terminal() + throw 'Skipped: Nvim does not support --ttyfail' + " Can't catch the output of gvim. + CheckNotGui + CheckUnix + CheckEnglish + let l = systemlist(GetVimProg() .. ' --ttyfail') + call assert_equal(['Vim: Warning: Output is not to a terminal', + \ 'Vim: Warning: Input is not from a terminal'], l) +endfunc + " Test for --not-a-term avoiding escape codes. func Test_not_a_term() CheckUnix @@ -948,6 +1103,93 @@ func Test_w_arg() call delete('Xscriptin') endfunc +" Test for the "-s scriptin" argument +func Test_s_arg() + " Can't catch the output of gvim. + CheckNotGui + CheckEnglish + " Test for failing to open the script input file. + let m = system(GetVimCommand() .. " -s abcxyz") + " call assert_equal("Cannot open for reading: \"abcxyz\"\n", m) + call assert_equal("Cannot open for reading: \"abcxyz\": no such file or directory\n", m) + + call writefile([], 'Xinput') + let m = system(GetVimCommand() .. " -s Xinput -s Xinput") + call assert_equal("Attempt to open script file again: \"-s Xinput\"\n", m) + call delete('Xinput') +endfunc + +" Test for the "-n" (no swap file) argument +func Test_n_arg() + let after =<< trim [CODE] + call assert_equal(0, &updatecount) + call writefile(v:errors, 'Xtestout') + qall + [CODE] + if RunVim([], after, '-n') + call assert_equal([], readfile('Xtestout')) + call delete('Xtestout') + endif +endfunc + +" Test for the "-h" (help) argument +func Test_h_arg() + throw 'Skipped: Nvim has different output for "-h" argument' + " Can't catch the output of gvim. + CheckNotGui + let l = systemlist(GetVimProg() .. ' -h') + call assert_match('^VIM - Vi IMproved', l[0]) + let l = systemlist(GetVimProg() .. ' -?') + call assert_match('^VIM - Vi IMproved', l[0]) +endfunc + +" Test for the "-F" (farsi) argument +func Test_F_arg() + throw 'Skipped: Nvim does not recognize "-F" argument' + " Can't catch the output of gvim. + CheckNotGui + let l = systemlist(GetVimProg() .. ' -F') + call assert_match('^E27:', l[0]) +endfunc + +" Test for the "-E" (improved Ex mode) argument +func Test_E_arg() + let after =<< trim [CODE] + call assert_equal('cv', mode(1)) + call writefile(v:errors, 'Xtestout') + qall + [CODE] + if RunVim([], after, '-E') + call assert_equal([], readfile('Xtestout')) + call delete('Xtestout') + endif +endfunc + +" Test for the "-D" (debugger) argument +func Test_D_arg() + CheckRunVimInTerminal + + let cmd = GetVimCommandCleanTerm() .. ' -D' + let buf = term_start(cmd, {'term_rows' : 10}) + call WaitForAssert({-> assert_equal("running", term_getstatus(buf))}) + + call WaitForAssert({-> assert_equal('Entering Debug mode. Type "cont" to continue.', + \ term_getline(buf, 7))}) + call WaitForAssert({-> assert_equal('>', term_getline(buf, 10))}) + + call StopVimInTerminal(buf) +endfunc + +" Test for too many edit argument errors +func Test_too_many_edit_args() + throw 'Skipped: N/A' + " Can't catch the output of gvim. + CheckNotGui + CheckEnglish + let l = systemlist(GetVimProg() .. ' - -') + call assert_match('^Too many edit arguments: "-"', l[1]) +endfunc + " Test starting vim with various names: vim, ex, view, evim, etc. func Test_progname() CheckUnix diff --git a/src/nvim/testdir/test_statusline.vim b/src/nvim/testdir/test_statusline.vim index ec35fac964..6bde052442 100644 --- a/src/nvim/testdir/test_statusline.vim +++ b/src/nvim/testdir/test_statusline.vim @@ -11,6 +11,10 @@ func SetUp() set laststatus=2 endfunc +func TearDown() + set laststatus& +endfunc + func s:get_statusline() return ScreenLines(&lines - 1, &columns)[0] endfunc @@ -39,7 +43,6 @@ endfunc func Test_caught_error_in_statusline() let s:func_in_statusline_called = 0 - set laststatus=2 let statusline = '%{StatuslineWithCaughtError()}' let &statusline = statusline redrawstatus @@ -50,7 +53,6 @@ endfunc func Test_statusline_will_be_disabled_with_error() let s:func_in_statusline_called = 0 - set laststatus=2 let statusline = '%{StatuslineWithError()}' try let &statusline = statusline @@ -77,7 +79,6 @@ func Test_statusline() call assert_match('^ ((2) of 2)\s*$', s:get_statusline()) only - set laststatus=2 set splitbelow call setline(1, range(1, 10000)) @@ -436,7 +437,6 @@ func Test_statusline() %bw! call delete('Xstatusline') set statusline& - set laststatus& set splitbelow& endfunc @@ -524,7 +524,6 @@ endfunc " with a custom 'statusline' func Test_statusline_mbyte_fillchar() only - set laststatus=2 set fillchars=vert:\|,fold:-,stl:━,stlnc:═ set statusline=a%=b call assert_match('^a\+━\+b$', s:get_statusline()) @@ -532,7 +531,7 @@ func Test_statusline_mbyte_fillchar() call assert_match('^a\+━\+b━a\+═\+b$', s:get_statusline()) wincmd w call assert_match('^a\+═\+b═a\+━\+b$', s:get_statusline()) - set statusline& fillchars& laststatus& + set statusline& fillchars& %bw! endfunc diff --git a/src/nvim/testdir/test_syn_attr.vim b/src/nvim/testdir/test_syn_attr.vim index fa0b08fde5..88f9d0d84d 100644 --- a/src/nvim/testdir/test_syn_attr.vim +++ b/src/nvim/testdir/test_syn_attr.vim @@ -1,19 +1,39 @@ " Test syntax highlighting functions. func Test_missing_attr() - hi Mine cterm=italic + throw 'Skipped: use test/functional/legacy/syn_attr_spec.lua' + + hi Mine term=bold cterm=italic call assert_equal('Mine', synIDattr(hlID("Mine"), "name")) + call assert_equal('', synIDattr("Mine"->hlID(), "bg", 'term')) + call assert_equal('', synIDattr("Mine"->hlID(), "fg", 'term')) + call assert_equal('', synIDattr("Mine"->hlID(), "sp", 'term')) + call assert_equal('1', synIDattr(hlID("Mine"), "bold", 'term')) call assert_equal('1', synIDattr(hlID("Mine"), "italic", 'cterm')) - hi Mine cterm=inverse + hi Mine term=reverse cterm=inverse + call assert_equal('1', synIDattr(hlID("Mine"), "reverse", 'term')) call assert_equal('1', synIDattr(hlID("Mine"), "inverse", 'cterm')) - hi Mine cterm=standout gui=undercurl + + hi Mine term=underline cterm=standout gui=undercurl + call assert_equal('1', synIDattr(hlID("Mine"), "underline", 'term')) call assert_equal('1', synIDattr(hlID("Mine"), "standout", 'cterm')) call assert_equal('1', synIDattr("Mine"->hlID(), "undercurl", 'gui')) - hi Mine gui=strikethrough + + hi Mine term=underdouble cterm=underdotted gui=underdashed + call assert_equal('1', synIDattr(hlID("Mine"), "underdouble", 'term')) + call assert_equal('1', synIDattr(hlID("Mine"), "underdotted", 'cterm')) + call assert_equal('1', synIDattr("Mine"->hlID(), "underdashed", 'gui')) + + hi Mine term=nocombine gui=strikethrough call assert_equal('1', synIDattr(hlID("Mine"), "strikethrough", 'gui')) - hi Mine cterm=NONE gui=NONE + call assert_equal('1', synIDattr(hlID("Mine"), "nocombine", 'term')) + call assert_equal('', synIDattr(hlID("Mine"), "nocombine", 'gui')) + hi Mine term=NONE cterm=NONE gui=NONE + call assert_equal('', synIDattr(hlID("Mine"), "bold", 'term')) call assert_equal('', synIDattr(hlID("Mine"), "italic", 'cterm')) + call assert_equal('', synIDattr(hlID("Mine"), "reverse", 'term')) call assert_equal('', synIDattr(hlID("Mine"), "inverse", 'cterm')) + call assert_equal('', synIDattr(hlID("Mine"), "underline", 'term')) call assert_equal('', synIDattr(hlID("Mine"), "standout", 'cterm')) call assert_equal('', synIDattr(hlID("Mine"), "undercurl", 'gui')) call assert_equal('', synIDattr(hlID("Mine"), "strikethrough", 'gui')) diff --git a/src/nvim/testdir/test_termcodes.vim b/src/nvim/testdir/test_termcodes.vim index c0712aa892..eda485c512 100644 --- a/src/nvim/testdir/test_termcodes.vim +++ b/src/nvim/testdir/test_termcodes.vim @@ -33,28 +33,5 @@ func Test_special_term_keycodes() bw! endfunc -func Test_simplify_ctrl_at() - " feeding unsimplified CTRL-@ should still trigger i_CTRL-@ - call feedkeys("ifoo\<Esc>A\<*C-@>x", 'xt') - call assert_equal('foofo', getline(1)) - bw! -endfunc - -func Test_simplify_noremap() - call feedkeys("i\<*C-M>", 'nx') - call assert_equal('', getline(1)) - call assert_equal([0, 2, 1, 0, 1], getcurpos()) - bw! -endfunc - -func Test_simplify_timedout() - inoremap <C-M>a b - call feedkeys("i\<*C-M>", 'xt') - call assert_equal('', getline(1)) - call assert_equal([0, 2, 1, 0, 1], getcurpos()) - iunmap <C-M>a - bw! -endfunc - " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_textformat.vim b/src/nvim/testdir/test_textformat.vim index 970f5ae0d0..0fc56083aa 100644 --- a/src/nvim/testdir/test_textformat.vim +++ b/src/nvim/testdir/test_textformat.vim @@ -1260,6 +1260,41 @@ func Test_comment_nested() %bw! endfunc +" Test for a space character in 'comments' setting +func Test_comment_space() + new + setlocal comments=b:\ > fo+=ro + exe "normal i> B\nD\<C-C>ggOA\<C-C>joC" + exe "normal Go > F\nH\<C-C>kOE\<C-C>joG" + let expected =<< trim END + A + > B + C + D + > E + > F + > G + > H + END + call assert_equal(expected, getline(1, '$')) + %bw! +endfunc + +" Test for the 'O' flag in 'comments' +func Test_comment_O() + new + setlocal comments=Ob:* fo+=ro + exe "normal i* B\nD\<C-C>kOA\<C-C>joC" + let expected =<< trim END + A + * B + * C + * D + END + call assert_equal(expected, getline(1, '$')) + %bw! +endfunc + " Test for 'a' and 'w' flags in 'formatoptions' func Test_fo_a_w() new @@ -1299,6 +1334,25 @@ func Test_fo_a_w() %bw! endfunc +" Test for 'j' flag in 'formatoptions' +func Test_fo_j() + new + setlocal fo+=j comments=:// + call setline(1, ['i++; // comment1', ' // comment2']) + normal J + call assert_equal('i++; // comment1 comment2', getline(1)) + setlocal fo-=j + call setline(1, ['i++; // comment1', ' // comment2']) + normal J + call assert_equal('i++; // comment1 // comment2', getline(1)) + " Test with nested comments + setlocal fo+=j comments=n:>,n:) + call setline(1, ['i++; > ) > ) comment1', ' > ) comment2']) + normal J + call assert_equal('i++; > ) > ) comment1 comment2', getline(1)) + %bw! +endfunc + " Test for formatting lines using gq in visual mode func Test_visual_gq_format() new @@ -1480,4 +1534,16 @@ func Test_autoformat_comments() close! endfunc +" This was leaving the cursor after the end of a line. Complicated way to +" have the problem show up with valgrind. +func Test_correct_cursor_position() + " set encoding=iso8859 + new + norm a0000 + sil! norm gggg0i0gw0gg + + bwipe! + set encoding=utf8 +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_timers.vim b/src/nvim/testdir/test_timers.vim index 56a5ec96af..6adf503f14 100644 --- a/src/nvim/testdir/test_timers.vim +++ b/src/nvim/testdir/test_timers.vim @@ -337,7 +337,7 @@ endfunc " Test that the garbage collector isn't triggered if a timer callback invokes " vgetc(). -func Test_timer_nocatch_garbage_collect() +func Test_nocatch_timer_garbage_collect() " skipped: Nvim does not support test_garbagecollect_soon(), test_override() return " 'uptimetime. must be bigger than the timer timeout diff --git a/src/nvim/testdir/test_trycatch.vim b/src/nvim/testdir/test_trycatch.vim index 598402fafe..646594e482 100644 --- a/src/nvim/testdir/test_trycatch.vim +++ b/src/nvim/testdir/test_trycatch.vim @@ -2014,11 +2014,11 @@ endfunc " Test for verbose messages with :try :catch, and :finally {{{1 func Test_try_catch_verbose() " This test works only when the language is English - if v:lang != "C" && v:lang !~ '^[Ee]n' - return - endif + CheckEnglish set verbose=14 + + " Test for verbose messages displayed when an exception is caught redir => msg try echo i diff --git a/src/nvim/testdir/test_vimscript.vim b/src/nvim/testdir/test_vimscript.vim index 1323288676..de4629451b 100644 --- a/src/nvim/testdir/test_vimscript.vim +++ b/src/nvim/testdir/test_vimscript.vim @@ -1661,16 +1661,25 @@ func Test_compound_assignment_operators() call assert_equal(6, &scrolljump) let &scrolljump %= 5 call assert_equal(1, &scrolljump) - call assert_fails('let &scrolljump .= "j"', 'E734') + call assert_fails('let &scrolljump .= "j"', 'E734:') set scrolljump&vim + let &foldlevelstart = 2 + let &foldlevelstart -= 1 + call assert_equal(1, &foldlevelstart) + let &foldlevelstart -= 1 + call assert_equal(0, &foldlevelstart) + let &foldlevelstart = 2 + let &foldlevelstart -= 2 + call assert_equal(0, &foldlevelstart) + " Test for register let @/ = 1 - call assert_fails('let @/ += 1', 'E734') - call assert_fails('let @/ -= 1', 'E734') - call assert_fails('let @/ *= 1', 'E734') - call assert_fails('let @/ /= 1', 'E734') - call assert_fails('let @/ %= 1', 'E734') + call assert_fails('let @/ += 1', 'E734:') + call assert_fails('let @/ -= 1', 'E734:') + call assert_fails('let @/ *= 1', 'E734:') + call assert_fails('let @/ /= 1', 'E734:') + call assert_fails('let @/ %= 1', 'E734:') let @/ .= 's' call assert_equal('1s', @/) let @/ = '' diff --git a/src/nvim/testdir/test_window_cmd.vim b/src/nvim/testdir/test_window_cmd.vim index 7decac2c36..83a3216534 100644 --- a/src/nvim/testdir/test_window_cmd.vim +++ b/src/nvim/testdir/test_window_cmd.vim @@ -1074,7 +1074,6 @@ func Test_split_cmds_with_no_room() endfunc func Test_window_resize() - throw 'Skipped: Nvim supports cmdheight=0' " Vertical :resize (absolute, relative, min and max size). vsplit vert resize 8 @@ -1360,7 +1359,6 @@ func Test_win_move_separator() endfunc func Test_win_move_statusline() - redraw " This test fails in Nvim without a redraw to clear messages. edit a leftabove split b let h = winheight(0) @@ -1391,11 +1389,9 @@ func Test_win_move_statusline() call assert_equal(h0, winheight(0)) call assert_equal(1, &cmdheight) endfor - " Nvim supports cmdheight=0 + " supports cmdheight=0 set cmdheight=0 call assert_true(win_move_statusline(0, 1)) - "call assert_equal(h0, winheight(0)) - "call assert_equal(1, &cmdheight) call assert_equal(h0 + 1, winheight(0)) call assert_equal(0, &cmdheight) set cmdheight& diff --git a/src/nvim/testing.h b/src/nvim/testing.h index 1522ebc7b7..69596d725c 100644 --- a/src/nvim/testing.h +++ b/src/nvim/testing.h @@ -1,7 +1,6 @@ #ifndef NVIM_TESTING_H #define NVIM_TESTING_H -#include "nvim/eval/funcs.h" #include "nvim/eval/typval.h" #ifdef INCLUDE_GENERATED_DECLARATIONS diff --git a/src/nvim/types.h b/src/nvim/types.h index 73cd2204d6..00b9e6fc09 100644 --- a/src/nvim/types.h +++ b/src/nvim/types.h @@ -22,6 +22,8 @@ typedef int handle_T; // absent callback etc. typedef int LuaRef; +typedef void (*FunPtr)(void); + typedef handle_T NS; typedef struct expand expand_T; diff --git a/src/nvim/ui.c b/src/nvim/ui.c index a49e9df9ee..4fcfee1192 100644 --- a/src/nvim/ui.c +++ b/src/nvim/ui.c @@ -348,7 +348,8 @@ void ui_attach_impl(UI *ui, uint64_t chanid) if (ui_count == MAX_UI_COUNT) { abort(); } - if (!ui->ui_ext[kUIMultigrid] && !ui->ui_ext[kUIFloatDebug]) { + if (!ui->ui_ext[kUIMultigrid] && !ui->ui_ext[kUIFloatDebug] + && !ui_client_channel_id) { ui_comp_attach(ui); } @@ -502,6 +503,9 @@ handle_T ui_cursor_grid(void) void ui_flush(void) { + if (!ui_active()) { + return; + } cmdline_ui_flush(); win_ui_flush(); msg_ext_ui_flush(); @@ -608,6 +612,12 @@ bool ui_has(UIExtension ext) return ui_ext[ext]; } +/// Returns true if the UI has messages area. +bool ui_has_messages(void) +{ + return p_ch > 0 || ui_has(kUIMessages); +} + Array ui_array(void) { Array all_uis = ARRAY_DICT_INIT; diff --git a/src/nvim/ui_client.c b/src/nvim/ui_client.c index be01538f67..a586fec3bf 100644 --- a/src/nvim/ui_client.c +++ b/src/nvim/ui_client.c @@ -22,11 +22,6 @@ # include "ui_events_client.generated.h" #endif -// Temporary buffer for converting a single grid_line event -static size_t buf_size = 0; -static schar_T *buf_char = NULL; -static sattr_T *buf_attr = NULL; - void ui_client_init(uint64_t chan) { Array args = ARRAY_DICT_INIT; @@ -46,37 +41,23 @@ void ui_client_init(uint64_t chan) ui_client_channel_id = chan; } -/// Handler for "redraw" events sent by the NVIM server -/// -/// This function will be called by handle_request (in msgpack_rpc/channel.c) -/// The individual ui_events sent by the server are individually handled -/// by their respective handlers defined in ui_events_client.generated.h -/// -/// @note The "flush" event is called only once and only after handling all -/// the other events -/// @param channel_id: The id of the rpc channel -/// @param uidata: The dense array containing the ui_events sent by the server -/// @param[out] err Error details, if any -Object handle_ui_client_redraw(uint64_t channel_id, Array args, Error *error) +UIClientHandler ui_client_get_redraw_handler(const char *name, size_t name_len, Error *error) { - for (size_t i = 0; i < args.size; i++) { - Array call = args.items[i].data.array; - String name = call.items[0].data.string; - - int hash = ui_client_handler_hash(name.data, name.size); - if (hash < 0) { - ELOG("No ui client handler for %s", name.size ? name.data : "<empty>"); - continue; - } - UIClientHandler handler = event_handlers[hash]; - - // fprintf(stderr, "%s: %zu\n", name.data, call.size-1); - DLOG("Invoke ui client handler for %s", name.data); - for (size_t j = 1; j < call.size; j++) { - handler.fn(call.items[j].data.array); - } + int hash = ui_client_handler_hash(name, name_len); + if (hash < 0) { + return (UIClientHandler){ NULL, NULL }; } + return event_handlers[hash]; +} +/// Placeholder for _sync_ requests with 'redraw' method name +/// +/// async 'redraw' events, which are expected when nvim acts as an ui client. +/// get handled in msgpack_rpc/unpacker.c and directly dispatched to handlers +/// of specific ui events, like ui_client_event_grid_resize and so on. +Object handle_ui_client_redraw(uint64_t channel_id, Array args, Error *error) +{ + api_set_error(error, kErrorTypeValidation, "'redraw' cannot be sent as a request"); return NIL; } @@ -120,88 +101,30 @@ void ui_client_event_grid_resize(Array args) Integer height = args.items[2].data.integer; ui_call_grid_resize(grid, width, height); - if (buf_size < (size_t)width) { - xfree(buf_char); - xfree(buf_attr); - buf_size = (size_t)width; - buf_char = xmalloc(buf_size * sizeof(schar_T)); - buf_attr = xmalloc(buf_size * sizeof(sattr_T)); + if (grid_line_buf_size < (size_t)width) { + xfree(grid_line_buf_char); + xfree(grid_line_buf_attr); + grid_line_buf_size = (size_t)width; + grid_line_buf_char = xmalloc(grid_line_buf_size * sizeof(schar_T)); + grid_line_buf_attr = xmalloc(grid_line_buf_size * sizeof(sattr_T)); } } void ui_client_event_grid_line(Array args) + FUNC_ATTR_NORETURN { - if (args.size < 4 - || args.items[0].type != kObjectTypeInteger - || args.items[1].type != kObjectTypeInteger - || args.items[2].type != kObjectTypeInteger - || args.items[3].type != kObjectTypeArray) { - goto error; - } + abort(); // unreachable +} - Integer grid = args.items[0].data.integer; - Integer row = args.items[1].data.integer; - Integer startcol = args.items[2].data.integer; - Array cells = args.items[3].data.array; +void ui_client_event_raw_line(GridLineEvent *g) +{ + int grid = g->args[0], row = g->args[1], startcol = g->args[2]; + Integer endcol = startcol + g->coloff; + Integer clearcol = endcol + g->clear_width; // TODO(hlpr98): Accommodate other LineFlags when included in grid_line LineFlags lineflags = 0; - size_t j = 0; - int cur_attr = 0; - int clear_attr = 0; - int clear_width = 0; - for (size_t i = 0; i < cells.size; i++) { - if (cells.items[i].type != kObjectTypeArray) { - goto error; - } - Array cell = cells.items[i].data.array; - - if (cell.size < 1 || cell.items[0].type != kObjectTypeString) { - goto error; - } - String sstring = cell.items[0].data.string; - - char *schar = sstring.data; - int repeat = 1; - if (cell.size >= 2) { - if (cell.items[1].type != kObjectTypeInteger - || cell.items[1].data.integer < 0) { - goto error; - } - cur_attr = (int)cell.items[1].data.integer; - } - - if (cell.size >= 3) { - if (cell.items[2].type != kObjectTypeInteger - || cell.items[2].data.integer < 0) { - goto error; - } - repeat = (int)cell.items[2].data.integer; - } - - if (i == cells.size - 1 && sstring.size == 1 && sstring.data[0] == ' ' && repeat > 1) { - clear_width = repeat; - break; - } - - for (int r = 0; r < repeat; r++) { - if (j >= buf_size) { - goto error; // _YIKES_ - } - STRLCPY(buf_char[j], schar, sizeof(schar_T)); - buf_attr[j++] = cur_attr; - } - } - - Integer endcol = startcol + (int)j; - Integer clearcol = endcol + clear_width; - clear_attr = cur_attr; - - ui_call_raw_line(grid, row, startcol, endcol, clearcol, clear_attr, lineflags, - (const schar_T *)buf_char, (const sattr_T *)buf_attr); - return; - -error: - ELOG("Error handling ui event 'grid_line'"); + ui_call_raw_line(grid, row, startcol, endcol, clearcol, g->cur_attr, lineflags, + (const schar_T *)grid_line_buf_char, grid_line_buf_attr); } diff --git a/src/nvim/ui_client.h b/src/nvim/ui_client.h index 41d9fa6227..311dafaa0b 100644 --- a/src/nvim/ui_client.h +++ b/src/nvim/ui_client.h @@ -2,12 +2,18 @@ #define NVIM_UI_CLIENT_H #include "nvim/api/private/defs.h" +#include "nvim/grid_defs.h" typedef struct { const char *name; void (*fn)(Array args); } UIClientHandler; +// Temporary buffer for converting a single grid_line event +EXTERN size_t grid_line_buf_size INIT(= 0); +EXTERN schar_T *grid_line_buf_char INIT(= NULL); +EXTERN sattr_T *grid_line_buf_attr INIT(= NULL); + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "ui_client.h.generated.h" diff --git a/src/nvim/undo.c b/src/nvim/undo.c index 8324db37c6..45c083b034 100644 --- a/src/nvim/undo.c +++ b/src/nvim/undo.c @@ -2765,7 +2765,7 @@ void ex_undolist(exarg_T *eap) if (GA_EMPTY(&ga)) { msg(_("Nothing to undo")); } else { - sort_strings((char_u **)ga.ga_data, ga.ga_len); + sort_strings(ga.ga_data, ga.ga_len); msg_start(); msg_puts_attr(_("number changes when saved"), diff --git a/src/nvim/window.c b/src/nvim/window.c index 2bffe2055f..abb277bd23 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -13,6 +13,7 @@ #include "nvim/diff.h" #include "nvim/edit.h" #include "nvim/eval.h" +#include "nvim/eval/vars.h" #include "nvim/ex_cmds.h" #include "nvim/ex_cmds2.h" #include "nvim/ex_docmd.h" @@ -171,7 +172,7 @@ void do_window(int nchar, long Prenum, int xchar) CHECK_CMDWIN; reset_VIsual_and_resel(); // stop Visual mode - if (buflist_findnr(Prenum == 0 ? curwin->w_alt_fnum : Prenum) == NULL) { + if (buflist_findnr(Prenum == 0 ? curwin->w_alt_fnum : (int)Prenum) == NULL) { if (Prenum == 0) { emsg(_(e_noalt)); } else { @@ -181,7 +182,7 @@ void do_window(int nchar, long Prenum, int xchar) } if (!curbuf_locked() && win_split(0, 0) == OK) { - (void)buflist_getfile(Prenum == 0 ? curwin->w_alt_fnum : Prenum, + (void)buflist_getfile(Prenum == 0 ? curwin->w_alt_fnum : (int)Prenum, (linenr_T)0, GETF_ALT, false); } break; @@ -451,9 +452,9 @@ newwindow: case '}': CHECK_CMDWIN; if (Prenum) { - g_do_tagpreview = Prenum; + g_do_tagpreview = (int)Prenum; } else { - g_do_tagpreview = p_pvh; + g_do_tagpreview = (int)p_pvh; } FALLTHROUGH; case ']': @@ -461,7 +462,7 @@ newwindow: CHECK_CMDWIN; // Keep visual mode, can select words to use as a tag. if (Prenum) { - postponed_split = Prenum; + postponed_split = (int)Prenum; } else { postponed_split = -1; } @@ -550,16 +551,16 @@ wingotofile: case '}': xchar = Ctrl_RSB; if (Prenum) { - g_do_tagpreview = Prenum; + g_do_tagpreview = (int)Prenum; } else { - g_do_tagpreview = p_pvh; + g_do_tagpreview = (int)p_pvh; } FALLTHROUGH; case ']': case Ctrl_RSB: // Keep visual mode, can select words to use as a tag. if (Prenum) { - postponed_split = Prenum; + postponed_split = (int)Prenum; } else { postponed_split = -1; } @@ -725,31 +726,31 @@ void win_set_minimal_style(win_T *wp) wp->w_p_fcs = ((*old == NUL) ? (char_u *)xstrdup("eob: ") : concat_str(old, (char_u *)",eob: ")); - xfree(old); + free_string_option(old); } if (wp->w_hl_ids[HLF_EOB] != -1) { char_u *old = wp->w_p_winhl; wp->w_p_winhl = ((*old == NUL) ? (char_u *)xstrdup("EndOfBuffer:") : concat_str(old, (char_u *)",EndOfBuffer:")); - xfree(old); + free_string_option(old); } // signcolumn: use 'auto' if (wp->w_p_scl[0] != 'a' || STRLEN(wp->w_p_scl) >= 8) { - xfree(wp->w_p_scl); + free_string_option(wp->w_p_scl); wp->w_p_scl = (char_u *)xstrdup("auto"); } // foldcolumn: use '0' if (wp->w_p_fdc[0] != '0') { - xfree(wp->w_p_fdc); + free_string_option(wp->w_p_fdc); wp->w_p_fdc = (char_u *)xstrdup("0"); } // colorcolumn: cleared if (wp->w_p_cc != NULL && *wp->w_p_cc != NUL) { - xfree(wp->w_p_cc); + free_string_option(wp->w_p_cc); wp->w_p_cc = (char_u *)xstrdup(""); } } @@ -799,8 +800,8 @@ void win_config_float(win_T *wp, FloatConfig fconfig) // compute initial position if (wp->w_float_config.relative == kFloatRelativeWindow) { - int row = wp->w_float_config.row; - int col = wp->w_float_config.col; + int row = (int)wp->w_float_config.row; + int col = (int)wp->w_float_config.col; Error dummy = ERROR_INIT; win_T *parent = find_window_by_handle(wp->w_float_config.window, &dummy); if (parent) { @@ -824,8 +825,8 @@ void win_config_float(win_T *wp, FloatConfig fconfig) wp->w_winrow = row; wp->w_wincol = col; } else { - wp->w_winrow = fconfig.row; - wp->w_wincol = fconfig.col; + wp->w_winrow = (int)fconfig.row; + wp->w_wincol = (int)fconfig.col; } // changing border style while keeping border only requires redrawing border @@ -1064,10 +1065,10 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) * width. */ // Current window requires at least 1 space. - wmw1 = (p_wmw == 0 ? 1 : p_wmw); + wmw1 = (p_wmw == 0 ? 1 : (int)p_wmw); needed = wmw1 + 1; if (flags & WSP_ROOM) { - needed += p_wiw - wmw1; + needed += (int)p_wiw - wmw1; } if (flags & (WSP_BOT | WSP_TOP)) { minwidth = frame_minwidth(topframe, NOWIN); @@ -1141,10 +1142,10 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) // Check if we are able to split the current window and compute its height. // Current window requires at least 1 space plus space for the window bar. - wmh1 = MAX(p_wmh, 1) + oldwin->w_winbar_height; + wmh1 = MAX((int)p_wmh, 1) + oldwin->w_winbar_height; needed = wmh1 + STATUS_HEIGHT; if (flags & WSP_ROOM) { - needed += p_wh - wmh1 + oldwin->w_winbar_height; + needed += (int)p_wh - wmh1 + oldwin->w_winbar_height; } if (p_ch < 1) { needed += 1; // Adjust for cmdheight=0. @@ -1304,7 +1305,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) // Need to create a new frame in the tree to make a branch. frp = xcalloc(1, sizeof(frame_T)); *frp = *curfrp; - curfrp->fr_layout = layout; + curfrp->fr_layout = (char)layout; frp->fr_parent = curfrp; frp->fr_next = NULL; frp->fr_prev = NULL; @@ -1489,20 +1490,17 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) // Don't change the window height/width to 'winheight' / 'winwidth' if a // size was given. if (flags & WSP_VERT) { - i = p_wiw; + i = (int)p_wiw; if (size != 0) { p_wiw = size; } } else { - i = p_wh; + i = (int)p_wh; if (size != 0) { p_wh = size; } } - // Keep same changelist position in new window. - wp->w_changelistidx = oldwin->w_changelistidx; - // make the new window the current window win_enter_ext(wp, WEE_TRIGGER_NEW_AUTOCMDS | WEE_TRIGGER_ENTER_AUTOCMDS | WEE_TRIGGER_LEAVE_AUTOCMDS); @@ -1573,6 +1571,10 @@ static void win_init(win_T *newp, win_T *oldp, int flags) } newp->w_tagstackidx = oldp->w_tagstackidx; newp->w_tagstacklen = oldp->w_tagstacklen; + + // Keep same changelist position in new window. + newp->w_changelistidx = oldp->w_changelistidx; + copyFoldingState(oldp, newp); win_init_some(newp, oldp); @@ -1687,14 +1689,14 @@ int make_windows(int count, bool vertical) if (vertical) { // Each window needs at least 'winminwidth' lines and a separator column. - maxcount = (curwin->w_width + curwin->w_vsep_width - - (p_wiw - p_wmw)) / (p_wmw + 1); + maxcount = (int)(curwin->w_width + curwin->w_vsep_width + - (p_wiw - p_wmw)) / ((int)p_wmw + 1); } else { // Each window needs at least 'winminheight' lines. // If statusline isn't global, each window also needs a statusline. // If 'winbar' is set, each window also needs a winbar. - maxcount = (curwin->w_height + curwin->w_hsep_height + curwin->w_status_height - - (p_wh - p_wmh)) / (p_wmh + STATUS_HEIGHT + global_winbar_height()); + maxcount = (int)(curwin->w_height + curwin->w_hsep_height + curwin->w_status_height + - (p_wh - p_wmh)) / ((int)p_wmh + STATUS_HEIGHT + global_winbar_height()); } if (maxcount < 2) { @@ -2037,10 +2039,10 @@ void win_move_after(win_T *win1, win_T *win2) static int get_maximum_wincount(frame_T *fr, int height) { if (fr->fr_layout != FR_COL) { - return (height / (p_wmh + STATUS_HEIGHT + frame2win(fr)->w_winbar_height)); + return (height / ((int)p_wmh + STATUS_HEIGHT + frame2win(fr)->w_winbar_height)); } else if (global_winbar_height()) { // If winbar is globally enabled, no need to check each window for it. - return (height / (p_wmh + STATUS_HEIGHT + 1)); + return (height / ((int)p_wmh + STATUS_HEIGHT + 1)); } frame_T *frp; @@ -2053,13 +2055,13 @@ static int get_maximum_wincount(frame_T *fr, int height) if (height < (p_wmh + STATUS_HEIGHT + wp->w_winbar_height)) { break; } - height -= p_wmh + STATUS_HEIGHT + wp->w_winbar_height; + height -= (int)p_wmh + STATUS_HEIGHT + wp->w_winbar_height; total_wincount += 1; } // If we still have enough room for more windows, just use the default winbar height (which is 0) // in order to get the amount of windows that'd fit in the remaining space - total_wincount += height / (p_wmh + STATUS_HEIGHT); + total_wincount += height / ((int)p_wmh + STATUS_HEIGHT); return total_wincount; } @@ -2132,7 +2134,7 @@ static void win_equal_rec(win_T *next_curwin, bool current, frame_T *topfr, int } else { extra_sep = 0; } - totwincount = (n + extra_sep) / (p_wmw + 1); + totwincount = (n + extra_sep) / ((int)p_wmw + 1); has_next_curwin = frame_has_win(topfr, next_curwin); /* @@ -2143,29 +2145,27 @@ static void win_equal_rec(win_T *next_curwin, bool current, frame_T *topfr, int m = frame_minwidth(topfr, next_curwin); room = width - m; if (room < 0) { - next_curwin_size = p_wiw + room; + next_curwin_size = (int)p_wiw + room; room = 0; } else { next_curwin_size = -1; FOR_ALL_FRAMES(fr, topfr->fr_child) { - // If 'winfixwidth' set keep the window width if - // possible. - // Watch out for this window being the next_curwin. if (!frame_fixed_width(fr)) { continue; } + // If 'winfixwidth' set keep the window width if possible. + // Watch out for this window being the next_curwin. n = frame_minwidth(fr, NOWIN); new_size = fr->fr_width; if (frame_has_win(fr, next_curwin)) { - room += p_wiw - p_wmw; + room += (int)p_wiw - (int)p_wmw; next_curwin_size = 0; if (new_size < p_wiw) { - new_size = p_wiw; + new_size = (int)p_wiw; } } else { // These windows don't use up room. - totwincount -= (n + (fr->fr_next == NULL - ? extra_sep : 0)) / (p_wmw + 1); + totwincount -= (n + (fr->fr_next == NULL ? extra_sep : 0)) / ((int)p_wmw + 1); } room -= new_size - n; if (room < 0) { @@ -2182,12 +2182,12 @@ static void win_equal_rec(win_T *next_curwin, bool current, frame_T *topfr, int / (totwincount - 1) > p_wiw) { // Can make all windows wider than 'winwidth', spread // the room equally. - next_curwin_size = (room + p_wiw - + (totwincount - 1) * p_wmw - + (totwincount - 1)) / totwincount; - room -= next_curwin_size - p_wiw; + next_curwin_size = (int)(room + p_wiw + + (totwincount - 1) * p_wmw + + (totwincount - 1)) / totwincount; + room -= next_curwin_size - (int)p_wiw; } else { - next_curwin_size = p_wiw; + next_curwin_size = (int)p_wiw; } } } @@ -2210,8 +2210,7 @@ static void win_equal_rec(win_T *next_curwin, bool current, frame_T *topfr, int } else { // Compute the maximum number of windows horiz. in "fr". n = frame_minwidth(fr, NOWIN); - wincount = (n + (fr->fr_next == NULL ? extra_sep : 0)) - / (p_wmw + 1); + wincount = (n + (fr->fr_next == NULL ? extra_sep : 0)) / ((int)p_wmw + 1); m = frame_minwidth(fr, next_curwin); if (has_next_curwin) { hnc = frame_has_win(fr, next_curwin); @@ -2227,7 +2226,7 @@ static void win_equal_rec(win_T *next_curwin, bool current, frame_T *topfr, int new_size = (wincount * room + (totwincount / 2)) / totwincount; } if (hnc) { // add next_curwin size - next_curwin_size -= p_wiw - (m - n); + next_curwin_size -= (int)p_wiw - (m - n); new_size += next_curwin_size; room -= new_size - next_curwin_size; } else { @@ -2276,24 +2275,23 @@ static void win_equal_rec(win_T *next_curwin, bool current, frame_T *topfr, int if (room < 0) { // The room is less than 'winheight', use all space for the // current window. - next_curwin_size = p_wh + room; + next_curwin_size = (int)p_wh + room; room = 0; } else { next_curwin_size = -1; FOR_ALL_FRAMES(fr, topfr->fr_child) { - // If 'winfixheight' set keep the window height if - // possible. - // Watch out for this window being the next_curwin. if (!frame_fixed_height(fr)) { continue; } + // If 'winfixheight' set keep the window height if possible. + // Watch out for this window being the next_curwin. n = frame_minheight(fr, NOWIN); new_size = fr->fr_height; if (frame_has_win(fr, next_curwin)) { - room += p_wh - p_wmh; + room += (int)p_wh - (int)p_wmh; next_curwin_size = 0; if (new_size < p_wh) { - new_size = p_wh; + new_size = (int)p_wh; } } else { // These windows don't use up room. @@ -2314,12 +2312,12 @@ static void win_equal_rec(win_T *next_curwin, bool current, frame_T *topfr, int / (totwincount - 1) > p_wh) { // can make all windows higher than 'winheight', // spread the room equally. - next_curwin_size = (room + p_wh - + (totwincount - 1) * p_wmh - + (totwincount - 1)) / totwincount; - room -= next_curwin_size - p_wh; + next_curwin_size = (int)(room + p_wh + + (totwincount - 1) * p_wmh + + (totwincount - 1)) / totwincount; + room -= next_curwin_size - (int)p_wh; } else { - next_curwin_size = p_wh; + next_curwin_size = (int)p_wh; } } } @@ -2358,7 +2356,7 @@ static void win_equal_rec(win_T *next_curwin, bool current, frame_T *topfr, int new_size = (wincount * room + (totwincount / 2)) / totwincount; } if (hnc) { // add next_curwin size - next_curwin_size -= p_wh - (m - n); + next_curwin_size -= (int)p_wh - (m - n); new_size += next_curwin_size; room -= new_size - next_curwin_size; } else { @@ -3778,9 +3776,9 @@ static int frame_minheight(frame_T *topfrp, win_T *next_curwin) + topfrp->fr_win->w_status_height; if (topfrp->fr_win == next_curwin) { - m = p_wh + extra_height; + m = (int)p_wh + extra_height; } else { - m = p_wmh + extra_height; + m = (int)p_wmh + extra_height; if (topfrp->fr_win == curwin && next_curwin == NULL) { // Current window is minimal one line high. if (p_wmh == 0) { @@ -3821,10 +3819,10 @@ static int frame_minwidth(frame_T *topfrp, win_T *next_curwin) if (topfrp->fr_win != NULL) { if (topfrp->fr_win == next_curwin) { - m = p_wiw + topfrp->fr_win->w_vsep_width; + m = (int)p_wiw + topfrp->fr_win->w_vsep_width; } else { // window: minimal width of the window plus separator column - m = p_wmw + topfrp->fr_win->w_vsep_width; + m = (int)p_wmw + topfrp->fr_win->w_vsep_width; // Current window is minimal one column wide if (p_wmw == 0 && topfrp->fr_win == curwin && next_curwin == NULL) { ++m; @@ -3903,9 +3901,7 @@ void close_others(int message, int forceit) continue; } } - win_close(wp, - !buf_hide(wp->w_buffer) && !bufIsChanged(wp->w_buffer), - false); + win_close(wp, !buf_hide(wp->w_buffer) && !bufIsChanged(wp->w_buffer), false); } if (message && !ONE_WINDOW) { @@ -3981,7 +3977,7 @@ static int win_alloc_firstwin(win_T *oldwin) new_frame(curwin); topframe = curwin->w_frame; topframe->fr_width = Columns; - topframe->fr_height = Rows - p_ch - global_stl_height(); + topframe->fr_height = Rows - (int)p_ch - global_stl_height(); return OK; } @@ -4003,11 +3999,11 @@ static void new_frame(win_T *wp) */ void win_init_size(void) { - firstwin->w_height = ROWS_AVAIL; + firstwin->w_height = (int)ROWS_AVAIL; firstwin->w_height_inner = firstwin->w_height - firstwin->w_winbar_height; firstwin->w_height_outer = firstwin->w_height; firstwin->w_winrow_off = firstwin->w_winbar_height; - topframe->fr_height = ROWS_AVAIL; + topframe->fr_height = (int)ROWS_AVAIL; firstwin->w_width = Columns; firstwin->w_width_inner = firstwin->w_width; firstwin->w_width_outer = firstwin->w_width; @@ -4116,7 +4112,6 @@ int win_new_tabpage(int after, char_u *filename) newtp->tp_topframe = topframe; last_status(false); - set_winbar(); redraw_all_later(NOT_VALID); @@ -4167,7 +4162,7 @@ int make_tabpages(int maxcount) // Limit to 'tabpagemax' tabs. if (count > p_tpm) { - count = p_tpm; + count = (int)p_tpm; } /* @@ -4303,7 +4298,7 @@ static int leave_tabpage(buf_T *new_curbuf, bool trigger_leave_autocmds) tp->tp_prevwin = prevwin; tp->tp_firstwin = firstwin; tp->tp_lastwin = lastwin; - tp->tp_old_Rows = Rows; + tp->tp_old_Rows_avail = ROWS_AVAIL; tp->tp_old_Columns = Columns; firstwin = NULL; lastwin = NULL; @@ -4343,10 +4338,7 @@ static void enter_tabpage(tabpage_T *tp, buf_T *old_curbuf, bool trigger_enter_a const int row = win_comp_pos(); // recompute w_winrow for all windows diff_need_scrollbind = true; - // The tabpage line may have appeared or disappeared, may need to resize - // the frames for that. When the Vim window was resized need to update - // frame sizes too. Use the stored value of p_ch, so that it can be - // different for each tab page. + // Use the stored value of p_ch, so that it can be different for each tab page. if (p_ch != curtab->tp_ch_used) { clear_cmdline = true; } @@ -4359,7 +4351,9 @@ static void enter_tabpage(tabpage_T *tp, buf_T *old_curbuf, bool trigger_enter_a clear_cmdline = true; } - if (curtab->tp_old_Rows != Rows || (old_off != firstwin->w_winrow)) { + // The tabpage line may have appeared or disappeared, may need to resize the frames for that. + // When the Vim window was resized or ROWS_AVAIL changed need to update frame sizes too. + if (curtab->tp_old_Rows_avail != ROWS_AVAIL || (old_off != firstwin->w_winrow)) { win_new_screen_rows(); } if (curtab->tp_old_Columns != Columns && starting == 0) { @@ -5118,10 +5112,10 @@ static void win_free(win_T *wp, tabpage_T *tp) xfree(wp->w_localdir); xfree(wp->w_prevdir); - stl_clear_click_defs(wp->w_status_click_defs, wp->w_status_click_defs_size); + stl_clear_click_defs(wp->w_status_click_defs, (long)wp->w_status_click_defs_size); xfree(wp->w_status_click_defs); - stl_clear_click_defs(wp->w_winbar_click_defs, wp->w_winbar_click_defs_size); + stl_clear_click_defs(wp->w_winbar_click_defs, (long)wp->w_winbar_click_defs_size); xfree(wp->w_winbar_click_defs); // Remove the window from the b_wininfo lists, it may happen that the @@ -5503,7 +5497,7 @@ void win_setheight_win(int height, win_T *win) { // Always keep current window at least one line high, even when 'winminheight' is zero. // Keep window at least two lines high if 'winbar' is enabled. - height = MAX(height, (win == curwin ? MAX(p_wmh, 1) : p_wmh) + win->w_winbar_height); + height = MAX(height, (int)(win == curwin ? MAX(p_wmh, 1) : p_wmh) + win->w_winbar_height); if (win->w_floating) { win->w_float_config.height = height; @@ -5566,7 +5560,7 @@ static void frame_setheight(frame_T *curfrp, int height) // If height is greater than the available space, try to create space for // the frame by reducing 'cmdheight' if possible, while making sure // `cmdheight` doesn't go below 1. - height = MIN((p_ch > 0 ? ROWS_AVAIL + (p_ch - 1) : ROWS_AVAIL), height); + height = (int)MIN((p_ch > 0 ? ROWS_AVAIL + (p_ch - 1) : ROWS_AVAIL), height); } if (height > 0) { frame_new_height(curfrp, height, false, false); @@ -5608,7 +5602,7 @@ static void frame_setheight(frame_T *curfrp, int height) room_cmdline = 0; } else { win_T *wp = lastwin_nofloating(); - room_cmdline = Rows - p_ch - global_stl_height() + room_cmdline = Rows - (int)p_ch - global_stl_height() - (wp->w_winrow + wp->w_height + wp->w_hsep_height + wp->w_status_height); if (room_cmdline < 0) { room_cmdline = 0; @@ -5718,7 +5712,7 @@ void win_setwidth_win(int width, win_T *wp) // 'winminwidth' is zero. if (wp == curwin) { if (width < p_wmw) { - width = p_wmw; + width = (int)p_wmw; } if (width == 0) { width = 1; @@ -5882,8 +5876,8 @@ void win_setminheight(void) // loop until there is a 'winminheight' that is possible while (p_wmh > 0) { - const int room = Rows - p_ch; - const int needed = min_rows() - 1; // 1 was added for the cmdline + const int room = Rows - (int)p_ch; + const int needed = min_rows(); if (room >= needed) { break; } @@ -5973,7 +5967,7 @@ void win_drag_status_line(win_T *dragwin, int offset) // Only dragging the last status line can reduce p_ch. room = Rows - cmdline_row; if (curfr->fr_next != NULL) { - room -= p_ch + global_stl_height(); + room -= (int)p_ch + global_stl_height(); } if (room < 0) { room = 0; @@ -6151,8 +6145,7 @@ void set_fraction(win_T *wp) // When cursor is in the first line the percentage is computed as if // it's halfway that line. Thus with two lines it is 25%, with three // lines 17%, etc. Similarly for the last line: 75%, 83%, etc. - wp->w_fraction = ((long)wp->w_wrow * FRACTION_MULT + FRACTION_MULT / 2) - / (long)wp->w_height_inner; + wp->w_fraction = (int)(wp->w_wrow * FRACTION_MULT + FRACTION_MULT / 2) / wp->w_height_inner; } } @@ -6199,7 +6192,7 @@ void scroll_to_fraction(win_T *wp, int prev_height) if (lnum < 1) { // can happen when starting up lnum = 1; } - wp->w_wrow = ((long)wp->w_fraction * (long)height - 1L) / FRACTION_MULT; + wp->w_wrow = (int)((long)wp->w_fraction * (long)height - 1L) / FRACTION_MULT; line_size = plines_win_col(wp, lnum, (long)(wp->w_cursor.col)) - 1; sline = wp->w_wrow - line_size; @@ -6314,7 +6307,8 @@ void win_set_inner_size(win_T *wp) // There is no point in adjusting the scroll position when exiting. Some // values might be invalid. - if (!exiting) { + // Skip scroll_to_fraction() when 'cmdheight' was set to one from zero. + if (!exiting && !made_cmdheight_nonzero) { scroll_to_fraction(wp, prev_height); } redraw_later(wp, NOT_VALID); // SOME_VALID?? @@ -6384,7 +6378,7 @@ void command_height(void) { int h; frame_T *frp; - int old_p_ch = curtab->tp_ch_used; + int old_p_ch = (int)curtab->tp_ch_used; // Use the value of p_ch that we remembered. This is needed for when the // GUI starts up, we can't be sure in what order things happen. And when @@ -6404,7 +6398,7 @@ void command_height(void) } if (starting != NO_SCREEN) { - cmdline_row = Rows - p_ch; + cmdline_row = Rows - (int)p_ch; if (p_ch > old_p_ch) { // p_ch got bigger while (p_ch > old_p_ch) { @@ -6412,12 +6406,12 @@ void command_height(void) emsg(_(e_noroom)); p_ch = old_p_ch; curtab->tp_ch_used = p_ch; - cmdline_row = Rows - p_ch; + cmdline_row = Rows - (int)p_ch; break; } h = frp->fr_height - frame_minheight(frp, NULL); if (h > p_ch - old_p_ch) { - h = p_ch - old_p_ch; + h = (int)p_ch - old_p_ch; } old_p_ch += h; frame_add_height(frp, -h); @@ -6481,9 +6475,9 @@ char_u *grab_file_name(long count, linenr_T *file_lnum) } // Only recognize ":123" here if (file_lnum != NULL && ptr[len] == ':' && isdigit(ptr[len + 1])) { - char_u *p = ptr + len + 1; + char *p = (char *)ptr + len + 1; - *file_lnum = getdigits_long(&p, false, 0); + *file_lnum = (linenr_T)getdigits_long(&p, false, 0); } return find_file_name_in_path(ptr, len, options, count, (char_u *)curbuf->b_ffname); } @@ -6607,7 +6601,7 @@ char_u *file_name_in_line(char_u *line, int col, int options, long count, char_u } p = skipwhite(p); if (isdigit(*p)) { - *file_lnum = getdigits_long((char_u **)&p, false, 0); + *file_lnum = (linenr_T)getdigits_long(&p, false, 0); } } } @@ -6637,7 +6631,7 @@ static void win_remove_status_line(win_T *wp, bool add_hsep) } comp_col(); - stl_clear_click_defs(wp->w_status_click_defs, wp->w_status_click_defs_size); + stl_clear_click_defs(wp->w_status_click_defs, (long)wp->w_status_click_defs_size); xfree(wp->w_status_click_defs); wp->w_status_click_defs_size = 0; wp->w_status_click_defs = NULL; @@ -6745,34 +6739,50 @@ static void last_status_rec(frame_T *fr, bool statusline, bool is_stl_global) } } -// Add or remove window bars from windows depending on the value of 'winbar'. -void set_winbar(void) -{ - FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { - // Require the local value to be set in order to show winbar on a floating window. - int winbar_height = wp->w_floating ? ((*wp->w_p_wbr != NUL) ? 1 : 0) - : ((*p_wbr != NUL || *wp->w_p_wbr != NUL) ? 1 : 0); - - if (wp->w_winbar_height != winbar_height) { - if (winbar_height == 1 && wp->w_height_inner <= 1) { - if (wp->w_floating) { - emsg(_(e_noroom)); - continue; - } else if (!resize_frame_for_winbar(wp->w_frame)) { - return; - } +/// Add or remove window bar from window "wp". +/// +/// @param make_room Whether to resize frames to make room for winbar. +/// +/// @return Success status. +int set_winbar_win(win_T *wp, bool make_room) +{ + // Require the local value to be set in order to show winbar on a floating window. + int winbar_height = wp->w_floating ? ((*wp->w_p_wbr != NUL) ? 1 : 0) + : ((*p_wbr != NUL || *wp->w_p_wbr != NUL) ? 1 : 0); + + if (wp->w_winbar_height != winbar_height) { + if (winbar_height == 1 && wp->w_height_inner <= 1) { + if (wp->w_floating) { + emsg(_(e_noroom)); + return NOTDONE; + } else if (!make_room || !resize_frame_for_winbar(wp->w_frame)) { + return FAIL; } - wp->w_winbar_height = winbar_height; - win_set_inner_size(wp); - wp->w_redr_status = wp->w_redr_status || winbar_height; + } + wp->w_winbar_height = winbar_height; + win_set_inner_size(wp); + wp->w_redr_status = wp->w_redr_status || winbar_height; - if (winbar_height == 0) { - // When removing winbar, deallocate the w_winbar_click_defs array - stl_clear_click_defs(wp->w_winbar_click_defs, wp->w_winbar_click_defs_size); - xfree(wp->w_winbar_click_defs); - wp->w_winbar_click_defs_size = 0; - wp->w_winbar_click_defs = NULL; - } + if (winbar_height == 0) { + // When removing winbar, deallocate the w_winbar_click_defs array + stl_clear_click_defs(wp->w_winbar_click_defs, (long)wp->w_winbar_click_defs_size); + xfree(wp->w_winbar_click_defs); + wp->w_winbar_click_defs_size = 0; + wp->w_winbar_click_defs = NULL; + } + } + + return OK; +} + +/// Add or remove window bars from all windows in tab depending on the value of 'winbar'. +/// +/// @param make_room Whether to resize frames to make room for winbar. +void set_winbar(bool make_room) +{ + FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { + if (set_winbar_win(wp, make_room) == FAIL) { + break; } } } @@ -6821,7 +6831,9 @@ int min_rows(void) } } total += tabline_height() + global_stl_height(); - total += 1; // count the room for the command line + if (p_ch > 0) { + total += 1; // count the room for the command line + } return total; } @@ -6846,43 +6858,67 @@ bool only_one_window(void) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT return count <= 1; } -/// Correct the cursor line number in other windows. Used after changing the -/// current buffer, and before applying autocommands. -/// -/// @param do_curwin when true, also check current window. -void check_lnums(bool do_curwin) +/// Implementation of check_lnums() and check_lnums_nested(). +static void check_lnums_both(bool do_curwin, bool nested) { FOR_ALL_TAB_WINDOWS(tp, wp) { if ((do_curwin || wp != curwin) && wp->w_buffer == curbuf) { - // save the original cursor position and topline - wp->w_save_cursor.w_cursor_save = wp->w_cursor; - wp->w_save_cursor.w_topline_save = wp->w_topline; + if (!nested) { + // save the original cursor position and topline + wp->w_save_cursor.w_cursor_save = wp->w_cursor; + wp->w_save_cursor.w_topline_save = wp->w_topline; + } - if (wp->w_cursor.lnum > curbuf->b_ml.ml_line_count) { + bool need_adjust = wp->w_cursor.lnum > curbuf->b_ml.ml_line_count; + if (need_adjust) { wp->w_cursor.lnum = curbuf->b_ml.ml_line_count; } - if (wp->w_topline > curbuf->b_ml.ml_line_count) { - wp->w_topline = curbuf->b_ml.ml_line_count; + if (need_adjust || !nested) { + // save the (corrected) cursor position + wp->w_save_cursor.w_cursor_corr = wp->w_cursor; } - // save the corrected cursor position and topline - wp->w_save_cursor.w_cursor_corr = wp->w_cursor; - wp->w_save_cursor.w_topline_corr = wp->w_topline; + need_adjust = wp->w_topline > curbuf->b_ml.ml_line_count; + if (need_adjust) { + wp->w_topline = curbuf->b_ml.ml_line_count; + } + if (need_adjust || !nested) { + // save the (corrected) topline + wp->w_save_cursor.w_topline_corr = wp->w_topline; + } } } } +/// Correct the cursor line number in other windows. Used after changing the +/// current buffer, and before applying autocommands. +/// +/// @param do_curwin when true, also check current window. +void check_lnums(bool do_curwin) +{ + check_lnums_both(do_curwin, false); +} + +/// Like check_lnums() but for when check_lnums() was already called. +void check_lnums_nested(bool do_curwin) +{ + check_lnums_both(do_curwin, true); +} + /// Reset cursor and topline to its stored values from check_lnums(). /// check_lnums() must have been called first! void reset_lnums(void) { FOR_ALL_TAB_WINDOWS(tp, wp) { if (wp->w_buffer == curbuf) { - // Restore the value if the autocommand didn't change it. - if (equalpos(wp->w_save_cursor.w_cursor_corr, wp->w_cursor)) { + // Restore the value if the autocommand didn't change it and it was + // set. + if (equalpos(wp->w_save_cursor.w_cursor_corr, wp->w_cursor) + && wp->w_save_cursor.w_cursor_save.lnum != 0) { wp->w_cursor = wp->w_save_cursor.w_cursor_save; } - if (wp->w_save_cursor.w_topline_corr == wp->w_topline) { + if (wp->w_save_cursor.w_topline_corr == wp->w_topline + && wp->w_save_cursor.w_topline_save != 0) { wp->w_topline = wp->w_save_cursor.w_topline_save; } } @@ -7201,14 +7237,14 @@ int win_getid(typval_T *argvars) if (argvars[0].v_type == VAR_UNKNOWN) { return curwin->handle; } - int winnr = tv_get_number(&argvars[0]); + int winnr = (int)tv_get_number(&argvars[0]); win_T *wp; if (winnr > 0) { if (argvars[1].v_type == VAR_UNKNOWN) { wp = firstwin; } else { tabpage_T *tp = NULL; - int tabnr = tv_get_number(&argvars[1]); + int tabnr = (int)tv_get_number(&argvars[1]); FOR_ALL_TABS(tp2) { if (--tabnr == 0) { tp = tp2; @@ -7235,7 +7271,7 @@ int win_getid(typval_T *argvars) int win_gotoid(typval_T *argvars) { - int id = tv_get_number(&argvars[0]); + int id = (int)tv_get_number(&argvars[0]); FOR_ALL_TAB_WINDOWS(tp, wp) { if (wp->handle == id) { @@ -7302,7 +7338,7 @@ win_T *win_id2wp_tp(int id, tabpage_T **tpp) int win_id2win(typval_T *argvars) { int nr = 1; - int id = tv_get_number(&argvars[0]); + int id = (int)tv_get_number(&argvars[0]); FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { if (wp->handle == id) { @@ -7315,7 +7351,7 @@ int win_id2win(typval_T *argvars) void win_findbuf(typval_T *argvars, list_T *list) { - int bufnr = tv_get_number(&argvars[0]); + int bufnr = (int)tv_get_number(&argvars[0]); FOR_ALL_TAB_WINDOWS(tp, wp) { if (wp->w_buffer->b_fnum == bufnr) { |