commit 3fb56ff8af5bbc4f0253632aca8bc3d3642c9f1f
parent ae826362761aca9d1bdb065c156fecfcd2e9e132
Author: zeertzjq <zeertzjq@outlook.com>
Date: Wed, 29 Oct 2025 06:50:14 +0800
vim-patch:9.1.1885: Wrong restored cursor pos when re-entering buffer after changes
Problem: Wrong restored cursor position when re-entering a buffer
previously viewed in a window after making changes to the same
buffer in another window.
Solution: Adjust per-window "last cursor" positions on buffer changes.
(zeertzjq)
closes: vim/vim#18655
https://github.com/vim/vim/commit/b2e6b328dafe1cdfff10f1e811355e514da8071d
Diffstat:
2 files changed, 55 insertions(+), 14 deletions(-)
diff --git a/src/nvim/mark.c b/src/nvim/mark.c
@@ -1168,6 +1168,23 @@ void ex_changes(exarg_T *eap)
} \
}
+// Like ONE_ADJUST_NODEL(), but if the position is within the deleted range,
+// move it to the start of the line before the range.
+#define ONE_ADJUST_CURSOR(pp) \
+ { \
+ pos_T *posp = pp; \
+ if (posp->lnum >= line1 && posp->lnum <= line2) { \
+ if (amount == MAXLNUM) { /* line with cursor is deleted */ \
+ posp->lnum = MAX(line1 - 1, 1); \
+ posp->col = 0; \
+ } else { /* keep cursor on the same line */ \
+ posp->lnum += amount; \
+ } \
+ } else if (amount_after && posp->lnum > line2) { \
+ posp->lnum += amount_after; \
+ } \
+ }
+
// Adjust marks between "line1" and "line2" (inclusive) to move "amount" lines.
// Must be called before changed_*(), appended_lines() or deleted_lines().
// May be called before or after changing the text.
@@ -1332,20 +1349,7 @@ void mark_adjust_buf(buf_T *buf, linenr_T line1, linenr_T line2, linenr_T amount
}
}
if (!by_api && (by_term ? win->w_cursor.lnum < buf->b_ml.ml_line_count : win != curwin)) {
- if (win->w_cursor.lnum >= line1 && win->w_cursor.lnum <= line2) {
- if (amount == MAXLNUM) { // line with cursor is deleted
- if (line1 <= 1) {
- win->w_cursor.lnum = 1;
- } else {
- win->w_cursor.lnum = line1 - 1;
- }
- win->w_cursor.col = 0;
- } else { // keep cursor on the same line
- win->w_cursor.lnum += amount;
- }
- } else if (amount_after && win->w_cursor.lnum > line2) {
- win->w_cursor.lnum += amount_after;
- }
+ ONE_ADJUST_CURSOR(&(win->w_cursor));
}
if (adjust_folds) {
@@ -1356,6 +1360,12 @@ void mark_adjust_buf(buf_T *buf, linenr_T line1, linenr_T line2, linenr_T amount
// adjust diffs
diff_mark_adjust(buf, line1, line2, amount, amount_after);
+
+ // adjust per-window "last cursor" positions
+ for (size_t i = 0; i < kv_size(buf->b_wininfo); i++) {
+ WinInfo *wip = kv_A(buf->b_wininfo, i);
+ ONE_ADJUST_CURSOR(&(wip->wi_mark.mark));
+ }
}
// This code is used often, needs to be fast.
diff --git a/test/old/testdir/test_buffer.vim b/test/old/testdir/test_buffer.vim
@@ -619,17 +619,48 @@ func Test_switch_to_previously_viewed_buffer()
vsplit
call cursor(100, 3)
+ call assert_equal('100', getline('.'))
edit Xotherbuf
buffer Xviewbuf
call assert_equal([0, 100, 3, 0], getpos('.'))
+ call assert_equal('100', getline('.'))
+ edit Xotherbuf
+ wincmd p
+ normal! gg10dd
+ wincmd p
+ buffer Xviewbuf
+ call assert_equal([0, 90, 3, 0], getpos('.'))
+ call assert_equal('100', getline('.'))
+
+ edit Xotherbuf
+ wincmd p
+ normal! ggP
+ wincmd p
+ buffer Xviewbuf
+ call assert_equal([0, 100, 3, 0], getpos('.'))
+ call assert_equal('100', getline('.'))
+
+ edit Xotherbuf
+ wincmd p
+ normal! 96gg10ddgg
+ wincmd p
+ buffer Xviewbuf
+ " The original cursor line was deleted, so cursor is restored to the start
+ " of the line before the deleted range.
+ call assert_equal([0, 95, 1, 0], getpos('.'))
+ call assert_equal('95', getline('.'))
+
+ normal! u
exe win_id2win(oldwin) .. 'close'
setlocal bufhidden=hide
call cursor(200, 3)
+ call assert_equal('200', getline('.'))
edit Xotherbuf
buffer Xviewbuf
call assert_equal([0, 200, 3, 0], getpos('.'))
+ call assert_equal('200', getline('.'))
bwipe! Xotherbuf
bwipe! Xviewbuf