1" Tests for popup windows
2
3source check.vim
4CheckFeature popupwin
5
6source screendump.vim
7
8func Test_simple_popup()
9  CheckScreendump
10
11  let lines =<< trim END
12	call setline(1, range(1, 100))
13	hi PopupColor1 ctermbg=lightblue
14	hi PopupColor2 ctermbg=lightcyan
15	hi EndOfBuffer ctermbg=lightgrey
16	hi Comment ctermfg=red
17	call prop_type_add('comment', #{highlight: 'Comment'})
18	let winid = popup_create('hello there', #{line: 3, col: 11, minwidth: 20, highlight: 'PopupColor1'})
19	let winid2 = popup_create(['another one', 'another two', 'another three'], #{line: 3, col: 25, minwidth: 20})
20	call setwinvar(winid2, '&wincolor', 'PopupColor2')
21  END
22  call writefile(lines, 'XtestPopup')
23  let buf = RunVimInTerminal('-S XtestPopup', #{rows: 10})
24  call VerifyScreenDump(buf, 'Test_popupwin_01', {})
25
26  " Add a tabpage
27  call term_sendkeys(buf, ":tabnew\<CR>")
28  call term_sendkeys(buf, ":let popupwin = popup_create(["
29	\ .. "#{text: 'other tab'},"
30	\ .. "#{text: 'a comment line', props: [#{"
31	\ .. "col: 3, length: 7, minwidth: 20, type: 'comment'"
32	\ .. "}]},"
33	\ .. "], #{line: 4, col: 9, minwidth: 20})\<CR>")
34  call VerifyScreenDump(buf, 'Test_popupwin_02', {})
35
36  " switch back to first tabpage
37  call term_sendkeys(buf, "gt")
38  call VerifyScreenDump(buf, 'Test_popupwin_03', {})
39
40  " close that tabpage
41  call term_sendkeys(buf, ":quit!\<CR>")
42  call VerifyScreenDump(buf, 'Test_popupwin_04', {})
43
44  " set 'columns' to a small value, size must be recomputed
45  call term_sendkeys(buf, ":let cols = &columns\<CR>")
46  call term_sendkeys(buf, ":set columns=12\<CR>")
47  call VerifyScreenDump(buf, 'Test_popupwin_04a', {})
48  call term_sendkeys(buf, ":let &columns = cols\<CR>")
49
50  " resize popup, show empty line at bottom
51  call term_sendkeys(buf, ":call popup_move(popupwin, #{minwidth: 15, maxwidth: 25, minheight: 3, maxheight: 5})\<CR>")
52  call term_sendkeys(buf, ":redraw\<CR>")
53  call VerifyScreenDump(buf, 'Test_popupwin_05', {})
54
55  " show not fitting line at bottom
56  call term_sendkeys(buf, ":call setbufline(winbufnr(popupwin), 3, 'this line will not fit here')\<CR>")
57  call term_sendkeys(buf, ":redraw\<CR>")
58  call VerifyScreenDump(buf, 'Test_popupwin_06', {})
59
60  " move popup over ruler
61  call term_sendkeys(buf, ":set cmdheight=2\<CR>")
62  call term_sendkeys(buf, ":call popup_move(popupwin, #{line: 7, col: 55})\<CR>")
63  call term_sendkeys(buf, ":\<CR>")
64  call VerifyScreenDump(buf, 'Test_popupwin_07', {})
65
66  " clear all popups after moving the cursor a bit, so that ruler is updated
67  call term_sendkeys(buf, "axxx\<Esc>")
68  call TermWait(buf)
69  call term_sendkeys(buf, "0")
70  call TermWait(buf)
71  call term_sendkeys(buf, ":call popup_clear()\<CR>")
72  call VerifyScreenDump(buf, 'Test_popupwin_08', {})
73
74  " clean up
75  call StopVimInTerminal(buf)
76  call delete('XtestPopup')
77endfunc
78
79func Test_popup_with_border_and_padding()
80  CheckScreendump
81
82  for iter in range(0, 1)
83    let lines =<< trim END
84	  call setline(1, range(1, 100))
85	  call popup_create('hello border', #{line: 2, col: 3, border: []})
86	  call popup_create('hello padding', #{line: 2, col: 23, padding: []})
87	  call popup_create('hello both', #{line: 2, col: 43, border: [], padding: [], highlight: 'Normal'})
88	  call popup_create('border TL', #{line: 6, col: 3, border: [1, 0, 0, 4]})
89	  call popup_create('paddings', #{line: 6, col: 23, padding: range(1, 4)})
90	  call popup_create('wrapped longer text', #{line: 8, col: 55, padding: [0, 3, 0, 3], border: [0, 1, 0, 1]})
91	  call popup_create('right aligned text', #{line: 11, col: 56, wrap: 0, padding: [0, 3, 0, 3], border: [0, 1, 0, 1]})
92	  call popup_create('X', #{line: 2, col: 73})
93	  call popup_create('X', #{line: 3, col: 74})
94	  call popup_create('X', #{line: 4, col: 75})
95	  call popup_create('X', #{line: 5, col: 76})
96    END
97    call insert(lines, iter == 1 ? '' : 'set enc=latin1')
98    call writefile(lines, 'XtestPopupBorder')
99    let buf = RunVimInTerminal('-S XtestPopupBorder', #{rows: 15})
100    call VerifyScreenDump(buf, 'Test_popupwin_2' .. iter, {})
101
102    call StopVimInTerminal(buf)
103    call delete('XtestPopupBorder')
104  endfor
105
106  let lines =<< trim END
107	call setline(1, range(1, 100))
108	hi BlueColor ctermbg=lightblue
109	hi TopColor ctermbg=253
110	hi RightColor ctermbg=245
111	hi BottomColor ctermbg=240
112	hi LeftColor ctermbg=248
113	call popup_create('hello border', #{line: 2, col: 3, border: [], borderhighlight: ['BlueColor']})
114	call popup_create(['hello border', 'and more'], #{line: 2, col: 23, border: [], borderhighlight: ['TopColor', 'RightColor', 'BottomColor', 'LeftColor']})
115	call popup_create(['hello border', 'lines only'], #{line: 2, col: 43, border: [], borderhighlight: ['BlueColor'], borderchars: ['x']})
116	call popup_create(['hello border', 'with corners'], #{line: 2, col: 60, border: [], borderhighlight: ['BlueColor'], borderchars: ['x', '#']})
117	let winid = popup_create(['hello border', 'with numbers'], #{line: 6, col: 3, border: [], borderhighlight: ['BlueColor'], borderchars: ['0', '1', '2', '3', '4', '5', '6', '7']})
118	call popup_create(['hello border', 'just blanks'], #{line: 7, col: 23, border: [], borderhighlight: ['BlueColor'], borderchars: [' ']})
119	func MultiByte()
120	  call popup_create(['hello'], #{line: 8, col: 43, border: [], borderchars: ['─', '│', '─', '│', '┌', '┐', '┘', '└']})
121	endfunc
122  END
123  call writefile(lines, 'XtestPopupBorder')
124  let buf = RunVimInTerminal('-S XtestPopupBorder', #{rows: 12})
125  call VerifyScreenDump(buf, 'Test_popupwin_22', {})
126
127  " check that changing borderchars triggers a redraw
128  call term_sendkeys(buf, ":call popup_setoptions(winid, #{borderchars: ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']})\<CR>")
129  call VerifyScreenDump(buf, 'Test_popupwin_23', {})
130
131  " check multi-byte border only with 'ambiwidth' single
132  if &ambiwidth == 'single'
133    call term_sendkeys(buf, ":call MultiByte()\<CR>")
134    call VerifyScreenDump(buf, 'Test_popupwin_24', {})
135  endif
136
137  call StopVimInTerminal(buf)
138  call delete('XtestPopupBorder')
139
140  let with_border_or_padding = #{
141	\ line: 2,
142	\ core_line: 3,
143	\ col: 3,
144	\ core_col: 4,
145	\ width: 14,
146	\ core_width: 12,
147	\ height: 3,
148	\ core_height: 1,
149	\ firstline: 1,
150	\ lastline: 1,
151	\ scrollbar: 0,
152	\ visible: 1}
153  let winid = popup_create('hello border', #{line: 2, col: 3, border: []})",
154  call assert_equal(with_border_or_padding, winid->popup_getpos())
155  let options = popup_getoptions(winid)
156  call assert_equal([], options.border)
157  call assert_false(has_key(options, "padding"))
158
159  let winid = popup_create('hello padding', #{line: 2, col: 3, padding: []})
160  let with_border_or_padding.width = 15
161  let with_border_or_padding.core_width = 13
162  call assert_equal(with_border_or_padding, popup_getpos(winid))
163  let options = popup_getoptions(winid)
164  call assert_false(has_key(options, "border"))
165  call assert_equal([], options.padding)
166
167  call popup_setoptions(winid, #{
168	\ padding: [1, 2, 3, 4],
169	\ border: [4, 0, 7, 8],
170	\ borderhighlight: ['Top', 'Right', 'Bottom', 'Left'],
171	\ borderchars: ['1', '^', '2', '>', '3', 'v', '4', '<'],
172	\ })
173  let options = popup_getoptions(winid)
174  call assert_equal([1, 0, 1, 1], options.border)
175  call assert_equal([1, 2, 3, 4], options.padding)
176  call assert_equal(['Top', 'Right', 'Bottom', 'Left'], options.borderhighlight)
177  call assert_equal(['1', '^', '2', '>', '3', 'v', '4', '<'], options.borderchars)
178
179  " Check that popup_setoptions() takes the output of popup_getoptions()
180  call popup_setoptions(winid, options)
181  call assert_equal(options, popup_getoptions(winid))
182
183  " Check that range() doesn't crash
184  call popup_setoptions(winid, #{
185	\ padding: range(1, 4),
186	\ border: range(5, 8),
187	\ borderhighlight: range(4),
188	\ borderchars: range(8),
189	\ })
190
191  let winid = popup_create('hello both', #{line: 3, col: 8, border: [], padding: []})
192  call assert_equal(#{
193	\ line: 3,
194	\ core_line: 5,
195	\ col: 8,
196	\ core_col: 10,
197	\ width: 14,
198	\ core_width: 10,
199	\ height: 5,
200	\ scrollbar: 0,
201	\ core_height: 1,
202	\ firstline: 1,
203	\ lastline: 1,
204	\ visible: 1}, popup_getpos(winid))
205
206  call popup_clear()
207endfunc
208
209func Test_popup_with_syntax_win_execute()
210  CheckScreendump
211
212  let lines =<< trim END
213	call setline(1, range(1, 100))
214	hi PopupColor ctermbg=lightblue
215	let winid = popup_create([
216	    \ '#include <stdio.h>',
217	    \ 'int main(void)',
218	    \ '{',
219	    \ '    printf(123);',
220	    \ '}',
221	    \], #{line: 3, col: 25, highlight: 'PopupColor'})
222	call win_execute(winid, 'set syntax=cpp')
223  END
224  call writefile(lines, 'XtestPopup')
225  let buf = RunVimInTerminal('-S XtestPopup', #{rows: 10})
226  call VerifyScreenDump(buf, 'Test_popupwin_10', {})
227
228  " clean up
229  call StopVimInTerminal(buf)
230  call delete('XtestPopup')
231endfunc
232
233func Test_popup_with_syntax_setbufvar()
234  CheckScreendump
235
236  let lines =<< trim END
237	call setline(1, range(1, 100))
238	hi PopupColor ctermbg=lightgrey
239	let winid = popup_create([
240	    \ '#include <stdio.h>',
241	    \ 'int main(void)',
242	    \ '{',
243	    \ "\tprintf(567);",
244	    \ '}',
245	    \], #{line: 3, col: 21, highlight: 'PopupColor'})
246	call setbufvar(winbufnr(winid), '&syntax', 'cpp')
247  END
248  call writefile(lines, 'XtestPopup')
249  let buf = RunVimInTerminal('-S XtestPopup', #{rows: 10})
250  call VerifyScreenDump(buf, 'Test_popupwin_11', {})
251
252  " clean up
253  call StopVimInTerminal(buf)
254  call delete('XtestPopup')
255endfunc
256
257func Test_popup_with_matches()
258  CheckScreendump
259
260  let lines =<< trim END
261	call setline(1, ['111 222 333', '444 555 666'])
262	let winid = popup_create([
263	    \ '111 222 333',
264	    \ '444 555 666',
265	    \], #{line: 3, col: 10, border: []})
266	set hlsearch
267	hi VeryBlue ctermfg=blue guifg=blue
268	/666
269	call matchadd('ErrorMsg', '111')
270	call matchadd('VeryBlue', '444')
271	call win_execute(winid, "call matchadd('ErrorMsg', '111')")
272	call win_execute(winid, "call matchadd('VeryBlue', '555')")
273  END
274  call writefile(lines, 'XtestPopupMatches')
275  let buf = RunVimInTerminal('-S XtestPopupMatches', #{rows: 10})
276  call VerifyScreenDump(buf, 'Test_popupwin_matches', {})
277
278  " clean up
279  call StopVimInTerminal(buf)
280  call delete('XtestPopupMatches')
281endfunc
282
283func Test_popup_all_corners()
284  CheckScreendump
285
286  let lines =<< trim END
287	call setline(1, repeat([repeat('-', 60)], 15))
288	set so=0
289	normal 2G3|r#
290	let winid1 = popup_create(['first', 'second'], #{
291	      \ line: 'cursor+1',
292	      \ col: 'cursor',
293	      \ pos: 'topleft',
294	      \ border: [],
295	      \ padding: [],
296	      \ })
297	normal 24|r@
298	let winid1 = popup_create(['First', 'SeconD'], #{
299	      \ line: 'cursor+1',
300	      \ col: 'cursor',
301	      \ pos: 'topright',
302	      \ border: [],
303	      \ padding: [],
304	      \ })
305	normal 9G27|r%
306	let winid1 = popup_create(['fiRSt', 'seCOnd'], #{
307	      \ line: 'cursor-1',
308	      \ col: 'cursor',
309	      \ pos: 'botleft',
310	      \ border: [],
311	      \ padding: [],
312	      \ })
313	normal 48|r&
314	let winid1 = popup_create(['FIrsT', 'SEcoND'], #{
315	      \ line: 'cursor-1',
316	      \ col: 'cursor',
317	      \ pos: 'botright',
318	      \ border: [],
319	      \ padding: [],
320	      \ })
321	normal 1G51|r*
322	let winid1 = popup_create(['one', 'two'], #{
323	      \ line: 'cursor-1',
324	      \ col: 'cursor',
325	      \ pos: 'botleft',
326	      \ border: [],
327	      \ padding: [],
328	      \ })
329  END
330  call writefile(lines, 'XtestPopupCorners')
331  let buf = RunVimInTerminal('-S XtestPopupCorners', #{rows: 12})
332  call VerifyScreenDump(buf, 'Test_popupwin_corners', {})
333
334  " clean up
335  call StopVimInTerminal(buf)
336  call delete('XtestPopupCorners')
337endfunc
338
339func Test_popup_nospace()
340  CheckScreendump
341
342  let lines =<< trim END
343	call setline(1, repeat([repeat('-', 60)], 15))
344	set so=0
345
346	" cursor in a line in top half, using "botleft" with popup that
347	" does fit
348	normal 5G2|r@
349	let winid1 = popup_create(['one', 'two'], #{
350	      \ line: 'cursor-1',
351	      \ col: 'cursor',
352	      \ pos: 'botleft',
353	      \ border: [],
354	      \ })
355	" cursor in a line in top half, using "botleft" with popup that
356	" doesn't fit: gets truncated
357	normal 5G9|r#
358	let winid1 = popup_create(['one', 'two', 'tee'], #{
359	      \ line: 'cursor-1',
360	      \ col: 'cursor',
361	      \ pos: 'botleft',
362	      \ posinvert: 0,
363	      \ border: [],
364	      \ })
365	" cursor in a line in top half, using "botleft" with popup that
366	" doesn't fit and 'posinvert' set: flips to below.
367	normal 5G16|r%
368	let winid1 = popup_create(['one', 'two', 'tee'], #{
369	      \ line: 'cursor-1',
370	      \ col: 'cursor',
371	      \ pos: 'botleft',
372	      \ border: [],
373	      \ })
374	" cursor in a line in bottom half, using "botleft" with popup that
375	" doesn't fit: does not flip.
376	normal 8G23|r*
377	let winid1 = popup_create(['aaa', 'bbb', 'ccc', 'ddd', 'eee', 'fff'], #{
378	      \ line: 'cursor-1',
379	      \ col: 'cursor',
380	      \ pos: 'botleft',
381	      \ border: [],
382	      \ })
383
384	" cursor in a line in bottom half, using "topleft" with popup that
385	" does fit
386	normal 8G30|r@
387	let winid1 = popup_create(['one', 'two'], #{
388	      \ line: 'cursor+1',
389	      \ col: 'cursor',
390	      \ pos: 'topleft',
391	      \ border: [],
392	      \ })
393	" cursor in a line in top half, using "topleft" with popup that
394	" doesn't fit: truncated
395	normal 8G37|r#
396	let winid1 = popup_create(['one', 'two', 'tee'], #{
397	      \ line: 'cursor+1',
398	      \ col: 'cursor',
399	      \ pos: 'topleft',
400	      \ posinvert: 0,
401	      \ border: [],
402	      \ })
403	" cursor in a line in top half, using "topleft" with popup that
404	" doesn't fit and "posinvert" set: flips to above.
405	normal 8G44|r%
406	let winid1 = popup_create(['one', 'two', 'tee', 'fou', 'fiv'], #{
407	      \ line: 'cursor+1',
408	      \ col: 'cursor',
409	      \ pos: 'topleft',
410	      \ border: [],
411	      \ })
412	" cursor in a line in top half, using "topleft" with popup that
413	" doesn't fit: does not flip.
414	normal 5G51|r*
415	let winid1 = popup_create(['aaa', 'bbb', 'ccc', 'ddd', 'eee', 'fff'], #{
416	      \ line: 'cursor+1',
417	      \ col: 'cursor',
418	      \ pos: 'topleft',
419	      \ border: [],
420	      \ })
421  END
422  call writefile(lines, 'XtestPopupNospace')
423  let buf = RunVimInTerminal('-S XtestPopupNospace', #{rows: 12})
424  call VerifyScreenDump(buf, 'Test_popupwin_nospace', {})
425
426  " clean up
427  call StopVimInTerminal(buf)
428  call delete('XtestPopupNospace')
429endfunc
430
431func Test_popup_firstline_dump()
432  CheckScreendump
433
434  let lines =<< trim END
435	call setline(1, range(1, 20))
436	let winid = popup_create(['1111', '222222', '33333', '44', '5', '666666', '77777', '888', '9999999999999999'], #{
437	      \ maxheight: 4,
438	      \ firstline: 3,
439	      \ })
440  END
441  call writefile(lines, 'XtestPopupFirstline')
442  let buf = RunVimInTerminal('-S XtestPopupFirstline', #{rows: 10})
443  call VerifyScreenDump(buf, 'Test_popupwin_firstline_1', {})
444
445  call term_sendkeys(buf, ":call popup_setoptions(winid, #{firstline: -1})\<CR>")
446  call term_sendkeys(buf, ":\<CR>")
447  call VerifyScreenDump(buf, 'Test_popupwin_firstline_2', {})
448
449  " clean up
450  call StopVimInTerminal(buf)
451  call delete('XtestPopupFirstline')
452endfunc
453
454func Test_popup_firstline()
455  let winid = popup_create(['1111', '222222', '33333', '44444'], #{
456	\ maxheight: 2,
457	\ firstline: 3,
458	\ })
459  call assert_equal(3, popup_getoptions(winid).firstline)
460  call popup_setoptions(winid, #{firstline: 1})
461  call assert_equal(1, popup_getoptions(winid).firstline)
462  eval winid->popup_close()
463
464  let winid = popup_create(['xxx']->repeat(50), #{
465	\ maxheight: 3,
466	\ firstline: 11,
467	\ })
468  redraw
469  call assert_equal(11, popup_getoptions(winid).firstline)
470  call assert_equal(11, popup_getpos(winid).firstline)
471  " check line() works with popup window
472  call assert_equal(11, line('.', winid))
473  call assert_equal(50, line('$', winid))
474  call assert_equal(0, line('$', 123456))
475
476  " Normal command changes what is displayed but not "firstline"
477  call win_execute(winid, "normal! \<c-y>")
478  call assert_equal(11, popup_getoptions(winid).firstline)
479  call assert_equal(10, popup_getpos(winid).firstline)
480
481  " Making some property change applies "firstline" again
482  call popup_setoptions(winid, #{line: 4})
483  call assert_equal(11, popup_getoptions(winid).firstline)
484  call assert_equal(11, popup_getpos(winid).firstline)
485
486  " Remove "firstline" property and scroll
487  call popup_setoptions(winid, #{firstline: 0})
488  call win_execute(winid, "normal! \<c-y>")
489  call assert_equal(0, popup_getoptions(winid).firstline)
490  call assert_equal(10, popup_getpos(winid).firstline)
491
492  " Making some property change has no side effect
493  call popup_setoptions(winid, #{line: 3})
494  call assert_equal(0, popup_getoptions(winid).firstline)
495  call assert_equal(10, popup_getpos(winid).firstline)
496  call popup_close(winid)
497
498  " CTRL-D scrolls down half a page
499  let winid = popup_create(['xxx']->repeat(50), #{
500	\ maxheight: 8,
501	\ })
502  redraw
503  call assert_equal(1, popup_getpos(winid).firstline)
504  call win_execute(winid, "normal! \<C-D>")
505  call assert_equal(5, popup_getpos(winid).firstline)
506  call win_execute(winid, "normal! \<C-D>")
507  call assert_equal(9, popup_getpos(winid).firstline)
508  call win_execute(winid, "normal! \<C-U>")
509  call assert_equal(5, popup_getpos(winid).firstline)
510
511  call win_execute(winid, "normal! \<C-F>")
512  call assert_equal(11, popup_getpos(winid).firstline)
513  call win_execute(winid, "normal! \<C-B>")
514  call assert_equal(5, popup_getpos(winid).firstline)
515
516  call popup_close(winid)
517endfunc
518
519func Test_popup_noscrolloff()
520  set scrolloff=5
521  let winid = popup_create(['xxx']->repeat(50), #{
522	\ maxheight: 5,
523	\ firstline: 11,
524	\ })
525  redraw
526  call assert_equal(11, popup_getoptions(winid).firstline)
527  call assert_equal(11, popup_getpos(winid).firstline)
528
529  call popup_setoptions(winid, #{firstline: 0})
530  call win_execute(winid, "normal! \<c-y>")
531  call assert_equal(0, popup_getoptions(winid).firstline)
532  call assert_equal(10, popup_getpos(winid).firstline)
533
534  call popup_close(winid)
535endfunc
536
537func Test_popup_drag()
538  CheckScreendump
539
540  " create a popup that covers the command line
541  let lines =<< trim END
542	call setline(1, range(1, 20))
543	split
544	vsplit
545	$wincmd w
546	vsplit
547	1wincmd w
548	let winid = popup_create(['1111', '222222', '33333'], #{
549	      \ drag: 1,
550	      \ resize: 1,
551	      \ border: [],
552	      \ line: &lines - 4,
553	      \ })
554	func Dragit()
555	  call feedkeys("\<F3>\<LeftMouse>\<F4>\<LeftDrag>\<LeftRelease>", "xt")
556	endfunc
557	map <silent> <F3> :call test_setmouse(&lines - 4, &columns / 2)<CR>
558	map <silent> <F4> :call test_setmouse(&lines - 8, &columns / 2 - 20)<CR>
559	func Resize()
560	  call feedkeys("\<F5>\<LeftMouse>\<F6>\<LeftDrag>\<LeftRelease>", "xt")
561	endfunc
562	map <silent> <F5> :call test_setmouse(6, 21)<CR>
563	map <silent> <F6> :call test_setmouse(7, 25)<CR>
564  END
565  call writefile(lines, 'XtestPopupDrag')
566  let buf = RunVimInTerminal('-S XtestPopupDrag', #{rows: 10})
567  call VerifyScreenDump(buf, 'Test_popupwin_drag_01', {})
568
569  call term_sendkeys(buf, ":call Dragit()\<CR>")
570  call VerifyScreenDump(buf, 'Test_popupwin_drag_02', {})
571
572  call term_sendkeys(buf, ":call Resize()\<CR>")
573  call VerifyScreenDump(buf, 'Test_popupwin_drag_03', {})
574
575  " clean up
576  call StopVimInTerminal(buf)
577  call delete('XtestPopupDrag')
578endfunc
579
580func Test_popup_close_with_mouse()
581  CheckScreendump
582
583  let lines =<< trim END
584	call setline(1, range(1, 20))
585	" With border, can click on X
586	let winid = popup_create('foobar', #{
587	      \ close: 'button',
588	      \ border: [],
589	      \ line: 1,
590	      \ col: 1,
591	      \ })
592	func CloseMsg(id, result)
593	  echomsg 'Popup closed with ' .. a:result
594	endfunc
595	let winid = popup_create('notification', #{
596	      \ close: 'click',
597	      \ line: 3,
598	      \ col: 15,
599	      \ callback: 'CloseMsg',
600	      \ })
601	let winid = popup_create('no border here', #{
602	      \ close: 'button',
603	      \ line: 5,
604	      \ col: 3,
605	      \ })
606	let winid = popup_create('only padding', #{
607	      \ close: 'button',
608	      \ padding: [],
609	      \ line: 5,
610	      \ col: 23,
611	      \ })
612	func CloseWithX()
613	  call feedkeys("\<F3>\<LeftMouse>\<LeftRelease>", "xt")
614	endfunc
615	map <silent> <F3> :call test_setmouse(1, len('foobar') + 2)<CR>
616	func CloseWithClick()
617	  call feedkeys("\<F4>\<LeftMouse>\<LeftRelease>", "xt")
618	endfunc
619	map <silent> <F4> :call test_setmouse(3, 17)<CR>
620	func CreateWithMenuFilter()
621	  let winid = popup_create('barfoo', #{
622		\ close: 'button',
623		\ filter: 'popup_filter_menu',
624		\ border: [],
625		\ line: 1,
626		\ col: 40,
627		\ })
628	endfunc
629  END
630  call writefile(lines, 'XtestPopupClose')
631  let buf = RunVimInTerminal('-S XtestPopupClose', #{rows: 10})
632  call VerifyScreenDump(buf, 'Test_popupwin_close_01', {})
633
634  call term_sendkeys(buf, ":call CloseWithX()\<CR>")
635  call VerifyScreenDump(buf, 'Test_popupwin_close_02', {})
636
637  call term_sendkeys(buf, ":call CloseWithClick()\<CR>")
638  call VerifyScreenDump(buf, 'Test_popupwin_close_03', {})
639
640  call term_sendkeys(buf, ":call CreateWithMenuFilter()\<CR>")
641  call VerifyScreenDump(buf, 'Test_popupwin_close_04', {})
642
643  " We have to send the actual mouse code, feedkeys() would be caught the
644  " filter.
645  call term_sendkeys(buf, "\<Esc>[<0;47;1M")
646  call VerifyScreenDump(buf, 'Test_popupwin_close_05', {})
647
648  " clean up
649  call StopVimInTerminal(buf)
650  call delete('XtestPopupClose')
651endfunction
652
653func Test_popup_menu_wrap()
654  CheckScreendump
655
656  let lines =<< trim END
657	call setline(1, range(1, 20))
658	call popup_create([
659	      \ 'one',
660	      \ 'asdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfas',
661	      \ 'three',
662	      \ 'four',
663	      \ ], #{
664	      \ pos: "botleft",
665	      \ border: [],
666	      \ padding: [0,1,0,1],
667	      \ maxheight: 3,
668	      \ cursorline: 1,
669	      \ filter: 'popup_filter_menu',
670	      \ })
671  END
672  call writefile(lines, 'XtestPopupWrap')
673  let buf = RunVimInTerminal('-S XtestPopupWrap', #{rows: 10})
674  call VerifyScreenDump(buf, 'Test_popupwin_wrap_1', {})
675
676  call term_sendkeys(buf, "jj")
677  call VerifyScreenDump(buf, 'Test_popupwin_wrap_2', {})
678
679  " clean up
680  call term_sendkeys(buf, "\<Esc>")
681  call StopVimInTerminal(buf)
682  call delete('XtestPopupWrap')
683endfunction
684
685func Test_popup_with_mask()
686  CheckScreendump
687
688  let lines =<< trim END
689	call setline(1, repeat([join(range(1, 42), '')], 13))
690	hi PopupColor ctermbg=lightgrey
691	let winid = popup_create([
692	    \ 'some text',
693	    \ 'another line',
694	    \], #{
695	    \ line: 1,
696	    \ col: 10,
697	    \ posinvert: 0,
698	    \ wrap: 0,
699	    \ fixed: 1,
700	    \ zindex: 90,
701	    \ padding: [],
702	    \ highlight: 'PopupColor',
703	    \ mask: [[1,1,1,1], [-5,-1,4,4], [7,9,2,3], [2,4,3,3]]})
704	call popup_create([
705	    \ 'xxxxxxxxx',
706	    \ 'yyyyyyyyy',
707	    \], #{
708	    \ line: 3,
709	    \ col: 18,
710	    \ zindex: 20})
711	let winidb = popup_create([
712	    \ 'just one line',
713	    \], #{
714	    \ line: 7,
715	    \ col: 10,
716	    \ posinvert: 0,
717	    \ wrap: 0,
718	    \ fixed: 1,
719	    \ close: 'button',
720	    \ zindex: 90,
721	    \ padding: [],
722	    \ border: [],
723	    \ mask: [[1,2,1,1], [-5,-1,4,4], [7,9,2,3], [3,5,5,5],[-7,-4,5,5]]})
724  END
725  call writefile(lines, 'XtestPopupMask')
726  let buf = RunVimInTerminal('-S XtestPopupMask', #{rows: 13})
727  call VerifyScreenDump(buf, 'Test_popupwin_mask_1', {})
728
729  call term_sendkeys(buf, ":call popup_move(winid, #{col: 11, line: 2})\<CR>")
730  call term_sendkeys(buf, ":call popup_move(winidb, #{col: 12})\<CR>")
731  call term_sendkeys(buf, ":\<CR>")
732  call VerifyScreenDump(buf, 'Test_popupwin_mask_2', {})
733
734  call term_sendkeys(buf, ":call popup_move(winid, #{col: 65, line: 2})\<CR>")
735  call term_sendkeys(buf, ":call popup_move(winidb, #{col: 63})\<CR>")
736  call term_sendkeys(buf, ":\<CR>")
737  call VerifyScreenDump(buf, 'Test_popupwin_mask_3', {})
738
739  call term_sendkeys(buf, ":call popup_move(winid, #{pos: 'topright', col: 12, line: 2})\<CR>")
740  call term_sendkeys(buf, ":call popup_move(winidb, #{pos: 'topright', col: 12})\<CR>")
741  call term_sendkeys(buf, ":\<CR>")
742  call VerifyScreenDump(buf, 'Test_popupwin_mask_4', {})
743
744  call term_sendkeys(buf, ":call popup_move(winid, #{pos: 'topright', col: 12, line: 11})\<CR>")
745  call term_sendkeys(buf, ":call popup_move(winidb, #{pos: 'topleft', col: 42, line: 11})\<CR>")
746  call term_sendkeys(buf, ":\<CR>")
747  call VerifyScreenDump(buf, 'Test_popupwin_mask_5', {})
748
749  " clean up
750  call StopVimInTerminal(buf)
751  call delete('XtestPopupMask')
752endfunc
753
754func Test_popup_select()
755  CheckScreendump
756  CheckFeature clipboard_working
757
758  " create a popup with some text to be selected
759  let lines =<< trim END
760    set clipboard=autoselect
761    call setline(1, range(1, 20))
762    let winid = popup_create(['the word', 'some more', 'several words here', 'invisible', '5', '6', '7'], #{
763	  \ drag: 1,
764	  \ border: [],
765	  \ line: 3,
766	  \ col: 10,
767	  \ maxheight: 3,
768	  \ })
769    func Select1()
770      call feedkeys("\<F3>\<LeftMouse>\<F4>\<LeftDrag>\<LeftRelease>", "xt")
771    endfunc
772    map <silent> <F3> :call test_setmouse(4, 15)<CR>
773    map <silent> <F4> :call test_setmouse(6, 23)<CR>
774  END
775  call writefile(lines, 'XtestPopupSelect')
776  let buf = RunVimInTerminal('-S XtestPopupSelect', #{rows: 10})
777  call term_sendkeys(buf, ":call Select1()\<CR>")
778  call VerifyScreenDump(buf, 'Test_popupwin_select_01', {})
779
780  call term_sendkeys(buf, ":call popup_close(winid)\<CR>")
781  call term_sendkeys(buf, "\"*p")
782  " clean the command line, sometimes it still shows a command
783  call term_sendkeys(buf, ":\<esc>")
784
785  call VerifyScreenDump(buf, 'Test_popupwin_select_02', {})
786
787  " clean up
788  call StopVimInTerminal(buf)
789  call delete('XtestPopupSelect')
790endfunc
791
792func Test_popup_in_tab()
793  " default popup is local to tab, not visible when in other tab
794  let winid = popup_create("text", {})
795  let bufnr = winbufnr(winid)
796  call assert_equal(1, popup_getpos(winid).visible)
797  call assert_equal(0, popup_getoptions(winid).tabpage)
798  tabnew
799  call assert_equal(0, popup_getpos(winid).visible)
800  call assert_equal(1, popup_getoptions(winid).tabpage)
801  quit
802  call assert_equal(1, popup_getpos(winid).visible)
803
804  call assert_equal(1, bufexists(bufnr))
805  call popup_clear()
806  " buffer is gone now
807  call assert_equal(0, bufexists(bufnr))
808
809  " global popup is visible in any tab
810  let winid = popup_create("text", #{tabpage: -1})
811  call assert_equal(1, popup_getpos(winid).visible)
812  call assert_equal(-1, popup_getoptions(winid).tabpage)
813  tabnew
814  call assert_equal(1, popup_getpos(winid).visible)
815  call assert_equal(-1, popup_getoptions(winid).tabpage)
816  quit
817  call assert_equal(1, popup_getpos(winid).visible)
818  call popup_clear()
819
820  " create popup in other tab
821  tabnew
822  let winid = popup_create("text", #{tabpage: 1})
823  call assert_equal(0, popup_getpos(winid).visible)
824  call assert_equal(1, popup_getoptions(winid).tabpage)
825  quit
826  call assert_equal(1, popup_getpos(winid).visible)
827  call assert_equal(0, popup_getoptions(winid).tabpage)
828  call popup_clear()
829endfunc
830
831func Test_popup_valid_arguments()
832  call assert_equal(0, len(popup_list()))
833
834  " Zero value is like the property wasn't there
835  let winid = popup_create("text", #{col: 0})
836  let pos = popup_getpos(winid)
837  call assert_inrange(&columns / 2 - 1, &columns / 2 + 1, pos.col)
838  call assert_equal([winid], popup_list())
839  call popup_clear()
840
841  " using cursor column has minimum value of 1
842  let winid = popup_create("text", #{col: 'cursor-100'})
843  let pos = popup_getpos(winid)
844  call assert_equal(1, pos.col)
845  call popup_clear()
846
847  " center
848  let winid = popup_create("text", #{pos: 'center'})
849  let pos = popup_getpos(winid)
850  let around = (&columns - pos.width) / 2
851  call assert_inrange(around - 1, around + 1, pos.col)
852  let around = (&lines - pos.height) / 2
853  call assert_inrange(around - 1, around + 1, pos.line)
854  call popup_clear()
855endfunc
856
857func Test_popup_invalid_arguments()
858  call assert_fails('call popup_create(666, {})', 'E86:')
859  call popup_clear()
860  call assert_fails('call popup_create("text", "none")', 'E715:')
861  call popup_clear()
862  call assert_fails('call popup_create(test_null_string(), {})', 'E450:')
863  call assert_fails('call popup_create(test_null_list(), {})', 'E450:')
864  call popup_clear()
865
866  call assert_fails('call popup_create("text", #{col: "xxx"})', 'E475:')
867  call popup_clear()
868  call assert_fails('call popup_create("text", #{col: "cursor8"})', 'E15:')
869  call popup_clear()
870  call assert_fails('call popup_create("text", #{col: "cursor+x"})', 'E15:')
871  call popup_clear()
872  call assert_fails('call popup_create("text", #{col: "cursor+8x"})', 'E15:')
873  call popup_clear()
874
875  call assert_fails('call popup_create("text", #{line: "xxx"})', 'E475:')
876  call popup_clear()
877  call assert_fails('call popup_create("text", #{line: "cursor8"})', 'E15:')
878  call popup_clear()
879  call assert_fails('call popup_create("text", #{line: "cursor+x"})', 'E15:')
880  call popup_clear()
881  call assert_fails('call popup_create("text", #{line: "cursor+8x"})', 'E15:')
882  call popup_clear()
883
884  call assert_fails('call popup_create("text", #{pos: "there"})', 'E475:')
885  call popup_clear()
886  call assert_fails('call popup_create("text", #{padding: "none"})', 'E714:')
887  call popup_clear()
888  call assert_fails('call popup_create("text", #{border: "none"})', 'E714:')
889  call popup_clear()
890  call assert_fails('call popup_create("text", #{borderhighlight: "none"})', 'E714:')
891  call popup_clear()
892  call assert_fails('call popup_create("text", #{borderhighlight: test_null_list()})', 'E714:')
893  call popup_clear()
894  call assert_fails('call popup_create("text", #{borderchars: "none"})', 'E714:')
895  call popup_clear()
896
897  call assert_fails('call popup_create([#{text: "text"}, 666], {})', 'E715:')
898  call popup_clear()
899  call assert_fails('call popup_create([#{text: "text", props: "none"}], {})', 'E714:')
900  call popup_clear()
901  call assert_fails('call popup_create([#{text: "text", props: ["none"]}], {})', 'E715:')
902  call popup_clear()
903  call assert_fails('call popup_create([#{text: "text", props: range(3)}], {})', 'E715:')
904  call popup_clear()
905  call assert_fails('call popup_create("text", #{mask: ["asdf"]})', 'E475:')
906  call popup_clear()
907  call assert_fails('call popup_create("text", #{mask: range(5)})', 'E475:')
908  call popup_clear()
909  call popup_create("text", #{mask: [range(4)]})
910  call popup_clear()
911  call assert_fails('call popup_create("text", #{mask: test_null_list()})', 'E475:')
912  call assert_fails('call popup_create("text", #{mapping: []})', 'E745:')
913  call popup_clear()
914  call assert_fails('call popup_create("text", #{tabpage : 4})', 'E997:')
915  call popup_clear()
916endfunc
917
918func Test_win_execute_closing_curwin()
919  split
920  let winid = popup_create('some text', {})
921  call assert_fails('call win_execute(winid, winnr() .. "close")', 'E994')
922  call popup_clear()
923
924  let winid = popup_create('some text', {})
925  call assert_fails('call win_execute(winid, printf("normal! :\<C-u>call popup_close(%d)\<CR>", winid))', 'E994')
926  call popup_clear()
927endfunc
928
929func Test_win_execute_not_allowed()
930  let winid = popup_create('some text', {})
931  call assert_fails('call win_execute(winid, "split")', 'E994:')
932  call assert_fails('call win_execute(winid, "vsplit")', 'E994:')
933  call assert_fails('call win_execute(winid, "close")', 'E994:')
934  call assert_fails('call win_execute(winid, "bdelete")', 'E994:')
935  call assert_fails('call win_execute(winid, "bwipe!")', 'E994:')
936  call assert_fails('call win_execute(winid, "tabnew")', 'E994:')
937  call assert_fails('call win_execute(winid, "tabnext")', 'E994:')
938  call assert_fails('call win_execute(winid, "next")', 'E994:')
939  call assert_fails('call win_execute(winid, "rewind")', 'E994:')
940  call assert_fails('call win_execute(winid, "pedit filename")', 'E994:')
941  call assert_fails('call win_execute(winid, "buf")', 'E994:')
942  call assert_fails('call win_execute(winid, "bnext")', 'E994:')
943  call assert_fails('call win_execute(winid, "bprev")', 'E994:')
944  call assert_fails('call win_execute(winid, "bfirst")', 'E994:')
945  call assert_fails('call win_execute(winid, "blast")', 'E994:')
946  call assert_fails('call win_execute(winid, "edit")', 'E994:')
947  call assert_fails('call win_execute(winid, "enew")', 'E994:')
948  call assert_fails('call win_execute(winid, "wincmd x")', 'E994:')
949  call assert_fails('call win_execute(winid, "wincmd w")', 'E994:')
950  call assert_fails('call win_execute(winid, "wincmd t")', 'E994:')
951  call assert_fails('call win_execute(winid, "wincmd b")', 'E994:')
952  call popup_clear()
953endfunc
954
955func Test_popup_with_wrap()
956  CheckScreendump
957
958  let lines =<< trim END
959	 call setline(1, range(1, 100))
960	 let winid = popup_create(
961	   \ 'a long line that wont fit',
962	   \ #{line: 3, col: 20, maxwidth: 10, wrap: 1})
963  END
964  call writefile(lines, 'XtestPopup')
965  let buf = RunVimInTerminal('-S XtestPopup', #{rows: 10})
966  call VerifyScreenDump(buf, 'Test_popupwin_wrap', {})
967
968  " clean up
969  call StopVimInTerminal(buf)
970  call delete('XtestPopup')
971endfunc
972
973func Test_popup_without_wrap()
974  CheckScreendump
975
976  let lines =<< trim END
977	 call setline(1, range(1, 100))
978	 let winid = popup_create(
979	   \ 'a long line that wont fit',
980	   \ #{line: 3, col: 20, maxwidth: 10, wrap: 0})
981  END
982  call writefile(lines, 'XtestPopup')
983  let buf = RunVimInTerminal('-S XtestPopup', #{rows: 10})
984  call VerifyScreenDump(buf, 'Test_popupwin_nowrap', {})
985
986  " clean up
987  call StopVimInTerminal(buf)
988  call delete('XtestPopup')
989endfunc
990
991func Test_popup_with_showbreak()
992  CheckScreendump
993
994  let lines =<< trim END
995	 set showbreak=>>\
996	 call setline(1, range(1, 20))
997	 let winid = popup_dialog(
998	   \ 'a long line here that wraps',
999	   \ #{filter: 'popup_filter_yesno',
1000	   \   maxwidth: 12})
1001  END
1002  call writefile(lines, 'XtestPopupShowbreak')
1003  let buf = RunVimInTerminal('-S XtestPopupShowbreak', #{rows: 10})
1004  call VerifyScreenDump(buf, 'Test_popupwin_showbreak', {})
1005
1006  " clean up
1007  call term_sendkeys(buf, "y")
1008  call StopVimInTerminal(buf)
1009  call delete('XtestPopupShowbreak')
1010endfunc
1011
1012func Test_popup_time()
1013  CheckFeature timers
1014
1015  topleft vnew
1016  call setline(1, 'hello')
1017
1018  let winid = popup_create('world', #{
1019	\ line: 1,
1020	\ col: 1,
1021	\ minwidth: 20,
1022	\ time: 500,
1023	\})
1024  redraw
1025  let line = join(map(range(1, 5), 'screenstring(1, v:val)'), '')
1026  call assert_equal('world', line)
1027
1028  call assert_equal(winid, popup_locate(1, 1))
1029  call assert_equal(winid, popup_locate(1, 20))
1030  call assert_equal(0, popup_locate(1, 21))
1031  call assert_equal(0, popup_locate(2, 1))
1032
1033  sleep 700m
1034  redraw
1035  let line = join(map(range(1, 5), '1->screenstring(v:val)'), '')
1036  call assert_equal('hello', line)
1037
1038  call popup_create('on the command line', #{
1039	\ line: &lines,
1040	\ col: 10,
1041	\ minwidth: 20,
1042	\ time: 500,
1043	\})
1044  redraw
1045  let line = join(map(range(1, 30), 'screenstring(&lines, v:val)'), '')
1046  call assert_match('.*on the command line.*', line)
1047
1048  sleep 700m
1049  redraw
1050  let line = join(map(range(1, 30), 'screenstring(&lines, v:val)'), '')
1051  call assert_notmatch('.*on the command line.*', line)
1052
1053  bwipe!
1054endfunc
1055
1056func Test_popup_hide()
1057  topleft vnew
1058  call setline(1, 'hello')
1059
1060  let winid = popup_create('world', #{
1061	\ line: 1,
1062	\ col: 1,
1063	\ minwidth: 20,
1064	\})
1065  redraw
1066  let line = join(map(range(1, 5), 'screenstring(1, v:val)'), '')
1067  call assert_equal('world', line)
1068  call assert_equal(1, popup_getpos(winid).visible)
1069  " buffer is still listed and active
1070  call assert_match(winbufnr(winid) .. 'u a.*\[Popup\]', execute('ls u'))
1071
1072  call popup_hide(winid)
1073  redraw
1074  let line = join(map(range(1, 5), 'screenstring(1, v:val)'), '')
1075  call assert_equal('hello', line)
1076  call assert_equal(0, popup_getpos(winid).visible)
1077  " buffer is still listed but hidden
1078  call assert_match(winbufnr(winid) .. 'u a.*\[Popup\]', execute('ls u'))
1079
1080  eval winid->popup_show()
1081  redraw
1082  let line = join(map(range(1, 5), 'screenstring(1, v:val)'), '')
1083  call assert_equal('world', line)
1084  call assert_equal(1, popup_getpos(winid).visible)
1085
1086
1087  call popup_close(winid)
1088  redraw
1089  let line = join(map(range(1, 5), 'screenstring(1, v:val)'), '')
1090  call assert_equal('hello', line)
1091
1092  " error is given for existing non-popup window
1093  call assert_fails('call popup_hide(win_getid())', 'E993:')
1094
1095  " no error non-existing window
1096  eval 1234234->popup_hide()
1097  call popup_show(41234234)
1098
1099  bwipe!
1100endfunc
1101
1102func Test_popup_move()
1103  topleft vnew
1104  call setline(1, 'hello')
1105
1106  let winid = popup_create('world', #{
1107	\ line: 1,
1108	\ col: 1,
1109	\ minwidth: 20,
1110	\})
1111  redraw
1112  let line = join(map(range(1, 6), 'screenstring(1, v:val)'), '')
1113  call assert_equal('world ', line)
1114
1115  call popup_move(winid, #{line: 2, col: 2})
1116  redraw
1117  let line = join(map(range(1, 6), 'screenstring(1, v:val)'), '')
1118  call assert_equal('hello ', line)
1119  let line = join(map(range(1, 6), 'screenstring(2, v:val)'), '')
1120  call assert_equal('~world', line)
1121
1122  eval winid->popup_move(#{line: 1})
1123  redraw
1124  let line = join(map(range(1, 6), 'screenstring(1, v:val)'), '')
1125  call assert_equal('hworld', line)
1126
1127  call assert_fails('call popup_move(winid, [])', 'E715:')
1128  call assert_fails('call popup_move(winid, test_null_dict())', 'E715:')
1129
1130  call popup_close(winid)
1131
1132  call assert_equal(0, popup_move(-1, {}))
1133
1134  bwipe!
1135endfunc
1136
1137func Test_popup_getpos()
1138  let winid = popup_create('hello', #{
1139    \ line: 2,
1140    \ col: 3,
1141    \ minwidth: 10,
1142    \ minheight: 11,
1143    \})
1144  redraw
1145  let res = popup_getpos(winid)
1146  call assert_equal(2, res.line)
1147  call assert_equal(3, res.col)
1148  call assert_equal(10, res.width)
1149  call assert_equal(11, res.height)
1150  call assert_equal(1, res.visible)
1151
1152  call popup_close(winid)
1153endfunc
1154
1155func Test_popup_width_longest()
1156  let tests = [
1157	\ [['hello', 'this', 'window', 'displays', 'all of its text'], 15],
1158	\ [['hello', 'this', 'window', 'all of its text', 'displays'], 15],
1159	\ [['hello', 'this', 'all of its text', 'window', 'displays'], 15],
1160	\ [['hello', 'all of its text', 'this', 'window', 'displays'], 15],
1161	\ [['all of its text', 'hello', 'this', 'window', 'displays'], 15],
1162	\ ]
1163
1164  for test in tests
1165    let winid = popup_create(test[0], #{line: 2, col: 3})
1166    redraw
1167    let position = popup_getpos(winid)
1168    call assert_equal(test[1], position.width)
1169    call popup_close(winid)
1170  endfor
1171endfunc
1172
1173func Test_popup_wraps()
1174  let tests = [
1175	\ ['nowrap', 6, 1],
1176	\ ['a line that wraps once', 12, 2],
1177	\ ['a line that wraps two times', 12, 3],
1178	\ ]
1179  for test in tests
1180    let winid = popup_create(test[0],
1181	  \ #{line: 2, col: 3, maxwidth: 12})
1182    redraw
1183    let position = popup_getpos(winid)
1184    call assert_equal(test[1], position.width)
1185    call assert_equal(test[2], position.height)
1186
1187    call popup_close(winid)
1188    call assert_equal({}, popup_getpos(winid))
1189  endfor
1190endfunc
1191
1192func Test_popup_getoptions()
1193  let winid = popup_create('hello', #{
1194    \ line: 2,
1195    \ col: 3,
1196    \ minwidth: 10,
1197    \ minheight: 11,
1198    \ maxwidth: 20,
1199    \ maxheight: 21,
1200    \ zindex: 100,
1201    \ time: 5000,
1202    \ fixed: 1
1203    \})
1204  redraw
1205  let res = popup_getoptions(winid)
1206  call assert_equal(2, res.line)
1207  call assert_equal(3, res.col)
1208  call assert_equal(10, res.minwidth)
1209  call assert_equal(11, res.minheight)
1210  call assert_equal(20, res.maxwidth)
1211  call assert_equal(21, res.maxheight)
1212  call assert_equal(100, res.zindex)
1213  call assert_equal(1, res.fixed)
1214  call assert_equal(1, res.mapping)
1215  if has('timers')
1216    call assert_equal(5000, res.time)
1217  endif
1218  call popup_close(winid)
1219
1220  let winid = popup_create('hello', {})
1221  redraw
1222  let res = popup_getoptions(winid)
1223  call assert_equal(0, res.line)
1224  call assert_equal(0, res.col)
1225  call assert_equal(0, res.minwidth)
1226  call assert_equal(0, res.minheight)
1227  call assert_equal(0, res.maxwidth)
1228  call assert_equal(0, res.maxheight)
1229  call assert_equal(50, res.zindex)
1230  call assert_equal(0, res.fixed)
1231  if has('timers')
1232    call assert_equal(0, res.time)
1233  endif
1234  call popup_close(winid)
1235  call assert_equal({}, popup_getoptions(winid))
1236endfunc
1237
1238func Test_popup_option_values()
1239  new
1240  " window-local
1241  setlocal number
1242  setlocal nowrap
1243  " buffer-local
1244  setlocal omnifunc=Something
1245  " global/buffer-local
1246  setlocal path=/there
1247  " global/window-local
1248  setlocal statusline=2
1249
1250  let winid = popup_create('hello', {})
1251  call assert_equal(0, getwinvar(winid, '&number'))
1252  call assert_equal(1, getwinvar(winid, '&wrap'))
1253  call assert_equal('', getwinvar(winid, '&omnifunc'))
1254  call assert_equal(&g:path, getwinvar(winid, '&path'))
1255  call assert_equal(&g:statusline, getwinvar(winid, '&statusline'))
1256
1257  " 'scrolloff' is reset to zero
1258  call assert_equal(5, &scrolloff)
1259  call assert_equal(0, getwinvar(winid, '&scrolloff'))
1260
1261  call popup_close(winid)
1262  bwipe
1263endfunc
1264
1265func Test_popup_atcursor()
1266  topleft vnew
1267  call setline(1, [
1268  \  'xxxxxxxxxxxxxxxxx',
1269  \  'xxxxxxxxxxxxxxxxx',
1270  \  'xxxxxxxxxxxxxxxxx',
1271  \])
1272
1273  call cursor(2, 2)
1274  redraw
1275  let winid = popup_atcursor('vim', {})
1276  redraw
1277  let line = join(map(range(1, 17), 'screenstring(1, v:val)'), '')
1278  call assert_equal('xvimxxxxxxxxxxxxx', line)
1279  call popup_close(winid)
1280
1281  call cursor(3, 4)
1282  redraw
1283  let winid = 'vim'->popup_atcursor({})
1284  redraw
1285  let line = join(map(range(1, 17), 'screenstring(2, v:val)'), '')
1286  call assert_equal('xxxvimxxxxxxxxxxx', line)
1287  call popup_close(winid)
1288
1289  call cursor(1, 1)
1290  redraw
1291  let winid = popup_create('vim', #{
1292	\ line: 'cursor+2',
1293	\ col: 'cursor+1',
1294	\})
1295  redraw
1296  let line = join(map(range(1, 17), 'screenstring(3, v:val)'), '')
1297  call assert_equal('xvimxxxxxxxxxxxxx', line)
1298  call popup_close(winid)
1299
1300  call cursor(3, 3)
1301  redraw
1302  let winid = popup_create('vim', #{
1303	\ line: 'cursor-2',
1304	\ col: 'cursor-1',
1305	\})
1306  redraw
1307  let line = join(map(range(1, 17), 'screenstring(1, v:val)'), '')
1308  call assert_equal('xvimxxxxxxxxxxxxx', line)
1309  call popup_close(winid)
1310
1311  " just enough room above
1312  call cursor(3, 3)
1313  redraw
1314  let winid = popup_atcursor(['vim', 'is great'], {})
1315  redraw
1316  let pos = popup_getpos(winid)
1317  call assert_equal(1, pos.line)
1318  call popup_close(winid)
1319
1320  " not enough room above, popup goes below the cursor
1321  call cursor(3, 3)
1322  redraw
1323  let winid = popup_atcursor(['vim', 'is', 'great'], {})
1324  redraw
1325  let pos = popup_getpos(winid)
1326  call assert_equal(4, pos.line)
1327  call popup_close(winid)
1328
1329  " cursor in first line, popup in line 2
1330  call cursor(1, 1)
1331  redraw
1332  let winid = popup_atcursor(['vim', 'is', 'great'], {})
1333  redraw
1334  let pos = popup_getpos(winid)
1335  call assert_equal(2, pos.line)
1336  call popup_close(winid)
1337
1338  bwipe!
1339endfunc
1340
1341func Test_popup_atcursor_pos()
1342  CheckScreendump
1343
1344  let lines =<< trim END
1345	call setline(1, repeat([repeat('-', 60)], 15))
1346	set so=0
1347
1348	normal 9G3|r#
1349	let winid1 = popup_atcursor(['first', 'second'], #{
1350	      \ moved: [0, 0, 0],
1351	      \ })
1352	normal 9G21|r&
1353	let winid1 = popup_atcursor(['FIrsT', 'SEcoND'], #{
1354	      \ pos: 'botright',
1355	      \ moved: [0, 0, 0],
1356	      \ })
1357	normal 3G27|r%
1358	let winid1 = popup_atcursor(['fiRSt', 'seCOnd'], #{
1359	      \ pos: 'topleft',
1360	      \ moved: [0, 0, 0],
1361	      \ })
1362	normal 3G45|r@
1363	let winid1 = popup_atcursor(['First', 'SeconD'], #{
1364	      \ pos: 'topright',
1365	      \ moved: range(3),
1366	      \ mousemoved: range(3),
1367	      \ })
1368  END
1369  call writefile(lines, 'XtestPopupAtcursorPos')
1370  let buf = RunVimInTerminal('-S XtestPopupAtcursorPos', #{rows: 12})
1371  call VerifyScreenDump(buf, 'Test_popupwin_atcursor_pos', {})
1372
1373  " clean up
1374  call StopVimInTerminal(buf)
1375  call delete('XtestPopupAtcursorPos')
1376endfunc
1377
1378func Test_popup_beval()
1379  CheckScreendump
1380  CheckFeature balloon_eval_term
1381
1382  let lines =<< trim END
1383	call setline(1, range(1, 20))
1384	call setline(5, 'here is some text to hover over')
1385	set balloonevalterm
1386	set balloonexpr=BalloonExpr()
1387	set balloondelay=100
1388	func BalloonExpr()
1389	  let s:winid = [v:beval_text]->popup_beval({})
1390	  return ''
1391	endfunc
1392	func Hover()
1393	  call test_setmouse(5, 15)
1394	  call feedkeys("\<MouseMove>\<Ignore>", "xt")
1395	  sleep 100m
1396	endfunc
1397	func MoveOntoPopup()
1398	  call test_setmouse(4, 17)
1399	  call feedkeys("\<F4>\<MouseMove>\<Ignore>", "xt")
1400	endfunc
1401	func MoveAway()
1402	  call test_setmouse(5, 13)
1403	  call feedkeys("\<F5>\<MouseMove>\<Ignore>", "xt")
1404	endfunc
1405  END
1406  call writefile(lines, 'XtestPopupBeval')
1407  let buf = RunVimInTerminal('-S XtestPopupBeval', #{rows: 10})
1408  call TermWait(buf, 50)
1409  call term_sendkeys(buf, 'j')
1410  call term_sendkeys(buf, ":call Hover()\<CR>")
1411  call VerifyScreenDump(buf, 'Test_popupwin_beval_1', {})
1412
1413  call term_sendkeys(buf, ":call MoveOntoPopup()\<CR>")
1414  call VerifyScreenDump(buf, 'Test_popupwin_beval_2', {})
1415
1416  call term_sendkeys(buf, ":call MoveAway()\<CR>")
1417  call VerifyScreenDump(buf, 'Test_popupwin_beval_3', {})
1418
1419  " clean up
1420  call StopVimInTerminal(buf)
1421  call delete('XtestPopupBeval')
1422endfunc
1423
1424func Test_popup_filter()
1425  new
1426  call setline(1, 'some text')
1427
1428  func MyPopupFilter(winid, c)
1429    if a:c == 'e' || a:c == "\<F9>"
1430      let g:eaten = a:c
1431      return 1
1432    endif
1433    if a:c == '0'
1434      let g:ignored = '0'
1435      return 0
1436    endif
1437    if a:c == 'x'
1438      call popup_close(a:winid)
1439      return 1
1440    endif
1441    return 0
1442  endfunc
1443
1444  let winid = 'something'->popup_create(#{filter: 'MyPopupFilter'})
1445  redraw
1446
1447  " e is consumed by the filter
1448  call feedkeys('e', 'xt')
1449  call assert_equal('e', g:eaten)
1450  call feedkeys("\<F9>", 'xt')
1451  call assert_equal("\<F9>", g:eaten)
1452
1453  " 0 is ignored by the filter
1454  normal $
1455  call assert_equal(9, getcurpos()[2])
1456  call feedkeys('0', 'xt')
1457  call assert_equal('0', g:ignored)
1458  call assert_equal(1, getcurpos()[2])
1459
1460  " x closes the popup
1461  call feedkeys('x', 'xt')
1462  call assert_equal("\<F9>", g:eaten)
1463  call assert_equal(-1, winbufnr(winid))
1464
1465  delfunc MyPopupFilter
1466  call popup_clear()
1467endfunc
1468
1469func ShowDialog(key, result)
1470  let s:cb_res = 999
1471  let winid = popup_dialog('do you want to quit (Yes/no)?', #{
1472	  \ filter: 'popup_filter_yesno',
1473	  \ callback: 'QuitCallback',
1474	  \ })
1475  redraw
1476  call feedkeys(a:key, "xt")
1477  call assert_equal(winid, s:cb_winid)
1478  call assert_equal(a:result, s:cb_res)
1479endfunc
1480
1481func Test_popup_dialog()
1482  func QuitCallback(id, res)
1483    let s:cb_winid = a:id
1484    let s:cb_res = a:res
1485  endfunc
1486
1487  let winid = ShowDialog("y", 1)
1488  let winid = ShowDialog("Y", 1)
1489  let winid = ShowDialog("n", 0)
1490  let winid = ShowDialog("N", 0)
1491  let winid = ShowDialog("x", 0)
1492  let winid = ShowDialog("X", 0)
1493  let winid = ShowDialog("\<Esc>", 0)
1494  let winid = ShowDialog("\<C-C>", -1)
1495
1496  delfunc QuitCallback
1497endfunc
1498
1499func ShowMenu(key, result)
1500  let s:cb_res = 999
1501  let winid = popup_menu(['one', 'two', 'something else'], #{
1502	  \ callback: 'QuitCallback',
1503	  \ })
1504  redraw
1505  call feedkeys(a:key, "xt")
1506  call assert_equal(winid, s:cb_winid)
1507  call assert_equal(a:result, s:cb_res)
1508endfunc
1509
1510func Test_popup_menu()
1511  func QuitCallback(id, res)
1512    let s:cb_winid = a:id
1513    let s:cb_res = a:res
1514  endfunc
1515  " mapping won't be used in popup
1516  map j k
1517
1518  let winid = ShowMenu(" ", 1)
1519  let winid = ShowMenu("j \<CR>", 2)
1520  let winid = ShowMenu("JjK \<CR>", 2)
1521  let winid = ShowMenu("jjjjjj ", 3)
1522  let winid = ShowMenu("kkk ", 1)
1523  let winid = ShowMenu("x", -1)
1524  let winid = ShowMenu("X", -1)
1525  let winid = ShowMenu("\<Esc>", -1)
1526  let winid = ShowMenu("\<C-C>", -1)
1527
1528  delfunc QuitCallback
1529  unmap j
1530endfunc
1531
1532func Test_popup_menu_screenshot()
1533  CheckScreendump
1534
1535  let lines =<< trim END
1536	call setline(1, range(1, 20))
1537	hi PopupSelected ctermbg=lightblue
1538	call popup_menu(['one', 'two', 'another'], #{callback: 'MenuDone', title: ' make a choice from the list '})
1539	func MenuDone(id, res)
1540	  echomsg "selected " .. a:res
1541	endfunc
1542  END
1543  call writefile(lines, 'XtestPopupMenu')
1544  let buf = RunVimInTerminal('-S XtestPopupMenu', #{rows: 10})
1545  call VerifyScreenDump(buf, 'Test_popupwin_menu_01', {})
1546
1547  call term_sendkeys(buf, "jj")
1548  call VerifyScreenDump(buf, 'Test_popupwin_menu_02', {})
1549
1550  call term_sendkeys(buf, " ")
1551  call VerifyScreenDump(buf, 'Test_popupwin_menu_03', {})
1552
1553  " clean up
1554  call StopVimInTerminal(buf)
1555  call delete('XtestPopupMenu')
1556endfunc
1557
1558func Test_popup_menu_narrow()
1559  CheckScreendump
1560
1561  let lines =<< trim END
1562	call setline(1, range(1, 20))
1563	hi PopupSelected ctermbg=green
1564	call popup_menu(['one', 'two', 'three'], #{callback: 'MenuDone'})
1565	func MenuDone(id, res)
1566	  echomsg "selected " .. a:res
1567	endfunc
1568  END
1569  call writefile(lines, 'XtestPopupNarrowMenu')
1570  let buf = RunVimInTerminal('-S XtestPopupNarrowMenu', #{rows: 10})
1571  call VerifyScreenDump(buf, 'Test_popupwin_menu_04', {})
1572
1573  " clean up
1574  call term_sendkeys(buf, "x")
1575  call StopVimInTerminal(buf)
1576  call delete('XtestPopupNarrowMenu')
1577endfunc
1578
1579func Test_popup_title()
1580  CheckScreendump
1581
1582  " Create a popup without title or border, a line of padding will be added to
1583  " put the title on.
1584  let lines =<< trim END
1585	call setline(1, range(1, 20))
1586	let winid = popup_create(['one', 'two', 'another'], #{title: 'Title String'})
1587  END
1588  call writefile(lines, 'XtestPopupTitle')
1589  let buf = RunVimInTerminal('-S XtestPopupTitle', #{rows: 10})
1590  call VerifyScreenDump(buf, 'Test_popupwin_title', {})
1591
1592  call term_sendkeys(buf, ":call popup_setoptions(winid, #{maxwidth: 20, title: 'a very long title that is not going to fit'})\<CR>")
1593  call term_sendkeys(buf, ":\<CR>")
1594  call VerifyScreenDump(buf, 'Test_popupwin_longtitle_1', {})
1595
1596  call term_sendkeys(buf, ":call popup_setoptions(winid, #{border: []})\<CR>")
1597  call term_sendkeys(buf, ":\<CR>")
1598  call VerifyScreenDump(buf, 'Test_popupwin_longtitle_2', {})
1599
1600  " clean up
1601  call StopVimInTerminal(buf)
1602  call delete('XtestPopupTitle')
1603
1604  let winid = popup_create('something', #{title: 'Some Title'})
1605  call assert_equal('Some Title', popup_getoptions(winid).title)
1606  call popup_setoptions(winid, #{title: 'Another Title'})
1607  call assert_equal('Another Title', popup_getoptions(winid).title)
1608
1609  call popup_clear()
1610endfunc
1611
1612func Test_popup_close_callback()
1613  func PopupDone(id, result)
1614    let g:result = a:result
1615  endfunc
1616  let winid = popup_create('something', #{callback: 'PopupDone'})
1617  redraw
1618  call popup_close(winid, 'done')
1619  call assert_equal('done', g:result)
1620endfunc
1621
1622func Test_popup_empty()
1623  let winid = popup_create('', #{padding: [2,2,2,2]})
1624  redraw
1625  let pos = popup_getpos(winid)
1626  call assert_equal(5, pos.width)
1627  call assert_equal(5, pos.height)
1628  call popup_close(winid)
1629
1630  let winid = popup_create([], #{border: []})
1631  redraw
1632  let pos = popup_getpos(winid)
1633  call assert_equal(3, pos.width)
1634  call assert_equal(3, pos.height)
1635  call popup_close(winid)
1636endfunc
1637
1638func Test_popup_never_behind()
1639  CheckScreendump
1640
1641  " +-----------------------------+
1642  " |             |               |
1643  " |             |               |
1644  " |             |               |
1645  " |            line1            |
1646  " |------------line2------------|
1647  " |            line3            |
1648  " |            line4            |
1649  " |                             |
1650  " |                             |
1651  " +-----------------------------+
1652  let lines =<< trim END
1653    split
1654    vsplit
1655    let info_window1 = getwininfo()[0]
1656    let line = info_window1['height']
1657    let col = info_window1['width']
1658    call popup_create(['line1', 'line2', 'line3', 'line4'], #{
1659	      \   line : line,
1660	      \   col : col,
1661	      \ })
1662  END
1663  call writefile(lines, 'XtestPopupBehind')
1664  let buf = RunVimInTerminal('-S XtestPopupBehind', #{rows: 10})
1665  call term_sendkeys(buf, "\<C-W>w")
1666  call VerifyScreenDump(buf, 'Test_popupwin_behind', {})
1667
1668  " clean up
1669  call StopVimInTerminal(buf)
1670  call delete('XtestPopupBehind')
1671endfunc
1672
1673func s:VerifyPosition(p, msg, line, col, width, height)
1674  call assert_equal(a:line,   popup_getpos(a:p).line,   a:msg . ' (l)')
1675  call assert_equal(a:col,    popup_getpos(a:p).col,    a:msg . ' (c)')
1676  call assert_equal(a:width,  popup_getpos(a:p).width,  a:msg . ' (w)')
1677  call assert_equal(a:height, popup_getpos(a:p).height, a:msg . ' (h)')
1678endfunc
1679
1680func Test_popup_position_adjust()
1681  " Anything placed past the last cell on the right of the screen is moved to
1682  " the left.
1683  "
1684  " When wrapping is disabled, we also shift to the left to display on the
1685  " screen, unless fixed is set.
1686
1687  " Entries for cases which don't vary based on wrapping.
1688  " Format is per tests described below
1689  let both_wrap_tests = [
1690	\       ['a', 5, &columns,        5, &columns, 1, 1],
1691	\       ['b', 5, &columns + 1,    5, &columns, 1, 1],
1692	\       ['c', 5, &columns - 1,    5, &columns - 1, 1, 1],
1693	\       ['d', 5, &columns - 2,    5, &columns - 2, 1, 1],
1694	\       ['e', 5, &columns - 3,    5, &columns - 3, 1, 1]]
1695
1696  " these test groups are dicts with:
1697  "  - comment: something to identify the group of tests by
1698  "  - options: dict of options to merge with the row/col in tests
1699  "  - tests: list of cases. Each one is a list with elements:
1700  "     - text
1701  "     - row
1702  "     - col
1703  "     - expected row
1704  "     - expected col
1705  "     - expected width
1706  "     - expected height
1707  let tests = [
1708	\ #{
1709	\   comment: 'left-aligned with wrapping',
1710	\   options: #{
1711	\     wrap: 1,
1712	\     pos: 'botleft',
1713	\   },
1714	\   tests: both_wrap_tests + [
1715	\       ['aa', 5, &columns,        4, &columns, 1, 2],
1716	\       ['bb', 5, &columns + 1,    4, &columns, 1, 2],
1717	\       ['cc', 5, &columns - 1,    5, &columns - 1, 2, 1],
1718	\       ['dd', 5, &columns - 2,    5, &columns - 2, 2, 1],
1719	\       ['ee', 5, &columns - 3,    5, &columns - 3, 2, 1],
1720	\
1721	\       ['aaa', 5, &columns,        3, &columns, 1, 3],
1722	\       ['bbb', 5, &columns + 1,    3, &columns, 1, 3],
1723	\       ['ccc', 5, &columns - 1,    4, &columns - 1, 2, 2],
1724	\       ['ddd', 5, &columns - 2,    5, &columns - 2, 3, 1],
1725	\       ['eee', 5, &columns - 3,    5, &columns - 3, 3, 1],
1726	\
1727	\       ['aaaa', 5, &columns,        2, &columns, 1, 4],
1728	\       ['bbbb', 5, &columns + 1,    2, &columns, 1, 4],
1729	\       ['cccc', 5, &columns - 1,    4, &columns - 1, 2, 2],
1730	\       ['dddd', 5, &columns - 2,    4, &columns - 2, 3, 2],
1731	\       ['eeee', 5, &columns - 3,    5, &columns - 3, 4, 1],
1732	\       ['eeee', 5, &columns - 4,    5, &columns - 4, 4, 1],
1733	\   ],
1734	\ },
1735	\ #{
1736	\   comment: 'left aligned without wrapping',
1737	\   options: #{
1738	\     wrap: 0,
1739	\     pos: 'botleft',
1740	\   },
1741	\   tests: both_wrap_tests + [
1742	\       ['aa', 5, &columns,        5, &columns - 1, 2, 1],
1743	\       ['bb', 5, &columns + 1,    5, &columns - 1, 2, 1],
1744	\       ['cc', 5, &columns - 1,    5, &columns - 1, 2, 1],
1745	\       ['dd', 5, &columns - 2,    5, &columns - 2, 2, 1],
1746	\       ['ee', 5, &columns - 3,    5, &columns - 3, 2, 1],
1747	\
1748	\       ['aaa', 5, &columns,        5, &columns - 2, 3, 1],
1749	\       ['bbb', 5, &columns + 1,    5, &columns - 2, 3, 1],
1750	\       ['ccc', 5, &columns - 1,    5, &columns - 2, 3, 1],
1751	\       ['ddd', 5, &columns - 2,    5, &columns - 2, 3, 1],
1752	\       ['eee', 5, &columns - 3,    5, &columns - 3, 3, 1],
1753	\
1754	\       ['aaaa', 5, &columns,        5, &columns - 3, 4, 1],
1755	\       ['bbbb', 5, &columns + 1,    5, &columns - 3, 4, 1],
1756	\       ['cccc', 5, &columns - 1,    5, &columns - 3, 4, 1],
1757	\       ['dddd', 5, &columns - 2,    5, &columns - 3, 4, 1],
1758	\       ['eeee', 5, &columns - 3,    5, &columns - 3, 4, 1],
1759	\   ],
1760	\ },
1761	\ #{
1762	\   comment: 'left aligned with fixed position',
1763	\   options: #{
1764	\     wrap: 0,
1765	\     fixed: 1,
1766	\     pos: 'botleft',
1767	\   },
1768	\   tests: both_wrap_tests + [
1769	\       ['aa', 5, &columns,        5, &columns, 1, 1],
1770	\       ['bb', 5, &columns + 1,    5, &columns, 1, 1],
1771	\       ['cc', 5, &columns - 1,    5, &columns - 1, 2, 1],
1772	\       ['dd', 5, &columns - 2,    5, &columns - 2, 2, 1],
1773	\       ['ee', 5, &columns - 3,    5, &columns - 3, 2, 1],
1774	\
1775	\       ['aaa', 5, &columns,        5, &columns, 1, 1],
1776	\       ['bbb', 5, &columns + 1,    5, &columns, 1, 1],
1777	\       ['ccc', 5, &columns - 1,    5, &columns - 1, 2, 1],
1778	\       ['ddd', 5, &columns - 2,    5, &columns - 2, 3, 1],
1779	\       ['eee', 5, &columns - 3,    5, &columns - 3, 3, 1],
1780	\
1781	\       ['aaaa', 5, &columns,        5, &columns, 1, 1],
1782	\       ['bbbb', 5, &columns + 1,    5, &columns, 1, 1],
1783	\       ['cccc', 5, &columns - 1,    5, &columns - 1, 2, 1],
1784	\       ['dddd', 5, &columns - 2,    5, &columns - 2, 3, 1],
1785	\       ['eeee', 5, &columns - 3,    5, &columns - 3, 4, 1],
1786	\   ],
1787	\ },
1788	\ ]
1789
1790  for test_group in tests
1791    for test in test_group.tests
1792      let [ text, line, col, e_line, e_col, e_width, e_height ] = test
1793      let options = #{
1794	    \ line: line,
1795	    \ col: col,
1796	    \ }
1797      call extend(options, test_group.options)
1798
1799      let p = popup_create(text, options)
1800
1801      let msg = string(extend(options, #{text: text}))
1802      call s:VerifyPosition(p, msg, e_line, e_col, e_width, e_height)
1803      call popup_close(p)
1804    endfor
1805  endfor
1806
1807  call popup_clear()
1808  %bwipe!
1809endfunc
1810
1811func Test_adjust_left_past_screen_width()
1812  " width of screen
1813  let X = join(map(range(&columns), {->'X'}), '')
1814
1815  let p = popup_create(X, #{line: 1, col: 1, wrap: 0})
1816  call s:VerifyPosition(p, 'full width topleft', 1, 1, &columns, 1)
1817
1818  redraw
1819  let line = join(map(range(1, &columns + 1), 'screenstring(1, v:val)'), '')
1820  call assert_equal(X, line)
1821
1822  call popup_close(p)
1823  redraw
1824
1825  " Same if placed on the right hand side
1826  let p = popup_create(X, #{line: 1, col: &columns, wrap: 0})
1827  call s:VerifyPosition(p, 'full width topright', 1, 1, &columns, 1)
1828
1829  redraw
1830  let line = join(map(range(1, &columns + 1), 'screenstring(1, v:val)'), '')
1831  call assert_equal(X, line)
1832
1833  call popup_close(p)
1834  redraw
1835
1836  " Extend so > window width
1837  let X .= 'x'
1838
1839  let p = popup_create(X, #{line: 1, col: 1, wrap: 0})
1840  call s:VerifyPosition(p, 'full width +  1 topleft', 1, 1, &columns, 1)
1841
1842  redraw
1843  let line = join(map(range(1, &columns + 1), 'screenstring(1, v:val)'), '')
1844  call assert_equal(X[ : -2 ], line)
1845
1846  call popup_close(p)
1847  redraw
1848
1849  " Shifted then truncated (the x is not visible)
1850  let p = popup_create(X, #{line: 1, col: &columns - 3, wrap: 0})
1851  call s:VerifyPosition(p, 'full width + 1 topright', 1, 1, &columns, 1)
1852
1853  redraw
1854  let line = join(map(range(1, &columns + 1), 'screenstring(1, v:val)'), '')
1855  call assert_equal(X[ : -2 ], line)
1856
1857  call popup_close(p)
1858  redraw
1859
1860  " Not shifted, just truncated
1861  let p = popup_create(X,
1862	\ #{line: 1, col: 2, wrap: 0, fixed: 1})
1863  call s:VerifyPosition(p, 'full width + 1 fixed', 1, 2, &columns - 1, 1)
1864
1865  redraw
1866  let line = join(map(range(1, &columns + 1), 'screenstring(1, v:val)'), '')
1867  let e_line = ' ' . X[ 1 : -2 ]
1868  call assert_equal(e_line, line)
1869
1870  call popup_close(p)
1871  redraw
1872
1873  call popup_clear()
1874  %bwipe!
1875endfunc
1876
1877func Test_popup_moved()
1878  new
1879  call test_override('char_avail', 1)
1880  call setline(1, ['one word to move around', 'a WORD.and->some thing'])
1881
1882  exe "normal gg0/word\<CR>"
1883  let winid = popup_atcursor('text', #{moved: 'any'})
1884  redraw
1885  call assert_equal(1, popup_getpos(winid).visible)
1886  call assert_equal([1, 4, 4], popup_getoptions(winid).moved)
1887  " trigger the check for last_cursormoved by going into insert mode
1888  call feedkeys("li\<Esc>", 'xt')
1889  call assert_equal({}, popup_getpos(winid))
1890  call popup_clear()
1891
1892  exe "normal gg0/word\<CR>"
1893  let winid = popup_atcursor('text', #{moved: 'word'})
1894  redraw
1895  call assert_equal(1, popup_getpos(winid).visible)
1896  call assert_equal([1, 4, 7], popup_getoptions(winid).moved)
1897  call feedkeys("hi\<Esc>", 'xt')
1898  call assert_equal({}, popup_getpos(winid))
1899  call popup_clear()
1900
1901  exe "normal gg0/word\<CR>"
1902  let winid = popup_atcursor('text', #{moved: 'word'})
1903  redraw
1904  call assert_equal(1, popup_getpos(winid).visible)
1905  call assert_equal([1, 4, 7], popup_getoptions(winid).moved)
1906  call feedkeys("li\<Esc>", 'xt')
1907  call assert_equal(1, popup_getpos(winid).visible)
1908  call feedkeys("ei\<Esc>", 'xt')
1909  call assert_equal(1, popup_getpos(winid).visible)
1910  call feedkeys("eli\<Esc>", 'xt')
1911  call assert_equal({}, popup_getpos(winid))
1912  call popup_clear()
1913
1914  " WORD is the default
1915  exe "normal gg0/WORD\<CR>"
1916  let winid = popup_atcursor('text', {})
1917  redraw
1918  call assert_equal(1, popup_getpos(winid).visible)
1919  call assert_equal([2, 2, 15], popup_getoptions(winid).moved)
1920  call feedkeys("eli\<Esc>", 'xt')
1921  call assert_equal(1, popup_getpos(winid).visible)
1922  call feedkeys("wi\<Esc>", 'xt')
1923  call assert_equal(1, popup_getpos(winid).visible)
1924  call feedkeys("Eli\<Esc>", 'xt')
1925  call assert_equal({}, popup_getpos(winid))
1926  call popup_clear()
1927
1928  exe "normal gg0/word\<CR>"
1929  let winid = popup_atcursor('text', #{moved: [5, 10]})
1930  redraw
1931  call assert_equal(1, popup_getpos(winid).visible)
1932  call feedkeys("eli\<Esc>", 'xt')
1933  call feedkeys("ei\<Esc>", 'xt')
1934  call assert_equal(1, popup_getpos(winid).visible)
1935  call feedkeys("eli\<Esc>", 'xt')
1936  call assert_equal({}, popup_getpos(winid))
1937  call popup_clear()
1938
1939  bwipe!
1940  call test_override('ALL', 0)
1941endfunc
1942
1943func Test_notifications()
1944  CheckFeature timers
1945  CheckScreendump
1946
1947  let lines =<< trim END
1948	call setline(1, range(1, 20))
1949	hi Notification ctermbg=lightblue
1950	call popup_notification('first notification', {})
1951  END
1952  call writefile(lines, 'XtestNotifications')
1953  let buf = RunVimInTerminal('-S XtestNotifications', #{rows: 10})
1954  call VerifyScreenDump(buf, 'Test_popupwin_notify_01', {})
1955
1956  " second one goes below the first one
1957  call term_sendkeys(buf, ":hi link PopupNotification Notification\<CR>")
1958  call term_sendkeys(buf, ":call popup_notification('another important notification', {})\<CR>")
1959  call VerifyScreenDump(buf, 'Test_popupwin_notify_02', {})
1960
1961  " clean up
1962  call StopVimInTerminal(buf)
1963  call delete('XtestNotifications')
1964endfunc
1965
1966func Test_popup_scrollbar()
1967  CheckScreendump
1968
1969  let lines =<< trim END
1970    call setline(1, range(1, 20))
1971    hi ScrollThumb ctermbg=blue
1972    hi ScrollBar ctermbg=red
1973    let winid = popup_create(['one', 'two', 'three', 'four', 'five',
1974	  \ 'six', 'seven', 'eight', 'nine'], #{
1975	  \ minwidth: 8,
1976	  \ maxheight: 4,
1977	  \ })
1978    func ScrollUp()
1979      call feedkeys("\<F3>\<ScrollWheelUp>", "xt")
1980    endfunc
1981    func ScrollDown()
1982      call feedkeys("\<F3>\<ScrollWheelDown>", "xt")
1983    endfunc
1984    func ClickTop()
1985      call feedkeys("\<F4>\<LeftMouse>", "xt")
1986    endfunc
1987    func ClickBot()
1988      call popup_setoptions(g:winid, #{border: [], close: 'button'})
1989      call feedkeys("\<F5>\<LeftMouse>", "xt")
1990    endfunc
1991    func Popup_filter(winid, key)
1992      if a:key == 'j'
1993	let line = popup_getoptions(a:winid).firstline
1994	let nlines = line('$', a:winid)
1995	let newline = line < nlines ? (line + 1) : nlines
1996	call popup_setoptions(a:winid, #{firstline: newline})
1997	return v:true
1998      elseif a:key == 'x'
1999	call popup_close(a:winid)
2000	return v:true
2001      endif
2002    endfunc
2003
2004    func PopupScroll()
2005      call popup_clear()
2006      let text =<< trim END
2007	  1
2008	  2
2009	  3
2010	  4
2011	  long line long line long line long line long line long line
2012	  long line long line long line long line long line long line
2013	  long line long line long line long line long line long line
2014      END
2015      call popup_create(text, #{
2016	    \ minwidth: 30,
2017	    \ maxwidth: 30,
2018	    \ minheight: 4,
2019	    \ maxheight: 4,
2020	    \ firstline: 1,
2021	    \ lastline: 4,
2022	    \ wrap: v:true,
2023	    \ scrollbar: v:true,
2024	    \ mapping: v:false,
2025	    \ filter: funcref('Popup_filter')
2026	    \ })
2027    endfunc
2028    map <silent> <F3> :call test_setmouse(5, 36)<CR>
2029    map <silent> <F4> :call test_setmouse(4, 42)<CR>
2030    map <silent> <F5> :call test_setmouse(7, 42)<CR>
2031  END
2032  call writefile(lines, 'XtestPopupScroll')
2033  let buf = RunVimInTerminal('-S XtestPopupScroll', #{rows: 10})
2034  call VerifyScreenDump(buf, 'Test_popupwin_scroll_1', {})
2035
2036  call term_sendkeys(buf, ":call popup_setoptions(winid, #{firstline: 2})\<CR>")
2037  call term_sendkeys(buf, ":\<CR>")
2038  call VerifyScreenDump(buf, 'Test_popupwin_scroll_2', {})
2039
2040  call term_sendkeys(buf, ":call popup_setoptions(winid, #{firstline: 6})\<CR>")
2041  call term_sendkeys(buf, ":\<CR>")
2042  call VerifyScreenDump(buf, 'Test_popupwin_scroll_3', {})
2043
2044  call term_sendkeys(buf, ":call popup_setoptions(winid, #{firstline: 9})\<CR>")
2045  call term_sendkeys(buf, ":\<CR>")
2046  call VerifyScreenDump(buf, 'Test_popupwin_scroll_4', {})
2047
2048  call term_sendkeys(buf, ":call popup_setoptions(winid, #{scrollbarhighlight: 'ScrollBar', thumbhighlight: 'ScrollThumb', firstline: 5})\<CR>")
2049  " this scrolls two lines (half the window height)
2050  call term_sendkeys(buf, ":call ScrollUp()\<CR>")
2051  call VerifyScreenDump(buf, 'Test_popupwin_scroll_5', {})
2052
2053  call term_sendkeys(buf, ":call ScrollDown()\<CR>")
2054  call VerifyScreenDump(buf, 'Test_popupwin_scroll_6', {})
2055
2056  call term_sendkeys(buf, ":call ScrollDown()\<CR>")
2057  " wait a bit, otherwise it fails sometimes (double click recognized?)
2058  sleep 100m
2059  call term_sendkeys(buf, ":call ScrollDown()\<CR>")
2060  call VerifyScreenDump(buf, 'Test_popupwin_scroll_7', {})
2061
2062  call term_sendkeys(buf, ":call ClickTop()\<CR>")
2063  sleep 100m
2064  call term_sendkeys(buf, ":call ClickTop()\<CR>")
2065  call VerifyScreenDump(buf, 'Test_popupwin_scroll_8', {})
2066
2067  call term_sendkeys(buf, ":call ClickBot()\<CR>")
2068  call VerifyScreenDump(buf, 'Test_popupwin_scroll_9', {})
2069
2070  " remove the minwidth and maxheight
2071  call term_sendkeys(buf, ":call popup_setoptions(winid, #{maxheight: 0, minwidth: 0})\<CR>")
2072  call term_sendkeys(buf, ":\<CR>")
2073  call VerifyScreenDump(buf, 'Test_popupwin_scroll_10', {})
2074
2075  " check size with non-wrapping lines
2076  call term_sendkeys(buf, ":call PopupScroll()\<CR>")
2077  call VerifyScreenDump(buf, 'Test_popupwin_scroll_11', {})
2078
2079  " check size with wrapping lines
2080  call term_sendkeys(buf, "j")
2081  call VerifyScreenDump(buf, 'Test_popupwin_scroll_12', {})
2082  call term_sendkeys(buf, "x")
2083
2084  " clean up
2085  call StopVimInTerminal(buf)
2086  call delete('XtestPopupScroll')
2087endfunc
2088
2089func Test_popup_fitting_scrollbar()
2090  " this was causing a crash, divide by zero
2091  let winid = popup_create([
2092	\ 'one', 'two', 'longer line that wraps', 'four', 'five'], #{
2093	\ scrollbar: 1,
2094	\ maxwidth: 10,
2095	\ maxheight: 5,
2096	\ firstline: 2})
2097  redraw
2098  call popup_clear()
2099endfunc
2100
2101func Test_popup_settext()
2102  CheckScreendump
2103
2104  let lines =<< trim END
2105    let opts = #{wrap: 0}
2106    let p = popup_create('test', opts)
2107    eval p->popup_settext('this is a text')
2108  END
2109
2110  call writefile(lines, 'XtestPopupSetText')
2111  let buf = RunVimInTerminal('-S XtestPopupSetText', #{rows: 10})
2112  call VerifyScreenDump(buf, 'Test_popup_settext_01', {})
2113
2114  " Setting to empty string clears it
2115  call term_sendkeys(buf, ":call popup_settext(p, '')\<CR>")
2116  call VerifyScreenDump(buf, 'Test_popup_settext_02', {})
2117
2118  " Setting a list
2119  call term_sendkeys(buf, ":call popup_settext(p, ['a','b','c'])\<CR>")
2120  call VerifyScreenDump(buf, 'Test_popup_settext_03', {})
2121
2122  " Shrinking with a list
2123  call term_sendkeys(buf, ":call popup_settext(p, ['a'])\<CR>")
2124  call VerifyScreenDump(buf, 'Test_popup_settext_04', {})
2125
2126  " Growing with a list
2127  call term_sendkeys(buf, ":call popup_settext(p, ['a','b','c'])\<CR>")
2128  call VerifyScreenDump(buf, 'Test_popup_settext_03', {})
2129
2130  " Empty list clears
2131  call term_sendkeys(buf, ":call popup_settext(p, [])\<CR>")
2132  call VerifyScreenDump(buf, 'Test_popup_settext_05', {})
2133
2134  " Dicts
2135  call term_sendkeys(buf, ":call popup_settext(p, [#{text: 'aaaa'}, #{text: 'bbbb'}, #{text: 'cccc'}])\<CR>")
2136  call VerifyScreenDump(buf, 'Test_popup_settext_06', {})
2137
2138  " range() (doesn't work)
2139  call term_sendkeys(buf, ":call popup_settext(p, range(4, 8))\<CR>")
2140  call VerifyScreenDump(buf, 'Test_popup_settext_07', {})
2141
2142  " clean up
2143  call StopVimInTerminal(buf)
2144  call delete('XtestPopupSetText')
2145endfunc
2146
2147func Test_popup_hidden()
2148  new
2149
2150  let winid = popup_atcursor('text', #{hidden: 1})
2151  redraw
2152  call assert_equal(0, popup_getpos(winid).visible)
2153  call popup_close(winid)
2154
2155  let winid = popup_create('text', #{hidden: 1})
2156  redraw
2157  call assert_equal(0, popup_getpos(winid).visible)
2158  call popup_close(winid)
2159
2160  func QuitCallback(id, res)
2161    let s:cb_winid = a:id
2162    let s:cb_res = a:res
2163  endfunc
2164  let winid = 'make a choice'->popup_dialog(#{hidden: 1,
2165	  \ filter: 'popup_filter_yesno',
2166	  \ callback: 'QuitCallback',
2167	  \ })
2168  redraw
2169  call assert_equal(0, popup_getpos(winid).visible)
2170  call assert_equal(function('popup_filter_yesno'), popup_getoptions(winid).filter)
2171  call assert_equal(function('QuitCallback'), popup_getoptions(winid).callback)
2172  exe "normal anot used by filter\<Esc>"
2173  call assert_equal('not used by filter', getline(1))
2174
2175  call popup_show(winid)
2176  call feedkeys('y', "xt")
2177  call assert_equal(1, s:cb_res)
2178
2179  bwipe!
2180  delfunc QuitCallback
2181endfunc
2182
2183" Test options not checked elsewhere
2184func Test_set_get_options()
2185  let winid = popup_create('some text', #{highlight: 'Beautiful'})
2186  let options = popup_getoptions(winid)
2187  call assert_equal(1, options.wrap)
2188  call assert_equal(0, options.drag)
2189  call assert_equal('Beautiful', options.highlight)
2190
2191  call popup_setoptions(winid, #{wrap: 0, drag: 1, highlight: 'Another'})
2192  let options = popup_getoptions(winid)
2193  call assert_equal(0, options.wrap)
2194  call assert_equal(1, options.drag)
2195  call assert_equal('Another', options.highlight)
2196
2197  call assert_fails('call popup_setoptions(winid, [])', 'E715:')
2198  call assert_fails('call popup_setoptions(winid, test_null_dict())', 'E715:')
2199
2200  call popup_close(winid)
2201  call assert_equal(0, popup_setoptions(winid, options.wrap))
2202endfunc
2203
2204func Test_popupwin_garbage_collect()
2205  func MyPopupFilter(x, winid, c)
2206    " NOP
2207  endfunc
2208
2209  let winid = popup_create('something', #{filter: function('MyPopupFilter', [{}])})
2210  call test_garbagecollect_now()
2211  redraw
2212  " Must not crash caused by invalid memory access
2213  call feedkeys('j', 'xt')
2214  call assert_true(v:true)
2215
2216  call popup_close(winid)
2217  delfunc MyPopupFilter
2218endfunc
2219
2220func Test_popupwin_filter_mode()
2221  func MyPopupFilter(winid, c)
2222    let s:typed = a:c
2223    if a:c == ':' || a:c == "\r" || a:c == 'v'
2224      " can start cmdline mode, get out, and start/stop Visual mode
2225      return 0
2226    endif
2227    return 1
2228  endfunc
2229
2230  " Normal, Visual and Insert mode
2231  let winid = popup_create('something', #{filter: 'MyPopupFilter', filtermode: 'nvi'})
2232  redraw
2233  call feedkeys('x', 'xt')
2234  call assert_equal('x', s:typed)
2235
2236  call feedkeys(":let g:foo = 'foo'\<CR>", 'xt')
2237  call assert_equal(':', s:typed)
2238  call assert_equal('foo', g:foo)
2239
2240  let @x = 'something'
2241  call feedkeys('v$"xy', 'xt')
2242  call assert_equal('y', s:typed)
2243  call assert_equal('something', @x)  " yank command is filtered out
2244  call feedkeys('v', 'xt')  " end Visual mode
2245
2246  call popup_close(winid)
2247
2248  " only Normal mode
2249  let winid = popup_create('something', #{filter: 'MyPopupFilter', filtermode: 'n'})
2250  redraw
2251  call feedkeys('x', 'xt')
2252  call assert_equal('x', s:typed)
2253
2254  call feedkeys(":let g:foo = 'foo'\<CR>", 'xt')
2255  call assert_equal(':', s:typed)
2256  call assert_equal('foo', g:foo)
2257
2258  let @x = 'something'
2259  call feedkeys('v$"xy', 'xt')
2260  call assert_equal('v', s:typed)
2261  call assert_notequal('something', @x)
2262
2263  call popup_close(winid)
2264
2265  " default: all modes
2266  let winid = popup_create('something', #{filter: 'MyPopupFilter'})
2267  redraw
2268  call feedkeys('x', 'xt')
2269  call assert_equal('x', s:typed)
2270
2271  let g:foo = 'bar'
2272  call feedkeys(":let g:foo = 'foo'\<CR>", 'xt')
2273  call assert_equal("\r", s:typed)
2274  call assert_equal('bar', g:foo)
2275
2276  let @x = 'something'
2277  call feedkeys('v$"xy', 'xt')
2278  call assert_equal('y', s:typed)
2279  call assert_equal('something', @x)  " yank command is filtered out
2280  call feedkeys('v', 'xt')  " end Visual mode
2281
2282  call popup_close(winid)
2283  delfunc MyPopupFilter
2284endfunc
2285
2286func Test_popupwin_filter_mouse()
2287  func MyPopupFilter(winid, c)
2288    let g:got_mousepos = getmousepos()
2289    return 0
2290  endfunc
2291
2292  call setline(1, ['.'->repeat(25)]->repeat(10))
2293  let winid = popup_create(['short', 'long line that will wrap', 'other'], #{
2294	\ line: 2,
2295	\ col: 4,
2296	\ maxwidth: 12,
2297	\ padding: [],
2298	\ border: [],
2299	\ filter: 'MyPopupFilter',
2300	\ })
2301  redraw
2302  "    123456789012345678901
2303  "  1 .....................
2304  "  2 ...+--------------+..
2305  "  3 ...|              |..
2306  "  4 ...| short        |..
2307  "  5 ...| long line th |..
2308  "  6 ...| at will wrap |..
2309  "  7 ...| other        |..
2310  "  8 ...|              |..
2311  "  9 ...+--------------+..
2312  " 10 .....................
2313  let tests = []
2314
2315  func AddItemOutsidePopup(tests, row, col)
2316    eval a:tests->add(#{clickrow: a:row, clickcol: a:col, result: #{
2317	  \ screenrow: a:row, screencol: a:col,
2318	  \ winid: win_getid(), winrow: a:row, wincol: a:col,
2319	  \ line: a:row, column: a:col,
2320	  \ }})
2321  endfunc
2322  func AddItemInPopupBorder(tests, winid, row, col)
2323    eval a:tests->add(#{clickrow: a:row, clickcol: a:col, result: #{
2324	  \ screenrow: a:row, screencol: a:col,
2325	  \ winid: a:winid, winrow: a:row - 1, wincol: a:col - 3,
2326	  \ line: 0, column: 0,
2327	  \ }})
2328  endfunc
2329  func AddItemInPopupText(tests, winid, row, col, textline, textcol)
2330    eval a:tests->add(#{clickrow: a:row, clickcol: a:col, result: #{
2331	  \ screenrow: a:row, screencol: a:col,
2332	  \ winid: a:winid, winrow: a:row - 1, wincol: a:col - 3,
2333	  \ line: a:textline, column: a:textcol,
2334	  \ }})
2335  endfunc
2336
2337  " above and below popup
2338  for c in range(1, 21)
2339    call AddItemOutsidePopup(tests, 1, c)
2340    call AddItemOutsidePopup(tests, 10, c)
2341  endfor
2342  " left and right of popup
2343  for r in range(1, 10)
2344    call AddItemOutsidePopup(tests, r, 3)
2345    call AddItemOutsidePopup(tests, r, 20)
2346  endfor
2347  " top and bottom in popup
2348  for c in range(4, 19)
2349    call AddItemInPopupBorder(tests, winid, 2, c)
2350    call AddItemInPopupBorder(tests, winid, 3, c)
2351    call AddItemInPopupBorder(tests, winid, 8, c)
2352    call AddItemInPopupBorder(tests, winid, 9, c)
2353  endfor
2354  " left and right margin in popup
2355  for r in range(2, 9)
2356    call AddItemInPopupBorder(tests, winid, r, 4)
2357    call AddItemInPopupBorder(tests, winid, r, 5)
2358    call AddItemInPopupBorder(tests, winid, r, 18)
2359    call AddItemInPopupBorder(tests, winid, r, 19)
2360  endfor
2361  " text "short"
2362  call AddItemInPopupText(tests, winid, 4, 6, 1, 1)
2363  call AddItemInPopupText(tests, winid, 4, 10, 1, 5)
2364  call AddItemInPopupText(tests, winid, 4, 11, 1, 6)
2365  call AddItemInPopupText(tests, winid, 4, 17, 1, 6)
2366  " text "long line th"
2367  call AddItemInPopupText(tests, winid, 5, 6, 2, 1)
2368  call AddItemInPopupText(tests, winid, 5, 10, 2, 5)
2369  call AddItemInPopupText(tests, winid, 5, 17, 2, 12)
2370  " text "at will wrap"
2371  call AddItemInPopupText(tests, winid, 6, 6, 2, 13)
2372  call AddItemInPopupText(tests, winid, 6, 10, 2, 17)
2373  call AddItemInPopupText(tests, winid, 6, 17, 2, 24)
2374  " text "other"
2375  call AddItemInPopupText(tests, winid, 7, 6, 3, 1)
2376  call AddItemInPopupText(tests, winid, 7, 10, 3, 5)
2377  call AddItemInPopupText(tests, winid, 7, 11, 3, 6)
2378  call AddItemInPopupText(tests, winid, 7, 17, 3, 6)
2379
2380  for item in tests
2381    call test_setmouse(item.clickrow, item.clickcol)
2382    call feedkeys("\<LeftMouse>", 'xt')
2383    call assert_equal(item.result, g:got_mousepos)
2384  endfor
2385
2386  call popup_close(winid)
2387  enew!
2388  delfunc MyPopupFilter
2389endfunc
2390
2391func Test_popupwin_with_buffer()
2392  call writefile(['some text', 'in a buffer'], 'XsomeFile')
2393  let buf = bufadd('XsomeFile')
2394  call assert_equal(0, bufloaded(buf))
2395
2396  setlocal number
2397  call setbufvar(buf, "&wrapmargin", 13)
2398
2399  let winid = popup_create(buf, {})
2400  call assert_notequal(0, winid)
2401  let pos = popup_getpos(winid)
2402  call assert_equal(2, pos.height)
2403  call assert_equal(1, bufloaded(buf))
2404
2405  " window-local option is set to default, buffer-local is not
2406  call assert_equal(0, getwinvar(winid, '&number'))
2407  call assert_equal(13, getbufvar(buf, '&wrapmargin'))
2408
2409  call popup_close(winid)
2410  call assert_equal({}, popup_getpos(winid))
2411  call assert_equal(1, bufloaded(buf))
2412  exe 'bwipe! ' .. buf
2413  setlocal nonumber
2414
2415  edit test_popupwin.vim
2416  let winid = popup_create(bufnr(''), {})
2417  redraw
2418  call popup_close(winid)
2419  call delete('XsomeFile')
2420endfunc
2421
2422func Test_popupwin_terminal_buffer()
2423  CheckFeature terminal
2424  CheckUnix
2425  " Starting a terminal to run a shell in is considered flaky.
2426  let g:test_is_flaky = 1
2427
2428  let origwin = win_getid()
2429  let ptybuf = term_start(&shell, #{hidden: 1})
2430  let winid = popup_create(ptybuf, #{minwidth: 40, minheight: 10})
2431  " Wait for shell to start
2432  call WaitForAssert({-> assert_equal("run", job_status(term_getjob(ptybuf)))})
2433  sleep 100m
2434  " Check this doesn't crash
2435  call assert_equal(winnr(), winnr('j'))
2436  call assert_equal(winnr(), winnr('k'))
2437  call assert_equal(winnr(), winnr('h'))
2438  call assert_equal(winnr(), winnr('l'))
2439
2440  " Cannot quit while job is running
2441  call assert_fails('call feedkeys("\<C-W>:quit\<CR>", "xt")', 'E948:')
2442
2443  " Cannot enter Terminal-Normal mode.
2444  call feedkeys("xxx\<C-W>N", 'xt')
2445  call assert_fails('call feedkeys("gf", "xt")', 'E863:')
2446  call feedkeys("a\<C-U>", 'xt')
2447
2448  " Exiting shell closes popup window
2449  call feedkeys("exit\<CR>", 'xt')
2450  " Wait for shell to exit
2451  sleep 100m
2452  call feedkeys(":quit\<CR>", 'xt')
2453  call assert_equal(origwin, win_getid())
2454endfunc
2455
2456func Test_popupwin_with_buffer_and_filter()
2457  new Xwithfilter
2458  call setline(1, range(100))
2459  let bufnr = bufnr()
2460  hide
2461
2462  func BufferFilter(win, key)
2463    if a:key == 'G'
2464      " recursive use of "G" does not cause problems.
2465      call win_execute(a:win, 'normal! G')
2466      return 1
2467    endif
2468    return 0
2469  endfunc
2470
2471  let winid = popup_create(bufnr, #{maxheight: 5, filter: 'BufferFilter'})
2472  call assert_equal(1, popup_getpos(winid).firstline)
2473  redraw
2474  call feedkeys("G", 'xt')
2475  call assert_equal(99, popup_getpos(winid).firstline)
2476
2477  call popup_close(winid)
2478  exe 'bwipe! ' .. bufnr
2479endfunc
2480
2481func Test_popupwin_width()
2482  let winid = popup_create(repeat(['short', 'long long long line', 'medium width'], 50), #{
2483	\ maxwidth: 40,
2484	\ maxheight: 10,
2485	\ })
2486  for top in range(1, 20)
2487    eval winid->popup_setoptions(#{firstline: top})
2488    redraw
2489    call assert_equal(19, popup_getpos(winid).width)
2490  endfor
2491  call popup_clear()
2492endfunc
2493
2494func Test_popupwin_buf_close()
2495  let buf = bufadd('Xtestbuf')
2496  call bufload(buf)
2497  call setbufline(buf, 1, ['just', 'some', 'lines'])
2498  let winid = popup_create(buf, {})
2499  redraw
2500  call assert_equal(3, popup_getpos(winid).height)
2501  let bufinfo = getbufinfo(buf)[0]
2502  call assert_equal(1, bufinfo.changed)
2503  call assert_equal(0, bufinfo.hidden)
2504  call assert_equal(0, bufinfo.listed)
2505  call assert_equal(1, bufinfo.loaded)
2506  call assert_equal([], bufinfo.windows)
2507  call assert_equal([winid], bufinfo.popups)
2508
2509  call popup_close(winid)
2510  call assert_equal({}, popup_getpos(winid))
2511  let bufinfo = getbufinfo(buf)[0]
2512  call assert_equal(1, bufinfo.changed)
2513  call assert_equal(1, bufinfo.hidden)
2514  call assert_equal(0, bufinfo.listed)
2515  call assert_equal(1, bufinfo.loaded)
2516  call assert_equal([], bufinfo.windows)
2517  call assert_equal([], bufinfo.popups)
2518  exe 'bwipe! ' .. buf
2519endfunc
2520
2521func Test_popup_menu_with_maxwidth()
2522  CheckScreendump
2523
2524  let lines =<< trim END
2525	call setline(1, range(1, 10))
2526	hi ScrollThumb ctermbg=blue
2527	hi ScrollBar ctermbg=red
2528	func PopupMenu(lines, line, col, scrollbar = 0)
2529		return popup_menu(a:lines, #{
2530			\ maxwidth: 10,
2531			\ maxheight: 3,
2532			\ pos : 'topleft',
2533			\ col : a:col,
2534			\ line : a:line,
2535			\ scrollbar : a:scrollbar,
2536			\ })
2537	endfunc
2538	call PopupMenu(['x'], 1, 1)
2539	call PopupMenu(['123456789|'], 1, 16)
2540	call PopupMenu(['123456789|' .. ' '], 7, 1)
2541	call PopupMenu([repeat('123456789|', 100)], 7, 16)
2542	call PopupMenu(repeat(['123456789|' .. ' '], 5), 1, 33, 1)
2543  END
2544  call writefile(lines, 'XtestPopupMenuMaxWidth')
2545  let buf = RunVimInTerminal('-S XtestPopupMenuMaxWidth', #{rows: 13})
2546  call VerifyScreenDump(buf, 'Test_popupwin_menu_maxwidth_1', {})
2547
2548  " close the menu popupwin.
2549  call term_sendkeys(buf, " ")
2550  call term_sendkeys(buf, " ")
2551  call term_sendkeys(buf, " ")
2552  call term_sendkeys(buf, " ")
2553  call term_sendkeys(buf, " ")
2554
2555  " clean up
2556  call StopVimInTerminal(buf)
2557  call delete('XtestPopupMenuMaxWidth')
2558endfunc
2559
2560func Test_popup_menu_with_scrollbar()
2561  CheckScreendump
2562
2563  let lines =<< trim END
2564    call setline(1, range(1, 20))
2565    hi ScrollThumb ctermbg=blue
2566    hi ScrollBar ctermbg=red
2567    eval ['one', 'two', 'three', 'four', 'five',
2568	  \ 'six', 'seven', 'eight', 'nine']
2569	  \ ->popup_menu(#{
2570	  \ minwidth: 8,
2571	  \ maxheight: 3,
2572	  \ })
2573  END
2574  call writefile(lines, 'XtestPopupMenuScroll')
2575  let buf = RunVimInTerminal('-S XtestPopupMenuScroll', #{rows: 10})
2576
2577  call term_sendkeys(buf, "j")
2578  call VerifyScreenDump(buf, 'Test_popupwin_menu_scroll_1', {})
2579
2580  call term_sendkeys(buf, "jjj")
2581  call VerifyScreenDump(buf, 'Test_popupwin_menu_scroll_2', {})
2582
2583  " if the cursor is the bottom line, it stays at the bottom line.
2584  call term_sendkeys(buf, repeat("j", 20))
2585  call VerifyScreenDump(buf, 'Test_popupwin_menu_scroll_3', {})
2586
2587  call term_sendkeys(buf, "kk")
2588  call VerifyScreenDump(buf, 'Test_popupwin_menu_scroll_4', {})
2589
2590  call term_sendkeys(buf, "k")
2591  call VerifyScreenDump(buf, 'Test_popupwin_menu_scroll_5', {})
2592
2593  " if the cursor is in the top line, it stays in the top line.
2594  call term_sendkeys(buf, repeat("k", 20))
2595  call VerifyScreenDump(buf, 'Test_popupwin_menu_scroll_6', {})
2596
2597  " close the menu popupwin.
2598  call term_sendkeys(buf, " ")
2599
2600  " clean up
2601  call StopVimInTerminal(buf)
2602  call delete('XtestPopupMenuScroll')
2603endfunc
2604
2605func Test_popup_menu_filter()
2606  CheckScreendump
2607
2608  let lines =<< trim END
2609	function! MyFilter(winid, key) abort
2610	  if a:key == "0"
2611		call win_execute(a:winid, "call setpos('.', [0, 1, 1, 0])")
2612		return 1
2613	  endif
2614	  if a:key == "G"
2615		call win_execute(a:winid, "call setpos('.', [0, line('$'), 1, 0])")
2616		return 1
2617	  endif
2618	  if a:key == "j"
2619		call win_execute(a:winid, "call setpos('.', [0, line('.') + 1, 1, 0])")
2620		return 1
2621	  endif
2622	  if a:key == "k"
2623		call win_execute(a:winid, "call setpos('.', [0, line('.') - 1, 1, 0])")
2624		return 1
2625	  endif
2626	  if a:key == ':'
2627		call popup_close(a:winid)
2628		return 0
2629	  endif
2630	  return 0
2631	endfunction
2632	call popup_menu(['111', '222', '333', '444', '555', '666', '777', '888', '999'], #{
2633	  \ maxheight : 3,
2634	  \ filter : 'MyFilter'
2635	  \ })
2636  END
2637  call writefile(lines, 'XtestPopupMenuFilter')
2638  let buf = RunVimInTerminal('-S XtestPopupMenuFilter', #{rows: 10})
2639
2640  call term_sendkeys(buf, "j")
2641  call VerifyScreenDump(buf, 'Test_popupwin_menu_filter_1', {})
2642
2643  call term_sendkeys(buf, "k")
2644  call VerifyScreenDump(buf, 'Test_popupwin_menu_filter_2', {})
2645
2646  call term_sendkeys(buf, "G")
2647  call VerifyScreenDump(buf, 'Test_popupwin_menu_filter_3', {})
2648
2649  call term_sendkeys(buf, "0")
2650  call VerifyScreenDump(buf, 'Test_popupwin_menu_filter_4', {})
2651
2652  " check that when the popup is closed in the filter the screen is redrawn
2653  call term_sendkeys(buf, ":")
2654  call VerifyScreenDump(buf, 'Test_popupwin_menu_filter_5', {})
2655  call term_sendkeys(buf, "\<CR>")
2656
2657  " clean up
2658  call StopVimInTerminal(buf)
2659  call delete('XtestPopupMenuFilter')
2660endfunc
2661
2662func Test_popup_cursorline()
2663  CheckScreendump
2664
2665  let winid = popup_create('some text', {})
2666  call assert_equal(0, popup_getoptions(winid).cursorline)
2667  call popup_close(winid)
2668
2669  let winid = popup_create('some text', #{ cursorline: 1, })
2670  call assert_equal(1, popup_getoptions(winid).cursorline)
2671  call popup_close(winid)
2672
2673  let winid = popup_create('some text', #{ cursorline: 0, })
2674  call assert_equal(0, popup_getoptions(winid).cursorline)
2675  call popup_close(winid)
2676
2677  let winid = popup_menu('some text', {})
2678  call assert_equal(1, popup_getoptions(winid).cursorline)
2679  call popup_close(winid)
2680
2681  let winid = popup_menu('some text', #{ cursorline: 1, })
2682  call assert_equal(1, popup_getoptions(winid).cursorline)
2683  call popup_close(winid)
2684
2685  let winid = popup_menu('some text', #{ cursorline: 0, })
2686  call assert_equal(0, popup_getoptions(winid).cursorline)
2687  call popup_close(winid)
2688
2689  " ---------
2690  " Pattern 1
2691  " ---------
2692  let lines =<< trim END
2693	call popup_create(['111', '222', '333'], #{ cursorline : 0 })
2694  END
2695  call writefile(lines, 'XtestPopupCursorLine')
2696  let buf = RunVimInTerminal('-S XtestPopupCursorLine', #{rows: 10})
2697  call VerifyScreenDump(buf, 'Test_popupwin_cursorline_1', {})
2698  call term_sendkeys(buf, ":call popup_clear()\<cr>")
2699  call StopVimInTerminal(buf)
2700
2701  " ---------
2702  " Pattern 2
2703  " ---------
2704  let lines =<< trim END
2705	call popup_create(['111', '222', '333'], #{ cursorline : 1 })
2706  END
2707  call writefile(lines, 'XtestPopupCursorLine')
2708  let buf = RunVimInTerminal('-S XtestPopupCursorLine', #{rows: 10})
2709  call VerifyScreenDump(buf, 'Test_popupwin_cursorline_2', {})
2710  call term_sendkeys(buf, ":call popup_clear()\<cr>")
2711  call StopVimInTerminal(buf)
2712
2713  " ---------
2714  " Pattern 3
2715  " ---------
2716  let lines =<< trim END
2717	function! MyFilter(winid, key) abort
2718	  if a:key == "j"
2719		call win_execute(a:winid, "call setpos('.', [0, line('.') + 1, 1, 0]) | redraw")
2720		return 1
2721	  endif
2722	  if a:key == 'x'
2723		call popup_close(a:winid)
2724		return 1
2725	  endif
2726	  return 0
2727	endfunction
2728	call popup_menu(['111', '222', '333'], #{
2729	  \ cursorline : 0,
2730	  \ maxheight : 2,
2731	  \ filter : 'MyFilter',
2732	  \ })
2733  END
2734  call writefile(lines, 'XtestPopupCursorLine')
2735  let buf = RunVimInTerminal('-S XtestPopupCursorLine', #{rows: 10})
2736  call VerifyScreenDump(buf, 'Test_popupwin_cursorline_3', {})
2737  call term_sendkeys(buf, "j")
2738  call term_sendkeys(buf, "j")
2739  call VerifyScreenDump(buf, 'Test_popupwin_cursorline_4', {})
2740  call term_sendkeys(buf, "x")
2741  call StopVimInTerminal(buf)
2742
2743  " ---------
2744  " Pattern 4
2745  " ---------
2746  let lines =<< trim END
2747	function! MyFilter(winid, key) abort
2748	  if a:key == "j"
2749		call win_execute(a:winid, "call setpos('.', [0, line('.') + 1, 1, 0]) | redraw")
2750		return 1
2751	  endif
2752	  if a:key == 'x'
2753		call popup_close(a:winid)
2754		return 1
2755	  endif
2756	  return 0
2757	endfunction
2758	call popup_menu(['111', '222', '333'], #{
2759	  \ cursorline : 1,
2760	  \ maxheight : 2,
2761	  \ filter : 'MyFilter',
2762	  \ })
2763  END
2764  call writefile(lines, 'XtestPopupCursorLine')
2765  let buf = RunVimInTerminal('-S XtestPopupCursorLine', #{rows: 10})
2766  call VerifyScreenDump(buf, 'Test_popupwin_cursorline_5', {})
2767  call term_sendkeys(buf, "j")
2768  call term_sendkeys(buf, "j")
2769  call VerifyScreenDump(buf, 'Test_popupwin_cursorline_6', {})
2770  call term_sendkeys(buf, "x")
2771  call StopVimInTerminal(buf)
2772
2773  " ---------
2774  " Cursor in second line when creating the popup
2775  " ---------
2776  let lines =<< trim END
2777    let winid = popup_create(['111', '222', '333'], #{
2778	  \ cursorline : 1,
2779	  \ })
2780    call win_execute(winid, "2")
2781  END
2782  call writefile(lines, 'XtestPopupCursorLine')
2783  let buf = RunVimInTerminal('-S XtestPopupCursorLine', #{rows: 10})
2784  call VerifyScreenDump(buf, 'Test_popupwin_cursorline_7', {})
2785  call StopVimInTerminal(buf)
2786
2787  call delete('XtestPopupCursorLine')
2788
2789  " ---------
2790  " Use current buffer for popupmenu
2791  " ---------
2792  let lines =<< trim END
2793    call setline(1, ['one', 'two', 'three'])
2794    let winid = popup_create(bufnr('%'), #{
2795	  \ cursorline : 1,
2796	  \ })
2797    call win_execute(winid, "2")
2798  END
2799  call writefile(lines, 'XtestPopupCursorLine')
2800  let buf = RunVimInTerminal('-S XtestPopupCursorLine', #{rows: 10})
2801  call VerifyScreenDump(buf, 'Test_popupwin_cursorline_8', {})
2802  call StopVimInTerminal(buf)
2803
2804  call delete('XtestPopupCursorLine')
2805endfunc
2806
2807func Test_previewpopup()
2808  CheckScreendump
2809  CheckFeature quickfix
2810
2811  call writefile([
2812        \ "!_TAG_FILE_ENCODING\tutf-8\t//",
2813        \ "another\tXtagfile\t/^this is another",
2814        \ "theword\tXtagfile\t/^theword"],
2815        \ 'Xtags')
2816  call writefile(range(1,20)
2817        \ + ['theword is here']
2818        \ + range(22, 27)
2819        \ + ['this is another place']
2820        \ + range(29, 40),
2821        \ "Xtagfile")
2822  call writefile(range(1,10)
2823        \ + ['searched word is here']
2824        \ + range(12, 20),
2825        \ "Xheader.h")
2826  let lines =<< trim END
2827        set tags=Xtags
2828	call setline(1, [
2829	      \ 'one',
2830	      \ '#include "Xheader.h"',
2831	      \ 'three',
2832	      \ 'four',
2833	      \ 'five',
2834	      \ 'six',
2835	      \ 'seven',
2836	      \ 'find theword somewhere',
2837	      \ 'nine',
2838	      \ 'this is another word',
2839	      \ 'very long line where the word is also another'])
2840        set previewpopup=height:4,width:40
2841	set path=.
2842  END
2843  call writefile(lines, 'XtestPreviewPopup')
2844  let buf = RunVimInTerminal('-S XtestPreviewPopup', #{rows: 14})
2845
2846  call term_sendkeys(buf, "/theword\<CR>\<C-W>}")
2847  call term_sendkeys(buf, ":\<CR>")
2848  call VerifyScreenDump(buf, 'Test_popupwin_previewpopup_1', {})
2849
2850  call term_sendkeys(buf, "/another\<CR>\<C-W>}")
2851  call VerifyScreenDump(buf, 'Test_popupwin_previewpopup_2', {})
2852
2853  call term_sendkeys(buf, ":call popup_move(popup_findpreview(), #{col: 15})\<CR>")
2854  call term_sendkeys(buf, ":\<CR>")
2855  call VerifyScreenDump(buf, 'Test_popupwin_previewpopup_3', {})
2856
2857  call term_sendkeys(buf, "/another\<CR>\<C-W>}")
2858  call VerifyScreenDump(buf, 'Test_popupwin_previewpopup_4', {})
2859
2860  call term_sendkeys(buf, ":silent cd ..\<CR>:\<CR>")
2861  call VerifyScreenDump(buf, 'Test_popupwin_previewpopup_5', {})
2862  call term_sendkeys(buf, ":silent cd testdir\<CR>")
2863
2864  call term_sendkeys(buf, ":pclose\<CR>")
2865  call term_sendkeys(buf, ":\<BS>")
2866  call VerifyScreenDump(buf, 'Test_popupwin_previewpopup_6', {})
2867
2868  call term_sendkeys(buf, ":pedit +/theword Xtagfile\<CR>")
2869  call term_sendkeys(buf, ":\<CR>")
2870  call VerifyScreenDump(buf, 'Test_popupwin_previewpopup_7', {})
2871
2872  call term_sendkeys(buf, ":pclose\<CR>")
2873  call term_sendkeys(buf, ":psearch searched\<CR>")
2874  call term_sendkeys(buf, ":\<CR>")
2875  call VerifyScreenDump(buf, 'Test_popupwin_previewpopup_8', {})
2876
2877  call term_sendkeys(buf, "\<C-W>p")
2878  call VerifyScreenDump(buf, 'Test_popupwin_previewpopup_9', {})
2879
2880  call term_sendkeys(buf, ":call win_execute(popup_findpreview(), 'call popup_clear()')\<CR>")
2881  call VerifyScreenDump(buf, 'Test_popupwin_previewpopup_10', {})
2882
2883  call StopVimInTerminal(buf)
2884  call delete('Xtags')
2885  call delete('Xtagfile')
2886  call delete('XtestPreviewPopup')
2887  call delete('Xheader.h')
2888endfunc
2889
2890func Get_popupmenu_lines()
2891  let lines =<< trim END
2892      set completeopt+=preview,popup
2893      set completefunc=CompleteFuncDict
2894      hi InfoPopup ctermbg=yellow
2895
2896      func CompleteFuncDict(findstart, base)
2897	if a:findstart
2898	  if col('.') > 10
2899	    return col('.') - 10
2900	  endif
2901	  return 0
2902	endif
2903
2904	return {
2905		\ 'words': [
2906		  \ {
2907		    \ 'word': 'aword',
2908		    \ 'abbr': 'wrd',
2909		    \ 'menu': 'extra text',
2910		    \ 'info': 'words are cool',
2911		    \ 'kind': 'W',
2912		    \ 'user_data': 'test'
2913		  \ },
2914		  \ {
2915		    \ 'word': 'anotherword',
2916		    \ 'abbr': 'anotwrd',
2917		    \ 'menu': 'extra text',
2918		    \ 'info': "other words are\ncooler than this and some more text\nto make wrap",
2919		    \ 'kind': 'W',
2920		    \ 'user_data': 'notest'
2921		  \ },
2922		  \ {
2923		    \ 'word': 'noinfo',
2924		    \ 'abbr': 'noawrd',
2925		    \ 'menu': 'extra text',
2926		    \ 'info': "lets\nshow\na\nscrollbar\nhere",
2927		    \ 'kind': 'W',
2928		    \ 'user_data': 'notest'
2929		  \ },
2930		  \ {
2931		    \ 'word': 'thatword',
2932		    \ 'abbr': 'thatwrd',
2933		    \ 'menu': 'extra text',
2934		    \ 'info': 'that word is cool',
2935		    \ 'kind': 'W',
2936		    \ 'user_data': 'notest'
2937		  \ },
2938		\ ]
2939	      \ }
2940      endfunc
2941      call setline(1, 'text text text text text text text ')
2942      func ChangeColor()
2943	let id = popup_findinfo()
2944	eval id->popup_setoptions(#{highlight: 'InfoPopup'})
2945      endfunc
2946
2947      func InfoHidden()
2948	set completepopup=height:4,border:off,align:menu
2949	set completeopt-=popup completeopt+=popuphidden
2950	au CompleteChanged * call HandleChange()
2951      endfunc
2952
2953      let s:counter = 0
2954      func HandleChange()
2955	let s:counter += 1
2956	let selected = complete_info(['selected']).selected
2957	if selected <= 0
2958	  " First time: do nothing, info remains hidden
2959	  return
2960	endif
2961	if selected == 1
2962	  " Second time: show info right away
2963	  let id = popup_findinfo()
2964	  if id
2965	    call popup_settext(id, 'immediate info ' .. s:counter)
2966	    call popup_show(id)
2967	  endif
2968	else
2969	  " Third time: show info after a short delay
2970	  call timer_start(100, 'ShowInfo')
2971	endif
2972      endfunc
2973
2974      func ShowInfo(...)
2975	let id = popup_findinfo()
2976	if id
2977	  call popup_settext(id, 'async info ' .. s:counter)
2978	  call popup_show(id)
2979	endif
2980      endfunc
2981  END
2982  return lines
2983endfunc
2984
2985func Test_popupmenu_info_border()
2986  CheckScreendump
2987  CheckFeature quickfix
2988
2989  let lines = Get_popupmenu_lines()
2990  call add(lines, 'set completepopup=height:4,highlight:InfoPopup')
2991  call writefile(lines, 'XtestInfoPopup')
2992
2993  let buf = RunVimInTerminal('-S XtestInfoPopup', #{rows: 14})
2994  call TermWait(buf, 25)
2995
2996  call term_sendkeys(buf, "A\<C-X>\<C-U>")
2997  call VerifyScreenDump(buf, 'Test_popupwin_infopopup_1', {})
2998
2999  call term_sendkeys(buf, "\<C-N>")
3000  call VerifyScreenDump(buf, 'Test_popupwin_infopopup_2', {})
3001
3002  call term_sendkeys(buf, "\<C-N>")
3003  call VerifyScreenDump(buf, 'Test_popupwin_infopopup_3', {})
3004
3005  call term_sendkeys(buf, "\<C-N>\<C-N>")
3006  call VerifyScreenDump(buf, 'Test_popupwin_infopopup_4', {})
3007
3008  " info on the left with scrollbar
3009  call term_sendkeys(buf, "test text test text\<C-X>\<C-U>")
3010  call term_sendkeys(buf, "\<C-N>\<C-N>")
3011  call VerifyScreenDump(buf, 'Test_popupwin_infopopup_5', {})
3012
3013  " Test that the popupmenu's scrollbar and infopopup do not overlap
3014  call term_sendkeys(buf, "\<Esc>")
3015  call term_sendkeys(buf, ":set pumheight=3\<CR>")
3016  call term_sendkeys(buf, "cc\<C-X>\<C-U>")
3017  call VerifyScreenDump(buf, 'Test_popupwin_infopopup_6', {})
3018
3019  " Hide the info popup, cycle trough buffers, make sure it didn't get
3020  " deleted.
3021  call term_sendkeys(buf, "\<Esc>")
3022  call term_sendkeys(buf, ":set hidden\<CR>")
3023  call term_sendkeys(buf, ":bn\<CR>")
3024  call term_sendkeys(buf, ":bn\<CR>")
3025  call term_sendkeys(buf, "otest text test text\<C-X>\<C-U>")
3026  call VerifyScreenDump(buf, 'Test_popupwin_infopopup_7', {})
3027
3028  call term_sendkeys(buf, "\<Esc>")
3029  call StopVimInTerminal(buf)
3030  call delete('XtestInfoPopup')
3031endfunc
3032
3033func Test_popupmenu_info_noborder()
3034  CheckScreendump
3035  CheckFeature quickfix
3036
3037  let lines = Get_popupmenu_lines()
3038  call add(lines, 'set completepopup=height:4,border:off')
3039  call writefile(lines, 'XtestInfoPopupNb')
3040
3041  let buf = RunVimInTerminal('-S XtestInfoPopupNb', #{rows: 14})
3042  call TermWait(buf, 25)
3043
3044  call term_sendkeys(buf, "A\<C-X>\<C-U>")
3045  call VerifyScreenDump(buf, 'Test_popupwin_infopopup_nb_1', {})
3046
3047  call StopVimInTerminal(buf)
3048  call delete('XtestInfoPopupNb')
3049endfunc
3050
3051func Test_popupmenu_info_align_menu()
3052  CheckScreendump
3053  CheckFeature quickfix
3054
3055  let lines = Get_popupmenu_lines()
3056  call add(lines, 'set completepopup=height:4,border:off,align:menu')
3057  call writefile(lines, 'XtestInfoPopupNb')
3058
3059  let buf = RunVimInTerminal('-S XtestInfoPopupNb', #{rows: 14})
3060  call TermWait(buf, 25)
3061
3062  call term_sendkeys(buf, "A\<C-X>\<C-U>")
3063  call term_sendkeys(buf, "\<C-N>")
3064  call term_sendkeys(buf, "\<C-N>")
3065  call term_sendkeys(buf, "\<C-N>")
3066  call VerifyScreenDump(buf, 'Test_popupwin_infopopup_align_1', {})
3067
3068  call term_sendkeys(buf, "test text test text test\<C-X>\<C-U>")
3069  call term_sendkeys(buf, "\<C-N>")
3070  call VerifyScreenDump(buf, 'Test_popupwin_infopopup_align_2', {})
3071
3072  call term_sendkeys(buf, "\<Esc>")
3073  call term_sendkeys(buf, ":call ChangeColor()\<CR>")
3074  call term_sendkeys(buf, ":call setline(2, ['x']->repeat(10))\<CR>")
3075  call term_sendkeys(buf, "Gotest text test text\<C-X>\<C-U>")
3076  call VerifyScreenDump(buf, 'Test_popupwin_infopopup_align_3', {})
3077
3078  call StopVimInTerminal(buf)
3079  call delete('XtestInfoPopupNb')
3080endfunc
3081
3082func Test_popupmenu_info_hidden()
3083  CheckScreendump
3084  CheckFeature quickfix
3085
3086  let lines = Get_popupmenu_lines()
3087  call add(lines, 'call InfoHidden()')
3088  call writefile(lines, 'XtestInfoPopupHidden')
3089
3090  let buf = RunVimInTerminal('-S XtestInfoPopupHidden', #{rows: 14})
3091  call TermWait(buf, 25)
3092
3093  call term_sendkeys(buf, "A\<C-X>\<C-U>")
3094  call VerifyScreenDump(buf, 'Test_popupwin_infopopup_hidden_1', {})
3095
3096  call term_sendkeys(buf, "\<C-N>")
3097  call VerifyScreenDump(buf, 'Test_popupwin_infopopup_hidden_2', {})
3098
3099  call term_sendkeys(buf, "\<C-N>")
3100  call VerifyScreenDump(buf, 'Test_popupwin_infopopup_hidden_3', {})
3101
3102  call term_sendkeys(buf, "\<Esc>")
3103  call StopVimInTerminal(buf)
3104  call delete('XtestInfoPopupHidden')
3105endfunc
3106
3107func Test_popupmenu_info_too_wide()
3108  CheckScreendump
3109  CheckFeature quickfix
3110
3111  let lines =<< trim END
3112    call setline(1, range(10))
3113
3114    set completeopt+=preview,popup
3115    set completepopup=align:menu
3116    set omnifunc=OmniFunc
3117    hi InfoPopup ctermbg=lightgrey
3118
3119    func OmniFunc(findstart, base)
3120      if a:findstart
3121        return 0
3122      endif
3123
3124      let menuText = 'some long text to make sure the menu takes up all of the width of the window'
3125      return #{
3126    	\ words: [
3127    	  \ #{
3128    	    \ word: 'scrap',
3129    	    \ menu: menuText,
3130    	    \ info: "other words are\ncooler than this and some more text\nto make wrap",
3131    	  \ },
3132    	  \ #{
3133    	    \ word: 'scappier',
3134    	    \ menu: menuText,
3135    	    \ info: 'words are cool',
3136    	  \ },
3137    	  \ #{
3138    	    \ word: 'scrappier2',
3139    	    \ menu: menuText,
3140    	    \ info: 'words are cool',
3141    	  \ },
3142    	\ ]
3143     \ }
3144    endfunc
3145  END
3146
3147  call writefile(lines, 'XtestInfoPopupWide')
3148  let buf = RunVimInTerminal('-S XtestInfoPopupWide', #{rows: 8})
3149  call TermWait(buf, 25)
3150
3151  call term_sendkeys(buf, "Ascr\<C-X>\<C-O>")
3152  call VerifyScreenDump(buf, 'Test_popupwin_infopopup_wide_1', {})
3153
3154  call term_sendkeys(buf, "\<Esc>")
3155  call StopVimInTerminal(buf)
3156  call delete('XtestInfoPopupWide')
3157endfunc
3158
3159func Test_popupwin_recycle_bnr()
3160  let bufnr = popup_notification('nothing wrong', {})->winbufnr()
3161  call popup_clear()
3162  let winid = 'nothing wrong'->popup_notification({})
3163  call assert_equal(bufnr, winbufnr(winid))
3164  call popup_clear()
3165endfunc
3166
3167func Test_popupwin_getoptions_tablocal()
3168  topleft split
3169  let win1 = popup_create('nothing', #{maxheight: 8})
3170  let win2 = popup_create('something', #{maxheight: 10})
3171  let win3 = popup_create('something', #{maxheight: 15})
3172  call assert_equal(8, popup_getoptions(win1).maxheight)
3173  call assert_equal(10, popup_getoptions(win2).maxheight)
3174  call assert_equal(15, popup_getoptions(win3).maxheight)
3175  call popup_clear()
3176  quit
3177endfunc
3178
3179func Test_popupwin_cancel()
3180  let win1 = popup_create('one', #{line: 5, filter: {... -> 0}})
3181  let win2 = popup_create('two', #{line: 10, filter: {... -> 0}})
3182  let win3 = popup_create('three', #{line: 15, filter: {... -> 0}})
3183  call assert_equal(5, popup_getpos(win1).line)
3184  call assert_equal(10, popup_getpos(win2).line)
3185  call assert_equal(15, popup_getpos(win3).line)
3186  " TODO: this also works without patch 8.1.2110
3187  call feedkeys("\<C-C>", 'xt')
3188  call assert_equal(5, popup_getpos(win1).line)
3189  call assert_equal(10, popup_getpos(win2).line)
3190  call assert_equal({}, popup_getpos(win3))
3191  call feedkeys("\<C-C>", 'xt')
3192  call assert_equal(5, popup_getpos(win1).line)
3193  call assert_equal({}, popup_getpos(win2))
3194  call assert_equal({}, popup_getpos(win3))
3195  call feedkeys("\<C-C>", 'xt')
3196  call assert_equal({}, popup_getpos(win1))
3197  call assert_equal({}, popup_getpos(win2))
3198  call assert_equal({}, popup_getpos(win3))
3199endfunc
3200
3201func Test_popupwin_filter_redraw()
3202  " Create two popups with a filter that closes the popup when typing "0".
3203  " Both popups should close, even though the redraw also calls
3204  " popup_reset_handled()
3205
3206  func CloseFilter(winid, key)
3207    if a:key == '0'
3208      call popup_close(a:winid)
3209      redraw
3210    endif
3211    return 0  " pass the key
3212  endfunc
3213
3214  let id1 = popup_create('first one', #{
3215	\ line: 1,
3216	\ col: 1,
3217	\ filter: 'CloseFilter',
3218	\ })
3219  let id2 = popup_create('second one', #{
3220	\ line: 9,
3221	\ col: 1,
3222	\ filter: 'CloseFilter',
3223	\ })
3224  call assert_equal(1, popup_getpos(id1).line)
3225  call assert_equal(9, popup_getpos(id2).line)
3226
3227  call feedkeys('0', 'xt')
3228  call assert_equal({}, popup_getpos(id1))
3229  call assert_equal({}, popup_getpos(id2))
3230
3231  call popup_clear()
3232  delfunc CloseFilter
3233endfunc
3234
3235func Test_popupwin_double_width()
3236  CheckScreendump
3237
3238  let lines =<< trim END
3239    call setline(1, 'x你好世界你好世你好世界你好')
3240    call setline(2, '你好世界你好世你好世界你好')
3241    call setline(3, 'x你好世界你好世你好世界你好')
3242    call popup_create('你好,世界 - 你好,世界xxxxx', #{line: 1, col: 3, maxwidth: 14})
3243  END
3244  call writefile(lines, 'XtestPopupWide')
3245
3246  let buf = RunVimInTerminal('-S XtestPopupWide', #{rows: 10})
3247  call VerifyScreenDump(buf, 'Test_popupwin_doublewidth_1', {})
3248
3249  call StopVimInTerminal(buf)
3250  call delete('XtestPopupWide')
3251endfunc
3252
3253func Test_popupwin_sign()
3254  CheckScreendump
3255
3256  let lines =<< trim END
3257    call setline(1, range(10))
3258    call sign_define('Current', {
3259	    \ 'text': '>>',
3260	    \ 'texthl': 'WarningMsg',
3261	    \ 'linehl': 'Error',
3262	    \ })
3263    call sign_define('Other', {
3264	    \ 'text': '#!',
3265	    \ 'texthl': 'Error',
3266	    \ 'linehl': 'Search',
3267	    \ })
3268    let winid = popup_create(['hello', 'bright', 'world'], {
3269	    \ 'minwidth': 20,
3270	    \ })
3271    call setwinvar(winid, "&signcolumn", "yes")
3272    let winbufnr = winbufnr(winid)
3273
3274    " add sign to current buffer, shows
3275    call sign_place(1, 'Selected', 'Current', bufnr('%'), {'lnum': 1})
3276    " add sign to current buffer, does not show
3277    call sign_place(2, 'PopUpSelected', 'Other', bufnr('%'), {'lnum': 2})
3278
3279    " add sign to popup buffer, shows
3280    call sign_place(3, 'PopUpSelected', 'Other', winbufnr, {'lnum': 1})
3281    " add sign to popup buffer, does not show
3282    call sign_place(4, 'Selected', 'Current', winbufnr, {'lnum': 2})
3283  END
3284  call writefile(lines, 'XtestPopupSign')
3285
3286  let buf = RunVimInTerminal('-S XtestPopupSign', #{rows: 10})
3287  call VerifyScreenDump(buf, 'Test_popupwin_sign_1', {})
3288
3289  call StopVimInTerminal(buf)
3290  call delete('XtestPopupSign')
3291endfunc
3292
3293func Test_popupwin_bufnr()
3294  let popwin = popup_create(['blah'], #{})
3295  let popbuf = winbufnr(popwin)
3296  split asdfasdf
3297  let newbuf = bufnr()
3298  call assert_true(newbuf > popbuf, 'New buffer number is higher')
3299  call assert_equal(newbuf, bufnr('$'))
3300  call popup_clear()
3301  let popwin = popup_create(['blah'], #{})
3302  " reuses previous buffer number
3303  call assert_equal(popbuf, winbufnr(popwin))
3304  call assert_equal(newbuf, bufnr('$'))
3305
3306  call popup_clear()
3307  bwipe!
3308endfunc
3309
3310func Test_popupwin_filter_input_multibyte()
3311  func MyPopupFilter(winid, c)
3312    let g:bytes = range(a:c->strlen())->map({i -> char2nr(a:c[i])})
3313    return 0
3314  endfunc
3315  let winid = popup_create('', #{mapping: 0, filter: 'MyPopupFilter'})
3316
3317  " UTF-8: E3 80 80, including K_SPECIAL(0x80)
3318  call feedkeys("\u3000", 'xt')
3319  call assert_equal([0xe3, 0x80, 0x80], g:bytes)
3320
3321  " UTF-8: E3 80 9B, including CSI(0x9B)
3322  call feedkeys("\u301b", 'xt')
3323  call assert_equal([0xe3, 0x80, 0x9b], g:bytes)
3324
3325  call popup_clear()
3326  delfunc MyPopupFilter
3327  unlet g:bytes
3328endfunc
3329
3330func Test_popupwin_atcursor_far_right()
3331  new
3332
3333  " this was getting stuck
3334  set signcolumn=yes
3335  call setline(1, repeat('=', &columns))
3336  normal! ggg$
3337  let winid = popup_atcursor(repeat('x', 500), #{moved: 'any', border: []})
3338
3339  call popup_close(winid)
3340  bwipe!
3341  set signcolumn&
3342endfunc
3343
3344func Test_popupwin_splitmove()
3345  vsplit
3346  let win2 = win_getid()
3347  let popup_winid = popup_dialog('hello', {})
3348  call assert_fails('call win_splitmove(popup_winid, win2)', 'E957:')
3349  call assert_fails('call win_splitmove(win2, popup_winid)', 'E957:')
3350
3351  call popup_clear()
3352  bwipe
3353endfunc
3354
3355
3356" vim: shiftwidth=2 sts=2
3357