Skip to content

Commit 228e422

Browse files
zeertzjqbrammool
authored andcommitted
patch 9.0.0914: deletebufline() may move marks in the wrong window
Problem: deletebufline() may move marks in the wrong window. Solution: Find a window for the buffer being changed. (closes #11583)
1 parent 0a60f79 commit 228e422

File tree

3 files changed

+91
-50
lines changed

3 files changed

+91
-50
lines changed

src/evalbuffer.c

Lines changed: 64 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,59 @@ find_win_for_curbuf(void)
119119
}
120120
}
121121

122+
typedef struct {
123+
win_T *cob_curwin_save;
124+
aco_save_T cob_aco;
125+
int cob_using_aco;
126+
int cob_save_VIsual_active;
127+
} cob_T;
128+
129+
/*
130+
* Used before making a change in "buf", which is not the current one: Make
131+
* "buf" the current buffer and find a window for this buffer, so that side
132+
* effects are done correctly (e.g., adjusting marks).
133+
*
134+
* Information is saved in "cob" and MUST be restored by calling
135+
* change_other_buffer_restore().
136+
*/
137+
static void
138+
change_other_buffer_prepare(cob_T *cob, buf_T *buf)
139+
{
140+
CLEAR_POINTER(cob);
141+
142+
// Set "curbuf" to the buffer being changed. Then make sure there is a
143+
// window for it to handle any side effects.
144+
cob->cob_save_VIsual_active = VIsual_active;
145+
VIsual_active = FALSE;
146+
cob->cob_curwin_save = curwin;
147+
curbuf = buf;
148+
find_win_for_curbuf(); // simplest: find existing window for "buf"
149+
150+
if (curwin->w_buffer != buf)
151+
{
152+
// No existing window for this buffer. It is dangerous to have
153+
// curwin->w_buffer differ from "curbuf", use the autocmd window.
154+
curbuf = curwin->w_buffer;
155+
aucmd_prepbuf(&cob->cob_aco, buf);
156+
cob->cob_using_aco = TRUE;
157+
}
158+
}
159+
160+
static void
161+
change_other_buffer_restore(cob_T *cob)
162+
{
163+
if (cob->cob_using_aco)
164+
{
165+
aucmd_restbuf(&cob->cob_aco);
166+
}
167+
else
168+
{
169+
curwin = cob->cob_curwin_save;
170+
curbuf = curwin->w_buffer;
171+
}
172+
VIsual_active = cob->cob_save_VIsual_active;
173+
}
174+
122175
/*
123176
* Set line or list of lines in buffer "buf" to "lines".
124177
* Any type is allowed and converted to a string.
@@ -137,10 +190,6 @@ set_buffer_lines(
137190
listitem_T *li = NULL;
138191
long added = 0;
139192
linenr_T append_lnum;
140-
win_T *curwin_save = NULL;
141-
aco_save_T aco;
142-
int using_aco = FALSE;
143-
int save_VIsual_active = VIsual_active;
144193

145194
// When using the current buffer ml_mfp will be set if needed. Useful when
146195
// setline() is used on startup. For other buffers the buffer must be
@@ -154,24 +203,11 @@ set_buffer_lines(
154203
return;
155204
}
156205

206+
// After this don't use "return", goto "cleanup"!
207+
cob_T cob;
157208
if (!is_curbuf)
158-
{
159-
// Set "curbuf" to the buffer being changed. Then make sure there is a
160-
// window for it to handle any side effects.
161-
VIsual_active = FALSE;
162-
curwin_save = curwin;
163-
curbuf = buf;
164-
find_win_for_curbuf(); // simplest: find existing window for "buf"
165-
166-
if (curwin->w_buffer != buf)
167-
{
168-
// No existing window for this buffer. It is dangerous to have
169-
// curwin->w_buffer differ from "curbuf", use the autocmd window.
170-
curbuf = curwin->w_buffer;
171-
aucmd_prepbuf(&aco, buf);
172-
using_aco = TRUE;
173-
}
174-
}
209+
// set "curbuf" to "buf" and find a window for this buffer
210+
change_other_buffer_prepare(&cob, buf);
175211

176212
if (append)
177213
// appendbufline() uses the line number below which we insert
@@ -272,18 +308,7 @@ set_buffer_lines(
272308

273309
done:
274310
if (!is_curbuf)
275-
{
276-
if (using_aco)
277-
{
278-
aucmd_restbuf(&aco);
279-
}
280-
else
281-
{
282-
curwin = curwin_save;
283-
curbuf = curwin->w_buffer;
284-
}
285-
VIsual_active = save_VIsual_active;
286-
}
311+
change_other_buffer_restore(&cob);
287312
}
288313

289314
/*
@@ -521,12 +546,9 @@ f_deletebufline(typval_T *argvars, typval_T *rettv)
521546
linenr_T lnum;
522547
long count;
523548
int is_curbuf;
524-
buf_T *curbuf_save = NULL;
525-
win_T *curwin_save = NULL;
526549
tabpage_T *tp;
527550
win_T *wp;
528551
int did_emsg_before = did_emsg;
529-
int save_VIsual_active = VIsual_active;
530552

531553
rettv->vval.v_number = 1; // FAIL by default
532554

@@ -539,7 +561,6 @@ f_deletebufline(typval_T *argvars, typval_T *rettv)
539561
buf = tv_get_buf(&argvars[0], FALSE);
540562
if (buf == NULL)
541563
return;
542-
is_curbuf = buf == curbuf;
543564

544565
first = tv_get_lnum_buf(&argvars[1], buf);
545566
if (did_emsg > did_emsg_before)
@@ -554,14 +575,12 @@ f_deletebufline(typval_T *argvars, typval_T *rettv)
554575
return;
555576

556577
// After this don't use "return", goto "cleanup"!
578+
is_curbuf = buf == curbuf;
579+
cob_T cob;
557580
if (!is_curbuf)
558-
{
559-
VIsual_active = FALSE;
560-
curbuf_save = curbuf;
561-
curwin_save = curwin;
562-
curbuf = buf;
563-
find_win_for_curbuf();
564-
}
581+
// set "curbuf" to "buf" and find a window for this buffer
582+
change_other_buffer_prepare(&cob, buf);
583+
565584
if (last > curbuf->b_ml.ml_line_count)
566585
last = curbuf->b_ml.ml_line_count;
567586
count = last - first + 1;
@@ -599,11 +618,7 @@ f_deletebufline(typval_T *argvars, typval_T *rettv)
599618

600619
cleanup:
601620
if (!is_curbuf)
602-
{
603-
curbuf = curbuf_save;
604-
curwin = curwin_save;
605-
VIsual_active = save_VIsual_active;
606-
}
621+
change_other_buffer_restore(&cob);
607622
}
608623

609624
/*

src/testdir/test_bufline.vim

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,12 +98,25 @@ func Test_appendbufline()
9898
new
9999
let b = bufnr('%')
100100
hide
101+
102+
new
103+
call setline(1, ['line1', 'line2', 'line3'])
104+
normal! 2gggg
105+
call assert_equal(2, line("''"))
106+
101107
call assert_equal(0, appendbufline(b, 0, ['foo', 'bar']))
102108
call assert_equal(['foo'], getbufline(b, 1))
103109
call assert_equal(['bar'], getbufline(b, 2))
104110
call assert_equal(['foo', 'bar'], getbufline(b, 1, 2))
111+
call assert_equal(0, appendbufline(b, 0, 'baz'))
112+
call assert_equal(['baz', 'foo', 'bar'], getbufline(b, 1, 3))
113+
114+
" appendbufline() in a hidden buffer shouldn't move marks in current window.
115+
call assert_equal(2, line("''"))
116+
bwipe!
117+
105118
exe "bd!" b
106-
call assert_equal([], getbufline(b, 1, 2))
119+
call assert_equal([], getbufline(b, 1, 3))
107120

108121
split Xtest
109122
call setline(1, ['a', 'b', 'c'])
@@ -173,10 +186,21 @@ func Test_deletebufline()
173186
let b = bufnr('%')
174187
call setline(1, ['aaa', 'bbb', 'ccc'])
175188
hide
189+
190+
new
191+
call setline(1, ['line1', 'line2', 'line3'])
192+
normal! 2gggg
193+
call assert_equal(2, line("''"))
194+
176195
call assert_equal(0, deletebufline(b, 2))
177196
call assert_equal(['aaa', 'ccc'], getbufline(b, 1, 2))
178197
call assert_equal(0, deletebufline(b, 2, 8))
179198
call assert_equal(['aaa'], getbufline(b, 1, 2))
199+
200+
" deletebufline() in a hidden buffer shouldn't move marks in current window.
201+
call assert_equal(2, line("''"))
202+
bwipe!
203+
180204
exe "bd!" b
181205
call assert_equal(1, b->deletebufline(1))
182206

src/version.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -695,6 +695,8 @@ static char *(features[]) =
695695

696696
static int included_patches[] =
697697
{ /* Add new patch number below this line */
698+
/**/
699+
914,
698700
/**/
699701
913,
700702
/**/

0 commit comments

Comments
 (0)