diff options
| author | Josh Rahm <joshuarahm@gmail.com> | 2024-05-24 19:18:11 +0000 |
|---|---|---|
| committer | Josh Rahm <joshuarahm@gmail.com> | 2024-05-24 19:18:11 +0000 |
| commit | ff7ed8f586589d620a806c3758fac4a47a8e7e15 (patch) | |
| tree | 729bbcb92231538fa61dab6c3d890b025484b7f5 /src/nvim/eval | |
| parent | 376914f419eb08fdf4c1a63a77e1f035898a0f10 (diff) | |
| parent | 28c04948a1c887a1cc0cb64de79fa32631700466 (diff) | |
| download | rneovim-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.c | 6 | ||||
| -rw-r--r-- | src/nvim/eval/funcs.c | 361 | ||||
| -rw-r--r-- | src/nvim/eval/typval.c | 2 | ||||
| -rw-r--r-- | src/nvim/eval/typval_defs.h | 1 | ||||
| -rw-r--r-- | src/nvim/eval/userfunc.c | 39 | ||||
| -rw-r--r-- | src/nvim/eval/vars.c | 90 | ||||
| -rw-r--r-- | src/nvim/eval/window.c | 105 |
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, ®ion_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, ®ion_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); } |