1" tests for listener_add() and listener_remove()
2
3func s:StoreList(s, e, a, l)
4  let s:start = a:s
5  let s:end = a:e
6  let s:added = a:a
7  let s:text = getline(a:s)
8  let s:list = a:l
9endfunc
10
11func s:AnotherStoreList(l)
12  let s:list2 = a:l
13endfunc
14
15func s:EvilStoreList(l)
16  let s:list3 = a:l
17  call assert_fails("call add(a:l, 'myitem')", "E742:")
18endfunc
19
20func Test_listening()
21  new
22  call setline(1, ['one', 'two'])
23  let s:list = []
24  let id = listener_add({b, s, e, a, l -> s:StoreList(s, e, a, l)})
25  call setline(1, 'one one')
26  call listener_flush()
27  call assert_equal([{'lnum': 1, 'end': 2, 'col': 1, 'added': 0}], s:list)
28
29  " Undo is also a change
30  set undolevels&  " start new undo block
31  call append(2, 'two two')
32  undo
33  call assert_equal([{'lnum': 3, 'end': 3, 'col': 1, 'added': 1}], s:list)
34  redraw
35  " the two changes are not merged
36  call assert_equal([{'lnum': 3, 'end': 4, 'col': 1, 'added': -1}], s:list)
37  1
38
39  " Two listeners, both get called.  Also check column.
40  call setline(1, ['one one', 'two'])
41  call listener_flush()
42  let id2 = listener_add({b, s, e, a, l -> s:AnotherStoreList(l)})
43  let s:list = []
44  let s:list2 = []
45  exe "normal $asome\<Esc>"
46  redraw
47  call assert_equal([{'lnum': 1, 'end': 2, 'col': 8, 'added': 0}], s:list)
48  call assert_equal([{'lnum': 1, 'end': 2, 'col': 8, 'added': 0}], s:list2)
49
50  " removing listener works
51  call listener_remove(id2)
52  call setline(1, ['one one', 'two'])
53  call listener_flush()
54  let s:list = []
55  let s:list2 = []
56  call setline(3, 'three')
57  redraw
58  call assert_equal([{'lnum': 3, 'end': 3, 'col': 1, 'added': 1}], s:list)
59  call assert_equal([], s:list2)
60
61  " a change above a previous change without a line number change is reported
62  " together
63  call setline(1, ['one one', 'two'])
64  call listener_flush(bufnr())
65  call append(2, 'two two')
66  call setline(1, 'something')
67  call bufnr()->listener_flush()
68  call assert_equal([{'lnum': 3, 'end': 3, 'col': 1, 'added': 1},
69	\ {'lnum': 1, 'end': 2, 'col': 1, 'added': 0}], s:list)
70  call assert_equal(1, s:start)
71  call assert_equal(3, s:end)
72  call assert_equal(1, s:added)
73
74  " an insert just above a previous change that was the last one does not get
75  " merged
76  call setline(1, ['one one', 'two'])
77  call listener_flush()
78  let s:list = []
79  call setline(2, 'something')
80  call append(1, 'two two')
81  call assert_equal([{'lnum': 2, 'end': 3, 'col': 1, 'added': 0}], s:list)
82  call listener_flush()
83  call assert_equal([{'lnum': 2, 'end': 2, 'col': 1, 'added': 1}], s:list)
84
85  " an insert above a previous change causes a flush
86  call setline(1, ['one one', 'two'])
87  call listener_flush()
88  call setline(2, 'something')
89  call append(0, 'two two')
90  call assert_equal([{'lnum': 2, 'end': 3, 'col': 1, 'added': 0}], s:list)
91  call assert_equal('something', s:text)
92  call listener_flush()
93  call assert_equal([{'lnum': 1, 'end': 1, 'col': 1, 'added': 1}], s:list)
94  call assert_equal('two two', s:text)
95
96  " a delete at a previous change that was the last one does not get merged
97  call setline(1, ['one one', 'two'])
98  call listener_flush()
99  let s:list = []
100  call setline(2, 'something')
101  2del
102  call assert_equal([{'lnum': 2, 'end': 3, 'col': 1, 'added': 0}], s:list)
103  call listener_flush()
104  call assert_equal([{'lnum': 2, 'end': 3, 'col': 1, 'added': -1}], s:list)
105
106  " a delete above a previous change causes a flush
107  call setline(1, ['one one', 'two'])
108  call listener_flush()
109  call setline(2, 'another')
110  1del
111  call assert_equal([{'lnum': 2, 'end': 3, 'col': 1, 'added': 0}], s:list)
112  call assert_equal(2, s:start)
113  call assert_equal('another', s:text)
114  call listener_flush()
115  call assert_equal([{'lnum': 1, 'end': 2, 'col': 1, 'added': -1}], s:list)
116  call assert_equal('another', s:text)
117
118  " the "o" command first adds an empty line and then changes it
119  %del
120  call setline(1, ['one one', 'two'])
121  call listener_flush()
122  let s:list = []
123  exe "normal Gofour\<Esc>"
124  redraw
125  call assert_equal([{'lnum': 3, 'end': 3, 'col': 1, 'added': 1},
126	\ {'lnum': 3, 'end': 4, 'col': 1, 'added': 0}], s:list)
127
128  " Remove last listener
129  let s:list = []
130  call listener_remove(id)
131  call setline(1, 'asdfasdf')
132  redraw
133  call assert_equal([], s:list)
134
135  " Trying to change the list fails
136  let id = listener_add({b, s, e, a, l -> s:EvilStoreList(l)})
137  let s:list3 = []
138  call setline(1, 'asdfasdf')
139  redraw
140  call assert_equal([{'lnum': 1, 'end': 2, 'col': 1, 'added': 0}], s:list3)
141
142  eval id->listener_remove()
143  bwipe!
144endfunc
145
146func s:StoreListArgs(buf, start, end, added, list)
147  let s:buf = a:buf
148  let s:start = a:start
149  let s:end = a:end
150  let s:added = a:added
151  let s:list = a:list
152endfunc
153
154func Test_listener_args()
155  new
156  call setline(1, ['one', 'two'])
157  let s:list = []
158  let id = listener_add('s:StoreListArgs')
159
160  " just one change
161  call setline(1, 'one one')
162  call listener_flush()
163  call assert_equal(bufnr(''), s:buf)
164  call assert_equal(1, s:start)
165  call assert_equal(2, s:end)
166  call assert_equal(0, s:added)
167  call assert_equal([{'lnum': 1, 'end': 2, 'col': 1, 'added': 0}], s:list)
168
169  " two disconnected changes
170  call setline(1, ['one', 'two', 'three', 'four'])
171  call listener_flush()
172  call setline(1, 'one one')
173  call setline(3, 'three three')
174  call listener_flush()
175  call assert_equal(bufnr(''), s:buf)
176  call assert_equal(1, s:start)
177  call assert_equal(4, s:end)
178  call assert_equal(0, s:added)
179  call assert_equal([{'lnum': 1, 'end': 2, 'col': 1, 'added': 0},
180	\ {'lnum': 3, 'end': 4, 'col': 1, 'added': 0}], s:list)
181
182  " add and remove lines
183  call setline(1, ['one', 'two', 'three', 'four', 'five', 'six'])
184  call listener_flush()
185  call append(2, 'two two')
186  4del
187  call append(5, 'five five')
188  call listener_flush()
189  call assert_equal(bufnr(''), s:buf)
190  call assert_equal(3, s:start)
191  call assert_equal(6, s:end)
192  call assert_equal(1, s:added)
193  call assert_equal([{'lnum': 3, 'end': 3, 'col': 1, 'added': 1},
194	\ {'lnum': 4, 'end': 5, 'col': 1, 'added': -1},
195	\ {'lnum': 6, 'end': 6, 'col': 1, 'added': 1}], s:list)
196
197  " split a line then insert one, should get two disconnected change lists
198  call setline(1, 'split here')
199  call listener_flush()
200  let s:list = []
201  exe "normal 1ggwi\<CR>\<Esc>"
202  1
203  normal o
204  call assert_equal([{'lnum': 1, 'end': 2, 'col': 7, 'added': 1}], s:list)
205  call listener_flush()
206  call assert_equal([{'lnum': 2, 'end': 2, 'col': 1, 'added': 1}], s:list)
207
208  call listener_remove(id)
209  bwipe!
210
211  " Invalid arguments
212  call assert_fails('call listener_add([])', 'E921:')
213  call assert_fails('call listener_add("s:StoreListArgs", [])', 'E730:')
214  call assert_fails('call listener_flush([])', 'E730:')
215endfunc
216
217func s:StoreBufList(buf, start, end, added, list)
218  let s:bufnr = a:buf
219  let s:list = a:list
220endfunc
221
222func Test_listening_other_buf()
223  new
224  call setline(1, ['one', 'two'])
225  let bufnr = bufnr('')
226  normal ww
227  let id = bufnr->listener_add(function('s:StoreBufList'))
228  let s:list = []
229  call setbufline(bufnr, 1, 'hello')
230  redraw
231  call assert_equal(bufnr, s:bufnr)
232  call assert_equal([{'lnum': 1, 'end': 2, 'col': 1, 'added': 0}], s:list)
233
234  call listener_remove(id)
235  exe "buf " .. bufnr
236  bwipe!
237endfunc
238
239func Test_listener_garbage_collect()
240  func MyListener(x, bufnr, start, end, added, changes)
241    " NOP
242  endfunc
243
244  new
245  let id = listener_add(function('MyListener', [{}]), bufnr(''))
246  call test_garbagecollect_now()
247  " must not crash caused by invalid memory access
248  normal ia
249  call assert_true(v:true)
250
251  call listener_remove(id)
252  delfunc MyListener
253  bwipe!
254endfunc
255
256" This verifies the fix for issue #4455
257func Test_listener_caches_buffer_line()
258  new
259  inoremap <silent> <CR> <CR><Esc>O
260
261  function EchoChanges(bufnr, start, end, added, changes)
262    for l:change in a:changes
263      let text = getbufline(a:bufnr, l:change.lnum, l:change.end-1+l:change.added)
264    endfor
265  endfunction
266  let lid = listener_add("EchoChanges")
267  set autoindent
268  set cindent
269
270  call setline(1, ["{", "\tif true {}", "}"])
271  exe "normal /{}\nl"
272  call feedkeys("i\r\e", 'xt')
273  call assert_equal(["{", "\tif true {", "", "\t}", "}"], getline(1, 5))
274
275  bwipe!
276  delfunc EchoChanges
277  call listener_remove(lid)
278  iunmap <CR>
279  set nocindent
280endfunc
281
282" Verify the fix for issue #4908
283func Test_listener_undo_line_number()
284  function DoIt()
285    " NOP
286  endfunction
287  function EchoChanges(bufnr, start, end, added, changes)
288    call DoIt()
289  endfunction
290
291  new
292  let lid = listener_add("EchoChanges")
293  call setline(1, ['a', 'b', 'c'])
294  set undolevels&  " start new undo block
295  call feedkeys("ggcG\<Esc>", 'xt')
296  undo
297
298  bwipe!
299  delfunc DoIt
300  delfunc EchoChanges
301  call listener_remove(lid)
302endfunc
303
304func Test_listener_undo_delete_all()
305  new
306  call setline(1, [1, 2, 3, 4])
307  let s:changes = []
308  func s:ExtendList(bufnr, start, end, added, changes)
309    call extend(s:changes, a:changes)
310  endfunc
311  let id = listener_add('s:ExtendList')
312
313  set undolevels&  " start new undo block
314  normal! ggdG
315  undo
316  call listener_flush()
317  call assert_equal(2, s:changes->len())
318  " delete removes four lines, empty line remains
319  call assert_equal({'lnum': 1, 'end': 5, 'col': 1, 'added': -4}, s:changes[0])
320  " undo replaces empty line and adds 3 lines
321  call assert_equal({'lnum': 1, 'end': 2, 'col': 1, 'added': 3}, s:changes[1])
322
323  call listener_remove(id)
324  delfunc s:ExtendList
325  unlet s:changes
326  bwipe!
327endfunc
328
329func Test_listener_cleared_newbuf()
330  func Listener(bufnr, start, end, added, changes)
331    let g:gotCalled += 1
332  endfunc
333  new
334  " check that listening works
335  let g:gotCalled = 0
336  let lid = listener_add("Listener")
337  call feedkeys("axxx\<Esc>", 'xt')
338  call listener_flush(bufnr())
339  call assert_equal(1, g:gotCalled)
340  %bwipe!
341  let bufnr = bufnr()
342  let b:testing = 123
343  let lid = listener_add("Listener")
344  enew!
345  " check buffer is reused
346  call assert_equal(bufnr, bufnr())
347  call assert_false(exists('b:testing'))
348
349  " check that listening stops when reusing the buffer
350  let g:gotCalled = 0
351  call feedkeys("axxx\<Esc>", 'xt')
352  call listener_flush(bufnr())
353  call assert_equal(0, g:gotCalled)
354  unlet g:gotCalled
355
356  bwipe!
357  delfunc Listener
358endfunc
359
360func Test_col_after_deletion_moved_cur()
361  func Listener(bufnr, start, end, added, changes)
362    call assert_equal([#{lnum: 1, end: 2, added: 0, col: 2}], a:changes)
363  endfunc
364  new
365  call setline(1, ['foo'])
366  let lid = listener_add('Listener')
367  call feedkeys("lD", 'xt')
368  call listener_flush()
369  bwipe!
370  delfunc Listener
371endfunc
372
373func Test_remove_listener_in_callback()
374  new
375  let s:ID = listener_add('Listener')
376  func Listener(...)
377    call listener_remove(s:ID)
378    let g:listener_called = 'yes'
379  endfunc
380  call setline(1, ['foo'])
381  call feedkeys("lD", 'xt')
382  call listener_flush()
383  call assert_equal('yes', g:listener_called)
384
385  bwipe!
386  delfunc Listener
387  unlet g:listener_called
388endfunc
389
390
391" vim: shiftwidth=2 sts=2 expandtab
392