diff options
Diffstat (limited to 'src')
60 files changed, 1436 insertions, 785 deletions
diff --git a/src/mpack/mpack_core.c b/src/mpack/mpack_core.c index f8ca63b7a3..4ee67a032a 100644 --- a/src/mpack/mpack_core.c +++ b/src/mpack/mpack_core.c @@ -12,8 +12,6 @@ # define MIN(X, Y) ((X) < (Y) ? (X) : (Y)) #endif -static int mpack_rtoken(const char **buf, size_t *buflen, - mpack_token_t *tok); static int mpack_rpending(const char **b, size_t *nl, mpack_tokbuf_t *tb); static int mpack_rvalue(mpack_token_type_t t, mpack_uint32_t l, const char **b, size_t *bl, mpack_token_t *tok); @@ -52,7 +50,10 @@ MPACK_API int mpack_read(mpack_tokbuf_t *tokbuf, const char **buf, int status; size_t initial_ppos, ptrlen, advanced; const char *ptr, *ptr_save; - assert(*buf && *buflen); + assert(*buf); + if (*buflen == 0) { + return MPACK_EOF; + } if (tokbuf->passthrough) { /* pass data from str/bin/ext directly as a MPACK_TOKEN_CHUNK, adjusting @@ -170,8 +171,7 @@ MPACK_API int mpack_write(mpack_tokbuf_t *tokbuf, char **buf, size_t *buflen, return MPACK_OK; } -static int mpack_rtoken(const char **buf, size_t *buflen, - mpack_token_t *tok) +int mpack_rtoken(const char **buf, size_t *buflen, mpack_token_t *tok) { unsigned char t = ADVANCE(buf, buflen); if (t < 0x80) { diff --git a/src/mpack/mpack_core.h b/src/mpack/mpack_core.h index 9edd13c41e..1d601bc82d 100644 --- a/src/mpack/mpack_core.h +++ b/src/mpack/mpack_core.h @@ -83,5 +83,7 @@ MPACK_API int mpack_read(mpack_tokbuf_t *tb, const char **b, size_t *bl, mpack_token_t *tok) FUNUSED FNONULL; MPACK_API int mpack_write(mpack_tokbuf_t *tb, char **b, size_t *bl, const mpack_token_t *tok) FUNUSED FNONULL; +int mpack_rtoken(const char **buf, size_t *buflen, + mpack_token_t *tok); #endif /* MPACK_CORE_H */ diff --git a/src/nvim/README.md b/src/nvim/README.md index 9417629691..91fb3ca2f6 100644 --- a/src/nvim/README.md +++ b/src/nvim/README.md @@ -23,7 +23,7 @@ Logs Low-level log messages sink to `$NVIM_LOG_FILE`. -UI events are logged at DEBUG level (`DEBUG_LOG_LEVEL`). +UI events are logged at DEBUG level (`LOGLVL_DBG`). rm -rf build/ make CMAKE_EXTRA_FLAGS="-DMIN_LOG_LEVEL=0" @@ -204,9 +204,14 @@ Then you can compare `bar` with another session, to debug TUI behavior. ### TUI redraw -Set the 'writedelay' option to see where and when the UI is painted. +Set the 'writedelay' and 'redrawdebug' options to see where and when the UI is painted. - :set writedelay=1 + :set writedelay=50 rdb=compositor + +Note: neovim uses an internal screenbuffer to only send minimal updates even if a large +region is repainted internally. To also highlight excess internal redraws, use + + :set writedelay=50 rdb=compositor,nodelta ### Terminal reference diff --git a/src/nvim/api/keysets.lua b/src/nvim/api/keysets.lua index d4882abffe..881a83e606 100644 --- a/src/nvim/api/keysets.lua +++ b/src/nvim/api/keysets.lua @@ -53,6 +53,7 @@ return { "force"; "keepscript"; "nargs"; + "preview"; "range"; "register"; }; diff --git a/src/nvim/api/private/dispatch.c b/src/nvim/api/private/dispatch.c index ba2e560d63..3da2c2cde4 100644 --- a/src/nvim/api/private/dispatch.c +++ b/src/nvim/api/private/dispatch.c @@ -32,37 +32,22 @@ #include "nvim/api/window.h" #include "nvim/ui_client.h" -static Map(String, MsgpackRpcRequestHandler) methods = MAP_INIT; - -static void msgpack_rpc_add_method_handler(String method, MsgpackRpcRequestHandler handler) -{ - map_put(String, MsgpackRpcRequestHandler)(&methods, method, handler); -} - -void msgpack_rpc_add_redraw(void) -{ - msgpack_rpc_add_method_handler(STATIC_CSTR_AS_STRING("redraw"), - (MsgpackRpcRequestHandler) { .fn = ui_client_handle_redraw, - .fast = true }); -} +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "api/private/dispatch_wrappers.generated.h" +#endif /// @param name API method name /// @param name_len name size (includes terminating NUL) MsgpackRpcRequestHandler msgpack_rpc_get_handler_for(const char *name, size_t name_len, Error *error) { - String m = { .data = (char *)name, .size = name_len }; - MsgpackRpcRequestHandler rv = - map_get(String, MsgpackRpcRequestHandler)(&methods, m); + int hash = msgpack_rpc_get_handler_for_hash(name, name_len); - if (!rv.fn) { + if (hash < 0) { api_set_error(error, kErrorTypeException, "Invalid method: %.*s", - m.size > 0 ? (int)m.size : (int)sizeof("<empty>"), - m.size > 0 ? m.data : "<empty>"); + name_len > 0 ? (int)name_len : (int)sizeof("<empty>"), + name_len > 0 ? name : "<empty>"); + return (MsgpackRpcRequestHandler){ 0 }; } - return rv; + return method_handlers[hash]; } - -#ifdef INCLUDE_GENERATED_DECLARATIONS -# include "api/private/dispatch_wrappers.generated.h" -#endif diff --git a/src/nvim/api/private/dispatch.h b/src/nvim/api/private/dispatch.h index bad5a13934..4b7c394944 100644 --- a/src/nvim/api/private/dispatch.h +++ b/src/nvim/api/private/dispatch.h @@ -10,6 +10,7 @@ typedef Object (*ApiDispatchWrapper)(uint64_t channel_id, /// The rpc_method_handlers table, used in msgpack_rpc_dispatch(), stores /// functions of this type. typedef struct { + const char *name; ApiDispatchWrapper fn; bool fast; // Function is safe to be executed immediately while running the // uv loop (the loop is run very frequently due to breakcheck). diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 3cccbc3cdf..af4aaf01aa 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -678,11 +678,7 @@ void modify_keymap(uint64_t channel_id, Buffer buffer, bool is_unmap, String mod if (rhs.size == 0) { // assume that the user wants RHS to be a <Nop> parsed_args.rhs_is_noop = true; } else { - // the given RHS was nonempty and not a <Nop>, but was parsed as if it - // were empty? - assert(false && "Failed to parse nonempty RHS!"); - api_set_error(err, kErrorTypeValidation, "Parsing of nonempty RHS failed: %s", rhs.data); - goto fail_and_free; + abort(); // should never happen } } else if (is_unmap && (parsed_args.rhs_len || parsed_args.rhs_lua != LUA_NOREF)) { if (parsed_args.rhs_len) { @@ -1438,6 +1434,7 @@ void create_user_command(String name, Object command, Dict(user_command) *opts, char *rep = NULL; LuaRef luaref = LUA_NOREF; LuaRef compl_luaref = LUA_NOREF; + LuaRef preview_luaref = LUA_NOREF; if (!uc_validate_name(name.data)) { api_set_error(err, kErrorTypeValidation, "Invalid command name"); @@ -1592,6 +1589,14 @@ void create_user_command(String name, Object command, Dict(user_command) *opts, goto err; } + if (opts->preview.type == kObjectTypeLuaRef) { + argt |= EX_PREVIEW; + preview_luaref = api_new_luaref(opts->preview.data.luaref); + } else if (HAS_KEY(opts->preview)) { + api_set_error(err, kErrorTypeValidation, "Invalid value for 'preview'"); + goto err; + } + switch (command.type) { case kObjectTypeLuaRef: luaref = api_new_luaref(command.data.luaref); @@ -1611,7 +1616,7 @@ void create_user_command(String name, Object command, Dict(user_command) *opts, } if (uc_add_command(name.data, name.size, rep, argt, def, flags, compl, compl_arg, compl_luaref, - addr_type_arg, luaref, force) != OK) { + preview_luaref, addr_type_arg, luaref, force) != OK) { api_set_error(err, kErrorTypeException, "Failed to create user command"); // Do not goto err, since uc_add_command now owns luaref, compl_luaref, and compl_arg } diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index dd0b75bbfb..9430a37d27 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -45,6 +45,7 @@ #include "nvim/move.h" #include "nvim/msgpack_rpc/channel.h" #include "nvim/msgpack_rpc/helpers.h" +#include "nvim/msgpack_rpc/unpacker.h" #include "nvim/ops.h" #include "nvim/option.h" #include "nvim/os/input.h" @@ -156,7 +157,6 @@ Dictionary nvim__get_hl_defs(Integer ns_id, Error *err) /// - reverse: boolean /// - nocombine: boolean /// - link: name of another highlight group to link to, see |:hi-link|. -/// Additionally, the following keys are recognized: /// - default: Don't override existing definition |:hi-default| /// - ctermfg: Sets foreground of cterm color |highlight-ctermfg| /// - ctermbg: Sets background of cterm color |highlight-ctermbg| @@ -480,7 +480,7 @@ Object nvim_notify(String msg, Integer log_level, Dictionary opts, Error *err) } /// Calculates the number of display cells occupied by `text`. -/// <Tab> counts as one cell. +/// Control characters including <Tab> count as one cell. /// /// @param text Some text /// @param[out] err Error details, if any @@ -2188,6 +2188,12 @@ void nvim__screenshot(String path) ui_call_screenshot(path); } +Object nvim__unpack(String str, Error *err) + FUNC_API_FAST +{ + return unpack(str.data, str.size, err); +} + /// Deletes an uppercase/file named mark. See |mark-motions|. /// /// @note fails with error if a lowercase or buffer local named mark is used. @@ -2501,6 +2507,8 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error * /// - count: (number) Any count supplied |<count>| /// - reg: (string) The optional register, if specified |<reg>| /// - mods: (string) Command modifiers, if any |<mods>| +/// - smods: (table) Command modifiers in a structured format. Has the same +/// structure as the "mods" key of |nvim_parse_cmd()|. /// @param opts Optional command attributes. See |command-attributes| for more details. To use /// boolean attributes (such as |:command-bang| or |:command-bar|) set the value to /// "true". In addition to the string options listed in |:command-complete|, the @@ -2509,6 +2517,7 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error * /// - desc: (string) Used for listing the command when a Lua function is used for /// {command}. /// - force: (boolean, default true) Override any previous definition. +/// - preview: (function) Preview callback for 'inccommand' |:command-preview| /// @param[out] err Error details, if any. void nvim_create_user_command(String name, Object command, Dict(user_command) *opts, Error *err) FUNC_API_SINCE(9) diff --git a/src/nvim/api/vimscript.c b/src/nvim/api/vimscript.c index e71f1a11ec..4b4404ea09 100644 --- a/src/nvim/api/vimscript.c +++ b/src/nvim/api/vimscript.c @@ -1241,10 +1241,12 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error } if (HAS_KEY(mods.verbose)) { - if (mods.verbose.type != kObjectTypeInteger || mods.verbose.data.integer <= 0) { - VALIDATION_ERROR("'mods.verbose' must be a non-negative Integer"); + if (mods.verbose.type != kObjectTypeInteger) { + VALIDATION_ERROR("'mods.verbose' must be a Integer"); + } else if (mods.verbose.data.integer >= 0) { + // Silently ignore negative integers to allow mods.verbose to be set to -1. + cmdinfo.verbose = mods.verbose.data.integer; } - cmdinfo.verbose = mods.verbose.data.integer; } bool vertical; @@ -1256,8 +1258,10 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error VALIDATION_ERROR("'mods.split' must be a String"); } - if (STRCMP(mods.split.data.string.data, "aboveleft") == 0 - || STRCMP(mods.split.data.string.data, "leftabove") == 0) { + if (*mods.split.data.string.data == NUL) { + // Empty string, do nothing. + } else if (STRCMP(mods.split.data.string.data, "aboveleft") == 0 + || STRCMP(mods.split.data.string.data, "leftabove") == 0) { cmdinfo.cmdmod.split |= WSP_ABOVE; } else if (STRCMP(mods.split.data.string.data, "belowright") == 0 || STRCMP(mods.split.data.string.data, "rightbelow") == 0) { @@ -1311,7 +1315,7 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error } WITH_SCRIPT_CONTEXT(channel_id, { - execute_cmd(&ea, &cmdinfo); + execute_cmd(&ea, &cmdinfo, false); }); if (output) { diff --git a/src/nvim/buffer_updates.c b/src/nvim/buffer_updates.c index cb08ba0cfb..47b88945c7 100644 --- a/src/nvim/buffer_updates.c +++ b/src/nvim/buffer_updates.c @@ -187,7 +187,7 @@ void buf_updates_unload(buf_T *buf, bool can_reload) } void buf_updates_send_changes(buf_T *buf, linenr_T firstline, int64_t num_added, - int64_t num_removed, bool send_tick) + int64_t num_removed) { size_t deleted_codepoints, deleted_codeunits; size_t deleted_bytes = ml_flush_deleted_bytes(buf, &deleted_codepoints, @@ -197,6 +197,9 @@ void buf_updates_send_changes(buf_T *buf, linenr_T firstline, int64_t num_added, return; } + // Don't send b:changedtick during 'inccommand' preview if "buf" is the current buffer. + bool send_tick = !(cmdpreview && buf == curbuf); + // if one the channels doesn't work, put its ID here so we can remove it later uint64_t badchannelid = 0; @@ -253,7 +256,7 @@ void buf_updates_send_changes(buf_T *buf, linenr_T firstline, int64_t num_added, for (size_t i = 0; i < kv_size(buf->update_callbacks); i++) { BufUpdateCallbacks cb = kv_A(buf->update_callbacks, i); bool keep = true; - if (cb.on_lines != LUA_NOREF && (cb.preview || !(State & MODE_CMDPREVIEW))) { + if (cb.on_lines != LUA_NOREF && (cb.preview || !cmdpreview)) { Array args = ARRAY_DICT_INIT; Object items[8]; args.size = 6; // may be increased to 8 below @@ -312,7 +315,7 @@ void buf_updates_send_splice(buf_T *buf, int start_row, colnr_T start_col, bcoun for (size_t i = 0; i < kv_size(buf->update_callbacks); i++) { BufUpdateCallbacks cb = kv_A(buf->update_callbacks, i); bool keep = true; - if (cb.on_bytes != LUA_NOREF && (cb.preview || !(State & MODE_CMDPREVIEW))) { + if (cb.on_bytes != LUA_NOREF && (cb.preview || !cmdpreview)) { FIXED_TEMP_ARRAY(args, 11); // the first argument is always the buffer handle diff --git a/src/nvim/change.c b/src/nvim/change.c index 9fd5083fd3..fa1de69e2c 100644 --- a/src/nvim/change.c +++ b/src/nvim/change.c @@ -351,7 +351,7 @@ void changed_bytes(linenr_T lnum, colnr_T col) changedOneline(curbuf, lnum); changed_common(lnum, col, lnum + 1, 0L); // notify any channels that are watching - buf_updates_send_changes(curbuf, lnum, 1, 1, true); + buf_updates_send_changes(curbuf, lnum, 1, 1); // Diff highlighting in other diff windows may need to be updated too. if (curwin->w_p_diff) { @@ -501,7 +501,7 @@ void changed_lines(linenr_T lnum, colnr_T col, linenr_T lnume, long xtra, bool d if (do_buf_event) { int64_t num_added = (int64_t)(lnume + xtra - lnum); int64_t num_removed = lnume - lnum; - buf_updates_send_changes(curbuf, lnum, num_added, num_removed, true); + buf_updates_send_changes(curbuf, lnum, num_added, num_removed); } } diff --git a/src/nvim/channel.c b/src/nvim/channel.c index 7a71be58c1..ecc3a24784 100644 --- a/src/nvim/channel.c +++ b/src/nvim/channel.c @@ -188,7 +188,7 @@ Channel *channel_alloc(ChannelStreamType type) void channel_create_event(Channel *chan, const char *ext_source) { -#if MIN_LOG_LEVEL <= INFO_LOG_LEVEL +#if MIN_LOG_LEVEL <= LOGLVL_INF const char *source; if (ext_source) { diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 02dc5ec954..6eb210fc79 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -10413,7 +10413,7 @@ char *do_string_sub(char *str, char *pat, char *sub, typval_T *expr, char *flags // - 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, false, true, false); + 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]))); @@ -10421,8 +10421,9 @@ char *do_string_sub(char *str, char *pat, char *sub, typval_T *expr, char *flags 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, true, true, false); + (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) { diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index f6bdfc6175..7e56f131ba 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -14,7 +14,6 @@ #include <string.h> #include "nvim/api/buffer.h" -#include "nvim/api/extmark.h" #include "nvim/api/private/defs.h" #include "nvim/ascii.h" #include "nvim/buffer.h" @@ -111,8 +110,6 @@ typedef struct { # include "ex_cmds.c.generated.h" #endif -static int preview_bufnr = 0; - /// ":ascii" and "ga" implementation void do_ascii(const exarg_T *const eap) { @@ -1013,7 +1010,7 @@ int do_move(linenr_T line1, linenr_T line2, linenr_T dest) disable_fold_update--; // send update regarding the new lines that were added - buf_updates_send_changes(curbuf, dest + 1, num_lines, 0, true); + buf_updates_send_changes(curbuf, dest + 1, num_lines, 0); /* * Now we delete the original text -- webb @@ -1055,7 +1052,7 @@ int do_move(linenr_T line1, linenr_T line2, linenr_T dest) } // send nvim_buf_lines_event regarding lines that were deleted - buf_updates_send_changes(curbuf, line1 + extra, 0, num_lines, true); + buf_updates_send_changes(curbuf, line1 + extra, 0, num_lines); return OK; } @@ -3438,8 +3435,8 @@ static int check_regexp_delim(int c) /// The usual escapes are supported as described in the regexp docs. /// /// @param do_buf_event If `true`, send buffer updates. -/// @return buffer used for 'inccommand' preview -static buf_T *do_sub(exarg_T *eap, proftime_T timeout, bool do_buf_event, handle_T bufnr) +/// @return 0, 1 or 2. See show_cmdpreview() for more information on what the return value means. +static int do_sub(exarg_T *eap, proftime_T timeout, long cmdpreview_ns, handle_T cmdpreview_bufnr) { long i = 0; regmmatch_T regmatch; @@ -3467,14 +3464,10 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, bool do_buf_event, handle char *sub_firstline; // allocated copy of first sub line bool endcolumn = false; // cursor in last column when done PreviewLines preview_lines = { KV_INITIAL_VALUE, 0 }; - static int pre_src_id = 0; // Source id for the preview highlight static int pre_hl_id = 0; - buf_T *orig_buf = curbuf; // save to reset highlighting pos_T old_cursor = curwin->w_cursor; int start_nsubs; int save_ma = 0; - int save_b_changed = curbuf->b_changed; - bool preview = (State & MODE_CMDPREVIEW); bool did_save = false; @@ -3494,7 +3487,7 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, bool do_buf_event, handle && vim_strchr("0123456789cegriIp|\"", *cmd) == NULL) { // don't accept alphanumeric for separator if (check_regexp_delim(*cmd) == FAIL) { - return NULL; + return 0; } // undocumented vi feature: @@ -3504,7 +3497,7 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, bool do_buf_event, handle cmd++; if (vim_strchr("/?&", *cmd) == NULL) { emsg(_(e_backslash)); - return NULL; + return 0; } if (*cmd != '&') { which_pat = RE_SEARCH; // use last '/' pattern @@ -3540,7 +3533,7 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, bool do_buf_event, handle MB_PTR_ADV(cmd); } - if (!eap->skip && !preview) { + if (!eap->skip && !cmdpreview) { sub_set_replacement((SubReplacementString) { .sub = xstrdup(sub), .timestamp = os_time(), @@ -3550,7 +3543,7 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, bool do_buf_event, handle } else if (!eap->skip) { // use previous pattern and substitution if (old_sub.sub == NULL) { // there is no previous command emsg(_(e_nopresub)); - return NULL; + return 0; } pat = NULL; // search_regcomp() will use previous pattern sub = old_sub.sub; @@ -3560,8 +3553,8 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, bool do_buf_event, handle endcolumn = (curwin->w_curswant == MAXCOL); } - if (sub != NULL && sub_joining_lines(eap, pat, sub, cmd, !preview)) { - return NULL; + if (sub != NULL && sub_joining_lines(eap, pat, sub, cmd, !cmdpreview)) { + return 0; } cmd = sub_parse_flags(cmd, &subflags, &which_pat); @@ -3575,7 +3568,7 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, bool do_buf_event, handle i = getdigits_long((char_u **)&cmd, true, 0); if (i <= 0 && !eap->skip && subflags.do_error) { emsg(_(e_zerocount)); - return NULL; + return 0; } eap->line1 = eap->line2; eap->line2 += i - 1; @@ -3592,26 +3585,26 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, bool do_buf_event, handle eap->nextcmd = (char *)check_nextcmd((char_u *)cmd); if (eap->nextcmd == NULL) { emsg(_(e_trailing)); - return NULL; + return 0; } } if (eap->skip) { // not executing commands, only parsing - return NULL; + return 0; } if (!subflags.do_count && !MODIFIABLE(curbuf)) { // Substitution is not allowed in non-'modifiable' buffer emsg(_(e_modifiable)); - return NULL; + return 0; } - if (search_regcomp((char_u *)pat, RE_SUBST, which_pat, (preview ? 0 : SEARCH_HIS), + if (search_regcomp((char_u *)pat, RE_SUBST, which_pat, (cmdpreview ? 0 : SEARCH_HIS), ®match) == FAIL) { if (subflags.do_error) { emsg(_(e_invcmd)); } - return NULL; + return 0; } // the 'i' or 'I' flag overrules 'ignorecase' and 'smartcase' @@ -3638,10 +3631,10 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, bool do_buf_event, handle sub_copy = sub; } else { char *source = sub; - sub = (char *)regtilde((char_u *)sub, p_magic, preview); + 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 = preview && sub != source; + sub_needs_free = cmdpreview && sub != source; } // Check for a match on each line. @@ -3650,7 +3643,7 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, bool do_buf_event, handle for (linenr_T lnum = eap->line1; lnum <= line2 && !got_quit && !aborting() - && (!preview || preview_lines.lines_needed <= (linenr_T)p_cwh + && (!cmdpreview || preview_lines.lines_needed <= (linenr_T)p_cwh || lnum <= curwin->w_botline); lnum++) { long nmatch = vim_regexec_multi(®match, curwin, curbuf, lnum, @@ -3817,7 +3810,7 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, bool do_buf_event, handle } } - if (subflags.do_ask && !preview) { + if (subflags.do_ask && !cmdpreview) { int typed = 0; // change State to MODE_CONFIRM, so that the mouse works @@ -4049,7 +4042,7 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, bool do_buf_event, handle // Save the line numbers for the preview buffer // NOTE: If the pattern matches a final newline, the next line will // be shown also, but should not be highlighted. Intentional for now. - if (preview && !has_second_delim) { + if (cmdpreview && !has_second_delim) { current_match.start.col = regmatch.startpos[0].col; if (current_match.end.lnum == 0) { current_match.end.lnum = sub_firstlnum + nmatch - 1; @@ -4064,7 +4057,7 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, bool do_buf_event, handle // 3. Substitute the string. During 'inccommand' preview only do this if // there is a replace pattern. - if (!preview || has_second_delim) { + if (!cmdpreview || has_second_delim) { long lnum_start = lnum; // save the start lnum save_ma = curbuf->b_p_ma; if (subflags.do_count) { @@ -4078,7 +4071,8 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, bool do_buf_event, handle // get length of substitution part sublen = vim_regsub_multi(®match, sub_firstlnum - regmatch.startpos[0].lnum, - (char_u *)sub, (char_u *)sub_firstline, false, p_magic, true); + (char_u *)sub, (char_u *)sub_firstline, 0, + REGSUB_BACKSLASH | (p_magic ? REGSUB_MAGIC : 0)); // If getting the substitute string caused an error, don't do // the replacement. // Don't keep flags set by a recursive call @@ -4118,7 +4112,8 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, bool do_buf_event, handle (void)vim_regsub_multi(®match, sub_firstlnum - regmatch.startpos[0].lnum, - (char_u *)sub, (char_u *)new_end, true, p_magic, true); + (char_u *)sub, (char_u *)new_end, sublen, + REGSUB_COPY | REGSUB_BACKSLASH | (p_magic ? REGSUB_MAGIC : 0)); sub_nsubs++; did_sub = true; @@ -4310,7 +4305,7 @@ skip: #define PUSH_PREVIEW_LINES() \ do { \ - if (preview) { \ + if (cmdpreview) { \ linenr_T match_lines = current_match.end.lnum \ - current_match.start.lnum +1; \ if (preview_lines.subresults.size > 0) { \ @@ -4366,8 +4361,7 @@ skip: int64_t num_added = last_line - first_line; int64_t num_removed = num_added - i; - buf_updates_send_changes(curbuf, first_line, num_added, num_removed, - do_buf_event); + buf_updates_send_changes(curbuf, first_line, num_added, num_removed); } xfree(sub_firstline); // may have to free allocated copy of the line @@ -4394,7 +4388,7 @@ skip: beginline(BL_WHITE | BL_FIX); } } - if (!preview && !do_sub_msg(subflags.do_count) && subflags.do_ask) { + if (!cmdpreview && !do_sub_msg(subflags.do_count) && subflags.do_ask) { msg(""); } } else { @@ -4431,34 +4425,23 @@ skip: subflags.do_all = save_do_all; subflags.do_ask = save_do_ask; + int retv = 0; + // Show 'inccommand' preview if there are matched lines. - buf_T *preview_buf = NULL; - size_t subsize = preview_lines.subresults.size; - if (preview && !aborting()) { + if (cmdpreview && !aborting()) { if (got_quit || profile_passed_limit(timeout)) { // Too slow, disable. set_string_option_direct("icm", -1, (char_u *)"", OPT_FREE, SID_NONE); } else if (*p_icm != NUL && pat != NULL) { - if (pre_src_id == 0) { - // Get a unique new src_id, saved in a static - pre_src_id = (int)nvim_create_namespace((String)STRING_INIT); - } if (pre_hl_id == 0) { pre_hl_id = syn_check_group(S_LEN("Substitute")); } - curbuf->b_changed = save_b_changed; // preserve 'modified' during preview - preview_buf = show_sub(eap, old_cursor, &preview_lines, - pre_hl_id, pre_src_id, bufnr); - if (subsize > 0) { - extmark_clear(orig_buf, pre_src_id, eap->line1 - 1, 0, - kv_last(preview_lines.subresults).end.lnum - 1, MAXCOL); - } + retv = show_sub(eap, old_cursor, &preview_lines, pre_hl_id, cmdpreview_ns, cmdpreview_bufnr); } } kv_destroy(preview_lines.subresults); - - return preview_buf; + return retv; #undef ADJUST_SUB_FIRSTLNUM #undef PUSH_PREVIEW_LINES } @@ -5854,52 +5837,26 @@ void ex_helpclose(exarg_T *eap) } } -/// Tries to enter to an existing window of given buffer. If no existing buffer -/// is found, creates a new split. -/// -/// @return OK/FAIL. -int sub_preview_win(buf_T *preview_buf) -{ - if (preview_buf != NULL) { - FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { - if (wp->w_buffer == preview_buf) { - win_enter(wp, false); - - return OK; - } - } - } - return win_split((int)p_cwh, WSP_BOT); -} - /// Shows the effects of the :substitute command being typed ('inccommand'). /// If inccommand=split, shows a preview window and later restores the layout. -static buf_T *show_sub(exarg_T *eap, pos_T old_cusr, PreviewLines *preview_lines, int hl_id, - int src_id, handle_T bufnr) +/// +/// @return 1 if preview window isn't needed, 2 if preview window is needed. +static int show_sub(exarg_T *eap, pos_T old_cusr, PreviewLines *preview_lines, int hl_id, + long cmdpreview_ns, handle_T cmdpreview_bufnr) FUNC_ATTR_NONNULL_ALL { - win_T *save_curwin = curwin; - cmdmod_T save_cmdmod = cmdmod; char *save_shm_p = (char *)vim_strsave(p_shm); PreviewLines lines = *preview_lines; buf_T *orig_buf = curbuf; - // We keep a special-purpose buffer around, but don't assume it exists. - buf_T *preview_buf = bufnr ? buflist_findnr(bufnr) : 0; - cmdmod.split = 0; // disable :leftabove/botright modifiers - cmdmod.tab = 0; // disable :tab modifier - cmdmod.noswapfile = true; // disable swap for preview buffer + buf_T *cmdpreview_buf = NULL; + // disable file info message set_string_option_direct("shm", -1, (char_u *)"F", OPT_FREE, SID_NONE); - bool outside_curline = (eap->line1 != old_cusr.lnum - || eap->line2 != old_cusr.lnum); - bool preview = outside_curline && (*p_icm != 'n'); - if (preview_buf == curbuf) { // Preview buffer cannot preview itself! - preview = false; - preview_buf = NULL; - } + // Update the topline to ensure that main window is on the correct line + update_topline(curwin); // Place cursor on nearest matching line, to undo do_sub() cursor placement. for (size_t i = 0; i < lines.subresults.size; i++) { @@ -5914,27 +5871,17 @@ static buf_T *show_sub(exarg_T *eap, pos_T old_cusr, PreviewLines *preview_lines // Width of the "| lnum|..." column which displays the line numbers. linenr_T highest_num_line = 0; int col_width = 0; + // Use preview window only when inccommand=split and range is not just the current line + bool preview = (*p_icm == 's') && (eap->line1 != old_cusr.lnum || eap->line2 != old_cusr.lnum); - if (preview && sub_preview_win(preview_buf) != FAIL) { - buf_open_scratch(preview_buf ? bufnr : 0, "[Preview]"); - buf_clear(); - preview_buf = curbuf; - curbuf->b_p_bl = false; - curbuf->b_p_ma = true; - curbuf->b_p_ul = -1; - curbuf->b_p_tw = 0; // Reset 'textwidth' (was set by ftplugin) - curwin->w_p_cul = false; - curwin->w_p_cuc = false; - curwin->w_p_spell = false; - curwin->w_p_fen = false; + if (preview) { + cmdpreview_buf = buflist_findnr(cmdpreview_bufnr); + assert(cmdpreview_buf != NULL); if (lines.subresults.size > 0) { highest_num_line = kv_last(lines.subresults).end.lnum; col_width = log10(highest_num_line) + 1 + 3; } - } else { - // Failed to split the window, don't show 'inccommand' preview. - preview_buf = NULL; } char *str = NULL; // construct the line to show in here @@ -5944,10 +5891,13 @@ static buf_T *show_sub(exarg_T *eap, pos_T old_cusr, PreviewLines *preview_lines linenr_T linenr_origbuf = 0; // last line added to original buffer linenr_T next_linenr = 0; // next line to show for the match + // Temporarily switch to preview buffer + aco_save_T aco; + for (size_t matchidx = 0; matchidx < lines.subresults.size; matchidx++) { SubResult match = lines.subresults.items[matchidx]; - if (preview_buf) { + if (cmdpreview_buf) { lpos_T p_start = { 0, match.start.col }; // match starts here in preview lpos_T p_end = { 0, match.end.col }; // ... and ends here @@ -5986,115 +5936,50 @@ static buf_T *show_sub(exarg_T *eap, pos_T old_cusr, PreviewLines *preview_lines // Put "|lnum| line" into `str` and append it to the preview buffer. snprintf(str, line_size, "|%*ld| %s", col_width - 3, next_linenr, line); + // Temporarily switch to preview buffer + aucmd_prepbuf(&aco, cmdpreview_buf); if (linenr_preview == 0) { ml_replace(1, str, true); } else { ml_append(linenr_preview, str, (colnr_T)line_size, false); } + aucmd_restbuf(&aco); linenr_preview += 1; } linenr_origbuf = match.end.lnum; - bufhl_add_hl_pos_offset(preview_buf, src_id, hl_id, p_start, - p_end, col_width); + bufhl_add_hl_pos_offset(cmdpreview_buf, cmdpreview_ns, hl_id, p_start, p_end, col_width); } - bufhl_add_hl_pos_offset(orig_buf, src_id, hl_id, match.start, - match.end, 0); + bufhl_add_hl_pos_offset(orig_buf, cmdpreview_ns, hl_id, match.start, match.end, 0); } - xfree(str); - redraw_later(curwin, SOME_VALID); - win_enter(save_curwin, false); // Return to original window - update_topline(curwin); - - // Update screen now. - int save_rd = RedrawingDisabled; - RedrawingDisabled = 0; - update_screen(SOME_VALID); - RedrawingDisabled = save_rd; + xfree(str); set_string_option_direct("shm", -1, (char_u *)save_shm_p, OPT_FREE, SID_NONE); xfree(save_shm_p); - cmdmod = save_cmdmod; - - return preview_buf; + return preview ? 2 : 1; } -/// Closes any open windows for inccommand preview buffer. -void close_preview_windows(void) +/// :substitute command. +void ex_substitute(exarg_T *eap) { - block_autocmds(); - buf_T *buf = preview_bufnr ? buflist_findnr(preview_bufnr) : NULL; - if (buf != NULL) { - close_windows(buf, false); - } - unblock_autocmds(); + (void)do_sub(eap, profile_zero(), 0, 0); + return; } -/// :substitute command -/// -/// If 'inccommand' is empty: calls do_sub(). -/// If 'inccommand' is set: shows a "live" preview then removes the changes. -/// from undo history. -void ex_substitute(exarg_T *eap) +/// :substitute command preview callback. +int ex_substitute_preview(exarg_T *eap, long cmdpreview_ns, handle_T cmdpreview_bufnr) { - bool preview = (State & MODE_CMDPREVIEW); - if (*p_icm == NUL || !preview) { // 'inccommand' is disabled - close_preview_windows(); - (void)do_sub(eap, profile_zero(), true, preview_bufnr); - - return; + // Only preview once the pattern delimiter has been typed + if (*eap->arg && !ASCII_ISALNUM(*eap->arg)) { + char *save_eap = eap->arg; + int retv = do_sub(eap, profile_setlimit(p_rdt), cmdpreview_ns, cmdpreview_bufnr); + eap->arg = save_eap; + return retv; } - block_autocmds(); // Disable events during command preview. - - char *save_eap = eap->arg; - garray_T save_view; - win_size_save(&save_view); // Save current window sizes. - save_search_patterns(); - int save_changedtick = buf_get_changedtick(curbuf); - time_t save_b_u_time_cur = curbuf->b_u_time_cur; - u_header_T *save_b_u_newhead = curbuf->b_u_newhead; - long save_b_p_ul = curbuf->b_p_ul; - int save_w_p_cul = curwin->w_p_cul; - int save_w_p_cuc = curwin->w_p_cuc; - - curbuf->b_p_ul = LONG_MAX; // make sure we can undo all changes - curwin->w_p_cul = false; // Disable 'cursorline' - curwin->w_p_cuc = false; // Disable 'cursorcolumn' - - // Don't show search highlighting during live substitution - bool save_hls = p_hls; - p_hls = false; - buf_T *preview_buf = do_sub(eap, profile_setlimit(p_rdt), false, - preview_bufnr); - p_hls = save_hls; - - if (preview_buf != NULL) { - preview_bufnr = preview_buf->handle; - } - - if (save_changedtick != buf_get_changedtick(curbuf)) { - // Undo invisibly. This also moves the cursor! - if (!u_undo_and_forget(1)) { - abort(); - } - // Restore newhead. It is meaningless when curhead is valid, but we must - // restore it so that undotree() is identical before/after the preview. - curbuf->b_u_newhead = save_b_u_newhead; - curbuf->b_u_time_cur = save_b_u_time_cur; - buf_set_changedtick(curbuf, save_changedtick); - } - - curbuf->b_p_ul = save_b_p_ul; - curwin->w_p_cul = save_w_p_cul; // Restore 'cursorline' - curwin->w_p_cuc = save_w_p_cuc; // Restore 'cursorcolumn' - eap->arg = save_eap; - restore_search_patterns(); - win_size_restore(&save_view); - ga_clear(&save_view); - unblock_autocmds(); + return 0; } /// Skip over the pattern argument of ":vimgrep /pat/[g][j]". diff --git a/src/nvim/ex_cmds.lua b/src/nvim/ex_cmds.lua index 427e018141..b18bdefc2a 100644 --- a/src/nvim/ex_cmds.lua +++ b/src/nvim/ex_cmds.lua @@ -4,28 +4,29 @@ local module = {} -- Description of the values below is contained in ex_cmds_defs.h file. -- "EX_" prefix is omitted. -local RANGE = 0x001 -local BANG = 0x002 -local EXTRA = 0x004 -local XFILE = 0x008 -local NOSPC = 0x010 -local DFLALL = 0x020 -local WHOLEFOLD = 0x040 -local NEEDARG = 0x080 -local TRLBAR = 0x100 -local REGSTR = 0x200 -local COUNT = 0x400 -local NOTRLCOM = 0x800 -local ZEROR = 0x1000 -local CTRLV = 0x2000 -local CMDARG = 0x4000 -local BUFNAME = 0x8000 -local BUFUNL = 0x10000 -local ARGOPT = 0x20000 -local SBOXOK = 0x40000 -local CMDWIN = 0x80000 -local MODIFY = 0x100000 -local FLAGS = 0x200000 +local RANGE = 0x001 +local BANG = 0x002 +local EXTRA = 0x004 +local XFILE = 0x008 +local NOSPC = 0x010 +local DFLALL = 0x020 +local WHOLEFOLD = 0x040 +local NEEDARG = 0x080 +local TRLBAR = 0x100 +local REGSTR = 0x200 +local COUNT = 0x400 +local NOTRLCOM = 0x800 +local ZEROR = 0x1000 +local CTRLV = 0x2000 +local CMDARG = 0x4000 +local BUFNAME = 0x8000 +local BUFUNL = 0x10000 +local ARGOPT = 0x20000 +local SBOXOK = 0x40000 +local CMDWIN = 0x80000 +local MODIFY = 0x100000 +local FLAGS = 0x200000 +local PREVIEW = 0x8000000 local FILES = bit.bor(XFILE, EXTRA) local WORD1 = bit.bor(EXTRA, NOSPC) local FILE1 = bit.bor(FILES, NOSPC) @@ -33,6 +34,7 @@ local FILE1 = bit.bor(FILES, NOSPC) module.flags = { RANGE = RANGE, DFLALL = DFLALL, + PREVIEW = PREVIEW } -- The following table is described in ex_cmds_defs.h file. @@ -2305,9 +2307,10 @@ module.cmds = { }, { command='substitute', - flags=bit.bor(RANGE, WHOLEFOLD, EXTRA, CMDWIN), + flags=bit.bor(RANGE, WHOLEFOLD, EXTRA, CMDWIN, PREVIEW), addr_type='ADDR_LINES', func='ex_substitute', + preview_func='ex_substitute_preview', }, { command='sNext', @@ -2479,9 +2482,10 @@ module.cmds = { }, { command='smagic', - flags=bit.bor(RANGE, WHOLEFOLD, EXTRA, CMDWIN), + flags=bit.bor(RANGE, WHOLEFOLD, EXTRA, CMDWIN, PREVIEW), addr_type='ADDR_LINES', func='ex_submagic', + preview_func='ex_submagic_preview', }, { command='smap', @@ -2509,9 +2513,10 @@ module.cmds = { }, { command='snomagic', - flags=bit.bor(RANGE, WHOLEFOLD, EXTRA, CMDWIN), + flags=bit.bor(RANGE, WHOLEFOLD, EXTRA, CMDWIN, PREVIEW), addr_type='ADDR_LINES', func='ex_submagic', + preview_func='ex_submagic_preview', }, { command='snoremap', diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c index 0a91072036..45c05c9980 100644 --- a/src/nvim/ex_cmds2.c +++ b/src/nvim/ex_cmds2.c @@ -2798,15 +2798,11 @@ void ex_language(exarg_T *eap) } if (*name == NUL) { -# ifdef HAVE_WORKING_LIBINTL if (what == VIM_LC_MESSAGES) { p = get_mess_env(); } else { -# endif - p = setlocale(what, NULL); -# ifdef HAVE_WORKING_LIBINTL - } -# endif + p = setlocale(what, NULL); + } if (p == NULL || *p == NUL) { p = "Unknown"; } diff --git a/src/nvim/ex_cmds_defs.h b/src/nvim/ex_cmds_defs.h index d8dd3da9e6..a5c9c6be2d 100644 --- a/src/nvim/ex_cmds_defs.h +++ b/src/nvim/ex_cmds_defs.h @@ -63,6 +63,7 @@ #define EX_MODIFY 0x100000 // forbidden in non-'modifiable' buffer #define EX_FLAGS 0x200000 // allow flags after count in argument #define EX_KEEPSCRIPT 0x4000000 // keep sctx of where command was invoked +#define EX_PREVIEW 0x8000000 // allow incremental command preview #define EX_FILES (EX_XFILE | EX_EXTRA) // multiple extra files allowed #define EX_FILE1 (EX_FILES | EX_NOSPC) // 1 file, defaults to current file #define EX_WORD1 (EX_EXTRA | EX_NOSPC) // one extra word allowed @@ -91,6 +92,7 @@ typedef struct exarg exarg_T; #define BAD_DROP (-2) // erase it typedef void (*ex_func_T)(exarg_T *eap); +typedef int (*ex_preview_func_T)(exarg_T *eap, long cmdpreview_ns, handle_T cmdpreview_bufnr); // NOTE: These possible could be removed and changed so that // Callback could take a "command" style string, and simply @@ -125,10 +127,11 @@ typedef char *(*LineGetter)(int, void *, int, bool); /// Structure for command definition. typedef struct cmdname { - char *cmd_name; ///< Name of the command. - ex_func_T cmd_func; ///< Function with implementation of this command. - uint32_t cmd_argt; ///< Relevant flags from the declared above. - cmd_addr_T cmd_addr_type; ///< Flag for address type + char *cmd_name; ///< Name of the command. + ex_func_T cmd_func; ///< Function with implementation of this command. + ex_preview_func_T cmd_preview_func; ///< Preview callback function of this command. + uint32_t cmd_argt; ///< Relevant flags from the declared above. + cmd_addr_T cmd_addr_type; ///< Flag for address type. } CommandDefinition; // A list used for saving values of "emsg_silent". Used by ex_try() to save the diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 4b7958efa5..0e9c8dcf01 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -294,7 +294,6 @@ int do_cmdline_cmd(const char *cmd) /// DOCMD_KEYTYPED - Don't reset KeyTyped. /// DOCMD_EXCRESET - Reset the exception environment (used for debugging). /// DOCMD_KEEPLINE - Store first typed line (for repeating with "."). -/// DOCMD_PREVIEW - During 'inccommand' preview. /// /// @param cookie argument for fgetline() /// @@ -593,11 +592,6 @@ int do_cmdline(char *cmdline, LineGetter fgetline, void *cookie, int flags) next_cmdline = do_one_cmd(&cmdline_copy, flags, &cstack, cmd_getline, cmd_cookie); recursive--; - // Ignore trailing '|'-separated commands in preview-mode ('inccommand'). - if ((State & MODE_CMDPREVIEW) && (flags & DOCMD_PREVIEW)) { - next_cmdline = NULL; - } - if (cmd_cookie == (void *)&cmd_loop_cookie) { // Use "current_line" from "cmd_loop_cookie", it may have been // incremented when defining a function. @@ -1578,9 +1572,11 @@ bool parse_cmdline(char *cmdline, exarg_T *eap, CmdParseInfo *cmdinfo, char **er /// /// @param eap Ex-command arguments /// @param cmdinfo Command parse information -void execute_cmd(exarg_T *eap, CmdParseInfo *cmdinfo) +/// @param preview Execute command preview callback instead of actual command +int execute_cmd(exarg_T *eap, CmdParseInfo *cmdinfo, bool preview) { char *errormsg = NULL; + int retv = 0; #define ERROR(msg) \ do { \ @@ -1698,11 +1694,17 @@ void execute_cmd(exarg_T *eap, CmdParseInfo *cmdinfo) // Execute the command if (IS_USER_CMDIDX(eap->cmdidx)) { // Execute a user-defined command. - do_ucmd(eap); + retv = do_ucmd(eap, preview); } else { - // Call the function to execute the command. + // Call the function to execute the command or the preview callback. eap->errmsg = NULL; - (cmdnames[eap->cmdidx].cmd_func)(eap); + + if (preview) { + retv = (cmdnames[eap->cmdidx].cmd_preview_func)(eap, cmdpreview_get_ns(), + cmdpreview_get_bufnr()); + } else { + (cmdnames[eap->cmdidx].cmd_func)(eap); + } if (eap->errmsg != NULL) { errormsg = _(eap->errmsg); } @@ -1718,6 +1720,7 @@ end: if (eap->did_sandbox) { sandbox--; } + return retv; #undef ERROR } @@ -2350,7 +2353,7 @@ static char *do_one_cmd(char **cmdlinep, int flags, cstack_T *cstack, LineGetter /* * Execute a user-defined command. */ - do_ucmd(&ea); + do_ucmd(&ea, false); } else { /* * Call the function to execute the command. @@ -2751,6 +2754,8 @@ int parse_cmd_address(exarg_T *eap, char **errormsg, bool silent) { int address_count = 1; linenr_T lnum; + bool need_check_cursor = false; + int ret = FAIL; // Repeat for all ',' or ';' separated addresses. for (;;) { @@ -2760,7 +2765,7 @@ int parse_cmd_address(exarg_T *eap, char **errormsg, bool silent) lnum = get_address(eap, &eap->cmd, eap->addr_type, eap->skip, silent, eap->addr_count == 0, address_count++); if (eap->cmd == NULL) { // error detected - return FAIL; + goto theend; } if (lnum == MAXLNUM) { if (*eap->cmd == '%') { // '%' - all lines @@ -2799,14 +2804,14 @@ int parse_cmd_address(exarg_T *eap, char **errormsg, bool silent) // there is no Vim command which uses '%' and // ADDR_WINDOWS or ADDR_TABS *errormsg = _(e_invrange); - return FAIL; + goto theend; } break; case ADDR_TABS_RELATIVE: case ADDR_UNSIGNED: case ADDR_QUICKFIX: *errormsg = _(e_invrange); - return FAIL; + goto theend; case ADDR_ARGUMENTS: if (ARGCOUNT == 0) { eap->line1 = eap->line2 = 0; @@ -2831,19 +2836,19 @@ int parse_cmd_address(exarg_T *eap, char **errormsg, bool silent) // '*' - visual area if (eap->addr_type != ADDR_LINES) { *errormsg = _(e_invrange); - return FAIL; + goto theend; } eap->cmd++; if (!eap->skip) { pos_T *fp = getmark('<', false); if (check_mark(fp) == FAIL) { - return FAIL; + goto theend; } eap->line1 = fp->lnum; fp = getmark('>', false); if (check_mark(fp) == FAIL) { - return FAIL; + goto theend; } eap->line2 = fp->lnum; eap->addr_count++; @@ -2857,11 +2862,14 @@ int parse_cmd_address(exarg_T *eap, char **errormsg, bool silent) if (*eap->cmd == ';') { if (!eap->skip) { curwin->w_cursor.lnum = eap->line2; + // Don't leave the cursor on an illegal line or column, but do // accept zero as address, so 0;/PATTERN/ works correctly. + // Check the cursor position before returning. if (eap->line2 > 0) { check_cursor(); } + need_check_cursor = true; } } else if (*eap->cmd != ',') { break; @@ -2877,7 +2885,13 @@ int parse_cmd_address(exarg_T *eap, char **errormsg, bool silent) eap->addr_count = 0; } } - return OK; + ret = OK; + +theend: + if (need_check_cursor) { + check_cursor(); + } + return ret; } /// Check for an Ex command with optional tail. @@ -5530,8 +5544,8 @@ char *uc_validate_name(char *name) /// /// @return OK if the command is created, FAIL otherwise. int uc_add_command(char *name, size_t name_len, char *rep, uint32_t argt, long def, int flags, - int compl, char *compl_arg, LuaRef compl_luaref, cmd_addr_T addr_type, - LuaRef luaref, bool force) + int compl, char *compl_arg, LuaRef compl_luaref, LuaRef preview_luaref, + cmd_addr_T addr_type, LuaRef luaref, bool force) FUNC_ATTR_NONNULL_ARG(1, 3) { ucmd_T *cmd = NULL; @@ -5586,6 +5600,7 @@ int uc_add_command(char *name, size_t name_len, char *rep, uint32_t argt, long d XFREE_CLEAR(cmd->uc_compl_arg); NLUA_CLEAR_REF(cmd->uc_luaref); NLUA_CLEAR_REF(cmd->uc_compl_luaref); + NLUA_CLEAR_REF(cmd->uc_preview_luaref); break; } @@ -5618,6 +5633,7 @@ int uc_add_command(char *name, size_t name_len, char *rep, uint32_t argt, long d nlua_set_sctx(&cmd->uc_script_ctx); cmd->uc_compl_arg = (char_u *)compl_arg; cmd->uc_compl_luaref = compl_luaref; + cmd->uc_preview_luaref = preview_luaref; cmd->uc_addr_type = addr_type; cmd->uc_luaref = luaref; @@ -5628,6 +5644,7 @@ fail: xfree(compl_arg); NLUA_CLEAR_REF(luaref); NLUA_CLEAR_REF(compl_luaref); + NLUA_CLEAR_REF(preview_luaref); return FAIL; } @@ -6060,8 +6077,7 @@ static void ex_command(exarg_T *eap) } else if (compl > 0 && (argt & EX_EXTRA) == 0) { emsg(_(e_complete_used_without_nargs)); } else { - uc_add_command(name, name_len, p, argt, def, flags, compl, - compl_arg, LUA_NOREF, + uc_add_command(name, name_len, p, argt, def, flags, compl, compl_arg, LUA_NOREF, LUA_NOREF, addr_type_arg, LUA_NOREF, eap->forceit); } } @@ -6081,6 +6097,7 @@ void free_ucmd(ucmd_T *cmd) xfree(cmd->uc_compl_arg); NLUA_CLEAR_REF(cmd->uc_compl_luaref); NLUA_CLEAR_REF(cmd->uc_luaref); + NLUA_CLEAR_REF(cmd->uc_preview_luaref); } /// Clear all user commands for "gap". @@ -6611,7 +6628,7 @@ size_t uc_mods(char *buf) return result; } -static void do_ucmd(exarg_T *eap) +static int do_ucmd(exarg_T *eap, bool preview) { char *buf; char *p; @@ -6632,9 +6649,14 @@ static void do_ucmd(exarg_T *eap) cmd = USER_CMD_GA(&curbuf->b_ucmds, eap->useridx); } + if (preview) { + assert(cmd->uc_preview_luaref > 0); + return nlua_do_ucmd(cmd, eap, true); + } + if (cmd->uc_luaref > 0) { - nlua_do_ucmd(cmd, eap); - return; + nlua_do_ucmd(cmd, eap, false); + return 0; } /* @@ -6729,6 +6751,8 @@ static void do_ucmd(exarg_T *eap) } xfree(buf); xfree(split_buf); + + return 0; } static char *expand_user_command_name(int idx) @@ -6785,7 +6809,8 @@ char *get_user_cmd_flags(expand_T *xp, int idx) { static char *user_cmd_flags[] = { "addr", "bang", "bar", "buffer", "complete", "count", - "nargs", "range", "register", "keepscript" }; + "nargs", "range", "register", + "keepscript" }; if (idx >= (int)ARRAY_SIZE(user_cmd_flags)) { return NULL; @@ -8557,6 +8582,18 @@ static void ex_submagic(exarg_T *eap) p_magic = magic_save; } +/// ":smagic" and ":snomagic" preview callback. +static int ex_submagic_preview(exarg_T *eap, long cmdpreview_ns, handle_T cmdpreview_bufnr) +{ + int magic_save = p_magic; + + p_magic = (eap->cmdidx == CMD_smagic); + int retv = ex_substitute_preview(eap, cmdpreview_ns, cmdpreview_bufnr); + p_magic = magic_save; + + return retv; +} + /// ":join". static void ex_join(exarg_T *eap) { @@ -8798,7 +8835,7 @@ static void ex_redir(exarg_T *eap) /// ":redraw": force redraw static void ex_redraw(exarg_T *eap) { - if (State & MODE_CMDPREVIEW) { + if (cmdpreview) { return; // Ignore :redraw during 'inccommand' preview. #9777 } int r = RedrawingDisabled; @@ -8832,7 +8869,7 @@ static void ex_redraw(exarg_T *eap) /// ":redrawstatus": force redraw of status line(s) and window bar(s) static void ex_redrawstatus(exarg_T *eap) { - if (State & MODE_CMDPREVIEW) { + if (cmdpreview) { return; // Ignore :redrawstatus during 'inccommand' preview. #9777 } int r = RedrawingDisabled; @@ -10096,22 +10133,15 @@ bool cmd_can_preview(char *cmd) if (*ea.cmd == '*') { ea.cmd = skipwhite(ea.cmd + 1); } - char *end = find_ex_command(&ea, NULL); - switch (ea.cmdidx) { - case CMD_substitute: - case CMD_smagic: - case CMD_snomagic: - // Only preview once the pattern delimiter has been typed - if (*end && !ASCII_ISALNUM(*end)) { - return true; - } - break; - default: - break; + if (find_ex_command(&ea, NULL) == NULL || ea.cmdidx == CMD_SIZE) { + return false; + } else if (!IS_USER_CMDIDX(ea.cmdidx)) { + // find_ex_command sets the flags for user commands automatically + ea.argt = cmdnames[(int)ea.cmdidx].cmd_argt; } - return false; + return (ea.argt & EX_PREVIEW); } /// Gets a map of maps describing user-commands defined for buffer `buf` or @@ -10138,6 +10168,7 @@ Dictionary commands_array(buf_T *buf) PUT(d, "bar", BOOLEAN_OBJ(!!(cmd->uc_argt & EX_TRLBAR))); PUT(d, "register", BOOLEAN_OBJ(!!(cmd->uc_argt & EX_REGSTR))); PUT(d, "keepscript", BOOLEAN_OBJ(!!(cmd->uc_argt & EX_KEEPSCRIPT))); + PUT(d, "preview", BOOLEAN_OBJ(!!(cmd->uc_argt & EX_PREVIEW))); switch (cmd->uc_argt & (EX_EXTRA | EX_NOSPC | EX_NEEDARG)) { case 0: diff --git a/src/nvim/ex_docmd.h b/src/nvim/ex_docmd.h index 24656f3851..dbe095ab13 100644 --- a/src/nvim/ex_docmd.h +++ b/src/nvim/ex_docmd.h @@ -12,7 +12,6 @@ #define DOCMD_KEYTYPED 0x08 // don't reset KeyTyped #define DOCMD_EXCRESET 0x10 // reset exception environment (for debugging #define DOCMD_KEEPLINE 0x20 // keep typed line for repeating with "." -#define DOCMD_PREVIEW 0x40 // during 'inccommand' preview // defines for eval_vars() #define VALID_PATH 1 @@ -42,6 +41,7 @@ typedef struct ucmd { sctx_T uc_script_ctx; // SCTX where the command was defined char_u *uc_compl_arg; // completion argument if any LuaRef uc_compl_luaref; // Reference to Lua completion function + LuaRef uc_preview_luaref; // Reference to Lua preview function LuaRef uc_luaref; // Reference to Lua function } ucmd_T; diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 13cfd76adf..ad3ccf2a99 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -11,7 +11,9 @@ #include <stdlib.h> #include <string.h> +#include "nvim/api/extmark.h" #include "nvim/api/private/helpers.h" +#include "nvim/api/vim.h" #include "nvim/arabic.h" #include "nvim/ascii.h" #include "nvim/assert.h" @@ -69,6 +71,7 @@ #include "nvim/syntax.h" #include "nvim/tag.h" #include "nvim/ui.h" +#include "nvim/undo.h" #include "nvim/vim.h" #include "nvim/viml/parser/expressions.h" #include "nvim/viml/parser/parser.h" @@ -230,27 +233,13 @@ static int compl_match_arraysize; static int compl_startcol; static int compl_selected; -/// |:checkhealth| completion items -/// -/// Regenerates on every new command line prompt, to accommodate changes on the -/// runtime files. -typedef struct { - garray_T names; // healthcheck names - unsigned last_gen; // last_prompt_id where names were generated -} CheckhealthComp; - -/// Cookie used when converting filepath to name -struct healthchecks_cookie { - garray_T *names; // global healthchecks - bool is_lua; // true if the current entry is a Lua healthcheck -}; - -static CheckhealthComp healthchecks = { GA_INIT(sizeof(char_u *), 10), 0 }; - #ifdef INCLUDE_GENERATED_DECLARATIONS # include "ex_getln.c.generated.h" #endif +static handle_T cmdpreview_bufnr = 0; +static long cmdpreview_ns = 0; + static int cmd_hkmap = 0; // Hebrew mapping during command line static void save_viewstate(viewstate_T *vs) @@ -295,59 +284,23 @@ static void init_incsearch_state(incsearch_state_T *s) /// @param[in] xp Not used. static char *get_healthcheck_names(expand_T *xp, int idx) { - // Generate the first time or on new prompt. - if (healthchecks.last_gen == 0 || healthchecks.last_gen != last_prompt_id) { - ga_clear_strings(&healthchecks.names); - char *patterns[3] = { "autoload/health/**.vim", "lua/**/**/health/init.lua", // NOLINT - "lua/**/**/health.lua" }; // NOLINT - for (int i = 0; i < 3; i++) { - struct healthchecks_cookie hcookie = { .names = &healthchecks.names, .is_lua = i != 0 }; - do_in_runtimepath(patterns[i], DIP_ALL, get_healthcheck_cb, &hcookie); - - if (healthchecks.names.ga_len > 0) { - ga_remove_duplicate_strings(&healthchecks.names); - } - } - // Tracked to regenerate items on next prompt. - healthchecks.last_gen = last_prompt_id; - } - return idx < healthchecks.names.ga_len - ? ((char **)(healthchecks.names.ga_data))[idx] : NULL; -} - -/// Transform healthcheck file path into it's name. -/// -/// Used as a callback for do_in_runtimepath -/// @param[in] path Expanded path to a possible healthcheck. -/// @param[out] cookie Array where names will be inserted. -static void get_healthcheck_cb(char *path, void *cookie) -{ - if (path != NULL) { - struct healthchecks_cookie *hcookie = (struct healthchecks_cookie *)cookie; - char *pattern; - char *sub = "\\1"; - char *res; - - if (hcookie->is_lua) { - // Lua: transform "../lua/vim/lsp/health.lua" into "vim.lsp" - pattern = ".*lua[\\/]\\(.\\{-}\\)[\\/]health\\([\\/]init\\)\\?\\.lua$"; - } else { - // Vim: transform "../autoload/health/provider.vim" into "provider" - pattern = ".*[\\/]\\([^\\/]*\\)\\.vim$"; - } + static Object names = OBJECT_INIT; + static unsigned last_gen = 0; - res = do_string_sub(path, pattern, sub, NULL, "g"); - if (hcookie->is_lua && res != NULL) { - // Replace slashes with dots as represented by the healthcheck plugin. - char *ares = do_string_sub(res, "[\\/]", ".", NULL, "g"); - xfree(res); - res = ares; - } + if (last_gen != last_prompt_id || last_gen == 0) { + Array a = ARRAY_DICT_INIT; + Error err = ERROR_INIT; + Object res = nlua_exec(STATIC_CSTR_AS_STRING("return vim.health._complete()"), a, &err); + api_clear_error(&err); + api_free_object(names); + names = res; + last_gen = last_prompt_id; + } - if (res != NULL) { - GA_APPEND(char *, hcookie->names, res); - } + if (names.type == kObjectTypeArray && idx < (int)names.data.array.size) { + return names.data.array.items[idx].data.string.data; } + return NULL; } // Return true when 'incsearch' highlighting is to be done. @@ -740,6 +693,8 @@ static uint8_t *command_line_enter(int firstc, long count, int indent, bool init static int cmdline_level = 0; cmdline_level++; + bool save_cmdpreview = cmdpreview; + cmdpreview = false; CommandLineState state = { .firstc = firstc, .count = count, @@ -951,11 +906,6 @@ static uint8_t *command_line_enter(int firstc, long count, int indent, bool init ExpandCleanup(&s->xpc); ccline.xpc = NULL; - if (s->gotesc) { - // There might be a preview window open for inccommand. Close it. - close_preview_windows(); - } - finish_incsearch_highlighting(s->gotesc, &s->is_state, false); if (ccline.cmdbuff != NULL) { @@ -998,6 +948,10 @@ static uint8_t *command_line_enter(int firstc, long count, int indent, bool init set_string_option_direct("icm", -1, s->save_p_icm, OPT_FREE, SID_NONE); State = s->save_State; + if (cmdpreview != save_cmdpreview) { + cmdpreview = save_cmdpreview; // restore preview state + redraw_all_later(SOME_VALID); + } setmouse(); ui_cursor_shape(); // may show different cursor shape sb_text_end_cmdline(); @@ -2306,6 +2260,268 @@ static int empty_pattern(char_u *p) return n == 0 || (n >= 2 && p[n - 2] == '\\' && p[n - 1] == '|'); } +handle_T cmdpreview_get_bufnr(void) +{ + return cmdpreview_bufnr; +} + +long cmdpreview_get_ns(void) +{ + return cmdpreview_ns; +} + +/// Sets up command preview buffer. +/// +/// @return Pointer to command preview buffer if succeeded, NULL if failed. +static buf_T *cmdpreview_open_buf(void) +{ + buf_T *cmdpreview_buf = cmdpreview_bufnr ? buflist_findnr(cmdpreview_bufnr) : NULL; + + // If preview buffer doesn't exist, open one. + if (cmdpreview_buf == NULL) { + Error err = ERROR_INIT; + handle_T bufnr = nvim_create_buf(false, true, &err); + + if (ERROR_SET(&err)) { + return NULL; + } + + cmdpreview_buf = buflist_findnr(bufnr); + } + + // Preview buffer cannot preview itself! + if (cmdpreview_buf == curbuf) { + return NULL; + } + + // Rename preview buffer. + aco_save_T aco; + aucmd_prepbuf(&aco, cmdpreview_buf); + int retv = rename_buffer("[Preview]"); + aucmd_restbuf(&aco); + + if (retv == FAIL) { + return NULL; + } + + // Temporarily switch to preview buffer to set it up for previewing. + aucmd_prepbuf(&aco, cmdpreview_buf); + buf_clear(); + curbuf->b_p_ma = true; + curbuf->b_p_ul = -1; + curbuf->b_p_tw = 0; // Reset 'textwidth' (was set by ftplugin) + aucmd_restbuf(&aco); + cmdpreview_bufnr = cmdpreview_buf->handle; + + return cmdpreview_buf; +} + +/// Open command preview window if it's not already open. +/// Returns to original window after opening command preview window. +/// +/// @param cmdpreview_buf Pointer to command preview buffer +/// +/// @return Pointer to command preview window if succeeded, NULL if failed. +static win_T *cmdpreview_open_win(buf_T *cmdpreview_buf) +{ + win_T *save_curwin = curwin; + bool win_found = false; + + // Try to find an existing preview window. + FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { + if (wp->w_buffer == cmdpreview_buf) { + win_enter(wp, false); + win_found = true; + break; + } + } + + // If an existing window is not found, create one. + if (!win_found && win_split((int)p_cwh, WSP_BOT) == FAIL) { + return NULL; + } + + win_T *preview_win = curwin; + Error err = ERROR_INIT; + + // Switch to preview buffer + try_start(); + int result = do_buffer(DOBUF_GOTO, DOBUF_FIRST, FORWARD, cmdpreview_buf->handle, 0); + if (try_end(&err) || result == FAIL) { + api_clear_error(&err); + return NULL; + } + + curwin->w_p_cul = false; + curwin->w_p_cuc = false; + curwin->w_p_spell = false; + curwin->w_p_fen = false; + + win_enter(save_curwin, false); + return preview_win; +} + +/// Closes any open command preview windows. +static void cmdpreview_close_win(void) +{ + buf_T *buf = cmdpreview_bufnr ? buflist_findnr(cmdpreview_bufnr) : NULL; + if (buf != NULL) { + close_windows(buf, false); + } +} + +/// Show 'inccommand' preview. It works like this: +/// 1. Store current undo information so we can revert to current state later. +/// 2. Execute the preview callback with the parsed command, preview buffer number and preview +/// namespace number as arguments. The preview callback sets the highlight and does the +/// changes required for the preview if needed. +/// 3. Preview callback returns 0, 1 or 2. 0 means no preview is shown. 1 means preview is shown +/// but preview window doesn't need to be opened. 2 means preview is shown and preview window +/// needs to be opened if inccommand=split. +/// 4. Use the return value of the preview callback to determine whether to +/// open the preview window or not and open preview window if needed. +/// 5. If the return value of the preview callback is not 0, update the screen while the effects +/// of the preview are still in place. +/// 6. Revert all changes made by the preview callback. +static void cmdpreview_show(CommandLineState *s) +{ + // Parse the command line and return if it fails. + exarg_T ea; + CmdParseInfo cmdinfo; + // Copy the command line so we can modify it. + char *cmdline = xstrdup((char *)ccline.cmdbuff); + char *errormsg = NULL; + + parse_cmdline(cmdline, &ea, &cmdinfo, &errormsg); + if (errormsg != NULL) { + goto end; + } + + // Swap invalid command range if needed + if ((ea.argt & EX_RANGE) && ea.line1 > ea.line2) { + linenr_T lnum = ea.line1; + ea.line1 = ea.line2; + ea.line2 = lnum; + } + + time_t save_b_u_time_cur = curbuf->b_u_time_cur; + long save_b_u_seq_cur = curbuf->b_u_seq_cur; + u_header_T *save_b_u_newhead = curbuf->b_u_newhead; + long save_b_p_ul = curbuf->b_p_ul; + int save_b_changed = curbuf->b_changed; + int save_w_p_cul = curwin->w_p_cul; + int save_w_p_cuc = curwin->w_p_cuc; + bool save_hls = p_hls; + varnumber_T save_changedtick = buf_get_changedtick(curbuf); + bool icm_split = *p_icm == 's'; // inccommand=split + buf_T *cmdpreview_buf; + win_T *cmdpreview_win; + cmdmod_T save_cmdmod = cmdmod; + + cmdpreview = true; + emsg_silent++; // Block error reporting as the command may be incomplete + msg_silent++; // Block messages, namely ones that prompt + block_autocmds(); // Block events + garray_T save_view; + win_size_save(&save_view); // Save current window sizes + save_search_patterns(); // Save search patterns + curbuf->b_p_ul = LONG_MAX; // Make sure we can undo all changes + curwin->w_p_cul = false; // Disable 'cursorline' so it doesn't mess up the highlights + curwin->w_p_cuc = false; // Disable 'cursorcolumn' so it doesn't mess up the highlights + p_hls = false; // Don't show search highlighting during live substitution + cmdmod.split = 0; // Disable :leftabove/botright modifiers + cmdmod.tab = 0; // Disable :tab modifier + cmdmod.noswapfile = true; // Disable swap for preview buffer + + // Open preview buffer if inccommand=split. + if (!icm_split) { + cmdpreview_bufnr = 0; + } else if ((cmdpreview_buf = cmdpreview_open_buf()) == NULL) { + abort(); + } + + // Setup preview namespace if it's not already set. + if (!cmdpreview_ns) { + cmdpreview_ns = (int)nvim_create_namespace((String)STRING_INIT); + } + + // Execute the preview callback and use its return value to determine whether to show preview or + // open the preview window. The preview callback also handles doing the changes and highlights for + // the preview. + Error err = ERROR_INIT; + try_start(); + int cmdpreview_type = execute_cmd(&ea, &cmdinfo, true); + if (try_end(&err)) { + api_clear_error(&err); + cmdpreview_type = 0; + } + + // If inccommand=split and preview callback returns 2, open preview window. + if (icm_split && cmdpreview_type == 2 + && (cmdpreview_win = cmdpreview_open_win(cmdpreview_buf)) == NULL) { + abort(); + } + + // If preview callback is nonzero, update screen now. + if (cmdpreview_type != 0) { + int save_rd = RedrawingDisabled; + RedrawingDisabled = 0; + update_screen(SOME_VALID); + RedrawingDisabled = save_rd; + } + + // Close preview window if it's open. + if (icm_split && cmdpreview_type == 2 && cmdpreview_win != NULL) { + cmdpreview_close_win(); + } + // Clear preview highlights. + extmark_clear(curbuf, (uint32_t)cmdpreview_ns, 0, 0, MAXLNUM, MAXCOL); + + curbuf->b_changed = save_b_changed; // Preserve 'modified' during preview + + if (curbuf->b_u_seq_cur != save_b_u_seq_cur) { + // Undo invisibly. This also moves the cursor! + while (curbuf->b_u_seq_cur != save_b_u_seq_cur) { + if (!u_undo_and_forget(1)) { + abort(); + } + } + // Restore newhead. It is meaningless when curhead is valid, but we must + // restore it so that undotree() is identical before/after the preview. + curbuf->b_u_newhead = save_b_u_newhead; + curbuf->b_u_time_cur = save_b_u_time_cur; + } + if (save_changedtick != buf_get_changedtick(curbuf)) { + buf_set_changedtick(curbuf, save_changedtick); + } + + cmdmod = save_cmdmod; // Restore cmdmod + p_hls = save_hls; // Restore 'hlsearch' + curwin->w_p_cul = save_w_p_cul; // Restore 'cursorline' + curwin->w_p_cuc = save_w_p_cuc; // Restore 'cursorcolumn' + curbuf->b_p_ul = save_b_p_ul; // Restore 'undolevels' + restore_search_patterns(); // Restore search patterns + win_size_restore(&save_view); // Restore window sizes + ga_clear(&save_view); + unblock_autocmds(); // Unblock events + msg_silent--; // Unblock messages + emsg_silent--; // Unblock error reporting + + // Restore the window "view". + curwin->w_cursor = s->is_state.save_cursor; + restore_viewstate(&s->is_state.old_viewstate); + update_topline(curwin); + + redrawcmdline(); + + // If preview callback returned 0, update screen to clear remnants of an earlier preview. + if (cmdpreview_type == 0) { + update_screen(SOME_VALID); + } +end: + xfree(cmdline); +} + static int command_line_changed(CommandLineState *s) { // Trigger CmdlineChanged autocommands. @@ -2337,7 +2553,6 @@ static int command_line_changed(CommandLineState *s) } } - // 'incsearch' highlighting. if (s->firstc == ':' && current_sctx.sc_sid == 0 // only if interactive && *p_icm != NUL // 'inccommand' is set @@ -2345,27 +2560,9 @@ static int command_line_changed(CommandLineState *s) && cmdline_star == 0 // not typing a password && cmd_can_preview((char *)ccline.cmdbuff) && !vpeekc_any()) { - // Show 'inccommand' preview. It works like this: - // 1. Do the command. - // 2. Command implementation detects MODE_CMDPREVIEW state, then: - // - Update the screen while the effects are in place. - // - Immediately undo the effects. - State |= MODE_CMDPREVIEW; - emsg_silent++; // Block error reporting as the command may be incomplete - msg_silent++; // Block messages, namely ones that prompt - do_cmdline((char *)ccline.cmdbuff, NULL, NULL, DOCMD_KEEPLINE|DOCMD_NOWAIT|DOCMD_PREVIEW); - msg_silent--; // Unblock messages - emsg_silent--; // Unblock error reporting - - // Restore the window "view". - curwin->w_cursor = s->is_state.save_cursor; - restore_viewstate(&s->is_state.old_viewstate); - update_topline(curwin); - - redrawcmdline(); - } else if (State & MODE_CMDPREVIEW) { - State = (State & ~MODE_CMDPREVIEW); - close_preview_windows(); + cmdpreview_show(s); + } else if (cmdpreview) { + cmdpreview = false; update_screen(SOME_VALID); // Clear 'inccommand' preview. } else { if (s->xpc.xp_context == EXPAND_NOTHING && (KeyTyped || vpeekc() == NUL)) { diff --git a/src/nvim/fold.c b/src/nvim/fold.c index 234c11227d..d5277b9910 100644 --- a/src/nvim/fold.c +++ b/src/nvim/fold.c @@ -754,8 +754,7 @@ void deleteFold(win_T *const wp, const linenr_T start, const linenr_T end, const // the modification of the *first* line of the fold, but we send through a // notification that includes every line that was part of the fold int64_t num_changed = last_lnum - first_lnum; - buf_updates_send_changes(wp->w_buffer, first_lnum, num_changed, - num_changed, true); + buf_updates_send_changes(wp->w_buffer, first_lnum, num_changed, num_changed); } } @@ -1614,7 +1613,7 @@ static void foldCreateMarkers(win_T *wp, pos_T start, pos_T end) // u_save() is unable to save the buffer line, but we send the // nvim_buf_lines_event anyway since it won't do any harm. int64_t num_changed = 1 + end.lnum - start.lnum; - buf_updates_send_changes(buf, start.lnum, num_changed, num_changed, true); + buf_updates_send_changes(buf, start.lnum, num_changed, num_changed); } // foldAddMarker() {{{2 diff --git a/src/nvim/generators/gen_api_dispatch.lua b/src/nvim/generators/gen_api_dispatch.lua index c6dd25154b..0f7052d351 100644 --- a/src/nvim/generators/gen_api_dispatch.lua +++ b/src/nvim/generators/gen_api_dispatch.lua @@ -16,6 +16,10 @@ local functions = {} local nvimdir = arg[1] package.path = nvimdir .. '/?.lua;' .. package.path +_G.vim = loadfile(nvimdir..'/../../runtime/lua/vim/shared.lua')() + +local hashy = require'generators.hashy' + -- names of all headers relative to the source root (for inclusion in the -- generated file) local headers = {} @@ -208,8 +212,8 @@ for i = 1, #functions do output:write('Object handle_'..fn.name..'(uint64_t channel_id, Array args, Error *error)') output:write('\n{') - output:write('\n#if MIN_LOG_LEVEL <= DEBUG_LOG_LEVEL') - output:write('\n logmsg(DEBUG_LOG_LEVEL, "RPC: ", NULL, -1, true, "ch %" PRIu64 ": invoke ' + output:write('\n#if MIN_LOG_LEVEL <= LOGLVL_DBG') + output:write('\n logmsg(LOGLVL_DBG, "RPC: ", NULL, -1, true, "ch %" PRIu64 ": invoke ' ..fn.name..'", channel_id);') output:write('\n#endif') output:write('\n Object ret = NIL;') @@ -339,24 +343,27 @@ for i = 1, #functions do end end --- Generate a function that initializes method names with handler functions -output:write([[ -void msgpack_rpc_init_method_table(void) -{ -]]) - -for i = 1, #functions do - local fn = functions[i] +local remote_fns = {} +for _,fn in ipairs(functions) do if fn.remote then - output:write(' msgpack_rpc_add_method_handler('.. - '(String) {.data = "'..fn.name..'", '.. - '.size = sizeof("'..fn.name..'") - 1}, '.. - '(MsgpackRpcRequestHandler) {.fn = handle_'.. (fn.impl_name or fn.name).. - ', .fast = '..tostring(fn.fast)..'});\n') + remote_fns[fn.name] = fn end end +remote_fns.redraw = {impl_name="ui_client_redraw", fast=true} + +local hashorder, hashfun = hashy.hashy_hash("msgpack_rpc_get_handler_for", vim.tbl_keys(remote_fns), function (idx) + return "method_handlers["..idx.."].name" +end) + +output:write("static const MsgpackRpcRequestHandler method_handlers[] = {\n") +for _, name in ipairs(hashorder) do + local fn = remote_fns[name] + output:write(' { .name = "'..name..'", .fn = handle_'.. (fn.impl_name or fn.name).. + ', .fast = '..tostring(fn.fast)..'},\n') +end +output:write("};\n\n") +output:write(hashfun) -output:write('\n}\n\n') output:close() local mpack_output = io.open(mpack_outputf, 'wb') diff --git a/src/nvim/generators/gen_api_ui_events.lua b/src/nvim/generators/gen_api_ui_events.lua index 5e70442dce..99dfac05e8 100755 --- a/src/nvim/generators/gen_api_ui_events.lua +++ b/src/nvim/generators/gen_api_ui_events.lua @@ -15,6 +15,9 @@ local client_output = io.open(arg[8], 'wb') local c_grammar = require('generators.c_grammar') local events = c_grammar.grammar:match(input:read('*all')) +_G.vim = loadfile(nvimdir..'/../../runtime/lua/vim/shared.lua')() +local hashy = require'generators.hashy' + local function write_signature(output, ev, prefix, notype) output:write('('..prefix) if prefix == "" and #ev.parameters == 0 then @@ -213,24 +216,25 @@ for i = 1, #events do end end --- Generate the map_init method for client handlers -client_output:write([[ -void ui_client_methods_table_init(void) -{ +local client_events = {} +for _,ev in ipairs(events) do + if (not ev.noexport) and ((not ev.remote_only) or ev.client_impl) then + client_events[ev.name] = ev + end +end -]]) +local hashorder, hashfun = hashy.hashy_hash("ui_client_handler", vim.tbl_keys(client_events), function (idx) + return "event_handlers["..idx.."].name" +end) -for i = 1, #events do - local fn = events[i] - if (not fn.noexport) and ((not fn.remote_only) or fn.client_impl) then - client_output:write(' add_ui_client_event_handler('.. - '(String) {.data = "'..fn.name..'", '.. - '.size = sizeof("'..fn.name..'") - 1}, '.. - '(UIClientHandler) ui_client_event_'..fn.name..');\n') - end +client_output:write("static const UIClientHandler event_handlers[] = {\n") + +for _, name in ipairs(hashorder) do + client_output:write(' { .name = "'..name..'", .fn = ui_client_event_'..name..'},\n') end -client_output:write('\n}\n\n') +client_output:write('\n};\n\n') +client_output:write(hashfun) proto_output:close() call_output:close() diff --git a/src/nvim/generators/gen_ex_cmds.lua b/src/nvim/generators/gen_ex_cmds.lua index 27cfe194fa..255c415a4d 100644 --- a/src/nvim/generators/gen_ex_cmds.lua +++ b/src/nvim/generators/gen_ex_cmds.lua @@ -65,20 +65,31 @@ for _, cmd in ipairs(defs) do assert(cmd.addr_type ~= 'ADDR_OTHER' and cmd.addr_type ~= 'ADDR_NONE', string.format('ex_cmds.lua:%s: Missing misplaced DFLALL\n', cmd.command)) end + if bit.band(cmd.flags, flags.PREVIEW) == flags.PREVIEW then + assert(cmd.preview_func ~= nil, + string.format('ex_cmds.lua:%s: Missing preview_func\n', cmd.command)) + end local enumname = cmd.enum or ('CMD_' .. cmd.command) local byte_cmd = cmd.command:sub(1, 1):byte() if byte_a <= byte_cmd and byte_cmd <= byte_z then table.insert(cmds, cmd.command) end + local preview_func + if cmd.preview_func then + preview_func = string.format("(ex_preview_func_T)&%s", cmd.preview_func) + else + preview_func = "NULL" + end enumfile:write(' ' .. enumname .. ',\n') defsfile:write(string.format([[ [%s] = { .cmd_name = "%s", .cmd_func = (ex_func_T)&%s, + .cmd_preview_func = %s, .cmd_argt = %uL, .cmd_addr_type = %s }, -]], enumname, cmd.command, cmd.func, cmd.flags, cmd.addr_type)) +]], enumname, cmd.command, cmd.func, preview_func, cmd.flags, cmd.addr_type)) end for i = #cmds, 1, -1 do local cmd = cmds[i] diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index 788e1113e2..6b716c2e1f 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -629,7 +629,7 @@ void stuffReadbuffSpec(const char *s) stuffReadbuffLen(s, 3); s += 3; } else { - int c = mb_ptr2char_adv((const char_u **)&s); + int c = mb_cptr2char_adv((const char_u **)&s); if (c == CAR || c == NL || c == ESC) { c = ' '; } @@ -2328,19 +2328,19 @@ static int vgetorpeek(bool advance) // try re-mapping. for (;;) { check_end_reg_executing(advance); - // os_breakcheck() can call input_enqueue() - if ((mapped_ctrl_c | curbuf->b_mapped_ctrl_c) & get_real_state()) { - ctrl_c_interrupts = false; - } // os_breakcheck() is slow, don't use it too often when // inside a mapping. But call it each time for typed // characters. if (typebuf.tb_maplen) { line_breakcheck(); } else { + // os_breakcheck() can call input_enqueue() + if ((mapped_ctrl_c | curbuf->b_mapped_ctrl_c) & get_real_state()) { + ctrl_c_interrupts = false; + } os_breakcheck(); // check for CTRL-C + ctrl_c_interrupts = true; } - ctrl_c_interrupts = true; int keylen = 0; if (got_int) { // flush all input @@ -2585,8 +2585,8 @@ static int vgetorpeek(bool advance) // get a character: 3. from the user - get it if (typebuf.tb_len == 0) { - // timedout may have been set while waiting for a mapping - // that has a <Nop> RHS. + // timedout may have been set if a mapping with empty RHS + // fully matched while longer mappings timed out. timedout = false; } @@ -2839,8 +2839,8 @@ int fix_input_buffer(char_u *buf, int len) /// the final `lhs` exceeds `MAXMAPLEN`, `lhs_len` will be set equal to the /// original larger length and `lhs` will be truncated. /// -/// If RHS is equal to "<Nop>", `rhs` will be the empty string, `rhs_len` -/// will be zero, and `rhs_is_noop` will be set to true. +/// If RHS should be <Nop>, `rhs` will be an empty string, `rhs_len` will be +/// zero, and `rhs_is_noop` will be set to true. /// /// Any memory allocated by @ref replace_termcodes is freed before this function /// returns. @@ -2898,8 +2898,9 @@ void set_maparg_lhs_rhs(const char *const orig_lhs, const size_t orig_lhs_len, replaced = replace_termcodes(orig_rhs, orig_rhs_len, &rhs_buf, REPTERM_DO_LT, NULL, cpo_flags); mapargs->rhs_len = STRLEN(replaced); - // XXX: even when orig_rhs is non-empty, replace_termcodes may produce an empty string. - mapargs->rhs_is_noop = orig_rhs[0] != NUL && mapargs->rhs_len == 0; + // XXX: replace_termcodes may produce an empty string even if orig_rhs is non-empty + // (e.g. a single ^V, see :h map-empty-rhs) + mapargs->rhs_is_noop = orig_rhs_len != 0 && mapargs->rhs_len == 0; mapargs->rhs = xcalloc(mapargs->rhs_len + 1, sizeof(char_u)); STRLCPY(mapargs->rhs, replaced, mapargs->rhs_len + 1); } diff --git a/src/nvim/getchar.h b/src/nvim/getchar.h index 237f0632bd..ddd6d81aef 100644 --- a/src/nvim/getchar.h +++ b/src/nvim/getchar.h @@ -54,8 +54,8 @@ struct map_arguments { char_u *rhs; /// The {rhs} of the mapping. size_t rhs_len; - LuaRef rhs_lua; /// lua function as rhs - bool rhs_is_noop; /// True when the {orig_rhs} is <nop>. + LuaRef rhs_lua; /// lua function as {rhs} + bool rhs_is_noop; /// True when the {rhs} should be <Nop>. char_u *orig_rhs; /// The original text of the {rhs}. size_t orig_rhs_len; diff --git a/src/nvim/globals.h b/src/nvim/globals.h index b0006ebaca..a42c8e979d 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -76,7 +76,8 @@ EXTERN struct nvim_stats_s { int64_t fsync; int64_t redraw; -} g_stats INIT(= { 0, 0 }); + int16_t log_skip; // How many logs were tried and skipped before log_init. +} g_stats INIT(= { 0, 0, 0 }); // Values for "starting". #define NO_SCREEN 2 // no screen updating yet @@ -638,6 +639,9 @@ EXTERN int motion_force INIT(=0); // motion force for pending operator EXTERN bool exmode_active INIT(= false); // true if Ex mode is active EXTERN bool ex_no_reprint INIT(=false); // No need to print after z or p. +// 'inccommand' command preview state +EXTERN bool cmdpreview INIT(= false); + EXTERN int reg_recording INIT(= 0); // register for recording or zero EXTERN int reg_executing INIT(= 0); // register being executed or zero // Flag set when peeking a character and found the end of executed register @@ -1029,7 +1033,7 @@ EXTERN FILE *time_fd INIT(= NULL); // where to write startup timing // the warning. EXTERN int vim_ignored; -// Start a msgpack-rpc channel over stdin/stdout. +// stdio is an RPC channel (--embed). EXTERN bool embedded_mode INIT(= false); // Do not start a UI nor read/write to stdio (unless embedding). EXTERN bool headless_mode INIT(= false); diff --git a/src/nvim/highlight_defs.h b/src/nvim/highlight_defs.h index caf345386d..9084cb500c 100644 --- a/src/nvim/highlight_defs.h +++ b/src/nvim/highlight_defs.h @@ -113,6 +113,7 @@ typedef enum { HLF_BORDER, // Floating window border HLF_WBR, // Window bars HLF_WBRNC, // Window bars of not-current windows + HLF_CU, // Cursor HLF_COUNT, // MUST be the last one } hlf_T; @@ -176,6 +177,7 @@ EXTERN const char *hlf_names[] INIT(= { [HLF_BORDER] = "FloatBorder", [HLF_WBR] = "WinBar", [HLF_WBRNC] = "WinBarNC", + [HLF_CU] = "Cursor", }); EXTERN int highlight_attr[HLF_COUNT]; // Highl. attr for each context. diff --git a/src/nvim/highlight_group.c b/src/nvim/highlight_group.c index 05781dd7e2..9d61141e98 100644 --- a/src/nvim/highlight_group.c +++ b/src/nvim/highlight_group.c @@ -738,6 +738,8 @@ void set_hl_group(int id, HlAttrs attrs, Dict(highlight) *dict, int link_id) g->sg_script_ctx = current_sctx; g->sg_script_ctx.sc_lnum += sourcing_lnum; + g->sg_attr = hl_get_syn_attr(0, id, attrs); + // 'Normal' is special if (STRCMP(g->sg_name_u, "NORMAL") == 0) { cterm_normal_fg_color = g->sg_cterm_fg; @@ -747,8 +749,6 @@ void set_hl_group(int id, HlAttrs attrs, Dict(highlight) *dict, int link_id) normal_sp = g->sg_rgb_sp; ui_default_colors_set(); } else { - g->sg_attr = hl_get_syn_attr(0, id, attrs); - // a cursor style uses this syn_id, make sure its attribute is updated. if (cursor_mode_uses_syn_id(id)) { ui_mode_info_set(); diff --git a/src/nvim/log.c b/src/nvim/log.c index 815d53b570..0f410f7c8c 100644 --- a/src/nvim/log.c +++ b/src/nvim/log.c @@ -17,6 +17,8 @@ #include "auto/config.h" #include "nvim/log.h" +#include "nvim/main.h" +#include "nvim/message.h" #include "nvim/os/os.h" #include "nvim/os/time.h" #include "nvim/types.h" @@ -26,6 +28,7 @@ /// Cached location of the expanded log file path decided by log_path_init(). static char log_file_path[MAXPATHL + 1] = { 0 }; +static bool did_log_init = false; static uv_mutex_t mutex; #ifdef INCLUDE_GENERATED_DECLARATIONS @@ -51,17 +54,11 @@ static bool log_try_create(char *fname) /// Initializes path to log file. Sets $NVIM_LOG_FILE if empty. /// -/// Tries $NVIM_LOG_FILE, or falls back to $XDG_STATE_HOME/nvim/log. Path to log -/// file is cached, so only the first call has effect, unless first call was not -/// successful. Failed initialization indicates either a bug in expand_env() -/// or both $NVIM_LOG_FILE and $HOME environment variables are undefined. -/// -/// @return true if path was initialized, false otherwise. -static bool log_path_init(void) +/// Tries $NVIM_LOG_FILE, or falls back to $XDG_STATE_HOME/nvim/log. Failed +/// initialization indicates either a bug in expand_env() or both $NVIM_LOG_FILE +/// and $HOME environment variables are undefined. +static void log_path_init(void) { - if (log_file_path[0]) { - return true; - } size_t size = sizeof(log_file_path); expand_env((char_u *)"$" LOG_FILE_ENV, (char_u *)log_file_path, (int)size - 1); @@ -69,7 +66,7 @@ static bool log_path_init(void) || log_file_path[0] == '\0' || os_isdir((char_u *)log_file_path) || !log_try_create(log_file_path)) { - // Make kXDGStateHome if it does not exist. + // Make $XDG_STATE_HOME if it does not exist. char *loghome = get_xdg_home(kXDGStateHome); char *failed_dir = NULL; bool log_dir_failure = false; @@ -88,7 +85,7 @@ static bool log_path_init(void) // Fall back to stderr if (len >= size || !log_try_create(log_file_path)) { log_file_path[0] = '\0'; - return false; + return; } os_setenv(LOG_FILE_ENV, log_file_path, true); if (log_dir_failure) { @@ -97,13 +94,14 @@ static bool log_path_init(void) } XFREE_CLEAR(failed_dir); } - return true; } void log_init(void) { - uv_mutex_init(&mutex); + uv_mutex_init_recursive(&mutex); + // AFTER init_homedir ("~", XDG) and set_init_1 (env vars). 22b52dd462e5 #11501 log_path_init(); + did_log_init = true; } void log_lock(void) @@ -116,6 +114,14 @@ void log_unlock(void) uv_mutex_unlock(&mutex); } +static void on_log_recursive_event(void **argv) +{ + char *fn_name = argv[0]; + ptrdiff_t linenr = (ptrdiff_t)argv[1]; + siemsg("E5430: %s:%d: recursive log!", fn_name, linenr); + xfree(fn_name); +} + /// Logs a message to $NVIM_LOG_FILE. /// /// @param log_level Log level (see log.h) @@ -124,10 +130,20 @@ void log_unlock(void) /// @param line_num Source line number, or -1 /// @param eol Append linefeed "\n" /// @param fmt printf-style format string +/// +/// @return true if log was emitted normally, false if failed or recursive bool logmsg(int log_level, const char *context, const char *func_name, int line_num, bool eol, const char *fmt, ...) FUNC_ATTR_UNUSED FUNC_ATTR_PRINTF(6, 7) { + static bool recursive = false; + static bool did_msg = false; // Showed recursion message? + if (!did_log_init) { + g_stats.log_skip++; + // set_init_1 may try logging before we are ready. 6f27f5ef91b3 #10183 + return false; + } + if (log_level < MIN_LOG_LEVEL) { return false; } @@ -139,13 +155,21 @@ bool logmsg(int log_level, const char *context, const char *func_name, int line_ #endif log_lock(); + if (recursive) { + if (!did_msg) { + did_msg = true; + char *arg1 = func_name ? xstrdup(func_name) : (context ? xstrdup(context) : NULL); + // coverity[leaked_storage] + loop_schedule_deferred(&main_loop, event_create(on_log_recursive_event, 2, arg1, line_num)); + } + g_stats.log_skip++; + log_unlock(); + return false; + } + recursive = true; bool ret = false; FILE *log_file = open_log_file(); - if (log_file == NULL) { - goto end; - } - va_list args; va_start(args, fmt); ret = v_do_log_to_file(log_file, log_level, context, func_name, line_num, @@ -155,7 +179,8 @@ bool logmsg(int log_level, const char *context, const char *func_name, int line_ if (log_file != stderr && log_file != stdout) { fclose(log_file); } -end: + + recursive = false; log_unlock(); return ret; } @@ -166,51 +191,36 @@ void log_uv_handles(void *loop) log_lock(); FILE *log_file = open_log_file(); - if (log_file == NULL) { - goto end; - } - uv_print_all_handles(l, log_file); if (log_file != stderr && log_file != stdout) { fclose(log_file); } -end: + log_unlock(); } /// Open the log file for appending. /// -/// @return FILE* decided by log_path_init() or stderr in case of error +/// @return Log file, or stderr on failure FILE *open_log_file(void) { - static bool opening_log_file = false; - // Disallow recursion. (This only matters for log_path_init; for logmsg and - // friends we use a mutex: log_lock). - if (opening_log_file) { - do_log_to_file(stderr, ERROR_LOG_LEVEL, NULL, __func__, __LINE__, true, - "Cannot LOG() recursively."); - return stderr; - } - - FILE *log_file = NULL; - opening_log_file = true; - if (log_path_init()) { - log_file = fopen(log_file_path, "a"); - } - opening_log_file = false; - - if (log_file != NULL) { - return log_file; + errno = 0; + if (log_file_path[0]) { + FILE *f = fopen(log_file_path, "a"); + if (f != NULL) { + return f; + } } // May happen if: - // - LOG() is called before early_init() + // - fopen() failed + // - LOG() is called before log_init() // - Directory does not exist // - File is not writable - do_log_to_file(stderr, ERROR_LOG_LEVEL, NULL, __func__, __LINE__, true, - "Logging to stderr, failed to open $" LOG_FILE_ENV ": %s", - log_file_path); + do_log_to_file(stderr, LOGLVL_ERR, NULL, __func__, __LINE__, true, + "failed to open $" LOG_FILE_ENV " (%s): %s", + strerror(errno), log_file_path); return stderr; } @@ -237,8 +247,7 @@ void log_callstack_to_file(FILE *log_file, const char *const func_name, const in // Now we have a command string like: // addr2line -e /path/to/exe -f -p 0x123 0x456 ... - do_log_to_file(log_file, DEBUG_LOG_LEVEL, NULL, func_name, line_num, true, - "trace:"); + do_log_to_file(log_file, LOGLVL_DBG, NULL, func_name, line_num, true, "trace:"); FILE *fp = popen(cmdbuf, "r"); char linebuf[IOSIZE]; while (fgets(linebuf, sizeof(linebuf) - 1, fp) != NULL) { @@ -255,13 +264,7 @@ void log_callstack(const char *const func_name, const int line_num) { log_lock(); FILE *log_file = open_log_file(); - if (log_file == NULL) { - goto end; - } - log_callstack_to_file(log_file, func_name, line_num); - -end: log_unlock(); } #endif @@ -285,12 +288,12 @@ static bool v_do_log_to_file(FILE *log_file, int log_level, const char *context, FUNC_ATTR_PRINTF(7, 0) { static const char *log_levels[] = { - [DEBUG_LOG_LEVEL] = "DEBUG", - [INFO_LOG_LEVEL] = "INFO ", - [WARN_LOG_LEVEL] = "WARN ", - [ERROR_LOG_LEVEL] = "ERROR", + [LOGLVL_DBG] = "DBG", + [LOGLVL_INF] = "INF", + [LOGLVL_WRN] = "WRN", + [LOGLVL_ERR] = "ERR", }; - assert(log_level >= DEBUG_LOG_LEVEL && log_level <= ERROR_LOG_LEVEL); + assert(log_level >= LOGLVL_DBG && log_level <= LOGLVL_ERR); // Format the timestamp. struct tm local_time; diff --git a/src/nvim/log.h b/src/nvim/log.h index 724d073d02..cbee0e0f81 100644 --- a/src/nvim/log.h +++ b/src/nvim/log.h @@ -16,11 +16,11 @@ # define NVIM_PROBE(name, n, ...) #endif -#define TRACE_LOG_LEVEL 0 -#define DEBUG_LOG_LEVEL 1 -#define INFO_LOG_LEVEL 2 -#define WARN_LOG_LEVEL 3 -#define ERROR_LOG_LEVEL 4 +#define LOGLVL_TRC 0 +#define LOGLVL_DBG 1 +#define LOGLVL_INF 2 +#define LOGLVL_WRN 3 +#define LOGLVL_ERR 4 #define DLOG(...) #define DLOGN(...) @@ -32,46 +32,37 @@ #define ELOGN(...) #ifndef MIN_LOG_LEVEL -# define MIN_LOG_LEVEL INFO_LOG_LEVEL +# define MIN_LOG_LEVEL LOGLVL_INF #endif -#define LOG(level, ...) logmsg((level), NULL, __func__, __LINE__, true, \ - __VA_ARGS__) +#define LOG(level, ...) logmsg((level), NULL, __func__, __LINE__, true, __VA_ARGS__) -#if MIN_LOG_LEVEL <= DEBUG_LOG_LEVEL +#if MIN_LOG_LEVEL <= LOGLVL_DBG # undef DLOG # undef DLOGN -# define DLOG(...) logmsg(DEBUG_LOG_LEVEL, NULL, __func__, __LINE__, true, \ - __VA_ARGS__) -# define DLOGN(...) logmsg(DEBUG_LOG_LEVEL, NULL, __func__, __LINE__, false, \ - __VA_ARGS__) +# define DLOG(...) logmsg(LOGLVL_DBG, NULL, __func__, __LINE__, true, __VA_ARGS__) +# define DLOGN(...) logmsg(LOGLVL_DBG, NULL, __func__, __LINE__, false, __VA_ARGS__) #endif -#if MIN_LOG_LEVEL <= INFO_LOG_LEVEL +#if MIN_LOG_LEVEL <= LOGLVL_INF # undef ILOG # undef ILOGN -# define ILOG(...) logmsg(INFO_LOG_LEVEL, NULL, __func__, __LINE__, true, \ - __VA_ARGS__) -# define ILOGN(...) logmsg(INFO_LOG_LEVEL, NULL, __func__, __LINE__, false, \ - __VA_ARGS__) +# define ILOG(...) logmsg(LOGLVL_INF, NULL, __func__, __LINE__, true, __VA_ARGS__) +# define ILOGN(...) logmsg(LOGLVL_INF, NULL, __func__, __LINE__, false, __VA_ARGS__) #endif -#if MIN_LOG_LEVEL <= WARN_LOG_LEVEL +#if MIN_LOG_LEVEL <= LOGLVL_WRN # undef WLOG # undef WLOGN -# define WLOG(...) logmsg(WARN_LOG_LEVEL, NULL, __func__, __LINE__, true, \ - __VA_ARGS__) -# define WLOGN(...) logmsg(WARN_LOG_LEVEL, NULL, __func__, __LINE__, false, \ - __VA_ARGS__) +# define WLOG(...) logmsg(LOGLVL_WRN, NULL, __func__, __LINE__, true, __VA_ARGS__) +# define WLOGN(...) logmsg(LOGLVL_WRN, NULL, __func__, __LINE__, false, __VA_ARGS__) #endif -#if MIN_LOG_LEVEL <= ERROR_LOG_LEVEL +#if MIN_LOG_LEVEL <= LOGLVL_ERR # undef ELOG # undef ELOGN -# define ELOG(...) logmsg(ERROR_LOG_LEVEL, NULL, __func__, __LINE__, true, \ - __VA_ARGS__) -# define ELOGN(...) logmsg(ERROR_LOG_LEVEL, NULL, __func__, __LINE__, false, \ - __VA_ARGS__) +# define ELOG(...) logmsg(LOGLVL_ERR, NULL, __func__, __LINE__, true, __VA_ARGS__) +# define ELOGN(...) logmsg(LOGLVL_ERR, NULL, __func__, __LINE__, false, __VA_ARGS__) #endif #ifdef HAVE_EXECINFO_BACKTRACE diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index 0009420281..3ba4d0d70c 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -40,6 +40,7 @@ #include "nvim/undo.h" #include "nvim/version.h" #include "nvim/vim.h" +#include "nvim/window.h" static int in_fast_callback = 0; @@ -1839,11 +1840,12 @@ cleanup: xfree(info); } -void nlua_do_ucmd(ucmd_T *cmd, exarg_T *eap) +/// @param preview Invoke the callback as a |:command-preview| handler. +int nlua_do_ucmd(ucmd_T *cmd, exarg_T *eap, bool preview) { lua_State *const lstate = global_lstate; - nlua_pushref(lstate, cmd->uc_luaref); + nlua_pushref(lstate, preview ? cmd->uc_preview_luaref : cmd->uc_luaref); lua_newtable(lstate); lua_pushboolean(lstate, eap->forceit == 1); @@ -1913,7 +1915,87 @@ void nlua_do_ucmd(ucmd_T *cmd, exarg_T *eap) lua_pushstring(lstate, buf); lua_setfield(lstate, -2, "mods"); - if (nlua_pcall(lstate, 1, 0)) { + lua_newtable(lstate); // smods table + + lua_pushinteger(lstate, cmdmod.tab); + lua_setfield(lstate, -2, "tab"); + + lua_pushinteger(lstate, eap->verbose_save != -1 ? p_verbose : -1); + lua_setfield(lstate, -2, "verbose"); + + if (cmdmod.split & WSP_ABOVE) { + lua_pushstring(lstate, "aboveleft"); + } else if (cmdmod.split & WSP_BELOW) { + lua_pushstring(lstate, "belowright"); + } else if (cmdmod.split & WSP_TOP) { + lua_pushstring(lstate, "topleft"); + } else if (cmdmod.split & WSP_BOT) { + lua_pushstring(lstate, "botright"); + } else { + lua_pushstring(lstate, ""); + } + lua_setfield(lstate, -2, "split"); + + lua_pushboolean(lstate, cmdmod.split & WSP_VERT); + lua_setfield(lstate, -2, "vertical"); + lua_pushboolean(lstate, eap->save_msg_silent != -1 ? (msg_silent != 0) : 0); + lua_setfield(lstate, -2, "silent"); + lua_pushboolean(lstate, eap->did_esilent); + lua_setfield(lstate, -2, "emsg_silent"); + lua_pushboolean(lstate, eap->did_sandbox); + lua_setfield(lstate, -2, "sandbox"); + lua_pushboolean(lstate, cmdmod.save_ei != NULL); + lua_setfield(lstate, -2, "noautocmd"); + + typedef struct { + bool *set; + char *name; + } mod_entry_T; + static mod_entry_T mod_entries[] = { + { &cmdmod.browse, "browse" }, + { &cmdmod.confirm, "confirm" }, + { &cmdmod.hide, "hide" }, + { &cmdmod.keepalt, "keepalt" }, + { &cmdmod.keepjumps, "keepjumps" }, + { &cmdmod.keepmarks, "keepmarks" }, + { &cmdmod.keeppatterns, "keeppatterns" }, + { &cmdmod.lockmarks, "lockmarks" }, + { &cmdmod.noswapfile, "noswapfile" } + }; + + // The modifiers that are simple flags + for (size_t i = 0; i < ARRAY_SIZE(mod_entries); i++) { + lua_pushboolean(lstate, *mod_entries[i].set); + lua_setfield(lstate, -2, mod_entries[i].name); + } + + lua_setfield(lstate, -2, "smods"); + + if (preview) { + lua_pushinteger(lstate, cmdpreview_get_ns()); + + handle_T cmdpreview_bufnr = cmdpreview_get_bufnr(); + if (cmdpreview_bufnr != 0) { + lua_pushinteger(lstate, cmdpreview_bufnr); + } else { + lua_pushnil(lstate); + } + } + + if (nlua_pcall(lstate, preview ? 3 : 1, preview ? 1 : 0)) { nlua_error(lstate, _("Error executing Lua callback: %.*s")); + return 0; } + + int retv = 0; + + if (preview) { + if (lua_isnumber(lstate, -1) && (retv = (int)lua_tointeger(lstate, -1)) >= 0 && retv <= 2) { + lua_pop(lstate, 1); + } else { + retv = 0; + } + } + + return retv; } diff --git a/src/nvim/lua/treesitter.c b/src/nvim/lua/treesitter.c index 6ec9cfd21d..b96193d199 100644 --- a/src/nvim/lua/treesitter.c +++ b/src/nvim/lua/treesitter.c @@ -391,7 +391,7 @@ static int parser_parse(lua_State *L) return luaL_error(L, "An error occurred when parsing."); } - // The new tree will be pushed to the stack, without copy, owwership is now to + // The new tree will be pushed to the stack, without copy, ownership is now to // the lua GC. // Old tree is still owned by the lua GC. uint32_t n_ranges = 0; diff --git a/src/nvim/main.c b/src/nvim/main.c index 936b42be23..71c1ddfae1 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -121,7 +121,6 @@ void event_init(void) resize_events = multiqueue_new_child(main_loop.events); // early msgpack-rpc initialization - msgpack_rpc_init_method_table(); msgpack_rpc_helpers_init(); input_init(); signal_init(); @@ -264,6 +263,8 @@ int main(int argc, char **argv) nlua_init(); + TIME_MSG("init lua interpreter"); + if (embedded_mode) { const char *err; if (!channel_from_stdio(true, CALLBACK_READER_INIT, &err)) { diff --git a/src/nvim/map.c b/src/nvim/map.c index 05ad113008..d27e40b4ee 100644 --- a/src/nvim/map.c +++ b/src/nvim/map.c @@ -14,7 +14,6 @@ #include <stdlib.h> #include <string.h> -#include "nvim/api/private/dispatch.h" #include "nvim/lib/khash.h" #include "nvim/map.h" #include "nvim/map_defs.h" @@ -171,13 +170,10 @@ MAP_IMPL(uint64_t, ssize_t, SSIZE_INITIALIZER) MAP_IMPL(uint64_t, uint64_t, DEFAULT_INITIALIZER) MAP_IMPL(uint32_t, uint32_t, DEFAULT_INITIALIZER) MAP_IMPL(handle_T, ptr_t, DEFAULT_INITIALIZER) -#define MSGPACK_HANDLER_INITIALIZER { .fn = NULL, .fast = false } -MAP_IMPL(String, MsgpackRpcRequestHandler, MSGPACK_HANDLER_INITIALIZER) MAP_IMPL(HlEntry, int, DEFAULT_INITIALIZER) MAP_IMPL(String, handle_T, 0) MAP_IMPL(String, int, DEFAULT_INITIALIZER) MAP_IMPL(int, String, DEFAULT_INITIALIZER) -MAP_IMPL(String, UIClientHandler, NULL) MAP_IMPL(ColorKey, ColorItem, COLOR_ITEM_INITIALIZER) diff --git a/src/nvim/map.h b/src/nvim/map.h index 693ef50127..4f4aaa3552 100644 --- a/src/nvim/map.h +++ b/src/nvim/map.h @@ -4,7 +4,6 @@ #include <stdbool.h> #include "nvim/api/private/defs.h" -#include "nvim/api/private/dispatch.h" #include "nvim/extmark_defs.h" #include "nvim/highlight_defs.h" #include "nvim/map_defs.h" @@ -44,12 +43,10 @@ MAP_DECLS(uint64_t, uint64_t) MAP_DECLS(uint32_t, uint32_t) MAP_DECLS(handle_T, ptr_t) -MAP_DECLS(String, MsgpackRpcRequestHandler) MAP_DECLS(HlEntry, int) MAP_DECLS(String, handle_T) MAP_DECLS(String, int) MAP_DECLS(int, String) -MAP_DECLS(String, UIClientHandler) MAP_DECLS(ColorKey, ColorItem) diff --git a/src/nvim/match.c b/src/nvim/match.c index 54f3bff472..4bfc06d7e5 100644 --- a/src/nvim/match.c +++ b/src/nvim/match.c @@ -873,11 +873,11 @@ void f_getmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) int i; win_T *win = get_optional_window(argvars, 0); + tv_list_alloc_ret(rettv, kListLenMayKnow); if (win == NULL) { return; } - tv_list_alloc_ret(rettv, kListLenMayKnow); cur = win->w_match_head; while (cur != NULL) { dict_T *dict = tv_dict_alloc(); diff --git a/src/nvim/move.c b/src/nvim/move.c index 9f72b36638..8c927d30d1 100644 --- a/src/nvim/move.c +++ b/src/nvim/move.c @@ -108,14 +108,14 @@ void redraw_for_cursorline(win_T *wp) } /// Redraw when w_virtcol changes and 'cursorcolumn' is set or 'cursorlineopt' -/// contains "screenline" or when the 'CurSearch' highlight is in use. +/// contains "screenline" or when the "CurSearch" highlight is in use. /// Also when concealing is on and 'concealcursor' is active. static void redraw_for_cursorcolumn(win_T *wp) FUNC_ATTR_NONNULL_ALL { if ((wp->w_valid & VALID_VIRTCOL) == 0 && !pum_visible()) { if (wp->w_p_cuc || ((HL_ATTR(HLF_LC) || wp->w_hl_ids[HLF_LC]) && using_hlsearch())) { - // When 'cursorcolumn' is set or 'CurSearch' is in use + // When 'cursorcolumn' is set or "CurSearch" is in use // need to redraw with SOME_VALID. redraw_later(wp, SOME_VALID); } else if (wp->w_p_cul && (wp->w_p_culopt_flags & CULOPT_SCRLINE)) { diff --git a/src/nvim/msgpack_rpc/channel.c b/src/nvim/msgpack_rpc/channel.c index 79ecd9f827..79a9e1082d 100644 --- a/src/nvim/msgpack_rpc/channel.c +++ b/src/nvim/msgpack_rpc/channel.c @@ -31,7 +31,7 @@ #include "nvim/ui.h" #include "nvim/vim.h" -#if MIN_LOG_LEVEL > DEBUG_LOG_LEVEL +#if MIN_LOG_LEVEL > LOGLVL_DBG # define log_client_msg(...) # define log_server_msg(...) #endif @@ -55,14 +55,15 @@ void rpc_start(Channel *channel) channel->is_rpc = true; RpcState *rpc = &channel->rpc; rpc->closed = false; - rpc->unpacker = msgpack_unpacker_new(MSGPACK_UNPACKER_INIT_BUFFER_SIZE); + rpc->unpacker = xcalloc(1, sizeof *rpc->unpacker); + unpacker_init(rpc->unpacker); rpc->next_request_id = 1; rpc->info = (Dictionary)ARRAY_DICT_INIT; kv_init(rpc->call_stack); if (channel->streamtype != kChannelStreamInternal) { Stream *out = channel_outstream(channel); -#if MIN_LOG_LEVEL <= DEBUG_LOG_LEVEL +#if MIN_LOG_LEVEL <= LOGLVL_DBG Stream *in = channel_instream(channel); DLOG("rpc ch %" PRIu64 " in-stream=%p out-stream=%p", channel->id, (void *)in, (void *)out); @@ -209,20 +210,20 @@ static void receive_msgpack(Stream *stream, RBuffer *rbuf, size_t c, void *data, char buf[256]; snprintf(buf, sizeof(buf), "ch %" PRIu64 " was closed by the client", channel->id); - call_set_error(channel, buf, INFO_LOG_LEVEL); + chan_close_with_error(channel, buf, LOGLVL_INF); goto end; } - size_t count = rbuffer_size(rbuf); DLOG("ch %" PRIu64 ": parsing %zu bytes from msgpack Stream: %p", - channel->id, count, (void *)stream); - - // Feed the unpacker with data - msgpack_unpacker_reserve_buffer(channel->rpc.unpacker, count); - rbuffer_read(rbuf, msgpack_unpacker_buffer(channel->rpc.unpacker), count); - msgpack_unpacker_buffer_consumed(channel->rpc.unpacker, count); + channel->id, rbuffer_size(rbuf), (void *)stream); + Unpacker *p = channel->rpc.unpacker; + size_t size = 0; + p->read_ptr = rbuffer_read_ptr(rbuf, &size); + p->read_size = size; parse_msgpack(channel); + size_t consumed = size - p->read_size; + rbuffer_consumed_compact(rbuf, consumed); end: channel_decref(channel); @@ -230,111 +231,70 @@ end: static void parse_msgpack(Channel *channel) { - msgpack_unpacked unpacked; - msgpack_unpacked_init(&unpacked); - msgpack_unpack_return result; - - // Deserialize everything we can. - while ((result = msgpack_unpacker_next(channel->rpc.unpacker, &unpacked)) == - MSGPACK_UNPACK_SUCCESS) { - bool is_response = is_rpc_response(&unpacked.data); - log_client_msg(channel->id, !is_response, unpacked.data); - - if (is_response) { - if (is_valid_rpc_response(&unpacked.data, channel)) { - complete_call(&unpacked.data, channel); - } else { + Unpacker *p = channel->rpc.unpacker; + while (unpacker_advance(p)) { + if (p->type == kMessageTypeResponse) { + ChannelCallFrame *frame = kv_last(channel->rpc.call_stack); + if (p->request_id != frame->request_id) { char buf[256]; snprintf(buf, sizeof(buf), "ch %" PRIu64 " returned a response with an unknown request " "id. Ensure the client is properly synchronized", channel->id); - call_set_error(channel, buf, ERROR_LOG_LEVEL); + chan_close_with_error(channel, buf, LOGLVL_ERR); + } + frame->returned = true; + frame->errored = (p->error.type != kObjectTypeNil); + + if (frame->errored) { + frame->result = p->error; + // TODO(bfredl): p->result should not even be decoded + api_free_object(p->result); + } else { + frame->result = p->result; } - msgpack_unpacked_destroy(&unpacked); } else { - handle_request(channel, &unpacked.data); - } - } + log_client_msg(channel->id, p->type == kMessageTypeRequest, p->handler.name); - if (result == MSGPACK_UNPACK_NOMEM_ERROR) { - mch_errmsg(e_outofmem); - mch_errmsg("\n"); - channel_decref(channel); - preserve_exit(); + Object res = p->result; + if (p->result.type != kObjectTypeArray) { + chan_close_with_error(channel, "msgpack-rpc request args has to be an array", LOGLVL_ERR); + api_free_object(p->result); + return; + } + Array arg = res.data.array; + handle_request(channel, p, arg); + } } - if (result == MSGPACK_UNPACK_PARSE_ERROR) { - // See src/msgpack/unpack_template.h in msgpack source tree for - // causes for this error(search for 'goto _failed') - // - // A not so uncommon cause for this might be deserializing objects with - // a high nesting level: msgpack will break when its internal parse stack - // size exceeds MSGPACK_EMBED_STACK_SIZE (defined as 32 by default) - send_error(channel, kMessageTypeRequest, 0, - "Invalid msgpack payload. " - "This error can also happen when deserializing " - "an object with high level of nesting"); + if (unpacker_closed(p)) { + chan_close_with_error(channel, p->unpack_error.msg, LOGLVL_ERR); + api_clear_error(&p->unpack_error); } } /// Handles requests and notifications received on the channel. -static void handle_request(Channel *channel, msgpack_object *request) +static void handle_request(Channel *channel, Unpacker *p, Array args) FUNC_ATTR_NONNULL_ALL { - uint32_t request_id; - Error error = ERROR_INIT; - MessageType type = msgpack_rpc_validate(&request_id, request, &error); - - if (ERROR_SET(&error)) { - // Validation failed, send response with error - if (channel_write(channel, - serialize_response(channel->id, - type, - request_id, - &error, - NIL, - &out_buffer))) { - char buf[256]; - snprintf(buf, sizeof(buf), - "ch %" PRIu64 " sent an invalid message, closed.", - channel->id); - call_set_error(channel, buf, ERROR_LOG_LEVEL); - } - api_clear_error(&error); - return; - } - assert(type == kMessageTypeRequest || type == kMessageTypeNotification); - - MsgpackRpcRequestHandler handler; - msgpack_object *method = msgpack_rpc_method(request); - handler = msgpack_rpc_get_handler_for(method->via.bin.ptr, - method->via.bin.size, - &error); + assert(p->type == kMessageTypeRequest || p->type == kMessageTypeNotification); - // check method arguments - Array args = ARRAY_DICT_INIT; - if (!ERROR_SET(&error) - && !msgpack_rpc_to_array(msgpack_rpc_args(request), &args)) { - api_set_error(&error, kErrorTypeException, "Invalid method arguments"); - } - - if (ERROR_SET(&error)) { - send_error(channel, type, request_id, error.msg); - api_clear_error(&error); + if (!p->handler.fn) { + send_error(channel, p->type, p->request_id, p->unpack_error.msg); + api_clear_error(&p->unpack_error); api_free_array(args); return; } RequestEvent *evdata = xmalloc(sizeof(RequestEvent)); - evdata->type = type; + evdata->type = p->type; evdata->channel = channel; - evdata->handler = handler; + evdata->handler = p->handler; evdata->args = args; - evdata->request_id = request_id; + evdata->request_id = p->request_id; channel_incref(channel); - if (handler.fast) { - bool is_get_mode = handler.fn == handle_nvim_get_mode; + if (p->handler.fast) { + bool is_get_mode = p->handler.fn == handle_nvim_get_mode; if (is_get_mode && !input_blocking()) { // Defer the event to a special queue used by os/input.c. #6247 @@ -344,7 +304,7 @@ static void handle_request(Channel *channel, msgpack_object *request) request_event((void **)&evdata); } } else { - bool is_resize = handler.fn == handle_nvim_ui_try_resize; + bool is_resize = p->handler.fn == handle_nvim_ui_try_resize; if (is_resize) { Event ev = event_create_oneshot(event_create(request_event, 1, evdata), 2); @@ -352,7 +312,7 @@ static void handle_request(Channel *channel, msgpack_object *request) multiqueue_put_event(resize_events, ev); } else { multiqueue_put(channel->events, request_event, 1, evdata); - DLOG("RPC: scheduled %.*s", method->via.bin.size, method->via.bin.ptr); + DLOG("RPC: scheduled %.*s", (int)p->method_name_len, p->handler.name); } } } @@ -418,7 +378,7 @@ static bool channel_write(Channel *channel, WBuffer *buffer) "ch %" PRIu64 ": stream write failed. " "RPC canceled; closing channel", channel->id); - call_set_error(channel, buf, ERROR_LOG_LEVEL); + chan_close_with_error(channel, buf, LOGLVL_ERR); } return success; @@ -428,14 +388,19 @@ static void internal_read_event(void **argv) { Channel *channel = argv[0]; WBuffer *buffer = argv[1]; + Unpacker *p = channel->rpc.unpacker; - msgpack_unpacker_reserve_buffer(channel->rpc.unpacker, buffer->size); - memcpy(msgpack_unpacker_buffer(channel->rpc.unpacker), - buffer->data, buffer->size); - msgpack_unpacker_buffer_consumed(channel->rpc.unpacker, buffer->size); - + p->read_ptr = buffer->data; + p->read_size = buffer->size; parse_msgpack(channel); + if (p->read_size) { + // This should not happen, as WBuffer is one single serialized message. + if (!channel->rpc.closed) { + chan_close_with_error(channel, "internal channel: internal error", LOGLVL_ERR); + } + } + channel_decref(channel); wstream_release_wbuffer(buffer); } @@ -558,7 +523,7 @@ static void exit_event(void **argv) void rpc_free(Channel *channel) { remote_ui_disconnect(channel->id); - msgpack_unpacker_free(channel->rpc.unpacker); + xfree(channel->rpc.unpacker); // Unsubscribe from all events char *event_string; @@ -571,41 +536,7 @@ void rpc_free(Channel *channel) api_free_dictionary(channel->rpc.info); } -static bool is_rpc_response(msgpack_object *obj) -{ - return obj->type == MSGPACK_OBJECT_ARRAY - && obj->via.array.size == 4 - && obj->via.array.ptr[0].type == MSGPACK_OBJECT_POSITIVE_INTEGER - && obj->via.array.ptr[0].via.u64 == 1 - && obj->via.array.ptr[1].type == MSGPACK_OBJECT_POSITIVE_INTEGER; -} - -static bool is_valid_rpc_response(msgpack_object *obj, Channel *channel) -{ - uint32_t response_id = (uint32_t)obj->via.array.ptr[1].via.u64; - if (kv_size(channel->rpc.call_stack) == 0) { - return false; - } - - // Must be equal to the frame at the stack's bottom - ChannelCallFrame *frame = kv_last(channel->rpc.call_stack); - return response_id == frame->request_id; -} - -static void complete_call(msgpack_object *obj, Channel *channel) -{ - ChannelCallFrame *frame = kv_last(channel->rpc.call_stack); - frame->returned = true; - frame->errored = obj->via.array.ptr[2].type != MSGPACK_OBJECT_NIL; - - if (frame->errored) { - msgpack_rpc_to_object(&obj->via.array.ptr[2], &frame->result); - } else { - msgpack_rpc_to_object(&obj->via.array.ptr[3], &frame->result); - } -} - -static void call_set_error(Channel *channel, char *msg, int loglevel) +static void chan_close_with_error(Channel *channel, char *msg, int loglevel) { LOG(loglevel, "RPC: %s", msg); for (size_t i = 0; i < kv_size(channel->rpc.call_stack); i++) { @@ -693,7 +624,7 @@ const char *rpc_client_name(Channel *chan) return NULL; } -#if MIN_LOG_LEVEL <= DEBUG_LOG_LEVEL +#if MIN_LOG_LEVEL <= LOGLVL_DBG # define REQ "[request] " # define RES "[response] " # define NOT "[notify] " @@ -723,7 +654,8 @@ static void log_server_msg(uint64_t channel_id, msgpack_sbuffer *packed) log_lock(); FILE *f = open_log_file(); fprintf(f, type ? (type == 1 ? RES : NOT) : REQ); - log_msg_close(f, unpacked.data); + msgpack_object_print(f, unpacked.data); + log_close(f); msgpack_unpacked_destroy(&unpacked); break; } @@ -734,30 +666,24 @@ static void log_server_msg(uint64_t channel_id, msgpack_sbuffer *packed) log_lock(); FILE *f = open_log_file(); fprintf(f, ERR); - log_msg_close(f, (msgpack_object) { - .type = MSGPACK_OBJECT_STR, - .via.str = { - .ptr = (char *)msgpack_error_messages[result + MUR_OFF], - .size = (uint32_t)strlen(msgpack_error_messages[result + MUR_OFF]), - }, - }); + fprintf(f, "%s", msgpack_error_messages[result + MUR_OFF]); + log_close(f); break; } } } -static void log_client_msg(uint64_t channel_id, bool is_request, msgpack_object msg) +static void log_client_msg(uint64_t channel_id, bool is_request, const char *name) { DLOGN("RPC <-ch %" PRIu64 ": ", channel_id); log_lock(); FILE *f = open_log_file(); - fprintf(f, is_request ? REQ : RES); - log_msg_close(f, msg); + fprintf(f, "%s: %s", is_request ? REQ : RES, name); + log_close(f); } -static void log_msg_close(FILE *f, msgpack_object msg) +static void log_close(FILE *f) { - msgpack_object_print(f, msg); fputc('\n', f); fflush(f); fclose(f); diff --git a/src/nvim/msgpack_rpc/channel_defs.h b/src/nvim/msgpack_rpc/channel_defs.h index 6647779db9..4dc3c7f22d 100644 --- a/src/nvim/msgpack_rpc/channel_defs.h +++ b/src/nvim/msgpack_rpc/channel_defs.h @@ -6,8 +6,10 @@ #include <uv.h> #include "nvim/api/private/defs.h" +#include "nvim/api/private/dispatch.h" #include "nvim/event/process.h" #include "nvim/event/socket.h" +#include "nvim/msgpack_rpc/unpacker.h" #include "nvim/vim.h" typedef struct Channel Channel; @@ -29,7 +31,7 @@ typedef struct { typedef struct { PMap(cstr_t) subscribed_events[1]; bool closed; - msgpack_unpacker *unpacker; + Unpacker *unpacker; uint32_t next_request_id; kvec_t(ChannelCallFrame *) call_stack; Dictionary info; diff --git a/src/nvim/msgpack_rpc/unpacker.c b/src/nvim/msgpack_rpc/unpacker.c new file mode 100644 index 0000000000..e60d9f220f --- /dev/null +++ b/src/nvim/msgpack_rpc/unpacker.c @@ -0,0 +1,310 @@ +// 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 + +#include "nvim/api/private/helpers.h" +#include "nvim/log.h" +#include "nvim/msgpack_rpc/helpers.h" +#include "nvim/msgpack_rpc/unpacker.h" + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "msgpack_rpc/unpacker.c.generated.h" +#endif + +Object unpack(const char *data, size_t size, Error *err) +{ + Unpacker unpacker; + mpack_parser_init(&unpacker.parser, 0); + unpacker.parser.data.p = &unpacker; + + int result = mpack_parse(&unpacker.parser, &data, &size, + api_parse_enter, api_parse_exit); + + if (result == MPACK_NOMEM) { + api_set_error(err, kErrorTypeException, "object was too deep to unpack"); + } else if (result == MPACK_EOF) { + api_set_error(err, kErrorTypeException, "incomplete msgpack string"); + } else if (result == MPACK_ERROR) { + api_set_error(err, kErrorTypeException, "invalid msgpack string"); + } else if (result == MPACK_OK && size) { + api_set_error(err, kErrorTypeException, "trailing data in msgpack string"); + } + + return unpacker.result; +} + +static void api_parse_enter(mpack_parser_t *parser, mpack_node_t *node) +{ + Unpacker *unpacker = parser->data.p; + Object *result = NULL; + String *key_location = NULL; + + mpack_node_t *parent = MPACK_PARENT_NODE(node); + if (parent) { + switch (parent->tok.type) { + case MPACK_TOKEN_ARRAY: { + Object *obj = parent->data[0].p; + result = &kv_A(obj->data.array, parent->pos); + break; + } + case MPACK_TOKEN_MAP: { + Object *obj = parent->data[0].p; + KeyValuePair *kv = &kv_A(obj->data.dictionary, parent->pos); + if (!parent->key_visited) { + // TODO(bfredl): when implementing interrupt parse on error, + // stop parsing here when node is not a STR/BIN + kv->key = (String)STRING_INIT; + key_location = &kv->key; + } + result = &kv->value; + break; + } + + case MPACK_TOKEN_STR: + case MPACK_TOKEN_BIN: + case MPACK_TOKEN_EXT: + assert(node->tok.type == MPACK_TOKEN_CHUNK); + break; + + default: + abort(); + } + } else { + result = &unpacker->result; + } + + switch (node->tok.type) { + case MPACK_TOKEN_NIL: + *result = NIL; + break; + case MPACK_TOKEN_BOOLEAN: + *result = BOOL(mpack_unpack_boolean(node->tok)); + break; + case MPACK_TOKEN_SINT: + *result = INTEGER_OBJ(mpack_unpack_sint(node->tok)); + break; + case MPACK_TOKEN_UINT: + *result = INTEGER_OBJ((Integer)mpack_unpack_uint(node->tok)); + break; + case MPACK_TOKEN_FLOAT: + *result = FLOAT_OBJ(mpack_unpack_float(node->tok)); + break; + case MPACK_TOKEN_BIN: + case MPACK_TOKEN_STR: { + String str = { .data = xmallocz(node->tok.length), .size = node->tok.length }; + + if (key_location) { + *key_location = str; + } else { + *result = STRING_OBJ(str); + } + + node->data[0].p = str.data; + break; + } + case MPACK_TOKEN_EXT: + // handled in chunk; but save result location + node->data[0].p = result; + break; + + case MPACK_TOKEN_CHUNK: + assert(parent); + if (parent->tok.type == MPACK_TOKEN_STR || parent->tok.type == MPACK_TOKEN_BIN) { + char *data = parent->data[0].p; + memcpy(data + parent->pos, + node->tok.data.chunk_ptr, node->tok.length); + } else { + Object *res = parent->data[0].p; + + size_t endlen = parent->pos + node->tok.length; + if (endlen > MAX_EXT_LEN) { + *res = NIL; + break; + } + memcpy(unpacker->ext_buf + parent->pos, + node->tok.data.chunk_ptr, node->tok.length); + if (parent->pos + node->tok.length < parent->tok.length) { + break; // EOF, let's get back to it later + } + const char *buf = unpacker->ext_buf; + size_t size = parent->tok.length; + mpack_token_t ext_tok; + int status = mpack_rtoken(&buf, &size, &ext_tok); + if (status || ext_tok.type != MPACK_TOKEN_UINT) { + // TODO(bfredl): once we fixed memory management, we can set + // p->unpack_error and a flag like p->interrupted + *res = NIL; + break; + } + int ext_type = parent->tok.data.ext_type; + if (0 <= ext_type && ext_type <= EXT_OBJECT_TYPE_MAX) { + res->type = (ObjectType)(ext_type + EXT_OBJECT_TYPE_SHIFT); + res->data.integer = (int64_t)mpack_unpack_uint(ext_tok); + } else { + *res = NIL; + break; + } + } + break; + + case MPACK_TOKEN_ARRAY: { + Array arr = KV_INITIAL_VALUE; + kv_resize(arr, node->tok.length); + kv_size(arr) = node->tok.length; + *result = ARRAY_OBJ(arr); + node->data[0].p = result; + break; + } + case MPACK_TOKEN_MAP: { + Dictionary dict = KV_INITIAL_VALUE; + kv_resize(dict, node->tok.length); + kv_size(dict) = node->tok.length; + *result = DICTIONARY_OBJ(dict); + node->data[0].p = result; + break; + } + default: + abort(); + } +} + +static void api_parse_exit(mpack_parser_t *parser, mpack_node_t *node) +{} + +void unpacker_init(Unpacker *p) +{ + mpack_parser_init(&p->parser, 0); + p->parser.data.p = p; + mpack_tokbuf_init(&p->reader); + p->unpack_error = (Error)ERROR_INIT; +} + +bool unpacker_parse_header(Unpacker *p) +{ + mpack_token_t tok; + int result; + + const char *data = p->read_ptr; + size_t size = p->read_size; + + assert(!ERROR_SET(&p->unpack_error)); + +#define NEXT(tok) \ + result = mpack_read(&p->reader, &data, &size, &tok); \ + if (result) { goto error; } + + NEXT(tok); + if (tok.type != MPACK_TOKEN_ARRAY || tok.length < 3 || tok.length > 4) { + goto error; + } + size_t array_length = tok.length; + + NEXT(tok); + if (tok.type != MPACK_TOKEN_UINT) { + goto error; + } + uint32_t type = (uint32_t)mpack_unpack_uint(tok); + if ((array_length == 3) ? type != 2 : (type >= 2)) { + goto error; + } + p->type = (MessageType)type; + p->request_id = 0; + + if (p->type != kMessageTypeNotification) { + NEXT(tok); + if (tok.type != MPACK_TOKEN_UINT) { + goto error; + } + p->request_id = (uint32_t)mpack_unpack_uint(tok); + } + + if (p->type != kMessageTypeResponse) { + NEXT(tok); + if ((tok.type != MPACK_TOKEN_STR && tok.type != MPACK_TOKEN_BIN) + || tok.length > 100) { + goto error; + } + p->method_name_len = tok.length; + + if (p->method_name_len > 0) { + NEXT(tok); + assert(tok.type == MPACK_TOKEN_CHUNK); + } + if (tok.length < p->method_name_len) { + result = MPACK_EOF; + goto error; + } + // if this fails, p->handler.fn will be NULL + p->handler = msgpack_rpc_get_handler_for(tok.length ? tok.data.chunk_ptr : "", + tok.length, &p->unpack_error); + } + + p->read_ptr = data; + p->read_size = size; + return true; +#undef NEXT + +error: + if (result == MPACK_EOF) { + // recover later by retrying from scratch + // when more data is available. + mpack_tokbuf_init(&p->reader); + } else { + api_set_error(&p->unpack_error, kErrorTypeValidation, "failed to decode msgpack"); + p->state = -1; + } + return false; +} + +// BASIC BITCH STATE MACHINE +// +// With some basic assumptions, we can parse the overall structure of msgpack-rpc +// messages with a hand-rolled FSM of just 3 states (<x> = p->state): +// +// <0>[0, request_id, method_name, <2>args] +// <0>[1, request_id, <1>err, <2>result] +// <0>[2, method_name, <2>args] +// +// The assumption here is that the header of the message, which we define as the +// initial array head, the kind integer, request_id and/or method name (when needed), +// 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 +// 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. + +bool unpacker_advance(Unpacker *p) +{ + assert(p->state >= 0); + if (p->state == 0) { + if (!unpacker_parse_header(p)) { + return false; + } + p->state = p->type == kMessageTypeResponse ? 1 : 2; + } + + int result; + +rerun: + result = mpack_parse(&p->parser, &p->read_ptr, &p->read_size, + api_parse_enter, api_parse_exit); + + if (result == MPACK_EOF) { + return false; + } else if (result != MPACK_OK) { + api_set_error(&p->unpack_error, kErrorTypeValidation, "failed to parse msgpack"); + p->state = -1; + return false; + } + + if (p->state == 1) { + p->error = p->result; + p->state = 2; + goto rerun; + } else { + assert(p->state == 2); + p->state = 0; + } + return true; +} diff --git a/src/nvim/msgpack_rpc/unpacker.h b/src/nvim/msgpack_rpc/unpacker.h new file mode 100644 index 0000000000..bbd6b1ef4f --- /dev/null +++ b/src/nvim/msgpack_rpc/unpacker.h @@ -0,0 +1,40 @@ +#ifndef NVIM_MSGPACK_RPC_UNPACKER_H +#define NVIM_MSGPACK_RPC_UNPACKER_H + +#include <inttypes.h> +#include <stdbool.h> +#include <string.h> + +#include "mpack/mpack_core.h" +#include "mpack/object.h" +#include "nvim/api/private/dispatch.h" +#include "nvim/api/private/helpers.h" + +typedef struct { + mpack_parser_t parser; + mpack_tokbuf_t reader; + + const char *read_ptr; + size_t read_size; + +#define MAX_EXT_LEN 9 // byte + 8-byte integer + char ext_buf[MAX_EXT_LEN]; + + int state; + MessageType type; + uint32_t request_id; + size_t method_name_len; + MsgpackRpcRequestHandler handler; + Object error; // error return + Object result; // arg list or result + Error unpack_error; +} Unpacker; + +// unrecovareble error. unpack_error should be set! +#define unpacker_closed(p) ((p)->state < 0) + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "msgpack_rpc/unpacker.h.generated.h" +#endif + +#endif // NVIM_MSGPACK_RPC_UNPACKER_H diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 3b91044f41..bf0ea2aeec 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -3917,7 +3917,7 @@ static void nv_regreplay(cmdarg_T *cap) } } -/// Handle a ":" command and <Cmd> or Lua keymaps. +/// Handle a ":" command and <Cmd> or Lua mappings. static void nv_colon(cmdarg_T *cap) { bool cmd_result; diff --git a/src/nvim/po/cleanup.vim b/src/nvim/po/cleanup.vim index b27d88092f..ff5579a67a 100644 --- a/src/nvim/po/cleanup.vim +++ b/src/nvim/po/cleanup.vim @@ -12,6 +12,9 @@ setl nodiff silent g/^#, c-format\n#/.d silent g/^#\..*\n#/.d +" c-format comments have no effect, the check.vim scripts checks it. +silent g/^#, c-format$/d + silent g/^#[:~] /d silent g/^#, fuzzy\(, .*\)\=\nmsgid ""\@!/.+1,/^$/-1s/^/#\~ / silent g/^msgstr"/s//msgstr "/ diff --git a/src/nvim/rbuffer.c b/src/nvim/rbuffer.c index d280e08c03..6407ac172e 100644 --- a/src/nvim/rbuffer.c +++ b/src/nvim/rbuffer.c @@ -154,6 +154,23 @@ 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 +/// except some small incomplete token, like when parsing msgpack. +void rbuffer_consumed_compact(RBuffer *buf, size_t count) + FUNC_ATTR_NONNULL_ALL +{ + assert(buf->read_ptr <= buf->write_ptr); + rbuffer_consumed(buf, count); + if (buf->read_ptr > buf->start_ptr) { + assert((size_t)(buf->read_ptr - buf->write_ptr) == buf->size); + memmove(buf->start_ptr, buf->read_ptr, buf->size); + buf->read_ptr = buf->start_ptr; + buf->write_ptr = buf->read_ptr + buf->size; + } +} + // Higher level functions for copying from/to RBuffer instances and data // pointers size_t rbuffer_write(RBuffer *buf, const char *src, size_t src_size) diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c index 6b8c75e430..44c9928f7b 100644 --- a/src/nvim/regexp.c +++ b/src/nvim/regexp.c @@ -1653,21 +1653,22 @@ static void clear_submatch_list(staticList10_T *sl) /// vim_regsub() - perform substitutions after a vim_regexec() or /// vim_regexec_multi() match. /// -/// If "copy" is true really copy into "dest". -/// If "copy" is false nothing is copied, this is just to find out the length -/// of the result. +/// If "flags" has REGSUB_COPY really copy into "dest[destlen]". +/// Oterwise nothing is copied, only compue the length of the result. /// -/// If "backslash" is true, a backslash will be removed later, need to double -/// them to keep them, and insert a backslash before a CR to avoid it being -/// replaced with a line break later. +/// If "flags" has REGSUB_MAGIC then behave like 'magic' is set. +/// +/// If "flags" has REGSUB_BACKSLASH a backslash will be removed later, need to +/// double them to keep them, and insert a backslash before a CR to avoid it +/// being replaced with a line break later. /// /// Note: The matched text must not change between the call of /// vim_regexec()/vim_regexec_multi() and vim_regsub()! It would make the back /// references invalid! /// /// Returns the size of the replacement, including terminating NUL. -int vim_regsub(regmatch_T *rmp, char_u *source, typval_T *expr, char_u *dest, int copy, int magic, - int backslash) +int vim_regsub(regmatch_T *rmp, char_u *source, typval_T *expr, char_u *dest, int destlen, + int flags) { regexec_T rex_save; bool rex_in_use_save = rex_in_use; @@ -1683,7 +1684,7 @@ int vim_regsub(regmatch_T *rmp, char_u *source, typval_T *expr, char_u *dest, in rex.reg_maxline = 0; rex.reg_buf = curbuf; rex.reg_line_lbr = true; - int result = vim_regsub_both(source, expr, dest, copy, magic, backslash); + int result = vim_regsub_both(source, expr, dest, destlen, flags); rex_in_use = rex_in_use_save; if (rex_in_use) { @@ -1693,8 +1694,8 @@ int vim_regsub(regmatch_T *rmp, char_u *source, typval_T *expr, char_u *dest, in return result; } -int vim_regsub_multi(regmmatch_T *rmp, linenr_T lnum, char_u *source, char_u *dest, int copy, - int magic, int backslash) +int vim_regsub_multi(regmmatch_T *rmp, linenr_T lnum, char_u *source, char_u *dest, int destlen, + int flags) { regexec_T rex_save; bool rex_in_use_save = rex_in_use; @@ -1711,7 +1712,7 @@ int vim_regsub_multi(regmmatch_T *rmp, linenr_T lnum, char_u *source, char_u *de rex.reg_firstlnum = lnum; rex.reg_maxline = curbuf->b_ml.ml_line_count - lnum; rex.reg_line_lbr = false; - int result = vim_regsub_both(source, NULL, dest, copy, magic, backslash); + int result = vim_regsub_both(source, NULL, dest, destlen, flags); rex_in_use = rex_in_use_save; if (rex_in_use) { @@ -1721,8 +1722,7 @@ int vim_regsub_multi(regmmatch_T *rmp, linenr_T lnum, char_u *source, char_u *de return result; } -static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, int copy, int magic, - int backslash) +static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, int destlen, int flags) { char_u *src; char_u *dst; @@ -1735,6 +1735,7 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, int cop linenr_T clnum = 0; // init for GCC int len = 0; // init for GCC static char_u *eval_result = NULL; + bool copy = flags & REGSUB_COPY; // We need to keep track of how many backslashes we escape, so that the byte // counts for `extmark_splice` are correct. @@ -1755,8 +1756,8 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, int cop if (expr != NULL || (source[0] == '\\' && source[1] == '=')) { // To make sure that the length doesn't change between checking the // length and copying the string, and to speed up things, the - // resulting string is saved from the call with "copy" == false to the - // call with "copy" == true. + // resulting string is saved from the call with "flags & REGSUB_COPY" + // == 0 to the call with "flags & REGSUB_COPY" != 0. if (copy) { if (eval_result != NULL) { STRCPY(dest, eval_result); @@ -1845,7 +1846,7 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, int cop had_backslash = true; } } - if (had_backslash && backslash) { + if (had_backslash && (flags & REGSUB_BACKSLASH)) { // Backslashes will be consumed, need to double them. s = vim_strsave_escaped(eval_result, (char_u *)"\\"); xfree(eval_result); @@ -1862,11 +1863,11 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, int cop } } else { while ((c = *src++) != NUL) { - if (c == '&' && magic) { + if (c == '&' && (flags & REGSUB_MAGIC)) { no = 0; } else if (c == '\\' && *src != NUL) { - if (*src == '&' && !magic) { - ++src; + if (*src == '&' && !(flags & REGSUB_MAGIC)) { + src++; no = 0; } else if ('0' <= *src && *src <= '9') { no = *src++ - '0'; @@ -1895,6 +1896,10 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, int cop if (c == K_SPECIAL && src[0] != NUL && src[1] != NUL) { // Copy a special key as-is. if (copy) { + if (dst + 3 > dest + destlen) { + iemsg("vim_regsub_both(): not enough space"); + return 0; + } *dst++ = c; *dst++ = *src++; *dst++ = *src++; @@ -1922,9 +1927,13 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, int cop // If "backslash" is true the backslash will be removed // later. Used to insert a literal CR. default: - if (backslash) { + if (flags & REGSUB_BACKSLASH) { num_escaped += 1; if (copy) { + if (dst + 1 > dest + destlen) { + iemsg("vim_regsub_both(): not enough space"); + return 0; + } *dst = '\\'; } dst++; @@ -1945,17 +1954,26 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, int cop } int totlen = utfc_ptr2len((char *)src - 1); + int charlen = utf_char2len(cc); if (copy) { + if (dst + charlen > dest + destlen) { + iemsg("vim_regsub_both(): not enough space"); + return 0; + } utf_char2bytes(cc, (char *)dst); } - dst += utf_char2len(cc) - 1; + dst += charlen - 1; int clen = utf_ptr2len((char *)src - 1); // If the character length is shorter than "totlen", there // are composing characters; copy them as-is. if (clen < totlen) { if (copy) { + if (dst + totlen - clen > dest + destlen) { + iemsg("vim_regsub_both(): not enough space"); + return 0; + } memmove(dst + 1, src - 1 + clen, (size_t)(totlen - clen)); } dst += totlen - clen; @@ -1992,6 +2010,10 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, int cop break; } if (copy) { + if (dst + 1 > dest + destlen) { + iemsg("vim_regsub_both(): not enough space"); + return 0; + } *dst = CAR; } dst++; @@ -2010,14 +2032,16 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, int cop } goto exit; } else { - if (backslash && (*s == CAR || *s == '\\')) { - /* - * Insert a backslash in front of a CR, otherwise - * it will be replaced by a line break. - * Number of backslashes will be halved later, - * double them here. - */ + if ((flags & REGSUB_BACKSLASH) && (*s == CAR || *s == '\\')) { + // Insert a backslash in front of a CR, otherwise + // it will be replaced by a line break. + // Number of backslashes will be halved later, + // double them here. if (copy) { + if (dst + 2 > dest + destlen) { + iemsg("vim_regsub_both(): not enough space"); + return 0; + } dst[0] = '\\'; dst[1] = *s; } @@ -2037,6 +2061,7 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, int cop { int l; + int charlen; // Copy composing characters separately, one // at a time. @@ -2044,10 +2069,15 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, int cop s += l; len -= l; + charlen = utf_char2len(cc); if (copy) { + if (dst + charlen > dest + destlen) { + iemsg("vim_regsub_both(): not enough space"); + return 0; + } utf_char2bytes(cc, (char *)dst); } - dst += utf_char2len(cc) - 1; + dst += charlen - 1; } dst++; } @@ -2386,8 +2416,8 @@ static void report_re_switch(char_u *pat) } } -/// Matches a regexp against a string. -/// "rmp->regprog" is a compiled regexp as returned by vim_regcomp(). +/// Match a regexp against a string. +/// "rmp->regprog" must be a compiled regexp as returned by vim_regcomp(). /// Note: "rmp->regprog" may be freed and changed. /// Uses curbuf for line count and 'iskeyword'. /// When "nl" is true consider a "\n" in "line" to be a line break. diff --git a/src/nvim/regexp_defs.h b/src/nvim/regexp_defs.h index decc832051..09f244c2f6 100644 --- a/src/nvim/regexp_defs.h +++ b/src/nvim/regexp_defs.h @@ -168,4 +168,9 @@ struct regengine { // char_u *expr; }; +// Flags used by vim_regsub() and vim_regsub_both() +#define REGSUB_COPY 1 +#define REGSUB_MAGIC 2 +#define REGSUB_BACKSLASH 4 + #endif // NVIM_REGEXP_DEFS_H diff --git a/src/nvim/screen.c b/src/nvim/screen.c index b0edad7740..fe306f8c6b 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -326,10 +326,11 @@ int update_screen(int type) type = must_redraw; } - /* must_redraw is reset here, so that when we run into some weird - * reason to redraw while busy redrawing (e.g., asynchronous - * scrolling), or update_topline() in win_update() will cause a - * scroll, the screen will be redrawn later or in win_update(). */ + // must_redraw is reset here, so that when we run into some weird + // reason to redraw while busy redrawing (e.g., asynchronous + // scrolling), or update_topline() in win_update() will cause a + // scroll, or a decoration provider requires a redraw, the screen + // will be redrawn later or in win_update(). must_redraw = 0; } @@ -689,6 +690,9 @@ bool win_cursorline_standout(const win_T *wp) */ static void win_update(win_T *wp, DecorProviders *providers) { + bool called_decor_providers = false; +win_update_start: + ; buf_T *buf = wp->w_buffer; int type; int top_end = 0; /* Below last row of the top area that needs @@ -1306,6 +1310,14 @@ static void win_update(win_T *wp, DecorProviders *providers) DecorProviders line_providers; decor_providers_invoke_win(wp, providers, &line_providers, &provider_err); + (void)win_signcol_count(wp); // check if provider changed signcol width + if (must_redraw != 0) { + must_redraw = 0; + if (!called_decor_providers) { + called_decor_providers = true; + goto win_update_start; + } + } bool cursorline_standout = win_cursorline_standout(wp); diff --git a/src/nvim/state.c b/src/nvim/state.c index f6d9b535fc..6475105192 100644 --- a/src/nvim/state.c +++ b/src/nvim/state.c @@ -81,8 +81,8 @@ getkey: may_sync_undo(); } -#if MIN_LOG_LEVEL <= DEBUG_LOG_LEVEL - log_key(DEBUG_LOG_LEVEL, key); +#if MIN_LOG_LEVEL <= LOGLVL_DBG + log_key(LOGLVL_DBG, key); #endif int execute_result = s->execute(s, key); @@ -137,7 +137,7 @@ bool virtual_active(void) || ((cur_ve_flags & VE_INSERT) && (State & MODE_INSERT)); } -/// MODE_VISUAL, MODE_SELECTMODE and MODE_OP_PENDING State are never set, they are +/// MODE_VISUAL, MODE_SELECT and MODE_OP_PENDING State are never set, they are /// equal to MODE_NORMAL State with a condition. This function returns the real /// State. int get_real_state(void) diff --git a/src/nvim/testdir/test_excmd.vim b/src/nvim/testdir/test_excmd.vim index 8055a51a11..7dde8a0439 100644 --- a/src/nvim/testdir/test_excmd.vim +++ b/src/nvim/testdir/test_excmd.vim @@ -422,5 +422,13 @@ func Test_address_line_overflow() bwipe! endfunc +" This was leaving the cursor in line zero +func Test_using_zero_in_range() + new + norm o00 + silent! 0;s/\%') + bwipe! +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim index 4819c4877c..fb6c9e46aa 100644 --- a/src/nvim/testdir/test_filetype.vim +++ b/src/nvim/testdir/test_filetype.vim @@ -114,8 +114,9 @@ let s:filename_checks = { \ 'cobol': ['file.cbl', 'file.cob', 'file.lib'], \ 'coco': ['file.atg'], \ 'conaryrecipe': ['file.recipe'], - \ 'conf': ['/etc/pacman.conf', 'any/etc/pacman.conf', 'auto.master'], + \ 'conf': ['auto.master'], \ 'config': ['configure.in', 'configure.ac', '/etc/hostname.file'], + \ 'confini': ['/etc/pacman.conf', 'any/etc/pacman.conf', 'mpv.conf'], \ 'context': ['tex/context/any/file.tex', 'file.mkii', 'file.mkiv', 'file.mkvi', 'file.mkxl', 'file.mklx'], \ 'cook': ['file.cook'], \ 'cpp': ['file.cxx', 'file.c++', 'file.hh', 'file.hxx', 'file.hpp', 'file.ipp', 'file.moc', 'file.tcc', 'file.inl', 'file.tlh'], diff --git a/src/nvim/testdir/test_filetype_lua.vim b/src/nvim/testdir/test_filetype_lua.vim index f73e4ca33f..d9c0dcba9c 100644 --- a/src/nvim/testdir/test_filetype_lua.vim +++ b/src/nvim/testdir/test_filetype_lua.vim @@ -1,2 +1,3 @@ let g:do_filetype_lua = 1 +let g:did_load_filetypes = 0 source test_filetype.vim diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim index 87606f17b8..2391b4a485 100644 --- a/src/nvim/testdir/test_functions.vim +++ b/src/nvim/testdir/test_functions.vim @@ -1180,6 +1180,45 @@ func Test_col() bw! endfunc +" Test for input() +func Test_input_func() + " Test for prompt with multiple lines + redir => v + call feedkeys(":let c = input(\"A\\nB\\nC\\n? \")\<CR>B\<CR>", 'xt') + redir END + call assert_equal("B", c) + call assert_equal(['A', 'B', 'C'], split(v, "\n")) + + " Test for default value + call feedkeys(":let c = input('color? ', 'red')\<CR>\<CR>", 'xt') + call assert_equal('red', c) + + " Test for completion at the input prompt + func! Tcomplete(arglead, cmdline, pos) + return "item1\nitem2\nitem3" + endfunc + call feedkeys(":let c = input('Q? ', '', 'custom,Tcomplete')\<CR>" + \ .. "\<C-A>\<CR>", 'xt') + delfunc Tcomplete + call assert_equal('item1 item2 item3', c) + + " Test for using special characters as default input + call feedkeys(":let c = input('name? ', \"x\\<BS>y\")\<CR>\<CR>", 'xt') + call assert_equal('y', c) + + " Test for using text with composing characters as default input + call feedkeys(":let c = input('name? ', \"ã̳\")\<CR>\<CR>", 'xt') + call assert_equal('ã̳', c) + + " Test for using <CR> as default input + call feedkeys(":let c = input('name? ', \"\\<CR>\")\<CR>x\<CR>", 'xt') + call assert_equal(' x', c) + + call assert_fails("call input('F:', '', 'invalid')", 'E180:') + call assert_fails("call input('F:', '', [])", 'E730:') +endfunc + +" Test for inputlist() func Test_inputlist() call feedkeys(":let c = inputlist(['Select color:', '1. red', '2. green', '3. blue'])\<cr>1\<cr>", 'tx') call assert_equal(1, c) diff --git a/src/nvim/testdir/test_mapping.vim b/src/nvim/testdir/test_mapping.vim index 995511cddf..494f09e0e5 100644 --- a/src/nvim/testdir/test_mapping.vim +++ b/src/nvim/testdir/test_mapping.vim @@ -1081,4 +1081,34 @@ func Test_expr_map_escape_special() nunmap … endfunc +" Testing for mapping after an <Nop> mapping is triggered on timeout. +" Test for what patch 8.1.0052 fixes. +func Test_map_after_timed_out_nop() + CheckRunVimInTerminal + + let lines =<< trim END + set timeout timeoutlen=400 + inoremap ab TEST + inoremap a <Nop> + END + call writefile(lines, 'Xtest_map_after_timed_out_nop') + let buf = RunVimInTerminal('-S Xtest_map_after_timed_out_nop', #{rows: 6}) + + " Enter Insert mode + call term_sendkeys(buf, 'i') + " Wait for the "a" mapping to timeout + call term_sendkeys(buf, 'a') + call term_wait(buf, 500) + " Send "a" and wait for a period shorter than 'timeoutlen' + call term_sendkeys(buf, 'a') + call term_wait(buf, 100) + " Send "b", should trigger the "ab" mapping + call term_sendkeys(buf, 'b') + call WaitForAssert({-> assert_equal("TEST", term_getline(buf, 1))}) + + " clean up + call StopVimInTerminal(buf) + call delete('Xtest_map_after_timed_out_nop') +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/tui/terminfo_defs.h b/src/nvim/tui/terminfo_defs.h index 4e8affa4d3..0bc4972dd3 100644 --- a/src/nvim/tui/terminfo_defs.h +++ b/src/nvim/tui/terminfo_defs.h @@ -285,7 +285,7 @@ static const int8_t ansi_terminfo[] = { // set_a_background=\E[48;5;%p1%dm, // set_a_foreground=\E[38;5;%p1%dm, // set_attributes=\E[0%?%p1%p3%|%t;7%;%?%p2%t;4%;%?%p6%t;1%;m, -// set_lr_margin=\E[?69h\E[%i%p1%d;%p2%ds, +// set_lr_margin@, // set_tab@, // tab=^I, // user6@, @@ -293,7 +293,7 @@ static const int8_t ansi_terminfo[] = { // user8@, // user9@, static const int8_t conemu_terminfo[] = { - 30,2,61,0,38,0,15,0,-99,1,80,3,99,111,110,101,109,117,124,65,78,73,83,32,88,51,46,54,52,32,97,110,100,32,88,116,101,114,109,32,50,53,54,32,99,111,108,111,114,115,32,102,111,114,32,67,111,110,69,109,117,32,119,105,116,104,32,108,105,98,117,118,0,0,1,0,0,0,0,0,0,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,1,0,80,0,0,0,8,0,0,0,24,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,1,0,0,0,0,1,0,-2,-1,0,0,2,0,4,0,-2,-1,21,0,29,0,33,0,37,0,-1,-1,48,0,65,0,69,0,73,0,80,0,-1,-1,82,0,89,0,-1,-1,93,0,-2,-1,97,0,101,0,-1,-1,-1,-1,-2,-1,-2,-1,105,0,110,0,-1,-1,-2,-1,-2,-1,-2,-1,-1,-1,119,0,124,0,-127,0,-122,0,-2,-1,-113,0,-108,0,-1,-1,-2,-1,-99,0,-93,0,-2,-1,-1,-1,-1,-1,-1,-1,-2,-1,-1,-1,-1,-1,-1,-1,-87,0,-1,-1,-83,0,-1,-1,-1,-1,-1,-1,-81,0,-1,-1,-76,0,-1,-1,-1,-1,-1,-1,-1,-1,-72,0,-67,0,-61,0,-56,0,-51,0,-46,0,-41,0,-35,0,-29,0,-23,0,-17,0,-12,0,-1,-1,-7,0,-1,-1,-3,0,2,1,7,1,11,1,18,1,-1,-1,25,1,-2,-1,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,29,1,-1,-1,32,1,41,1,50,1,59,1,68,1,77,1,86,1,95,1,104,1,113,1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,-2,-1,122,1,-2,-1,-2,-1,-1,-1,-1,-1,-114,1,-111,1,-100,1,-97,1,-95,1,-92,1,-2,-1,-1,-1,-49,1,-1,-1,-1,-1,-1,-1,-1,-1,-47,1,-43,1,-39,1,-35,1,-31,1,-1,-1,-1,-1,-2,-1,-1,-1,-27,1,-1,-1,-1,-1,-2,-1,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-23,1,-18,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-14,1,-1,-1,-1,-1,-7,1,-1,-1,-1,-1,-1,-1,-1,-1,0,2,7,2,14,2,-1,-1,-1,-1,21,2,-1,-1,28,2,-1,-1,-1,-1,-1,-1,35,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,42,2,48,2,54,2,60,2,66,2,72,2,78,2,84,2,90,2,96,2,102,2,108,2,114,2,120,2,126,2,-124,2,-118,2,-112,2,-106,2,-100,2,-94,2,-88,2,-82,2,-76,2,-70,2,-64,2,-58,2,-52,2,-46,2,-40,2,-33,2,-27,2,-21,2,-15,2,-9,2,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-3,2,2,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,-2,-1,-2,-1,9,3,-2,-1,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,18,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,23,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-1,-1,-1,-1,-1,-1,29,3,43,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,57,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,7,0,13,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,114,0,27,91,72,27,91,50,74,0,27,91,75,0,27,91,74,0,27,91,37,105,37,112,49,37,100,71,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,72,0,27,91,66,0,27,91,72,0,27,91,63,50,53,108,0,8,0,27,91,63,50,53,104,0,27,91,67,0,27,91,65,0,27,91,80,0,27,91,77,0,27,91,49,109,0,27,91,63,49,48,52,57,104,0,27,91,55,109,0,27,91,55,109,0,27,91,52,109,0,27,91,37,112,49,37,100,88,0,27,91,48,109,0,27,91,63,49,48,52,57,108,0,27,91,50,55,109,0,27,91,50,52,109,0,27,91,76,0,8,0,27,91,51,126,0,27,91,66,0,27,91,91,65,0,27,91,50,49,126,0,27,91,91,66,0,27,91,91,67,0,27,91,91,68,0,27,91,91,69,0,27,91,49,55,126,0,27,91,49,56,126,0,27,91,49,57,126,0,27,91,50,48,126,0,27,91,49,126,0,27,91,50,126,0,27,91,68,0,27,91,54,126,0,27,91,53,126,0,27,91,67,0,27,91,49,59,50,66,0,27,91,49,59,50,65,0,27,91,65,0,27,69,0,27,91,37,112,49,37,100,80,0,27,91,37,112,49,37,100,77,0,27,91,37,112,49,37,100,66,0,27,91,37,112,49,37,100,64,0,27,91,37,112,49,37,100,83,0,27,91,37,112,49,37,100,76,0,27,91,37,112,49,37,100,68,0,27,91,37,112,49,37,100,67,0,27,91,37,112,49,37,100,84,0,27,91,37,112,49,37,100,65,0,37,112,49,37,99,27,91,37,112,50,37,123,49,125,37,45,37,100,98,0,27,56,0,27,91,37,105,37,112,49,37,100,100,0,27,55,0,10,0,27,77,0,27,91,48,37,63,37,112,49,37,112,51,37,124,37,116,59,55,37,59,37,63,37,112,50,37,116,59,52,37,59,37,63,37,112,54,37,116,59,49,37,59,109,0,9,0,27,79,119,0,27,79,121,0,27,91,71,0,27,79,113,0,27,79,115,0,27,91,90,0,27,91,52,126,0,27,79,77,0,27,91,51,59,50,126,0,27,91,52,59,50,126,0,27,91,49,59,50,126,0,27,91,50,59,50,126,0,27,91,49,59,50,68,0,27,91,54,59,50,126,0,27,91,53,59,50,126,0,27,91,49,59,50,67,0,27,91,50,51,126,0,27,91,50,52,126,0,27,91,50,53,126,0,27,91,50,54,126,0,27,91,50,56,126,0,27,91,50,57,126,0,27,91,51,49,126,0,27,91,51,50,126,0,27,91,51,51,126,0,27,91,51,52,126,0,27,91,50,51,36,0,27,91,50,52,36,0,27,91,49,49,94,0,27,91,49,50,94,0,27,91,49,51,94,0,27,91,49,52,94,0,27,91,49,53,94,0,27,91,49,55,94,0,27,91,49,56,94,0,27,91,49,57,94,0,27,91,50,48,94,0,27,91,50,49,94,0,27,91,50,51,94,0,27,91,50,52,94,0,27,91,50,53,94,0,27,91,50,54,94,0,27,91,50,56,94,0,27,91,50,57,94,0,27,91,51,49,94,0,27,91,49,59,54,83,0,27,91,51,50,94,0,27,91,51,51,94,0,27,91,51,52,94,0,27,91,50,51,64,0,27,91,50,52,64,0,27,91,49,75,0,27,91,63,54,57,108,0,27,91,51,57,59,52,57,109,0,27,91,51,109,0,27,91,50,51,109,0,27,91,51,56,59,53,59,37,112,49,37,100,109,0,27,91,52,56,59,53,59,37,112,49,37,100,109,0,27,91,63,54,57,104,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,115,0,2,0,0,0,74,0,92,0,-46,1,1,1,-2,-1,-2,-1,0,0,-2,-1,5,0,11,0,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,21,0,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,28,0,32,0,36,0,40,0,44,0,48,0,52,0,56,0,60,0,64,0,68,0,72,0,-2,-1,-2,-1,-2,-1,0,0,3,0,6,0,9,0,12,0,15,0,18,0,21,0,24,0,27,0,32,0,37,0,42,0,47,0,52,0,56,0,61,0,66,0,71,0,76,0,81,0,87,0,93,0,99,0,105,0,111,0,117,0,123,0,-127,0,-121,0,-115,0,-110,0,-105,0,-100,0,-95,0,-90,0,-84,0,-78,0,-72,0,-66,0,-60,0,-54,0,-48,0,-42,0,-36,0,-30,0,-24,0,-18,0,-12,0,-6,0,0,1,6,1,12,1,18,1,24,1,30,1,34,1,39,1,44,1,49,1,54,1,59,1,63,1,67,1,71,1,75,1,79,1,85,1,91,1,97,1,103,1,109,1,115,1,121,1,126,1,-125,1,27,91,51,74,0,27,91,50,32,113,0,27,91,37,112,49,37,100,32,113,0,27,91,49,59,50,65,0,27,79,120,0,27,79,116,0,27,79,118,0,27,79,114,0,27,79,69,0,27,79,107,0,27,79,108,0,27,79,111,0,27,79,110,0,27,79,106,0,27,79,109,0,27,79,112,0,65,88,0,88,84,0,67,114,0,67,115,0,69,51,0,77,115,0,83,101,0,83,115,0,88,77,0,107,68,67,51,0,107,68,67,52,0,107,68,67,53,0,107,68,67,54,0,107,68,67,55,0,107,68,78,0,107,68,78,51,0,107,68,78,52,0,107,68,78,53,0,107,68,78,54,0,107,68,78,55,0,107,69,78,68,51,0,107,69,78,68,52,0,107,69,78,68,53,0,107,69,78,68,54,0,107,69,78,68,55,0,107,72,79,77,51,0,107,72,79,77,52,0,107,72,79,77,53,0,107,72,79,77,54,0,107,72,79,77,55,0,107,73,67,51,0,107,73,67,52,0,107,73,67,53,0,107,73,67,54,0,107,73,67,55,0,107,76,70,84,51,0,107,76,70,84,52,0,107,76,70,84,53,0,107,76,70,84,54,0,107,76,70,84,55,0,107,78,88,84,51,0,107,78,88,84,52,0,107,78,88,84,53,0,107,78,88,84,54,0,107,78,88,84,55,0,107,80,82,86,51,0,107,80,82,86,52,0,107,80,82,86,53,0,107,80,82,86,54,0,107,80,82,86,55,0,107,82,73,84,51,0,107,82,73,84,52,0,107,82,73,84,53,0,107,82,73,84,54,0,107,82,73,84,55,0,107,85,80,0,107,85,80,51,0,107,85,80,52,0,107,85,80,53,0,107,85,80,54,0,107,85,80,55,0,107,97,50,0,107,98,49,0,107,98,51,0,107,99,50,0,107,112,53,0,107,112,65,68,68,0,107,112,67,77,65,0,107,112,68,73,86,0,107,112,68,79,84,0,107,112,77,85,76,0,107,112,83,85,66,0,107,112,90,82,79,0,114,109,120,120,0,115,109,120,120,0,120,109,0 + 30,2,61,0,38,0,15,0,-99,1,57,3,99,111,110,101,109,117,124,65,78,73,83,32,88,51,46,54,52,32,97,110,100,32,88,116,101,114,109,32,50,53,54,32,99,111,108,111,114,115,32,102,111,114,32,67,111,110,69,109,117,32,119,105,116,104,32,108,105,98,117,118,0,0,1,0,0,0,0,0,0,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,1,0,80,0,0,0,8,0,0,0,24,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,1,0,0,0,0,1,0,-2,-1,0,0,2,0,4,0,-2,-1,21,0,29,0,33,0,37,0,-1,-1,48,0,65,0,69,0,73,0,80,0,-1,-1,82,0,89,0,-1,-1,93,0,-2,-1,97,0,101,0,-1,-1,-1,-1,-2,-1,-2,-1,105,0,110,0,-1,-1,-2,-1,-2,-1,-2,-1,-1,-1,119,0,124,0,-127,0,-122,0,-2,-1,-113,0,-108,0,-1,-1,-2,-1,-99,0,-93,0,-2,-1,-1,-1,-1,-1,-1,-1,-2,-1,-1,-1,-1,-1,-1,-1,-87,0,-1,-1,-83,0,-1,-1,-1,-1,-1,-1,-81,0,-1,-1,-76,0,-1,-1,-1,-1,-1,-1,-1,-1,-72,0,-67,0,-61,0,-56,0,-51,0,-46,0,-41,0,-35,0,-29,0,-23,0,-17,0,-12,0,-1,-1,-7,0,-1,-1,-3,0,2,1,7,1,11,1,18,1,-1,-1,25,1,-2,-1,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,29,1,-1,-1,32,1,41,1,50,1,59,1,68,1,77,1,86,1,95,1,104,1,113,1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,-2,-1,122,1,-2,-1,-2,-1,-1,-1,-1,-1,-114,1,-111,1,-100,1,-97,1,-95,1,-92,1,-2,-1,-1,-1,-49,1,-1,-1,-1,-1,-1,-1,-1,-1,-47,1,-43,1,-39,1,-35,1,-31,1,-1,-1,-1,-1,-2,-1,-1,-1,-27,1,-1,-1,-1,-1,-2,-1,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-23,1,-18,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-14,1,-1,-1,-1,-1,-7,1,-1,-1,-1,-1,-1,-1,-1,-1,0,2,7,2,14,2,-1,-1,-1,-1,21,2,-1,-1,28,2,-1,-1,-1,-1,-1,-1,35,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,42,2,48,2,54,2,60,2,66,2,72,2,78,2,84,2,90,2,96,2,102,2,108,2,114,2,120,2,126,2,-124,2,-118,2,-112,2,-106,2,-100,2,-94,2,-88,2,-82,2,-76,2,-70,2,-64,2,-58,2,-52,2,-46,2,-40,2,-33,2,-27,2,-21,2,-15,2,-9,2,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-3,2,2,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,-2,-1,-2,-1,9,3,-2,-1,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,18,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,23,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-1,-1,-1,-1,-1,-1,29,3,43,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,7,0,13,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,114,0,27,91,72,27,91,50,74,0,27,91,75,0,27,91,74,0,27,91,37,105,37,112,49,37,100,71,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,72,0,27,91,66,0,27,91,72,0,27,91,63,50,53,108,0,8,0,27,91,63,50,53,104,0,27,91,67,0,27,91,65,0,27,91,80,0,27,91,77,0,27,91,49,109,0,27,91,63,49,48,52,57,104,0,27,91,55,109,0,27,91,55,109,0,27,91,52,109,0,27,91,37,112,49,37,100,88,0,27,91,48,109,0,27,91,63,49,48,52,57,108,0,27,91,50,55,109,0,27,91,50,52,109,0,27,91,76,0,8,0,27,91,51,126,0,27,91,66,0,27,91,91,65,0,27,91,50,49,126,0,27,91,91,66,0,27,91,91,67,0,27,91,91,68,0,27,91,91,69,0,27,91,49,55,126,0,27,91,49,56,126,0,27,91,49,57,126,0,27,91,50,48,126,0,27,91,49,126,0,27,91,50,126,0,27,91,68,0,27,91,54,126,0,27,91,53,126,0,27,91,67,0,27,91,49,59,50,66,0,27,91,49,59,50,65,0,27,91,65,0,27,69,0,27,91,37,112,49,37,100,80,0,27,91,37,112,49,37,100,77,0,27,91,37,112,49,37,100,66,0,27,91,37,112,49,37,100,64,0,27,91,37,112,49,37,100,83,0,27,91,37,112,49,37,100,76,0,27,91,37,112,49,37,100,68,0,27,91,37,112,49,37,100,67,0,27,91,37,112,49,37,100,84,0,27,91,37,112,49,37,100,65,0,37,112,49,37,99,27,91,37,112,50,37,123,49,125,37,45,37,100,98,0,27,56,0,27,91,37,105,37,112,49,37,100,100,0,27,55,0,10,0,27,77,0,27,91,48,37,63,37,112,49,37,112,51,37,124,37,116,59,55,37,59,37,63,37,112,50,37,116,59,52,37,59,37,63,37,112,54,37,116,59,49,37,59,109,0,9,0,27,79,119,0,27,79,121,0,27,91,71,0,27,79,113,0,27,79,115,0,27,91,90,0,27,91,52,126,0,27,79,77,0,27,91,51,59,50,126,0,27,91,52,59,50,126,0,27,91,49,59,50,126,0,27,91,50,59,50,126,0,27,91,49,59,50,68,0,27,91,54,59,50,126,0,27,91,53,59,50,126,0,27,91,49,59,50,67,0,27,91,50,51,126,0,27,91,50,52,126,0,27,91,50,53,126,0,27,91,50,54,126,0,27,91,50,56,126,0,27,91,50,57,126,0,27,91,51,49,126,0,27,91,51,50,126,0,27,91,51,51,126,0,27,91,51,52,126,0,27,91,50,51,36,0,27,91,50,52,36,0,27,91,49,49,94,0,27,91,49,50,94,0,27,91,49,51,94,0,27,91,49,52,94,0,27,91,49,53,94,0,27,91,49,55,94,0,27,91,49,56,94,0,27,91,49,57,94,0,27,91,50,48,94,0,27,91,50,49,94,0,27,91,50,51,94,0,27,91,50,52,94,0,27,91,50,53,94,0,27,91,50,54,94,0,27,91,50,56,94,0,27,91,50,57,94,0,27,91,51,49,94,0,27,91,49,59,54,83,0,27,91,51,50,94,0,27,91,51,51,94,0,27,91,51,52,94,0,27,91,50,51,64,0,27,91,50,52,64,0,27,91,49,75,0,27,91,63,54,57,108,0,27,91,51,57,59,52,57,109,0,27,91,51,109,0,27,91,50,51,109,0,27,91,51,56,59,53,59,37,112,49,37,100,109,0,27,91,52,56,59,53,59,37,112,49,37,100,109,0,0,2,0,0,0,74,0,92,0,-46,1,1,1,-2,-1,-2,-1,0,0,-2,-1,5,0,11,0,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,21,0,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,28,0,32,0,36,0,40,0,44,0,48,0,52,0,56,0,60,0,64,0,68,0,72,0,-2,-1,-2,-1,-2,-1,0,0,3,0,6,0,9,0,12,0,15,0,18,0,21,0,24,0,27,0,32,0,37,0,42,0,47,0,52,0,56,0,61,0,66,0,71,0,76,0,81,0,87,0,93,0,99,0,105,0,111,0,117,0,123,0,-127,0,-121,0,-115,0,-110,0,-105,0,-100,0,-95,0,-90,0,-84,0,-78,0,-72,0,-66,0,-60,0,-54,0,-48,0,-42,0,-36,0,-30,0,-24,0,-18,0,-12,0,-6,0,0,1,6,1,12,1,18,1,24,1,30,1,34,1,39,1,44,1,49,1,54,1,59,1,63,1,67,1,71,1,75,1,79,1,85,1,91,1,97,1,103,1,109,1,115,1,121,1,126,1,-125,1,27,91,51,74,0,27,91,50,32,113,0,27,91,37,112,49,37,100,32,113,0,27,91,49,59,50,65,0,27,79,120,0,27,79,116,0,27,79,118,0,27,79,114,0,27,79,69,0,27,79,107,0,27,79,108,0,27,79,111,0,27,79,110,0,27,79,106,0,27,79,109,0,27,79,112,0,65,88,0,88,84,0,67,114,0,67,115,0,69,51,0,77,115,0,83,101,0,83,115,0,88,77,0,107,68,67,51,0,107,68,67,52,0,107,68,67,53,0,107,68,67,54,0,107,68,67,55,0,107,68,78,0,107,68,78,51,0,107,68,78,52,0,107,68,78,53,0,107,68,78,54,0,107,68,78,55,0,107,69,78,68,51,0,107,69,78,68,52,0,107,69,78,68,53,0,107,69,78,68,54,0,107,69,78,68,55,0,107,72,79,77,51,0,107,72,79,77,52,0,107,72,79,77,53,0,107,72,79,77,54,0,107,72,79,77,55,0,107,73,67,51,0,107,73,67,52,0,107,73,67,53,0,107,73,67,54,0,107,73,67,55,0,107,76,70,84,51,0,107,76,70,84,52,0,107,76,70,84,53,0,107,76,70,84,54,0,107,76,70,84,55,0,107,78,88,84,51,0,107,78,88,84,52,0,107,78,88,84,53,0,107,78,88,84,54,0,107,78,88,84,55,0,107,80,82,86,51,0,107,80,82,86,52,0,107,80,82,86,53,0,107,80,82,86,54,0,107,80,82,86,55,0,107,82,73,84,51,0,107,82,73,84,52,0,107,82,73,84,53,0,107,82,73,84,54,0,107,82,73,84,55,0,107,85,80,0,107,85,80,51,0,107,85,80,52,0,107,85,80,53,0,107,85,80,54,0,107,85,80,55,0,107,97,50,0,107,98,49,0,107,98,51,0,107,99,50,0,107,112,53,0,107,112,65,68,68,0,107,112,67,77,65,0,107,112,68,73,86,0,107,112,68,79,84,0,107,112,77,85,76,0,107,112,83,85,66,0,107,112,90,82,79,0,114,109,120,120,0,115,109,120,120,0,120,109,0 }; // cygwin|ANSI emulation for Cygwin, @@ -1963,7 +1963,7 @@ static const int8_t vte_256colour_terminfo[] = { // set_a_background=\E[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m, // set_a_foreground=\E[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m, // set_attributes=\E[0%?%p1%p3%|%t;7%;%?%p2%t;4%;%?%p6%t;1%;m, -// set_lr_margin=\E[?69h\E[%i%p1%d;%p2%ds, +// set_lr_margin@, // set_tab=\EH, // tab=^I, // user6@, @@ -1971,7 +1971,7 @@ static const int8_t vte_256colour_terminfo[] = { // user8@, // user9@, static const int8_t vtpcon_terminfo[] = { - 30,2,71,0,38,0,15,0,-99,1,70,4,118,116,112,99,111,110,124,65,78,73,83,32,101,109,117,108,97,116,105,111,110,32,102,111,114,32,99,111,110,115,111,108,101,32,118,105,114,116,117,97,108,32,116,101,114,109,105,110,97,108,32,115,101,113,117,101,110,99,101,32,119,105,116,104,32,108,105,98,117,118,0,0,1,0,0,0,0,0,0,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,1,0,80,0,0,0,8,0,0,0,24,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,1,0,0,0,0,1,0,0,0,4,0,6,0,8,0,-2,-1,25,0,33,0,37,0,41,0,-1,-1,52,0,69,0,73,0,77,0,84,0,-1,-1,86,0,99,0,-1,-1,103,0,-2,-1,107,0,111,0,-1,-1,-1,-1,115,0,-2,-1,119,0,124,0,-1,-1,-2,-1,-2,-1,-2,-1,-1,-1,-123,0,-118,0,-113,0,-108,0,-99,0,-95,0,-90,0,-1,-1,-2,-1,-81,0,-75,0,-2,-1,-1,-1,-1,-1,-1,-1,-69,0,-1,-1,-1,-1,-1,-1,-59,0,-1,-1,-55,0,-1,-1,-1,-1,-1,-1,-53,0,-1,-1,-48,0,-1,-1,-1,-1,-1,-1,-1,-1,-44,0,-39,0,-33,0,-28,0,-23,0,-18,0,-13,0,-7,0,-1,0,5,1,11,1,16,1,-1,-1,21,1,-1,-1,25,1,30,1,35,1,39,1,46,1,-1,-1,53,1,-2,-1,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,57,1,-1,-1,60,1,69,1,78,1,87,1,96,1,105,1,114,1,123,1,-124,1,-115,1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,-2,-1,-106,1,-2,-1,-2,-1,-1,-1,-1,-1,-86,1,-83,1,-72,1,-69,1,-67,1,-64,1,-21,1,-1,-1,-18,1,-1,-1,-1,-1,-1,-1,-1,-1,-16,1,-12,1,-8,1,-4,1,0,2,-1,-1,-1,-1,4,2,-1,-1,27,2,-1,-1,-1,-1,-2,-1,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,31,2,36,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,40,2,-1,-1,-1,-1,47,2,-1,-1,-1,-1,-1,-1,-1,-1,54,2,61,2,68,2,-1,-1,-1,-1,75,2,-1,-1,82,2,-1,-1,-1,-1,-1,-1,89,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,96,2,102,2,108,2,114,2,120,2,126,2,-124,2,-118,2,-112,2,-106,2,-100,2,-94,2,-88,2,-82,2,-76,2,-70,2,-64,2,-58,2,-52,2,-46,2,-40,2,-34,2,-28,2,-22,2,-16,2,-10,2,-4,2,2,3,8,3,14,3,21,3,27,3,33,3,39,3,45,3,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,51,3,56,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,-2,-1,-2,-1,63,3,-2,-1,72,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-91,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-86,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-1,-1,-1,-1,-1,-1,-80,3,-17,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,47,4,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,27,91,90,0,7,0,13,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,114,0,27,91,72,27,91,50,74,0,27,91,75,0,27,91,74,0,27,91,37,105,37,112,49,37,100,71,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,72,0,27,91,66,0,27,91,72,0,27,91,63,50,53,108,0,8,0,27,91,63,49,50,108,27,91,63,50,53,104,0,27,91,67,0,27,91,65,0,27,91,80,0,27,91,77,0,27,40,48,0,27,91,49,109,0,27,91,63,49,48,52,57,104,0,27,91,55,109,0,27,91,55,109,0,27,91,52,109,0,27,91,37,112,49,37,100,88,0,27,40,66,0,27,91,48,109,0,27,91,63,49,48,52,57,108,0,27,91,50,55,109,0,27,91,50,52,109,0,27,91,33,112,27,91,63,51,108,0,27,91,76,0,8,0,27,91,51,126,0,27,91,66,0,27,91,91,65,0,27,91,50,49,126,0,27,91,91,66,0,27,91,91,67,0,27,91,91,68,0,27,91,91,69,0,27,91,49,55,126,0,27,91,49,56,126,0,27,91,49,57,126,0,27,91,50,48,126,0,27,91,49,126,0,27,91,50,126,0,27,91,68,0,27,91,54,126,0,27,91,53,126,0,27,91,67,0,27,91,49,59,50,66,0,27,91,49,59,50,65,0,27,91,65,0,27,69,0,27,91,37,112,49,37,100,80,0,27,91,37,112,49,37,100,77,0,27,91,37,112,49,37,100,66,0,27,91,37,112,49,37,100,64,0,27,91,37,112,49,37,100,83,0,27,91,37,112,49,37,100,76,0,27,91,37,112,49,37,100,68,0,27,91,37,112,49,37,100,67,0,27,91,37,112,49,37,100,84,0,27,91,37,112,49,37,100,65,0,37,112,49,37,99,27,91,37,112,50,37,123,49,125,37,45,37,100,98,0,27,56,0,27,91,37,105,37,112,49,37,100,100,0,27,55,0,10,0,27,77,0,27,91,48,37,63,37,112,49,37,112,51,37,124,37,116,59,55,37,59,37,63,37,112,50,37,116,59,52,37,59,37,63,37,112,54,37,116,59,49,37,59,109,0,27,72,0,9,0,27,79,119,0,27,79,121,0,27,91,71,0,27,79,113,0,27,79,115,0,106,106,107,107,108,108,109,109,110,110,113,113,116,116,117,117,118,118,119,119,120,120,0,27,91,90,0,27,91,52,126,0,27,79,77,0,27,91,51,59,50,126,0,27,91,52,59,50,126,0,27,91,49,59,50,126,0,27,91,50,59,50,126,0,27,91,49,59,50,68,0,27,91,54,59,50,126,0,27,91,53,59,50,126,0,27,91,49,59,50,67,0,27,91,50,51,126,0,27,91,50,52,126,0,27,91,50,53,126,0,27,91,50,54,126,0,27,91,50,56,126,0,27,91,50,57,126,0,27,91,51,49,126,0,27,91,51,50,126,0,27,91,51,51,126,0,27,91,51,52,126,0,27,91,50,51,36,0,27,91,50,52,36,0,27,91,49,49,94,0,27,91,49,50,94,0,27,91,49,51,94,0,27,91,49,52,94,0,27,91,49,53,94,0,27,91,49,55,94,0,27,91,49,56,94,0,27,91,49,57,94,0,27,91,50,48,94,0,27,91,50,49,94,0,27,91,50,51,94,0,27,91,50,52,94,0,27,91,50,53,94,0,27,91,50,54,94,0,27,91,50,56,94,0,27,91,50,57,94,0,27,91,51,49,94,0,27,91,49,59,54,83,0,27,91,51,50,94,0,27,91,51,51,94,0,27,91,51,52,94,0,27,91,50,51,64,0,27,91,50,52,64,0,27,91,49,75,0,27,91,63,54,57,108,0,27,91,51,57,59,52,57,109,0,27,93,52,59,37,112,49,37,100,59,114,103,98,58,37,112,50,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,50,46,50,88,47,37,112,51,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,50,46,50,88,47,37,112,52,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,50,46,50,88,27,0,27,91,51,109,0,27,91,50,51,109,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,51,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,57,37,112,49,37,123,56,125,37,45,37,100,37,101,51,56,59,53,59,37,112,49,37,100,37,59,109,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,52,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,49,48,37,112,49,37,123,56,125,37,45,37,100,37,101,52,56,59,53,59,37,112,49,37,100,37,59,109,0,27,91,63,54,57,104,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,115,0,2,0,0,0,74,0,92,0,-46,1,1,1,-2,-1,-2,-1,0,0,-2,-1,5,0,11,0,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,21,0,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,28,0,32,0,36,0,40,0,44,0,48,0,52,0,56,0,60,0,64,0,68,0,72,0,-2,-1,-2,-1,-2,-1,0,0,3,0,6,0,9,0,12,0,15,0,18,0,21,0,24,0,27,0,32,0,37,0,42,0,47,0,52,0,56,0,61,0,66,0,71,0,76,0,81,0,87,0,93,0,99,0,105,0,111,0,117,0,123,0,-127,0,-121,0,-115,0,-110,0,-105,0,-100,0,-95,0,-90,0,-84,0,-78,0,-72,0,-66,0,-60,0,-54,0,-48,0,-42,0,-36,0,-30,0,-24,0,-18,0,-12,0,-6,0,0,1,6,1,12,1,18,1,24,1,30,1,34,1,39,1,44,1,49,1,54,1,59,1,63,1,67,1,71,1,75,1,79,1,85,1,91,1,97,1,103,1,109,1,115,1,121,1,126,1,-125,1,27,91,51,74,0,27,91,50,32,113,0,27,91,37,112,49,37,100,32,113,0,27,91,49,59,50,65,0,27,79,120,0,27,79,116,0,27,79,118,0,27,79,114,0,27,79,69,0,27,79,107,0,27,79,108,0,27,79,111,0,27,79,110,0,27,79,106,0,27,79,109,0,27,79,112,0,65,88,0,88,84,0,67,114,0,67,115,0,69,51,0,77,115,0,83,101,0,83,115,0,88,77,0,107,68,67,51,0,107,68,67,52,0,107,68,67,53,0,107,68,67,54,0,107,68,67,55,0,107,68,78,0,107,68,78,51,0,107,68,78,52,0,107,68,78,53,0,107,68,78,54,0,107,68,78,55,0,107,69,78,68,51,0,107,69,78,68,52,0,107,69,78,68,53,0,107,69,78,68,54,0,107,69,78,68,55,0,107,72,79,77,51,0,107,72,79,77,52,0,107,72,79,77,53,0,107,72,79,77,54,0,107,72,79,77,55,0,107,73,67,51,0,107,73,67,52,0,107,73,67,53,0,107,73,67,54,0,107,73,67,55,0,107,76,70,84,51,0,107,76,70,84,52,0,107,76,70,84,53,0,107,76,70,84,54,0,107,76,70,84,55,0,107,78,88,84,51,0,107,78,88,84,52,0,107,78,88,84,53,0,107,78,88,84,54,0,107,78,88,84,55,0,107,80,82,86,51,0,107,80,82,86,52,0,107,80,82,86,53,0,107,80,82,86,54,0,107,80,82,86,55,0,107,82,73,84,51,0,107,82,73,84,52,0,107,82,73,84,53,0,107,82,73,84,54,0,107,82,73,84,55,0,107,85,80,0,107,85,80,51,0,107,85,80,52,0,107,85,80,53,0,107,85,80,54,0,107,85,80,55,0,107,97,50,0,107,98,49,0,107,98,51,0,107,99,50,0,107,112,53,0,107,112,65,68,68,0,107,112,67,77,65,0,107,112,68,73,86,0,107,112,68,79,84,0,107,112,77,85,76,0,107,112,83,85,66,0,107,112,90,82,79,0,114,109,120,120,0,115,109,120,120,0,120,109,0 + 30,2,71,0,38,0,15,0,-99,1,47,4,118,116,112,99,111,110,124,65,78,73,83,32,101,109,117,108,97,116,105,111,110,32,102,111,114,32,99,111,110,115,111,108,101,32,118,105,114,116,117,97,108,32,116,101,114,109,105,110,97,108,32,115,101,113,117,101,110,99,101,32,119,105,116,104,32,108,105,98,117,118,0,0,1,0,0,0,0,0,0,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,1,0,80,0,0,0,8,0,0,0,24,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,1,0,0,0,0,1,0,0,0,4,0,6,0,8,0,-2,-1,25,0,33,0,37,0,41,0,-1,-1,52,0,69,0,73,0,77,0,84,0,-1,-1,86,0,99,0,-1,-1,103,0,-2,-1,107,0,111,0,-1,-1,-1,-1,115,0,-2,-1,119,0,124,0,-1,-1,-2,-1,-2,-1,-2,-1,-1,-1,-123,0,-118,0,-113,0,-108,0,-99,0,-95,0,-90,0,-1,-1,-2,-1,-81,0,-75,0,-2,-1,-1,-1,-1,-1,-1,-1,-69,0,-1,-1,-1,-1,-1,-1,-59,0,-1,-1,-55,0,-1,-1,-1,-1,-1,-1,-53,0,-1,-1,-48,0,-1,-1,-1,-1,-1,-1,-1,-1,-44,0,-39,0,-33,0,-28,0,-23,0,-18,0,-13,0,-7,0,-1,0,5,1,11,1,16,1,-1,-1,21,1,-1,-1,25,1,30,1,35,1,39,1,46,1,-1,-1,53,1,-2,-1,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,57,1,-1,-1,60,1,69,1,78,1,87,1,96,1,105,1,114,1,123,1,-124,1,-115,1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,-2,-1,-106,1,-2,-1,-2,-1,-1,-1,-1,-1,-86,1,-83,1,-72,1,-69,1,-67,1,-64,1,-21,1,-1,-1,-18,1,-1,-1,-1,-1,-1,-1,-1,-1,-16,1,-12,1,-8,1,-4,1,0,2,-1,-1,-1,-1,4,2,-1,-1,27,2,-1,-1,-1,-1,-2,-1,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,31,2,36,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,40,2,-1,-1,-1,-1,47,2,-1,-1,-1,-1,-1,-1,-1,-1,54,2,61,2,68,2,-1,-1,-1,-1,75,2,-1,-1,82,2,-1,-1,-1,-1,-1,-1,89,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,96,2,102,2,108,2,114,2,120,2,126,2,-124,2,-118,2,-112,2,-106,2,-100,2,-94,2,-88,2,-82,2,-76,2,-70,2,-64,2,-58,2,-52,2,-46,2,-40,2,-34,2,-28,2,-22,2,-16,2,-10,2,-4,2,2,3,8,3,14,3,21,3,27,3,33,3,39,3,45,3,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,51,3,56,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,-2,-1,-2,-1,63,3,-2,-1,72,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-91,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-86,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-1,-1,-1,-1,-1,-1,-80,3,-17,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,27,91,90,0,7,0,13,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,114,0,27,91,72,27,91,50,74,0,27,91,75,0,27,91,74,0,27,91,37,105,37,112,49,37,100,71,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,72,0,27,91,66,0,27,91,72,0,27,91,63,50,53,108,0,8,0,27,91,63,49,50,108,27,91,63,50,53,104,0,27,91,67,0,27,91,65,0,27,91,80,0,27,91,77,0,27,40,48,0,27,91,49,109,0,27,91,63,49,48,52,57,104,0,27,91,55,109,0,27,91,55,109,0,27,91,52,109,0,27,91,37,112,49,37,100,88,0,27,40,66,0,27,91,48,109,0,27,91,63,49,48,52,57,108,0,27,91,50,55,109,0,27,91,50,52,109,0,27,91,33,112,27,91,63,51,108,0,27,91,76,0,8,0,27,91,51,126,0,27,91,66,0,27,91,91,65,0,27,91,50,49,126,0,27,91,91,66,0,27,91,91,67,0,27,91,91,68,0,27,91,91,69,0,27,91,49,55,126,0,27,91,49,56,126,0,27,91,49,57,126,0,27,91,50,48,126,0,27,91,49,126,0,27,91,50,126,0,27,91,68,0,27,91,54,126,0,27,91,53,126,0,27,91,67,0,27,91,49,59,50,66,0,27,91,49,59,50,65,0,27,91,65,0,27,69,0,27,91,37,112,49,37,100,80,0,27,91,37,112,49,37,100,77,0,27,91,37,112,49,37,100,66,0,27,91,37,112,49,37,100,64,0,27,91,37,112,49,37,100,83,0,27,91,37,112,49,37,100,76,0,27,91,37,112,49,37,100,68,0,27,91,37,112,49,37,100,67,0,27,91,37,112,49,37,100,84,0,27,91,37,112,49,37,100,65,0,37,112,49,37,99,27,91,37,112,50,37,123,49,125,37,45,37,100,98,0,27,56,0,27,91,37,105,37,112,49,37,100,100,0,27,55,0,10,0,27,77,0,27,91,48,37,63,37,112,49,37,112,51,37,124,37,116,59,55,37,59,37,63,37,112,50,37,116,59,52,37,59,37,63,37,112,54,37,116,59,49,37,59,109,0,27,72,0,9,0,27,79,119,0,27,79,121,0,27,91,71,0,27,79,113,0,27,79,115,0,106,106,107,107,108,108,109,109,110,110,113,113,116,116,117,117,118,118,119,119,120,120,0,27,91,90,0,27,91,52,126,0,27,79,77,0,27,91,51,59,50,126,0,27,91,52,59,50,126,0,27,91,49,59,50,126,0,27,91,50,59,50,126,0,27,91,49,59,50,68,0,27,91,54,59,50,126,0,27,91,53,59,50,126,0,27,91,49,59,50,67,0,27,91,50,51,126,0,27,91,50,52,126,0,27,91,50,53,126,0,27,91,50,54,126,0,27,91,50,56,126,0,27,91,50,57,126,0,27,91,51,49,126,0,27,91,51,50,126,0,27,91,51,51,126,0,27,91,51,52,126,0,27,91,50,51,36,0,27,91,50,52,36,0,27,91,49,49,94,0,27,91,49,50,94,0,27,91,49,51,94,0,27,91,49,52,94,0,27,91,49,53,94,0,27,91,49,55,94,0,27,91,49,56,94,0,27,91,49,57,94,0,27,91,50,48,94,0,27,91,50,49,94,0,27,91,50,51,94,0,27,91,50,52,94,0,27,91,50,53,94,0,27,91,50,54,94,0,27,91,50,56,94,0,27,91,50,57,94,0,27,91,51,49,94,0,27,91,49,59,54,83,0,27,91,51,50,94,0,27,91,51,51,94,0,27,91,51,52,94,0,27,91,50,51,64,0,27,91,50,52,64,0,27,91,49,75,0,27,91,63,54,57,108,0,27,91,51,57,59,52,57,109,0,27,93,52,59,37,112,49,37,100,59,114,103,98,58,37,112,50,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,50,46,50,88,47,37,112,51,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,50,46,50,88,47,37,112,52,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,50,46,50,88,27,0,27,91,51,109,0,27,91,50,51,109,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,51,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,57,37,112,49,37,123,56,125,37,45,37,100,37,101,51,56,59,53,59,37,112,49,37,100,37,59,109,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,52,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,49,48,37,112,49,37,123,56,125,37,45,37,100,37,101,52,56,59,53,59,37,112,49,37,100,37,59,109,0,0,2,0,0,0,74,0,92,0,-46,1,1,1,-2,-1,-2,-1,0,0,-2,-1,5,0,11,0,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,21,0,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,28,0,32,0,36,0,40,0,44,0,48,0,52,0,56,0,60,0,64,0,68,0,72,0,-2,-1,-2,-1,-2,-1,0,0,3,0,6,0,9,0,12,0,15,0,18,0,21,0,24,0,27,0,32,0,37,0,42,0,47,0,52,0,56,0,61,0,66,0,71,0,76,0,81,0,87,0,93,0,99,0,105,0,111,0,117,0,123,0,-127,0,-121,0,-115,0,-110,0,-105,0,-100,0,-95,0,-90,0,-84,0,-78,0,-72,0,-66,0,-60,0,-54,0,-48,0,-42,0,-36,0,-30,0,-24,0,-18,0,-12,0,-6,0,0,1,6,1,12,1,18,1,24,1,30,1,34,1,39,1,44,1,49,1,54,1,59,1,63,1,67,1,71,1,75,1,79,1,85,1,91,1,97,1,103,1,109,1,115,1,121,1,126,1,-125,1,27,91,51,74,0,27,91,50,32,113,0,27,91,37,112,49,37,100,32,113,0,27,91,49,59,50,65,0,27,79,120,0,27,79,116,0,27,79,118,0,27,79,114,0,27,79,69,0,27,79,107,0,27,79,108,0,27,79,111,0,27,79,110,0,27,79,106,0,27,79,109,0,27,79,112,0,65,88,0,88,84,0,67,114,0,67,115,0,69,51,0,77,115,0,83,101,0,83,115,0,88,77,0,107,68,67,51,0,107,68,67,52,0,107,68,67,53,0,107,68,67,54,0,107,68,67,55,0,107,68,78,0,107,68,78,51,0,107,68,78,52,0,107,68,78,53,0,107,68,78,54,0,107,68,78,55,0,107,69,78,68,51,0,107,69,78,68,52,0,107,69,78,68,53,0,107,69,78,68,54,0,107,69,78,68,55,0,107,72,79,77,51,0,107,72,79,77,52,0,107,72,79,77,53,0,107,72,79,77,54,0,107,72,79,77,55,0,107,73,67,51,0,107,73,67,52,0,107,73,67,53,0,107,73,67,54,0,107,73,67,55,0,107,76,70,84,51,0,107,76,70,84,52,0,107,76,70,84,53,0,107,76,70,84,54,0,107,76,70,84,55,0,107,78,88,84,51,0,107,78,88,84,52,0,107,78,88,84,53,0,107,78,88,84,54,0,107,78,88,84,55,0,107,80,82,86,51,0,107,80,82,86,52,0,107,80,82,86,53,0,107,80,82,86,54,0,107,80,82,86,55,0,107,82,73,84,51,0,107,82,73,84,52,0,107,82,73,84,53,0,107,82,73,84,54,0,107,82,73,84,55,0,107,85,80,0,107,85,80,51,0,107,85,80,52,0,107,85,80,53,0,107,85,80,54,0,107,85,80,55,0,107,97,50,0,107,98,49,0,107,98,51,0,107,99,50,0,107,112,53,0,107,112,65,68,68,0,107,112,67,77,65,0,107,112,68,73,86,0,107,112,68,79,84,0,107,112,77,85,76,0,107,112,83,85,66,0,107,112,90,82,79,0,114,109,120,120,0,115,109,120,120,0,120,109,0 }; // win32con|ANSI emulation for libuv on legacy console, diff --git a/src/nvim/ui.c b/src/nvim/ui.c index b40033296e..d66e57b13b 100644 --- a/src/nvim/ui.c +++ b/src/nvim/ui.c @@ -64,7 +64,7 @@ static handle_T cursor_grid_handle = DEFAULT_GRID_HANDLE; static bool has_mouse = false; static int pending_has_mouse = -1; -#if MIN_LOG_LEVEL > DEBUG_LOG_LEVEL +#if MIN_LOG_LEVEL > LOGLVL_DBG # define UI_LOG(funname) #else static size_t uilog_seen = 0; @@ -82,10 +82,10 @@ static char uilog_last_event[1024] = { 0 }; uilog_seen++; \ } else { \ if (uilog_seen > 0) { \ - logmsg(DEBUG_LOG_LEVEL, "UI: ", NULL, -1, true, \ + logmsg(LOGLVL_DBG, "UI: ", NULL, -1, true, \ "%s (+%zu times...)", uilog_last_event, uilog_seen); \ } \ - logmsg(DEBUG_LOG_LEVEL, "UI: ", NULL, -1, true, STR(funname)); \ + logmsg(LOGLVL_DBG, "UI: ", NULL, -1, true, STR(funname)); \ uilog_seen = 0; \ xstrlcpy(uilog_last_event, STR(funname), sizeof(uilog_last_event)); \ } \ diff --git a/src/nvim/ui_client.c b/src/nvim/ui_client.c index 4d1b9b1c52..be01538f67 100644 --- a/src/nvim/ui_client.c +++ b/src/nvim/ui_client.c @@ -16,18 +16,17 @@ #include "nvim/ui_client.h" #include "nvim/vim.h" -static Map(String, UIClientHandler) ui_client_handlers = MAP_INIT; +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "ui_client.c.generated.h" + +# 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; -static void add_ui_client_event_handler(String method, UIClientHandler handler) -{ - map_put(String, UIClientHandler)(&ui_client_handlers, method, handler); -} - void ui_client_init(uint64_t chan) { Array args = ARRAY_DICT_INIT; @@ -44,9 +43,6 @@ void ui_client_init(uint64_t chan) ADD(args, DICTIONARY_OBJ(opts)); rpc_send_event(chan, "nvim_ui_attach", args); - msgpack_rpc_add_redraw(); // GAME! - // TODO(bfredl): use a keyset instead - ui_client_methods_table_init(); ui_client_channel_id = chan; } @@ -61,22 +57,23 @@ void ui_client_init(uint64_t chan) /// @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 ui_client_handle_redraw(uint64_t channel_id, Array args, Error *error) +Object handle_ui_client_redraw(uint64_t channel_id, Array args, 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; - UIClientHandler handler = map_get(String, UIClientHandler)(&ui_client_handlers, name); - if (!handler) { + 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(call.items[j].data.array); + handler.fn(call.items[j].data.array); } } @@ -108,10 +105,6 @@ static HlAttrs ui_client_dict2hlattrs(Dictionary d, bool rgb) return dict2hlattrs(&dict, true, NULL, &err); } -#ifdef INCLUDE_GENERATED_DECLARATIONS -# include "ui_events_client.generated.h" -#endif - void ui_client_event_grid_resize(Array args) { if (args.size < 3 diff --git a/src/nvim/ui_client.h b/src/nvim/ui_client.h index d31341ae60..41d9fa6227 100644 --- a/src/nvim/ui_client.h +++ b/src/nvim/ui_client.h @@ -3,7 +3,10 @@ #include "nvim/api/private/defs.h" -typedef void (*UIClientHandler)(Array args); +typedef struct { + const char *name; + void (*fn)(Array args); +} UIClientHandler; #ifdef INCLUDE_GENERATED_DECLARATIONS # include "ui_client.h.generated.h" diff --git a/src/nvim/vim.h b/src/nvim/vim.h index 7e82af2d93..31ac5a67ff 100644 --- a/src/nvim/vim.h +++ b/src/nvim/vim.h @@ -70,7 +70,6 @@ enum { NUMBUFLEN = 65, }; #define MODE_EXTERNCMD 0x5000 // executing an external command #define MODE_SHOWMATCH (0x6000 | MODE_INSERT) // show matching paren #define MODE_CONFIRM 0x7000 // ":confirm" prompt -#define MODE_CMDPREVIEW 0x8000 // Showing 'inccommand' command "live" preview. /// Directions. typedef enum { diff --git a/src/nvim/window.c b/src/nvim/window.c index 060f498f07..5f4179944d 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -3993,6 +3993,7 @@ void win_init_size(void) firstwin->w_height = 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; firstwin->w_width = Columns; firstwin->w_width_inner = firstwin->w_width; @@ -6677,7 +6678,7 @@ static bool resize_frame_for_winbar(frame_T *fr) frame_new_height(fp, fp->fr_height - 1, false, false); win_new_height(wp, wp->w_height + 1); frame_fix_height(wp); - win_comp_pos(); + (void)win_comp_pos(); } return true; |
