aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/eval
diff options
context:
space:
mode:
authorJosh Rahm <joshuarahm@gmail.com>2024-05-24 19:18:11 +0000
committerJosh Rahm <joshuarahm@gmail.com>2024-05-24 19:18:11 +0000
commitff7ed8f586589d620a806c3758fac4a47a8e7e15 (patch)
tree729bbcb92231538fa61dab6c3d890b025484b7f5 /src/nvim/eval
parent376914f419eb08fdf4c1a63a77e1f035898a0f10 (diff)
parent28c04948a1c887a1cc0cb64de79fa32631700466 (diff)
downloadrneovim-ff7ed8f586589d620a806c3758fac4a47a8e7e15.tar.gz
rneovim-ff7ed8f586589d620a806c3758fac4a47a8e7e15.tar.bz2
rneovim-ff7ed8f586589d620a806c3758fac4a47a8e7e15.zip
Merge remote-tracking branch 'upstream/master' into mix_20240309
Diffstat (limited to 'src/nvim/eval')
-rw-r--r--src/nvim/eval/buffer.c6
-rw-r--r--src/nvim/eval/funcs.c361
-rw-r--r--src/nvim/eval/typval.c2
-rw-r--r--src/nvim/eval/typval_defs.h1
-rw-r--r--src/nvim/eval/userfunc.c39
-rw-r--r--src/nvim/eval/vars.c90
-rw-r--r--src/nvim/eval/window.c105
7 files changed, 399 insertions, 205 deletions
diff --git a/src/nvim/eval/buffer.c b/src/nvim/eval/buffer.c
index 7b8f71ef3f..73bfd6db2a 100644
--- a/src/nvim/eval/buffer.c
+++ b/src/nvim/eval/buffer.c
@@ -197,7 +197,7 @@ static void set_buffer_lines(buf_T *buf, linenr_T lnum_arg, bool append, typval_
&& ml_replace(lnum, line, true) == OK) {
inserted_bytes(lnum, 0, old_len, (int)strlen(line));
if (is_curbuf && lnum == curwin->w_cursor.lnum) {
- check_cursor_col();
+ check_cursor_col(curwin);
}
rettv->vval.v_number = 0; // OK
}
@@ -229,7 +229,7 @@ static void set_buffer_lines(buf_T *buf, linenr_T lnum_arg, bool append, typval_
wp->w_cursor.lnum += (linenr_T)added;
}
}
- check_cursor_col();
+ check_cursor_col(curwin);
update_topline(curwin);
}
@@ -469,7 +469,7 @@ void f_deletebufline(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
}
}
}
- check_cursor_col();
+ check_cursor_col(curwin);
deleted_lines_mark(first, count);
rettv->vval.v_number = 0; // OK
diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c
index d7237d6443..e3afc1cf54 100644
--- a/src/nvim/eval/funcs.c
+++ b/src/nvim/eval/funcs.c
@@ -727,7 +727,7 @@ static void get_col(typval_T *argvars, typval_T *rettv, bool charcol)
return;
}
- check_cursor();
+ check_cursor(curwin);
winchanged = true;
}
@@ -738,7 +738,7 @@ static void get_col(typval_T *argvars, typval_T *rettv, bool charcol)
if (fp->col == MAXCOL) {
// '> can be MAXCOL, get the length of the line then
if (fp->lnum <= curbuf->b_ml.ml_line_count) {
- col = (colnr_T)strlen(ml_get(fp->lnum)) + 1;
+ col = ml_get_len(fp->lnum) + 1;
} else {
col = MAXCOL;
}
@@ -746,7 +746,7 @@ static void get_col(typval_T *argvars, typval_T *rettv, bool charcol)
col = fp->col + 1;
// col(".") when the cursor is on the NUL at the end of the line
// because of "coladd" can be seen as an extra column.
- if (virtual_active() && fp == &curwin->w_cursor) {
+ if (virtual_active(curwin) && fp == &curwin->w_cursor) {
char *p = get_cursor_pos_ptr();
if (curwin->w_cursor.coladd >=
(colnr_T)win_chartabsize(curwin, p,
@@ -1185,13 +1185,14 @@ static void set_cursorpos(typval_T *argvars, typval_T *rettv, bool charcol)
if (lnum > 0) {
curwin->w_cursor.lnum = lnum;
}
- if (col > 0) {
- curwin->w_cursor.col = col - 1;
+ if (col != MAXCOL && --col < 0) {
+ col = 0;
}
+ curwin->w_cursor.col = col;
curwin->w_cursor.coladd = coladd;
// Make sure the cursor is in a valid position.
- check_cursor();
+ check_cursor(curwin);
// Correct cursor for multi-byte character.
mb_adjust_cursor();
@@ -1361,7 +1362,7 @@ static void f_dictwatcherdel(typval_T *argvars, typval_T *rettv, EvalFuncData fp
/// "did_filetype()" function
static void f_did_filetype(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- rettv->vval.v_number = did_filetype;
+ rettv->vval.v_number = curbuf->b_did_filetype;
}
/// "diff_filler()" function
@@ -2019,6 +2020,9 @@ static void extend_dict(typval_T *argvars, const char *arg_errmsg, bool is_new,
action = tv_get_string_chk(&argvars[2]);
if (action == NULL) {
+ if (is_new) {
+ tv_dict_unref(d1);
+ }
return; // Type error; error message already given.
}
size_t i;
@@ -2028,6 +2032,9 @@ static void extend_dict(typval_T *argvars, const char *arg_errmsg, bool is_new,
}
}
if (i == 3) {
+ if (is_new) {
+ tv_dict_unref(d1);
+ }
semsg(_(e_invarg2), action);
return;
}
@@ -2816,24 +2823,24 @@ static char *block_def2str(struct block_def *bd)
return ret;
}
-/// "getregion()" function
-static void f_getregion(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+static int getregionpos(typval_T *argvars, typval_T *rettv, pos_T *p1, pos_T *p2,
+ bool *const inclusive, MotionType *region_type, oparg_T *oa)
+ FUNC_ATTR_NONNULL_ALL
{
tv_list_alloc_ret(rettv, kListLenMayKnow);
if (tv_check_for_list_arg(argvars, 0) == FAIL
|| tv_check_for_list_arg(argvars, 1) == FAIL
|| tv_check_for_opt_dict_arg(argvars, 2) == FAIL) {
- return;
+ return FAIL;
}
int fnum1 = -1;
int fnum2 = -1;
- pos_T p1, p2;
- if (list2fpos(&argvars[0], &p1, &fnum1, NULL, false) != OK
- || list2fpos(&argvars[1], &p2, &fnum2, NULL, false) != OK
+ if (list2fpos(&argvars[0], p1, &fnum1, NULL, false) != OK
+ || list2fpos(&argvars[1], p2, &fnum2, NULL, false) != OK
|| fnum1 != fnum2) {
- return;
+ return FAIL;
}
bool is_select_exclusive;
@@ -2851,87 +2858,109 @@ static void f_getregion(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
type = default_type;
}
- MotionType region_type = kMTUnknown;
if (type[0] == 'v' && type[1] == NUL) {
- region_type = kMTCharWise;
+ *region_type = kMTCharWise;
} else if (type[0] == 'V' && type[1] == NUL) {
- region_type = kMTLineWise;
+ *region_type = kMTLineWise;
} else if (type[0] == Ctrl_V && type[1] == NUL) {
- region_type = kMTBlockWise;
+ *region_type = kMTBlockWise;
} else {
- return;
+ semsg(_(e_invargNval), "type", type);
+ return FAIL;
}
- buf_T *const save_curbuf = curbuf;
+ buf_T *findbuf = fnum1 != 0 ? buflist_findnr(fnum1) : curbuf;
+ if (findbuf == NULL || findbuf->b_ml.ml_mfp == NULL) {
+ emsg(_(e_buffer_is_not_loaded));
+ return FAIL;
+ }
- if (fnum1 != 0) {
- buf_T *findbuf = buflist_findnr(fnum1);
- // buffer not loaded
- if (findbuf == NULL || findbuf->b_ml.ml_mfp == NULL) {
- return;
- }
- curbuf = findbuf;
+ if (p1->lnum < 1 || p1->lnum > findbuf->b_ml.ml_line_count) {
+ semsg(_(e_invalid_line_number_nr), p1->lnum);
+ return FAIL;
+ }
+ if (p1->col == MAXCOL) {
+ p1->col = ml_get_buf_len(findbuf, p1->lnum) + 1;
+ } else if (p1->col < 1 || p1->col > ml_get_buf_len(findbuf, p1->lnum) + 1) {
+ semsg(_(e_invalid_column_number_nr), p1->col);
+ return FAIL;
}
- const TriState save_virtual = virtual_op;
- virtual_op = virtual_active();
+ if (p2->lnum < 1 || p2->lnum > findbuf->b_ml.ml_line_count) {
+ semsg(_(e_invalid_line_number_nr), p2->lnum);
+ return FAIL;
+ }
+ if (p2->col == MAXCOL) {
+ p2->col = ml_get_buf_len(findbuf, p2->lnum) + 1;
+ } else if (p2->col < 1 || p2->col > ml_get_buf_len(findbuf, p2->lnum) + 1) {
+ semsg(_(e_invalid_column_number_nr), p2->col);
+ return FAIL;
+ }
+
+ curbuf = findbuf;
+ curwin->w_buffer = curbuf;
+ virtual_op = virtual_active(curwin);
- // NOTE: Adjust is needed.
- p1.col--;
- p2.col--;
+ // NOTE: Adjustment is needed.
+ p1->col--;
+ p2->col--;
- if (!lt(p1, p2)) {
+ if (!lt(*p1, *p2)) {
// swap position
- pos_T p = p1;
- p1 = p2;
- p2 = p;
+ pos_T p = *p1;
+ *p1 = *p2;
+ *p2 = p;
}
- oparg_T oa;
- bool inclusive = true;
-
- if (region_type == kMTCharWise) {
- // handle 'selection' == "exclusive"
- if (is_select_exclusive && !equalpos(p1, p2)) {
- if (p2.coladd > 0) {
- p2.coladd--;
- } else if (p2.col > 0) {
- p2.col--;
- mark_mb_adjustpos(curbuf, &p2);
- } else if (p2.lnum > 1) {
- p2.lnum--;
- p2.col = (colnr_T)strlen(ml_get(p2.lnum));
- if (p2.col > 0) {
- p2.col--;
- mark_mb_adjustpos(curbuf, &p2);
- }
- }
+ if (*region_type == kMTCharWise) {
+ // Handle 'selection' == "exclusive".
+ if (is_select_exclusive && !equalpos(*p1, *p2)) {
+ // When backing up to previous line, inclusive becomes false.
+ *inclusive = !unadjust_for_sel_inner(p2);
}
- // if fp2 is on NUL (empty line) inclusive becomes false
- if (*ml_get_pos(&p2) == NUL && !virtual_op) {
- inclusive = false;
+ // If p2 is on NUL (end of line), inclusive becomes false.
+ if (*inclusive && !virtual_op && *ml_get_pos(p2) == NUL) {
+ *inclusive = false;
}
- } else if (region_type == kMTBlockWise) {
+ } else if (*region_type == kMTBlockWise) {
colnr_T sc1, ec1, sc2, ec2;
- getvvcol(curwin, &p1, &sc1, NULL, &ec1);
- getvvcol(curwin, &p2, &sc2, NULL, &ec2);
- oa.motion_type = kMTBlockWise;
- oa.inclusive = true;
- oa.op_type = OP_NOP;
- oa.start = p1;
- oa.end = p2;
- oa.start_vcol = MIN(sc1, sc2);
+ getvvcol(curwin, p1, &sc1, NULL, &ec1);
+ getvvcol(curwin, p2, &sc2, NULL, &ec2);
+ oa->motion_type = kMTBlockWise;
+ oa->inclusive = true;
+ oa->op_type = OP_NOP;
+ oa->start = *p1;
+ oa->end = *p2;
+ oa->start_vcol = MIN(sc1, sc2);
if (is_select_exclusive && ec1 < sc2 && 0 < sc2 && ec2 > ec1) {
- oa.end_vcol = sc2 - 1;
+ oa->end_vcol = sc2 - 1;
} else {
- oa.end_vcol = MAX(ec1, ec2);
+ oa->end_vcol = MAX(ec1, ec2);
}
}
// Include the trailing byte of a multi-byte char.
- int l = utfc_ptr2len(ml_get_pos(&p2));
+ int l = utfc_ptr2len(ml_get_pos(p2));
if (l > 1) {
- p2.col += l - 1;
+ p2->col += l - 1;
+ }
+
+ return OK;
+}
+
+/// "getregion()" function
+static void f_getregion(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ buf_T *const save_curbuf = curbuf;
+ const TriState save_virtual = virtual_op;
+
+ pos_T p1, p2;
+ bool inclusive = true;
+ MotionType region_type = kMTUnknown;
+ oparg_T oa;
+
+ if (getregionpos(argvars, rettv, &p1, &p2, &inclusive, &region_type, &oa) == FAIL) {
+ return;
}
for (linenr_T lnum = p1.lnum; lnum <= p2.lnum; lnum++) {
@@ -2955,10 +2984,127 @@ static void f_getregion(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
tv_list_append_allocated_string(rettv->vval.v_list, akt);
}
- if (curbuf != save_curbuf) {
- curbuf = save_curbuf;
+ // getregionpos() may change curbuf and virtual_op
+ curbuf = save_curbuf;
+ curwin->w_buffer = curbuf;
+ virtual_op = save_virtual;
+}
+
+static void add_regionpos_range(typval_T *rettv, pos_T p1, pos_T p2)
+{
+ list_T *l1 = tv_list_alloc(2);
+ tv_list_append_list(rettv->vval.v_list, l1);
+
+ list_T *l2 = tv_list_alloc(4);
+ tv_list_append_list(l1, l2);
+
+ list_T *l3 = tv_list_alloc(4);
+ tv_list_append_list(l1, l3);
+
+ tv_list_append_number(l2, curbuf->b_fnum);
+ tv_list_append_number(l2, p1.lnum);
+ tv_list_append_number(l2, p1.col);
+ tv_list_append_number(l2, p1.coladd);
+
+ tv_list_append_number(l3, curbuf->b_fnum);
+ tv_list_append_number(l3, p2.lnum);
+ tv_list_append_number(l3, p2.col);
+ tv_list_append_number(l3, p2.coladd);
+}
+
+/// "getregionpos()" function
+static void f_getregionpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ buf_T *const save_curbuf = curbuf;
+ const TriState save_virtual = virtual_op;
+
+ pos_T p1, p2;
+ bool inclusive = true;
+ MotionType region_type = kMTUnknown;
+ bool allow_eol = false;
+ oparg_T oa;
+
+ if (getregionpos(argvars, rettv, &p1, &p2, &inclusive, &region_type, &oa) == FAIL) {
+ return;
+ }
+
+ if (argvars[2].v_type == VAR_DICT) {
+ allow_eol = tv_dict_get_bool(argvars[2].vval.v_dict, "eol", false);
+ }
+
+ for (linenr_T lnum = p1.lnum; lnum <= p2.lnum; lnum++) {
+ pos_T ret_p1, ret_p2;
+ colnr_T line_len = ml_get_len(lnum);
+
+ if (region_type == kMTLineWise) {
+ ret_p1.col = 1;
+ ret_p1.coladd = 0;
+ ret_p2.col = MAXCOL;
+ ret_p2.coladd = 0;
+ } else {
+ struct block_def bd;
+
+ if (region_type == kMTBlockWise) {
+ block_prep(&oa, &bd, lnum, false);
+ } else {
+ charwise_block_prep(p1, p2, &bd, lnum, inclusive);
+ }
+
+ if (bd.is_oneChar) { // selection entirely inside one char
+ if (region_type == kMTBlockWise) {
+ ret_p1.col = bd.textcol;
+ ret_p1.coladd = bd.start_char_vcols - (bd.start_vcol - oa.start_vcol);
+ } else {
+ ret_p1.col = p1.col + 1;
+ ret_p1.coladd = p1.coladd;
+ }
+ } else if (region_type == kMTBlockWise && oa.start_vcol > bd.start_vcol) {
+ // blockwise selection entirely beyond end of line
+ ret_p1.col = MAXCOL;
+ ret_p1.coladd = oa.start_vcol - bd.start_vcol;
+ bd.is_oneChar = true;
+ } else if (bd.startspaces > 0) {
+ ret_p1.col = bd.textcol;
+ ret_p1.coladd = bd.start_char_vcols - bd.startspaces;
+ } else {
+ ret_p1.col = bd.textcol + 1;
+ ret_p1.coladd = 0;
+ }
+
+ if (bd.is_oneChar) { // selection entirely inside one char
+ ret_p2.col = ret_p1.col;
+ ret_p2.coladd = ret_p1.coladd + bd.startspaces + bd.endspaces;
+ } else if (bd.endspaces > 0) {
+ ret_p2.col = bd.textcol + bd.textlen + 1;
+ ret_p2.coladd = bd.endspaces;
+ } else {
+ ret_p2.col = bd.textcol + bd.textlen;
+ ret_p2.coladd = 0;
+ }
+ }
+
+ if (!allow_eol && ret_p1.col > line_len) {
+ ret_p1.col = 0;
+ ret_p1.coladd = 0;
+ } else if (ret_p1.col > line_len + 1) {
+ ret_p1.col = line_len + 1;
+ }
+
+ if (!allow_eol && ret_p2.col > line_len) {
+ ret_p2.col = ret_p1.col == 0 ? 0 : line_len;
+ ret_p2.coladd = 0;
+ } else if (ret_p2.col > line_len + 1) {
+ ret_p2.col = line_len + 1;
+ }
+
+ ret_p1.lnum = lnum;
+ ret_p2.lnum = lnum;
+ add_regionpos_range(rettv, ret_p1, ret_p2);
}
+ // getregionpos() may change curbuf and virtual_op
+ curbuf = save_curbuf;
+ curwin->w_buffer = curbuf;
virtual_op = save_virtual;
}
@@ -3416,19 +3562,19 @@ static void f_has(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
} else if (STRICMP(name, "syntax_items") == 0) {
n = syntax_present(curwin);
} else if (STRICMP(name, "clipboard_working") == 0) {
- n = eval_has_provider("clipboard");
+ n = eval_has_provider("clipboard", true);
} else if (STRICMP(name, "pythonx") == 0) {
- n = eval_has_provider("python3");
+ n = eval_has_provider("python3", true);
} else if (STRICMP(name, "wsl") == 0) {
n = has_wsl();
#ifdef UNIX
} else if (STRICMP(name, "unnamedplus") == 0) {
- n = eval_has_provider("clipboard");
+ n = eval_has_provider("clipboard", true);
#endif
}
}
- if (!n && eval_has_provider(name)) {
+ if (!n && eval_has_provider(name, true)) {
n = true;
}
@@ -3974,7 +4120,7 @@ static void f_islocked(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
FNE_CHECK_START);
if (end != NULL && lv.ll_name != NULL) {
if (*end != NUL) {
- semsg(_(e_trailing_arg), end);
+ semsg(_(lv.ll_name_len == 0 ? e_invarg2 : e_trailing_arg), end);
} else {
if (lv.ll_tv == NULL) {
dictitem_T *di = find_var(lv.ll_name, lv.ll_name_len, NULL, true);
@@ -4630,7 +4776,7 @@ static void f_line(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
if (wp != NULL && tp != NULL) {
switchwin_T switchwin;
if (switch_win_noblock(&switchwin, wp, tp, true) == OK) {
- check_cursor();
+ check_cursor(curwin);
fp = var2fpos(&argvars[0], true, &fnum, false);
}
restore_win_noblock(&switchwin, true);
@@ -6454,7 +6600,7 @@ static void f_resolve(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
char *v = os_resolve_shortcut(fname);
if (v == NULL) {
if (os_is_reparse_point_include(fname)) {
- v = os_realpath(fname, v);
+ v = os_realpath(fname, NULL, MAXPATHL + 1);
}
}
rettv->vval.v_string = (v == NULL ? xstrdup(fname) : v);
@@ -6606,7 +6752,7 @@ static void f_resolve(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
xfree(buf);
}
# else
- char *v = os_realpath(fname, NULL);
+ char *v = os_realpath(fname, NULL, MAXPATHL + 1);
rettv->vval.v_string = v == NULL ? xstrdup(fname) : v;
# endif
#endif
@@ -6957,12 +7103,13 @@ static int search_cmn(typval_T *argvars, pos_T *match_pos, int *flagsp)
.sa_tm = &tm,
};
+ const size_t patlen = strlen(pat);
int subpatnum;
// Repeat until {skip} returns false.
while (true) {
- subpatnum
- = searchit(curwin, curbuf, &pos, NULL, dir, (char *)pat, 1, options, RE_SEARCH, &sia);
+ subpatnum = searchit(curwin, curbuf, &pos, NULL, dir, (char *)pat, patlen, 1,
+ options, RE_SEARCH, &sia);
// finding the first match again means there is no match where {skip}
// evaluates to zero.
if (firstpos.lnum != 0 && equalpos(pos, firstpos)) {
@@ -7016,7 +7163,7 @@ static int search_cmn(typval_T *argvars, pos_T *match_pos, int *flagsp)
}
// "/$" will put the cursor after the end of the line, may need to
// correct that here
- check_cursor();
+ check_cursor(curwin);
}
// If 'n' flag is used: restore cursor position.
@@ -7517,16 +7664,20 @@ int do_searchpair(const char *spat, const char *mpat, const char *epat, int dir,
// Make two search patterns: start/end (pat2, for in nested pairs) and
// start/middle/end (pat3, for the top pair).
- const size_t pat2_len = strlen(spat) + strlen(epat) + 17;
- char *pat2 = xmalloc(pat2_len);
- const size_t pat3_len = strlen(spat) + strlen(mpat) + strlen(epat) + 25;
- char *pat3 = xmalloc(pat3_len);
- snprintf(pat2, pat2_len, "\\m\\(%s\\m\\)\\|\\(%s\\m\\)", spat, epat);
+ const size_t spatlen = strlen(spat);
+ const size_t epatlen = strlen(epat);
+ const size_t pat2size = spatlen + epatlen + 17;
+ char *pat2 = xmalloc(pat2size);
+ const size_t pat3size = spatlen + strlen(mpat) + epatlen + 25;
+ char *pat3 = xmalloc(pat3size);
+ int pat2len = snprintf(pat2, pat2size, "\\m\\(%s\\m\\)\\|\\(%s\\m\\)", spat, epat);
+ int pat3len;
if (*mpat == NUL) {
STRCPY(pat3, pat2);
+ pat3len = pat2len;
} else {
- snprintf(pat3, pat3_len,
- "\\m\\(%s\\m\\)\\|\\(%s\\m\\)\\|\\(%s\\m\\)", spat, epat, mpat);
+ pat3len = snprintf(pat3, pat3size,
+ "\\m\\(%s\\m\\)\\|\\(%s\\m\\)\\|\\(%s\\m\\)", spat, epat, mpat);
}
if (flags & SP_START) {
options |= SEARCH_START;
@@ -7543,13 +7694,15 @@ int do_searchpair(const char *spat, const char *mpat, const char *epat, int dir,
pos_T foundpos;
clearpos(&foundpos);
char *pat = pat3;
+ assert(pat3len >= 0);
+ size_t patlen = (size_t)pat3len;
while (true) {
searchit_arg_T sia = {
.sa_stop_lnum = lnum_stop,
.sa_tm = &tm,
};
- int n = searchit(curwin, curbuf, &pos, NULL, dir, pat, 1,
+ int n = searchit(curwin, curbuf, &pos, NULL, dir, pat, patlen, 1,
options, RE_SEARCH, &sia);
if (n == FAIL || (firstpos.lnum != 0 && equalpos(pos, firstpos))) {
// didn't find it or found the first match again: FAIL
@@ -7778,7 +7931,7 @@ static void set_position(typval_T *argvars, typval_T *rettv, bool charpos)
curwin->w_curswant = curswant - 1;
curwin->w_set_curswant = false;
}
- check_cursor();
+ check_cursor(curwin);
rettv->vval.v_number = 0;
} else if (name[0] == '\'' && name[1] != NUL && name[2] == NUL) {
// set mark
@@ -8283,7 +8436,7 @@ static void f_spellbadword(typval_T *argvars, typval_T *rettv, EvalFuncData fptr
size_t len = 0;
if (argvars[0].v_type == VAR_UNKNOWN) {
// Find the start and length of the badly spelled word.
- len = spell_move_to(curwin, FORWARD, true, true, &attr);
+ len = spell_move_to(curwin, FORWARD, SMT_ALL, true, &attr);
if (len != 0) {
word = get_cursor_pos_ptr();
curwin->w_set_curswant = true;
@@ -8675,7 +8828,7 @@ static void f_synID(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
int id = 0;
if (!transerr && lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count
- && col >= 0 && (size_t)col < strlen(ml_get(lnum))) {
+ && col >= 0 && col < ml_get_len(lnum)) {
id = syn_get_id(curwin, lnum, col, trans, NULL, false);
}
@@ -8798,8 +8951,8 @@ static void f_synconcealed(typval_T *argvars, typval_T *rettv, EvalFuncData fptr
CLEAR_FIELD(str);
- if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count && col >= 0
- && (size_t)col <= strlen(ml_get(lnum)) && curwin->w_p_cole > 0) {
+ if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count
+ && col >= 0 && col <= ml_get_len(lnum) && curwin->w_p_cole > 0) {
syn_get_id(curwin, lnum, col, false, NULL, false);
syntax_flags = get_syntax_info(&matchid);
@@ -8832,10 +8985,8 @@ static void f_synstack(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
const linenr_T lnum = tv_get_lnum(argvars);
const colnr_T col = (colnr_T)tv_get_number(&argvars[1]) - 1;
- if (lnum >= 1
- && lnum <= curbuf->b_ml.ml_line_count
- && col >= 0
- && (size_t)col <= strlen(ml_get(lnum))) {
+ if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count
+ && col >= 0 && col <= ml_get_len(lnum)) {
tv_list_alloc_ret(rettv, kListLenMayKnow);
syn_get_id(curwin, lnum, col, false, NULL, true);
@@ -9193,7 +9344,7 @@ static void f_virtcol(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
goto theend;
}
- check_cursor();
+ check_cursor(curwin);
winchanged = true;
}
@@ -9205,9 +9356,9 @@ static void f_virtcol(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
if (fp->col < 0) {
fp->col = 0;
} else {
- const size_t len = strlen(ml_get(fp->lnum));
- if (fp->col > (colnr_T)len) {
- fp->col = (colnr_T)len;
+ const colnr_T len = ml_get_len(fp->lnum);
+ if (fp->col > len) {
+ fp->col = len;
}
}
getvvcol(curwin, fp, &vcol_start, NULL, &vcol_end);
diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c
index 9328f53dbd..eb8c89c36e 100644
--- a/src/nvim/eval/typval.c
+++ b/src/nvim/eval/typval.c
@@ -859,7 +859,7 @@ int tv_list_slice_or_index(list_T *list, bool range, varnumber_T n1_arg, varnumb
// A list index out of range is an error.
if (!range) {
if (verbose) {
- semsg(_(e_list_index_out_of_range_nr), (int64_t)n1);
+ semsg(_(e_list_index_out_of_range_nr), (int64_t)n1_arg);
}
return FAIL;
}
diff --git a/src/nvim/eval/typval_defs.h b/src/nvim/eval/typval_defs.h
index 0d6ee28adc..e88e6a222a 100644
--- a/src/nvim/eval/typval_defs.h
+++ b/src/nvim/eval/typval_defs.h
@@ -364,6 +364,7 @@ struct ufunc {
struct partial_S {
int pt_refcount; ///< Reference count.
+ int pt_copyID;
char *pt_name; ///< Function name; when NULL use pt_func->name.
ufunc_T *pt_func; ///< Function pointer; when NULL lookup function with pt_name.
bool pt_auto; ///< When true the partial was created by using dict.member
diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c
index d16814ed1e..0ec07399b4 100644
--- a/src/nvim/eval/userfunc.c
+++ b/src/nvim/eval/userfunc.c
@@ -345,7 +345,7 @@ int get_lambda_tv(char **arg, typval_T *rettv, evalarg_T *evalarg)
char *p = xmalloc(len);
((char **)(newlines.ga_data))[newlines.ga_len++] = p;
STRCPY(p, "return ");
- xstrlcpy(p + 7, start, (size_t)(end - start) + 1);
+ xmemcpyz(p + 7, start, (size_t)(end - start));
if (strstr(p + 7, "a:") == NULL) {
// No a: variables are used for sure.
flags |= FC_NOARGS;
@@ -2300,17 +2300,28 @@ void ex_function(exarg_T *eap)
arg = fudi.fd_newkey;
}
if (arg != NULL && (fudi.fd_di == NULL || !tv_is_func(fudi.fd_di->di_tv))) {
- int j = ((uint8_t)(*arg) == K_SPECIAL) ? 3 : 0;
- while (arg[j] != NUL && (j == 0 ? eval_isnamec1(arg[j]) : eval_isnamec(arg[j]))) {
- j++;
+ char *name_base = arg;
+ if ((uint8_t)(*arg) == K_SPECIAL) {
+ name_base = vim_strchr(arg, '_');
+ if (name_base == NULL) {
+ name_base = arg + 3;
+ } else {
+ name_base++;
+ }
}
- if (arg[j] != NUL) {
+ int i;
+ for (i = 0; name_base[i] != NUL && (i == 0
+ ? eval_isnamec1(name_base[i])
+ : eval_isnamec(name_base[i])); i++) {}
+ if (name_base[i] != NUL) {
emsg_funcname(e_invarg2, arg);
+ goto ret_free;
}
}
// Disallow using the g: dict.
if (fudi.fd_dict != NULL && fudi.fd_dict->dv_scope == VAR_DEF_SCOPE) {
emsg(_("E862: Cannot use g: here"));
+ goto ret_free;
}
}
@@ -2360,7 +2371,7 @@ void ex_function(exarg_T *eap)
// Read the body of the function, until ":endfunction" is found.
if (KeyTyped) {
// Check if the function already exists, don't let the user type the
- // whole function before telling him it doesn't work! For a script we
+ // whole function before telling them it doesn't work! For a script we
// need to skip the body to be able to find what follows.
if (!eap->skip && !eap->forceit) {
if (fudi.fd_dict != NULL && fudi.fd_newkey == NULL) {
@@ -2404,10 +2415,10 @@ void ex_function(exarg_T *eap)
}
} else {
xfree(line_to_free);
- if (eap->getline == NULL) {
+ if (eap->ea_getline == NULL) {
theline = getcmdline(':', 0, indent, do_concat);
} else {
- theline = eap->getline(':', eap->cookie, indent, do_concat);
+ theline = eap->ea_getline(':', eap->cookie, indent, do_concat);
}
line_to_free = theline;
}
@@ -2428,7 +2439,7 @@ void ex_function(exarg_T *eap)
}
// Detect line continuation: SOURCING_LNUM increased more than one.
- linenr_T sourcing_lnum_off = get_sourced_lnum(eap->getline, eap->cookie);
+ linenr_T sourcing_lnum_off = get_sourced_lnum(eap->ea_getline, eap->cookie);
if (SOURCING_LNUM < sourcing_lnum_off) {
sourcing_lnum_off -= SOURCING_LNUM;
} else {
@@ -2571,11 +2582,13 @@ void ex_function(exarg_T *eap)
// and ":let [a, b] =<< [trim] EOF"
arg = p;
if (checkforcmd(&arg, "let", 2)) {
- while (vim_strchr("$@&", *arg) != NULL) {
- arg++;
+ int var_count = 0;
+ int semicolon = 0;
+ arg = (char *)skip_var_list(arg, &var_count, &semicolon, true);
+ if (arg != NULL) {
+ arg = skipwhite(arg);
}
- arg = skipwhite(find_name_end(arg, NULL, NULL, FNE_INCL_BR));
- if (arg[0] == '=' && arg[1] == '<' && arg[2] == '<') {
+ if (arg != NULL && strncmp(arg, "=<<", 3) == 0) {
p = skipwhite(arg + 3);
while (true) {
if (strncmp(p, "trim", 4) == 0) {
diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c
index 91ac60d8ea..2eca209ea3 100644
--- a/src/nvim/eval/vars.c
+++ b/src/nvim/eval/vars.c
@@ -57,11 +57,13 @@ typedef int (*ex_unletlock_callback)(lval_T *, char *, exarg_T *, int);
#define DICT_MAXNEST 100 // maximum nesting of lists and dicts
static const char *e_letunexp = N_("E18: Unexpected characters in :let");
+static const char e_double_semicolon_in_list_of_variables[]
+ = N_("E452: Double ; in list of variables");
static const char *e_lock_unlock = N_("E940: Cannot lock or unlock variable %s");
static const char e_setting_v_str_to_value_with_wrong_type[]
= N_("E963: Setting v:%s to value with wrong type");
-static const char e_cannot_use_heredoc_here[]
- = N_("E991: Cannot use =<< here");
+static const char e_missing_end_marker_str[] = N_("E990: Missing end marker '%s'");
+static const char e_cannot_use_heredoc_here[] = N_("E991: Cannot use =<< here");
/// Evaluate one Vim expression {expr} in string "p" and append the
/// resulting string to "gap". "p" points to the opening "{".
@@ -86,7 +88,7 @@ char *eval_one_expr_in_str(char *p, garray_T *gap, bool evaluate)
}
if (evaluate) {
*block_end = NUL;
- char *expr_val = eval_to_string(block_start, true);
+ char *expr_val = eval_to_string(block_start, false);
*block_end = '}';
if (expr_val == NULL) {
return NULL;
@@ -177,8 +179,15 @@ list_T *heredoc_get(exarg_T *eap, char *cmd, bool script_get)
int text_indent_len = 0;
char *text_indent = NULL;
char dot[] = ".";
-
- if (eap->getline == NULL) {
+ bool heredoc_in_string = false;
+ char *line_arg = NULL;
+ char *nl_ptr = vim_strchr(cmd, '\n');
+
+ if (nl_ptr != NULL) {
+ heredoc_in_string = true;
+ line_arg = nl_ptr + 1;
+ *nl_ptr = NUL;
+ } else if (eap->ea_getline == NULL) {
emsg(_(e_cannot_use_heredoc_here));
return NULL;
}
@@ -214,11 +223,12 @@ list_T *heredoc_get(exarg_T *eap, char *cmd, bool script_get)
break;
}
+ const char comment_char = '"';
// The marker is the next word.
- if (*cmd != NUL && *cmd != '"') {
+ if (*cmd != NUL && *cmd != comment_char) {
marker = skipwhite(cmd);
char *p = skiptowhite(marker);
- if (*skipwhite(p) != NUL && *skipwhite(p) != '"') {
+ if (*skipwhite(p) != NUL && *skipwhite(p) != comment_char) {
semsg(_(e_trailing_arg), p);
return NULL;
}
@@ -244,13 +254,34 @@ list_T *heredoc_get(exarg_T *eap, char *cmd, bool script_get)
int mi = 0;
int ti = 0;
- xfree(theline);
- theline = eap->getline(NUL, eap->cookie, 0, false);
- if (theline == NULL) {
- if (!script_get) {
- semsg(_("E990: Missing end marker '%s'"), marker);
+ if (heredoc_in_string) {
+ // heredoc in a string separated by newlines. Get the next line
+ // from the string.
+
+ if (*line_arg == NUL) {
+ if (!script_get) {
+ semsg(_(e_missing_end_marker_str), marker);
+ }
+ break;
+ }
+
+ theline = line_arg;
+ char *next_line = vim_strchr(theline, '\n');
+ if (next_line == NULL) {
+ line_arg += strlen(line_arg);
+ } else {
+ *next_line = NUL;
+ line_arg = next_line + 1;
+ }
+ } else {
+ xfree(theline);
+ theline = eap->ea_getline(NUL, eap->cookie, 0, false);
+ if (theline == NULL) {
+ if (!script_get) {
+ semsg(_(e_missing_end_marker_str), marker);
+ }
+ break;
}
- break;
}
// with "trim": skip the indent matching the :let line to find the
@@ -296,13 +327,17 @@ list_T *heredoc_get(exarg_T *eap, char *cmd, bool script_get)
eval_failed = true;
continue;
}
- xfree(theline);
- theline = str;
+ tv_list_append_allocated_string(l, str);
+ } else {
+ tv_list_append_string(l, str, -1);
}
-
- tv_list_append_string(l, str, -1);
}
- xfree(theline);
+ if (heredoc_in_string) {
+ // Next command follows the heredoc in the string.
+ eap->nextcmd = line_arg;
+ } else {
+ xfree(theline);
+ }
xfree(text_indent);
if (eval_failed) {
@@ -341,7 +376,7 @@ void ex_let(exarg_T *eap)
const char *argend;
int first = true;
- argend = skip_var_list(arg, &var_count, &semicolon);
+ argend = skip_var_list(arg, &var_count, &semicolon, false);
if (argend == NULL) {
return;
}
@@ -515,10 +550,11 @@ int ex_let_vars(char *arg_start, typval_T *tv, int copy, int semicolon, int var_
/// Skip over assignable variable "var" or list of variables "[var, var]".
/// Used for ":let varvar = expr" and ":for varvar in expr".
/// For "[var, var]" increment "*var_count" for each variable.
-/// for "[var, var; var]" set "semicolon".
+/// for "[var, var; var]" set "semicolon" to 1.
+/// If "silent" is true do not give an "invalid argument" error message.
///
/// @return NULL for an error.
-const char *skip_var_list(const char *arg, int *var_count, int *semicolon)
+const char *skip_var_list(const char *arg, int *var_count, int *semicolon, bool silent)
{
if (*arg == '[') {
const char *s;
@@ -528,7 +564,9 @@ const char *skip_var_list(const char *arg, int *var_count, int *semicolon)
p = skipwhite(p + 1); // skip whites after '[', ';' or ','
s = skip_var_one(p);
if (s == p) {
- semsg(_(e_invarg2), p);
+ if (!silent) {
+ semsg(_(e_invarg2), p);
+ }
return NULL;
}
(*var_count)++;
@@ -538,12 +576,16 @@ const char *skip_var_list(const char *arg, int *var_count, int *semicolon)
break;
} else if (*p == ';') {
if (*semicolon == 1) {
- emsg(_("E452: Double ; in list of variables"));
+ if (!silent) {
+ emsg(_(e_double_semicolon_in_list_of_variables));
+ }
return NULL;
}
*semicolon = 1;
} else if (*p != ',') {
- semsg(_(e_invarg2), p);
+ if (!silent) {
+ semsg(_(e_invarg2), p);
+ }
return NULL;
}
}
diff --git a/src/nvim/eval/window.c b/src/nvim/eval/window.c
index b8aa0c9641..68de40f983 100644
--- a/src/nvim/eval/window.c
+++ b/src/nvim/eval/window.c
@@ -14,6 +14,7 @@
#include "nvim/eval/typval.h"
#include "nvim/eval/typval_defs.h"
#include "nvim/eval/window.h"
+#include "nvim/ex_getln.h"
#include "nvim/garray.h"
#include "nvim/garray_defs.h"
#include "nvim/gettext_defs.h"
@@ -515,7 +516,7 @@ bool win_execute_before(win_execute_T *args, win_T *wp, tabpage_T *tp)
}
if (switch_win_noblock(&args->switchwin, wp, tp, true) == OK) {
- check_cursor();
+ check_cursor(curwin);
return true;
}
return false;
@@ -539,7 +540,7 @@ void win_execute_after(win_execute_T *args)
// In case the command moved the cursor or changed the Visual area,
// check it is valid.
- check_cursor();
+ check_cursor(curwin);
if (VIsual_active) {
check_pos(curbuf, &VIsual);
}
@@ -583,9 +584,13 @@ void f_win_getid(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
void f_win_gotoid(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
int id = (int)tv_get_number(&argvars[0]);
+ if (curwin->handle == id) {
+ // Nothing to do.
+ rettv->vval.v_number = 1;
+ return;
+ }
- if (cmdwin_type != 0) {
- emsg(_(e_cmdwin));
+ if (text_or_buf_locked()) {
return;
}
FOR_ALL_TAB_WINDOWS(tp, wp) {
@@ -659,55 +664,19 @@ void f_win_screenpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
tv_list_append_number(rettv->vval.v_list, wp == NULL ? 0 : wp->w_wincol + 1);
}
-/// Move the window wp into a new split of targetwin in a given direction
-static void win_move_into_split(win_T *wp, win_T *targetwin, int size, int flags)
-{
- int height = wp->w_height;
- win_T *oldwin = curwin;
-
- if (wp == targetwin || is_aucmd_win(wp)) {
- return;
- }
-
- // Jump to the target window
- if (curwin != targetwin) {
- win_goto(targetwin);
- }
-
- // Remove the old window and frame from the tree of frames
- int dir;
- winframe_remove(wp, &dir, NULL);
- win_remove(wp, NULL);
- last_status(false); // may need to remove last status line
- win_comp_pos(); // recompute window positions
-
- // Split a window on the desired side and put the old window there
- win_split_ins(size, flags, wp, dir);
-
- // If splitting horizontally, try to preserve height
- if (size == 0 && !(flags & WSP_VERT)) {
- win_setheight_win(height, wp);
- if (p_ea) {
- win_equal(wp, true, 'v');
- }
- }
-
- if (oldwin != curwin) {
- win_goto(oldwin);
- }
-}
-
/// "win_splitmove()" function
void f_win_splitmove(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
win_T *wp = find_win_by_nr_or_id(&argvars[0]);
win_T *targetwin = find_win_by_nr_or_id(&argvars[1]);
+ win_T *oldwin = curwin;
+
+ rettv->vval.v_number = -1;
if (wp == NULL || targetwin == NULL || wp == targetwin
|| !win_valid(wp) || !win_valid(targetwin)
- || win_float_valid(wp) || win_float_valid(targetwin)) {
+ || targetwin->w_floating) {
emsg(_(e_invalwindow));
- rettv->vval.v_number = -1;
return;
}
@@ -732,7 +701,27 @@ void f_win_splitmove(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
size = (int)tv_dict_get_number(d, "size");
}
- win_move_into_split(wp, targetwin, size, flags);
+ // Check if we're allowed to continue before we bother switching windows.
+ if (is_aucmd_win(wp) || text_or_buf_locked() || check_split_disallowed(wp) == FAIL) {
+ return;
+ }
+
+ if (curwin != targetwin) {
+ win_goto(targetwin);
+ }
+
+ // Autocommands may have sent us elsewhere or closed "wp" or "oldwin".
+ if (curwin == targetwin && win_valid(wp)) {
+ if (win_splitmove(wp, size, flags) == OK) {
+ rettv->vval.v_number = 0;
+ }
+ } else {
+ emsg(_(e_auabort));
+ }
+
+ if (oldwin != curwin && win_valid(oldwin)) {
+ win_goto(oldwin);
+ }
}
/// "win_gettype(nr)" function
@@ -785,7 +774,7 @@ void f_winbufnr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
/// "wincol()" function
void f_wincol(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- validate_cursor();
+ validate_cursor(curwin);
rettv->vval.v_number = curwin->w_wcol + 1;
}
@@ -822,7 +811,7 @@ void f_winlayout(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
/// "winline()" function
void f_winline(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- validate_cursor();
+ validate_cursor(curwin);
rettv->vval.v_number = curwin->w_wrow + 1;
}
@@ -894,10 +883,10 @@ void f_winrestview(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
curwin->w_skipcol = (colnr_T)tv_get_number(&di->di_tv);
}
- check_cursor();
+ check_cursor(curwin);
win_new_height(curwin, curwin->w_height);
win_new_width(curwin, curwin->w_width);
- changed_window_setting();
+ changed_window_setting(curwin);
if (curwin->w_topline <= 0) {
curwin->w_topline = 1;
@@ -967,11 +956,8 @@ int switch_win_noblock(switchwin_T *switchwin, win_T *win, tabpage_T *tp, bool n
if (tp != NULL) {
switchwin->sw_curtab = curtab;
if (no_display) {
- curtab->tp_firstwin = firstwin;
- curtab->tp_lastwin = lastwin;
- curtab = tp;
- firstwin = curtab->tp_firstwin;
- lastwin = curtab->tp_lastwin;
+ unuse_tabpage(curtab);
+ use_tabpage(tp);
} else {
goto_tabpage_tp(tp, false, false);
}
@@ -998,11 +984,12 @@ void restore_win_noblock(switchwin_T *switchwin, bool no_display)
{
if (switchwin->sw_curtab != NULL && valid_tabpage(switchwin->sw_curtab)) {
if (no_display) {
- curtab->tp_firstwin = firstwin;
- curtab->tp_lastwin = lastwin;
- curtab = switchwin->sw_curtab;
- firstwin = curtab->tp_firstwin;
- lastwin = curtab->tp_lastwin;
+ win_T *const old_tp_curwin = curtab->tp_curwin;
+
+ unuse_tabpage(curtab);
+ // Don't change the curwin of the tabpage we temporarily visited.
+ curtab->tp_curwin = old_tp_curwin;
+ use_tabpage(switchwin->sw_curtab);
} else {
goto_tabpage_tp(switchwin->sw_curtab, false, false);
}