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_firstline_cursorline()
520  let winid = popup_create(['1111', '222222', '33333', '44444'], #{
521	\ maxheight: 2,
522	\ firstline: 3,
523	\ cursorline: 1,
524	\ })
525  call assert_equal(3, popup_getoptions(winid).firstline)
526  call assert_equal(3, getwininfo(winid)[0].topline)
527  call assert_equal(3, getcurpos(winid)[1])
528
529  call popup_close(winid)
530endfunc
531
532func Test_popup_noscrolloff()
533  set scrolloff=5
534  let winid = popup_create(['xxx']->repeat(50), #{
535	\ maxheight: 5,
536	\ firstline: 11,
537	\ })
538  redraw
539  call assert_equal(11, popup_getoptions(winid).firstline)
540  call assert_equal(11, popup_getpos(winid).firstline)
541
542  call popup_setoptions(winid, #{firstline: 0})
543  call win_execute(winid, "normal! \<c-y>")
544  call assert_equal(0, popup_getoptions(winid).firstline)
545  call assert_equal(10, popup_getpos(winid).firstline)
546
547  call popup_close(winid)
548endfunc
549
550func Test_popup_drag()
551  CheckScreendump
552
553  " create a popup that covers the command line
554  let lines =<< trim END
555	call setline(1, range(1, 20))
556	split
557	vsplit
558	$wincmd w
559	vsplit
560	1wincmd w
561	let winid = popup_create(['1111', '222222', '33333'], #{
562	      \ drag: 1,
563	      \ resize: 1,
564	      \ border: [],
565	      \ line: &lines - 4,
566	      \ })
567	func Dragit()
568	  call feedkeys("\<F3>\<LeftMouse>\<F4>\<LeftDrag>\<LeftRelease>", "xt")
569	endfunc
570	map <silent> <F3> :call test_setmouse(&lines - 4, &columns / 2)<CR>
571	map <silent> <F4> :call test_setmouse(&lines - 8, &columns / 2 - 20)<CR>
572	func Resize()
573	  call feedkeys("\<F5>\<LeftMouse>\<F6>\<LeftDrag>\<LeftRelease>", "xt")
574	endfunc
575	map <silent> <F5> :call test_setmouse(6, 21)<CR>
576	map <silent> <F6> :call test_setmouse(7, 25)<CR>
577  END
578  call writefile(lines, 'XtestPopupDrag')
579  let buf = RunVimInTerminal('-S XtestPopupDrag', #{rows: 10})
580  call VerifyScreenDump(buf, 'Test_popupwin_drag_01', {})
581
582  call term_sendkeys(buf, ":call Dragit()\<CR>")
583  call VerifyScreenDump(buf, 'Test_popupwin_drag_02', {})
584
585  call term_sendkeys(buf, ":call Resize()\<CR>")
586  call VerifyScreenDump(buf, 'Test_popupwin_drag_03', {})
587
588  " clean up
589  call StopVimInTerminal(buf)
590  call delete('XtestPopupDrag')
591endfunc
592
593func Test_popup_drag_termwin()
594  CheckUnix
595  CheckScreendump
596  CheckFeature terminal
597
598  " create a popup that covers the terminal window
599  let lines =<< trim END
600	set foldmethod=marker
601	call setline(1, range(100))
602	for nr in range(7)
603	  call setline(nr * 12 + 1, "fold {{{")
604	  call setline(nr * 12 + 11, "end }}}")
605	endfor
606	%foldclose
607	set shell=/bin/sh noruler
608	unlet $PROMPT_COMMAND
609	let $PS1 = 'vim> '
610        terminal ++rows=4
611	$wincmd w
612	let winid = popup_create(['1111', '2222'], #{
613	      \ drag: 1,
614	      \ resize: 1,
615	      \ border: [],
616	      \ line: 3,
617	      \ })
618	func DragitLeft()
619	  call feedkeys("\<F3>\<LeftMouse>\<F4>\<LeftDrag>\<LeftRelease>", "xt")
620	endfunc
621	func DragitDown()
622	  call feedkeys("\<F4>\<LeftMouse>\<F5>\<LeftDrag>\<LeftRelease>", "xt")
623	endfunc
624	func DragitDownLeft()
625	  call feedkeys("\<F5>\<LeftMouse>\<F6>\<LeftDrag>\<LeftRelease>", "xt")
626	endfunc
627	map <silent> <F3> :call test_setmouse(3, &columns / 2)<CR>
628	map <silent> <F4> :call test_setmouse(3, &columns / 2 - 20)<CR>
629	map <silent> <F5> :call test_setmouse(12, &columns / 2)<CR>
630	map <silent> <F6> :call test_setmouse(12, &columns / 2 - 20)<CR>
631  END
632  call writefile(lines, 'XtestPopupTerm')
633  let buf = RunVimInTerminal('-S XtestPopupTerm', #{rows: 16})
634  call VerifyScreenDump(buf, 'Test_popupwin_term_01', {})
635
636  call term_sendkeys(buf, ":call DragitLeft()\<CR>")
637  call VerifyScreenDump(buf, 'Test_popupwin_term_02', {})
638
639  call term_sendkeys(buf, ":call DragitDown()\<CR>")
640  call VerifyScreenDump(buf, 'Test_popupwin_term_03', {})
641
642  call term_sendkeys(buf, ":call DragitDownLeft()\<CR>")
643  call VerifyScreenDump(buf, 'Test_popupwin_term_04', {})
644
645  " clean up
646  call StopVimInTerminal(buf)
647  call delete('XtestPopupTerm')
648endfunc
649
650func Test_popup_close_with_mouse()
651  CheckScreendump
652
653  let lines =<< trim END
654	call setline(1, range(1, 20))
655	" With border, can click on X
656	let winid = popup_create('foobar', #{
657	      \ close: 'button',
658	      \ border: [],
659	      \ line: 1,
660	      \ col: 1,
661	      \ })
662	func CloseMsg(id, result)
663	  echomsg 'Popup closed with ' .. a:result
664	endfunc
665	let winid = popup_create('notification', #{
666	      \ close: 'click',
667	      \ line: 3,
668	      \ col: 15,
669	      \ callback: 'CloseMsg',
670	      \ })
671	let winid = popup_create('no border here', #{
672	      \ close: 'button',
673	      \ line: 5,
674	      \ col: 3,
675	      \ })
676	let winid = popup_create('only padding', #{
677	      \ close: 'button',
678	      \ padding: [],
679	      \ line: 5,
680	      \ col: 23,
681	      \ })
682	func CloseWithX()
683	  call feedkeys("\<F3>\<LeftMouse>\<LeftRelease>", "xt")
684	endfunc
685	map <silent> <F3> :call test_setmouse(1, len('foobar') + 2)<CR>
686	func CloseWithClick()
687	  call feedkeys("\<F4>\<LeftMouse>\<LeftRelease>", "xt")
688	endfunc
689	map <silent> <F4> :call test_setmouse(3, 17)<CR>
690	func CreateWithMenuFilter()
691	  let winid = popup_create('barfoo', #{
692		\ close: 'button',
693		\ filter: 'popup_filter_menu',
694		\ border: [],
695		\ line: 1,
696		\ col: 40,
697		\ })
698	endfunc
699  END
700  call writefile(lines, 'XtestPopupClose')
701  let buf = RunVimInTerminal('-S XtestPopupClose', #{rows: 10})
702  call VerifyScreenDump(buf, 'Test_popupwin_close_01', {})
703
704  call term_sendkeys(buf, ":call CloseWithX()\<CR>")
705  call VerifyScreenDump(buf, 'Test_popupwin_close_02', {})
706
707  call term_sendkeys(buf, ":call CloseWithClick()\<CR>")
708  call VerifyScreenDump(buf, 'Test_popupwin_close_03', {})
709
710  call term_sendkeys(buf, ":call CreateWithMenuFilter()\<CR>")
711  call VerifyScreenDump(buf, 'Test_popupwin_close_04', {})
712
713  " We have to send the actual mouse code, feedkeys() would be caught the
714  " filter.
715  call term_sendkeys(buf, "\<Esc>[<0;47;1M")
716  call VerifyScreenDump(buf, 'Test_popupwin_close_05', {})
717
718  " clean up
719  call StopVimInTerminal(buf)
720  call delete('XtestPopupClose')
721endfunction
722
723func Test_popup_menu_wrap()
724  CheckScreendump
725
726  let lines =<< trim END
727	call setline(1, range(1, 20))
728	call popup_create([
729	      \ 'one',
730	      \ 'asdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfas',
731	      \ 'three',
732	      \ 'four',
733	      \ ], #{
734	      \ pos: "botleft",
735	      \ border: [],
736	      \ padding: [0,1,0,1],
737	      \ maxheight: 3,
738	      \ cursorline: 1,
739	      \ filter: 'popup_filter_menu',
740	      \ })
741  END
742  call writefile(lines, 'XtestPopupWrap')
743  let buf = RunVimInTerminal('-S XtestPopupWrap', #{rows: 10})
744  call VerifyScreenDump(buf, 'Test_popupwin_wrap_1', {})
745
746  call term_sendkeys(buf, "jj")
747  call VerifyScreenDump(buf, 'Test_popupwin_wrap_2', {})
748
749  " clean up
750  call term_sendkeys(buf, "\<Esc>")
751  call StopVimInTerminal(buf)
752  call delete('XtestPopupWrap')
753endfunction
754
755func Test_popup_with_mask()
756  CheckScreendump
757
758  let lines =<< trim END
759	call setline(1, repeat([join(range(1, 42), '')], 13))
760	hi PopupColor ctermbg=lightgrey
761	let winid = popup_create([
762	    \ 'some text',
763	    \ 'another line',
764	    \], #{
765	    \ line: 1,
766	    \ col: 10,
767	    \ posinvert: 0,
768	    \ wrap: 0,
769	    \ fixed: 1,
770	    \ scrollbar: v:false,
771	    \ zindex: 90,
772	    \ padding: [],
773	    \ highlight: 'PopupColor',
774	    \ mask: [[1,1,1,1], [-5,-1,4,4], [7,9,2,3], [2,4,3,3]]})
775	call popup_create([
776	    \ 'xxxxxxxxx',
777	    \ 'yyyyyyyyy',
778	    \], #{
779	    \ line: 3,
780	    \ col: 18,
781	    \ zindex: 20})
782	let winidb = popup_create([
783	    \ 'just one line',
784	    \], #{
785	    \ line: 7,
786	    \ col: 10,
787	    \ posinvert: 0,
788	    \ wrap: 0,
789	    \ fixed: 1,
790	    \ scrollbar: v:false,
791	    \ close: 'button',
792	    \ zindex: 90,
793	    \ padding: [],
794	    \ border: [],
795	    \ mask: [[1,2,1,1], [-5,-1,4,4], [7,9,2,3], [3,5,5,5],[-7,-4,5,5]]})
796  END
797  call writefile(lines, 'XtestPopupMask')
798  let buf = RunVimInTerminal('-S XtestPopupMask', #{rows: 13})
799  call VerifyScreenDump(buf, 'Test_popupwin_mask_1', {})
800
801  call term_sendkeys(buf, ":call popup_move(winid, #{col: 11, line: 2})\<CR>")
802  call term_sendkeys(buf, ":call popup_move(winidb, #{col: 12})\<CR>")
803  call term_sendkeys(buf, ":\<CR>")
804  call VerifyScreenDump(buf, 'Test_popupwin_mask_2', {})
805
806  call term_sendkeys(buf, ":call popup_move(winid, #{col: 65, line: 2})\<CR>")
807  call term_sendkeys(buf, ":call popup_move(winidb, #{col: 63})\<CR>")
808  call term_sendkeys(buf, ":\<CR>")
809  call VerifyScreenDump(buf, 'Test_popupwin_mask_3', {})
810
811  call term_sendkeys(buf, ":call popup_move(winid, #{pos: 'topright', col: 12, line: 2})\<CR>")
812  call term_sendkeys(buf, ":call popup_move(winidb, #{pos: 'topright', col: 12})\<CR>")
813  call term_sendkeys(buf, ":\<CR>")
814  call VerifyScreenDump(buf, 'Test_popupwin_mask_4', {})
815
816  call term_sendkeys(buf, ":call popup_move(winid, #{pos: 'topright', col: 12, line: 11})\<CR>")
817  call term_sendkeys(buf, ":call popup_move(winidb, #{pos: 'topleft', col: 42, line: 11})\<CR>")
818  call term_sendkeys(buf, ":\<CR>")
819  call VerifyScreenDump(buf, 'Test_popupwin_mask_5', {})
820
821  " clean up
822  call StopVimInTerminal(buf)
823  call delete('XtestPopupMask')
824
825  " this was causing a crash
826  call popup_create('test', #{mask: [[0, 0, 0, 0]]})
827  call popup_clear()
828
829  " this was causing an internal error
830  enew
831  set nowrap
832  call repeat('x', &columns)->setline(1)
833  call prop_type_add('textprop', {})
834  call prop_add(1, 1, #{length: &columns, type: 'textprop'})
835  vsplit
836  let opts = popup_create('', #{textprop: 'textprop'})
837	\ ->popup_getoptions()
838	\ ->extend(#{mask: [[1, 1, 1, 1]]})
839  call popup_create('', opts)
840  redraw
841
842  close!
843  bwipe!
844  call prop_type_delete('textprop')
845  call popup_clear()
846  set wrap&
847endfunc
848
849func Test_popup_select()
850  CheckScreendump
851  CheckFeature clipboard_working
852
853  " create a popup with some text to be selected
854  let lines =<< trim END
855    set clipboard=autoselect
856    call setline(1, range(1, 20))
857    let winid = popup_create(['the word', 'some more', 'several words here', 'invisible', '5', '6', '7'], #{
858	  \ drag: 1,
859	  \ border: [],
860	  \ line: 3,
861	  \ col: 10,
862	  \ maxheight: 3,
863	  \ })
864    func Select1()
865      call feedkeys("\<F3>\<LeftMouse>\<F4>\<LeftDrag>\<LeftRelease>", "xt")
866    endfunc
867    map <silent> <F3> :call test_setmouse(4, 15)<CR>
868    map <silent> <F4> :call test_setmouse(6, 23)<CR>
869  END
870  call writefile(lines, 'XtestPopupSelect')
871  let buf = RunVimInTerminal('-S XtestPopupSelect', #{rows: 10})
872  call term_sendkeys(buf, ":call Select1()\<CR>")
873  call VerifyScreenDump(buf, 'Test_popupwin_select_01', {})
874
875  call term_sendkeys(buf, ":call popup_close(winid)\<CR>")
876  call term_sendkeys(buf, "\"*p")
877  " clean the command line, sometimes it still shows a command
878  call term_sendkeys(buf, ":\<esc>")
879
880  call VerifyScreenDump(buf, 'Test_popupwin_select_02', {})
881
882  " clean up
883  call StopVimInTerminal(buf)
884  call delete('XtestPopupSelect')
885endfunc
886
887func Test_popup_in_tab()
888  " default popup is local to tab, not visible when in other tab
889  let winid = popup_create("text", {})
890  let bufnr = winbufnr(winid)
891  call assert_equal(1, popup_getpos(winid).visible)
892  call assert_equal(0, popup_getoptions(winid).tabpage)
893  tabnew
894  call assert_equal(0, popup_getpos(winid).visible)
895  call assert_equal(1, popup_getoptions(winid).tabpage)
896  quit
897  call assert_equal(1, popup_getpos(winid).visible)
898
899  call assert_equal(1, bufexists(bufnr))
900  call popup_clear()
901  " buffer is gone now
902  call assert_equal(0, bufexists(bufnr))
903
904  " global popup is visible in any tab
905  let winid = popup_create("text", #{tabpage: -1})
906  call assert_equal(1, popup_getpos(winid).visible)
907  call assert_equal(-1, popup_getoptions(winid).tabpage)
908  tabnew
909  call assert_equal(1, popup_getpos(winid).visible)
910  call assert_equal(-1, popup_getoptions(winid).tabpage)
911  quit
912  call assert_equal(1, popup_getpos(winid).visible)
913  call popup_clear()
914
915  " create popup in other tab
916  tabnew
917  let winid = popup_create("text", #{tabpage: 1})
918  call assert_equal(0, popup_getpos(winid).visible)
919  call assert_equal(1, popup_getoptions(winid).tabpage)
920  quit
921  call assert_equal(1, popup_getpos(winid).visible)
922  call assert_equal(0, popup_getoptions(winid).tabpage)
923  call popup_clear()
924endfunc
925
926func Test_popup_valid_arguments()
927  call assert_equal(0, len(popup_list()))
928
929  " Zero value is like the property wasn't there
930  let winid = popup_create("text", #{col: 0})
931  let pos = popup_getpos(winid)
932  call assert_inrange(&columns / 2 - 1, &columns / 2 + 1, pos.col)
933  call assert_equal([winid], popup_list())
934  call popup_clear()
935
936  " using cursor column has minimum value of 1
937  let winid = popup_create("text", #{col: 'cursor-100'})
938  let pos = popup_getpos(winid)
939  call assert_equal(1, pos.col)
940  call popup_clear()
941
942  " center
943  let winid = popup_create("text", #{pos: 'center'})
944  let pos = popup_getpos(winid)
945  let around = (&columns - pos.width) / 2
946  call assert_inrange(around - 1, around + 1, pos.col)
947  let around = (&lines - pos.height) / 2
948  call assert_inrange(around - 1, around + 1, pos.line)
949  call popup_clear()
950endfunc
951
952func Test_popup_invalid_arguments()
953  call assert_fails('call popup_create(666, {})', 'E86:')
954  call popup_clear()
955  call assert_fails('call popup_create("text", "none")', 'E715:')
956  call popup_clear()
957  call assert_fails('call popup_create(test_null_string(), {})', 'E450:')
958  call assert_fails('call popup_create(test_null_list(), {})', 'E450:')
959  call popup_clear()
960
961  call assert_fails('call popup_create("text", #{col: "xxx"})', 'E475:')
962  call popup_clear()
963  call assert_fails('call popup_create("text", #{col: "cursor8"})', 'E15:')
964  call popup_clear()
965  call assert_fails('call popup_create("text", #{col: "cursor+x"})', 'E15:')
966  call popup_clear()
967  call assert_fails('call popup_create("text", #{col: "cursor+8x"})', 'E15:')
968  call popup_clear()
969
970  call assert_fails('call popup_create("text", #{line: "xxx"})', 'E475:')
971  call popup_clear()
972  call assert_fails('call popup_create("text", #{line: "cursor8"})', 'E15:')
973  call popup_clear()
974  call assert_fails('call popup_create("text", #{line: "cursor+x"})', 'E15:')
975  call popup_clear()
976  call assert_fails('call popup_create("text", #{line: "cursor+8x"})', 'E15:')
977  call popup_clear()
978
979  call assert_fails('call popup_create("text", #{pos: "there"})', 'E475:')
980  call popup_clear()
981  call assert_fails('call popup_create("text", #{padding: "none"})', 'E714:')
982  call popup_clear()
983  call assert_fails('call popup_create("text", #{border: "none"})', 'E714:')
984  call popup_clear()
985  call assert_fails('call popup_create("text", #{borderhighlight: "none"})', 'E714:')
986  call popup_clear()
987  call assert_fails('call popup_create("text", #{borderhighlight: test_null_list()})', 'E714:')
988  call popup_clear()
989  call assert_fails('call popup_create("text", #{borderchars: "none"})', 'E714:')
990  call popup_clear()
991
992  call assert_fails('call popup_create([#{text: "text"}, 666], {})', 'E715:')
993  call popup_clear()
994  call assert_fails('call popup_create([#{text: "text", props: "none"}], {})', 'E714:')
995  call popup_clear()
996  call assert_fails('call popup_create([#{text: "text", props: ["none"]}], {})', 'E715:')
997  call popup_clear()
998  call assert_fails('call popup_create([#{text: "text", props: range(3)}], {})', 'E715:')
999  call popup_clear()
1000  call assert_fails('call popup_create("text", #{mask: ["asdf"]})', 'E475:')
1001  call popup_clear()
1002  call assert_fails('call popup_create("text", #{mask: range(5)})', 'E475:')
1003  call popup_clear()
1004  call popup_create("text", #{mask: [range(4)]})
1005  call popup_clear()
1006  call assert_fails('call popup_create("text", #{mask: test_null_list()})', 'E475:')
1007  call assert_fails('call popup_create("text", #{mapping: []})', 'E745:')
1008  call popup_clear()
1009  call assert_fails('call popup_create("text", #{tabpage : 4})', 'E997:')
1010  call popup_clear()
1011endfunc
1012
1013func Test_win_execute_closing_curwin()
1014  split
1015  let winid = popup_create('some text', {})
1016  call assert_fails('call win_execute(winid, winnr() .. "close")', 'E994:')
1017  call popup_clear()
1018
1019  let winid = popup_create('some text', {})
1020  call assert_fails('call win_execute(winid, printf("normal! :\<C-u>call popup_close(%d)\<CR>", winid))', 'E994:')
1021  call popup_clear()
1022endfunc
1023
1024func Test_win_execute_not_allowed()
1025  let winid = popup_create('some text', {})
1026  call assert_fails('call win_execute(winid, "split")', 'E994:')
1027  call assert_fails('call win_execute(winid, "vsplit")', 'E994:')
1028  call assert_fails('call win_execute(winid, "close")', 'E994:')
1029  call assert_fails('call win_execute(winid, "bdelete")', 'E994:')
1030  call assert_fails('call win_execute(winid, "bwipe!")', 'E994:')
1031  call assert_fails('call win_execute(winid, "tabnew")', 'E994:')
1032  call assert_fails('call win_execute(winid, "tabnext")', 'E994:')
1033  call assert_fails('call win_execute(winid, "next")', 'E994:')
1034  call assert_fails('call win_execute(winid, "rewind")', 'E994:')
1035  call assert_fails('call win_execute(winid, "pedit filename")', 'E994:')
1036  call assert_fails('call win_execute(winid, "buf")', 'E994:')
1037  call assert_fails('call win_execute(winid, "bnext")', 'E994:')
1038  call assert_fails('call win_execute(winid, "bprev")', 'E994:')
1039  call assert_fails('call win_execute(winid, "bfirst")', 'E994:')
1040  call assert_fails('call win_execute(winid, "blast")', 'E994:')
1041  call assert_fails('call win_execute(winid, "edit")', 'E994:')
1042  call assert_fails('call win_execute(winid, "enew")', 'E994:')
1043  call assert_fails('call win_execute(winid, "help")', 'E994:')
1044  call assert_fails('call win_execute(winid, "1only")', 'E994:')
1045  call assert_fails('call win_execute(winid, "wincmd x")', 'E994:')
1046  call assert_fails('call win_execute(winid, "wincmd w")', 'E994:')
1047  call assert_fails('call win_execute(winid, "wincmd t")', 'E994:')
1048  call assert_fails('call win_execute(winid, "wincmd b")', 'E994:')
1049  call popup_clear()
1050endfunc
1051
1052func Test_popup_with_wrap()
1053  CheckScreendump
1054
1055  let lines =<< trim END
1056	 call setline(1, range(1, 100))
1057	 let winid = popup_create(
1058	   \ 'a long line that wont fit',
1059	   \ #{line: 3, col: 20, maxwidth: 10, wrap: 1})
1060  END
1061  call writefile(lines, 'XtestPopup')
1062  let buf = RunVimInTerminal('-S XtestPopup', #{rows: 10})
1063  call VerifyScreenDump(buf, 'Test_popupwin_wrap', {})
1064
1065  " clean up
1066  call StopVimInTerminal(buf)
1067  call delete('XtestPopup')
1068endfunc
1069
1070func Test_popup_without_wrap()
1071  CheckScreendump
1072
1073  let lines =<< trim END
1074	 call setline(1, range(1, 100))
1075	 let winid = popup_create(
1076	   \ 'a long line that wont fit',
1077	   \ #{line: 3, col: 20, maxwidth: 10, wrap: 0})
1078  END
1079  call writefile(lines, 'XtestPopup')
1080  let buf = RunVimInTerminal('-S XtestPopup', #{rows: 10})
1081  call VerifyScreenDump(buf, 'Test_popupwin_nowrap', {})
1082
1083  " clean up
1084  call StopVimInTerminal(buf)
1085  call delete('XtestPopup')
1086endfunc
1087
1088func Test_popup_with_showbreak()
1089  CheckScreendump
1090
1091  let lines =<< trim END
1092	 set showbreak=>>\
1093	 call setline(1, range(1, 20))
1094	 let winid = popup_dialog(
1095	   \ 'a long line here that wraps',
1096	   \ #{filter: 'popup_filter_yesno',
1097	   \   maxwidth: 12})
1098  END
1099  call writefile(lines, 'XtestPopupShowbreak')
1100  let buf = RunVimInTerminal('-S XtestPopupShowbreak', #{rows: 10})
1101  call VerifyScreenDump(buf, 'Test_popupwin_showbreak', {})
1102
1103  " clean up
1104  call term_sendkeys(buf, "y")
1105  call StopVimInTerminal(buf)
1106  call delete('XtestPopupShowbreak')
1107endfunc
1108
1109func Test_popup_time()
1110  CheckFeature timers
1111
1112  topleft vnew
1113  call setline(1, 'hello')
1114
1115  let winid = popup_create('world', #{
1116	\ line: 1,
1117	\ col: 1,
1118	\ minwidth: 20,
1119	\ time: 500,
1120	\})
1121  redraw
1122  let line = join(map(range(1, 5), 'screenstring(1, v:val)'), '')
1123  call assert_equal('world', line)
1124
1125  call assert_equal(winid, popup_locate(1, 1))
1126  call assert_equal(winid, popup_locate(1, 20))
1127  call assert_equal(0, popup_locate(1, 21))
1128  call assert_equal(0, popup_locate(2, 1))
1129
1130  sleep 700m
1131  redraw
1132  let line = join(map(range(1, 5), '1->screenstring(v:val)'), '')
1133  call assert_equal('hello', line)
1134
1135  call popup_create('on the command line', #{
1136	\ line: &lines,
1137	\ col: 10,
1138	\ minwidth: 20,
1139	\ time: 500,
1140	\})
1141  redraw
1142  let line = join(map(range(1, 30), 'screenstring(&lines, v:val)'), '')
1143  call assert_match('.*on the command line.*', line)
1144
1145  sleep 700m
1146  redraw
1147  let line = join(map(range(1, 30), 'screenstring(&lines, v:val)'), '')
1148  call assert_notmatch('.*on the command line.*', line)
1149
1150  bwipe!
1151endfunc
1152
1153func Test_popup_hide()
1154  topleft vnew
1155  call setline(1, 'hello')
1156
1157  let winid = popup_create('world', #{
1158	\ line: 1,
1159	\ col: 1,
1160	\ minwidth: 20,
1161	\})
1162  redraw
1163  let line = join(map(range(1, 5), 'screenstring(1, v:val)'), '')
1164  call assert_equal('world', line)
1165  call assert_equal(1, popup_getpos(winid).visible)
1166  " buffer is still listed and active
1167  call assert_match(winbufnr(winid) .. 'u a.*\[Popup\]', execute('ls u'))
1168
1169  call popup_hide(winid)
1170  redraw
1171  let line = join(map(range(1, 5), 'screenstring(1, v:val)'), '')
1172  call assert_equal('hello', line)
1173  call assert_equal(0, popup_getpos(winid).visible)
1174  " buffer is still listed but hidden
1175  call assert_match(winbufnr(winid) .. 'u a.*\[Popup\]', execute('ls u'))
1176
1177  eval winid->popup_show()
1178  redraw
1179  let line = join(map(range(1, 5), 'screenstring(1, v:val)'), '')
1180  call assert_equal('world', line)
1181  call assert_equal(1, popup_getpos(winid).visible)
1182
1183
1184  call popup_close(winid)
1185  redraw
1186  let line = join(map(range(1, 5), 'screenstring(1, v:val)'), '')
1187  call assert_equal('hello', line)
1188
1189  " error is given for existing non-popup window
1190  call assert_fails('call popup_hide(win_getid())', 'E993:')
1191
1192  " no error non-existing window
1193  eval 1234234->popup_hide()
1194  call popup_show(41234234)
1195
1196  bwipe!
1197endfunc
1198
1199func Test_popup_move()
1200  topleft vnew
1201  call setline(1, 'hello')
1202
1203  let winid = popup_create('world', #{
1204	\ line: 1,
1205	\ col: 1,
1206	\ minwidth: 20,
1207	\})
1208  redraw
1209  let line = join(map(range(1, 6), 'screenstring(1, v:val)'), '')
1210  call assert_equal('world ', line)
1211
1212  call popup_move(winid, #{line: 2, col: 2})
1213  redraw
1214  let line = join(map(range(1, 6), 'screenstring(1, v:val)'), '')
1215  call assert_equal('hello ', line)
1216  let line = join(map(range(1, 6), 'screenstring(2, v:val)'), '')
1217  call assert_equal('~world', line)
1218
1219  eval winid->popup_move(#{line: 1})
1220  redraw
1221  let line = join(map(range(1, 6), 'screenstring(1, v:val)'), '')
1222  call assert_equal('hworld', line)
1223
1224  call assert_fails('call popup_move(winid, [])', 'E715:')
1225  call assert_fails('call popup_move(winid, test_null_dict())', 'E715:')
1226
1227  call popup_close(winid)
1228
1229  call assert_equal(0, popup_move(-1, {}))
1230
1231  bwipe!
1232endfunc
1233
1234func Test_popup_getpos()
1235  let winid = popup_create('hello', #{
1236    \ line: 2,
1237    \ col: 3,
1238    \ minwidth: 10,
1239    \ minheight: 11,
1240    \})
1241  redraw
1242  let res = popup_getpos(winid)
1243  call assert_equal(2, res.line)
1244  call assert_equal(3, res.col)
1245  call assert_equal(10, res.width)
1246  call assert_equal(11, res.height)
1247  call assert_equal(1, res.visible)
1248
1249  call popup_close(winid)
1250endfunc
1251
1252func Test_popup_width_longest()
1253  let tests = [
1254	\ [['hello', 'this', 'window', 'displays', 'all of its text'], 15],
1255	\ [['hello', 'this', 'window', 'all of its text', 'displays'], 15],
1256	\ [['hello', 'this', 'all of its text', 'window', 'displays'], 15],
1257	\ [['hello', 'all of its text', 'this', 'window', 'displays'], 15],
1258	\ [['all of its text', 'hello', 'this', 'window', 'displays'], 15],
1259	\ ]
1260
1261  for test in tests
1262    let winid = popup_create(test[0], #{line: 2, col: 3})
1263    redraw
1264    let position = popup_getpos(winid)
1265    call assert_equal(test[1], position.width)
1266    call popup_close(winid)
1267  endfor
1268endfunc
1269
1270func Test_popup_wraps()
1271  let tests = [
1272	\ ['nowrap', 6, 1],
1273	\ ['a line that wraps once', 12, 2],
1274	\ ['a line that wraps two times', 12, 3],
1275	\ ]
1276  for test in tests
1277    let winid = popup_create(test[0],
1278	  \ #{line: 2, col: 3, maxwidth: 12})
1279    redraw
1280    let position = popup_getpos(winid)
1281    call assert_equal(test[1], position.width)
1282    call assert_equal(test[2], position.height)
1283
1284    call popup_close(winid)
1285    call assert_equal({}, popup_getpos(winid))
1286  endfor
1287endfunc
1288
1289func Test_popup_getoptions()
1290  let winid = popup_create('hello', #{
1291    \ line: 2,
1292    \ col: 3,
1293    \ minwidth: 10,
1294    \ minheight: 11,
1295    \ maxwidth: 20,
1296    \ maxheight: 21,
1297    \ zindex: 100,
1298    \ time: 5000,
1299    \ fixed: 1
1300    \})
1301  redraw
1302  let res = popup_getoptions(winid)
1303  call assert_equal(2, res.line)
1304  call assert_equal(3, res.col)
1305  call assert_equal(10, res.minwidth)
1306  call assert_equal(11, res.minheight)
1307  call assert_equal(20, res.maxwidth)
1308  call assert_equal(21, res.maxheight)
1309  call assert_equal(100, res.zindex)
1310  call assert_equal(1, res.fixed)
1311  call assert_equal(1, res.mapping)
1312  if has('timers')
1313    call assert_equal(5000, res.time)
1314  endif
1315  call popup_close(winid)
1316
1317  let winid = popup_create('hello', {})
1318  redraw
1319  let res = popup_getoptions(winid)
1320  call assert_equal(0, res.line)
1321  call assert_equal(0, res.col)
1322  call assert_equal(0, res.minwidth)
1323  call assert_equal(0, res.minheight)
1324  call assert_equal(0, res.maxwidth)
1325  call assert_equal(0, res.maxheight)
1326  call assert_equal(50, res.zindex)
1327  call assert_equal(0, res.fixed)
1328  if has('timers')
1329    call assert_equal(0, res.time)
1330  endif
1331  call popup_close(winid)
1332  call assert_equal({}, popup_getoptions(winid))
1333endfunc
1334
1335func Test_popup_option_values()
1336  new
1337  " window-local
1338  setlocal number
1339  setlocal nowrap
1340  " buffer-local
1341  setlocal omnifunc=Something
1342  " global/buffer-local
1343  setlocal path=/there
1344  " global/window-local
1345  setlocal statusline=2
1346
1347  let winid = popup_create('hello', {})
1348  call assert_equal(0, getwinvar(winid, '&number'))
1349  call assert_equal(1, getwinvar(winid, '&wrap'))
1350  call assert_equal('', getwinvar(winid, '&omnifunc'))
1351  call assert_equal(&g:path, getwinvar(winid, '&path'))
1352  call assert_equal(&g:statusline, getwinvar(winid, '&statusline'))
1353
1354  " 'scrolloff' is reset to zero
1355  call assert_equal(5, &scrolloff)
1356  call assert_equal(0, getwinvar(winid, '&scrolloff'))
1357
1358  call popup_close(winid)
1359  bwipe
1360endfunc
1361
1362func Test_popup_atcursor()
1363  topleft vnew
1364  call setline(1, [
1365  \  'xxxxxxxxxxxxxxxxx',
1366  \  'xxxxxxxxxxxxxxxxx',
1367  \  'xxxxxxxxxxxxxxxxx',
1368  \])
1369
1370  call cursor(2, 2)
1371  redraw
1372  let winid = popup_atcursor('vim', {})
1373  redraw
1374  let line = join(map(range(1, 17), 'screenstring(1, v:val)'), '')
1375  call assert_equal('xvimxxxxxxxxxxxxx', line)
1376  call popup_close(winid)
1377
1378  call cursor(3, 4)
1379  redraw
1380  let winid = 'vim'->popup_atcursor({})
1381  redraw
1382  let line = join(map(range(1, 17), 'screenstring(2, v:val)'), '')
1383  call assert_equal('xxxvimxxxxxxxxxxx', line)
1384  call popup_close(winid)
1385
1386  call cursor(1, 1)
1387  redraw
1388  let winid = popup_create('vim', #{
1389	\ line: 'cursor+2',
1390	\ col: 'cursor+1',
1391	\})
1392  redraw
1393  let line = join(map(range(1, 17), 'screenstring(3, v:val)'), '')
1394  call assert_equal('xvimxxxxxxxxxxxxx', line)
1395  call popup_close(winid)
1396
1397  call cursor(3, 3)
1398  redraw
1399  let winid = popup_create('vim', #{
1400	\ line: 'cursor-2',
1401	\ col: 'cursor-1',
1402	\})
1403  redraw
1404  let line = join(map(range(1, 17), 'screenstring(1, v:val)'), '')
1405  call assert_equal('xvimxxxxxxxxxxxxx', line)
1406  call popup_close(winid)
1407
1408  " just enough room above
1409  call cursor(3, 3)
1410  redraw
1411  let winid = popup_atcursor(['vim', 'is great'], {})
1412  redraw
1413  let pos = popup_getpos(winid)
1414  call assert_equal(1, pos.line)
1415  call popup_close(winid)
1416
1417  " not enough room above, popup goes below the cursor
1418  call cursor(3, 3)
1419  redraw
1420  let winid = popup_atcursor(['vim', 'is', 'great'], {})
1421  redraw
1422  let pos = popup_getpos(winid)
1423  call assert_equal(4, pos.line)
1424  call popup_close(winid)
1425
1426  " cursor in first line, popup in line 2
1427  call cursor(1, 1)
1428  redraw
1429  let winid = popup_atcursor(['vim', 'is', 'great'], {})
1430  redraw
1431  let pos = popup_getpos(winid)
1432  call assert_equal(2, pos.line)
1433  call popup_close(winid)
1434
1435  bwipe!
1436endfunc
1437
1438func Test_popup_atcursor_pos()
1439  CheckScreendump
1440  CheckFeature conceal
1441
1442  let lines =<< trim END
1443	call setline(1, repeat([repeat('-', 60)], 15))
1444	set so=0
1445
1446	normal 9G3|r#
1447	let winid1 = popup_atcursor(['first', 'second'], #{
1448	      \ moved: [0, 0, 0],
1449	      \ })
1450	normal 9G21|r&
1451	let winid1 = popup_atcursor(['FIrsT', 'SEcoND'], #{
1452	      \ pos: 'botright',
1453	      \ moved: [0, 0, 0],
1454	      \ })
1455	normal 3G27|r%
1456	let winid1 = popup_atcursor(['fiRSt', 'seCOnd'], #{
1457	      \ pos: 'topleft',
1458	      \ moved: [0, 0, 0],
1459	      \ })
1460	normal 3G45|r@
1461	let winid1 = popup_atcursor(['First', 'SeconD'], #{
1462	      \ pos: 'topright',
1463	      \ moved: range(3),
1464	      \ mousemoved: range(3),
1465	      \ })
1466
1467	normal 9G27|Rconcealed  X
1468	syn match Hidden /concealed/ conceal
1469	set conceallevel=2 concealcursor=n
1470	redraw
1471	normal 0fX
1472	call popup_atcursor('mark', {})
1473  END
1474  call writefile(lines, 'XtestPopupAtcursorPos')
1475  let buf = RunVimInTerminal('-S XtestPopupAtcursorPos', #{rows: 12})
1476  call VerifyScreenDump(buf, 'Test_popupwin_atcursor_pos', {})
1477
1478  " clean up
1479  call StopVimInTerminal(buf)
1480  call delete('XtestPopupAtcursorPos')
1481endfunc
1482
1483func Test_popup_beval()
1484  CheckScreendump
1485  CheckFeature balloon_eval_term
1486
1487  let lines =<< trim END
1488	call setline(1, range(1, 20))
1489	call setline(5, 'here is some text to hover over')
1490	set balloonevalterm
1491	set balloonexpr=BalloonExpr()
1492	set balloondelay=100
1493	func BalloonExpr()
1494	  let s:winid = [v:beval_text]->popup_beval({})
1495	  return ''
1496	endfunc
1497	func Hover()
1498	  call test_setmouse(5, 15)
1499	  call feedkeys("\<MouseMove>\<Ignore>", "xt")
1500	  sleep 100m
1501	endfunc
1502	func MoveOntoPopup()
1503	  call test_setmouse(4, 17)
1504	  call feedkeys("\<F4>\<MouseMove>\<Ignore>", "xt")
1505	endfunc
1506	func MoveAway()
1507	  call test_setmouse(5, 13)
1508	  call feedkeys("\<F5>\<MouseMove>\<Ignore>", "xt")
1509	endfunc
1510  END
1511  call writefile(lines, 'XtestPopupBeval')
1512  let buf = RunVimInTerminal('-S XtestPopupBeval', #{rows: 10})
1513  call TermWait(buf, 50)
1514  call term_sendkeys(buf, 'j')
1515  call term_sendkeys(buf, ":call Hover()\<CR>")
1516  call VerifyScreenDump(buf, 'Test_popupwin_beval_1', {})
1517
1518  call term_sendkeys(buf, ":call MoveOntoPopup()\<CR>")
1519  call VerifyScreenDump(buf, 'Test_popupwin_beval_2', {})
1520
1521  call term_sendkeys(buf, ":call MoveAway()\<CR>")
1522  call VerifyScreenDump(buf, 'Test_popupwin_beval_3', {})
1523
1524  " clean up
1525  call StopVimInTerminal(buf)
1526  call delete('XtestPopupBeval')
1527endfunc
1528
1529func Test_popup_filter()
1530  new
1531  call setline(1, 'some text')
1532
1533  func MyPopupFilter(winid, c)
1534    if a:c == 'e' || a:c == "\<F9>"
1535      let g:eaten = a:c
1536      return 1
1537    endif
1538    if a:c == '0'
1539      let g:ignored = '0'
1540      return 0
1541    endif
1542    if a:c == 'x'
1543      call popup_close(a:winid)
1544      return 1
1545    endif
1546    return 0
1547  endfunc
1548
1549  let winid = 'something'->popup_create(#{filter: 'MyPopupFilter'})
1550  redraw
1551
1552  " e is consumed by the filter
1553  let g:eaten = ''
1554  call feedkeys('e', 'xt')
1555  call assert_equal('e', g:eaten)
1556  call feedkeys("\<F9>", 'xt')
1557  call assert_equal("\<F9>", g:eaten)
1558
1559  " 0 is ignored by the filter
1560  let g:ignored = ''
1561  normal $
1562  call assert_equal(9, getcurpos()[2])
1563  call feedkeys('0', 'xt')
1564  call assert_equal('0', g:ignored)
1565
1566  if has('win32') && has('gui_running')
1567    echo "FIXME: this check is very flaky on MS-Windows GUI, the cursor doesn't move"
1568  else
1569    call assert_equal(1, getcurpos()[2])
1570  endif
1571
1572  " x closes the popup
1573  call feedkeys('x', 'xt')
1574  call assert_equal("\<F9>", g:eaten)
1575  call assert_equal(-1, winbufnr(winid))
1576
1577  unlet g:eaten
1578  unlet g:ignored
1579  delfunc MyPopupFilter
1580  call popup_clear()
1581endfunc
1582
1583" this tests that the filter is not used for :normal command
1584func Test_popup_filter_normal_cmd()
1585  CheckScreendump
1586
1587  let lines =<< trim END
1588      let text = range(1, 20)->map({_, v -> string(v)})
1589      let g:winid = popup_create(text, #{maxheight: 5, minwidth: 3, filter: 'invalidfilter'})
1590      call timer_start(0, {-> win_execute(g:winid, 'norm! 10Gzz')})
1591  END
1592  call writefile(lines, 'XtestPopupNormal')
1593  let buf = RunVimInTerminal('-S XtestPopupNormal', #{rows: 10})
1594  call TermWait(buf, 100)
1595  call VerifyScreenDump(buf, 'Test_popupwin_normal_cmd', {})
1596
1597  call StopVimInTerminal(buf)
1598  call delete('XtestPopupNormal')
1599endfunc
1600
1601" test that cursor line highlight is updated after using win_execute()
1602func Test_popup_filter_win_execute()
1603  CheckScreendump
1604
1605  let lines =<< trim END
1606      let lines = range(1, &lines * 2)->map({_, v -> string(v)})
1607      let g:id = popup_create(lines, #{
1608	  \ minheight: &lines - 5,
1609	  \ maxheight: &lines - 5,
1610	  \ cursorline: 1,
1611	  \ })
1612      redraw
1613  END
1614  call writefile(lines, 'XtestPopupWinExecute')
1615  let buf = RunVimInTerminal('-S XtestPopupWinExecute', #{rows: 14})
1616
1617  call term_sendkeys(buf, ":call win_execute(g:id, ['normal 17Gzz'])\<CR>")
1618  call term_sendkeys(buf, ":\<CR>")
1619
1620  call VerifyScreenDump(buf, 'Test_popupwin_win_execute_cursorline', {})
1621
1622  call StopVimInTerminal(buf)
1623  call delete('XtestPopupWinExecute')
1624endfunc
1625
1626func Test_popup_set_firstline()
1627  CheckScreendump
1628
1629  let lines =<< trim END
1630      let lines = range(1, 50)->map({_, v -> string(v)})
1631      let g:id = popup_create(lines, #{
1632	  \ minwidth: 20,
1633	  \ maxwidth: 20,
1634	  \ minheight: &lines - 5,
1635	  \ maxheight: &lines - 5,
1636	  \ cursorline: 1,
1637	  \ })
1638      call popup_setoptions(g:id, #{firstline: 10})
1639      redraw
1640  END
1641  call writefile(lines, 'XtestPopupWinSetFirstline')
1642  let buf = RunVimInTerminal('-S XtestPopupWinSetFirstline', #{rows: 16})
1643
1644  call VerifyScreenDump(buf, 'Test_popupwin_set_firstline_1', {})
1645
1646  call term_sendkeys(buf, ":call popup_setoptions(g:id, #{firstline: 5})\<CR>")
1647  call term_sendkeys(buf, ":\<CR>")
1648  call VerifyScreenDump(buf, 'Test_popupwin_set_firstline_2', {})
1649
1650  call StopVimInTerminal(buf)
1651  call delete('XtestPopupWinSetFirstline')
1652endfunc
1653
1654" this tests that we don't get stuck with an error in "win_execute()"
1655func Test_popup_filter_win_execute_error()
1656  CheckScreendump
1657
1658  let lines =<< trim END
1659      let g:winid = popup_create('some text', {'filter': 'invalidfilter'})
1660      call timer_start(0, {-> win_execute(g:winid, 'invalidCommand')})
1661  END
1662  call writefile(lines, 'XtestPopupWinExecuteError')
1663  let buf = RunVimInTerminal('-S XtestPopupWinExecuteError', #{rows: 10, wait_for_ruler: 0})
1664
1665  call WaitFor({-> term_getline(buf, 9) =~ 'Not an editor command: invalidCommand'})
1666  call term_sendkeys(buf, "\<CR>")
1667  call WaitFor({-> term_getline(buf, 9) =~ 'Unknown function: invalidfilter'})
1668  call term_sendkeys(buf, "\<CR>")
1669  call WaitFor({-> term_getline(buf, 9) =~ 'Not allowed in a popup window'})
1670  call term_sendkeys(buf, "\<CR>")
1671  call term_sendkeys(buf, "\<CR>")
1672  call VerifyScreenDump(buf, 'Test_popupwin_win_execute', {})
1673
1674  call StopVimInTerminal(buf)
1675  call delete('XtestPopupWinExecuteError')
1676endfunc
1677
1678func ShowDialog(key, result)
1679  let s:cb_res = 999
1680  let winid = popup_dialog('do you want to quit (Yes/no)?', #{
1681	  \ filter: 'popup_filter_yesno',
1682	  \ callback: 'QuitCallback',
1683	  \ })
1684  redraw
1685  call feedkeys(a:key, "xt")
1686  call assert_equal(winid, s:cb_winid)
1687  call assert_equal(a:result, s:cb_res)
1688endfunc
1689
1690func Test_popup_dialog()
1691  func QuitCallback(id, res)
1692    let s:cb_winid = a:id
1693    let s:cb_res = a:res
1694  endfunc
1695
1696  let winid = ShowDialog("y", 1)
1697  let winid = ShowDialog("Y", 1)
1698  let winid = ShowDialog("n", 0)
1699  let winid = ShowDialog("N", 0)
1700  let winid = ShowDialog("x", 0)
1701  let winid = ShowDialog("X", 0)
1702  let winid = ShowDialog("\<Esc>", 0)
1703  let winid = ShowDialog("\<C-C>", -1)
1704
1705  delfunc QuitCallback
1706endfunc
1707
1708func ShowMenu(key, result)
1709  let s:cb_res = 999
1710  let winid = popup_menu(['one', 'two', 'something else'], #{
1711	  \ callback: 'QuitCallback',
1712	  \ })
1713  redraw
1714  call feedkeys(a:key, "xt")
1715  call assert_equal(winid, s:cb_winid)
1716  call assert_equal(a:result, s:cb_res)
1717endfunc
1718
1719func Test_popup_menu()
1720  func QuitCallback(id, res)
1721    let s:cb_winid = a:id
1722    let s:cb_res = a:res
1723  endfunc
1724  " mapping won't be used in popup
1725  map j k
1726
1727  let winid = ShowMenu(" ", 1)
1728  let winid = ShowMenu("j \<CR>", 2)
1729  let winid = ShowMenu("JjK \<CR>", 2)
1730  let winid = ShowMenu("jjjjjj ", 3)
1731  let winid = ShowMenu("kkk ", 1)
1732  let winid = ShowMenu("x", -1)
1733  let winid = ShowMenu("X", -1)
1734  let winid = ShowMenu("\<Esc>", -1)
1735  let winid = ShowMenu("\<C-C>", -1)
1736
1737  delfunc QuitCallback
1738  unmap j
1739endfunc
1740
1741func Test_popup_menu_screenshot()
1742  CheckScreendump
1743
1744  let lines =<< trim END
1745	call setline(1, range(1, 20))
1746	hi PopupSelected ctermbg=lightblue
1747	call popup_menu(['one', 'two', 'another'], #{callback: 'MenuDone', title: ' make a choice from the list '})
1748	func MenuDone(id, res)
1749	  echomsg "selected " .. a:res
1750	endfunc
1751  END
1752  call writefile(lines, 'XtestPopupMenu')
1753  let buf = RunVimInTerminal('-S XtestPopupMenu', #{rows: 10})
1754  call VerifyScreenDump(buf, 'Test_popupwin_menu_01', {})
1755
1756  call term_sendkeys(buf, "jj")
1757  call VerifyScreenDump(buf, 'Test_popupwin_menu_02', {})
1758
1759  call term_sendkeys(buf, " ")
1760  call VerifyScreenDump(buf, 'Test_popupwin_menu_03', {})
1761
1762  " clean up
1763  call StopVimInTerminal(buf)
1764  call delete('XtestPopupMenu')
1765endfunc
1766
1767func Test_popup_menu_narrow()
1768  CheckScreendump
1769
1770  let lines =<< trim END
1771	call setline(1, range(1, 20))
1772	hi PopupSelected ctermbg=green
1773	call popup_menu(['one', 'two', 'three'], #{callback: 'MenuDone'})
1774	func MenuDone(id, res)
1775	  echomsg "selected " .. a:res
1776	endfunc
1777  END
1778  call writefile(lines, 'XtestPopupNarrowMenu')
1779  let buf = RunVimInTerminal('-S XtestPopupNarrowMenu', #{rows: 10})
1780  call VerifyScreenDump(buf, 'Test_popupwin_menu_04', {})
1781
1782  " clean up
1783  call term_sendkeys(buf, "x")
1784  call StopVimInTerminal(buf)
1785  call delete('XtestPopupNarrowMenu')
1786endfunc
1787
1788func Test_popup_title()
1789  CheckScreendump
1790
1791  " Create a popup without title or border, a line of padding will be added to
1792  " put the title on.
1793  let lines =<< trim END
1794	call setline(1, range(1, 20))
1795	let winid = popup_create(['one', 'two', 'another'], #{title: 'Title String'})
1796  END
1797  call writefile(lines, 'XtestPopupTitle')
1798  let buf = RunVimInTerminal('-S XtestPopupTitle', #{rows: 10})
1799  call VerifyScreenDump(buf, 'Test_popupwin_title', {})
1800
1801  call term_sendkeys(buf, ":call popup_setoptions(winid, #{maxwidth: 20, title: 'a very long title that is not going to fit'})\<CR>")
1802  call term_sendkeys(buf, ":\<CR>")
1803  call VerifyScreenDump(buf, 'Test_popupwin_longtitle_1', {})
1804
1805  call term_sendkeys(buf, ":call popup_setoptions(winid, #{border: []})\<CR>")
1806  call term_sendkeys(buf, ":\<CR>")
1807  call VerifyScreenDump(buf, 'Test_popupwin_longtitle_2', {})
1808
1809  call term_sendkeys(buf, ":call popup_clear()\<CR>")
1810  call term_sendkeys(buf, ":call popup_create(['aaa', 'bbb'], #{title: 'Title', minwidth: 12, padding: [2, 2, 2, 2]})\<CR>")
1811  call term_sendkeys(buf, ":\<CR>")
1812  call VerifyScreenDump(buf, 'Test_popupwin_longtitle_3', {})
1813
1814  call term_sendkeys(buf, ":call popup_clear()\<CR>")
1815  call term_sendkeys(buf, ":call popup_create(['aaa', 'bbb'], #{title: 'Title', minwidth: 12, border: [], padding: [2, 2, 2, 2]})\<CR>")
1816  call term_sendkeys(buf, ":\<CR>")
1817  call VerifyScreenDump(buf, 'Test_popupwin_longtitle_4', {})
1818
1819  call term_sendkeys(buf, ":call popup_clear()\<CR>")
1820  call term_sendkeys(buf, ":call popup_menu(['This is a line', 'and another line'], #{title: '▶Äあいうえお◀', })\<CR>")
1821  call VerifyScreenDump(buf, 'Test_popupwin_multibytetitle', {})
1822  call term_sendkeys(buf, "x")
1823
1824  " clean up
1825  call StopVimInTerminal(buf)
1826  call delete('XtestPopupTitle')
1827
1828  let winid = popup_create('something', #{title: 'Some Title'})
1829  call assert_equal('Some Title', popup_getoptions(winid).title)
1830  call popup_setoptions(winid, #{title: 'Another Title'})
1831  call assert_equal('Another Title', popup_getoptions(winid).title)
1832
1833  call popup_clear()
1834endfunc
1835
1836func Test_popup_close_callback()
1837  func PopupDone(id, result)
1838    let g:result = a:result
1839  endfunc
1840  let winid = popup_create('something', #{callback: 'PopupDone'})
1841  redraw
1842  call popup_close(winid, 'done')
1843  call assert_equal('done', g:result)
1844endfunc
1845
1846func Test_popup_empty()
1847  let winid = popup_create('', #{padding: [2,2,2,2]})
1848  redraw
1849  let pos = popup_getpos(winid)
1850  call assert_equal(5, pos.width)
1851  call assert_equal(5, pos.height)
1852  call popup_close(winid)
1853
1854  let winid = popup_create([], #{border: []})
1855  redraw
1856  let pos = popup_getpos(winid)
1857  call assert_equal(3, pos.width)
1858  call assert_equal(3, pos.height)
1859  call popup_close(winid)
1860endfunc
1861
1862func Test_popup_never_behind()
1863  CheckScreendump
1864
1865  " +-----------------------------+
1866  " |             |               |
1867  " |             |               |
1868  " |             |               |
1869  " |            line1            |
1870  " |------------line2------------|
1871  " |            line3            |
1872  " |            line4            |
1873  " |                             |
1874  " |                             |
1875  " +-----------------------------+
1876  let lines =<< trim END
1877    split
1878    vsplit
1879    let info_window1 = getwininfo()[0]
1880    let line = info_window1['height']
1881    let col = info_window1['width']
1882    call popup_create(['line1', 'line2', 'line3', 'line4'], #{
1883	      \   line : line,
1884	      \   col : col,
1885	      \ })
1886  END
1887  call writefile(lines, 'XtestPopupBehind')
1888  let buf = RunVimInTerminal('-S XtestPopupBehind', #{rows: 10})
1889  call term_sendkeys(buf, "\<C-W>w")
1890  call VerifyScreenDump(buf, 'Test_popupwin_behind', {})
1891
1892  " clean up
1893  call StopVimInTerminal(buf)
1894  call delete('XtestPopupBehind')
1895endfunc
1896
1897func s:VerifyPosition(p, msg, line, col, width, height)
1898  call assert_equal(a:line,   popup_getpos(a:p).line,   a:msg . ' (l)')
1899  call assert_equal(a:col,    popup_getpos(a:p).col,    a:msg . ' (c)')
1900  call assert_equal(a:width,  popup_getpos(a:p).width,  a:msg . ' (w)')
1901  call assert_equal(a:height, popup_getpos(a:p).height, a:msg . ' (h)')
1902endfunc
1903
1904func Test_popup_position_adjust()
1905  " Anything placed past the last cell on the right of the screen is moved to
1906  " the left.
1907  "
1908  " When wrapping is disabled, we also shift to the left to display on the
1909  " screen, unless fixed is set.
1910
1911  " Entries for cases which don't vary based on wrapping.
1912  " Format is per tests described below
1913  let both_wrap_tests = [
1914	\       ['a', 5, &columns,        5, &columns, 1, 1],
1915	\       ['b', 5, &columns + 1,    5, &columns, 1, 1],
1916	\       ['c', 5, &columns - 1,    5, &columns - 1, 1, 1],
1917	\       ['d', 5, &columns - 2,    5, &columns - 2, 1, 1],
1918	\       ['e', 5, &columns - 3,    5, &columns - 3, 1, 1]]
1919
1920  " these test groups are dicts with:
1921  "  - comment: something to identify the group of tests by
1922  "  - options: dict of options to merge with the row/col in tests
1923  "  - tests: list of cases. Each one is a list with elements:
1924  "     - text
1925  "     - row
1926  "     - col
1927  "     - expected row
1928  "     - expected col
1929  "     - expected width
1930  "     - expected height
1931  let tests = [
1932	\ #{
1933	\   comment: 'left-aligned with wrapping',
1934	\   options: #{
1935	\     wrap: 1,
1936	\     pos: 'botleft',
1937	\   },
1938	\   tests: both_wrap_tests + [
1939	\       ['aa', 5, &columns,        4, &columns, 1, 2],
1940	\       ['bb', 5, &columns + 1,    4, &columns, 1, 2],
1941	\       ['cc', 5, &columns - 1,    5, &columns - 1, 2, 1],
1942	\       ['dd', 5, &columns - 2,    5, &columns - 2, 2, 1],
1943	\       ['ee', 5, &columns - 3,    5, &columns - 3, 2, 1],
1944	\
1945	\       ['aaa', 5, &columns,        3, &columns, 1, 3],
1946	\       ['bbb', 5, &columns + 1,    3, &columns, 1, 3],
1947	\       ['ccc', 5, &columns - 1,    4, &columns - 1, 2, 2],
1948	\       ['ddd', 5, &columns - 2,    5, &columns - 2, 3, 1],
1949	\       ['eee', 5, &columns - 3,    5, &columns - 3, 3, 1],
1950	\
1951	\       ['aaaa', 5, &columns,        2, &columns, 1, 4],
1952	\       ['bbbb', 5, &columns + 1,    2, &columns, 1, 4],
1953	\       ['cccc', 5, &columns - 1,    4, &columns - 1, 2, 2],
1954	\       ['dddd', 5, &columns - 2,    4, &columns - 2, 3, 2],
1955	\       ['eeee', 5, &columns - 3,    5, &columns - 3, 4, 1],
1956	\       ['eeee', 5, &columns - 4,    5, &columns - 4, 4, 1],
1957	\   ],
1958	\ },
1959	\ #{
1960	\   comment: 'left aligned without wrapping',
1961	\   options: #{
1962	\     wrap: 0,
1963	\     pos: 'botleft',
1964	\   },
1965	\   tests: both_wrap_tests + [
1966	\       ['aa', 5, &columns,        5, &columns - 1, 2, 1],
1967	\       ['bb', 5, &columns + 1,    5, &columns - 1, 2, 1],
1968	\       ['cc', 5, &columns - 1,    5, &columns - 1, 2, 1],
1969	\       ['dd', 5, &columns - 2,    5, &columns - 2, 2, 1],
1970	\       ['ee', 5, &columns - 3,    5, &columns - 3, 2, 1],
1971	\
1972	\       ['aaa', 5, &columns,        5, &columns - 2, 3, 1],
1973	\       ['bbb', 5, &columns + 1,    5, &columns - 2, 3, 1],
1974	\       ['ccc', 5, &columns - 1,    5, &columns - 2, 3, 1],
1975	\       ['ddd', 5, &columns - 2,    5, &columns - 2, 3, 1],
1976	\       ['eee', 5, &columns - 3,    5, &columns - 3, 3, 1],
1977	\
1978	\       ['aaaa', 5, &columns,        5, &columns - 3, 4, 1],
1979	\       ['bbbb', 5, &columns + 1,    5, &columns - 3, 4, 1],
1980	\       ['cccc', 5, &columns - 1,    5, &columns - 3, 4, 1],
1981	\       ['dddd', 5, &columns - 2,    5, &columns - 3, 4, 1],
1982	\       ['eeee', 5, &columns - 3,    5, &columns - 3, 4, 1],
1983	\   ],
1984	\ },
1985	\ #{
1986	\   comment: 'left aligned with fixed position',
1987	\   options: #{
1988	\     wrap: 0,
1989	\     fixed: 1,
1990	\     pos: 'botleft',
1991	\   },
1992	\   tests: both_wrap_tests + [
1993	\       ['aa', 5, &columns,        5, &columns, 1, 1],
1994	\       ['bb', 5, &columns + 1,    5, &columns, 1, 1],
1995	\       ['cc', 5, &columns - 1,    5, &columns - 1, 2, 1],
1996	\       ['dd', 5, &columns - 2,    5, &columns - 2, 2, 1],
1997	\       ['ee', 5, &columns - 3,    5, &columns - 3, 2, 1],
1998	\
1999	\       ['aaa', 5, &columns,        5, &columns, 1, 1],
2000	\       ['bbb', 5, &columns + 1,    5, &columns, 1, 1],
2001	\       ['ccc', 5, &columns - 1,    5, &columns - 1, 2, 1],
2002	\       ['ddd', 5, &columns - 2,    5, &columns - 2, 3, 1],
2003	\       ['eee', 5, &columns - 3,    5, &columns - 3, 3, 1],
2004	\
2005	\       ['aaaa', 5, &columns,        5, &columns, 1, 1],
2006	\       ['bbbb', 5, &columns + 1,    5, &columns, 1, 1],
2007	\       ['cccc', 5, &columns - 1,    5, &columns - 1, 2, 1],
2008	\       ['dddd', 5, &columns - 2,    5, &columns - 2, 3, 1],
2009	\       ['eeee', 5, &columns - 3,    5, &columns - 3, 4, 1],
2010	\   ],
2011	\ },
2012	\ ]
2013
2014  for test_group in tests
2015    for test in test_group.tests
2016      let [ text, line, col, e_line, e_col, e_width, e_height ] = test
2017      let options = #{
2018	    \ line: line,
2019	    \ col: col,
2020	    \ }
2021      call extend(options, test_group.options)
2022
2023      let p = popup_create(text, options)
2024
2025      let msg = string(extend(options, #{text: text}))
2026      call s:VerifyPosition(p, msg, e_line, e_col, e_width, e_height)
2027      call popup_close(p)
2028    endfor
2029  endfor
2030
2031  call popup_clear()
2032  %bwipe!
2033endfunc
2034
2035func Test_adjust_left_past_screen_width()
2036  " width of screen
2037  let X = join(map(range(&columns), {->'X'}), '')
2038
2039  let p = popup_create(X, #{line: 1, col: 1, wrap: 0})
2040  call s:VerifyPosition(p, 'full width topleft', 1, 1, &columns, 1)
2041
2042  redraw
2043  let line = join(map(range(1, &columns + 1), 'screenstring(1, v:val)'), '')
2044  call assert_equal(X, line)
2045
2046  call popup_close(p)
2047  redraw
2048
2049  " Same if placed on the right hand side
2050  let p = popup_create(X, #{line: 1, col: &columns, wrap: 0})
2051  call s:VerifyPosition(p, 'full width topright', 1, 1, &columns, 1)
2052
2053  redraw
2054  let line = join(map(range(1, &columns + 1), 'screenstring(1, v:val)'), '')
2055  call assert_equal(X, line)
2056
2057  call popup_close(p)
2058  redraw
2059
2060  " Extend so > window width
2061  let X .= 'x'
2062
2063  let p = popup_create(X, #{line: 1, col: 1, wrap: 0})
2064  call s:VerifyPosition(p, 'full width +  1 topleft', 1, 1, &columns, 1)
2065
2066  redraw
2067  let line = join(map(range(1, &columns + 1), 'screenstring(1, v:val)'), '')
2068  call assert_equal(X[ : -2 ], line)
2069
2070  call popup_close(p)
2071  redraw
2072
2073  " Shifted then truncated (the x is not visible)
2074  let p = popup_create(X, #{line: 1, col: &columns - 3, wrap: 0})
2075  call s:VerifyPosition(p, 'full width + 1 topright', 1, 1, &columns, 1)
2076
2077  redraw
2078  let line = join(map(range(1, &columns + 1), 'screenstring(1, v:val)'), '')
2079  call assert_equal(X[ : -2 ], line)
2080
2081  call popup_close(p)
2082  redraw
2083
2084  " Not shifted, just truncated
2085  let p = popup_create(X,
2086	\ #{line: 1, col: 2, wrap: 0, fixed: 1})
2087  call s:VerifyPosition(p, 'full width + 1 fixed', 1, 2, &columns - 1, 1)
2088
2089  redraw
2090  let line = join(map(range(1, &columns + 1), 'screenstring(1, v:val)'), '')
2091  let e_line = ' ' . X[ 1 : -2 ]
2092  call assert_equal(e_line, line)
2093
2094  call popup_close(p)
2095  redraw
2096
2097  call popup_clear()
2098  %bwipe!
2099endfunc
2100
2101func Test_popup_moved()
2102  new
2103  call test_override('char_avail', 1)
2104  call setline(1, ['one word to move around', 'a WORD.and->some thing'])
2105
2106  exe "normal gg0/word\<CR>"
2107  let winid = popup_atcursor('text', #{moved: 'any'})
2108  redraw
2109  call assert_equal(1, popup_getpos(winid).visible)
2110  call assert_equal([1, 4, 4], popup_getoptions(winid).moved)
2111  " trigger the check for last_cursormoved by going into insert mode
2112  call feedkeys("li\<Esc>", 'xt')
2113  call assert_equal({}, popup_getpos(winid))
2114  call popup_clear()
2115
2116  exe "normal gg0/word\<CR>"
2117  let winid = popup_atcursor('text', #{moved: 'word'})
2118  redraw
2119  call assert_equal(1, popup_getpos(winid).visible)
2120  call assert_equal([1, 4, 7], popup_getoptions(winid).moved)
2121  call feedkeys("hi\<Esc>", 'xt')
2122  call assert_equal({}, popup_getpos(winid))
2123  call popup_clear()
2124
2125  exe "normal gg0/word\<CR>"
2126  let winid = popup_atcursor('text', #{moved: 'word'})
2127  redraw
2128  call assert_equal(1, popup_getpos(winid).visible)
2129  call assert_equal([1, 4, 7], popup_getoptions(winid).moved)
2130  call feedkeys("li\<Esc>", 'xt')
2131  call assert_equal(1, popup_getpos(winid).visible)
2132  call feedkeys("ei\<Esc>", 'xt')
2133  call assert_equal(1, popup_getpos(winid).visible)
2134  call feedkeys("eli\<Esc>", 'xt')
2135  call assert_equal({}, popup_getpos(winid))
2136  call popup_clear()
2137
2138  " WORD is the default
2139  exe "normal gg0/WORD\<CR>"
2140  let winid = popup_atcursor('text', {})
2141  redraw
2142  call assert_equal(1, popup_getpos(winid).visible)
2143  call assert_equal([2, 2, 15], popup_getoptions(winid).moved)
2144  call feedkeys("eli\<Esc>", 'xt')
2145  call assert_equal(1, popup_getpos(winid).visible)
2146  call feedkeys("wi\<Esc>", 'xt')
2147  call assert_equal(1, popup_getpos(winid).visible)
2148  call feedkeys("Eli\<Esc>", 'xt')
2149  call assert_equal({}, popup_getpos(winid))
2150  call popup_clear()
2151
2152  exe "normal gg0/word\<CR>"
2153  let winid = popup_atcursor('text', #{moved: [5, 10]})
2154  redraw
2155  call assert_equal(1, popup_getpos(winid).visible)
2156  call feedkeys("eli\<Esc>", 'xt')
2157  call feedkeys("ei\<Esc>", 'xt')
2158  call assert_equal(1, popup_getpos(winid).visible)
2159  call feedkeys("eli\<Esc>", 'xt')
2160  call assert_equal({}, popup_getpos(winid))
2161  call popup_clear()
2162
2163  bwipe!
2164  call test_override('ALL', 0)
2165endfunc
2166
2167func Test_notifications()
2168  CheckFeature timers
2169  CheckScreendump
2170
2171  let lines =<< trim END
2172	call setline(1, range(1, 20))
2173	hi Notification ctermbg=lightblue
2174	call popup_notification('first notification', {})
2175  END
2176  call writefile(lines, 'XtestNotifications')
2177  let buf = RunVimInTerminal('-S XtestNotifications', #{rows: 10})
2178  call VerifyScreenDump(buf, 'Test_popupwin_notify_01', {})
2179
2180  " second one goes below the first one
2181  call term_sendkeys(buf, ":hi link PopupNotification Notification\<CR>")
2182  call term_sendkeys(buf, ":call popup_notification('another important notification', {})\<CR>")
2183  call VerifyScreenDump(buf, 'Test_popupwin_notify_02', {})
2184
2185  " clean up
2186  call StopVimInTerminal(buf)
2187  call delete('XtestNotifications')
2188endfunc
2189
2190func Test_popup_scrollbar()
2191  CheckScreendump
2192
2193  let lines =<< trim END
2194    call setline(1, range(1, 20))
2195    hi ScrollThumb ctermbg=blue
2196    hi ScrollBar ctermbg=red
2197    let winid = popup_create(['one', 'two', 'three', 'four', 'five',
2198	  \ 'six', 'seven', 'eight', 'nine'], #{
2199	  \ minwidth: 8,
2200	  \ maxheight: 4,
2201	  \ })
2202    func ScrollUp()
2203      call feedkeys("\<F3>\<ScrollWheelUp>", "xt")
2204    endfunc
2205    func ScrollDown()
2206      call feedkeys("\<F3>\<ScrollWheelDown>", "xt")
2207    endfunc
2208    func ClickTop()
2209      call feedkeys("\<F4>\<LeftMouse>", "xt")
2210    endfunc
2211    func ClickBot()
2212      call popup_setoptions(g:winid, #{border: [], close: 'button'})
2213      call feedkeys("\<F5>\<LeftMouse>", "xt")
2214    endfunc
2215    func Popup_filter(winid, key)
2216      if a:key == 'j'
2217	silent! this_throws_an_error_but_is_ignored
2218	let line = popup_getoptions(a:winid).firstline
2219	let nlines = line('$', a:winid)
2220	let newline = line < nlines ? (line + 1) : nlines
2221	call popup_setoptions(a:winid, #{firstline: newline})
2222	return v:true
2223      elseif a:key == 'x'
2224	call popup_close(a:winid)
2225	return v:true
2226      endif
2227    endfunc
2228
2229    def CreatePopup(text: list<string>)
2230      popup_create(text, {
2231	    \ minwidth: 30,
2232	    \ maxwidth: 30,
2233	    \ minheight: 4,
2234	    \ maxheight: 4,
2235	    \ firstline: 1,
2236	    \ lastline: 4,
2237	    \ wrap: true,
2238	    \ scrollbar: true,
2239	    \ mapping: false,
2240	    \ filter: Popup_filter,
2241	    \ })
2242    enddef
2243
2244    func PopupScroll()
2245      call popup_clear()
2246      let text =<< trim END
2247	  1
2248	  2
2249	  3
2250	  4
2251	  long line long line long line long line long line long line
2252	  long line long line long line long line long line long line
2253	  long line long line long line long line long line long line
2254      END
2255      call CreatePopup(text)
2256    endfunc
2257    map <silent> <F3> :call test_setmouse(5, 36)<CR>
2258    map <silent> <F4> :call test_setmouse(4, 42)<CR>
2259    map <silent> <F5> :call test_setmouse(7, 42)<CR>
2260  END
2261  call writefile(lines, 'XtestPopupScroll')
2262  let buf = RunVimInTerminal('-S XtestPopupScroll', #{rows: 10})
2263  call VerifyScreenDump(buf, 'Test_popupwin_scroll_1', {})
2264
2265  call term_sendkeys(buf, ":call popup_setoptions(winid, #{firstline: 2})\<CR>")
2266  call term_sendkeys(buf, ":\<CR>")
2267  call VerifyScreenDump(buf, 'Test_popupwin_scroll_2', {})
2268
2269  call term_sendkeys(buf, ":call popup_setoptions(winid, #{firstline: 6})\<CR>")
2270  call term_sendkeys(buf, ":\<CR>")
2271  call VerifyScreenDump(buf, 'Test_popupwin_scroll_3', {})
2272
2273  call term_sendkeys(buf, ":call popup_setoptions(winid, #{firstline: 9})\<CR>")
2274  call term_sendkeys(buf, ":\<CR>")
2275  call VerifyScreenDump(buf, 'Test_popupwin_scroll_4', {})
2276
2277  call term_sendkeys(buf, ":call popup_setoptions(winid, #{scrollbarhighlight: 'ScrollBar', thumbhighlight: 'ScrollThumb', firstline: 5})\<CR>")
2278  " this scrolls two lines (half the window height)
2279  call term_sendkeys(buf, ":call ScrollUp()\<CR>")
2280  call VerifyScreenDump(buf, 'Test_popupwin_scroll_5', {})
2281
2282  call term_sendkeys(buf, ":call ScrollDown()\<CR>")
2283  call VerifyScreenDump(buf, 'Test_popupwin_scroll_6', {})
2284
2285  call term_sendkeys(buf, ":call ScrollDown()\<CR>")
2286  " wait a bit, otherwise it fails sometimes (double click recognized?)
2287  sleep 100m
2288  call term_sendkeys(buf, ":call ScrollDown()\<CR>")
2289  call VerifyScreenDump(buf, 'Test_popupwin_scroll_7', {})
2290
2291  call term_sendkeys(buf, ":call ClickTop()\<CR>")
2292  sleep 100m
2293  call term_sendkeys(buf, ":call ClickTop()\<CR>")
2294  call VerifyScreenDump(buf, 'Test_popupwin_scroll_8', {})
2295
2296  call term_sendkeys(buf, ":call ClickBot()\<CR>")
2297  call VerifyScreenDump(buf, 'Test_popupwin_scroll_9', {})
2298
2299  " remove the minwidth and maxheight
2300  call term_sendkeys(buf, ":call popup_setoptions(winid, #{maxheight: 0, minwidth: 0})\<CR>")
2301  call term_sendkeys(buf, ":\<CR>")
2302  call VerifyScreenDump(buf, 'Test_popupwin_scroll_10', {})
2303
2304  " check size with non-wrapping lines
2305  call term_sendkeys(buf, ":call PopupScroll()\<CR>")
2306  call VerifyScreenDump(buf, 'Test_popupwin_scroll_11', {})
2307
2308  " check size with wrapping lines
2309  call term_sendkeys(buf, "j")
2310  call VerifyScreenDump(buf, 'Test_popupwin_scroll_12', {})
2311
2312  " clean up
2313  call term_sendkeys(buf, "x")
2314  call StopVimInTerminal(buf)
2315  call delete('XtestPopupScroll')
2316endfunc
2317
2318func Test_popup_too_high_scrollbar()
2319  CheckScreendump
2320
2321  let lines =<< trim END
2322    call setline(1, range(1, 20)->map({i, v -> repeat(v, 10)}))
2323    set scrolloff=0
2324    func ShowPopup()
2325      let winid = popup_atcursor(['one', 'two', 'three', 'four', 'five',
2326	    \ 'six', 'seven', 'eight', 'nine', 'ten', 'eleven', 'twelve'], #{
2327	    \ minwidth: 8,
2328	    \ border: [],
2329	    \ })
2330    endfunc
2331    normal 3G$
2332    call ShowPopup()
2333  END
2334  call writefile(lines, 'XtestPopupToohigh')
2335  let buf = RunVimInTerminal('-S XtestPopupToohigh', #{rows: 10})
2336  call VerifyScreenDump(buf, 'Test_popupwin_toohigh_1', {})
2337
2338  call term_sendkeys(buf, ":call popup_clear()\<CR>")
2339  call term_sendkeys(buf, "8G$")
2340  call term_sendkeys(buf, ":call ShowPopup()\<CR>")
2341  call VerifyScreenDump(buf, 'Test_popupwin_toohigh_2', {})
2342
2343  call term_sendkeys(buf, ":call popup_clear()\<CR>")
2344  call term_sendkeys(buf, "gg$")
2345  call term_sendkeys(buf, ":call ShowPopup()\<CR>")
2346  call VerifyScreenDump(buf, 'Test_popupwin_toohigh_3', {})
2347
2348  " clean up
2349  call StopVimInTerminal(buf)
2350  call delete('XtestPopupToohigh')
2351endfunc
2352
2353func Test_popup_fitting_scrollbar()
2354  " this was causing a crash, divide by zero
2355  let winid = popup_create([
2356	\ 'one', 'two', 'longer line that wraps', 'four', 'five'], #{
2357	\ scrollbar: 1,
2358	\ maxwidth: 10,
2359	\ maxheight: 5,
2360	\ firstline: 2})
2361  redraw
2362  call popup_clear()
2363endfunc
2364
2365func Test_popup_settext()
2366  CheckScreendump
2367
2368  let lines =<< trim END
2369    let opts = #{wrap: 0}
2370    let p = popup_create('test', opts)
2371    eval p->popup_settext('this is a text')
2372  END
2373
2374  call writefile(lines, 'XtestPopupSetText')
2375  let buf = RunVimInTerminal('-S XtestPopupSetText', #{rows: 10})
2376  call VerifyScreenDump(buf, 'Test_popup_settext_01', {})
2377
2378  " Setting to empty string clears it
2379  call term_sendkeys(buf, ":call popup_settext(p, '')\<CR>")
2380  call VerifyScreenDump(buf, 'Test_popup_settext_02', {})
2381
2382  " Setting a list
2383  call term_sendkeys(buf, ":call popup_settext(p, ['a','b','c'])\<CR>")
2384  call VerifyScreenDump(buf, 'Test_popup_settext_03', {})
2385
2386  " Shrinking with a list
2387  call term_sendkeys(buf, ":call popup_settext(p, ['a'])\<CR>")
2388  call VerifyScreenDump(buf, 'Test_popup_settext_04', {})
2389
2390  " Growing with a list
2391  call term_sendkeys(buf, ":call popup_settext(p, ['a','b','c'])\<CR>")
2392  call VerifyScreenDump(buf, 'Test_popup_settext_03', {})
2393
2394  " Empty list clears
2395  call term_sendkeys(buf, ":call popup_settext(p, [])\<CR>")
2396  call VerifyScreenDump(buf, 'Test_popup_settext_05', {})
2397
2398  " Dicts
2399  call term_sendkeys(buf, ":call popup_settext(p, [#{text: 'aaaa'}, #{text: 'bbbb'}, #{text: 'cccc'}])\<CR>")
2400  call VerifyScreenDump(buf, 'Test_popup_settext_06', {})
2401
2402  " range() (doesn't work)
2403  call term_sendkeys(buf, ":call popup_settext(p, range(4, 8))\<CR>")
2404  call VerifyScreenDump(buf, 'Test_popup_settext_07', {})
2405
2406  " clean up
2407  call StopVimInTerminal(buf)
2408  call delete('XtestPopupSetText')
2409endfunc
2410
2411func Test_popup_settext_getline()
2412  let id = popup_create('', #{ tabpage: 0 })
2413  call popup_settext(id, ['a','b'])
2414  call assert_equal(2, line('$', id)) " OK :)
2415  call popup_close(id)
2416
2417  let id = popup_create('', #{ tabpage: -1 })
2418  call popup_settext(id, ['a','b'])
2419  call assert_equal(2, line('$', id)) " Fails :(
2420  call popup_close(id)
2421endfunc
2422
2423func Test_popup_settext_null()
2424  let id = popup_create('', #{ tabpage: 0 })
2425  call popup_settext(id, test_null_list())
2426  call popup_close(id)
2427
2428  let id = popup_create('', #{ tabpage: 0 })
2429  call popup_settext(id, test_null_string())
2430  call popup_close(id)
2431endfunc
2432
2433func Test_popup_hidden()
2434  new
2435
2436  let winid = popup_atcursor('text', #{hidden: 1})
2437  redraw
2438  call assert_equal(0, popup_getpos(winid).visible)
2439  call popup_close(winid)
2440
2441  let winid = popup_create('text', #{hidden: 1})
2442  redraw
2443  call assert_equal(0, popup_getpos(winid).visible)
2444  call popup_close(winid)
2445
2446  func QuitCallback(id, res)
2447    let s:cb_winid = a:id
2448    let s:cb_res = a:res
2449  endfunc
2450  let winid = 'make a choice'->popup_dialog(#{hidden: 1,
2451	  \ filter: 'popup_filter_yesno',
2452	  \ callback: 'QuitCallback',
2453	  \ })
2454  redraw
2455  call assert_equal(0, popup_getpos(winid).visible)
2456  call assert_equal(function('popup_filter_yesno'), popup_getoptions(winid).filter)
2457  call assert_equal(function('QuitCallback'), popup_getoptions(winid).callback)
2458  exe "normal anot used by filter\<Esc>"
2459  call assert_equal('not used by filter', getline(1))
2460
2461  call popup_show(winid)
2462  call feedkeys('y', "xt")
2463  call assert_equal(1, s:cb_res)
2464
2465  bwipe!
2466  delfunc QuitCallback
2467endfunc
2468
2469" Test options not checked elsewhere
2470func Test_set_get_options()
2471  let winid = popup_create('some text', #{highlight: 'Beautiful'})
2472  let options = popup_getoptions(winid)
2473  call assert_equal(1, options.wrap)
2474  call assert_equal(0, options.drag)
2475  call assert_equal('Beautiful', options.highlight)
2476
2477  call popup_setoptions(winid, #{wrap: 0, drag: 1, highlight: 'Another'})
2478  let options = popup_getoptions(winid)
2479  call assert_equal(0, options.wrap)
2480  call assert_equal(1, options.drag)
2481  call assert_equal('Another', options.highlight)
2482
2483  call assert_fails('call popup_setoptions(winid, [])', 'E715:')
2484  call assert_fails('call popup_setoptions(winid, test_null_dict())', 'E715:')
2485
2486  call popup_close(winid)
2487  call assert_equal(0, popup_setoptions(winid, options.wrap))
2488endfunc
2489
2490func Test_popupwin_garbage_collect()
2491  func MyPopupFilter(x, winid, c)
2492    " NOP
2493  endfunc
2494
2495  let winid = popup_create('something', #{filter: function('MyPopupFilter', [{}])})
2496  call test_garbagecollect_now()
2497  redraw
2498  " Must not crash caused by invalid memory access
2499  call feedkeys('j', 'xt')
2500  call assert_true(v:true)
2501
2502  call popup_close(winid)
2503  delfunc MyPopupFilter
2504endfunc
2505
2506func Test_popupwin_filter_mode()
2507  func MyPopupFilter(winid, c)
2508    let s:typed = a:c
2509    if a:c == ':' || a:c == "\r" || a:c == 'v'
2510      " can start cmdline mode, get out, and start/stop Visual mode
2511      return 0
2512    endif
2513    return 1
2514  endfunc
2515
2516  " Normal, Visual and Insert mode
2517  let winid = popup_create('something', #{filter: 'MyPopupFilter', filtermode: 'nvi'})
2518  redraw
2519  call feedkeys('x', 'xt')
2520  call assert_equal('x', s:typed)
2521
2522  call feedkeys(":let g:foo = 'foo'\<CR>", 'xt')
2523  call assert_equal(':', s:typed)
2524  call assert_equal('foo', g:foo)
2525
2526  let @x = 'something'
2527  call feedkeys('v$"xy', 'xt')
2528  call assert_equal('y', s:typed)
2529  call assert_equal('something', @x)  " yank command is filtered out
2530  call feedkeys('v', 'xt')  " end Visual mode
2531
2532  call popup_close(winid)
2533
2534  " only Normal mode
2535  let winid = popup_create('something', #{filter: 'MyPopupFilter', filtermode: 'n'})
2536  redraw
2537  call feedkeys('x', 'xt')
2538  call assert_equal('x', s:typed)
2539
2540  call feedkeys(":let g:foo = 'foo'\<CR>", 'xt')
2541  call assert_equal(':', s:typed)
2542  call assert_equal('foo', g:foo)
2543
2544  let @x = 'something'
2545  call feedkeys('v$"xy', 'xt')
2546  call assert_equal('v', s:typed)
2547  call assert_notequal('something', @x)
2548
2549  call popup_close(winid)
2550
2551  " default: all modes
2552  let winid = popup_create('something', #{filter: 'MyPopupFilter'})
2553  redraw
2554  call feedkeys('x', 'xt')
2555  call assert_equal('x', s:typed)
2556
2557  let g:foo = 'bar'
2558  call feedkeys(":let g:foo = 'foo'\<CR>", 'xt')
2559  call assert_equal("\r", s:typed)
2560  call assert_equal('bar', g:foo)
2561
2562  let @x = 'something'
2563  call feedkeys('v$"xy', 'xt')
2564  call assert_equal('y', s:typed)
2565  call assert_equal('something', @x)  " yank command is filtered out
2566  call feedkeys('v', 'xt')  " end Visual mode
2567
2568  call popup_close(winid)
2569  delfunc MyPopupFilter
2570endfunc
2571
2572func Test_popupwin_filter_mouse()
2573  func MyPopupFilter(winid, c)
2574    let g:got_mousepos = getmousepos()
2575    return 0
2576  endfunc
2577
2578  call setline(1, ['.'->repeat(25)]->repeat(10))
2579  let winid = popup_create(['short', 'long line that will wrap', 'other'], #{
2580	\ line: 2,
2581	\ col: 4,
2582	\ maxwidth: 12,
2583	\ padding: [],
2584	\ border: [],
2585	\ filter: 'MyPopupFilter',
2586	\ })
2587  redraw
2588  "    123456789012345678901
2589  "  1 .....................
2590  "  2 ...+--------------+..
2591  "  3 ...|              |..
2592  "  4 ...| short        |..
2593  "  5 ...| long line th |..
2594  "  6 ...| at will wrap |..
2595  "  7 ...| other        |..
2596  "  8 ...|              |..
2597  "  9 ...+--------------+..
2598  " 10 .....................
2599  let tests = []
2600
2601  func AddItemOutsidePopup(tests, row, col)
2602    eval a:tests->add(#{clickrow: a:row, clickcol: a:col, result: #{
2603	  \ screenrow: a:row, screencol: a:col,
2604	  \ winid: win_getid(), winrow: a:row, wincol: a:col,
2605	  \ line: a:row, column: a:col,
2606	  \ }})
2607  endfunc
2608  func AddItemInPopupBorder(tests, winid, row, col)
2609    eval a:tests->add(#{clickrow: a:row, clickcol: a:col, result: #{
2610	  \ screenrow: a:row, screencol: a:col,
2611	  \ winid: a:winid, winrow: a:row - 1, wincol: a:col - 3,
2612	  \ line: 0, column: 0,
2613	  \ }})
2614  endfunc
2615  func AddItemInPopupText(tests, winid, row, col, textline, textcol)
2616    eval a:tests->add(#{clickrow: a:row, clickcol: a:col, result: #{
2617	  \ screenrow: a:row, screencol: a:col,
2618	  \ winid: a:winid, winrow: a:row - 1, wincol: a:col - 3,
2619	  \ line: a:textline, column: a:textcol,
2620	  \ }})
2621  endfunc
2622
2623  " above and below popup
2624  for c in range(1, 21)
2625    call AddItemOutsidePopup(tests, 1, c)
2626    call AddItemOutsidePopup(tests, 10, c)
2627  endfor
2628  " left and right of popup
2629  for r in range(1, 10)
2630    call AddItemOutsidePopup(tests, r, 3)
2631    call AddItemOutsidePopup(tests, r, 20)
2632  endfor
2633  " top and bottom in popup
2634  for c in range(4, 19)
2635    call AddItemInPopupBorder(tests, winid, 2, c)
2636    call AddItemInPopupBorder(tests, winid, 3, c)
2637    call AddItemInPopupBorder(tests, winid, 8, c)
2638    call AddItemInPopupBorder(tests, winid, 9, c)
2639  endfor
2640  " left and right margin in popup
2641  for r in range(2, 9)
2642    call AddItemInPopupBorder(tests, winid, r, 4)
2643    call AddItemInPopupBorder(tests, winid, r, 5)
2644    call AddItemInPopupBorder(tests, winid, r, 18)
2645    call AddItemInPopupBorder(tests, winid, r, 19)
2646  endfor
2647  " text "short"
2648  call AddItemInPopupText(tests, winid, 4, 6, 1, 1)
2649  call AddItemInPopupText(tests, winid, 4, 10, 1, 5)
2650  call AddItemInPopupText(tests, winid, 4, 11, 1, 6)
2651  call AddItemInPopupText(tests, winid, 4, 17, 1, 6)
2652  " text "long line th"
2653  call AddItemInPopupText(tests, winid, 5, 6, 2, 1)
2654  call AddItemInPopupText(tests, winid, 5, 10, 2, 5)
2655  call AddItemInPopupText(tests, winid, 5, 17, 2, 12)
2656  " text "at will wrap"
2657  call AddItemInPopupText(tests, winid, 6, 6, 2, 13)
2658  call AddItemInPopupText(tests, winid, 6, 10, 2, 17)
2659  call AddItemInPopupText(tests, winid, 6, 17, 2, 24)
2660  " text "other"
2661  call AddItemInPopupText(tests, winid, 7, 6, 3, 1)
2662  call AddItemInPopupText(tests, winid, 7, 10, 3, 5)
2663  call AddItemInPopupText(tests, winid, 7, 11, 3, 6)
2664  call AddItemInPopupText(tests, winid, 7, 17, 3, 6)
2665
2666  for item in tests
2667    call test_setmouse(item.clickrow, item.clickcol)
2668    call feedkeys("\<LeftMouse>", 'xt')
2669    call assert_equal(item.result, g:got_mousepos)
2670  endfor
2671
2672  call popup_close(winid)
2673  enew!
2674  delfunc MyPopupFilter
2675endfunc
2676
2677func Test_popupwin_with_buffer()
2678  call writefile(['some text', 'in a buffer'], 'XsomeFile')
2679  let buf = bufadd('XsomeFile')
2680  call assert_equal(0, bufloaded(buf))
2681
2682  setlocal number
2683  call setbufvar(buf, "&wrapmargin", 13)
2684
2685  let winid = popup_create(buf, {})
2686  call assert_notequal(0, winid)
2687  let pos = popup_getpos(winid)
2688  call assert_equal(2, pos.height)
2689  call assert_equal(1, bufloaded(buf))
2690
2691  " window-local option is set to default, buffer-local is not
2692  call assert_equal(0, getwinvar(winid, '&number'))
2693  call assert_equal(13, getbufvar(buf, '&wrapmargin'))
2694
2695  call popup_close(winid)
2696  call assert_equal({}, popup_getpos(winid))
2697  call assert_equal(1, bufloaded(buf))
2698  exe 'bwipe! ' .. buf
2699  setlocal nonumber
2700
2701  edit test_popupwin.vim
2702  let winid = popup_create(bufnr(''), {})
2703  redraw
2704  call popup_close(winid)
2705  call delete('XsomeFile')
2706endfunc
2707
2708func Test_popupwin_terminal_buffer()
2709  CheckFeature terminal
2710  CheckUnix
2711  " Starting a terminal to run a shell in is considered flaky.
2712  let g:test_is_flaky = 1
2713
2714  let origwin = win_getid()
2715
2716  " open help window to test that :help below fails
2717  help
2718
2719  let termbuf = term_start(&shell, #{hidden: 1})
2720  let winid = popup_create(termbuf, #{minwidth: 40, minheight: 10, border: []})
2721  " Wait for shell to start
2722  call WaitForAssert({-> assert_equal("run", job_status(term_getjob(termbuf)))})
2723  " Wait for a prompt (see border char first, then space after prompt)
2724  call WaitForAssert({ -> assert_equal(' ', screenstring(screenrow(), screencol() - 1))})
2725
2726  " When typing a character, the cursor is after it.
2727  call feedkeys("x", 'xt')
2728  call term_wait(termbuf)
2729  redraw
2730  call WaitForAssert({ -> assert_equal('x', screenstring(screenrow(), screencol() - 1))})
2731  call feedkeys("\<BS>", 'xt')
2732
2733  " Check this doesn't crash
2734  call assert_equal(winnr(), winnr('j'))
2735  call assert_equal(winnr(), winnr('k'))
2736  call assert_equal(winnr(), winnr('h'))
2737  call assert_equal(winnr(), winnr('l'))
2738
2739  " Cannot quit while job is running
2740  call assert_fails('call feedkeys("\<C-W>:quit\<CR>", "xt")', 'E948:')
2741
2742  " Cannot enter Terminal-Normal mode. (TODO: but it works...)
2743  call feedkeys("xxx\<C-W>N", 'xt')
2744  call assert_fails('call feedkeys("gf", "xt")', 'E863:')
2745  call feedkeys("a\<C-U>", 'xt')
2746
2747  " Cannot escape from terminal window
2748  call assert_fails('tab drop xxx', 'E863:')
2749  call assert_fails('help', 'E994:')
2750
2751  " Cannot open a second one.
2752  let termbuf2 = term_start(&shell, #{hidden: 1})
2753  call assert_fails('call popup_create(termbuf2, #{})', 'E861:')
2754  call term_sendkeys(termbuf2, "exit\<CR>")
2755
2756  " Exiting shell closes popup window
2757  call feedkeys("exit\<CR>", 'xt')
2758  " Wait for shell to exit
2759  call WaitForAssert({-> assert_equal("dead", job_status(term_getjob(termbuf)))})
2760
2761  helpclose
2762  call feedkeys(":quit\<CR>", 'xt')
2763  call assert_equal(origwin, win_getid())
2764endfunc
2765
2766func Test_popupwin_close_prevwin()
2767  CheckFeature terminal
2768  call Popupwin_close_prevwin()
2769endfunc
2770
2771def Popupwin_close_prevwin()
2772  assert_equal(1, winnr('$'))
2773  split
2774  wincmd b
2775  assert_equal(2, winnr())
2776  var buf = term_start(&shell, {hidden: 1})
2777  popup_create(buf, {})
2778  TermWait(buf, 100)
2779  popup_clear(true)
2780  assert_equal(2, winnr())
2781
2782  quit
2783  exe 'bwipe! ' .. buf
2784enddef
2785
2786func Test_popupwin_with_buffer_and_filter()
2787  new Xwithfilter
2788  call setline(1, range(100))
2789  let bufnr = bufnr()
2790  hide
2791
2792  func BufferFilter(win, key)
2793    if a:key == 'G'
2794      " recursive use of "G" does not cause problems.
2795      call win_execute(a:win, 'normal! G')
2796      return 1
2797    endif
2798    return 0
2799  endfunc
2800
2801  let winid = popup_create(bufnr, #{maxheight: 5, filter: 'BufferFilter'})
2802  call assert_equal(1, popup_getpos(winid).firstline)
2803  redraw
2804  call feedkeys("G", 'xt')
2805  call assert_equal(99, popup_getpos(winid).firstline)
2806
2807  call popup_close(winid)
2808  exe 'bwipe! ' .. bufnr
2809endfunc
2810
2811func Test_popupwin_width()
2812  let winid = popup_create(repeat(['short', 'long long long line', 'medium width'], 50), #{
2813	\ maxwidth: 40,
2814	\ maxheight: 10,
2815	\ })
2816  for top in range(1, 20)
2817    eval winid->popup_setoptions(#{firstline: top})
2818    redraw
2819    call assert_equal(19, popup_getpos(winid).width)
2820  endfor
2821  call popup_clear()
2822endfunc
2823
2824func Test_popupwin_buf_close()
2825  let buf = bufadd('Xtestbuf')
2826  call bufload(buf)
2827  call setbufline(buf, 1, ['just', 'some', 'lines'])
2828  let winid = popup_create(buf, {})
2829  redraw
2830  call assert_equal(3, popup_getpos(winid).height)
2831  let bufinfo = getbufinfo(buf)[0]
2832  call assert_equal(1, bufinfo.changed)
2833  call assert_equal(0, bufinfo.hidden)
2834  call assert_equal(0, bufinfo.listed)
2835  call assert_equal(1, bufinfo.loaded)
2836  call assert_equal([], bufinfo.windows)
2837  call assert_equal([winid], bufinfo.popups)
2838
2839  call popup_close(winid)
2840  call assert_equal({}, popup_getpos(winid))
2841  let bufinfo = getbufinfo(buf)[0]
2842  call assert_equal(1, bufinfo.changed)
2843  call assert_equal(1, bufinfo.hidden)
2844  call assert_equal(0, bufinfo.listed)
2845  call assert_equal(1, bufinfo.loaded)
2846  call assert_equal([], bufinfo.windows)
2847  call assert_equal([], bufinfo.popups)
2848  exe 'bwipe! ' .. buf
2849endfunc
2850
2851func Test_popup_menu_with_maxwidth()
2852  CheckScreendump
2853
2854  let lines =<< trim END
2855	call setline(1, range(1, 10))
2856	hi ScrollThumb ctermbg=blue
2857	hi ScrollBar ctermbg=red
2858	func PopupMenu(lines, line, col, scrollbar = 0)
2859		return popup_menu(a:lines, #{
2860			\ maxwidth: 10,
2861			\ maxheight: 3,
2862			\ pos : 'topleft',
2863			\ col : a:col,
2864			\ line : a:line,
2865			\ scrollbar : a:scrollbar,
2866			\ })
2867	endfunc
2868	call PopupMenu(['x'], 1, 1)
2869	call PopupMenu(['123456789|'], 1, 16)
2870	call PopupMenu(['123456789|' .. ' '], 7, 1)
2871	call PopupMenu([repeat('123456789|', 100)], 7, 16)
2872	call PopupMenu(repeat(['123456789|' .. ' '], 5), 1, 33, 1)
2873  END
2874  call writefile(lines, 'XtestPopupMenuMaxWidth')
2875  let buf = RunVimInTerminal('-S XtestPopupMenuMaxWidth', #{rows: 13})
2876  call VerifyScreenDump(buf, 'Test_popupwin_menu_maxwidth_1', {})
2877
2878  " close the menu popupwin.
2879  call term_sendkeys(buf, " ")
2880  call term_sendkeys(buf, " ")
2881  call term_sendkeys(buf, " ")
2882  call term_sendkeys(buf, " ")
2883  call term_sendkeys(buf, " ")
2884
2885  " clean up
2886  call StopVimInTerminal(buf)
2887  call delete('XtestPopupMenuMaxWidth')
2888endfunc
2889
2890func Test_popup_menu_with_scrollbar()
2891  CheckScreendump
2892
2893  let lines =<< trim END
2894    call setline(1, range(1, 20))
2895    hi ScrollThumb ctermbg=blue
2896    hi ScrollBar ctermbg=red
2897    eval ['one', 'two', 'three', 'four', 'five',
2898	  \ 'six', 'seven', 'eight', 'nine']
2899	  \ ->popup_menu(#{
2900	  \ minwidth: 8,
2901	  \ maxheight: 3,
2902	  \ })
2903  END
2904  call writefile(lines, 'XtestPopupMenuScroll')
2905  let buf = RunVimInTerminal('-S XtestPopupMenuScroll', #{rows: 10})
2906
2907  call term_sendkeys(buf, "j")
2908  call VerifyScreenDump(buf, 'Test_popupwin_menu_scroll_1', {})
2909
2910  call term_sendkeys(buf, "jjj")
2911  call VerifyScreenDump(buf, 'Test_popupwin_menu_scroll_2', {})
2912
2913  " if the cursor is the bottom line, it stays at the bottom line.
2914  call term_sendkeys(buf, repeat("j", 20))
2915  call VerifyScreenDump(buf, 'Test_popupwin_menu_scroll_3', {})
2916
2917  call term_sendkeys(buf, "kk")
2918  call VerifyScreenDump(buf, 'Test_popupwin_menu_scroll_4', {})
2919
2920  call term_sendkeys(buf, "k")
2921  call VerifyScreenDump(buf, 'Test_popupwin_menu_scroll_5', {})
2922
2923  " if the cursor is in the top line, it stays in the top line.
2924  call term_sendkeys(buf, repeat("k", 20))
2925  call VerifyScreenDump(buf, 'Test_popupwin_menu_scroll_6', {})
2926
2927  " close the menu popupwin.
2928  call term_sendkeys(buf, " ")
2929
2930  " clean up
2931  call StopVimInTerminal(buf)
2932  call delete('XtestPopupMenuScroll')
2933endfunc
2934
2935func Test_popup_menu_filter()
2936  CheckScreendump
2937
2938  let lines =<< trim END
2939	function! MyFilter(winid, key) abort
2940	  if a:key == "0"
2941		call win_execute(a:winid, "call setpos('.', [0, 1, 1, 0])")
2942		return 1
2943	  endif
2944	  if a:key == "G"
2945		call win_execute(a:winid, "call setpos('.', [0, line('$'), 1, 0])")
2946		return 1
2947	  endif
2948	  if a:key == "j"
2949		call win_execute(a:winid, "call setpos('.', [0, line('.') + 1, 1, 0])")
2950		return 1
2951	  endif
2952	  if a:key == "k"
2953		call win_execute(a:winid, "call setpos('.', [0, line('.') - 1, 1, 0])")
2954		return 1
2955	  endif
2956	  if a:key == ':'
2957		call popup_close(a:winid)
2958		return 0
2959	  endif
2960	  return 0
2961	endfunction
2962	call popup_menu(['111', '222', '333', '444', '555', '666', '777', '888', '999'], #{
2963	  \ maxheight : 3,
2964	  \ filter : 'MyFilter'
2965	  \ })
2966  END
2967  call writefile(lines, 'XtestPopupMenuFilter')
2968  let buf = RunVimInTerminal('-S XtestPopupMenuFilter', #{rows: 10})
2969
2970  call term_sendkeys(buf, "j")
2971  call VerifyScreenDump(buf, 'Test_popupwin_menu_filter_1', {})
2972
2973  call term_sendkeys(buf, "k")
2974  call VerifyScreenDump(buf, 'Test_popupwin_menu_filter_2', {})
2975
2976  call term_sendkeys(buf, "G")
2977  call VerifyScreenDump(buf, 'Test_popupwin_menu_filter_3', {})
2978
2979  call term_sendkeys(buf, "0")
2980  call VerifyScreenDump(buf, 'Test_popupwin_menu_filter_4', {})
2981
2982  " check that when the popup is closed in the filter the screen is redrawn
2983  call term_sendkeys(buf, ":")
2984  call VerifyScreenDump(buf, 'Test_popupwin_menu_filter_5', {})
2985  call term_sendkeys(buf, "\<CR>")
2986
2987  " clean up
2988  call StopVimInTerminal(buf)
2989  call delete('XtestPopupMenuFilter')
2990endfunc
2991
2992func Test_popup_cursorline()
2993  CheckScreendump
2994
2995  let winid = popup_create('some text', {})
2996  call assert_equal(0, popup_getoptions(winid).cursorline)
2997  call popup_close(winid)
2998
2999  let winid = popup_create('some text', #{ cursorline: 1, })
3000  call assert_equal(1, popup_getoptions(winid).cursorline)
3001  call popup_close(winid)
3002
3003  let winid = popup_create('some text', #{ cursorline: v:true, })
3004  call assert_equal(1, popup_getoptions(winid).cursorline)
3005  call popup_close(winid)
3006
3007  let winid = popup_create('some text', #{ cursorline: 0, })
3008  call assert_equal(0, popup_getoptions(winid).cursorline)
3009  call popup_close(winid)
3010
3011  let winid = popup_menu('some text', {})
3012  call assert_equal(1, popup_getoptions(winid).cursorline)
3013  call popup_close(winid)
3014
3015  let winid = popup_menu('some text', #{ cursorline: 1, })
3016  call assert_equal(1, popup_getoptions(winid).cursorline)
3017  call popup_close(winid)
3018
3019  let winid = popup_menu('some text', #{ cursorline: 0, })
3020  call assert_equal(0, popup_getoptions(winid).cursorline)
3021  call popup_close(winid)
3022
3023  " ---------
3024  " Pattern 1
3025  " ---------
3026  let lines =<< trim END
3027	call popup_create(['111', '222', '333'], #{ cursorline : 0 })
3028  END
3029  call writefile(lines, 'XtestPopupCursorLine')
3030  let buf = RunVimInTerminal('-S XtestPopupCursorLine', #{rows: 10})
3031  call VerifyScreenDump(buf, 'Test_popupwin_cursorline_1', {})
3032  call term_sendkeys(buf, ":call popup_clear()\<cr>")
3033  call StopVimInTerminal(buf)
3034
3035  " ---------
3036  " Pattern 2
3037  " ---------
3038  let lines =<< trim END
3039	call popup_create(['111', '222', '333'], #{ cursorline : 1 })
3040  END
3041  call writefile(lines, 'XtestPopupCursorLine')
3042  let buf = RunVimInTerminal('-S XtestPopupCursorLine', #{rows: 10})
3043  call VerifyScreenDump(buf, 'Test_popupwin_cursorline_2', {})
3044  call term_sendkeys(buf, ":call popup_clear()\<cr>")
3045  call StopVimInTerminal(buf)
3046
3047  " ---------
3048  " Pattern 3
3049  " ---------
3050  let lines =<< trim END
3051	function! MyFilter(winid, key) abort
3052	  if a:key == "j"
3053		call win_execute(a:winid, "call setpos('.', [0, line('.') + 1, 1, 0]) | redraw")
3054		return 1
3055	  endif
3056	  if a:key == 'x'
3057		call popup_close(a:winid)
3058		return 1
3059	  endif
3060	  return 0
3061	endfunction
3062	call popup_menu(['111', '222', '333'], #{
3063	  \ cursorline : 0,
3064	  \ maxheight : 2,
3065	  \ filter : 'MyFilter',
3066	  \ })
3067  END
3068  call writefile(lines, 'XtestPopupCursorLine')
3069  let buf = RunVimInTerminal('-S XtestPopupCursorLine', #{rows: 10})
3070  call VerifyScreenDump(buf, 'Test_popupwin_cursorline_3', {})
3071  call term_sendkeys(buf, "j")
3072  call term_sendkeys(buf, "j")
3073  call VerifyScreenDump(buf, 'Test_popupwin_cursorline_4', {})
3074  call term_sendkeys(buf, "x")
3075  call StopVimInTerminal(buf)
3076
3077  " ---------
3078  " Pattern 4
3079  " ---------
3080  let lines =<< trim END
3081	function! MyFilter(winid, key) abort
3082	  if a:key == "j"
3083		call win_execute(a:winid, "call setpos('.', [0, line('.') + 1, 1, 0]) | redraw")
3084		return 1
3085	  endif
3086	  if a:key == 'x'
3087		call popup_close(a:winid)
3088		return 1
3089	  endif
3090	  return 0
3091	endfunction
3092	call popup_menu(['111', '222', '333'], #{
3093	  \ cursorline : 1,
3094	  \ maxheight : 2,
3095	  \ filter : 'MyFilter',
3096	  \ })
3097  END
3098  call writefile(lines, 'XtestPopupCursorLine')
3099  let buf = RunVimInTerminal('-S XtestPopupCursorLine', #{rows: 10})
3100  call VerifyScreenDump(buf, 'Test_popupwin_cursorline_5', {})
3101  call term_sendkeys(buf, "j")
3102  call term_sendkeys(buf, "j")
3103  call VerifyScreenDump(buf, 'Test_popupwin_cursorline_6', {})
3104  call term_sendkeys(buf, "x")
3105  call StopVimInTerminal(buf)
3106
3107  " ---------
3108  " Cursor in second line when creating the popup
3109  " ---------
3110  let lines =<< trim END
3111    let winid = popup_create(['111', '222', '333'], #{
3112	  \ cursorline : 1,
3113	  \ })
3114    call win_execute(winid, "2")
3115  END
3116  call writefile(lines, 'XtestPopupCursorLine')
3117  let buf = RunVimInTerminal('-S XtestPopupCursorLine', #{rows: 10})
3118  call VerifyScreenDump(buf, 'Test_popupwin_cursorline_7', {})
3119  call StopVimInTerminal(buf)
3120
3121  call delete('XtestPopupCursorLine')
3122
3123  " ---------
3124  " Use current buffer for popupmenu
3125  " ---------
3126  let lines =<< trim END
3127    call setline(1, ['one', 'two', 'three'])
3128    let winid = popup_create(bufnr('%'), #{
3129	  \ cursorline : 1,
3130	  \ })
3131    call win_execute(winid, "2")
3132  END
3133  call writefile(lines, 'XtestPopupCursorLine')
3134  let buf = RunVimInTerminal('-S XtestPopupCursorLine', #{rows: 10})
3135  call VerifyScreenDump(buf, 'Test_popupwin_cursorline_8', {})
3136  call StopVimInTerminal(buf)
3137
3138  call delete('XtestPopupCursorLine')
3139endfunc
3140
3141def Test_popup_cursorline_vim9()
3142  var winid = popup_create('some text', { cursorline: true, })
3143  assert_equal(1, popup_getoptions(winid).cursorline)
3144  popup_close(winid)
3145
3146  assert_fails("popup_create('some text', { cursorline: 2, })", 'E1023:')
3147  popup_clear()
3148enddef
3149
3150func Test_previewpopup()
3151  CheckScreendump
3152  CheckFeature quickfix
3153
3154  call writefile([
3155        \ "!_TAG_FILE_ENCODING\tutf-8\t//",
3156        \ "another\tXtagfile\t/^this is another",
3157        \ "theword\tXtagfile\t/^theword"],
3158        \ 'Xtags')
3159  call writefile(range(1,20)
3160        \ + ['theword is here']
3161        \ + range(22, 27)
3162        \ + ['this is another place']
3163        \ + range(29, 40),
3164        \ "Xtagfile")
3165  call writefile(range(1,10)
3166        \ + ['searched word is here']
3167        \ + range(12, 20),
3168        \ "Xheader.h")
3169  let lines =<< trim END
3170        set tags=Xtags
3171	call setline(1, [
3172	      \ 'one',
3173	      \ '#include "Xheader.h"',
3174	      \ 'three',
3175	      \ 'four',
3176	      \ 'five',
3177	      \ 'six',
3178	      \ 'seven',
3179	      \ 'find theword somewhere',
3180	      \ 'nine',
3181	      \ 'this is another word',
3182	      \ 'very long line where the word is also another'])
3183        set previewpopup=height:4,width:40
3184	hi OtherColor ctermbg=lightcyan guibg=lightcyan
3185	set path=.
3186  END
3187  call writefile(lines, 'XtestPreviewPopup')
3188  let buf = RunVimInTerminal('-S XtestPreviewPopup', #{rows: 14})
3189
3190  call term_sendkeys(buf, "/theword\<CR>\<C-W>}")
3191  call term_sendkeys(buf, ":\<CR>")
3192  call VerifyScreenDump(buf, 'Test_popupwin_previewpopup_1', {})
3193
3194  call term_sendkeys(buf, ":set previewpopup+=highlight:OtherColor\<CR>")
3195  call term_sendkeys(buf, "/another\<CR>\<C-W>}")
3196  call VerifyScreenDump(buf, 'Test_popupwin_previewpopup_2', {})
3197
3198  call term_sendkeys(buf, ":call popup_move(popup_findpreview(), #{col: 15})\<CR>")
3199  call term_sendkeys(buf, ":\<CR>")
3200  call VerifyScreenDump(buf, 'Test_popupwin_previewpopup_3', {})
3201
3202  call term_sendkeys(buf, "/another\<CR>\<C-W>}")
3203  call VerifyScreenDump(buf, 'Test_popupwin_previewpopup_4', {})
3204
3205  call term_sendkeys(buf, ":silent cd ..\<CR>:\<CR>")
3206  call VerifyScreenDump(buf, 'Test_popupwin_previewpopup_5', {})
3207  call term_sendkeys(buf, ":silent cd testdir\<CR>")
3208
3209  call term_sendkeys(buf, ":set previewpopup-=highlight:OtherColor\<CR>")
3210  call term_sendkeys(buf, ":pclose\<CR>")
3211  call term_sendkeys(buf, ":\<BS>")
3212  call VerifyScreenDump(buf, 'Test_popupwin_previewpopup_6', {})
3213
3214  call term_sendkeys(buf, ":pedit +/theword Xtagfile\<CR>")
3215  call term_sendkeys(buf, ":\<CR>")
3216  call VerifyScreenDump(buf, 'Test_popupwin_previewpopup_7', {})
3217
3218  call term_sendkeys(buf, ":pclose\<CR>")
3219  call term_sendkeys(buf, ":psearch searched\<CR>")
3220  call term_sendkeys(buf, ":\<CR>")
3221  call VerifyScreenDump(buf, 'Test_popupwin_previewpopup_8', {})
3222
3223  call term_sendkeys(buf, "\<C-W>p")
3224  call VerifyScreenDump(buf, 'Test_popupwin_previewpopup_9', {})
3225
3226  call term_sendkeys(buf, ":call win_execute(popup_findpreview(), 'call popup_clear()')\<CR>")
3227  call VerifyScreenDump(buf, 'Test_popupwin_previewpopup_10', {})
3228
3229  call StopVimInTerminal(buf)
3230  call delete('Xtags')
3231  call delete('Xtagfile')
3232  call delete('XtestPreviewPopup')
3233  call delete('Xheader.h')
3234endfunc
3235
3236func Get_popupmenu_lines()
3237  let lines =<< trim END
3238      set completeopt+=preview,popup
3239      set completefunc=CompleteFuncDict
3240      hi InfoPopup ctermbg=yellow
3241
3242      func CompleteFuncDict(findstart, base)
3243	if a:findstart
3244	  if col('.') > 10
3245	    return col('.') - 10
3246	  endif
3247	  return 0
3248	endif
3249
3250	return {
3251		\ 'words': [
3252		  \ {
3253		    \ 'word': 'aword',
3254		    \ 'abbr': 'wrd',
3255		    \ 'menu': 'extra text',
3256		    \ 'info': 'words are cool',
3257		    \ 'kind': 'W',
3258		    \ 'user_data': 'test'
3259		  \ },
3260		  \ {
3261		    \ 'word': 'anotherword',
3262		    \ 'abbr': 'anotwrd',
3263		    \ 'menu': 'extra text',
3264		    \ 'info': "other words are\ncooler than this and some more text\nto make wrap",
3265		    \ 'kind': 'W',
3266		    \ 'user_data': 'notest'
3267		  \ },
3268		  \ {
3269		    \ 'word': 'noinfo',
3270		    \ 'abbr': 'noawrd',
3271		    \ 'menu': 'extra text',
3272		    \ 'info': "lets\nshow\na\nscrollbar\nhere",
3273		    \ 'kind': 'W',
3274		    \ 'user_data': 'notest'
3275		  \ },
3276		  \ {
3277		    \ 'word': 'thatword',
3278		    \ 'abbr': 'thatwrd',
3279		    \ 'menu': 'extra text',
3280		    \ 'info': 'that word is cool',
3281		    \ 'kind': 'W',
3282		    \ 'user_data': 'notest'
3283		  \ },
3284		\ ]
3285	      \ }
3286      endfunc
3287      call setline(1, 'text text text text text text text ')
3288      func ChangeColor()
3289	let id = popup_findinfo()
3290	if buflisted(winbufnr(id))
3291	  call setline(1, 'buffer is listed')
3292	endif
3293	eval id->popup_setoptions(#{highlight: 'InfoPopup'})
3294      endfunc
3295
3296      func InfoHidden()
3297	set completepopup=height:4,border:off,align:menu
3298	set completeopt-=popup completeopt+=popuphidden
3299	au CompleteChanged * call HandleChange()
3300      endfunc
3301
3302      let s:counter = 0
3303      func HandleChange()
3304	let s:counter += 1
3305	let selected = complete_info(['selected']).selected
3306	if selected <= 0
3307	  " First time: do nothing, info remains hidden
3308	  return
3309	endif
3310	if selected == 1
3311	  " Second time: show info right away
3312	  let id = popup_findinfo()
3313	  if id
3314	    call popup_settext(id, 'immediate info ' .. s:counter)
3315	    call popup_show(id)
3316	  endif
3317	else
3318	  " Third time: show info after a short delay
3319	  call timer_start(100, 'ShowInfo')
3320	endif
3321      endfunc
3322
3323      func ShowInfo(...)
3324	let id = popup_findinfo()
3325	if id
3326	  call popup_settext(id, 'async info ' .. s:counter)
3327	  call popup_show(id)
3328	endif
3329      endfunc
3330
3331      func OpenOtherPopups()
3332	call popup_create([
3333		\ 'popup below',
3334		\ 'popup below',
3335		\ 'popup below',
3336		\ 'popup below',
3337	      \ ], #{
3338		\ line: 'cursor',
3339		\ col: 'cursor+3',
3340		\ highlight: 'ErrorMsg',
3341		\ minwidth: 17,
3342		\ zindex: 50,
3343	      \ })
3344	call popup_create([
3345		\ 'popup on top',
3346		\ 'popup on top',
3347		\ 'popup on top',
3348	      \ ], #{
3349		\ line: 'cursor+3',
3350		\ col: 'cursor-10',
3351		\ highlight: 'Search',
3352		\ minwidth: 10,
3353		\ zindex: 200,
3354	      \ })
3355      endfunc
3356
3357      " Check that no autocommands are triggered for the info popup
3358      au WinEnter * if win_gettype() == 'popup' | call setline(2, 'WinEnter') | endif
3359      au WinLeave * if win_gettype() == 'popup' | call setline(2, 'WinLeave') | endif
3360  END
3361  return lines
3362endfunc
3363
3364func Test_popupmenu_info_border()
3365  CheckScreendump
3366  CheckFeature quickfix
3367
3368  let lines = Get_popupmenu_lines()
3369  call add(lines, 'set completepopup=height:4,highlight:InfoPopup')
3370  call writefile(lines, 'XtestInfoPopup')
3371
3372  let buf = RunVimInTerminal('-S XtestInfoPopup', #{rows: 14})
3373  call TermWait(buf, 25)
3374
3375  call term_sendkeys(buf, "A\<C-X>\<C-U>")
3376  call VerifyScreenDump(buf, 'Test_popupwin_infopopup_1', {})
3377
3378  call term_sendkeys(buf, "\<C-N>")
3379  call VerifyScreenDump(buf, 'Test_popupwin_infopopup_2', {})
3380
3381  call term_sendkeys(buf, "\<C-N>")
3382  call VerifyScreenDump(buf, 'Test_popupwin_infopopup_3', {})
3383
3384  call term_sendkeys(buf, "\<C-N>\<C-N>")
3385  call VerifyScreenDump(buf, 'Test_popupwin_infopopup_4', {})
3386
3387  " info on the left with scrollbar
3388  call term_sendkeys(buf, "test text test text\<C-X>\<C-U>")
3389  call term_sendkeys(buf, "\<C-N>\<C-N>")
3390  call VerifyScreenDump(buf, 'Test_popupwin_infopopup_5', {})
3391
3392  " Test that the popupmenu's scrollbar and infopopup do not overlap
3393  call term_sendkeys(buf, "\<Esc>")
3394  call term_sendkeys(buf, ":set pumheight=3\<CR>")
3395  call term_sendkeys(buf, "cc\<C-X>\<C-U>")
3396  call VerifyScreenDump(buf, 'Test_popupwin_infopopup_6', {})
3397
3398  " Hide the info popup, cycle through buffers, make sure it didn't get
3399  " deleted.
3400  call term_sendkeys(buf, "\<Esc>")
3401  call term_sendkeys(buf, ":set hidden\<CR>")
3402  call term_sendkeys(buf, ":bn\<CR>")
3403  call term_sendkeys(buf, ":bn\<CR>")
3404  call term_sendkeys(buf, "otest text test text\<C-X>\<C-U>")
3405  call VerifyScreenDump(buf, 'Test_popupwin_infopopup_7', {})
3406
3407  " Test that when the option is changed the popup changes.
3408  call term_sendkeys(buf, "\<Esc>")
3409  call term_sendkeys(buf, ":set completepopup=border:off\<CR>")
3410  call term_sendkeys(buf, "a\<C-X>\<C-U>")
3411  call VerifyScreenDump(buf, 'Test_popupwin_infopopup_8', {})
3412
3413  call term_sendkeys(buf, " \<Esc>")
3414  call term_sendkeys(buf, ":set completepopup+=width:10\<CR>")
3415  call term_sendkeys(buf, "a\<C-X>\<C-U>")
3416  call VerifyScreenDump(buf, 'Test_popupwin_infopopup_9', {})
3417
3418  call term_sendkeys(buf, "\<Esc>")
3419  call StopVimInTerminal(buf)
3420  call delete('XtestInfoPopup')
3421endfunc
3422
3423func Test_popupmenu_info_noborder()
3424  CheckScreendump
3425  CheckFeature quickfix
3426
3427  let lines = Get_popupmenu_lines()
3428  call add(lines, 'set completepopup=height:4,border:off')
3429  call writefile(lines, 'XtestInfoPopupNb')
3430
3431  let buf = RunVimInTerminal('-S XtestInfoPopupNb', #{rows: 14})
3432  call TermWait(buf, 25)
3433
3434  call term_sendkeys(buf, "A\<C-X>\<C-U>")
3435  call VerifyScreenDump(buf, 'Test_popupwin_infopopup_nb_1', {})
3436
3437  call StopVimInTerminal(buf)
3438  call delete('XtestInfoPopupNb')
3439endfunc
3440
3441func Test_popupmenu_info_align_menu()
3442  CheckScreendump
3443  CheckFeature quickfix
3444
3445  let lines = Get_popupmenu_lines()
3446  call add(lines, 'set completepopup=height:4,border:off,align:menu')
3447  call writefile(lines, 'XtestInfoPopupNb')
3448
3449  let buf = RunVimInTerminal('-S XtestInfoPopupNb', #{rows: 14})
3450  call TermWait(buf, 25)
3451
3452  call term_sendkeys(buf, "A\<C-X>\<C-U>")
3453  call term_sendkeys(buf, "\<C-N>")
3454  call term_sendkeys(buf, "\<C-N>")
3455  call term_sendkeys(buf, "\<C-N>")
3456  call VerifyScreenDump(buf, 'Test_popupwin_infopopup_align_1', {})
3457
3458  call term_sendkeys(buf, "test text test text test\<C-X>\<C-U>")
3459  call term_sendkeys(buf, "\<C-N>")
3460  call VerifyScreenDump(buf, 'Test_popupwin_infopopup_align_2', {})
3461
3462  call term_sendkeys(buf, "\<Esc>")
3463  call term_sendkeys(buf, ":call ChangeColor()\<CR>")
3464  call term_sendkeys(buf, ":call setline(2, ['x']->repeat(10))\<CR>")
3465  call term_sendkeys(buf, "Gotest text test text\<C-X>\<C-U>")
3466  call VerifyScreenDump(buf, 'Test_popupwin_infopopup_align_3', {})
3467
3468  call StopVimInTerminal(buf)
3469  call delete('XtestInfoPopupNb')
3470endfunc
3471
3472func Test_popupmenu_info_hidden()
3473  CheckScreendump
3474  CheckFeature quickfix
3475
3476  let lines = Get_popupmenu_lines()
3477  call add(lines, 'call InfoHidden()')
3478  call writefile(lines, 'XtestInfoPopupHidden')
3479
3480  let buf = RunVimInTerminal('-S XtestInfoPopupHidden', #{rows: 14})
3481  call TermWait(buf, 25)
3482
3483  call term_sendkeys(buf, "A\<C-X>\<C-U>")
3484  call VerifyScreenDump(buf, 'Test_popupwin_infopopup_hidden_1', {})
3485
3486  call term_sendkeys(buf, "\<C-N>")
3487  call VerifyScreenDump(buf, 'Test_popupwin_infopopup_hidden_2', {})
3488
3489  call term_sendkeys(buf, "\<C-N>")
3490  call VerifyScreenDump(buf, 'Test_popupwin_infopopup_hidden_3', {})
3491
3492  call term_sendkeys(buf, "\<Esc>")
3493  call StopVimInTerminal(buf)
3494  call delete('XtestInfoPopupHidden')
3495endfunc
3496
3497func Test_popupmenu_info_too_wide()
3498  CheckScreendump
3499  CheckFeature quickfix
3500
3501  let lines =<< trim END
3502    call setline(1, range(10))
3503
3504    set completeopt+=preview,popup
3505    set completepopup=align:menu
3506    set omnifunc=OmniFunc
3507    hi InfoPopup ctermbg=lightgrey
3508
3509    func OmniFunc(findstart, base)
3510      if a:findstart
3511        return 0
3512      endif
3513
3514      let menuText = 'some long text to make sure the menu takes up all of the width of the window'
3515      return #{
3516    	\ words: [
3517    	  \ #{
3518    	    \ word: 'scrap',
3519    	    \ menu: menuText,
3520    	    \ info: "other words are\ncooler than this and some more text\nto make wrap",
3521    	  \ },
3522    	  \ #{
3523    	    \ word: 'scappier',
3524    	    \ menu: menuText,
3525    	    \ info: 'words are cool',
3526    	  \ },
3527    	  \ #{
3528    	    \ word: 'scrappier2',
3529    	    \ menu: menuText,
3530    	    \ info: 'words are cool',
3531    	  \ },
3532    	\ ]
3533     \ }
3534    endfunc
3535  END
3536
3537  call writefile(lines, 'XtestInfoPopupWide')
3538  let buf = RunVimInTerminal('-S XtestInfoPopupWide', #{rows: 8})
3539  call TermWait(buf, 25)
3540
3541  call term_sendkeys(buf, "Ascr\<C-X>\<C-O>")
3542  call VerifyScreenDump(buf, 'Test_popupwin_infopopup_wide_1', {})
3543
3544  call term_sendkeys(buf, "\<Esc>")
3545  call StopVimInTerminal(buf)
3546  call delete('XtestInfoPopupWide')
3547endfunc
3548
3549func Test_popupmenu_masking()
3550  " Test that popup windows that are opened while popup menu is open are
3551  " properly displayed.
3552  CheckScreendump
3553  CheckFeature quickfix
3554
3555  let lines = Get_popupmenu_lines()
3556  call add(lines, 'inoremap <C-A> <Cmd>call OpenOtherPopups()<CR>')
3557  call writefile(lines, 'XtestPopupmenuMasking')
3558
3559  let buf = RunVimInTerminal('-S XtestPopupmenuMasking', #{rows: 14})
3560  call TermWait(buf, 25)
3561
3562  call term_sendkeys(buf, "A\<C-X>\<C-U>\<C-A>")
3563  call VerifyScreenDump(buf, 'Test_popupwin_popupmenu_masking_1', {})
3564
3565  call term_sendkeys(buf, "\<Esc>")
3566  call VerifyScreenDump(buf, 'Test_popupwin_popupmenu_masking_2', {})
3567
3568  call StopVimInTerminal(buf)
3569  call delete('XtestPopupmenuMasking')
3570endfunc
3571
3572func Test_popupwin_recycle_bnr()
3573  let bufnr = popup_notification('nothing wrong', {})->winbufnr()
3574  call popup_clear()
3575  let winid = 'nothing wrong'->popup_notification({})
3576  call assert_equal(bufnr, winbufnr(winid))
3577  call popup_clear()
3578endfunc
3579
3580func Test_popupwin_getoptions_tablocal()
3581  topleft split
3582  let win1 = popup_create('nothing', #{maxheight: 8})
3583  let win2 = popup_create('something', #{maxheight: 10})
3584  let win3 = popup_create('something', #{maxheight: 15})
3585  call assert_equal(8, popup_getoptions(win1).maxheight)
3586  call assert_equal(10, popup_getoptions(win2).maxheight)
3587  call assert_equal(15, popup_getoptions(win3).maxheight)
3588  call popup_clear()
3589  quit
3590endfunc
3591
3592func Test_popupwin_cancel()
3593  let win1 = popup_create('one', #{line: 5, filter: {... -> 0}})
3594  let win2 = popup_create('two', #{line: 10, filter: {... -> 0}})
3595  let win3 = popup_create('three', #{line: 15, filter: {... -> 0}})
3596  call assert_equal(5, popup_getpos(win1).line)
3597  call assert_equal(10, popup_getpos(win2).line)
3598  call assert_equal(15, popup_getpos(win3).line)
3599  " TODO: this also works without patch 8.1.2110
3600  call feedkeys("\<C-C>", 'xt')
3601  call assert_equal(5, popup_getpos(win1).line)
3602  call assert_equal(10, popup_getpos(win2).line)
3603  call assert_equal({}, popup_getpos(win3))
3604  call feedkeys("\<C-C>", 'xt')
3605  call assert_equal(5, popup_getpos(win1).line)
3606  call assert_equal({}, popup_getpos(win2))
3607  call assert_equal({}, popup_getpos(win3))
3608  call feedkeys("\<C-C>", 'xt')
3609  call assert_equal({}, popup_getpos(win1))
3610  call assert_equal({}, popup_getpos(win2))
3611  call assert_equal({}, popup_getpos(win3))
3612endfunc
3613
3614func Test_popupwin_filter_redraw()
3615  " Create two popups with a filter that closes the popup when typing "0".
3616  " Both popups should close, even though the redraw also calls
3617  " popup_reset_handled()
3618
3619  func CloseFilter(winid, key)
3620    if a:key == '0'
3621      call popup_close(a:winid)
3622      redraw
3623    endif
3624    return 0  " pass the key
3625  endfunc
3626
3627  let id1 = popup_create('first one', #{
3628	\ line: 1,
3629	\ col: 1,
3630	\ filter: 'CloseFilter',
3631	\ })
3632  let id2 = popup_create('second one', #{
3633	\ line: 9,
3634	\ col: 1,
3635	\ filter: 'CloseFilter',
3636	\ })
3637  call assert_equal(1, popup_getpos(id1).line)
3638  call assert_equal(9, popup_getpos(id2).line)
3639
3640  call feedkeys('0', 'xt')
3641  call assert_equal({}, popup_getpos(id1))
3642  call assert_equal({}, popup_getpos(id2))
3643
3644  call popup_clear()
3645  delfunc CloseFilter
3646endfunc
3647
3648func Test_popupwin_double_width()
3649  CheckScreendump
3650
3651  let lines =<< trim END
3652    call setline(1, 'x你好世界你好世你好世界你好')
3653    call setline(2, '你好世界你好世你好世界你好')
3654    call setline(3, 'x你好世界你好世你好世界你好')
3655    call popup_create('你好,世界 - 你好,世界xxxxx', #{line: 1, col: 3, maxwidth: 14})
3656  END
3657  call writefile(lines, 'XtestPopupWide')
3658
3659  let buf = RunVimInTerminal('-S XtestPopupWide', #{rows: 10})
3660  call VerifyScreenDump(buf, 'Test_popupwin_doublewidth_1', {})
3661
3662  call StopVimInTerminal(buf)
3663  call delete('XtestPopupWide')
3664endfunc
3665
3666func Test_popupwin_sign()
3667  CheckScreendump
3668
3669  let lines =<< trim END
3670    call setline(1, range(10))
3671    call sign_define('Current', {
3672	    \ 'text': '>>',
3673	    \ 'texthl': 'WarningMsg',
3674	    \ 'linehl': 'Error',
3675	    \ })
3676    call sign_define('Other', {
3677	    \ 'text': '#!',
3678	    \ 'texthl': 'Error',
3679	    \ 'linehl': 'Search',
3680	    \ })
3681    let winid = popup_create(['hello', 'bright', 'world'], {
3682	    \ 'minwidth': 20,
3683	    \ })
3684    call setwinvar(winid, "&signcolumn", "yes")
3685    let winbufnr = winbufnr(winid)
3686
3687    " add sign to current buffer, shows
3688    call sign_place(1, 'Selected', 'Current', bufnr('%'), {'lnum': 1})
3689    " add sign to current buffer, does not show
3690    call sign_place(2, 'PopUpSelected', 'Other', bufnr('%'), {'lnum': 2})
3691
3692    " add sign to popup buffer, shows
3693    call sign_place(3, 'PopUpSelected', 'Other', winbufnr, {'lnum': 1})
3694    " add sign to popup buffer, does not show
3695    call sign_place(4, 'Selected', 'Current', winbufnr, {'lnum': 2})
3696
3697    func SetOptions()
3698      call setwinvar(g:winid, '&number', 1)
3699      call setwinvar(g:winid, '&foldcolumn', 2)
3700      call popup_settext(g:winid, 'a longer line to check the width')
3701    endfunc
3702  END
3703  call writefile(lines, 'XtestPopupSign')
3704
3705  let buf = RunVimInTerminal('-S XtestPopupSign', #{rows: 10})
3706  call VerifyScreenDump(buf, 'Test_popupwin_sign_1', {})
3707
3708  " set more options to check the width is adjusted
3709  call term_sendkeys(buf, ":call SetOptions()\<CR>")
3710  call VerifyScreenDump(buf, 'Test_popupwin_sign_2', {})
3711
3712  call StopVimInTerminal(buf)
3713  call delete('XtestPopupSign')
3714endfunc
3715
3716func Test_popupwin_bufnr()
3717  let popwin = popup_create(['blah'], #{})
3718  let popbuf = winbufnr(popwin)
3719  split asdfasdf
3720  let newbuf = bufnr()
3721  call assert_true(newbuf > popbuf, 'New buffer number is higher')
3722  call assert_equal(newbuf, bufnr('$'))
3723  call popup_clear()
3724  let popwin = popup_create(['blah'], #{})
3725  " reuses previous buffer number
3726  call assert_equal(popbuf, winbufnr(popwin))
3727  call assert_equal(newbuf, bufnr('$'))
3728
3729  call popup_clear()
3730  bwipe!
3731endfunc
3732
3733func Test_popupwin_filter_input_multibyte()
3734  func MyPopupFilter(winid, c)
3735    let g:bytes = range(a:c->strlen())->map({i -> char2nr(a:c[i])})
3736    return 0
3737  endfunc
3738  let winid = popup_create('', #{mapping: 0, filter: 'MyPopupFilter'})
3739
3740  " UTF-8: E3 80 80, including K_SPECIAL(0x80)
3741  call feedkeys("\u3000", 'xt')
3742  call assert_equal([0xe3, 0x80, 0x80], g:bytes)
3743
3744  " UTF-8: E3 80 9B, including CSI(0x9B)
3745  call feedkeys("\u301b", 'xt')
3746  call assert_equal([0xe3, 0x80, 0x9b], g:bytes)
3747
3748  if has('unix')
3749    " with modifyOtherKeys <M-S-a> does not include a modifier sequence
3750    if has('gui_running')
3751      call feedkeys("\x9b\xfc\x08A", 'Lx!')
3752    else
3753      call feedkeys("\<Esc>[27;4;65~", 'Lx!')
3754    endif
3755    call assert_equal([0xc3, 0x81], g:bytes)
3756  endif
3757
3758  call popup_clear()
3759  delfunc MyPopupFilter
3760  unlet g:bytes
3761endfunc
3762
3763func Test_popupwin_filter_close_ctrl_c()
3764  CheckScreendump
3765
3766  let lines =<< trim END
3767      vsplit
3768      set laststatus=2
3769      set statusline=%!Statusline()
3770
3771      function Statusline() abort
3772	  return '%<%f %h%m%r%=%-14.(%l,%c%V%) %P'
3773      endfunction
3774
3775      call popup_create('test test test test...', {'filter': {-> 0}})
3776  END
3777  call writefile(lines, 'XtestPopupCtrlC')
3778
3779  let buf = RunVimInTerminal('-S XtestPopupCtrlC', #{rows: 10})
3780
3781  call term_sendkeys(buf, "\<C-C>")
3782  call VerifyScreenDump(buf, 'Test_popupwin_ctrl_c', {})
3783
3784  call StopVimInTerminal(buf)
3785  call delete('XtestPopupCtrlC')
3786endfunc
3787
3788func Test_popupwin_filter_close_wrong_name()
3789  CheckScreendump
3790
3791  let lines =<< trim END
3792      call popup_create('one two three...', {'filter': 'NoSuchFunc'})
3793  END
3794  call writefile(lines, 'XtestPopupWrongName')
3795
3796  let buf = RunVimInTerminal('-S XtestPopupWrongName', #{rows: 10})
3797
3798  call term_sendkeys(buf, "j")
3799  call VerifyScreenDump(buf, 'Test_popupwin_wrong_name', {})
3800
3801  call StopVimInTerminal(buf)
3802  call delete('XtestPopupWrongName')
3803endfunc
3804
3805func Test_popupwin_filter_close_three_errors()
3806  CheckScreendump
3807
3808  let lines =<< trim END
3809      set cmdheight=2
3810      call popup_create('one two three...', {'filter': 'filter'})
3811  END
3812  call writefile(lines, 'XtestPopupThreeErrors')
3813
3814  let buf = RunVimInTerminal('-S XtestPopupThreeErrors', #{rows: 10})
3815
3816  call term_sendkeys(buf, "jj")
3817  call VerifyScreenDump(buf, 'Test_popupwin_three_errors_1', {})
3818  call term_sendkeys(buf, "j")
3819  call VerifyScreenDump(buf, 'Test_popupwin_three_errors_2', {})
3820
3821  call StopVimInTerminal(buf)
3822  call delete('XtestPopupThreeErrors')
3823endfunc
3824
3825func Test_popupwin_latin1_encoding()
3826  CheckScreendump
3827  CheckUnix
3828
3829  " When 'encoding' is a single-byte encoding a terminal window will mess up
3830  " the display.  Check that showing a popup on top of that doesn't crash.
3831  let lines =<< trim END
3832      set encoding=latin1
3833      terminal cat Xmultibyte
3834      call popup_create(['one', 'two', 'three', 'four'], #{line: 1, col: 10})
3835      redraw
3836      " wait for "cat" to finish
3837      while execute('ls!') !~ 'finished'
3838	sleep 10m
3839      endwhile
3840      echo "Done"
3841  END
3842  call writefile(lines, 'XtestPopupLatin')
3843  call writefile([repeat("\u3042 ", 120)], 'Xmultibyte')
3844
3845  let buf = RunVimInTerminal('-S XtestPopupLatin', #{rows: 10})
3846  call WaitForAssert({-> assert_match('Done', term_getline(buf, 10))})
3847
3848  call term_sendkeys(buf, ":q\<CR>")
3849  call StopVimInTerminal(buf)
3850  call delete('XtestPopupLatin')
3851  call delete('Xmultibyte')
3852endfunc
3853
3854func Test_popupwin_atcursor_far_right()
3855  new
3856
3857  " this was getting stuck
3858  set signcolumn=yes
3859  call setline(1, repeat('=', &columns))
3860  normal! ggg$
3861  let winid = popup_atcursor(repeat('x', 500), #{moved: 'any', border: []})
3862
3863  " 'signcolumn' was getting reset
3864  call setwinvar(winid, '&signcolumn', 'yes')
3865  call popup_setoptions(winid, {'zindex': 1000})
3866  call assert_equal('yes', getwinvar(winid, '&signcolumn'))
3867
3868  call popup_close(winid)
3869  bwipe!
3870  set signcolumn&
3871endfunc
3872
3873func Test_popupwin_splitmove()
3874  vsplit
3875  let win2 = win_getid()
3876  let popup_winid = popup_dialog('hello', {})
3877  call assert_fails('call win_splitmove(popup_winid, win2)', 'E957:')
3878  call assert_fails('call win_splitmove(win2, popup_winid)', 'E957:')
3879
3880  call popup_clear()
3881  bwipe
3882endfunc
3883
3884func Test_popupwin_exiting_terminal()
3885  CheckFeature terminal
3886
3887  " Tests that when creating a popup right after closing a terminal window does
3888  " not make the popup the current window.
3889  let winid = win_getid()
3890  try
3891    augroup Test_popupwin_exiting_terminal
3892      autocmd!
3893      autocmd WinEnter * :call popup_create('test', {})
3894    augroup END
3895    let bnr = term_start(&shell, #{term_finish: 'close'})
3896    call term_sendkeys(bnr, "exit\r\n")
3897    call WaitForAssert({-> assert_equal(winid, win_getid())})
3898  finally
3899    call popup_clear(1)
3900    augroup Test_popupwin_exiting_terminal
3901      autocmd!
3902    augroup END
3903  endtry
3904endfunc
3905
3906func Test_popup_filter_menu()
3907  let colors = ['red', 'green', 'blue']
3908  call popup_menu(colors, #{callback: {_, result -> assert_equal('green', colors[result - 1])}})
3909  call feedkeys("\<c-n>\<c-n>\<c-p>\<cr>", 'xt')
3910endfunc
3911
3912func Test_popup_getoptions_other_tab()
3913  new
3914  call setline(1, 'some text')
3915  call prop_type_add('textprop', {})
3916  call prop_add(1, 1, #{type: 'textprop', length: 1})
3917  let id = popup_create('TEST', #{textprop: 'textprop', highlight: 'ErrorMsg', tabpage: 1})
3918  tab sp
3919  call assert_equal(['textprop', 'textpropid', 'textpropwin'], popup_getoptions(id)->keys()->filter({_, v -> v =~ 'textprop'}))
3920
3921  tabclose
3922  call popup_close(id)
3923  bwipe!
3924  call prop_type_delete('textprop')
3925endfunc
3926
3927
3928func Test_popup_setoptions_other_tab()
3929  new Xfile
3930  let winid = win_getid()
3931  call setline(1, 'some text')
3932  call prop_type_add('textprop', {})
3933  call prop_add(1, 1, #{type: 'textprop', length: 1})
3934  let id = popup_create('TEST', #{textprop: 'textprop'})
3935  tab sp
3936  call popup_setoptions(id, #{textprop: 'textprop', textpropwin: winid})
3937  call assert_equal(winid, popup_getoptions(id).textpropwin)
3938
3939  tabclose
3940  call popup_close(id)
3941  bwipe! Xfile
3942  call prop_type_delete('textprop')
3943endfunc
3944
3945func Test_popup_prop_not_visible()
3946  CheckScreendump
3947
3948  let lines =<< trim END
3949      vim9script
3950      set nowrap stal=2
3951      rightbelow :31vnew
3952      setline(1, ['', 'some text', '', 'other text'])
3953      prop_type_add('someprop', {})
3954      prop_add(2, 9, {type: 'someprop', length: 5})
3955      popup_create('attached to "some"', {
3956          textprop: 'someprop',
3957          highlight: 'ErrorMsg',
3958          line: -1,
3959          wrap: false,
3960          fixed: true,
3961          })
3962      prop_type_add('otherprop', {})
3963      prop_add(4, 10, {type: 'otherprop', length: 5})
3964      popup_create('attached to "other"', {
3965          textprop: 'otherprop',
3966          highlight: 'ErrorMsg',
3967          line: -1,
3968          wrap: false,
3969          fixed: false,
3970          })
3971  END
3972  call writefile(lines, 'XtestPropNotVisble')
3973  let buf = RunVimInTerminal('-S XtestPropNotVisble', #{rows: 10})
3974  call VerifyScreenDump(buf, 'Test_popup_prop_not_visible_01', {})
3975
3976  call term_sendkeys(buf, ":vert resize -14\<CR>")
3977  call VerifyScreenDump(buf, 'Test_popup_prop_not_visible_02', {})
3978
3979  call term_sendkeys(buf, ":vert resize -8\<CR>")
3980  call VerifyScreenDump(buf, 'Test_popup_prop_not_visible_03', {})
3981
3982  " clean up
3983  call StopVimInTerminal(buf)
3984  call delete('XtestPropNotVisble')
3985endfunction
3986
3987func Test_bufdel_skips_popupwin_buffer()
3988    let id = popup_create("Some text", {})
3989    %bd
3990    call popup_close(id)
3991endfunc
3992
3993
3994" vim: shiftwidth=2 sts=2
3995