blob: a68321f903aa45a2370cd3b50b1b0e17dc19352a [file] [log] [blame]
Bram Moolenaar170b10b2016-07-29 16:15:27 +02001" Tests for the undo tree.
2" Since this script is sourced we need to explicitly break changes up in
3" undo-able pieces. Do that by setting 'undolevels'.
4" Also tests :earlier and :later.
5
Christian Brabandteb380b92025-07-07 20:53:55 +02006source util/screendump.vim
Bram Moolenaar3f8f8272022-12-08 21:49:35 +00007
Bram Moolenaar170b10b2016-07-29 16:15:27 +02008func Test_undotree()
Bram Moolenaar80eaddd2017-11-11 23:37:08 +01009 new
10
11 normal! Aabc
Bram Moolenaar170b10b2016-07-29 16:15:27 +020012 set ul=100
Bram Moolenaar170b10b2016-07-29 16:15:27 +020013 let d = undotree()
Bram Moolenaar80eaddd2017-11-11 23:37:08 +010014 call assert_equal(1, d.seq_last)
15 call assert_equal(1, d.seq_cur)
16 call assert_equal(0, d.save_last)
17 call assert_equal(0, d.save_cur)
18 call assert_equal(1, len(d.entries))
19 call assert_equal(1, d.entries[0].newhead)
20 call assert_equal(1, d.entries[0].seq)
21 call assert_true(d.entries[0].time <= d.time_cur)
22
23 normal! Adef
24 set ul=100
25 let d = undotree()
26 call assert_equal(2, d.seq_last)
27 call assert_equal(2, d.seq_cur)
28 call assert_equal(0, d.save_last)
29 call assert_equal(0, d.save_cur)
30 call assert_equal(2, len(d.entries))
31 call assert_equal(1, d.entries[0].seq)
32 call assert_equal(1, d.entries[1].newhead)
33 call assert_equal(2, d.entries[1].seq)
34 call assert_true(d.entries[1].time <= d.time_cur)
35
36 undo
37 set ul=100
38 let d = undotree()
39 call assert_equal(2, d.seq_last)
40 call assert_equal(1, d.seq_cur)
41 call assert_equal(0, d.save_last)
42 call assert_equal(0, d.save_cur)
43 call assert_equal(2, len(d.entries))
44 call assert_equal(1, d.entries[0].seq)
45 call assert_equal(1, d.entries[1].curhead)
46 call assert_equal(1, d.entries[1].newhead)
47 call assert_equal(2, d.entries[1].seq)
48 call assert_true(d.entries[1].time == d.time_cur)
49
50 normal! Aghi
51 set ul=100
52 let d = undotree()
53 call assert_equal(3, d.seq_last)
54 call assert_equal(3, d.seq_cur)
55 call assert_equal(0, d.save_last)
56 call assert_equal(0, d.save_cur)
57 call assert_equal(2, len(d.entries))
58 call assert_equal(1, d.entries[0].seq)
59 call assert_equal(2, d.entries[1].alt[0].seq)
60 call assert_equal(1, d.entries[1].newhead)
61 call assert_equal(3, d.entries[1].seq)
62 call assert_true(d.entries[1].time <= d.time_cur)
63
64 undo
65 set ul=100
66 let d = undotree()
67 call assert_equal(3, d.seq_last)
68 call assert_equal(1, d.seq_cur)
69 call assert_equal(0, d.save_last)
70 call assert_equal(0, d.save_cur)
71 call assert_equal(2, len(d.entries))
72 call assert_equal(1, d.entries[0].seq)
73 call assert_equal(2, d.entries[1].alt[0].seq)
74 call assert_equal(1, d.entries[1].curhead)
75 call assert_equal(1, d.entries[1].newhead)
76 call assert_equal(3, d.entries[1].seq)
77 call assert_true(d.entries[1].time == d.time_cur)
Bram Moolenaar170b10b2016-07-29 16:15:27 +020078
79 w! Xtest
Bram Moolenaar80eaddd2017-11-11 23:37:08 +010080 let d = undotree()
81 call assert_equal(1, d.save_cur)
82 call assert_equal(1, d.save_last)
Bram Moolenaar170b10b2016-07-29 16:15:27 +020083 call delete('Xtest')
Bram Moolenaar80eaddd2017-11-11 23:37:08 +010084 bwipe! Xtest
Bram Moolenaar170b10b2016-07-29 16:15:27 +020085endfunc
86
87func FillBuffer()
88 for i in range(1,13)
89 put=i
Bram Moolenaare5fa1112018-05-26 18:46:30 +020090 " Set 'undolevels' to split undo.
Bram Moolenaar170b10b2016-07-29 16:15:27 +020091 exe "setg ul=" . &g:ul
92 endfor
93endfunc
94
Devin J. Pohly5fee1112023-04-23 20:26:59 -050095func Test_undotree_bufnr()
96 new
97 let buf1 = bufnr()
98
99 normal! Aabc
100 set ul=100
101
102 " Save undo tree without bufnr as ground truth for buffer 1
103 let d1 = undotree()
104
105 new
106 let buf2 = bufnr()
107
108 normal! Adef
109 set ul=100
110
111 normal! Aghi
112 set ul=100
113
114 " Save undo tree without bufnr as ground truth for buffer 2
115 let d2 = undotree()
116
117 " Check undotree() with bufnr argument
118 let d = undotree(buf1)
119 call assert_equal(d1, d)
120 call assert_notequal(d2, d)
121
122 let d = undotree(buf2)
123 call assert_notequal(d1, d)
124 call assert_equal(d2, d)
125
126 " Switch buffers and check again
127 wincmd p
128
129 let d = undotree(buf1)
130 call assert_equal(d1, d)
131
132 let d = undotree(buf2)
133 call assert_notequal(d1, d)
134 call assert_equal(d2, d)
135
zeertzjqab9f2ec2023-08-20 18:35:10 +0200136 " error cases
137 call assert_fails('call undotree(-1)', 'E158:')
138 call assert_fails('call undotree("nosuchbuf")', 'E158:')
139
140 " after creating a buffer nosuchbuf, undotree('nosuchbuf') should
141 " not error out
142 new nosuchbuf
143 let d = {'seq_last': 0, 'entries': [], 'time_cur': 0, 'save_last': 0, 'synced': 1, 'save_cur': 0, 'seq_cur': 0}
144 call assert_equal(d, undotree("nosuchbuf"))
145 " clean up
146 bw nosuchbuf
147
Devin J. Pohly5fee1112023-04-23 20:26:59 -0500148 " Drop created windows
149 set ul&
150 new
John Marriottfd1a8382024-11-06 21:21:50 +0100151 bw!
Devin J. Pohly5fee1112023-04-23 20:26:59 -0500152endfunc
153
Bram Moolenaar170b10b2016-07-29 16:15:27 +0200154func Test_global_local_undolevels()
155 new one
156 set undolevels=5
157 call FillBuffer()
158 " will only undo the last 5 changes, end up with 13 - (5 + 1) = 7 lines
159 earlier 10
160 call assert_equal(5, &g:undolevels)
161 call assert_equal(-123456, &l:undolevels)
162 call assert_equal('7', getline('$'))
163
164 new two
165 setlocal undolevels=2
166 call FillBuffer()
167 " will only undo the last 2 changes, end up with 13 - (2 + 1) = 10 lines
168 earlier 10
169 call assert_equal(5, &g:undolevels)
170 call assert_equal(2, &l:undolevels)
171 call assert_equal('10', getline('$'))
172
173 setlocal ul=10
174 call assert_equal(5, &g:undolevels)
175 call assert_equal(10, &l:undolevels)
176
177 " Setting local value in "two" must not change local value in "one"
178 wincmd p
179 call assert_equal(5, &g:undolevels)
180 call assert_equal(-123456, &l:undolevels)
181
182 new three
183 setglobal ul=50
184 call assert_equal(50, &g:undolevels)
185 call assert_equal(-123456, &l:undolevels)
186
Bram Moolenaar1363a302020-04-12 13:50:26 +0200187 " Resetting the local 'undolevels' value to use the global value
188 setlocal undolevels=5
189 setlocal undolevels<
190 call assert_equal(-123456, &l:undolevels)
191
Bram Moolenaar170b10b2016-07-29 16:15:27 +0200192 " Drop created windows
193 set ul&
194 new
John Marriottfd1a8382024-11-06 21:21:50 +0100195 bw! one two
Bram Moolenaar170b10b2016-07-29 16:15:27 +0200196 only!
197endfunc
198
199func BackOne(expected)
200 call feedkeys('g-', 'xt')
201 call assert_equal(a:expected, getline(1))
202endfunc
203
204func Test_undo_del_chars()
205 " Setup a buffer without creating undo entries
206 new
207 set ul=-1
208 call setline(1, ['123-456'])
209 set ul=100
210 1
211 call test_settime(100)
212
213 " Delete three characters and undo with g-
214 call feedkeys('x', 'xt')
215 call feedkeys('x', 'xt')
216 call feedkeys('x', 'xt')
217 call assert_equal('-456', getline(1))
218 call BackOne('3-456')
219 call BackOne('23-456')
220 call BackOne('123-456')
221 call assert_fails("BackOne('123-456')")
222
223 :" Delete three other characters and go back in time with g-
224 call feedkeys('$x', 'xt')
225 call feedkeys('x', 'xt')
226 call feedkeys('x', 'xt')
227 call assert_equal('123-', getline(1))
228 call test_settime(101)
229
230 call BackOne('123-4')
231 call BackOne('123-45')
232 " skips '123-456' because it's older
233 call BackOne('-456')
234 call BackOne('3-456')
235 call BackOne('23-456')
236 call BackOne('123-456')
237 call assert_fails("BackOne('123-456')")
238 normal 10g+
239 call assert_equal('123-', getline(1))
240
241 :" Jump two seconds and go some seconds forward and backward
242 call test_settime(103)
243 call feedkeys("Aa\<Esc>", 'xt')
244 call feedkeys("Ab\<Esc>", 'xt')
245 call feedkeys("Ac\<Esc>", 'xt')
246 call assert_equal('123-abc', getline(1))
247 earlier 1s
248 call assert_equal('123-', getline(1))
249 earlier 3s
250 call assert_equal('123-456', getline(1))
251 later 1s
252 call assert_equal('123-', getline(1))
253 later 1h
254 call assert_equal('123-abc', getline(1))
255
John Marriottfd1a8382024-11-06 21:21:50 +0100256 bw!
Bram Moolenaar170b10b2016-07-29 16:15:27 +0200257endfunc
258
Bram Moolenaarc628fdc2016-08-31 20:33:27 +0200259func Test_undolist()
260 new
261 set ul=100
262
Bram Moolenaare5fa1112018-05-26 18:46:30 +0200263 let a = execute('undolist')
Bram Moolenaarc628fdc2016-08-31 20:33:27 +0200264 call assert_equal("\nNothing to undo", a)
265
266 " 1 leaf (2 changes).
267 call feedkeys('achange1', 'xt')
268 call feedkeys('achange2', 'xt')
Bram Moolenaare5fa1112018-05-26 18:46:30 +0200269 let a = execute('undolist')
Bram Moolenaarc628fdc2016-08-31 20:33:27 +0200270 call assert_match("^\nnumber changes when *saved\n *2 *2 .*$", a)
271
272 " 2 leaves.
273 call feedkeys('u', 'xt')
274 call feedkeys('achange3\<Esc>', 'xt')
Bram Moolenaare5fa1112018-05-26 18:46:30 +0200275 let a = execute('undolist')
Bram Moolenaarc628fdc2016-08-31 20:33:27 +0200276 call assert_match("^\nnumber changes when *saved\n *2 *2 *.*\n *3 *2 .*$", a)
John Marriottfd1a8382024-11-06 21:21:50 +0100277
278 " 3 save number
279 if has("persistent_undo")
280 setl undofile
281 w Xundolist.txt
282 defer delete('Xundolist.txt')
283 let lastline = execute('undolist')->split("\n")[-1]
Christian Brabandt381ff772024-12-16 22:28:28 +0100284 call assert_match('seconds\? ago \?1', lastline)
285
John Marriottfd1a8382024-11-06 21:21:50 +0100286 endif
287 bw!
Bram Moolenaarc628fdc2016-08-31 20:33:27 +0200288endfunc
289
290func Test_U_command()
291 new
292 set ul=100
293 call feedkeys("achange1\<Esc>", 'xt')
294 call feedkeys("achange2\<Esc>", 'xt')
295 norm! U
296 call assert_equal('', getline(1))
297 norm! U
298 call assert_equal('change1change2', getline(1))
John Marriottfd1a8382024-11-06 21:21:50 +0100299 bw!
Bram Moolenaarc628fdc2016-08-31 20:33:27 +0200300endfunc
301
Bram Moolenaar170b10b2016-07-29 16:15:27 +0200302func Test_undojoin()
303 new
304 call feedkeys("Goaaaa\<Esc>", 'xt')
305 call feedkeys("obbbb\<Esc>", 'xt')
306 call assert_equal(['aaaa', 'bbbb'], getline(2, '$'))
307 call feedkeys("u", 'xt')
308 call assert_equal(['aaaa'], getline(2, '$'))
309 call feedkeys("obbbb\<Esc>", 'xt')
310 undojoin
311 " Note: next change must not be as if typed
312 call feedkeys("occcc\<Esc>", 'x')
313 call assert_equal(['aaaa', 'bbbb', 'cccc'], getline(2, '$'))
314 call feedkeys("u", 'xt')
315 call assert_equal(['aaaa'], getline(2, '$'))
Bram Moolenaar5e4e1b12017-01-17 22:09:45 +0100316 bwipe!
317endfunc
318
319func Test_undojoin_redo()
320 new
321 call setline(1, ['first line', 'second line'])
322 call feedkeys("ixx\<Esc>", 'xt')
323 call feedkeys(":undojoin | redo\<CR>", 'xt')
324 call assert_equal('xxfirst line', getline(1))
325 call assert_equal('second line', getline(2))
326 bwipe!
Bram Moolenaar170b10b2016-07-29 16:15:27 +0200327endfunc
328
Bram Moolenaar559b9c62019-12-15 18:09:19 +0100329" undojoin not allowed after undo
330func Test_undojoin_after_undo()
331 new
332 call feedkeys("ixx\<Esc>u", 'xt')
333 call assert_fails(':undojoin', 'E790:')
334 bwipe!
335endfunc
336
337" undojoin is a noop when no change yet, or when 'undolevels' is negative
338func Test_undojoin_noop()
339 new
340 call feedkeys(":undojoin\<CR>", 'xt')
341 call assert_equal([''], getline(1, '$'))
342 setlocal undolevels=-1
343 call feedkeys("ixx\<Esc>u", 'xt')
344 call feedkeys(":undojoin\<CR>", 'xt')
345 call assert_equal(['xx'], getline(1, '$'))
346 bwipe!
347endfunc
348
Bram Moolenaar170b10b2016-07-29 16:15:27 +0200349func Test_undo_write()
Bram Moolenaar5842a742017-11-04 22:36:53 +0100350 call delete('Xtest')
Bram Moolenaar170b10b2016-07-29 16:15:27 +0200351 split Xtest
352 call feedkeys("ione one one\<Esc>", 'xt')
353 w!
354 call feedkeys("otwo\<Esc>", 'xt')
355 call feedkeys("otwo\<Esc>", 'xt')
356 w
357 call feedkeys("othree\<Esc>", 'xt')
358 call assert_equal(['one one one', 'two', 'two', 'three'], getline(1, '$'))
359 earlier 1f
360 call assert_equal(['one one one', 'two', 'two'], getline(1, '$'))
361 earlier 1f
362 call assert_equal(['one one one'], getline(1, '$'))
363 earlier 1f
364 call assert_equal([''], getline(1, '$'))
365 later 1f
366 call assert_equal(['one one one'], getline(1, '$'))
367 later 1f
368 call assert_equal(['one one one', 'two', 'two'], getline(1, '$'))
369 later 1f
370 call assert_equal(['one one one', 'two', 'two', 'three'], getline(1, '$'))
371
372 close!
373 call delete('Xtest')
374 bwipe! Xtest
Bram Moolenaar9f6277b2020-02-11 22:04:02 +0100375
376 call assert_fails('earlier xyz', 'E475:')
Bram Moolenaar170b10b2016-07-29 16:15:27 +0200377endfunc
378
379func Test_insert_expr()
380 new
381 " calling setline() triggers undo sync
382 call feedkeys("oa\<Esc>", 'xt')
383 call feedkeys("ob\<Esc>", 'xt')
384 set ul=100
385 call feedkeys("o1\<Esc>a2\<C-R>=setline('.','1234')\<CR>\<CR>\<Esc>", 'x')
386 call assert_equal(['a', 'b', '120', '34'], getline(2, '$'))
387 call feedkeys("u", 'x')
388 call assert_equal(['a', 'b', '12'], getline(2, '$'))
389 call feedkeys("u", 'x')
390 call assert_equal(['a', 'b'], getline(2, '$'))
391
392 call feedkeys("oc\<Esc>", 'xt')
393 set ul=100
394 call feedkeys("o1\<Esc>a2\<C-R>=setline('.','1234')\<CR>\<CR>\<Esc>", 'x')
395 call assert_equal(['a', 'b', 'c', '120', '34'], getline(2, '$'))
396 call feedkeys("u", 'x')
397 call assert_equal(['a', 'b', 'c', '12'], getline(2, '$'))
398
399 call feedkeys("od\<Esc>", 'xt')
400 set ul=100
401 call feedkeys("o1\<Esc>a2\<C-R>=string(123)\<CR>\<Esc>", 'x')
402 call assert_equal(['a', 'b', 'c', '12', 'd', '12123'], getline(2, '$'))
403 call feedkeys("u", 'x')
404 call assert_equal(['a', 'b', 'c', '12', 'd'], getline(2, '$'))
405
John Marriottfd1a8382024-11-06 21:21:50 +0100406 bw!
Bram Moolenaar170b10b2016-07-29 16:15:27 +0200407endfunc
Bram Moolenaarcbd4de42017-01-07 16:14:57 +0100408
409func Test_undofile_earlier()
K.Takata0500e872022-09-08 12:28:02 +0100410 if has('win32')
411 " FIXME: This test is flaky on MS-Windows.
412 let g:test_is_flaky = 1
413 endif
414
Bram Moolenaarcbd4de42017-01-07 16:14:57 +0100415 " Issue #1254
416 " create undofile with timestamps older than Vim startup time.
417 let t0 = localtime() - 43200
418 call test_settime(t0)
Bram Moolenaarcce293f2022-08-15 17:28:27 +0100419 new XfileEarlier
Bram Moolenaarcbd4de42017-01-07 16:14:57 +0100420 call feedkeys("ione\<Esc>", 'xt')
421 set ul=100
422 call test_settime(t0 + 1)
423 call feedkeys("otwo\<Esc>", 'xt')
424 set ul=100
425 call test_settime(t0 + 2)
426 call feedkeys("othree\<Esc>", 'xt')
427 set ul=100
428 w
429 wundo Xundofile
430 bwipe!
431 " restore normal timestamps.
432 call test_settime(0)
Bram Moolenaarcce293f2022-08-15 17:28:27 +0100433 new XfileEarlier
Bram Moolenaarcbd4de42017-01-07 16:14:57 +0100434 rundo Xundofile
435 earlier 1d
436 call assert_equal('', getline(1))
437 bwipe!
Bram Moolenaarcce293f2022-08-15 17:28:27 +0100438 call delete('XfileEarlier')
Bram Moolenaarcbd4de42017-01-07 16:14:57 +0100439 call delete('Xundofile')
440endfunc
Bram Moolenaar15993ce2017-10-26 20:21:44 +0200441
Bram Moolenaar559b9c62019-12-15 18:09:19 +0100442func Test_wundo_errors()
443 new
444 call setline(1, 'hello')
445 call assert_fails('wundo! Xdoesnotexist/Xundofile', 'E828:')
446 bwipe!
447endfunc
448
449" Check that reading a truncated undo file doesn't hang.
Bram Moolenaarfb06d762019-08-04 18:55:35 +0200450func Test_undofile_truncated()
451 new
452 call setline(1, 'hello')
453 set ul=100
454 wundo Xundofile
455 let contents = readfile('Xundofile', 'B')
456
457 " try several sizes
458 for size in range(20, 500, 33)
Bram Moolenaar5b148ef2022-10-15 21:35:56 +0100459 call writefile(contents[0:size], 'Xundofile', 'D')
Bram Moolenaarfb06d762019-08-04 18:55:35 +0200460 call assert_fails('rundo Xundofile', 'E825:')
461 endfor
462
463 bwipe!
Bram Moolenaarfb06d762019-08-04 18:55:35 +0200464endfunc
465
Bram Moolenaar559b9c62019-12-15 18:09:19 +0100466func Test_rundo_errors()
467 call assert_fails('rundo XfileDoesNotExist', 'E822:')
468
Bram Moolenaar5b148ef2022-10-15 21:35:56 +0100469 call writefile(['abc'], 'Xundofile', 'D')
Bram Moolenaar559b9c62019-12-15 18:09:19 +0100470 call assert_fails('rundo Xundofile', 'E823:')
Bram Moolenaar559b9c62019-12-15 18:09:19 +0100471endfunc
472
Bram Moolenaar55b419b2020-10-04 19:56:39 +0200473func Test_undofile_next()
474 set undofile
475 new Xfoo.txt
476 execute "norm ix\<c-g>uy\<c-g>uz\<Esc>"
477 write
478 bwipe
479
480 next Xfoo.txt
481 call assert_equal('xyz', getline(1))
482 silent undo
483 call assert_equal('xy', getline(1))
484 silent undo
485 call assert_equal('x', getline(1))
486 bwipe!
487
488 call delete('Xfoo.txt')
489 call delete('.Xfoo.txt.un~')
490 set undofile&
491endfunc
492
Bram Moolenaar15993ce2017-10-26 20:21:44 +0200493" Test for undo working properly when executing commands from a register.
494" Also test this in an empty buffer.
495func Test_cmd_in_reg_undo()
496 enew!
Bram Moolenaare5fa1112018-05-26 18:46:30 +0200497 let @a = "Ox\<Esc>jAy\<Esc>kdd"
Bram Moolenaar15993ce2017-10-26 20:21:44 +0200498 edit +/^$ test_undo.vim
499 normal @au
500 call assert_equal(0, &modified)
501 return
502 new
503 normal @au
504 call assert_equal(0, &modified)
505 only!
Bram Moolenaare5fa1112018-05-26 18:46:30 +0200506 let @a = ''
Bram Moolenaar15993ce2017-10-26 20:21:44 +0200507endfunc
Bram Moolenaar95dbcbe2018-01-27 21:01:34 +0100508
509" This used to cause an illegal memory access
510func Test_undo_append()
511 new
512 call feedkeys("axx\<Esc>v", 'xt')
513 undo
514 norm o
515 quit
516endfunc
Bram Moolenaarce46d932018-01-30 22:46:06 +0100517
518func Test_undo_0()
519 new
520 set ul=100
521 normal i1
522 undo
523 normal i2
524 undo
525 normal i3
526
527 undo 0
528 let d = undotree()
529 call assert_equal('', getline(1))
530 call assert_equal(0, d.seq_cur)
531
532 redo
533 let d = undotree()
534 call assert_equal('3', getline(1))
535 call assert_equal(3, d.seq_cur)
536
537 undo 2
538 undo 0
539 let d = undotree()
540 call assert_equal('', getline(1))
541 call assert_equal(0, d.seq_cur)
542
543 redo
544 let d = undotree()
545 call assert_equal('2', getline(1))
546 call assert_equal(2, d.seq_cur)
547
548 undo 1
549 undo 0
550 let d = undotree()
551 call assert_equal('', getline(1))
552 call assert_equal(0, d.seq_cur)
553
554 redo
555 let d = undotree()
556 call assert_equal('1', getline(1))
557 call assert_equal(1, d.seq_cur)
558
559 bwipe!
560endfunc
Bram Moolenaarf12519d2018-02-06 22:52:49 +0100561
Bram Moolenaar559b9c62019-12-15 18:09:19 +0100562" undo or redo are noop if there is nothing to undo or redo
563func Test_undo_redo_noop()
564 new
565 call assert_fails('undo 2', 'E830:')
566
567 message clear
568 undo
569 let messages = split(execute('message'), "\n")
570 call assert_equal('Already at oldest change', messages[-1])
571
572 message clear
573 redo
574 let messages = split(execute('message'), "\n")
575 call assert_equal('Already at newest change', messages[-1])
576
577 bwipe!
578endfunc
579
Bram Moolenaarf12519d2018-02-06 22:52:49 +0100580func Test_redo_empty_line()
581 new
582 exe "norm\x16r\x160"
583 exe "norm."
584 bwipe!
585endfunc
Bram Moolenaare5fa1112018-05-26 18:46:30 +0200586
587funct Test_undofile()
588 " Test undofile() without setting 'undodir'.
589 if has('persistent_undo')
590 call assert_equal(fnamemodify('.Xundofoo.un~', ':p'), undofile('Xundofoo'))
591 else
592 call assert_equal('', undofile('Xundofoo'))
593 endif
594 call assert_equal('', undofile(''))
595
zeertzjq0df8f932024-03-07 21:40:53 +0100596 " Test undofile() with 'undodir' set to an existing directory.
Bram Moolenaare5fa1112018-05-26 18:46:30 +0200597 call mkdir('Xundodir')
598 set undodir=Xundodir
599 let cwd = getcwd()
600 if has('win32')
601 " Replace windows drive such as C:... into C%...
Bram Moolenaar56242f22018-12-14 15:48:48 +0100602 let cwd = substitute(cwd, '^\([a-zA-Z]\):', '\1%', 'g')
Bram Moolenaare5fa1112018-05-26 18:46:30 +0200603 endif
604 let cwd = substitute(cwd . '/Xundofoo', '/', '%', 'g')
605 if has('persistent_undo')
606 call assert_equal('Xundodir/' . cwd, undofile('Xundofoo'))
607 else
608 call assert_equal('', undofile('Xundofoo'))
609 endif
610 call assert_equal('', undofile(''))
611 call delete('Xundodir', 'd')
612
613 " Test undofile() with 'undodir' set to a non-existing directory.
Bram Moolenaarf92e58c2019-09-08 21:51:41 +0200614 call assert_equal('', 'Xundofoo'->undofile())
Bram Moolenaare5fa1112018-05-26 18:46:30 +0200615
Bram Moolenaar077ff432019-10-28 00:42:21 +0100616 if !has('win32') && isdirectory('/tmp')
Bram Moolenaare9ebc9a2019-05-19 15:27:14 +0200617 set undodir=/tmp
Bram Moolenaar2b39d802019-05-19 16:38:56 +0200618 if has('osx')
619 call assert_equal('/tmp/%private%tmp%file', undofile('///tmp/file'))
620 else
621 call assert_equal('/tmp/%tmp%file', undofile('///tmp/file'))
622 endif
Bram Moolenaare9ebc9a2019-05-19 15:27:14 +0200623 endif
624
Bram Moolenaare5fa1112018-05-26 18:46:30 +0200625 set undodir&
626endfunc
Bram Moolenaar3e2d1c82019-12-14 20:35:01 +0100627
628" Tests for the undo file
629" Explicitly break changes up in undo-able pieces by setting 'undolevels'.
630func Test_undofile_2()
631 set undolevels=100 undofile
632 edit Xtestfile
633 call append(0, 'this is one line')
634 call cursor(1, 1)
635
636 " first a simple one-line change.
637 set undolevels=100
638 s/one/ONE/
639 set undolevels=100
640 write
641 bwipe!
642 edit Xtestfile
643 undo
644 call assert_equal('this is one line', getline(1))
645
646 " change in original file fails check
647 set noundofile
648 edit! Xtestfile
649 s/line/Line/
650 write
651 set undofile
652 bwipe!
653 edit Xtestfile
654 undo
655 call assert_equal('this is ONE Line', getline(1))
656
657 " add 10 lines, delete 6 lines, undo 3
658 set undofile
Bram Moolenaarb8bd2e62021-08-21 17:13:14 +0200659 call setbufline('%', 1, ['one', 'two', 'three', 'four', 'five', 'six',
Bram Moolenaar3e2d1c82019-12-14 20:35:01 +0100660 \ 'seven', 'eight', 'nine', 'ten'])
661 set undolevels=100
662 normal 3Gdd
663 set undolevels=100
664 normal dd
665 set undolevels=100
666 normal dd
667 set undolevels=100
668 normal dd
669 set undolevels=100
670 normal dd
671 set undolevels=100
672 normal dd
673 set undolevels=100
674 write
675 bwipe!
676 edit Xtestfile
677 normal uuu
678 call assert_equal(['one', 'two', 'six', 'seven', 'eight', 'nine', 'ten'],
679 \ getline(1, '$'))
680
681 " Test that reading the undofiles when setting undofile works
682 set noundofile undolevels=0
683 exe "normal i\n"
684 undo
685 edit! Xtestfile
686 set undofile undolevels=100
687 normal uuuuuu
688 call assert_equal(['one', 'two', 'three', 'four', 'five', 'six', 'seven',
689 \ 'eight', 'nine', 'ten'], getline(1, '$'))
690
691 bwipe!
692 call delete('Xtestfile')
693 let ufile = has('vms') ? '_un_Xtestfile' : '.Xtestfile.un~'
694 call delete(ufile)
695 set undofile& undolevels&
696endfunc
697
698" Test 'undofile' using a file encrypted with 'zip' crypt method
699func Test_undofile_cryptmethod_zip()
700 edit Xtestfile
701 set undofile cryptmethod=zip
702 call append(0, ['monday', 'tuesday', 'wednesday', 'thursday', 'friday'])
703 call cursor(5, 1)
704
705 set undolevels=100
706 normal kkkdd
707 set undolevels=100
708 normal dd
709 set undolevels=100
710 normal dd
711 set undolevels=100
712 " encrypt the file using key 'foobar'
713 call feedkeys("foobar\nfoobar\n")
714 X
715 write!
716 bwipe!
717
718 call feedkeys("foobar\n")
719 edit Xtestfile
720 set key=
721 normal uu
722 call assert_equal(['monday', 'wednesday', 'thursday', 'friday', ''],
723 \ getline(1, '$'))
724
725 bwipe!
726 call delete('Xtestfile')
727 let ufile = has('vms') ? '_un_Xtestfile' : '.Xtestfile.un~'
728 call delete(ufile)
729 set undofile& undolevels& cryptmethod&
730endfunc
731
732" Test 'undofile' using a file encrypted with 'blowfish' crypt method
733func Test_undofile_cryptmethod_blowfish()
734 edit Xtestfile
735 set undofile cryptmethod=blowfish
736 call append(0, ['jan', 'feb', 'mar', 'apr', 'jun'])
737 call cursor(5, 1)
738
739 set undolevels=100
740 exe 'normal kk0ifoo '
741 set undolevels=100
742 normal dd
743 set undolevels=100
744 exe 'normal ibar '
745 set undolevels=100
746 " encrypt the file using key 'foobar'
747 call feedkeys("foobar\nfoobar\n")
748 X
749 write!
750 bwipe!
751
752 call feedkeys("foobar\n")
753 edit Xtestfile
754 set key=
755 call search('bar')
756 call assert_equal('bar apr', getline('.'))
757 undo
758 call assert_equal('apr', getline('.'))
759 undo
760 call assert_equal('foo mar', getline('.'))
761 undo
762 call assert_equal('mar', getline('.'))
763
764 bwipe!
765 call delete('Xtestfile')
766 let ufile = has('vms') ? '_un_Xtestfile' : '.Xtestfile.un~'
767 call delete(ufile)
768 set undofile& undolevels& cryptmethod&
769endfunc
770
771" Test 'undofile' using a file encrypted with 'blowfish2' crypt method
772func Test_undofile_cryptmethod_blowfish2()
773 edit Xtestfile
774 set undofile cryptmethod=blowfish2
775 call append(0, ['jan', 'feb', 'mar', 'apr', 'jun'])
776 call cursor(5, 1)
777
778 set undolevels=100
779 exe 'normal kk0ifoo '
780 set undolevels=100
781 normal dd
782 set undolevels=100
783 exe 'normal ibar '
784 set undolevels=100
785 " encrypt the file using key 'foo2bar'
786 call feedkeys("foo2bar\nfoo2bar\n")
787 X
788 write!
789 bwipe!
790
791 call feedkeys("foo2bar\n")
792 edit Xtestfile
793 set key=
794 call search('bar')
795 call assert_equal('bar apr', getline('.'))
796 normal u
797 call assert_equal('apr', getline('.'))
798 normal u
799 call assert_equal('foo mar', getline('.'))
800 normal u
801 call assert_equal('mar', getline('.'))
802
803 bwipe!
804 call delete('Xtestfile')
805 let ufile = has('vms') ? '_un_Xtestfile' : '.Xtestfile.un~'
806 call delete(ufile)
807 set undofile& undolevels& cryptmethod&
808endfunc
809
Bram Moolenaarf4fcedc2021-03-15 18:36:20 +0100810" Test for redoing with incrementing numbered registers
811func Test_redo_repeat_numbered_register()
812 new
813 for [i, v] in [[1, 'one'], [2, 'two'], [3, 'three'],
814 \ [4, 'four'], [5, 'five'], [6, 'six'],
815 \ [7, 'seven'], [8, 'eight'], [9, 'nine']]
816 exe 'let @' .. i .. '="' .. v .. '\n"'
817 endfor
818 call feedkeys('"1p.........', 'xt')
819 call assert_equal(['', 'one', 'two', 'three', 'four', 'five', 'six',
820 \ 'seven', 'eight', 'nine', 'nine'], getline(1, '$'))
821 bwipe!
822endfunc
823
Bram Moolenaar1f448d92021-03-22 19:37:06 +0100824" Test for redo in insert mode using CTRL-O with multibyte characters
825func Test_redo_multibyte_in_insert_mode()
826 new
827 call feedkeys("a\<C-K>ft", 'xt')
828 call feedkeys("uiHe\<C-O>.llo", 'xt')
829 call assert_equal("He\ufb05llo", getline(1))
830 bwipe!
831endfunc
832
LemonBoy82444ce2022-05-12 15:39:31 +0100833func Test_undo_mark()
834 new
835 " The undo is applied to the only line.
836 call setline(1, 'hello')
837 call feedkeys("ggyiw$p", 'xt')
838 undo
839 call assert_equal([0, 1, 1, 0], getpos("'["))
840 call assert_equal([0, 1, 1, 0], getpos("']"))
841 " The undo removes the last line.
842 call feedkeys("Goaaaa\<Esc>", 'xt')
843 call feedkeys("obbbb\<Esc>", 'xt')
844 undo
845 call assert_equal([0, 2, 1, 0], getpos("'["))
846 call assert_equal([0, 2, 1, 0], getpos("']"))
847 bwipe!
848endfunc
849
Bram Moolenaar3f8f8272022-12-08 21:49:35 +0000850func Test_undo_after_write()
Drew Vogelea67ba72025-05-07 22:05:17 +0200851 CheckScreendump
Bram Moolenaar3f8f8272022-12-08 21:49:35 +0000852 " use a terminal to make undo work like when text is typed
853 CheckRunVimInTerminal
854
855 let lines =<< trim END
856 edit Xtestfile.txt
857 set undolevels=100 undofile
858 imap . <Cmd>write<CR>
859 write
860 END
861 call writefile(lines, 'Xtest_undo_after_write', 'D')
862 let buf = RunVimInTerminal('-S Xtest_undo_after_write', #{rows: 6})
863
864 call term_sendkeys(buf, "Otest.\<CR>boo!!!\<Esc>")
865 sleep 100m
866 call term_sendkeys(buf, "u")
867 call VerifyScreenDump(buf, 'Test_undo_after_write_1', {})
868
869 call term_sendkeys(buf, "u")
870 call VerifyScreenDump(buf, 'Test_undo_after_write_2', {})
871
872 call StopVimInTerminal(buf)
873 call delete('Xtestfile.txt')
Dominique Pelléb07b9dc2023-10-09 18:07:24 +0200874 call delete('.Xtestfile.txt.un~')
Bram Moolenaar3f8f8272022-12-08 21:49:35 +0000875endfunc
876
zeertzjqdccc29c2023-09-04 22:25:07 +0200877func Test_undo_range_normal()
878 new
879 call setline(1, ['asa', 'bsb'])
880 let &l:undolevels = &l:undolevels
881 %normal dfs
882 call assert_equal(['a', 'b'], getline(1, '$'))
883 undo
884 call assert_equal(['asa', 'bsb'], getline(1, '$'))
885 bwipe!
886endfunc
Bram Moolenaar3f8f8272022-12-08 21:49:35 +0000887
Christian Brabandt14382c82024-11-28 21:59:33 +0100888func Test_load_existing_undofile()
889 CheckFeature persistent_undo
890 sp samples/test_undo.txt
891 let mess=execute(':verbose rundo samples/test_undo.txt.undo')
892 call assert_match('Finished reading undo file', mess)
893
894 call assert_equal(['one', 'two', 'three'], getline(1, '$'))
895 norm! u
896 call assert_equal(['one', 'two'], getline(1, '$'))
897 norm! u
898 call assert_equal(['one'], getline(1, '$'))
899 norm! u
900 call assert_equal([''], getline(1, '$'))
901 let mess = execute(':norm! u')
902 call assert_equal([''], getline(1, '$'))
903 call assert_match('Already at oldest change', mess)
904 exe ":norm! \<c-r>"
905 call assert_equal(['one'], getline(1, '$'))
906 exe ":norm! \<c-r>"
907 call assert_equal(['one', 'two'], getline(1, '$'))
908 exe ":norm! \<c-r>"
909 call assert_equal(['one', 'two', 'three'], getline(1, '$'))
910 let mess = execute(":norm! \<c-r>")
911 call assert_equal(['one', 'two', 'three'], getline(1, '$'))
912 call assert_match('Already at newest change', mess)
913 bw!
914endfunc
915
916
Bram Moolenaar3e2d1c82019-12-14 20:35:01 +0100917" vim: shiftwidth=2 sts=2 expandtab