diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/nvim/api/vim.c | 2 | ||||
-rw-r--r-- | src/nvim/autocmd.c | 3 | ||||
-rw-r--r-- | src/nvim/autocmd.h | 5 | ||||
-rw-r--r-- | src/nvim/buffer_defs.h | 1 | ||||
-rw-r--r-- | src/nvim/change.c | 5 | ||||
-rw-r--r-- | src/nvim/diff.c | 2 | ||||
-rw-r--r-- | src/nvim/edit.c | 6 | ||||
-rw-r--r-- | src/nvim/eval.lua | 17 | ||||
-rw-r--r-- | src/nvim/ex_getln.c | 3 | ||||
-rw-r--r-- | src/nvim/getchar.c | 14 | ||||
-rw-r--r-- | src/nvim/keycodes.c | 10 | ||||
-rw-r--r-- | src/nvim/lua/executor.c | 3 | ||||
-rw-r--r-- | src/nvim/lua/treesitter.c | 26 | ||||
-rw-r--r-- | src/nvim/main.c | 5 | ||||
-rw-r--r-- | src/nvim/mapping.c | 37 | ||||
-rw-r--r-- | src/nvim/menu.c | 2 | ||||
-rw-r--r-- | src/nvim/normal.c | 52 | ||||
-rw-r--r-- | src/nvim/quickfix.c | 106 | ||||
-rw-r--r-- | src/nvim/undo.c | 28 | ||||
-rw-r--r-- | src/nvim/usercmd.c | 2 | ||||
-rw-r--r-- | src/nvim/window.c | 1 |
21 files changed, 218 insertions, 112 deletions
diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 22fe69e447..4179ae40b8 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -431,7 +431,7 @@ String nvim_replace_termcodes(String str, Boolean from_part, Boolean do_lt, Bool } char *ptr = NULL; - replace_termcodes(str.data, str.size, &ptr, flags, NULL, CPO_TO_CPO_FLAGS); + replace_termcodes(str.data, str.size, &ptr, 0, flags, NULL, CPO_TO_CPO_FLAGS); return cstr_as_string(ptr); } diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index a8c5d00383..c43a59cbb3 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -1030,7 +1030,8 @@ int autocmd_register(int64_t id, event_T event, const char *pat, int patlen, int // If the event is CursorMoved, update the last cursor position // position to avoid immediately triggering the autocommand if (event == EVENT_CURSORMOVED && !has_event(EVENT_CURSORMOVED)) { - curwin->w_last_cursormoved = curwin->w_cursor; + last_cursormoved_win = curwin; + last_cursormoved = curwin->w_cursor; } // Initialize the fields checked by the WinScrolled and diff --git a/src/nvim/autocmd.h b/src/nvim/autocmd.h index 9e6c534581..b3de57311e 100644 --- a/src/nvim/autocmd.h +++ b/src/nvim/autocmd.h @@ -80,6 +80,11 @@ typedef kvec_t(AutoCmd) AutoCmdVec; // apply_autocmds_group. EXTERN bool au_did_filetype INIT(= false); +/// For CursorMoved event +EXTERN win_T *last_cursormoved_win INIT(= NULL); +/// For CursorMoved event, only used when last_cursormoved_win == curwin +EXTERN pos_T last_cursormoved INIT(= { 0, 0, 0 }); + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "autocmd.h.generated.h" #endif diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index 5708274848..ca1f791d28 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -1101,7 +1101,6 @@ struct window_S { ///< can be different from w_cursor.lnum ///< for closed folds. linenr_T w_last_cursorline; ///< where last 'cursorline' was drawn - pos_T w_last_cursormoved; ///< for CursorMoved event // the next seven are used to update the visual part char w_old_visual_mode; ///< last known VIsual_mode diff --git a/src/nvim/change.c b/src/nvim/change.c index 932de727b5..599e319dde 100644 --- a/src/nvim/change.c +++ b/src/nvim/change.c @@ -361,9 +361,10 @@ static void changed_common(linenr_T lnum, colnr_T col, linenr_T lnume, linenr_T } // when the cursor line is changed always trigger CursorMoved - if (lnum <= curwin->w_cursor.lnum + if (last_cursormoved_win == curwin + && lnum <= curwin->w_cursor.lnum && lnume + (xtra < 0 ? -xtra : xtra) > curwin->w_cursor.lnum) { - curwin->w_last_cursormoved.lnum = 0; + last_cursormoved.lnum = 0; } } diff --git a/src/nvim/diff.c b/src/nvim/diff.c index 64e47cbeb8..1f8d21220b 100644 --- a/src/nvim/diff.c +++ b/src/nvim/diff.c @@ -2384,7 +2384,7 @@ void diff_set_topline(win_T *fromwin, win_T *towin) towin->w_topline = lnum + (dp->df_lnum[toidx] - dp->df_lnum[fromidx]); if (lnum >= dp->df_lnum[fromidx]) { - if (diff_flags & DIFF_LINEMATCH) { + if (dp->is_linematched) { calculate_topfill_and_topline(fromidx, toidx, fromwin->w_topline, fromwin->w_topfill, &towin->w_topfill, &towin->w_topline); } else { diff --git a/src/nvim/edit.c b/src/nvim/edit.c index e44c49ad0b..b8d2eca810 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -1287,7 +1287,8 @@ void ins_redraw(bool ready) // Trigger CursorMoved if the cursor moved. Not when the popup menu is // visible, the command might delete it. if (ready && has_event(EVENT_CURSORMOVEDI) - && !equalpos(curwin->w_last_cursormoved, curwin->w_cursor) + && (last_cursormoved_win != curwin + || !equalpos(last_cursormoved, curwin->w_cursor)) && !pum_visible()) { // Need to update the screen first, to make sure syntax // highlighting is correct after making a change (e.g., inserting @@ -1300,7 +1301,8 @@ void ins_redraw(bool ready) // getcurpos() update_curswant(); ins_apply_autocmds(EVENT_CURSORMOVEDI); - curwin->w_last_cursormoved = curwin->w_cursor; + last_cursormoved_win = curwin; + last_cursormoved = curwin->w_cursor; } // Trigger TextChangedI if changedtick differs. diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index 4e6688a4a1..1e53014715 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -4105,6 +4105,9 @@ M.funcs = { text description of the error type type of the error, 'E', '1', etc. valid |TRUE|: recognized error message + user_data + custom data associated with the item, can be + any type. When there is no error list or it's empty, an empty list is returned. Quickfix list entries with a non-existing buffer @@ -9121,6 +9124,9 @@ M.funcs = { text description of the error type single-character error type, 'E', 'W', etc. valid recognized error message + user_data + custom data associated with the item, can be + any type. The "col", "vcol", "nr", "type" and "text" entries are optional. Either "lnum" or "pattern" entry can be used to @@ -11620,9 +11626,12 @@ M.funcs = { signature = 'undofile({name})', }, undotree = { + args = { 0, 1 }, + base = 1, desc = [=[ - Return the current state of the undo tree in a dictionary with - the following items: + Return the current state of the undo tree for the current + buffer, or for a specific buffer if {buf} is given. The + result is a dictionary with the following items: "seq_last" The highest undo sequence number used. "seq_cur" The sequence number of the current position in the undo tree. This differs from "seq_last" @@ -11664,8 +11673,8 @@ M.funcs = { item. ]=], name = 'undotree', - params = {}, - signature = 'undotree()', + params = { { 'buf', 'any' } }, + signature = 'undotree([{buf}])', }, uniq = { args = { 1, 3 }, diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 87e45cbb66..17c17e60ce 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -82,6 +82,7 @@ static unsigned last_prompt_id = 0; typedef struct { colnr_T vs_curswant; colnr_T vs_leftcol; + colnr_T vs_skipcol; linenr_T vs_topline; int vs_topfill; linenr_T vs_botline; @@ -208,6 +209,7 @@ static void save_viewstate(win_T *wp, viewstate_T *vs) { vs->vs_curswant = wp->w_curswant; vs->vs_leftcol = wp->w_leftcol; + vs->vs_skipcol = wp->w_skipcol; vs->vs_topline = wp->w_topline; vs->vs_topfill = wp->w_topfill; vs->vs_botline = wp->w_botline; @@ -219,6 +221,7 @@ static void restore_viewstate(win_T *wp, viewstate_T *vs) { wp->w_curswant = vs->vs_curswant; wp->w_leftcol = vs->vs_leftcol; + wp->w_skipcol = vs->vs_skipcol; wp->w_topline = vs->vs_topline; wp->w_topfill = vs->vs_topfill; wp->w_botline = vs->vs_botline; diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index 5c1366c5b2..2e584e7cff 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -1147,6 +1147,13 @@ static void gotchars(const uint8_t *chars, size_t len) maptick++; } +/// Record a <Nop> key. +void gotchars_nop(void) +{ + uint8_t nop_buf[3] = { K_SPECIAL, KS_EXTRA, KE_NOP }; + gotchars(nop_buf, 3); +} + /// Undo the last gotchars() for "len" bytes. To be used when putting a typed /// character back into the typeahead buffer, thus gotchars() will be called /// again. @@ -2745,14 +2752,9 @@ static int vgetorpeek(bool advance) } if (timedout && c == ESC) { - uint8_t nop_buf[3]; - // When recording there will be no timeout. Add a <Nop> after the ESC // to avoid that it forms a key code with following characters. - nop_buf[0] = K_SPECIAL; - nop_buf[1] = KS_EXTRA; - nop_buf[2] = KE_NOP; - gotchars(nop_buf, 3); + gotchars_nop(); } vgetc_busy--; diff --git a/src/nvim/keycodes.c b/src/nvim/keycodes.c index 34442ae5c4..6c64a2ca4a 100644 --- a/src/nvim/keycodes.c +++ b/src/nvim/keycodes.c @@ -873,6 +873,7 @@ int get_mouse_button(int code, bool *is_click, bool *is_drag) /// If `*bufp` is non-NULL, it will be used directly, /// and is assumed to be 128 bytes long (enough for transcoding LHS of mapping), /// and will be set to NULL in case of failure. +/// @param[in] sid_arg Script ID to use for <SID>, or 0 to use current_sctx /// @param[in] flags REPTERM_FROM_PART see above /// REPTERM_DO_LT also translate <lt> /// REPTERM_NO_SPECIAL do not accept <key> notation @@ -882,7 +883,8 @@ int get_mouse_button(int code, bool *is_click, bool *is_drag) /// /// @return The same as what `*bufp` is set to. char *replace_termcodes(const char *const from, const size_t from_len, char **const bufp, - const int flags, bool *const did_simplify, const int cpo_flags) + const scid_T sid_arg, const int flags, bool *const did_simplify, + const int cpo_flags) FUNC_ATTR_NONNULL_ARG(1, 3) { ssize_t i; @@ -916,15 +918,15 @@ char *replace_termcodes(const char *const from, const size_t from_len, char **co // Replace <SID> by K_SNR <script-nr> _. // (room: 5 * 6 = 30 bytes; needed: 3 + <nr> + 1 <= 14) if (end - src >= 4 && STRNICMP(src, "<SID>", 5) == 0) { - if (current_sctx.sc_sid <= 0) { + if (sid_arg < 0 || (sid_arg == 0 && current_sctx.sc_sid <= 0)) { emsg(_(e_usingsid)); } else { + const scid_T sid = sid_arg != 0 ? sid_arg : current_sctx.sc_sid; src += 5; result[dlen++] = (char)K_SPECIAL; result[dlen++] = (char)KS_EXTRA; result[dlen++] = KE_SNR; - snprintf(result + dlen, buf_len - dlen, "%" PRId64, - (int64_t)current_sctx.sc_sid); + snprintf(result + dlen, buf_len - dlen, "%" PRId64, (int64_t)sid); dlen += strlen(result + dlen); result[dlen++] = '_'; continue; diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index 9215926434..459c69f385 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -1302,6 +1302,9 @@ LuaRef nlua_ref(lua_State *lstate, nlua_ref_state_t *ref_state, int index) return ref; } +// TODO(lewis6991): Currently cannot be run in __gc metamethods as they are +// invoked in lua_close() which can be invoked after the ref_markers map is +// destroyed in nlua_common_free_all_mem. LuaRef nlua_ref_global(lua_State *lstate, int index) { return nlua_ref(lstate, nlua_global_refs, index); diff --git a/src/nvim/lua/treesitter.c b/src/nvim/lua/treesitter.c index 66a75f8d40..1e559316dd 100644 --- a/src/nvim/lua/treesitter.c +++ b/src/nvim/lua/treesitter.c @@ -325,6 +325,17 @@ static TSParser **parser_check(lua_State *L, uint16_t index) return luaL_checkudata(L, index, TS_META_PARSER); } +static void logger_gc(TSLogger logger) +{ + if (!logger.log) { + return; + } + + TSLuaLoggerOpts *opts = (TSLuaLoggerOpts *)logger.payload; + luaL_unref(opts->lstate, LUA_REGISTRYINDEX, opts->cb); + xfree(opts); +} + static int parser_gc(lua_State *L) { TSParser **p = parser_check(L, 1); @@ -332,12 +343,7 @@ static int parser_gc(lua_State *L) return 0; } - TSLogger logger = ts_parser_logger(*p); - if (logger.log) { - TSLuaLoggerOpts *opts = (TSLuaLoggerOpts *)logger.payload; - xfree(opts); - } - + logger_gc(ts_parser_logger(*p)); ts_parser_delete(*p); return 0; } @@ -698,7 +704,7 @@ static void logger_cb(void *payload, TSLogType logtype, const char *s) lua_State *lstate = opts->lstate; - nlua_pushref(lstate, opts->cb); + lua_rawgeti(lstate, LUA_REGISTRYINDEX, opts->cb); lua_pushstring(lstate, logtype == TSLogTypeParse ? "parse" : "lex"); lua_pushstring(lstate, s); if (lua_pcall(lstate, 2, 0, 0)) { @@ -726,11 +732,13 @@ static int parser_set_logger(lua_State *L) } TSLuaLoggerOpts *opts = xmalloc(sizeof(TSLuaLoggerOpts)); + lua_pushvalue(L, 4); + LuaRef ref = luaL_ref(L, LUA_REGISTRYINDEX); *opts = (TSLuaLoggerOpts){ .lex = lua_toboolean(L, 2), .parse = lua_toboolean(L, 3), - .cb = nlua_ref_global(L, 4), + .cb = ref, .lstate = L }; @@ -753,7 +761,7 @@ static int parser_get_logger(lua_State *L) TSLogger logger = ts_parser_logger(*p); if (logger.log) { TSLuaLoggerOpts *opts = (TSLuaLoggerOpts *)logger.payload; - nlua_pushref(L, opts->cb); + lua_rawgeti(L, LUA_REGISTRYINDEX, opts->cb); } else { lua_pushnil(L); } diff --git a/src/nvim/main.c b/src/nvim/main.c index 377b804661..d9ca82784f 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -921,6 +921,11 @@ static void remote_request(mparm_T *params, int remote_args, char *server_addr, os_errmsg(connect_error); os_errmsg("\n"); os_exit(1); + } else if (strequal(server_addr, os_getenv("NVIM"))) { + os_errmsg("Cannot attach UI of :terminal child to its parent. "); + os_errmsg("(Unset $NVIM to skip this check)"); + os_errmsg("\n"); + os_exit(1); } ui_client_channel_id = chan; diff --git a/src/nvim/mapping.c b/src/nvim/mapping.c index 0cb94e6f5b..f2732184db 100644 --- a/src/nvim/mapping.c +++ b/src/nvim/mapping.c @@ -279,16 +279,16 @@ static bool set_maparg_lhs_rhs(const char *const orig_lhs, const size_t orig_lhs bool did_simplify = false; const int flags = REPTERM_FROM_PART | REPTERM_DO_LT; char *bufarg = lhs_buf; - char *replaced = replace_termcodes(orig_lhs, orig_lhs_len, &bufarg, flags, &did_simplify, - cpo_flags); + char *replaced = replace_termcodes(orig_lhs, orig_lhs_len, &bufarg, 0, + flags, &did_simplify, cpo_flags); if (replaced == NULL) { return false; } mapargs->lhs_len = strlen(replaced); xstrlcpy(mapargs->lhs, replaced, sizeof(mapargs->lhs)); if (did_simplify) { - replaced = replace_termcodes(orig_lhs, orig_lhs_len, &bufarg, flags | REPTERM_NO_SIMPLIFY, - NULL, cpo_flags); + replaced = replace_termcodes(orig_lhs, orig_lhs_len, &bufarg, 0, + flags | REPTERM_NO_SIMPLIFY, NULL, cpo_flags); if (replaced == NULL) { return false; } @@ -298,14 +298,15 @@ static bool set_maparg_lhs_rhs(const char *const orig_lhs, const size_t orig_lhs mapargs->alt_lhs_len = 0; } - set_maparg_rhs(orig_rhs, orig_rhs_len, rhs_lua, cpo_flags, mapargs); + set_maparg_rhs(orig_rhs, orig_rhs_len, rhs_lua, 0, cpo_flags, mapargs); return true; } /// @see set_maparg_lhs_rhs static void set_maparg_rhs(const char *const orig_rhs, const size_t orig_rhs_len, - const LuaRef rhs_lua, const int cpo_flags, MapArguments *const mapargs) + const LuaRef rhs_lua, const scid_T sid, const int cpo_flags, + MapArguments *const mapargs) { mapargs->rhs_lua = rhs_lua; @@ -319,8 +320,8 @@ static void set_maparg_rhs(const char *const orig_rhs, const size_t orig_rhs_len mapargs->rhs_is_noop = true; } else { char *rhs_buf = NULL; - char *replaced = replace_termcodes(orig_rhs, orig_rhs_len, &rhs_buf, REPTERM_DO_LT, NULL, - cpo_flags); + char *replaced = replace_termcodes(orig_rhs, orig_rhs_len, &rhs_buf, sid, + REPTERM_DO_LT, NULL, cpo_flags); mapargs->rhs_len = strlen(replaced); // NB: replace_termcodes may produce an empty string even if orig_rhs is non-empty // (e.g. a single ^V, see :h map-empty-rhs) @@ -1079,9 +1080,8 @@ bool map_to_exists(const char *const str, const char *const modechars, const boo int retval; char *buf = NULL; - const char *const rhs = replace_termcodes(str, strlen(str), - &buf, REPTERM_DO_LT, - NULL, CPO_TO_CPO_FLAGS); + const char *const rhs = replace_termcodes(str, strlen(str), &buf, 0, + REPTERM_DO_LT, NULL, CPO_TO_CPO_FLAGS); #define MAPMODE(mode, modechars, chr, modeflags) \ do { \ @@ -1657,7 +1657,7 @@ char *eval_map_expr(mapblock_T *mp, int c) char *res = NULL; if (replace_keycodes) { - replace_termcodes(p, strlen(p), &res, REPTERM_DO_LT, NULL, CPO_TO_CPO_FLAGS); + replace_termcodes(p, strlen(p), &res, 0, REPTERM_DO_LT, NULL, CPO_TO_CPO_FLAGS); } else { // Escape K_SPECIAL in the result to be able to use the string as typeahead. res = vim_strsave_escape_ks(p); @@ -2157,8 +2157,8 @@ static void get_maparg(typval_T *argvars, typval_T *rettv, int exact) const int flags = REPTERM_FROM_PART | REPTERM_DO_LT; const int mode = get_map_mode((char **)&which, 0); - char *keys_simplified = replace_termcodes(keys, strlen(keys), &keys_buf, flags, &did_simplify, - CPO_TO_CPO_FLAGS); + char *keys_simplified = replace_termcodes(keys, strlen(keys), &keys_buf, 0, + flags, &did_simplify, CPO_TO_CPO_FLAGS); mapblock_T *mp = NULL; int buffer_local; LuaRef rhs_lua; @@ -2167,10 +2167,8 @@ static void get_maparg(typval_T *argvars, typval_T *rettv, int exact) if (did_simplify) { // When the lhs is being simplified the not-simplified keys are // preferred for printing, like in do_map(). - (void)replace_termcodes(keys, - strlen(keys), - &alt_keys_buf, flags | REPTERM_NO_SIMPLIFY, NULL, - CPO_TO_CPO_FLAGS); + (void)replace_termcodes(keys, strlen(keys), &alt_keys_buf, 0, + flags | REPTERM_NO_SIMPLIFY, NULL, CPO_TO_CPO_FLAGS); rhs = check_map(alt_keys_buf, mode, exact, false, abbr, &mp, &buffer_local, &rhs_lua); } @@ -2252,12 +2250,13 @@ void f_mapset(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) .replace_keycodes = tv_dict_get_number(d, "replace_keycodes") != 0, .desc = tv_dict_get_string(d, "desc", false), }; - set_maparg_rhs(orig_rhs, strlen(orig_rhs), rhs_lua, CPO_TO_CPO_FLAGS, &args); scid_T sid = (scid_T)tv_dict_get_number(d, "sid"); linenr_T lnum = (linenr_T)tv_dict_get_number(d, "lnum"); bool buffer = tv_dict_get_number(d, "buffer") != 0; // mode from the dict is not used + set_maparg_rhs(orig_rhs, strlen(orig_rhs), rhs_lua, sid, CPO_TO_CPO_FLAGS, &args); + mapblock_T **map_table = buffer ? curbuf->b_maphash : maphash; mapblock_T **abbr_table = buffer ? &curbuf->b_first_abbr : &first_abbr; diff --git a/src/nvim/menu.c b/src/nvim/menu.c index 898e3ddd27..1d35c97b39 100644 --- a/src/nvim/menu.c +++ b/src/nvim/menu.c @@ -232,7 +232,7 @@ void ex_menu(exarg_T *eap) map_buf = NULL; // Menu tips are plain text. } else { map_buf = NULL; - map_to = replace_termcodes(map_to, strlen(map_to), &map_buf, + map_to = replace_termcodes(map_to, strlen(map_to), &map_buf, 0, REPTERM_DO_LT, NULL, CPO_TO_CPO_FLAGS); } menuarg.modes = modes; diff --git a/src/nvim/normal.c b/src/nvim/normal.c index c5538fb7dd..edfc62ae17 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -807,25 +807,32 @@ static void normal_get_additional_char(NormalState *s) } } - // When getting a text character and the next character is a - // multi-byte character, it could be a composing character. - // However, don't wait for it to arrive. Also, do enable mapping, - // because if it's put back with vungetc() it's too late to apply - // mapping. - no_mapping--; - while (lang && (s->c = vpeekc()) > 0 - && (s->c >= 0x100 || MB_BYTE2LEN(vpeekc()) > 1)) { - s->c = plain_vgetc(); - if (!utf_iscomposing(s->c)) { - vungetc(s->c); // it wasn't, put it back - break; - } else if (s->ca.ncharC1 == 0) { - s->ca.ncharC1 = s->c; - } else { - s->ca.ncharC2 = s->c; + if (lang) { + // When getting a text character and the next character is a + // multi-byte character, it could be a composing character. + // However, don't wait for it to arrive. Also, do enable mapping, + // because if it's put back with vungetc() it's too late to apply + // mapping. + no_mapping--; + while (lang && (s->c = vpeekc()) > 0 + && (s->c >= 0x100 || MB_BYTE2LEN(vpeekc()) > 1)) { + s->c = plain_vgetc(); + if (!utf_iscomposing(s->c)) { + vungetc(s->c); // it wasn't, put it back + break; + } else if (s->ca.ncharC1 == 0) { + s->ca.ncharC1 = s->c; + } else { + s->ca.ncharC2 = s->c; + } } + no_mapping++; + // Vim may be in a different mode when the user types the next key, + // but when replaying a recording the next key is already in the + // typeahead buffer, so record a <Nop> before that to prevent the + // vpeekc() above from applying wrong mappings when replaying. + gotchars_nop(); } - no_mapping++; } no_mapping--; allow_keys--; @@ -1260,9 +1267,11 @@ static void normal_check_cursor_moved(NormalState *s) { // Trigger CursorMoved if the cursor moved. if (!finish_op && has_event(EVENT_CURSORMOVED) - && !equalpos(curwin->w_last_cursormoved, curwin->w_cursor)) { + && (last_cursormoved_win != curwin + || !equalpos(last_cursormoved, curwin->w_cursor))) { apply_autocmds(EVENT_CURSORMOVED, NULL, NULL, false, curbuf); - curwin->w_last_cursormoved = curwin->w_cursor; + last_cursormoved_win = curwin; + last_cursormoved = curwin->w_cursor; } } @@ -4506,7 +4515,7 @@ static void nv_replace(cmdarg_T *cap) } // get another character - if (cap->nchar == Ctrl_V) { + if (cap->nchar == Ctrl_V || cap->nchar == Ctrl_Q) { had_ctrl_v = Ctrl_V; cap->nchar = get_literal(false); // Don't redo a multibyte character with CTRL-V. @@ -4733,7 +4742,8 @@ static void nv_vreplace(cmdarg_T *cap) if (!MODIFIABLE(curbuf)) { emsg(_(e_modifiable)); } else { - if (cap->extra_char == Ctrl_V) { // get another character + if (cap->extra_char == Ctrl_V || cap->extra_char == Ctrl_Q) { + // get another character cap->extra_char = get_literal(false); } if (cap->extra_char < ' ') { diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index d42e0ed24f..00a9dad1fe 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -80,14 +80,14 @@ struct qfline_S { int qf_col; ///< column where the error occurred int qf_end_col; ///< column when the error has range or zero int qf_nr; ///< error number - char *qf_module; ///< module name for this error - char *qf_pattern; ///< search pattern for the error - char *qf_text; ///< description of the error - char qf_viscol; ///< set to true if qf_col and qf_end_col is - // screen column - char qf_cleared; ///< set to true if line has been deleted - char qf_type; ///< type of the error (mostly 'E'); 1 for :helpgrep - char qf_valid; ///< valid error message detected + char *qf_module; ///< module name for this error + char *qf_pattern; ///< search pattern for the error + char *qf_text; ///< description of the error + char qf_viscol; ///< set to true if qf_col and qf_end_col is screen column + char qf_cleared; ///< set to true if line has been deleted + char qf_type; ///< type of the error (mostly 'E'); 1 for :helpgrep + typval_T qf_user_data; ///< custom user data associated with this item + char qf_valid; ///< valid error message detected }; // There is a stack of error lists. @@ -109,18 +109,19 @@ typedef enum { /// created using setqflist()/setloclist() with a title and/or user context /// information and entries can be added later using setqflist()/setloclist(). typedef struct qf_list_S { - unsigned qf_id; ///< Unique identifier for this list + unsigned qf_id; ///< Unique identifier for this list qfltype_T qfl_type; - qfline_T *qf_start; ///< pointer to the first error - qfline_T *qf_last; ///< pointer to the last error - qfline_T *qf_ptr; ///< pointer to the current error - int qf_count; ///< number of errors (0 means empty list) - int qf_index; ///< current index in the error list - int qf_nonevalid; ///< true if not a single valid entry found - char *qf_title; ///< title derived from the command that created - ///< the error list or set by setqflist - typval_T *qf_ctx; ///< context set by setqflist/setloclist - Callback qf_qftf_cb; ///< 'quickfixtextfunc' callback function + qfline_T *qf_start; ///< pointer to the first error + qfline_T *qf_last; ///< pointer to the last error + qfline_T *qf_ptr; ///< pointer to the current error + int qf_count; ///< number of errors (0 means empty list) + int qf_index; ///< current index in the error list + bool qf_nonevalid; ///< true if not a single valid entry found + bool qf_has_user_data; ///< true if at least one item has user_data attached + char *qf_title; ///< title derived from the command that created + ///< the error list or set by setqflist + typval_T *qf_ctx; ///< context set by setqflist/setloclist + Callback qf_qftf_cb; ///< 'quickfixtextfunc' callback function struct dir_stack_T *qf_dir_stack; char *qf_directory; @@ -226,6 +227,7 @@ typedef struct { char *pattern; int enr; char type; + typval_T *user_data; bool valid; } qffields_T; @@ -351,6 +353,7 @@ static int qf_init_process_nextline(qf_list_T *qfl, efm_T *fmt_first, qfstate_T fields->pattern, fields->enr, fields->type, + fields->user_data, fields->valid); } @@ -1281,6 +1284,7 @@ static void qf_new_list(qf_info_T *qi, const char *qf_title) qf_store_title(qfl, qf_title); qfl->qfl_type = qi->qfl_type; qfl->qf_id = ++last_qf_id; + qfl->qf_has_user_data = false; } /// Parse the match for filename ('%f') pattern in regmatch. @@ -1836,12 +1840,14 @@ void check_quickfix_busy(void) /// @param pattern search pattern /// @param nr error number /// @param type type character +/// @param user_data custom user data or NULL /// @param valid valid entry /// /// @return QF_OK on success or QF_FAIL on failure. static int qf_add_entry(qf_list_T *qfl, char *dir, char *fname, char *module, int bufnum, char *mesg, linenr_T lnum, linenr_T end_lnum, int col, int end_col, - char vis_col, char *pattern, int nr, char type, char valid) + char vis_col, char *pattern, int nr, char type, typval_T *user_data, + char valid) { qfline_T *qfp = xmalloc(sizeof(qfline_T)); @@ -1862,6 +1868,12 @@ static int qf_add_entry(qf_list_T *qfl, char *dir, char *fname, char *module, in qfp->qf_col = col; qfp->qf_end_col = end_col; qfp->qf_viscol = vis_col; + if (user_data == NULL || user_data->v_type == VAR_UNKNOWN) { + qfp->qf_user_data.v_type = VAR_UNKNOWN; + } else { + tv_copy(user_data, &qfp->qf_user_data); + qfl->qf_has_user_data = true; + } if (pattern == NULL || *pattern == NUL) { qfp->qf_pattern = NULL; } else { @@ -1997,6 +2009,7 @@ static int copy_loclist_entries(const qf_list_T *from_qfl, qf_list_T *to_qfl) from_qfp->qf_pattern, from_qfp->qf_nr, 0, + &from_qfp->qf_user_data, from_qfp->qf_valid) == QF_FAIL) { return FAIL; } @@ -2022,6 +2035,7 @@ static int copy_loclist(qf_list_T *from_qfl, qf_list_T *to_qfl) // Some of the fields are populated by qf_add_entry() to_qfl->qfl_type = from_qfl->qfl_type; to_qfl->qf_nonevalid = from_qfl->qf_nonevalid; + to_qfl->qf_has_user_data = from_qfl->qf_has_user_data; to_qfl->qf_count = 0; to_qfl->qf_index = 0; to_qfl->qf_start = NULL; @@ -3374,6 +3388,7 @@ static void qf_free_items(qf_list_T *qfl) xfree(qfp->qf_module); xfree(qfp->qf_text); xfree(qfp->qf_pattern); + tv_clear(&qfp->qf_user_data); stop = (qfp == qfpnext); xfree(qfp); if (stop) { @@ -5239,6 +5254,7 @@ static bool vgr_match_buflines(qf_list_T *qfl, char *fname, buf_T *buf, char *sp NULL, // search pattern 0, // nr 0, // type + NULL, // user_data true) // valid == QF_FAIL) { got_int = true; @@ -5282,6 +5298,7 @@ static bool vgr_match_buflines(qf_list_T *qfl, char *fname, buf_T *buf, char *sp NULL, // search pattern 0, // nr 0, // type + NULL, // user_data true) // valid == QF_FAIL) { got_int = true; @@ -5809,6 +5826,8 @@ static int get_qfline_items(qfline_T *qfp, list_T *list) == FAIL) || (tv_dict_add_str(dict, S_LEN("text"), (qfp->qf_text == NULL ? "" : qfp->qf_text)) == FAIL) || (tv_dict_add_str(dict, S_LEN("type"), buf) == FAIL) + || (qfp->qf_user_data.v_type != VAR_UNKNOWN + && tv_dict_add_tv(dict, S_LEN("user_data"), &qfp->qf_user_data) == FAIL) || (tv_dict_add_nr(dict, S_LEN("valid"), (varnumber_T)qfp->qf_valid) == FAIL)) { // tv_dict_add* fail only if key already exist, but this is a newly // allocated dictionary which is thus guaranteed to have no existing keys. @@ -6288,8 +6307,7 @@ static int qf_setprop_qftf(qf_list_T *qfl, dictitem_T *di) /// Add a new quickfix entry to list at 'qf_idx' in the stack 'qi' from the /// items in the dict 'd'. If it is a valid error entry, then set 'valid_entry' /// to true. -static int qf_add_entry_from_dict(qf_list_T *qfl, const dict_T *d, bool first_entry, - bool *valid_entry) +static int qf_add_entry_from_dict(qf_list_T *qfl, dict_T *d, bool first_entry, bool *valid_entry) FUNC_ATTR_NONNULL_ALL { static bool did_bufnr_emsg; @@ -6313,6 +6331,9 @@ static int qf_add_entry_from_dict(qf_list_T *qfl, const dict_T *d, bool first_en if (text == NULL) { text = xcalloc(1, 1); } + typval_T user_data = { .v_type = VAR_UNKNOWN }; + tv_dict_get_tv(d, "user_data", &user_data); + bool valid = true; if ((filename == NULL && bufnum == 0) || (lnum == 0 && pattern == NULL)) { @@ -6349,12 +6370,14 @@ static int qf_add_entry_from_dict(qf_list_T *qfl, const dict_T *d, bool first_en pattern, // search pattern nr, type == NULL ? NUL : *type, + &user_data, valid); xfree(filename); xfree(module); xfree(pattern); xfree(text); + tv_clear(&user_data); if (valid) { *valid_entry = true; @@ -6390,13 +6413,12 @@ static int qf_add_entries(qf_info_T *qi, int qf_idx, list_T *list, char *title, continue; // Skip non-dict items. } - const dict_T *const d = TV_LIST_ITEM_TV(li)->vval.v_dict; + dict_T *const d = TV_LIST_ITEM_TV(li)->vval.v_dict; if (d == NULL) { continue; } - retval = qf_add_entry_from_dict(qfl, d, li == tv_list_first(list), - &valid_entry); + retval = qf_add_entry_from_dict(qfl, d, li == tv_list_first(list), &valid_entry); if (retval == QF_FAIL) { break; } @@ -6734,6 +6756,27 @@ int set_errorlist(win_T *wp, list_T *list, int action, char *title, dict_T *what return retval; } +static bool mark_quickfix_user_data(qf_info_T *qi, int copyID) +{ + bool abort = false; + for (int i = 0; i < LISTCOUNT && !abort; i++) { + qf_list_T *qfl = &qi->qf_lists[i]; + if (!qfl->qf_has_user_data) { + continue; + } + qfline_T *qfp; + int j; + FOR_ALL_QFL_ITEMS(qfl, qfp, j) { + typval_T *user_data = &qfp->qf_user_data; + if (user_data != NULL && user_data->v_type != VAR_NUMBER + && user_data->v_type != VAR_STRING && user_data->v_type != VAR_FLOAT) { + abort = abort || set_ref_in_item(user_data, copyID, NULL, NULL); + } + } + } + return abort; +} + /// Mark the quickfix context and callback function as in use for all the lists /// in a quickfix stack. static bool mark_quickfix_ctx(qf_info_T *qi, int copyID) @@ -6763,6 +6806,11 @@ bool set_ref_in_quickfix(int copyID) return abort; } + abort = mark_quickfix_user_data(&ql_info, copyID); + if (abort) { + return abort; + } + abort = set_ref_in_callback(&qftf_cb, copyID, NULL, NULL); if (abort) { return abort; @@ -6774,6 +6822,11 @@ bool set_ref_in_quickfix(int copyID) if (abort) { return abort; } + + abort = mark_quickfix_user_data(win->w_llist, copyID); + if (abort) { + return abort; + } } if (IS_LL_WINDOW(win) && (win->w_llist_ref->qf_refcount == 1)) { @@ -7054,7 +7107,8 @@ static void hgr_search_file(qf_list_T *qfl, char *fname, regmatch_T *p_regmatch) NULL, // search pattern 0, // nr 1, // type - true) // valid + NULL, // user_data + true) // valid == QF_FAIL) { got_int = true; if (line != IObuff) { diff --git a/src/nvim/undo.c b/src/nvim/undo.c index b324b777a6..695cf81f73 100644 --- a/src/nvim/undo.c +++ b/src/nvim/undo.c @@ -93,6 +93,7 @@ #include "nvim/cursor.h" #include "nvim/drawscreen.h" #include "nvim/edit.h" +#include "nvim/eval/funcs.h" #include "nvim/eval/typval.h" #include "nvim/eval/typval_defs.h" #include "nvim/ex_cmds_defs.h" @@ -3118,7 +3119,7 @@ bool curbufIsChanged(void) /// @param[in] first_uhp Undo blocks list to start with. /// /// @return [allocated] List with a representation of undo blocks. -static list_T *u_eval_tree(const u_header_T *const first_uhp) +static list_T *u_eval_tree(buf_T *const buf, const u_header_T *const first_uhp) FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_RET { list_T *const list = tv_list_alloc(kListLenMayKnow); @@ -3127,10 +3128,10 @@ static list_T *u_eval_tree(const u_header_T *const first_uhp) dict_T *const dict = tv_dict_alloc(); tv_dict_add_nr(dict, S_LEN("seq"), (varnumber_T)uhp->uh_seq); tv_dict_add_nr(dict, S_LEN("time"), (varnumber_T)uhp->uh_time); - if (uhp == curbuf->b_u_newhead) { + if (uhp == buf->b_u_newhead) { tv_dict_add_nr(dict, S_LEN("newhead"), 1); } - if (uhp == curbuf->b_u_curhead) { + if (uhp == buf->b_u_curhead) { tv_dict_add_nr(dict, S_LEN("curhead"), 1); } if (uhp->uh_save_nr > 0) { @@ -3139,7 +3140,7 @@ static list_T *u_eval_tree(const u_header_T *const first_uhp) if (uhp->uh_alt_next.ptr != NULL) { // Recursive call to add alternate undo tree. - tv_dict_add_list(dict, S_LEN("alt"), u_eval_tree(uhp->uh_alt_next.ptr)); + tv_dict_add_list(dict, S_LEN("alt"), u_eval_tree(buf, uhp->uh_alt_next.ptr)); } tv_list_append_dict(list, dict); @@ -3167,21 +3168,24 @@ void f_undofile(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) } } -/// "undotree()" function +/// "undotree(expr)" function void f_undotree(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { + typval_T *const tv = &argvars[0]; + buf_T *const buf = tv->v_type == VAR_UNKNOWN ? curbuf : tv_get_buf_from_arg(tv); + tv_dict_alloc_ret(rettv); dict_T *dict = rettv->vval.v_dict; - tv_dict_add_nr(dict, S_LEN("synced"), (varnumber_T)curbuf->b_u_synced); - tv_dict_add_nr(dict, S_LEN("seq_last"), (varnumber_T)curbuf->b_u_seq_last); - tv_dict_add_nr(dict, S_LEN("save_last"), (varnumber_T)curbuf->b_u_save_nr_last); - tv_dict_add_nr(dict, S_LEN("seq_cur"), (varnumber_T)curbuf->b_u_seq_cur); - tv_dict_add_nr(dict, S_LEN("time_cur"), (varnumber_T)curbuf->b_u_time_cur); - tv_dict_add_nr(dict, S_LEN("save_cur"), (varnumber_T)curbuf->b_u_save_nr_cur); + tv_dict_add_nr(dict, S_LEN("synced"), (varnumber_T)buf->b_u_synced); + tv_dict_add_nr(dict, S_LEN("seq_last"), (varnumber_T)buf->b_u_seq_last); + tv_dict_add_nr(dict, S_LEN("save_last"), (varnumber_T)buf->b_u_save_nr_last); + tv_dict_add_nr(dict, S_LEN("seq_cur"), (varnumber_T)buf->b_u_seq_cur); + tv_dict_add_nr(dict, S_LEN("time_cur"), (varnumber_T)buf->b_u_time_cur); + tv_dict_add_nr(dict, S_LEN("save_cur"), (varnumber_T)buf->b_u_save_nr_cur); - tv_dict_add_list(dict, S_LEN("entries"), u_eval_tree(curbuf->b_u_oldhead)); + tv_dict_add_list(dict, S_LEN("entries"), u_eval_tree(buf, buf->b_u_oldhead)); } // Given the buffer, Return the undo header. If none is set, set one first. diff --git a/src/nvim/usercmd.c b/src/nvim/usercmd.c index 41a8ad2d97..65720342ce 100644 --- a/src/nvim/usercmd.c +++ b/src/nvim/usercmd.c @@ -876,7 +876,7 @@ int uc_add_command(char *name, size_t name_len, const char *rep, uint32_t argt, char *rep_buf = NULL; garray_T *gap; - replace_termcodes(rep, strlen(rep), &rep_buf, 0, NULL, CPO_TO_CPO_FLAGS); + replace_termcodes(rep, strlen(rep), &rep_buf, 0, 0, NULL, CPO_TO_CPO_FLAGS); if (rep_buf == NULL) { // Can't replace termcodes - try using the string as is rep_buf = xstrdup(rep); diff --git a/src/nvim/window.c b/src/nvim/window.c index d6d677de3f..c475169261 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -4943,7 +4943,6 @@ static void win_enter_ext(win_T *const wp, const int flags) if (other_buffer) { apply_autocmds(EVENT_BUFENTER, NULL, NULL, false, curbuf); } - curwin->w_last_cursormoved.lnum = 0; } maketitle(); |