mirror of
https://github.com/neovim/neovim.git
synced 2026-02-21 09:50:19 +10:00
fix(marks): wrong line('w$', win) with conceal_lines (#37047)
Background:
Suppose a window has concealed lines, and sets conceallevel>2,
concealcursor="". The concealed lines are displayed if the window is
curwin and the cursor is on the those lines.
Problem:
line('w$', win) switches curwin to win, and then does validate_botline
for curwin. It computes botline assuming the concealed lines displayed,
resulting in a smaller value than the actual botline that the user sees.
Solution:
Evaluate line('w$', win) without switching curwin.
Apply similar changes to other functions that switches curwin.
Co-authored-by: zeertzjq <zeertzjq@outlook.com>
(cherry picked from commit 033f1123cd)
This commit is contained in:
committed by
github-actions[bot]
parent
d1cd79a4b6
commit
124c18261c
@@ -6431,14 +6431,17 @@ int buf_charidx_to_byteidx(buf_T *buf, linenr_T lnum, int charidx)
|
||||
/// @param[in] dollar_lnum True when "$" is last line.
|
||||
/// @param[out] ret_fnum Set to fnum for marks.
|
||||
/// @param[in] charcol True to return character column.
|
||||
/// @param[in] wp Window for which to get the position.
|
||||
///
|
||||
/// @return Pointer to position or NULL in case of error (e.g. invalid type).
|
||||
pos_T *var2fpos(const typval_T *const tv, const bool dollar_lnum, int *const ret_fnum,
|
||||
const bool charcol)
|
||||
const bool charcol, win_T *wp)
|
||||
FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
static pos_T pos;
|
||||
|
||||
buf_T *bp = wp->w_buffer;
|
||||
|
||||
// Argument can be [lnum, col, coladd].
|
||||
if (tv->v_type == VAR_LIST) {
|
||||
bool error = false;
|
||||
@@ -6450,7 +6453,7 @@ pos_T *var2fpos(const typval_T *const tv, const bool dollar_lnum, int *const ret
|
||||
|
||||
// Get the line number.
|
||||
pos.lnum = (linenr_T)tv_list_find_nr(l, 0, &error);
|
||||
if (error || pos.lnum <= 0 || pos.lnum > curbuf->b_ml.ml_line_count) {
|
||||
if (error || pos.lnum <= 0 || pos.lnum > bp->b_ml.ml_line_count) {
|
||||
// Invalid line number.
|
||||
return NULL;
|
||||
}
|
||||
@@ -6462,9 +6465,9 @@ pos_T *var2fpos(const typval_T *const tv, const bool dollar_lnum, int *const ret
|
||||
}
|
||||
int len;
|
||||
if (charcol) {
|
||||
len = mb_charlen(ml_get(pos.lnum));
|
||||
len = mb_charlen(ml_get_buf(bp, pos.lnum));
|
||||
} else {
|
||||
len = ml_get_len(pos.lnum);
|
||||
len = ml_get_buf_len(bp, pos.lnum);
|
||||
}
|
||||
|
||||
// We accept "$" for the column number: last column.
|
||||
@@ -6499,18 +6502,18 @@ pos_T *var2fpos(const typval_T *const tv, const bool dollar_lnum, int *const ret
|
||||
pos.lnum = 0;
|
||||
if (name[0] == '.') {
|
||||
// cursor
|
||||
pos = curwin->w_cursor;
|
||||
pos = wp->w_cursor;
|
||||
} else if (name[0] == 'v' && name[1] == NUL) {
|
||||
// Visual start
|
||||
if (VIsual_active) {
|
||||
if (VIsual_active && wp == curwin) {
|
||||
pos = VIsual;
|
||||
} else {
|
||||
pos = curwin->w_cursor;
|
||||
pos = wp->w_cursor;
|
||||
}
|
||||
} else if (name[0] == '\'') {
|
||||
// mark
|
||||
int mname = (uint8_t)name[1];
|
||||
const fmark_T *const fm = mark_get(curbuf, curwin, NULL, kMarkAll, mname);
|
||||
const fmark_T *const fm = mark_get(bp, wp, NULL, kMarkAll, mname);
|
||||
if (fm == NULL || fm->mark.lnum <= 0) {
|
||||
return NULL;
|
||||
}
|
||||
@@ -6520,7 +6523,7 @@ pos_T *var2fpos(const typval_T *const tv, const bool dollar_lnum, int *const ret
|
||||
}
|
||||
if (pos.lnum != 0) {
|
||||
if (charcol) {
|
||||
pos.col = buf_byteidx_to_charidx(curbuf, pos.lnum, pos.col);
|
||||
pos.col = buf_byteidx_to_charidx(bp, pos.lnum, pos.col);
|
||||
}
|
||||
return &pos;
|
||||
}
|
||||
@@ -6530,31 +6533,31 @@ pos_T *var2fpos(const typval_T *const tv, const bool dollar_lnum, int *const ret
|
||||
if (name[0] == 'w' && dollar_lnum) {
|
||||
// the "w_valid" flags are not reset when moving the cursor, but they
|
||||
// do matter for update_topline() and validate_botline().
|
||||
check_cursor_moved(curwin);
|
||||
check_cursor_moved(wp);
|
||||
|
||||
pos.col = 0;
|
||||
if (name[1] == '0') { // "w0": first visible line
|
||||
update_topline(curwin);
|
||||
update_topline(wp);
|
||||
// In silent Ex mode topline is zero, but that's not a valid line
|
||||
// number; use one instead.
|
||||
pos.lnum = curwin->w_topline > 0 ? curwin->w_topline : 1;
|
||||
pos.lnum = wp->w_topline > 0 ? wp->w_topline : 1;
|
||||
return &pos;
|
||||
} else if (name[1] == '$') { // "w$": last visible line
|
||||
validate_botline(curwin);
|
||||
validate_botline(wp);
|
||||
// In silent Ex mode botline is zero, return zero then.
|
||||
pos.lnum = curwin->w_botline > 0 ? curwin->w_botline - 1 : 0;
|
||||
pos.lnum = wp->w_botline > 0 ? wp->w_botline - 1 : 0;
|
||||
return &pos;
|
||||
}
|
||||
} else if (name[0] == '$') { // last column or line
|
||||
if (dollar_lnum) {
|
||||
pos.lnum = curbuf->b_ml.ml_line_count;
|
||||
pos.lnum = bp->b_ml.ml_line_count;
|
||||
pos.col = 0;
|
||||
} else {
|
||||
pos.lnum = curwin->w_cursor.lnum;
|
||||
pos.lnum = wp->w_cursor.lnum;
|
||||
if (charcol) {
|
||||
pos.col = (colnr_T)mb_charlen(get_cursor_line_ptr());
|
||||
pos.col = (colnr_T)mb_charlen(ml_get_buf(bp, wp->w_cursor.lnum));
|
||||
} else {
|
||||
pos.col = get_cursor_line_len();
|
||||
pos.col = ml_get_buf_len(bp, wp->w_cursor.lnum);
|
||||
}
|
||||
}
|
||||
return &pos;
|
||||
|
||||
@@ -718,33 +718,27 @@ static void get_col(typval_T *argvars, typval_T *rettv, bool charcol)
|
||||
return;
|
||||
}
|
||||
|
||||
switchwin_T switchwin;
|
||||
bool winchanged = false;
|
||||
win_T *wp = curwin;
|
||||
|
||||
if (argvars[1].v_type != VAR_UNKNOWN) {
|
||||
// use the window specified in the second argument
|
||||
tabpage_T *tp;
|
||||
win_T *wp = win_id2wp_tp((int)tv_get_number(&argvars[1]), &tp);
|
||||
wp = win_id2wp_tp((int)tv_get_number(&argvars[1]), &tp);
|
||||
if (wp == NULL || tp == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (switch_win_noblock(&switchwin, wp, tp, true) != OK) {
|
||||
return;
|
||||
}
|
||||
|
||||
check_cursor(curwin);
|
||||
winchanged = true;
|
||||
check_cursor(wp);
|
||||
}
|
||||
|
||||
buf_T *bp = wp->w_buffer;
|
||||
colnr_T col = 0;
|
||||
int fnum = curbuf->b_fnum;
|
||||
pos_T *fp = var2fpos(&argvars[0], false, &fnum, charcol);
|
||||
if (fp != NULL && fnum == curbuf->b_fnum) {
|
||||
int fnum = bp->b_fnum;
|
||||
pos_T *fp = var2fpos(&argvars[0], false, &fnum, charcol, wp);
|
||||
if (fp != NULL && fnum == bp->b_fnum) {
|
||||
if (fp->col == MAXCOL) {
|
||||
// '> can be MAXCOL, get the length of the line then
|
||||
if (fp->lnum <= curbuf->b_ml.ml_line_count) {
|
||||
col = ml_get_len(fp->lnum) + 1;
|
||||
if (fp->lnum <= bp->b_ml.ml_line_count) {
|
||||
col = ml_get_buf_len(bp, fp->lnum) + 1;
|
||||
} else {
|
||||
col = MAXCOL;
|
||||
}
|
||||
@@ -752,11 +746,11 @@ 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(curwin) && fp == &curwin->w_cursor) {
|
||||
char *p = get_cursor_pos_ptr();
|
||||
if (curwin->w_cursor.coladd >=
|
||||
(colnr_T)win_chartabsize(curwin, p,
|
||||
curwin->w_virtcol - curwin->w_cursor.coladd)) {
|
||||
if (virtual_active(wp) && fp == &wp->w_cursor) {
|
||||
char *p = ml_get_buf(bp, wp->w_cursor.lnum) + wp->w_cursor.col;
|
||||
if (wp->w_cursor.coladd >=
|
||||
(colnr_T)win_chartabsize(wp, p,
|
||||
wp->w_virtcol - wp->w_cursor.coladd)) {
|
||||
int l;
|
||||
if (*p != NUL && p[(l = utfc_ptr2len(p))] == NUL) {
|
||||
col += l;
|
||||
@@ -766,10 +760,6 @@ static void get_col(typval_T *argvars, typval_T *rettv, bool charcol)
|
||||
}
|
||||
}
|
||||
rettv->vval.v_number = col;
|
||||
|
||||
if (winchanged) {
|
||||
restore_win_noblock(&switchwin, true);
|
||||
}
|
||||
}
|
||||
|
||||
/// "charcol()" function
|
||||
@@ -2460,7 +2450,7 @@ static void getpos_both(typval_T *argvars, typval_T *rettv, bool getcurpos, bool
|
||||
fp = &pos;
|
||||
}
|
||||
} else {
|
||||
fp = var2fpos(&argvars[0], true, &fnum, charcol);
|
||||
fp = var2fpos(&argvars[0], true, &fnum, charcol, curwin);
|
||||
}
|
||||
|
||||
list_T *const l = tv_list_alloc_ret(rettv, 4 + getcurpos);
|
||||
@@ -4435,22 +4425,18 @@ static void f_line(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
||||
tabpage_T *tp;
|
||||
win_T *wp = win_id2wp_tp(id, &tp);
|
||||
if (wp != NULL && tp != NULL) {
|
||||
switchwin_T switchwin;
|
||||
if (switch_win_noblock(&switchwin, wp, tp, true) == OK) {
|
||||
// With 'splitkeep' != cursor and in diff mode, prevent that the
|
||||
// window scrolls and keep the topline.
|
||||
if (*p_spk != 'c' || (curwin->w_p_diff && switchwin.sw_curwin->w_p_diff)) {
|
||||
skip_update_topline = true;
|
||||
}
|
||||
check_cursor(curwin);
|
||||
fp = var2fpos(&argvars[0], true, &fnum, false);
|
||||
// With 'splitkeep' != cursor and in diff mode, prevent that the
|
||||
// window scrolls and keep the topline.
|
||||
if (*p_spk != 'c' || (wp->w_p_diff && curwin->w_p_diff)) {
|
||||
skip_update_topline = true;
|
||||
}
|
||||
check_cursor(wp);
|
||||
fp = var2fpos(&argvars[0], true, &fnum, false, wp);
|
||||
skip_update_topline = false;
|
||||
restore_win_noblock(&switchwin, true);
|
||||
}
|
||||
} else {
|
||||
// use current window
|
||||
fp = var2fpos(&argvars[0], true, &fnum, false);
|
||||
fp = var2fpos(&argvars[0], true, &fnum, false, curwin);
|
||||
}
|
||||
|
||||
if (fp != NULL) {
|
||||
@@ -8245,39 +8231,33 @@ static void f_virtcol(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
||||
{
|
||||
colnr_T vcol_start = 0;
|
||||
colnr_T vcol_end = 0;
|
||||
switchwin_T switchwin;
|
||||
bool winchanged = false;
|
||||
win_T *wp = curwin;
|
||||
|
||||
if (argvars[1].v_type != VAR_UNKNOWN && argvars[2].v_type != VAR_UNKNOWN) {
|
||||
// use the window specified in the third argument
|
||||
tabpage_T *tp;
|
||||
win_T *wp = win_id2wp_tp((int)tv_get_number(&argvars[2]), &tp);
|
||||
wp = win_id2wp_tp((int)tv_get_number(&argvars[2]), &tp);
|
||||
if (wp == NULL || tp == NULL) {
|
||||
goto theend;
|
||||
}
|
||||
|
||||
if (switch_win_noblock(&switchwin, wp, tp, true) != OK) {
|
||||
goto theend;
|
||||
}
|
||||
|
||||
check_cursor(curwin);
|
||||
winchanged = true;
|
||||
check_cursor(wp);
|
||||
}
|
||||
|
||||
int fnum = curbuf->b_fnum;
|
||||
pos_T *fp = var2fpos(&argvars[0], false, &fnum, false);
|
||||
if (fp != NULL && fp->lnum <= curbuf->b_ml.ml_line_count
|
||||
&& fnum == curbuf->b_fnum) {
|
||||
buf_T *bp = wp->w_buffer;
|
||||
int fnum = bp->b_fnum;
|
||||
pos_T *fp = var2fpos(&argvars[0], false, &fnum, false, wp);
|
||||
if (fp != NULL && fp->lnum <= bp->b_ml.ml_line_count
|
||||
&& fnum == bp->b_fnum) {
|
||||
// Limit the column to a valid value, getvvcol() doesn't check.
|
||||
if (fp->col < 0) {
|
||||
fp->col = 0;
|
||||
} else {
|
||||
const colnr_T len = ml_get_len(fp->lnum);
|
||||
const colnr_T len = ml_get_buf_len(bp, fp->lnum);
|
||||
if (fp->col > len) {
|
||||
fp->col = len;
|
||||
}
|
||||
}
|
||||
getvvcol(curwin, fp, &vcol_start, NULL, &vcol_end);
|
||||
getvvcol(wp, fp, &vcol_start, NULL, &vcol_end);
|
||||
vcol_start++;
|
||||
vcol_end++;
|
||||
}
|
||||
@@ -8290,10 +8270,6 @@ theend:
|
||||
} else {
|
||||
rettv->vval.v_number = vcol_end;
|
||||
}
|
||||
|
||||
if (winchanged) {
|
||||
restore_win_noblock(&switchwin, true);
|
||||
}
|
||||
}
|
||||
|
||||
/// "visualmode()" function
|
||||
|
||||
@@ -4235,7 +4235,7 @@ linenr_T tv_get_lnum(const typval_T *const tv)
|
||||
if (lnum <= 0 && did_emsg_before == did_emsg && tv->v_type != VAR_NUMBER) {
|
||||
int fnum;
|
||||
// No valid number, try using same function as line() does.
|
||||
pos_T *const fp = var2fpos(tv, true, &fnum, false);
|
||||
pos_T *const fp = var2fpos(tv, true, &fnum, false, curwin);
|
||||
if (fp != NULL) {
|
||||
lnum = fp->lnum;
|
||||
}
|
||||
|
||||
@@ -3088,6 +3088,42 @@ describe('extmark decorations', function()
|
||||
]]
|
||||
})
|
||||
end)
|
||||
|
||||
it('line("w$", win) considers conceal_lines', function()
|
||||
api.nvim_buf_set_lines(0, 0, -1, true, { 'line 1', 'line 2', 'line 3' })
|
||||
api.nvim_buf_set_extmark(0, ns, 0, 0, { conceal_lines = '' }) -- conceal line 1
|
||||
|
||||
local win = exec_lua(function()
|
||||
local provider_ns = vim.api.nvim_create_namespace('test_f_line')
|
||||
_G.line_w_dollar = {}
|
||||
vim.api.nvim_set_decoration_provider(provider_ns, {
|
||||
on_start = function()
|
||||
for _, win in ipairs(vim.api.nvim_tabpage_list_wins(0)) do
|
||||
table.insert(_G.line_w_dollar, { win, vim.fn.line('w$', win) })
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
local win = vim.api.nvim_open_win(0, false, {
|
||||
relative = 'editor',
|
||||
width = 20,
|
||||
height = 1,
|
||||
row = 0,
|
||||
col = 0,
|
||||
border = 'single',
|
||||
})
|
||||
vim.api.nvim_set_option_value('conceallevel', 2, { scope = 'local', win = win })
|
||||
|
||||
return win
|
||||
end)
|
||||
|
||||
local line_w_dollar = exec_lua('return _G.line_w_dollar')
|
||||
for _, win_line in ipairs(line_w_dollar) do
|
||||
if win_line[1] == win then
|
||||
eq(2, win_line[2])
|
||||
end
|
||||
end
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('decorations: inline virtual text', function()
|
||||
|
||||
Reference in New Issue
Block a user