From 66f331fef7ad3df480bd02f1705e176d1a07c785 Mon Sep 17 00:00:00 2001 From: Sean Dewar <6256228+seandewar@users.noreply.github.com> Date: Fri, 23 Feb 2024 09:49:28 +0000 Subject: vim-patch:9.1.0116: win_split_ins may not check available room Problem: win_split_ins has no check for E36 when moving an existing window Solution: check for room and fix the issues in f_win_splitmove() (Sean Dewar) https://github.com/vim/vim/commit/0fd44a5ad81ade342cb54d8984965bdedd2272c8 Omit WSP_FORCE_ROOM, as it's not needed for Nvim's autocmd window, which is floating. Shouldn't be difficult to port later if it's used for anything else. Make win_splitmove continue working for turning floating windows into splits. Move the logic for "unfloating" a float to win_split_ins; unlike splits, no changes to the window layout are needed before calling it, as floats take no room in the window layout and cannot affect the e_noroom check. Add missing tp_curwin-fixing logic for turning external windows into splits, and add a test. NOTE: there are other issues with the way "tabpage independence" is implemented for external windows; namely, some things assume that tp_curwin is indeed a window within that tabpage, and as such, functions like tabpage_winnr and nvim_tabpage_get_win currently don't always work for external windows (with the latter aborting!) Use last_status over frame_add_statusline, as Nvim's last_status already does this for all windows in the current tabpage. Adjust restore_full_snapshot_rec to handle this. This "restore everything" approach is changed in a future commit anyway, so only ensure it's robust enough to just pass tests. Keep check_split_disallowed's current doc comment, as it's actually a bit more accurate here. (I should probably PR Vim to use this one) Allow f_win_splitmove to move a floating "wp" into a split; Nvim supports this. Continue to disallow it from moving the autocommand window into a split (funnily enough, the check wasn't reachable before, as moving a float was disallowed), but now return -1 in that case (win_splitmove also returns FAIL for this, but handling it in f_win_splitmove avoids us needing to switch windows first). Cherry-pick Test_window_split_no_room fix from v9.1.0121. Update nvim_win_set_config to handle win_split_ins failure in later commits. --- src/nvim/eval/window.c | 66 +++++++++++++++++++------------------------------- 1 file changed, 25 insertions(+), 41 deletions(-) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/window.c b/src/nvim/eval/window.c index b8aa0c9641..17b8b01963 100644 --- a/src/nvim/eval/window.c +++ b/src/nvim/eval/window.c @@ -659,55 +659,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 +696,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 can split the target before we bother switching windows. + if (is_aucmd_win(wp) || check_split_disallowed(targetwin) == 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 -- cgit From 01b27410a347b90820d4255061944c31d20b8f33 Mon Sep 17 00:00:00 2001 From: Sean Dewar <6256228+seandewar@users.noreply.github.com> Date: Sun, 25 Feb 2024 01:11:40 +0000 Subject: vim-patch:9.1.0119: can move away from cmdwin using win_splitmove() Problem: can switch windows while textlocked via f_win_gotoid and f_win_splitmove (which also allows switching in the cmdwin). Solution: Check text_or_buf_locked in f_win_splitmove() (Sean Dewar) While at it, call text_or_buf_locked() in f_win_gotoid() instead of testing for cmdwin_type() (which text_buf_locked() does and in addition will also verify that the buffer is not locked). https://github.com/vim/vim/commit/f865895c874b0936b0563ebfef7490aac8cb8a1f --- src/nvim/eval/window.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/window.c b/src/nvim/eval/window.c index 17b8b01963..d20fc3f2f2 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" @@ -584,8 +585,7 @@ void f_win_gotoid(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { int id = (int)tv_get_number(&argvars[0]); - if (cmdwin_type != 0) { - emsg(_(e_cmdwin)); + if (text_or_buf_locked()) { return; } FOR_ALL_TAB_WINDOWS(tp, wp) { @@ -697,7 +697,7 @@ void f_win_splitmove(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) } // Check if we can split the target before we bother switching windows. - if (is_aucmd_win(wp) || check_split_disallowed(targetwin) == FAIL) { + if (is_aucmd_win(wp) || text_or_buf_locked() || check_split_disallowed(targetwin) == FAIL) { return; } -- cgit From e3d4dfb6c3fcd22205f6843b96f9a043871113ce Mon Sep 17 00:00:00 2001 From: Sean Dewar <6256228+seandewar@users.noreply.github.com> Date: Sun, 25 Feb 2024 01:22:55 +0000 Subject: vim-patch:9.1.0128: win_gotoid() may abort even when not switching a window Problem: win_gotoid() checks for textlock and other things when switching to a window that is already current (after v9.1.0119) Solution: return early with success when attempting to switch to curwin (Sean Dewar) https://github.com/vim/vim/commit/2a65e739447949a7aee966ce8a3b75521b2a79ea --- src/nvim/eval/window.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/window.c b/src/nvim/eval/window.c index d20fc3f2f2..c2b9574579 100644 --- a/src/nvim/eval/window.c +++ b/src/nvim/eval/window.c @@ -584,6 +584,11 @@ 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 (text_or_buf_locked()) { return; -- cgit From c3d22d32ee4b4c1911ec15f2a77683d09b09f845 Mon Sep 17 00:00:00 2001 From: Sean Dewar <6256228+seandewar@users.noreply.github.com> Date: Sat, 9 Mar 2024 20:24:08 +0000 Subject: vim-patch:8.2.3862: crash on exit with EXITFREE and using win_execute() Problem: Crash on exit with EXITFREE and using win_execute(). Solution: Also save and restore tp_topframe. (issue vim/vim#9374) https://github.com/vim/vim/commit/dab17a0689a2f31f69f428975f84b0c3c7ba3030 Couldn't repro the crash in the test, but I only care about this patch so switch_win sets topframe properly for win_split_ins in nvim_open_win and nvim_win_set_config. Add a test using nvim_win_call and :wincmd, as I couldn't repro the issue via nvim_open_win or nvim_win_set_config (though it's clear they're affected by this patch). That said, at that point, could just use {un}use_tabpage inside switch_win instead, which also updates tp_curwin (though maybe continue to not set it in restore_win). That would also fix possible inconsistent behaviour such as: :call win_execute(w, "let curwin_nr1 = tabpagewinnr(1)") :let curwin_nr2 = tabpagewinnr(1) Where it's possible for curwin_nr1 != curwin_nr2 if these commands are run from the 1st tabpage, but window "w" is in the 2nd (as the 1st tabpage's tp_curwin may still be invalid). I'll probably PR a fix for that later in Vim. Co-authored-by: Bram Moolenaar --- src/nvim/eval/window.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/window.c b/src/nvim/eval/window.c index c2b9574579..e54f46dcc3 100644 --- a/src/nvim/eval/window.c +++ b/src/nvim/eval/window.c @@ -958,9 +958,11 @@ int switch_win_noblock(switchwin_T *switchwin, win_T *win, tabpage_T *tp, bool n if (no_display) { curtab->tp_firstwin = firstwin; curtab->tp_lastwin = lastwin; + curtab->tp_topframe = topframe; curtab = tp; firstwin = curtab->tp_firstwin; lastwin = curtab->tp_lastwin; + topframe = curtab->tp_topframe; } else { goto_tabpage_tp(tp, false, false); } @@ -989,9 +991,11 @@ void restore_win_noblock(switchwin_T *switchwin, bool no_display) if (no_display) { curtab->tp_firstwin = firstwin; curtab->tp_lastwin = lastwin; + curtab->tp_topframe = topframe; curtab = switchwin->sw_curtab; firstwin = curtab->tp_firstwin; lastwin = curtab->tp_lastwin; + topframe = curtab->tp_topframe; } else { goto_tabpage_tp(switchwin->sw_curtab, false, false); } -- cgit From 92d4dbbd8cd330606d8a4e1ce1fc550eb6a70d9b Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 11 Mar 2024 06:21:32 +0800 Subject: vim-patch:9.1.0164: Internal error when passing invalid position to getregion() (#27805) Problem: Internal error or crash when passing invalid position to getregion(). Solution: Give an error for invalid position (zeertzjq). closes: vim/vim#14172 https://github.com/vim/vim/commit/26dd09ad5e86f4e2179be0181421bfab9a6b3b75 --- src/nvim/eval/funcs.c | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 2f9472f158..f37542890b 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -2859,20 +2859,40 @@ static void f_getregion(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) } else if (type[0] == Ctrl_V && type[1] == NUL) { region_type = kMTBlockWise; } else { + semsg(_(e_invargNval), "type", type); return; } buf_T *const save_curbuf = curbuf; + buf_T *findbuf = curbuf; if (fnum1 != 0) { - buf_T *findbuf = buflist_findnr(fnum1); + findbuf = buflist_findnr(fnum1); // buffer not loaded if (findbuf == NULL || findbuf->b_ml.ml_mfp == NULL) { + emsg(_(e_buffer_is_not_loaded)); return; } - curbuf = findbuf; } + if (p1.lnum < 1 || p1.lnum > findbuf->b_ml.ml_line_count) { + semsg(_(e_invalid_line_number_nr), p1.lnum); + return; + } + if (p1.col < 1 || p1.col > ml_get_buf_len(findbuf, p1.lnum) + 1) { + semsg(_(e_invalid_column_number_nr), p1.col); + return; + } + if (p2.lnum < 1 || p2.lnum > findbuf->b_ml.ml_line_count) { + semsg(_(e_invalid_line_number_nr), p2.lnum); + return; + } + if (p2.col < 1 || p2.col > ml_get_buf_len(findbuf, p2.lnum) + 1) { + semsg(_(e_invalid_column_number_nr), p2.col); + return; + } + + curbuf = findbuf; const TriState save_virtual = virtual_op; virtual_op = virtual_active(); @@ -2900,7 +2920,7 @@ static void f_getregion(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) mark_mb_adjustpos(curbuf, &p2); } else if (p2.lnum > 1) { p2.lnum--; - p2.col = (colnr_T)strlen(ml_get(p2.lnum)); + p2.col = ml_get_len(p2.lnum); if (p2.col > 0) { p2.col--; mark_mb_adjustpos(curbuf, &p2); -- cgit From 6481da3015fd6cf136e752c9123078223c50d91c Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 12 Mar 2024 07:19:30 +0800 Subject: vim-patch:9.1.0166: Internal error with blockwise getregion() in another buffer (#27819) Problem: Internal error with blockwise getregion() in another buffer Solution: Also change curwin->w_buffer when changing curbuf (zeertzjq) closes: vim/vim#14179 https://github.com/vim/vim/commit/5406eb8722bddb6a04876956f9a53c1752994851 --- src/nvim/eval/funcs.c | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index f37542890b..ab92aa7b34 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -2863,16 +2863,10 @@ static void f_getregion(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) return; } - buf_T *const save_curbuf = curbuf; - buf_T *findbuf = curbuf; - - if (fnum1 != 0) { - findbuf = buflist_findnr(fnum1); - // buffer not loaded - if (findbuf == NULL || findbuf->b_ml.ml_mfp == NULL) { - emsg(_(e_buffer_is_not_loaded)); - return; - } + 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; } if (p1.lnum < 1 || p1.lnum > findbuf->b_ml.ml_line_count) { @@ -2892,7 +2886,9 @@ static void f_getregion(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) return; } + buf_T *const save_curbuf = curbuf; curbuf = findbuf; + curwin->w_buffer = curbuf; const TriState save_virtual = virtual_op; virtual_op = virtual_active(); @@ -2975,10 +2971,8 @@ 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; - } - + curbuf = save_curbuf; + curwin->w_buffer = curbuf; virtual_op = save_virtual; } -- cgit From b02a4d8ac39bafdbfd490bfbab35e3202e6f709c Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 12 Mar 2024 07:20:22 +0800 Subject: vim-patch:9.1.0168: too many STRLEN() calls (#27823) Problem: too many STRLEN() calls Solution: Make use of ml_get_len() calls instead (John Marriott) closes: vim/vim#14123 https://github.com/vim/vim/commit/bfcc895482c717c9f6d86890d789ec739c3016b4 Co-authored-by: John Marriott --- src/nvim/eval/funcs.c | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index ab92aa7b34..1d5835c9bf 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -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; } @@ -8688,7 +8688,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); } @@ -8811,8 +8811,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); @@ -8845,10 +8845,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); @@ -9218,9 +9216,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); -- cgit From 29d0ed577c7d283402c84df602a031a25349eb59 Mon Sep 17 00:00:00 2001 From: Sean Dewar <6256228+seandewar@users.noreply.github.com> Date: Tue, 12 Mar 2024 21:02:29 +0000 Subject: vim-patch:9.1.0169: current window number returned by tabpagewinnr may be outdated Problem: current window number returned by tabpagewinnr may be outdated when called from win_execute for the original tabpage. Solution: update the original tabpage's tp_curwin in switch_win; use {un}use_tabpage instead. Don't do it in restore_win to ensure tp_curwin of the temporarily visited tabpage is unchanged from switch_win visiting it, as before. (Sean Dewar) Maybe restore_win should only restore tp_curwin if `curtab == switchwin->sw_curtab`, in case the user changed tabpages from within win_execute, but not doing that is consistent with the old behaviour. related: vim/vim#14186 https://github.com/vim/vim/commit/e101028a5c896480c61fef7ea16855255925709b --- src/nvim/eval/window.c | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/window.c b/src/nvim/eval/window.c index e54f46dcc3..26624c8dd7 100644 --- a/src/nvim/eval/window.c +++ b/src/nvim/eval/window.c @@ -956,13 +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_topframe = topframe; - curtab = tp; - firstwin = curtab->tp_firstwin; - lastwin = curtab->tp_lastwin; - topframe = curtab->tp_topframe; + unuse_tabpage(curtab); + use_tabpage(tp); } else { goto_tabpage_tp(tp, false, false); } @@ -989,13 +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->tp_topframe = topframe; - curtab = switchwin->sw_curtab; - firstwin = curtab->tp_firstwin; - lastwin = curtab->tp_lastwin; - topframe = curtab->tp_topframe; + 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); } -- cgit From 6bbb02d9ba76551dd4856ad50a237e92c678702d Mon Sep 17 00:00:00 2001 From: Sean Dewar <6256228+seandewar@users.noreply.github.com> Date: Tue, 12 Mar 2024 21:19:18 +0000 Subject: vim-patch:9.1.0171: Small split-move related improvements Problem: small improvements can be made to split-move related functions. Solution: apply them (Sean Dewar): Some of these changes were already applied to Nvim. Here are the ones which were missing: - Improve some doc comments (frame_flatten should still work for non-current tabpages, despite the topframe check, which looks benign, though I'm unsure if it's still needed; see vim/vim#2467). - f_win_splitmove should check_split_disallowed on wp, not targetwin, as that's what win_splitmove checks (though it's probably unnecessary to check b_locked_split at all; see vim/vim#14109, which I hope to get around to finishing at some point). - Apply the winframe_restore comment changes, and remove win_comp_pos from after winframe_restore in win_splitmove, as it shouldn't be necessary (no need to remove it from nvim_win_set_config too, as it was already omitted). Move win_append after winframe_restore in win_splitmove to match Vim. closes: vim/vim#14185 https://github.com/vim/vim/commit/5cac1a9bee0798d70a7fd80363a1f697759638e8 --- src/nvim/eval/window.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/window.c b/src/nvim/eval/window.c index 26624c8dd7..3e2f6301ca 100644 --- a/src/nvim/eval/window.c +++ b/src/nvim/eval/window.c @@ -701,8 +701,8 @@ void f_win_splitmove(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) size = (int)tv_dict_get_number(d, "size"); } - // Check if we can split the target before we bother switching windows. - if (is_aucmd_win(wp) || text_or_buf_locked() || check_split_disallowed(targetwin) == FAIL) { + // 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; } -- cgit From 08fc1ebbaa49e3110b65bddeed28d2e61a96f5d9 Mon Sep 17 00:00:00 2001 From: bfredl Date: Mon, 11 Mar 2024 13:19:49 +0100 Subject: fix(api/buffer): fix handling of viewport of non-current buffer A lot of functions in move.c only worked for curwin, alternatively took a `wp` arg but still only work if that happens to be curwin. Refactor those that are needed for update_topline(wp) to work for any window. fixes #27723 fixes #27720 --- src/nvim/eval/buffer.c | 6 +++--- src/nvim/eval/funcs.c | 16 ++++++++-------- src/nvim/eval/window.c | 12 ++++++------ 3 files changed, 17 insertions(+), 17 deletions(-) (limited to 'src/nvim/eval') 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 1d5835c9bf..99da15ddd7 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; } @@ -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, @@ -1191,7 +1191,7 @@ static void set_cursorpos(typval_T *argvars, typval_T *rettv, bool charcol) 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(); @@ -2890,7 +2890,7 @@ static void f_getregion(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) curbuf = findbuf; curwin->w_buffer = curbuf; const TriState save_virtual = virtual_op; - virtual_op = virtual_active(); + virtual_op = virtual_active(curwin); // NOTE: Adjust is needed. p1.col--; @@ -4643,7 +4643,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); @@ -7029,7 +7029,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. @@ -7791,7 +7791,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 @@ -9204,7 +9204,7 @@ static void f_virtcol(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) goto theend; } - check_cursor(); + check_cursor(curwin); winchanged = true; } diff --git a/src/nvim/eval/window.c b/src/nvim/eval/window.c index 3e2f6301ca..68de40f983 100644 --- a/src/nvim/eval/window.c +++ b/src/nvim/eval/window.c @@ -516,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; @@ -540,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); } @@ -774,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; } @@ -811,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; } @@ -883,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; -- cgit From ead3a1bd7a3fbc266ae5bf724d74cd1c44286b50 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 16 Mar 2024 17:05:59 +0800 Subject: vim-patch:8.2.3782: Vim9: no error if a function shadows a script variable (#27881) Problem: Vim9: no error if a function shadows a script variable. Solution: Check the function doesn't shadow a variable. (closes vim/vim#9310) https://github.com/vim/vim/commit/052ff291d72bc9c176f9562f021d7e8e030e74c0 Omit EVAL_VAR_NO_FUNC: Vim9 script only. Co-authored-by: Bram Moolenaar --- src/nvim/eval/userfunc.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c index fe10d56d49..2ada0a0e59 100644 --- a/src/nvim/eval/userfunc.c +++ b/src/nvim/eval/userfunc.c @@ -2294,17 +2294,27 @@ 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); } } // 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; } } -- cgit From ee89ba1d7531b184d5abc6b311db258da26bae42 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 16 Mar 2024 17:26:14 +0800 Subject: vim-patch:9.1.0182: Can define function with invalid name inside 'formatexpr' (#27883) Problem: Can define function with invalid name inside 'formatexpr'. Solution: Use goto instead of checking for did_emsg later. (zeertzjq) closes: vim/vim#14209 https://github.com/vim/vim/commit/6a04bf5ee523b2d6d01d7290e356a30de219f465 --- src/nvim/eval/userfunc.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c index 2ada0a0e59..27f7f42739 100644 --- a/src/nvim/eval/userfunc.c +++ b/src/nvim/eval/userfunc.c @@ -2309,6 +2309,7 @@ void ex_function(exarg_T *eap) : eval_isnamec(name_base[i])); i++) {} if (name_base[i] != NUL) { emsg_funcname(e_invarg2, arg); + goto ret_free; } } // Disallow using the g: dict. -- cgit From e4a23b6e0b4f68c284b9cc479df842f934b5ac05 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 23 Mar 2024 08:01:43 +0800 Subject: vim-patch:8.2.2318: Vim9: string and list index work differently Problem: Vim9: string and list index work differently. Solution: Make string index work like list index. (closes vim/vim#7643) https://github.com/vim/vim/commit/e7525c552060dd04aacdbca6bb5fe6460cf4da60 Co-authored-by: Bram Moolenaar --- src/nvim/eval/typval.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/eval') 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; } -- cgit From b08667d4f00e434eac9874e4005ab8e1fd9d3e95 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 1 Apr 2024 06:12:11 +0800 Subject: vim-patch:9.1.0231: Filetype may be undetected when SwapExists sets ft in other buf (#28136) Problem: Filetype may be undetected when a SwapExists autocommand sets filetype in another buffer. Solution: Make filetype detection state buffer-specific. Also fix a similar problem for 'modified' (zeertzjq). closes: vim/vim#14344 https://github.com/vim/vim/commit/5bf6c2117fcef85fcf046c098dd3eb72a0147859 --- src/nvim/eval/funcs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 99da15ddd7..d3fe2c7e33 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -1361,7 +1361,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 -- cgit From 869d30304319a2839306ea56c37256a120a7efef Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 9 Apr 2024 10:10:47 +0800 Subject: vim-patch:8.2.0981: Vim9: cannot compile "[var, var] = list" (#28247) Problem: Vim9: cannot compile "[var, var] = list". Solution: Implement list assignment. https://github.com/vim/vim/commit/47a519a933e8bcaf703a5feaac5c01491a658ee3 Co-authored-by: Bram Moolenaar --- src/nvim/eval/vars.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c index e016c65d90..7217f84420 100644 --- a/src/nvim/eval/vars.c +++ b/src/nvim/eval/vars.c @@ -341,7 +341,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 +515,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 +529,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)++; @@ -543,7 +546,9 @@ const char *skip_var_list(const char *arg, int *var_count, int *semicolon) } *semicolon = 1; } else if (*p != ',') { - semsg(_(e_invarg2), p); + if (!silent) { + semsg(_(e_invarg2), p); + } return NULL; } } -- cgit From c695caa7eeb16fdf5c5bf0c00644daf085f8a13a Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 9 Apr 2024 11:06:02 +0800 Subject: vim-patch:8.2.4395: some code lines not covered by tests (#28248) Problem: Some code lines not covered by tests. Solution: Add a few more test cases. Fix getting more than one error for invalid assignment. https://github.com/vim/vim/commit/8b716f5f2204f938769de283d43bcb2f77d403e7 Co-authored-by: Bram Moolenaar --- src/nvim/eval/vars.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c index 7217f84420..4ecf7a6d63 100644 --- a/src/nvim/eval/vars.c +++ b/src/nvim/eval/vars.c @@ -57,6 +57,8 @@ 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"); @@ -541,7 +543,9 @@ const char *skip_var_list(const char *arg, int *var_count, int *semicolon, bool 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; -- cgit From efb6640b2937dacef9eec1b2ace11b85b3b44a4a Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 10 Apr 2024 06:07:29 +0800 Subject: vim-patch:9.1.0287: Vim9: comment may be treated as heredoc start (#28257) Problem: Vim9: comment may be treated as heredoc start. (Ernie Rael) Solution: Use skip_var_list() instead of find_name_end(). (zeertzjq) fixes: vim/vim#14444 closes: vim/vim#14446 https://github.com/vim/vim/commit/9a91d2b72c20f213bbf77f27b7edd01e0e43d5e0 --- src/nvim/eval/userfunc.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c index 27f7f42739..72559fe9e0 100644 --- a/src/nvim/eval/userfunc.c +++ b/src/nvim/eval/userfunc.c @@ -2576,10 +2576,14 @@ 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; + const char *argend = skip_var_list(arg, &var_count, &semicolon, true); + if (argend == NULL) { + // Invalid list assignment: skip to closing bracket. + argend = find_name_end(arg, NULL, NULL, FNE_INCL_BR); } - arg = skipwhite(find_name_end(arg, NULL, NULL, FNE_INCL_BR)); + arg = skipwhite(argend); if (arg[0] == '=' && arg[1] == '<' && arg[2] == '<') { p = skipwhite(arg + 3); while (true) { -- cgit From f504e799a3f62e485b09fe8ab9470a991f43e7f5 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 11 Apr 2024 07:39:10 +0800 Subject: vim-patch:9.1.0301: Vim9: heredoc start may be recognized in string (#28266) Problem: Vim9: heredoc start may be recognized in string. Solution: Don't skip to closing bracket for invalid list assignment. (zeertzjq) closes: vim/vim#14472 https://github.com/vim/vim/commit/1817ccdb107ceeaf5c48fe193da5146682c15ca6 --- src/nvim/eval/userfunc.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c index 72559fe9e0..d82d396d4d 100644 --- a/src/nvim/eval/userfunc.c +++ b/src/nvim/eval/userfunc.c @@ -2578,13 +2578,11 @@ void ex_function(exarg_T *eap) if (checkforcmd(&arg, "let", 2)) { int var_count = 0; int semicolon = 0; - const char *argend = skip_var_list(arg, &var_count, &semicolon, true); - if (argend == NULL) { - // Invalid list assignment: skip to closing bracket. - argend = find_name_end(arg, NULL, NULL, FNE_INCL_BR); + arg = (char *)skip_var_list(arg, &var_count, &semicolon, true); + if (arg != NULL) { + arg = skipwhite(arg); } - arg = skipwhite(argend); - 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) { -- cgit From 4f3d018d15d299b66a341bed4d677d7ec03ad44f Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 13 Apr 2024 06:39:30 +0800 Subject: vim-patch:9.0.2180: POSIX function name in exarg causes issues (#28308) Problem: POSIX function name in exarg struct causes issues on OpenVMS Solution: Rename getline member in exarg struct to ea_getline, remove isinf() workaround for VMS There are compilers that do not treat well POSIX functions - like getline - usage in the structs. Older VMS compilers could digest this... but the newer OpenVMS compilers ( like VSI C x86-64 X7.4-843 (GEM 50XB9) ) cannot deal with these structs. This could be limited to getline() that is defined via getdelim() and might not affect all POSIX functions in general - but avoiding POSIX function names usage in the structs is a "safe side" practice without compromising the functionality or the code readability. The previous OpenVMS X86 port used a workaround limiting the compiler capabilities using __CRTL_VER_OVERRIDE=80400000 In order to make the OpenVMS port future proof, this pull request proposes a possible solution. closes: vim/vim#13704 https://github.com/vim/vim/commit/6fdb6280821a822768df5689a5d727e37d38306c Co-authored-by: Zoltan Arpadffy --- src/nvim/eval/userfunc.c | 6 +++--- src/nvim/eval/vars.c | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c index d82d396d4d..7bb115aed1 100644 --- a/src/nvim/eval/userfunc.c +++ b/src/nvim/eval/userfunc.c @@ -2409,10 +2409,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; } @@ -2433,7 +2433,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 { diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c index 4ecf7a6d63..65e648b625 100644 --- a/src/nvim/eval/vars.c +++ b/src/nvim/eval/vars.c @@ -180,7 +180,7 @@ list_T *heredoc_get(exarg_T *eap, char *cmd, bool script_get) char *text_indent = NULL; char dot[] = "."; - if (eap->getline == NULL) { + if (eap->ea_getline == NULL) { emsg(_(e_cannot_use_heredoc_here)); return NULL; } @@ -247,7 +247,7 @@ list_T *heredoc_get(exarg_T *eap, char *cmd, bool script_get) int ti = 0; xfree(theline); - theline = eap->getline(NUL, eap->cookie, 0, false); + theline = eap->ea_getline(NUL, eap->cookie, 0, false); if (theline == NULL) { if (!script_get) { semsg(_("E990: Missing end marker '%s'"), marker); -- cgit From 617a3851426434bc22d82fe7574ba8f0455c0dcd Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 13 Apr 2024 05:55:51 +0800 Subject: vim-patch:9.1.0312: heredocs are not supported for :commands Problem: heredocs are not supported for :commands (balki) Solution: Add heredoc support (Yegappan Lakshmanan) fixes: vim/vim#14491 closes: vim/vim#14528 https://github.com/vim/vim/commit/e74cad3321ce1dcefc1fc64f617511275b6cd930 Co-authored-by: Yegappan Lakshmanan --- src/nvim/eval/vars.c | 66 ++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 49 insertions(+), 17 deletions(-) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c index 65e648b625..70ecb76deb 100644 --- a/src/nvim/eval/vars.c +++ b/src/nvim/eval/vars.c @@ -62,8 +62,8 @@ static const char e_double_semicolon_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 "{". @@ -180,7 +180,7 @@ list_T *heredoc_get(exarg_T *eap, char *cmd, bool script_get) char *text_indent = NULL; char dot[] = "."; - if (eap->ea_getline == NULL) { + if (eap->ea_getline == NULL && vim_strchr(cmd, '\n') == NULL) { emsg(_(e_cannot_use_heredoc_here)); return NULL; } @@ -216,11 +216,18 @@ list_T *heredoc_get(exarg_T *eap, char *cmd, bool script_get) break; } + const char comment_char = '"'; + bool heredoc_in_string = false; + char *line_arg = NULL; // 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) != '"') { + char *p = skiptowhite_or_nl(marker); + if (*p == NL) { + // heredoc in a string + line_arg = p + 1; + heredoc_in_string = true; + } else if (*skipwhite(p) != NUL && *skipwhite(p) != comment_char) { semsg(_(e_trailing_arg), p); return NULL; } @@ -246,13 +253,34 @@ list_T *heredoc_get(exarg_T *eap, char *cmd, bool script_get) int mi = 0; int ti = 0; - xfree(theline); - theline = eap->ea_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 @@ -298,13 +326,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) { -- cgit From e81fe387d6291e5643a97a61e6d05b48aaeab2a1 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 14 Apr 2024 05:03:49 +0800 Subject: vim-patch:9.1.0313: Crash when using heredoc with comment in command block Problem: Crash when using heredoc with comment in command block. Solution: Handle a newline more like the end of the line, fix coverity warning (zeertzjq). closes: vim/vim#14535 https://github.com/vim/vim/commit/1f5175d9af3d3f37e19f23e0e6f84caec47390f2 --- src/nvim/eval/vars.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c index 70ecb76deb..37199cd95d 100644 --- a/src/nvim/eval/vars.c +++ b/src/nvim/eval/vars.c @@ -179,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[] = "."; + bool heredoc_in_string = false; + char *line_arg = NULL; + char *nl_ptr = vim_strchr(cmd, '\n'); - if (eap->ea_getline == NULL && vim_strchr(cmd, '\n') == NULL) { + 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; } @@ -217,17 +224,11 @@ list_T *heredoc_get(exarg_T *eap, char *cmd, bool script_get) } const char comment_char = '"'; - bool heredoc_in_string = false; - char *line_arg = NULL; // The marker is the next word. if (*cmd != NUL && *cmd != comment_char) { marker = skipwhite(cmd); - char *p = skiptowhite_or_nl(marker); - if (*p == NL) { - // heredoc in a string - line_arg = p + 1; - heredoc_in_string = true; - } else if (*skipwhite(p) != NUL && *skipwhite(p) != comment_char) { + char *p = skiptowhite(marker); + if (*skipwhite(p) != NUL && *skipwhite(p) != comment_char) { semsg(_(e_trailing_arg), p); return NULL; } -- cgit From e3c083832c77eb7c24442bd10bbb718599a764d9 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 16 Apr 2024 10:18:24 +0800 Subject: vim-patch:9.1.0335: String interpolation fails for List type (#28364) Problem: String interpolation fails for List type Solution: use implicit string(list) for string interpolation and :put = (Yegappan Lakshmanan) related: vim/vim#14529 closes: vim/vim#14556 https://github.com/vim/vim/commit/bce51d9005dd1c5bc002acbac2e12b649abcb013 Cherry-pick eval_to_string_eap() from patch 8.2.1914. Co-authored-by: Yegappan Lakshmanan --- src/nvim/eval/vars.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c index 37199cd95d..1c15274acc 100644 --- a/src/nvim/eval/vars.c +++ b/src/nvim/eval/vars.c @@ -88,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; -- cgit From 2f371ad7d0294072d407bb05816425b5694d1935 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 17 Apr 2024 06:52:29 +0800 Subject: vim-patch:9.1.0341: Problem: a few memory leaks are found (#28382) Problem: a few memory leaks are found (LuMingYinDetect ) Solution: properly free the memory Fixes the following problems: - Memory leak in f_maplist() fixes: vim/vim#14486 - Memory leak in option.c fixes: vim/vim#14485 - Memory leak in f_resolve() fixes: vim/vim#14484 - Memory leak in f_autocmd_get() related: vim/vim#14474 - Memory leak in dict_extend_func() fixes: vim/vim#14477 fixes: vim/vim#14238 closes: vim/vim#14517 https://github.com/vim/vim/commit/29269a71b5ac8a87c6c4beca35c173a19a2c9398 Co-authored-by: Christian Brabandt --- src/nvim/eval/funcs.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index d3fe2c7e33..8e62e3b96f 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -2019,6 +2019,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 +2031,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; } -- cgit From 0ea38c9a53dfcff17703ea22f701ed1cc5bbd7d3 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 20 Apr 2024 19:31:00 +0800 Subject: refactor: add xmemcpyz() and use it in place of some xstrlcpy() (#28422) Problem: Using xstrlcpy() when the exact length of the string to be copied is known is not ideal because it requires adding 1 to the length and an unnecessary strlen(). Solution: Add xmemcpyz() and use it in place of such xstrlcpy() calls. --- src/nvim/eval/userfunc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c index 7bb115aed1..2a3aec1bc7 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; -- cgit From 1294e221a205f1f3c6d2e31421b674db6e747406 Mon Sep 17 00:00:00 2001 From: bfredl Date: Wed, 24 Apr 2024 18:40:03 +0200 Subject: fix(lua): vim.fn.has('nvim-0.10') in fast context, used by vim.deprecate --- src/nvim/eval/funcs.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 8e62e3b96f..8528168be1 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -3435,19 +3435,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; } -- cgit From 234b5f67019b435b604308a96c366b1187c2cc3a Mon Sep 17 00:00:00 2001 From: dundargoc <33953936+dundargoc@users.noreply.github.com> Date: Tue, 30 Apr 2024 01:04:42 +0200 Subject: docs: various fixes (#28208) Co-authored-by: Evgeni Chasnovski Co-authored-by: Famiu Haque Co-authored-by: Gregory Anders Co-authored-by: Guilherme Soares Co-authored-by: Jannik Buhr Co-authored-by: thomaswuhoileong <72001875+thomaswuhoileong@users.noreply.github.com> Co-authored-by: tom-anders <13141438+tom-anders@users.noreply.github.com> Co-authored-by: zeertzjq --- src/nvim/eval/userfunc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c index 2a3aec1bc7..39bd63462c 100644 --- a/src/nvim/eval/userfunc.c +++ b/src/nvim/eval/userfunc.c @@ -2365,7 +2365,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) { -- cgit From c4627676f9a3bc88760c54bc6f1dc38904f064d2 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 2 May 2024 19:33:54 +0800 Subject: vim-patch:9.1.0388: cursor() and getregion() don't handle v:maxcol well (#28602) Problem: cursor() and getregion() don't handle v:maxcol well. Solution: Add special handling for v:maxcol like setpos() does. (zeertzjq) closes: vim/vim#14698 https://github.com/vim/vim/commit/2ffdae79487cb7e323383eda9ae96c2e9d1625bd --- src/nvim/eval/funcs.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 8528168be1..b1ee33929c 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -1185,9 +1185,10 @@ 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. @@ -2879,15 +2880,20 @@ static void f_getregion(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) semsg(_(e_invalid_line_number_nr), p1.lnum); return; } - if (p1.col < 1 || p1.col > ml_get_buf_len(findbuf, p1.lnum) + 1) { + 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; } + if (p2.lnum < 1 || p2.lnum > findbuf->b_ml.ml_line_count) { semsg(_(e_invalid_line_number_nr), p2.lnum); return; } - if (p2.col < 1 || p2.col > ml_get_buf_len(findbuf, p2.lnum) + 1) { + 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; } -- cgit From 42aa69b076cb338e20b5b4656771f1873e8930d8 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 17 May 2024 18:39:01 +0800 Subject: fix(path): avoid chdir() when resolving path (#28799) Use uv_fs_realpath() instead. It seems that uv_fs_realpath() has some problems on non-Linux platforms: - macOS and other BSDs: this function will fail with UV_ELOOP if more than 32 symlinks are found while resolving the given path. This limit is hardcoded and cannot be sidestepped. - Windows: while this function works in the common case, there are a number of corner cases where it doesn't: - Paths in ramdisk volumes created by tools which sidestep the Volume Manager (such as ImDisk) cannot be resolved. - Inconsistent casing when using drive letters. - Resolved path bypasses subst'd drives. Ref: https://docs.libuv.org/en/v1.x/fs.html#c.uv_fs_realpath I don't know if the old implementation that uses uv_chdir() and uv_cwd() also suffers from the same problems. - For the ELOOP case, chdir() seems to have the same limitations. - On Windows, Vim doesn't use anything like chdir() either. It uses _wfullpath(), while libuv uses GetFinalPathNameByHandleW(). --- src/nvim/eval/funcs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index b1ee33929c..1fc80e88c4 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -6479,7 +6479,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); @@ -6631,7 +6631,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 -- cgit From 62eb7e79a5f9b5f476f034b5446d2972c840ef87 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 18 May 2024 07:09:05 +0800 Subject: vim-patch:9.1.0418: Cannot move to previous/next rare word (#28822) Problem: Cannot move to previous/next rare word (Colin Kennedy) Solution: Add the ]r and [r motions (Christ van Willegen) fixes: vim/vim#14773 closes: vim/vim#14780 https://github.com/vim/vim/commit/8e4c4c7d87def2b100a5d64dc518ef85d9de8765 Co-authored-by: Christ van Willegen - van Noort --- src/nvim/eval/funcs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 1fc80e88c4..0b4b79c6ca 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -8308,7 +8308,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; -- cgit From d89144626e7429d9c499875ed426a6223f9013be Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 20 May 2024 06:15:58 +0800 Subject: vim-patch:9.1.0394: Cannot get a list of positions describing a region Problem: Cannot get a list of positions describing a region (Justin M. Keyes, after v9.1.0120) Solution: Add the getregionpos() function (Shougo Matsushita) fixes: vim/vim#14609 closes: vim/vim#14617 https://github.com/vim/vim/commit/b4757e627e6c83d1c8e5535d4887a82d6a5efdd0 Co-authored-by: Shougo Matsushita Co-authored-by: Justin M. Keyes --- src/nvim/eval/funcs.c | 228 +++++++++++++++++++++++++++++++++++--------------- 1 file changed, 159 insertions(+), 69 deletions(-) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 0b4b79c6ca..2667f4a694 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -2823,24 +2823,25 @@ 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, + int *const fnum) + 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; @@ -2858,108 +2859,123 @@ 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 { semsg(_(e_invargNval), "type", type); - return; + return FAIL; } buf_T *findbuf = fnum1 != 0 ? buflist_findnr(fnum1) : curbuf; + *fnum = fnum1 != 0 ? fnum1 : curbuf->b_fnum; if (findbuf == NULL || findbuf->b_ml.ml_mfp == NULL) { emsg(_(e_buffer_is_not_loaded)); - return; + return FAIL; } - if (p1.lnum < 1 || p1.lnum > findbuf->b_ml.ml_line_count) { - semsg(_(e_invalid_line_number_nr), p1.lnum); - return; + 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; + 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; } - if (p2.lnum < 1 || p2.lnum > findbuf->b_ml.ml_line_count) { - semsg(_(e_invalid_line_number_nr), p2.lnum); - return; + 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; + 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; } - buf_T *const save_curbuf = curbuf; curbuf = findbuf; curwin->w_buffer = curbuf; - const TriState save_virtual = virtual_op; 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) { + 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 = ml_get_len(p2.lnum); - if (p2.col > 0) { - p2.col--; - mark_mb_adjustpos(curbuf, &p2); + 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 = ml_get_len(p2->lnum); + if (p2->col > 0) { + p2->col--; + mark_mb_adjustpos(curbuf, p2); } } } // if fp2 is on NUL (empty line) inclusive becomes false - if (*ml_get_pos(&p2) == NUL && !virtual_op) { - inclusive = false; + if (*ml_get_pos(p2) == NUL && !virtual_op) { + *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; + int fnum; + + if (getregionpos(argvars, rettv, + &p1, &p2, &inclusive, ®ion_type, &oa, &fnum) == FAIL) { + return; } for (linenr_T lnum = p1.lnum; lnum <= p2.lnum; lnum++) { @@ -2983,6 +2999,80 @@ static void f_getregion(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) tv_list_append_allocated_string(rettv->vval.v_list, akt); } + // getregionpos() breaks curbuf and virtual_op + curbuf = save_curbuf; + curwin->w_buffer = curbuf; + virtual_op = save_virtual; +} + +static void add_regionpos_range(typval_T *rettv, int bufnr, int lnum1, int col1, int coladd1, + int lnum2, int col2, int coladd2) +{ + 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); + + buf_T *findbuf = bufnr != 0 ? buflist_findnr(bufnr) : curbuf; + + int max_col1 = ml_get_buf_len(findbuf, lnum1); + tv_list_append_number(l2, bufnr); + tv_list_append_number(l2, lnum1); + tv_list_append_number(l2, col1 > max_col1 ? max_col1 : col1); + tv_list_append_number(l2, coladd1); + + int max_col2 = ml_get_buf_len(findbuf, lnum2); + tv_list_append_number(l3, bufnr); + tv_list_append_number(l3, lnum2); + tv_list_append_number(l3, col2 > max_col2 ? max_col2 : col2); + tv_list_append_number(l3, coladd2); +} + +/// "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; + oparg_T oa; + int fnum; + + if (getregionpos(argvars, rettv, + &p1, &p2, &inclusive, ®ion_type, &oa, &fnum) == FAIL) { + return; + } + + for (linenr_T lnum = p1.lnum; lnum <= p2.lnum; lnum++) { + int start_col, end_col; + + if (region_type == kMTLineWise) { + start_col = 1; + end_col = MAXCOL; + } else if (region_type == kMTBlockWise) { + struct block_def bd; + block_prep(&oa, &bd, lnum, false); + start_col = bd.start_vcol + 1; + end_col = bd.end_vcol; + } else if (p1.lnum < lnum && lnum < p2.lnum) { + start_col = 1; + end_col = MAXCOL; + } else { + start_col = p1.lnum == lnum ? p1.col + 1 : 1; + end_col = p2.lnum == lnum ? p2.col + 1 : MAXCOL; + } + + add_regionpos_range(rettv, fnum, lnum, start_col, + p1.coladd, lnum, end_col, p2.coladd); + } + + // getregionpos() may change curbuf and virtual_op curbuf = save_curbuf; curwin->w_buffer = curbuf; virtual_op = save_virtual; -- cgit From 3383603c134944d374eb0814a2f707a7e3e89b43 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 20 May 2024 06:44:19 +0800 Subject: vim-patch:9.1.0395: getregionpos() may leak memory on error Problem: regionpos may leak memory on error, coverity complains about dereferencing Null pointer Solution: free all list pointers (after v9.1.394), return early if buflist_findnr() returns NULL closes: vim/vim#14731 https://github.com/vim/vim/commit/b8ecedce79149ac6b994177e9a68979f86065cb1 Co-authored-by: Christian Brabandt --- src/nvim/eval/funcs.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 2667f4a694..a5d6124eb3 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -3008,6 +3008,11 @@ static void f_getregion(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) static void add_regionpos_range(typval_T *rettv, int bufnr, int lnum1, int col1, int coladd1, int lnum2, int col2, int coladd2) { + buf_T *findbuf = bufnr != 0 ? buflist_findnr(bufnr) : curbuf; + if (findbuf == NULL || findbuf->b_ml.ml_mfp == NULL) { + return; + } + list_T *l1 = tv_list_alloc(2); tv_list_append_list(rettv->vval.v_list, l1); @@ -3017,8 +3022,6 @@ static void add_regionpos_range(typval_T *rettv, int bufnr, int lnum1, int col1, list_T *l3 = tv_list_alloc(4); tv_list_append_list(l1, l3); - buf_T *findbuf = bufnr != 0 ? buflist_findnr(bufnr) : curbuf; - int max_col1 = ml_get_buf_len(findbuf, lnum1); tv_list_append_number(l2, bufnr); tv_list_append_number(l2, lnum1); -- cgit From e0259b9466a0dd62b74d4aa195b3c5e6c7a183d0 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 20 May 2024 20:50:32 +0800 Subject: vim-patch:9.1.0423: getregionpos() wrong with blockwise mode and multibyte Problem: getregionpos() wrong with blockwise mode and multibyte. Solution: Use textcol and textlen instead of start_vcol and end_vcol. Handle coladd properly (zeertzjq). Also remove unnecessary buflist_findnr() in add_regionpos_range(), as getregionpos() has already switched buffer. closes: vim/vim#14805 https://github.com/vim/vim/commit/c95e64f41f7f6d1bdc95b047ae9b369743c8637b --- src/nvim/eval/funcs.c | 85 ++++++++++++++++++++++++++------------------------- 1 file changed, 43 insertions(+), 42 deletions(-) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index a5d6124eb3..0240e08dad 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -2824,8 +2824,7 @@ static char *block_def2str(struct block_def *bd) } static int getregionpos(typval_T *argvars, typval_T *rettv, pos_T *p1, pos_T *p2, - bool *const inclusive, MotionType *region_type, oparg_T *oa, - int *const fnum) + bool *const inclusive, MotionType *region_type, oparg_T *oa) FUNC_ATTR_NONNULL_ALL { tv_list_alloc_ret(rettv, kListLenMayKnow); @@ -2871,7 +2870,6 @@ static int getregionpos(typval_T *argvars, typval_T *rettv, pos_T *p1, pos_T *p2 } buf_T *findbuf = fnum1 != 0 ? buflist_findnr(fnum1) : curbuf; - *fnum = fnum1 != 0 ? fnum1 : curbuf->b_fnum; if (findbuf == NULL || findbuf->b_ml.ml_mfp == NULL) { emsg(_(e_buffer_is_not_loaded)); return FAIL; @@ -2971,10 +2969,8 @@ static void f_getregion(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) bool inclusive = true; MotionType region_type = kMTUnknown; oparg_T oa; - int fnum; - if (getregionpos(argvars, rettv, - &p1, &p2, &inclusive, ®ion_type, &oa, &fnum) == FAIL) { + if (getregionpos(argvars, rettv, &p1, &p2, &inclusive, ®ion_type, &oa) == FAIL) { return; } @@ -2999,20 +2995,14 @@ static void f_getregion(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) tv_list_append_allocated_string(rettv->vval.v_list, akt); } - // getregionpos() breaks curbuf and virtual_op + // 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, int bufnr, int lnum1, int col1, int coladd1, - int lnum2, int col2, int coladd2) +static void add_regionpos_range(typval_T *rettv, pos_T p1, pos_T p2) { - buf_T *findbuf = bufnr != 0 ? buflist_findnr(bufnr) : curbuf; - if (findbuf == NULL || findbuf->b_ml.ml_mfp == NULL) { - return; - } - list_T *l1 = tv_list_alloc(2); tv_list_append_list(rettv->vval.v_list, l1); @@ -3022,17 +3012,17 @@ static void add_regionpos_range(typval_T *rettv, int bufnr, int lnum1, int col1, list_T *l3 = tv_list_alloc(4); tv_list_append_list(l1, l3); - int max_col1 = ml_get_buf_len(findbuf, lnum1); - tv_list_append_number(l2, bufnr); - tv_list_append_number(l2, lnum1); - tv_list_append_number(l2, col1 > max_col1 ? max_col1 : col1); - tv_list_append_number(l2, coladd1); + int max_col1 = ml_get_len(p1.lnum); + tv_list_append_number(l2, curbuf->b_fnum); + tv_list_append_number(l2, p1.lnum); + tv_list_append_number(l2, p1.col > max_col1 ? max_col1 : p1.col); + tv_list_append_number(l2, p1.coladd); - int max_col2 = ml_get_buf_len(findbuf, lnum2); - tv_list_append_number(l3, bufnr); - tv_list_append_number(l3, lnum2); - tv_list_append_number(l3, col2 > max_col2 ? max_col2 : col2); - tv_list_append_number(l3, coladd2); + int max_col2 = ml_get_len(p2.lnum); + tv_list_append_number(l3, curbuf->b_fnum); + tv_list_append_number(l3, p2.lnum); + tv_list_append_number(l3, p2.col > max_col2 ? max_col2 : p2.col); + tv_list_append_number(l3, p2.coladd); } /// "getregionpos()" function @@ -3045,34 +3035,45 @@ static void f_getregionpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr bool inclusive = true; MotionType region_type = kMTUnknown; oparg_T oa; - int fnum; - if (getregionpos(argvars, rettv, - &p1, &p2, &inclusive, ®ion_type, &oa, &fnum) == FAIL) { + if (getregionpos(argvars, rettv, &p1, &p2, &inclusive, ®ion_type, &oa) == FAIL) { return; } for (linenr_T lnum = p1.lnum; lnum <= p2.lnum; lnum++) { - int start_col, end_col; + struct block_def bd; + pos_T ret_p1, ret_p2; if (region_type == kMTLineWise) { - start_col = 1; - end_col = MAXCOL; - } else if (region_type == kMTBlockWise) { - struct block_def bd; - block_prep(&oa, &bd, lnum, false); - start_col = bd.start_vcol + 1; - end_col = bd.end_vcol; - } else if (p1.lnum < lnum && lnum < p2.lnum) { - start_col = 1; - end_col = MAXCOL; + ret_p1.col = 1; + ret_p1.coladd = 0; + ret_p2.col = MAXCOL; + ret_p2.coladd = 0; } else { - start_col = p1.lnum == lnum ? p1.col + 1 : 1; - end_col = p2.lnum == lnum ? p2.col + 1 : MAXCOL; + if (region_type == kMTBlockWise) { + block_prep(&oa, &bd, lnum, false); + } else { + charwise_block_prep(p1, p2, &bd, lnum, inclusive); + } + 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.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; + } } - add_regionpos_range(rettv, fnum, lnum, start_col, - p1.coladd, lnum, end_col, p2.coladd); + ret_p1.lnum = lnum; + ret_p2.lnum = lnum; + add_regionpos_range(rettv, ret_p1, ret_p2); } // getregionpos() may change curbuf and virtual_op -- cgit From b86381f425223adf0ff91fa0746914a5774ddabb Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 21 May 2024 06:22:23 +0800 Subject: vim-patch:9.1.0426: too many strlen() calls in search.c Problem: too many strlen() calls in search.c Solution: refactor code and remove more strlen() calls, use explicit variable to remember strlen (John Marriott) closes: vim/vim#14796 https://github.com/vim/vim/commit/8c85a2a49acf80e4f53ec51e6ff2a5f3830eeddb Co-authored-by: John Marriott --- src/nvim/eval/funcs.c | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 0240e08dad..1d01f3ad98 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -7076,12 +7076,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)) { @@ -7636,16 +7637,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; @@ -7662,13 +7667,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 -- cgit From 5cbd6d9b9f232a6ff22ae3a9af80075404226e4b Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 23 May 2024 06:08:24 +0800 Subject: vim-patch:9.1.0430: getregionpos() doesn't handle one char selection (#28924) Problem: getregionpos() doesn't handle one char selection. Solution: Handle startspaces differently when is_oneChar is set. Also add a test for an exclusive charwise selection with multibyte chars (zeertzjq) closes: vim/vim#14825 https://github.com/vim/vim/commit/52a6f348874778cf315b47d9e8b5f818f4b97277 --- src/nvim/eval/funcs.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 1d01f3ad98..7453bbb601 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -3041,7 +3041,6 @@ static void f_getregionpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr } for (linenr_T lnum = p1.lnum; lnum <= p2.lnum; lnum++) { - struct block_def bd; pos_T ret_p1, ret_p2; if (region_type == kMTLineWise) { @@ -3050,19 +3049,34 @@ static void f_getregionpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr 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.startspaces > 0) { + + 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 (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.endspaces > 0) { + + if (bd.is_oneChar) { // selection entirely inside one char + ret_p2.col = ret_p1.col; + ret_p2.coladd = ret_p1.coladd + bd.startspaces; + } else if (bd.endspaces > 0) { ret_p2.col = bd.textcol + bd.textlen + 1; ret_p2.coladd = bd.endspaces; } else { -- cgit From a599183a274e5102b1244c9aaa7ac6c76d5ba5df Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 23 May 2024 07:04:53 +0800 Subject: vim-patch:8.2.0703: Vim9: closure cannot store value in outer context (#28925) Problem: Vim9: closure cannot store value in outer context. Solution: Make storing value in outer context work. Make :disassemble accept a function reference. https://github.com/vim/vim/commit/b68b346e6db6d3f848e0a89905fcd7777b73c5d8 Funcstack is Vim9script-only. Co-authored-by: Bram Moolenaar --- src/nvim/eval/typval_defs.h | 1 + 1 file changed, 1 insertion(+) (limited to 'src/nvim/eval') 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 -- cgit From bdf15dbe6909b39e5d3cf22ae844ebd37862a1a8 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 23 May 2024 14:23:09 +0800 Subject: vim-patch:9.1.0433: Wrong yanking with exclusive selection and ve=all (#28933) Problem: Wrong yanking with exclusive selection and virtualedit=all, and integer overflow when using getregion() on it. Solution: Set coladd when decreasing column and 'virtualedit' is active. Add more tests for getregion() with 'virtualedit' (zeertzjq). closes: vim/vim#14830 https://github.com/vim/vim/commit/701ad50a9efcf0adfe6d787b606c4e4dbd31f26d --- src/nvim/eval/funcs.c | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 7453bbb601..fcc86bb708 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -2913,24 +2913,13 @@ static int getregionpos(typval_T *argvars, typval_T *rettv, pos_T *p1, pos_T *p2 } if (*region_type == kMTCharWise) { - // handle 'selection' == "exclusive" + // 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 = ml_get_len(p2->lnum); - if (p2->col > 0) { - p2->col--; - mark_mb_adjustpos(curbuf, 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) { + // 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) { -- cgit From cd05fbef170b29083973fd11170d25225feb8bed Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 24 May 2024 15:44:52 +0800 Subject: vim-patch:9.1.0441: getregionpos() can't properly indicate positions beyond eol (#28957) Problem: getregionpos() can't properly indicate positions beyond eol. Solution: Add an "eol" flag that enables handling positions beyond end of line like getpos() does (zeertzjq). Also fix the problem that a position still has the coladd beyond the end of the line when its column has been clamped. In the last test case with TABs at the end of the line the old behavior is obviously wrong. I decided to gate this behind a flag because returning positions that don't correspond to actual characters in the line may lead to mistakes for callers that want to calculate the length of the selected text, so the behavior is only enabled if the caller wants it. closes: vim/vim#14838 https://github.com/vim/vim/commit/2b09de910458247b70751928217422c38fd5abf8 --- src/nvim/eval/funcs.c | 33 ++++++++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 5 deletions(-) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index fcc86bb708..048f744532 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -3001,16 +3001,14 @@ static void add_regionpos_range(typval_T *rettv, pos_T p1, pos_T p2) list_T *l3 = tv_list_alloc(4); tv_list_append_list(l1, l3); - int max_col1 = ml_get_len(p1.lnum); tv_list_append_number(l2, curbuf->b_fnum); tv_list_append_number(l2, p1.lnum); - tv_list_append_number(l2, p1.col > max_col1 ? max_col1 : p1.col); + tv_list_append_number(l2, p1.col); tv_list_append_number(l2, p1.coladd); - int max_col2 = ml_get_len(p2.lnum); tv_list_append_number(l3, curbuf->b_fnum); tv_list_append_number(l3, p2.lnum); - tv_list_append_number(l3, p2.col > max_col2 ? max_col2 : p2.col); + tv_list_append_number(l3, p2.col); tv_list_append_number(l3, p2.coladd); } @@ -3023,14 +3021,20 @@ static void f_getregionpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr 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; @@ -3054,6 +3058,11 @@ static void f_getregionpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr 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; @@ -3064,7 +3073,7 @@ static void f_getregionpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr if (bd.is_oneChar) { // selection entirely inside one char ret_p2.col = ret_p1.col; - ret_p2.coladd = ret_p1.coladd + bd.startspaces; + 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; @@ -3074,6 +3083,20 @@ static void f_getregionpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr } } + 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); -- cgit From 8db9a0e5a26616a84903ca29cd5a877df9037bd8 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 24 May 2024 18:29:17 +0800 Subject: vim-patch:8.2.3158: strange error message when using islocked() with a number (#28962) Problem: Strange error message when using islocked() with a number. (Yegappan Lakshmanan) Solution: Check that the name is empty. https://github.com/vim/vim/commit/1840a7b4e3577e617f724c9d07ccc78195cc010a Use ll_name_len instead. Co-authored-by: Bram Moolenaar --- src/nvim/eval/funcs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 048f744532..e4cb63eb8e 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -4119,7 +4119,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); -- cgit