diff --git a/src/nvim/cursor.c b/src/nvim/cursor.c index f2b3cfe690..036ae32589 100644 --- a/src/nvim/cursor.c +++ b/src/nvim/cursor.c @@ -93,11 +93,12 @@ int coladvance(colnr_T wcol) static int coladvance2( pos_T *pos, - bool addspaces, /* change the text to achieve our goal? */ - bool finetune, /* change char offset for the exact column */ - colnr_T wcol /* column to move to */ + bool addspaces, // change the text to achieve our goal? + bool finetune, // change char offset for the exact column + colnr_T wcol_arg // column to move to (can be negative) ) { + colnr_T wcol = wcol_arg; int idx; char_u *ptr; char_u *line; @@ -165,6 +166,7 @@ static int coladvance2( if (virtual_active() && addspaces + && wcol >= 0 && ((col != wcol && col != wcol + 1) || csize > 1)) { /* 'virtualedit' is set: The difference between wcol and col is * filled with spaces. */ @@ -244,8 +246,9 @@ static int coladvance2( mark_mb_adjustpos(curbuf, pos); } - if (col < wcol) + if (wcol < 0 || col < wcol) { return FAIL; + } return OK; } diff --git a/src/nvim/normal.c b/src/nvim/normal.c index e0dc9d4f23..d051ba33b8 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -3925,15 +3925,17 @@ static bool nv_screengo(oparg_T *oap, int dir, long dist) n = ((linelen - width1 - 1) / width2 + 1) * width2 + width1; else n = width1; - if (curwin->w_curswant > (colnr_T)n + 1) - curwin->w_curswant -= ((curwin->w_curswant - n) / width2 + 1) - * width2; + if (curwin->w_curswant >= n) { + curwin->w_curswant = n - 1; + } } while (dist--) { if (dir == BACKWARD) { - if (curwin->w_curswant > width2) { - // move back within line + if (curwin->w_curswant >= width1) { + // Move back within the line. This can give a negative value + // for w_curswant if width1 < width2 (with cpoptions+=n), + // which will get clipped to column 0. curwin->w_curswant -= width2; } else { // to previous line @@ -3973,6 +3975,13 @@ static bool nv_screengo(oparg_T *oap, int dir, long dist) } curwin->w_cursor.lnum++; curwin->w_curswant %= width2; + // Check if the cursor has moved below the number display + // when width1 < width2 (with cpoptions+=n). Subtract width2 + // to get a negative value for w_curswant, which will get + // clipped to column 0. + if (curwin->w_curswant >= width1) { + curwin->w_curswant -= width2; + } linelen = linetabsize(get_cursor_line_ptr()); } } diff --git a/src/nvim/testdir/test_normal.vim b/src/nvim/testdir/test_normal.vim index b967f84626..aeae6423d0 100644 --- a/src/nvim/testdir/test_normal.vim +++ b/src/nvim/testdir/test_normal.vim @@ -2813,4 +2813,29 @@ func Test_normal_gk() call assert_equal(95, virtcol('.')) bw! bw! + + " needs 80 column new window + new + vert 80new + set number + set numberwidth=10 + set cpoptions+=n + put =[repeat('0',90), repeat('1',90)] + norm! 075l + call assert_equal(76, col('.')) + norm! gk + call assert_equal(1, col('.')) + norm! gk + call assert_equal(76, col('.')) + norm! gk + call assert_equal(1, col('.')) + norm! gj + call assert_equal(76, col('.')) + norm! gj + call assert_equal(1, col('.')) + norm! gj + call assert_equal(76, col('.')) + bw! + bw! + set cpoptions& number& numberwidth& endfunc