diff options
-rw-r--r-- | src/nvim/eval.c | 34 | ||||
-rw-r--r-- | src/nvim/eval/funcs.c | 4 | ||||
-rw-r--r-- | src/nvim/testdir/test_cursor_func.vim | 67 |
3 files changed, 89 insertions, 16 deletions
diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 4b83bda804..07f0799bc4 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -8192,7 +8192,7 @@ char *save_tv_as_string(typval_T *tv, ptrdiff_t *const len, bool endnl) /// 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) { if (buf == NULL || buf->b_ml.ml_mfp == NULL) { @@ -8206,15 +8206,29 @@ int buf_byteidx_to_charidx(buf_T *buf, int lnum, int byteidx) char_u *str = ml_get_buf(buf, lnum, false); if (*str == NUL) { - return 1; + return 0; + } + + // count the number of characters + char_u *t = str; + int count; + for (count = 0; *t != NUL && t <= str + byteidx; count++) { + t += utfc_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 mb_charlen_len(str, byteidx + 1); + 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) { if (buf == NULL || buf->b_ml.ml_mfp == NULL) { @@ -8233,7 +8247,7 @@ int buf_charidx_to_byteidx(buf_T *buf, int lnum, int charidx) t += utfc_ptr2len(t); } - return t - str + 1; + return t - str; } /// Translate a VimL object into a position @@ -8315,7 +8329,7 @@ pos_T *var2fpos(const typval_T *const tv, const bool dollar_lnum, int *const ret if (name[0] == '.') { // Cursor. 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; } @@ -8326,7 +8340,7 @@ pos_T *var2fpos(const typval_T *const tv, const bool dollar_lnum, int *const ret 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; } @@ -8336,7 +8350,7 @@ pos_T *var2fpos(const typval_T *const tv, const bool dollar_lnum, int *const ret 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; } @@ -8423,7 +8437,7 @@ int list2fpos(typval_T *arg, pos_T *posp, int *fnump, colnr_T *curswantp, bool c 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/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 8beacc9988..7772d9ffc2 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -1588,7 +1588,7 @@ static void set_cursorpos(typval_T *argvars, typval_T *rettv, bool charcol) line = tv_get_lnum(argvars); 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); @@ -3327,7 +3327,7 @@ static void getpos_both(typval_T *argvars, typval_T *rettv, bool getcurpos, bool } 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; } } else { diff --git a/src/nvim/testdir/test_cursor_func.vim b/src/nvim/testdir/test_cursor_func.vim index 47e74a24d6..57825b4551 100644 --- a/src/nvim/testdir/test_cursor_func.vim +++ b/src/nvim/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 <expr> <F3> SaveVisualStartCharPos() let g:VisualStartPos = [] exe "normal 2G6lv$\<F3>ohh\<F3>o\<F3>" - 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$\<F3>o\<F3>" - 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$\<F3>o\<F3>" call assert_equal([[0, 1, 1, 0], [0, 1, 1, 0]], g:VisualStartPos) vunmap <F3> + " Test for getting the position in insert mode with the cursor after the + " last character in a line + inoremap <expr> <F3> SaveInsertCurrentCharPos() + let g:InsertCurrentPos = [] + exe "normal 1GA\<F3>" + exe "normal 2GA\<F3>" + exe "normal 3GA\<F3>" + exe "normal 4GA\<F3>" + exe "normal 2G6li\<F3>" + 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 <F3> + %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 <expr> <F3> SaveVisualStartCharCol() let g:VisualStartCol = [] exe "normal 2G6lv$\<F3>ohh\<F3>o\<F3>" - 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$\<F3>o\<F3>" - call assert_equal([1, 1], g:VisualStartCol) + call assert_equal([1, 2], g:VisualStartCol) let g:VisualStartCol = [] exe "normal 1Gv$\<F3>o\<F3>" call assert_equal([1, 1], g:VisualStartCol) vunmap <F3> + " Test for getting the column number in insert mode with the cursor after + " the last character in a line + inoremap <expr> <F3> SaveInsertCurrentCharCol() + let g:InsertCurrentCol = [] + exe "normal 1GA\<F3>" + exe "normal 2GA\<F3>" + exe "normal 3GA\<F3>" + exe "normal 4GA\<F3>" + exe "normal 2G6li\<F3>" + call assert_equal([1, 10, 2, 10, 7], g:InsertCurrentCol) + iunmap <F3> + %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 <expr> <F3> SaveInsertCursorCharPos() + let g:InsertCursorPos = [] + exe "normal 1GA\<F3>" + exe "normal 2GA\<F3>" + exe "normal 3GA\<F3>" + exe "normal 4GA\<F3>" + exe "normal 2G6li\<F3>" + 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 <F3> + let winid = win_getid() normal 2G5l wincmd w |