aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/nvim/api/vim.c2
-rw-r--r--src/nvim/autocmd.c3
-rw-r--r--src/nvim/autocmd.h5
-rw-r--r--src/nvim/buffer_defs.h1
-rw-r--r--src/nvim/change.c5
-rw-r--r--src/nvim/diff.c2
-rw-r--r--src/nvim/edit.c6
-rw-r--r--src/nvim/eval.lua17
-rw-r--r--src/nvim/ex_getln.c3
-rw-r--r--src/nvim/getchar.c14
-rw-r--r--src/nvim/keycodes.c10
-rw-r--r--src/nvim/lua/executor.c3
-rw-r--r--src/nvim/lua/treesitter.c26
-rw-r--r--src/nvim/main.c5
-rw-r--r--src/nvim/mapping.c37
-rw-r--r--src/nvim/menu.c2
-rw-r--r--src/nvim/normal.c52
-rw-r--r--src/nvim/quickfix.c106
-rw-r--r--src/nvim/undo.c28
-rw-r--r--src/nvim/usercmd.c2
-rw-r--r--src/nvim/window.c1
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();