# HG changeset patch # User Bram Moolenaar # Date 1610565304 -3600 # Node ID f4347a61ed384b233df4cdb99230d2fd3f2852ea # Parent 711b6238c9952021b10b91617376ecee38427ac9 patch 8.2.2342: "char" functions may return wrong column in Insert mode Commit: https://github.com/vim/vim/commit/9145846b6aa411e3ab5c0d145b37808654352877 Author: Bram Moolenaar Date: Wed Jan 13 20:08:38 2021 +0100 patch 8.2.2342: "char" functions may return wrong column in Insert mode Problem: "char" functions return the wront column in Insert mode when the cursor is beyond the end of the line. Solution: Compute the column correctly. (Yegappan Lakshmanan, closes #7669) diff --git a/src/eval.c b/src/eval.c --- a/src/eval.c +++ b/src/eval.c @@ -5056,12 +5056,14 @@ string2float( /* * Convert the specified byte index of line 'lnum' in buffer 'buf' to a * character index. Works only for loaded buffers. Returns -1 on failure. - * The index of the first character is one. + * The index of the first byte and the first character is zero. */ int buf_byteidx_to_charidx(buf_T *buf, int lnum, int byteidx) { char_u *str; + char_u *t; + int count; if (buf == NULL || buf->b_ml.ml_mfp == NULL) return -1; @@ -5074,15 +5076,26 @@ buf_byteidx_to_charidx(buf_T *buf, int l return -1; if (*str == NUL) - return 1; - - return mb_charlen_len(str, byteidx + 1); + return 0; + + // count the number of characters + t = str; + for (count = 0; *t != NUL && t <= str + byteidx; count++) + t += mb_ptr2len(t); + + // In insert mode, when the cursor is at the end of a non-empty line, + // byteidx points to the NUL character immediately past the end of the + // string. In this case, add one to the character count. + if (*t == NUL && byteidx != 0 && t == str + byteidx) + count++; + + return count - 1; } /* * Convert the specified character index of line 'lnum' in buffer 'buf' to a - * byte index. Works only for loaded buffers. Returns -1 on failure. The index - * of the first byte and the first character is one. + * byte index. Works only for loaded buffers. Returns -1 on failure. + * The index of the first byte and the first character is zero. */ int buf_charidx_to_byteidx(buf_T *buf, int lnum, int charidx) @@ -5105,7 +5118,7 @@ buf_charidx_to_byteidx(buf_T *buf, int l while (*t != NUL && --charidx > 0) t += mb_ptr2len(t); - return t - str + 1; + return t - str; } /* @@ -5180,7 +5193,7 @@ var2fpos( { pos = curwin->w_cursor; if (charcol) - pos.col = buf_byteidx_to_charidx(curbuf, pos.lnum, pos.col) - 1; + pos.col = buf_byteidx_to_charidx(curbuf, pos.lnum, pos.col); return &pos; } if (name[0] == 'v' && name[1] == NUL) // Visual start @@ -5190,7 +5203,7 @@ var2fpos( else pos = curwin->w_cursor; if (charcol) - pos.col = buf_byteidx_to_charidx(curbuf, pos.lnum, pos.col) - 1; + pos.col = buf_byteidx_to_charidx(curbuf, pos.lnum, pos.col); return &pos; } if (name[0] == '\'') // mark @@ -5199,7 +5212,7 @@ var2fpos( if (pp == NULL || pp == (pos_T *)-1 || pp->lnum <= 0) return NULL; if (charcol) - pp->col = buf_byteidx_to_charidx(curbuf, pp->lnum, pp->col) - 1; + pp->col = buf_byteidx_to_charidx(curbuf, pp->lnum, pp->col); return pp; } @@ -5300,7 +5313,7 @@ list2fpos( if (buf == NULL || buf->b_ml.ml_mfp == NULL) return FAIL; - n = buf_charidx_to_byteidx(buf, posp->lnum, n); + n = buf_charidx_to_byteidx(buf, posp->lnum, n) + 1; } posp->col = n; diff --git a/src/evalfunc.c b/src/evalfunc.c --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -2748,7 +2748,7 @@ set_cursorpos(typval_T *argvars, typval_ semsg(_(e_invarg2), tv_get_string(&argvars[0])); col = (long)tv_get_number_chk(&argvars[1], NULL); if (charcol) - col = buf_charidx_to_byteidx(curbuf, line, col); + col = buf_charidx_to_byteidx(curbuf, line, col) + 1; if (argvars[2].v_type != VAR_UNKNOWN) coladd = (long)tv_get_number_chk(&argvars[2], NULL); } @@ -4011,8 +4011,8 @@ getpos_both( if (fp != NULL && charcol) { pos = *fp; - pos.col = buf_byteidx_to_charidx(wp->w_buffer, pos.lnum, - pos.col) - 1; + pos.col = + buf_byteidx_to_charidx(wp->w_buffer, pos.lnum, pos.col); fp = &pos; } } diff --git a/src/testdir/test_cursor_func.vim b/src/testdir/test_cursor_func.vim --- a/src/testdir/test_cursor_func.vim +++ b/src/testdir/test_cursor_func.vim @@ -123,11 +123,18 @@ func Test_screenpos_number() bwipe! endfunc +" Save the visual start character position func SaveVisualStartCharPos() call add(g:VisualStartPos, getcharpos('v')) return '' endfunc +" Save the current cursor character position in insert mode +func SaveInsertCurrentCharPos() + call add(g:InsertCurrentPos, getcharpos('.')) + return '' +endfunc + " Test for the getcharpos() function func Test_getcharpos() call assert_fails('call getcharpos({})', 'E731:') @@ -156,16 +163,29 @@ func Test_getcharpos() vnoremap SaveVisualStartCharPos() let g:VisualStartPos = [] exe "normal 2G6lv$\ohh\o\" - call assert_equal([[0, 2, 7, 0], [0, 2, 9, 0], [0, 2, 5, 0]], g:VisualStartPos) + call assert_equal([[0, 2, 7, 0], [0, 2, 10, 0], [0, 2, 5, 0]], g:VisualStartPos) call assert_equal([0, 2, 9, 0], getcharpos('v')) let g:VisualStartPos = [] exe "normal 3Gv$\o\" - call assert_equal([[0, 3, 1, 0], [0, 3, 1, 0]], g:VisualStartPos) + call assert_equal([[0, 3, 1, 0], [0, 3, 2, 0]], g:VisualStartPos) let g:VisualStartPos = [] exe "normal 1Gv$\o\" call assert_equal([[0, 1, 1, 0], [0, 1, 1, 0]], g:VisualStartPos) vunmap + " Test for getting the position in insert mode with the cursor after the + " last character in a line + inoremap SaveInsertCurrentCharPos() + let g:InsertCurrentPos = [] + exe "normal 1GA\" + exe "normal 2GA\" + exe "normal 3GA\" + exe "normal 4GA\" + exe "normal 2G6li\" + call assert_equal([[0, 1, 1, 0], [0, 2, 10, 0], [0, 3, 2, 0], [0, 4, 10, 0], + \ [0, 2, 7, 0]], g:InsertCurrentPos) + iunmap + %bw! endfunc @@ -192,6 +212,10 @@ func Test_setcharpos() call setcharpos("'m", [0, 2, 9, 0]) normal `m call assert_equal([2, 11], [line('.'), col('.')]) + " unload the buffer and try to set the mark + let bnr = bufnr() + enew! + call assert_equal(-1, setcharpos("'m", [bnr, 2, 2, 0])) %bw! call assert_equal(-1, setcharpos('.', [10, 3, 1, 0])) @@ -202,6 +226,11 @@ func SaveVisualStartCharCol() return '' endfunc +func SaveInsertCurrentCharCol() + call add(g:InsertCurrentCol, charcol('.')) + return '' +endfunc + " Test for the charcol() function func Test_charcol() call assert_fails('call charcol({})', 'E731:') @@ -239,19 +268,36 @@ func Test_charcol() vnoremap SaveVisualStartCharCol() let g:VisualStartCol = [] exe "normal 2G6lv$\ohh\o\" - call assert_equal([7, 9, 5], g:VisualStartCol) + call assert_equal([7, 10, 5], g:VisualStartCol) call assert_equal(9, charcol('v')) let g:VisualStartCol = [] exe "normal 3Gv$\o\" - call assert_equal([1, 1], g:VisualStartCol) + call assert_equal([1, 2], g:VisualStartCol) let g:VisualStartCol = [] exe "normal 1Gv$\o\" call assert_equal([1, 1], g:VisualStartCol) vunmap + " Test for getting the column number in insert mode with the cursor after + " the last character in a line + inoremap SaveInsertCurrentCharCol() + let g:InsertCurrentCol = [] + exe "normal 1GA\" + exe "normal 2GA\" + exe "normal 3GA\" + exe "normal 4GA\" + exe "normal 2G6li\" + call assert_equal([1, 10, 2, 10, 7], g:InsertCurrentCol) + iunmap + %bw! endfunc +func SaveInsertCursorCharPos() + call add(g:InsertCursorPos, getcursorcharpos('.')) + return '' +endfunc + " Test for getcursorcharpos() func Test_getcursorcharpos() call assert_equal(getcursorcharpos(), getcursorcharpos(0)) @@ -269,6 +315,19 @@ func Test_getcursorcharpos() normal 4G9l call assert_equal([0, 4, 9, 0, 9], getcursorcharpos()) + " Test for getting the cursor position in insert mode with the cursor after + " the last character in a line + inoremap SaveInsertCursorCharPos() + let g:InsertCursorPos = [] + exe "normal 1GA\" + exe "normal 2GA\" + exe "normal 3GA\" + exe "normal 4GA\" + exe "normal 2G6li\" + call assert_equal([[0, 1, 1, 0, 1], [0, 2, 10, 0, 15], [0, 3, 2, 0, 2], + \ [0, 4, 10, 0, 10], [0, 2, 7, 0, 12]], g:InsertCursorPos) + iunmap + let winid = win_getid() normal 2G5l wincmd w diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -751,6 +751,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 2342, +/**/ 2341, /**/ 2340,