1" Tests for decoding escape sequences sent by the terminal.
2
3" This only works for Unix in a terminal
4source check.vim
5CheckNotGui
6CheckUnix
7
8source shared.vim
9
10" xterm2 and sgr always work, urxvt is optional.
11let s:ttymouse_values = ['xterm2', 'sgr']
12if has('mouse_urxvt')
13  call add(s:ttymouse_values, 'urxvt')
14endif
15
16" dec doesn't support all the functionality
17if has('mouse_dec')
18  let s:ttymouse_dec = ['dec']
19else
20  let s:ttymouse_dec = []
21endif
22
23" netterm only supports left click
24if has('mouse_netterm')
25  let s:ttymouse_netterm = ['netterm']
26else
27  let s:ttymouse_netterm = []
28endif
29
30" Helper function to emit a terminal escape code.
31func TerminalEscapeCode(code, row, col, m)
32  if &ttymouse ==# 'xterm2'
33    " need to use byte encoding here.
34    let str = list2str([a:code + 0x20, a:col + 0x20, a:row + 0x20])
35    if has('iconv')
36      let bytes = str->iconv('utf-8', 'latin1')
37    else
38      " Hopefully the numbers are not too big.
39      let bytes = str
40    endif
41    call feedkeys("\<Esc>[M" .. bytes, 'Lx!')
42  elseif &ttymouse ==# 'sgr'
43    call feedkeys(printf("\<Esc>[<%d;%d;%d%s", a:code, a:col, a:row, a:m), 'Lx!')
44  elseif &ttymouse ==# 'urxvt'
45    call feedkeys(printf("\<Esc>[%d;%d;%dM", a:code + 0x20, a:col, a:row), 'Lx!')
46  endif
47endfunc
48
49func DecEscapeCode(code, down, row, col)
50    call feedkeys(printf("\<Esc>[%d;%d;%d;%d&w", a:code, a:down, a:row, a:col), 'Lx!')
51endfunc
52
53func NettermEscapeCode(row, col)
54    call feedkeys(printf("\<Esc>}%d,%d\r", a:row, a:col), 'Lx!')
55endfunc
56
57func MouseLeftClick(row, col)
58  if &ttymouse ==# 'dec'
59    call DecEscapeCode(2, 4, a:row, a:col)
60  elseif &ttymouse ==# 'netterm'
61    call NettermEscapeCode(a:row, a:col)
62  else
63    call TerminalEscapeCode(0, a:row, a:col, 'M')
64  endif
65endfunc
66
67func MouseMiddleClick(row, col)
68  if &ttymouse ==# 'dec'
69    call DecEscapeCode(4, 2, a:row, a:col)
70  else
71    call TerminalEscapeCode(1, a:row, a:col, 'M')
72  endif
73endfunc
74
75func MouseRightClick(row, col)
76  if &ttymouse ==# 'dec'
77    call DecEscapeCode(6, 1, a:row, a:col)
78  else
79    call TerminalEscapeCode(2, a:row, a:col, 'M')
80  endif
81endfunc
82
83func MouseCtrlLeftClick(row, col)
84  let ctrl = 0x10
85  call TerminalEscapeCode(0 + ctrl, a:row, a:col, 'M')
86endfunc
87
88func MouseCtrlRightClick(row, col)
89  let ctrl = 0x10
90  call TerminalEscapeCode(2 + ctrl, a:row, a:col, 'M')
91endfunc
92
93func MouseLeftRelease(row, col)
94  if &ttymouse ==# 'dec'
95    call DecEscapeCode(3, 0, a:row, a:col)
96  elseif &ttymouse ==# 'netterm'
97    " send nothing
98  else
99    call TerminalEscapeCode(3, a:row, a:col, 'm')
100  endif
101endfunc
102
103func MouseMiddleRelease(row, col)
104  if &ttymouse ==# 'dec'
105    call DecEscapeCode(5, 0, a:row, a:col)
106  else
107    call TerminalEscapeCode(3, a:row, a:col, 'm')
108  endif
109endfunc
110
111func MouseRightRelease(row, col)
112  if &ttymouse ==# 'dec'
113    call DecEscapeCode(7, 0, a:row, a:col)
114  else
115    call TerminalEscapeCode(3, a:row, a:col, 'm')
116  endif
117endfunc
118
119func MouseLeftDrag(row, col)
120  if &ttymouse ==# 'dec'
121    call DecEscapeCode(1, 4, a:row, a:col)
122  else
123    call TerminalEscapeCode(0x20, a:row, a:col, 'M')
124  endif
125endfunc
126
127func MouseWheelUp(row, col)
128  call TerminalEscapeCode(0x40, a:row, a:col, 'M')
129endfunc
130
131func MouseWheelDown(row, col)
132  call TerminalEscapeCode(0x41, a:row, a:col, 'M')
133endfunc
134
135func Test_term_mouse_left_click()
136  new
137  let save_mouse = &mouse
138  let save_term = &term
139  let save_ttymouse = &ttymouse
140  call test_override('no_query_mouse', 1)
141  set mouse=a term=xterm
142  call setline(1, ['line 1', 'line 2', 'line 3 is a bit longer'])
143
144  for ttymouse_val in s:ttymouse_values + s:ttymouse_dec + s:ttymouse_netterm
145    let msg = 'ttymouse=' .. ttymouse_val
146    exe 'set ttymouse=' .. ttymouse_val
147    go
148    call assert_equal([0, 1, 1, 0], getpos('.'), msg)
149    let row = 2
150    let col = 6
151    call MouseLeftClick(row, col)
152    call MouseLeftRelease(row, col)
153    call assert_equal([0, 2, 6, 0], getpos('.'), msg)
154  endfor
155
156  let &mouse = save_mouse
157  let &term = save_term
158  let &ttymouse = save_ttymouse
159  call test_override('no_query_mouse', 0)
160  bwipe!
161endfunc
162
163func Test_xterm_mouse_right_click_extends_visual()
164  if has('mac')
165    throw "Skipped: test right click in visual mode does not work on macOs (why?)"
166  endif
167  let save_mouse = &mouse
168  let save_term = &term
169  let save_ttymouse = &ttymouse
170  call test_override('no_query_mouse', 1)
171  set mouse=a term=xterm
172
173  for visual_mode in ["v", "V", "\<C-V>"]
174    for ttymouse_val in s:ttymouse_values + s:ttymouse_dec
175      let msg = 'visual=' .. visual_mode .. ' ttymouse=' .. ttymouse_val
176      exe 'set ttymouse=' .. ttymouse_val
177
178      call setline(1, repeat([repeat('-', 7)], 7))
179      call MouseLeftClick(4, 4)
180      call MouseLeftRelease(4, 4)
181      exe  "norm! " .. visual_mode
182
183      " Right click extends top left of visual area.
184      call MouseRightClick(2, 2)
185      call MouseRightRelease(2, 2)
186
187      " Right click extends bottom bottom right of visual area.
188      call MouseRightClick(6, 6)
189      call MouseRightRelease(6, 6)
190      norm! r1gv
191
192      " Right click shrinks top left of visual area.
193      call MouseRightClick(3, 3)
194      call MouseRightRelease(3, 3)
195
196      " Right click shrinks bottom right of visual area.
197      call MouseRightClick(5, 5)
198      call MouseRightRelease(5, 5)
199      norm! r2
200
201      if visual_mode ==# 'v'
202        call assert_equal(['-------',
203              \            '-111111',
204              \            '1122222',
205              \            '2222222',
206              \            '2222211',
207              \            '111111-',
208              \            '-------'], getline(1, '$'), msg)
209      elseif visual_mode ==# 'V'
210        call assert_equal(['-------',
211              \            '1111111',
212              \            '2222222',
213              \            '2222222',
214              \            '2222222',
215              \            '1111111',
216              \            '-------'], getline(1, '$'), msg)
217      else
218        call assert_equal(['-------',
219              \            '-11111-',
220              \            '-12221-',
221              \            '-12221-',
222              \            '-12221-',
223              \            '-11111-',
224              \            '-------'], getline(1, '$'), msg)
225      endif
226    endfor
227  endfor
228
229  let &mouse = save_mouse
230  let &term = save_term
231  let &ttymouse = save_ttymouse
232  call test_override('no_query_mouse', 0)
233  bwipe!
234endfunc
235
236" Test that <C-LeftMouse> jumps to help tag and <C-RightMouse> jumps back.
237func Test_xterm_mouse_ctrl_click()
238  let save_mouse = &mouse
239  let save_term = &term
240  let save_ttymouse = &ttymouse
241  set mouse=a term=xterm
242
243  for ttymouse_val in s:ttymouse_values
244    let msg = 'ttymouse=' .. ttymouse_val
245    exe 'set ttymouse=' .. ttymouse_val
246    help
247    /usr_02.txt
248    norm! zt
249    let row = 1
250    let col = 1
251    call MouseCtrlLeftClick(row, col)
252    call MouseLeftRelease(row, col)
253    call assert_match('usr_02.txt$', bufname('%'), msg)
254    call assert_equal('*usr_02.txt*', expand('<cWORD>'), msg)
255
256    call MouseCtrlRightClick(row, col)
257    call MouseRightRelease(row, col)
258    call assert_match('help.txt$', bufname('%'), msg)
259    call assert_equal('|usr_02.txt|', expand('<cWORD>'), msg)
260
261    helpclose
262  endfor
263
264  let &mouse = save_mouse
265  let &term = save_term
266  let &ttymouse = save_ttymouse
267endfunc
268
269func Test_term_mouse_middle_click()
270  CheckFeature clipboard_working
271
272  new
273  let save_mouse = &mouse
274  let save_term = &term
275  let save_ttymouse = &ttymouse
276  call test_override('no_query_mouse', 1)
277  let save_quotestar = @*
278  let @* = 'abc'
279  set mouse=a term=xterm
280
281  for ttymouse_val in s:ttymouse_values + s:ttymouse_dec
282    let msg = 'ttymouse=' .. ttymouse_val
283    exe 'set ttymouse=' .. ttymouse_val
284    call setline(1, ['123456789', '123456789'])
285
286    " Middle-click in the middle of the line pastes text where clicked.
287    let row = 1
288    let col = 6
289    call MouseMiddleClick(row, col)
290    call MouseMiddleRelease(row, col)
291    call assert_equal(['12345abc6789', '123456789'], getline(1, '$'), msg)
292
293    " Middle-click beyond end of the line pastes text at the end of the line.
294    let col = 20
295    call MouseMiddleClick(row, col)
296    call MouseMiddleRelease(row, col)
297    call assert_equal(['12345abc6789abc', '123456789'], getline(1, '$'), msg)
298
299    " Middle-click beyond the last line pastes in the last line.
300    let row = 5
301    let col = 3
302    call MouseMiddleClick(row, col)
303    call MouseMiddleRelease(row, col)
304    call assert_equal(['12345abc6789abc', '12abc3456789'], getline(1, '$'), msg)
305  endfor
306
307  let &mouse = save_mouse
308  let &term = save_term
309  let &ttymouse = save_ttymouse
310  call test_override('no_query_mouse', 0)
311  let @* = save_quotestar
312  bwipe!
313endfunc
314
315" TODO: for unclear reasons this test fails if it comes after
316" Test_xterm_mouse_ctrl_click()
317func Test_1xterm_mouse_wheel()
318  new
319  let save_mouse = &mouse
320  let save_term = &term
321  let save_ttymouse = &ttymouse
322  set mouse=a term=xterm
323  call setline(1, range(1, 100))
324
325  for ttymouse_val in s:ttymouse_values
326    let msg = 'ttymouse=' .. ttymouse_val
327    exe 'set ttymouse=' .. ttymouse_val
328    go
329    call assert_equal(1, line('w0'), msg)
330    call assert_equal([0, 1, 1, 0], getpos('.'), msg)
331
332    call MouseWheelDown(1, 1)
333    call assert_equal(4, line('w0'), msg)
334    call assert_equal([0, 4, 1, 0], getpos('.'), msg)
335
336    call MouseWheelDown(1, 1)
337    call assert_equal(7, line('w0'), msg)
338    call assert_equal([0, 7, 1, 0], getpos('.'), msg)
339
340    call MouseWheelUp(1, 1)
341    call assert_equal(4, line('w0'), msg)
342    call assert_equal([0, 7, 1, 0], getpos('.'), msg)
343
344    call MouseWheelUp(1, 1)
345    call assert_equal(1, line('w0'), msg)
346    call assert_equal([0, 7, 1, 0], getpos('.'), msg)
347  endfor
348
349  let &mouse = save_mouse
350  let &term = save_term
351  let &ttymouse = save_ttymouse
352  bwipe!
353endfunc
354
355" Test that dragging beyond the window (at the bottom and at the top)
356" scrolls window content by the number of of lines beyond the window.
357func Test_term_mouse_drag_beyond_window()
358  let save_mouse = &mouse
359  let save_term = &term
360  let save_ttymouse = &ttymouse
361  call test_override('no_query_mouse', 1)
362  set mouse=a term=xterm
363  let col = 1
364  call setline(1, range(1, 100))
365
366  " Split into 3 windows, and go into the middle window
367  " so we test dragging mouse below and above the window.
368  2split
369  wincmd j
370  2split
371
372  for ttymouse_val in s:ttymouse_values + s:ttymouse_dec
373    let msg = 'ttymouse=' .. ttymouse_val
374    exe 'set ttymouse=' .. ttymouse_val
375
376    " Line #10 at the top.
377    norm! 10zt
378    redraw
379    call assert_equal(10, winsaveview().topline, msg)
380    call assert_equal(2, winheight(0), msg)
381
382    let row = 4
383    call MouseLeftClick(row, col)
384    call assert_equal(10, winsaveview().topline, msg)
385
386    " Drag downwards. We're still in the window so topline should
387    " not change yet.
388    let row += 1
389    call MouseLeftDrag(row, col)
390    call assert_equal(10, winsaveview().topline, msg)
391
392    " We now leave the window at the bottom, so the window content should
393    " scroll by 1 line, then 2 lines (etc) as we drag further away.
394    let row += 1
395    call MouseLeftDrag(row, col)
396    call assert_equal(11, winsaveview().topline, msg)
397
398    let row += 1
399    call MouseLeftDrag(row, col)
400    call assert_equal(13, winsaveview().topline, msg)
401
402    " Now drag upwards.
403    let row -= 1
404    call MouseLeftDrag(row, col)
405    call assert_equal(14, winsaveview().topline, msg)
406
407    " We're now back in the window so the topline should not change.
408    let row -= 1
409    call MouseLeftDrag(row, col)
410    call assert_equal(14, winsaveview().topline, msg)
411
412    let row -= 1
413    call MouseLeftDrag(row, col)
414    call assert_equal(14, winsaveview().topline, msg)
415
416    " We now leave the window at the top so the window content should
417    " scroll by 1 line, then 2, then 3 (etc) in the opposite direction.
418    let row -= 1
419    call MouseLeftDrag(row, col)
420    call assert_equal(13, winsaveview().topline, msg)
421
422    let row -= 1
423    call MouseLeftDrag(row, col)
424    call assert_equal(11, winsaveview().topline, msg)
425
426    let row -= 1
427    call MouseLeftDrag(row, col)
428    call assert_equal(8, winsaveview().topline, msg)
429
430    call MouseLeftRelease(row, col)
431    call assert_equal(8, winsaveview().topline, msg)
432    call assert_equal(2, winheight(0), msg)
433  endfor
434
435  let &mouse = save_mouse
436  let &term = save_term
437  let &ttymouse = save_ttymouse
438  call test_override('no_query_mouse', 0)
439  bwipe!
440endfunc
441
442func Test_term_mouse_drag_window_separator()
443  let save_mouse = &mouse
444  let save_term = &term
445  let save_ttymouse = &ttymouse
446  call test_override('no_query_mouse', 1)
447  set mouse=a term=xterm
448
449  for ttymouse_val in s:ttymouse_values + s:ttymouse_dec
450    let msg = 'ttymouse=' .. ttymouse_val
451    exe 'set ttymouse=' .. ttymouse_val
452
453    " Split horizontally and test dragging the horizontal window separator.
454    split
455    let rowseparator = winheight(0) + 1
456    let row = rowseparator
457    let col = 1
458
459    " When 'ttymouse' is 'xterm2', row/col bigger than 223 are not supported.
460    if ttymouse_val !=# 'xterm2' || row <= 223
461      call MouseLeftClick(row, col)
462      let row -= 1
463      call MouseLeftDrag(row, col)
464      call assert_equal(rowseparator - 1, winheight(0) + 1, msg)
465      let row += 1
466      call MouseLeftDrag(row, col)
467      call assert_equal(rowseparator, winheight(0) + 1, msg)
468      call MouseLeftRelease(row, col)
469      call assert_equal(rowseparator, winheight(0) + 1, msg)
470    endif
471    bwipe!
472
473    " Split vertically and test dragging the vertical window separator.
474    vsplit
475    let colseparator = winwidth(0) + 1
476    let row = 1
477    let col = colseparator
478
479    " When 'ttymouse' is 'xterm2', row/col bigger than 223 are not supported.
480    if ttymouse_val !=# 'xterm2' || col <= 223
481      call MouseLeftClick(row, col)
482      let col -= 1
483      call MouseLeftDrag(row, col)
484      call assert_equal(colseparator - 1, winwidth(0) + 1, msg)
485      let col += 1
486      call MouseLeftDrag(row, col)
487      call assert_equal(colseparator, winwidth(0) + 1, msg)
488      call MouseLeftRelease(row, col)
489      call assert_equal(colseparator, winwidth(0) + 1, msg)
490    endif
491    bwipe!
492  endfor
493
494  let &mouse = save_mouse
495  let &term = save_term
496  let &ttymouse = save_ttymouse
497  call test_override('no_query_mouse', 0)
498endfunc
499
500func Test_term_mouse_drag_statusline()
501  let save_mouse = &mouse
502  let save_term = &term
503  let save_ttymouse = &ttymouse
504  call test_override('no_query_mouse', 1)
505  let save_laststatus = &laststatus
506  set mouse=a term=xterm laststatus=2
507
508  for ttymouse_val in s:ttymouse_values + s:ttymouse_dec
509    let msg = 'ttymouse=' .. ttymouse_val
510    exe 'set ttymouse=' .. ttymouse_val
511
512    call assert_equal(1, &cmdheight, msg)
513    let rowstatusline = winheight(0) + 1
514    let row = rowstatusline
515    let col = 1
516
517    if ttymouse_val ==# 'xterm2' && row > 223
518      " When 'ttymouse' is 'xterm2', row/col bigger than 223 are not supported.
519      continue
520    endif
521
522    call MouseLeftClick(row, col)
523    let row -= 1
524    call MouseLeftDrag(row, col)
525    call assert_equal(2, &cmdheight, msg)
526    call assert_equal(rowstatusline - 1, winheight(0) + 1, msg)
527    let row += 1
528    call MouseLeftDrag(row, col)
529    call assert_equal(1, &cmdheight, msg)
530    call assert_equal(rowstatusline, winheight(0) + 1, msg)
531    call MouseLeftRelease(row, col)
532    call assert_equal(1, &cmdheight, msg)
533    call assert_equal(rowstatusline, winheight(0) + 1, msg)
534  endfor
535
536  let &mouse = save_mouse
537  let &term = save_term
538  let &ttymouse = save_ttymouse
539  call test_override('no_query_mouse', 0)
540  let &laststatus = save_laststatus
541endfunc
542
543func Test_term_mouse_click_tab()
544  let save_mouse = &mouse
545  let save_term = &term
546  let save_ttymouse = &ttymouse
547  call test_override('no_query_mouse', 1)
548  set mouse=a term=xterm
549  let row = 1
550
551  for ttymouse_val in s:ttymouse_values + s:ttymouse_dec + s:ttymouse_netterm
552    let msg = 'ttymouse=' .. ttymouse_val
553    exe 'set ttymouse=' .. ttymouse_val
554    e Xfoo
555    tabnew Xbar
556
557    let a = split(execute(':tabs'), "\n")
558    call assert_equal(['Tab page 1',
559        \              '    Xfoo',
560        \              'Tab page 2',
561        \              '>   Xbar'], a, msg)
562
563    " Test clicking on tab names in the tabline at the top.
564    let col = 2
565    redraw
566    call MouseLeftClick(row, col)
567    call MouseLeftRelease(row, col)
568    let a = split(execute(':tabs'), "\n")
569    call assert_equal(['Tab page 1',
570        \              '>   Xfoo',
571        \              'Tab page 2',
572        \              '    Xbar'], a, msg)
573
574    let col = 9
575    call MouseLeftClick(row, col)
576    call MouseLeftRelease(row, col)
577    let a = split(execute(':tabs'), "\n")
578    call assert_equal(['Tab page 1',
579        \              '    Xfoo',
580        \              'Tab page 2',
581        \              '>   Xbar'], a, msg)
582
583    %bwipe!
584  endfor
585
586  let &mouse = save_mouse
587  let &term = save_term
588  let &ttymouse = save_ttymouse
589  call test_override('no_query_mouse', 0)
590endfunc
591
592func Test_term_mouse_click_X_to_close_tab()
593  let save_mouse = &mouse
594  let save_term = &term
595  let save_ttymouse = &ttymouse
596  call test_override('no_query_mouse', 1)
597  set mouse=a term=xterm
598  let row = 1
599  let col = &columns
600
601  for ttymouse_val in s:ttymouse_values + s:ttymouse_dec + s:ttymouse_netterm
602    if ttymouse_val ==# 'xterm2' && col > 223
603      " When 'ttymouse' is 'xterm2', row/col bigger than 223 are not supported.
604      continue
605    endif
606    let msg = 'ttymouse=' .. ttymouse_val
607    exe 'set ttymouse=' .. ttymouse_val
608    e Xtab1
609    tabnew Xtab2
610    tabnew Xtab3
611    tabn 2
612
613    let a = split(execute(':tabs'), "\n")
614    call assert_equal(['Tab page 1',
615        \              '    Xtab1',
616        \              'Tab page 2',
617        \              '>   Xtab2',
618        \              'Tab page 3',
619        \              '    Xtab3'], a, msg)
620
621    " Click on "X" in tabline to close current tab i.e. Xtab2.
622    redraw
623    call MouseLeftClick(row, col)
624    call MouseLeftRelease(row, col)
625    let a = split(execute(':tabs'), "\n")
626    call assert_equal(['Tab page 1',
627        \              '    Xtab1',
628        \              'Tab page 2',
629        \              '>   Xtab3'], a, msg)
630
631    %bwipe!
632  endfor
633
634  let &mouse = save_mouse
635  let &term = save_term
636  let &ttymouse = save_ttymouse
637  call test_override('no_query_mouse', 0)
638endfunc
639
640func Test_term_mouse_drag_to_move_tab()
641  let save_mouse = &mouse
642  let save_term = &term
643  let save_ttymouse = &ttymouse
644  call test_override('no_query_mouse', 1)
645  " Set 'mousetime' to 1 to avoid recognizing a double-click in the loop
646  set mouse=a term=xterm mousetime=1
647  let row = 1
648
649  for ttymouse_val in s:ttymouse_values + s:ttymouse_dec
650    let msg = 'ttymouse=' .. ttymouse_val
651    exe 'set ttymouse=' .. ttymouse_val
652    e Xtab1
653    tabnew Xtab2
654
655    let a = split(execute(':tabs'), "\n")
656    call assert_equal(['Tab page 1',
657        \              '    Xtab1',
658        \              'Tab page 2',
659        \              '>   Xtab2'], a, msg)
660    redraw
661
662    " Click in tab2 and drag it to tab1.
663    " Check getcharmod() to verify that click is not
664    " interpreted as a spurious double-click.
665    call MouseLeftClick(row, 10)
666    call assert_equal(0, getcharmod(), msg)
667    for col in [9, 8, 7, 6]
668      call MouseLeftDrag(row, col)
669    endfor
670    call MouseLeftRelease(row, col)
671    let a = split(execute(':tabs'), "\n")
672    call assert_equal(['Tab page 1',
673        \              '>   Xtab2',
674        \              'Tab page 2',
675        \              '    Xtab1'], a, msg)
676
677    " brief sleep to avoid causing a double-click
678    sleep 20m
679    %bwipe!
680  endfor
681
682  let &mouse = save_mouse
683  let &term = save_term
684  let &ttymouse = save_ttymouse
685  call test_override('no_query_mouse', 0)
686  set mousetime&
687endfunc
688
689func Test_term_mouse_double_click_to_create_tab()
690  let save_mouse = &mouse
691  let save_term = &term
692  let save_ttymouse = &ttymouse
693  call test_override('no_query_mouse', 1)
694  " Set 'mousetime' to a small value, so that double-click works but we don't
695  " have to wait long to avoid a triple-click.
696  set mouse=a term=xterm mousetime=100
697  let row = 1
698  let col = 10
699
700  let round = 0
701  for ttymouse_val in s:ttymouse_values + s:ttymouse_dec
702    let msg = 'ttymouse=' .. ttymouse_val
703    exe 'set ttymouse=' .. ttymouse_val
704    e Xtab1
705    tabnew Xtab2
706
707    if round > 0
708      " We need to sleep, or else the first MouseLeftClick() will be
709      " interpreted as a spurious triple-click.
710      sleep 100m
711    endif
712    let round += 1
713
714    let a = split(execute(':tabs'), "\n")
715    call assert_equal(['Tab page 1',
716        \              '    Xtab1',
717        \              'Tab page 2',
718        \              '>   Xtab2'], a, msg)
719
720    redraw
721    call MouseLeftClick(row, col)
722    " Check getcharmod() to verify that first click is not
723    " interpreted as a spurious double-click.
724    call assert_equal(0, getcharmod(), msg)
725    call MouseLeftRelease(row, col)
726    call MouseLeftClick(row, col)
727    call assert_equal(32, getcharmod(), msg) " double-click
728    call MouseLeftRelease(row, col)
729    let a = split(execute(':tabs'), "\n")
730    call assert_equal(['Tab page 1',
731        \              '    Xtab1',
732        \              'Tab page 2',
733        \              '>   [No Name]',
734        \              'Tab page 3',
735        \              '    Xtab2'], a, msg)
736
737    %bwipe!
738  endfor
739
740  let &mouse = save_mouse
741  let &term = save_term
742  let &ttymouse = save_ttymouse
743  call test_override('no_query_mouse', 0)
744  set mousetime&
745endfunc
746
747func Test_xterm_mouse_click_in_fold_columns()
748  new
749  let save_mouse = &mouse
750  let save_term = &term
751  let save_ttymouse = &ttymouse
752  let save_foldcolumn = &foldcolumn
753  set mouse=a term=xterm foldcolumn=3 ttymouse=xterm2
754
755  " Create 2 nested folds.
756  call setline(1, range(1, 7))
757  2,6fold
758  norm! zR
759  4,5fold
760  call assert_equal([-1, -1, -1, 4, 4, -1, -1],
761        \           map(range(1, 7), 'foldclosed(v:val)'))
762
763  " Click in "+" of inner fold in foldcolumn should open it.
764  redraw
765  let row = 4
766  let col = 2
767  call MouseLeftClick(row, col)
768  call MouseLeftRelease(row, col)
769  call assert_equal([-1, -1, -1, -1, -1, -1, -1],
770        \           map(range(1, 7), 'foldclosed(v:val)'))
771
772  " Click in "-" of outer fold in foldcolumn should close it.
773  redraw
774  let row = 2
775  let col = 1
776  call MouseLeftClick(row, col)
777  call MouseLeftRelease(row, col)
778  call assert_equal([-1, 2, 2, 2, 2, 2, -1],
779        \           map(range(1, 7), 'foldclosed(v:val)'))
780  norm! zR
781
782  " Click in "|" of inner fold in foldcolumn should close it.
783  redraw
784  let row = 5
785  let col = 2
786  call MouseLeftClick(row, col)
787  call MouseLeftRelease(row, col)
788  call assert_equal([-1, -1, -1, 4, 4, -1, -1],
789        \           map(range(1, 7), 'foldclosed(v:val)'))
790
791  let &foldcolumn = save_foldcolumn
792  let &ttymouse = save_ttymouse
793  let &term = save_term
794  let &mouse = save_mouse
795  bwipe!
796endfunc
797
798" This only checks if the sequence is recognized.
799func Test_term_rgb_response()
800  set t_RF=x
801  set t_RB=y
802
803  " response to t_RF, 4 digits
804  let red = 0x12
805  let green = 0x34
806  let blue = 0x56
807  let seq = printf("\<Esc>]10;rgb:%02x00/%02x00/%02x00\x07", red, green, blue)
808  call feedkeys(seq, 'Lx!')
809  call assert_equal(seq, v:termrfgresp)
810
811  " response to t_RF, 2 digits
812  let red = 0x78
813  let green = 0x9a
814  let blue = 0xbc
815  let seq = printf("\<Esc>]10;rgb:%02x/%02x/%02x\x07", red, green, blue)
816  call feedkeys(seq, 'Lx!')
817  call assert_equal(seq, v:termrfgresp)
818
819  " response to t_RB, 4 digits, dark
820  set background=light
821  eval 'background'->test_option_not_set()
822  let red = 0x29
823  let green = 0x4a
824  let blue = 0x6b
825  let seq = printf("\<Esc>]11;rgb:%02x00/%02x00/%02x00\x07", red, green, blue)
826  call feedkeys(seq, 'Lx!')
827  call assert_equal(seq, v:termrbgresp)
828  call assert_equal('dark', &background)
829
830  " response to t_RB, 4 digits, light
831  set background=dark
832  call test_option_not_set('background')
833  let red = 0x81
834  let green = 0x63
835  let blue = 0x65
836  let seq = printf("\<Esc>]11;rgb:%02x00/%02x00/%02x00\x07", red, green, blue)
837  call feedkeys(seq, 'Lx!')
838  call assert_equal(seq, v:termrbgresp)
839  call assert_equal('light', &background)
840
841  " response to t_RB, 2 digits, dark
842  set background=light
843  call test_option_not_set('background')
844  let red = 0x47
845  let green = 0x59
846  let blue = 0x5b
847  let seq = printf("\<Esc>]11;rgb:%02x/%02x/%02x\x07", red, green, blue)
848  call feedkeys(seq, 'Lx!')
849  call assert_equal(seq, v:termrbgresp)
850  call assert_equal('dark', &background)
851
852  " response to t_RB, 2 digits, light
853  set background=dark
854  call test_option_not_set('background')
855  let red = 0x83
856  let green = 0xa4
857  let blue = 0xc2
858  let seq = printf("\<Esc>]11;rgb:%02x/%02x/%02x\x07", red, green, blue)
859  call feedkeys(seq, 'Lx!')
860  call assert_equal(seq, v:termrbgresp)
861  call assert_equal('light', &background)
862
863  set t_RF= t_RB=
864endfunc
865
866" This only checks if the sequence is recognized.
867" This must be after other tests, because it has side effects to xterm
868" properties.
869func Test_xx01_term_style_response()
870  " Termresponse is only parsed when t_RV is not empty.
871  set t_RV=x
872
873  " send the termresponse to trigger requesting the XT codes
874  let seq = "\<Esc>[>41;337;0c"
875  call feedkeys(seq, 'Lx!')
876  call assert_equal(seq, v:termresponse)
877
878  let seq = "\<Esc>P1$r2 q\<Esc>\\"
879  call feedkeys(seq, 'Lx!')
880  call assert_equal(seq, v:termstyleresp)
881
882  set t_RV=
883endfunc
884
885" This checks the libvterm version response.
886" This must be after other tests, because it has side effects to xterm
887" properties.
888func Test_xx02_libvterm_response()
889  " Termresponse is only parsed when t_RV is not empty.
890  set t_RV=x
891
892  set ttymouse=xterm
893  call test_option_not_set('ttymouse')
894  let seq = "\<Esc>[>0;100;0c"
895  call feedkeys(seq, 'Lx!')
896  call assert_equal(seq, v:termresponse)
897  call assert_equal('sgr', &ttymouse)
898
899  set t_RV=
900endfunc
901
902" This checks the xterm version response.
903" This must be after other tests, because it has side effects to xterm
904" properties.
905func Test_xx03_xterm_response()
906  " Termresponse is only parsed when t_RV is not empty.
907  set t_RV=x
908
909  " xterm < 95: "xterm" (actually unmodified)
910  set ttymouse=xterm
911  call test_option_not_set('ttymouse')
912  let seq = "\<Esc>[>0;94;0c"
913  call feedkeys(seq, 'Lx!')
914  call assert_equal(seq, v:termresponse)
915  call assert_equal('xterm', &ttymouse)
916
917  " xterm >= 95 < 277 "xterm2"
918  set ttymouse=xterm
919  call test_option_not_set('ttymouse')
920  let seq = "\<Esc>[>0;267;0c"
921  call feedkeys(seq, 'Lx!')
922  call assert_equal(seq, v:termresponse)
923  call assert_equal('xterm2', &ttymouse)
924
925  " xterm >= 277: "sgr"
926  set ttymouse=xterm
927  call test_option_not_set('ttymouse')
928  let seq = "\<Esc>[>0;277;0c"
929  call feedkeys(seq, 'Lx!')
930  call assert_equal(seq, v:termresponse)
931  call assert_equal('sgr', &ttymouse)
932
933  set t_RV=
934endfunc
935
936" TODO: check other terminals response
937
938func Test_get_termcode()
939  try
940    let k1 = &t_k1
941  catch /E113/
942    throw 'Skipped: Unable to query termcodes'
943  endtry
944  set t_k1=
945  set t_k1&
946  call assert_equal(k1, &t_k1)
947
948  " use external termcap first
949  set nottybuiltin
950  set t_k1=
951  set t_k1&
952  " when using external termcap may get something else, but it must not be
953  " empty, since we would fallback to the builtin one.
954  call assert_notequal('', &t_k1)
955
956  if &term =~ 'xterm'
957    " use internal termcap first
958    let term_save = &term
959    let &term = 'builtin_' .. &term
960    set t_k1=
961    set t_k1&
962    call assert_equal(k1, &t_k1)
963    let &term = term_save
964  endif
965
966  set ttybuiltin
967endfunc
968
969func GetEscCodeCSI27(key, modifier)
970  let key = printf("%d", char2nr(a:key))
971  let mod = printf("%d", a:modifier)
972  return "\<Esc>[27;" .. mod .. ';' .. key .. '~'
973endfunc
974
975func GetEscCodeCSIu(key, modifier)
976  let key = printf("%d", char2nr(a:key))
977  let mod = printf("%d", a:modifier)
978  return "\<Esc>[" .. key .. ';' .. mod .. 'u'
979endfunc
980
981" This checks the CSI sequences when in modifyOtherKeys mode.
982" The mode doesn't need to be enabled, the codes are always detected.
983func RunTest_modifyOtherKeys(func)
984  new
985  set timeoutlen=10
986
987  " Shift-X is send as 'X' with the shift modifier
988  call feedkeys('a' .. a:func('X', 2) .. "\<Esc>", 'Lx!')
989  call assert_equal('X', getline(1))
990
991  " Ctrl-i is Tab
992  call setline(1, '')
993  call feedkeys('a' .. a:func('i', 5) .. "\<Esc>", 'Lx!')
994  call assert_equal("\t", getline(1))
995
996  " Ctrl-I is also Tab
997  call setline(1, '')
998  call feedkeys('a' .. a:func('I', 5) .. "\<Esc>", 'Lx!')
999  call assert_equal("\t", getline(1))
1000
1001  " Alt-x is ø
1002  call setline(1, '')
1003  call feedkeys('a' .. a:func('x', 3) .. "\<Esc>", 'Lx!')
1004  call assert_equal("ø", getline(1))
1005
1006  " Meta-x is also ø
1007  call setline(1, '')
1008  call feedkeys('a' .. a:func('x', 9) .. "\<Esc>", 'Lx!')
1009  call assert_equal("ø", getline(1))
1010
1011  " Alt-X is Ø
1012  call setline(1, '')
1013  call feedkeys('a' .. a:func('X', 3) .. "\<Esc>", 'Lx!')
1014  call assert_equal("Ø", getline(1))
1015
1016  " Meta-X is ø
1017  call setline(1, '')
1018  call feedkeys('a' .. a:func('X', 9) .. "\<Esc>", 'Lx!')
1019  call assert_equal("Ø", getline(1))
1020
1021  bwipe!
1022  set timeoutlen&
1023endfunc
1024
1025func Test_modifyOtherKeys_basic()
1026  call RunTest_modifyOtherKeys(function('GetEscCodeCSI27'))
1027  call RunTest_modifyOtherKeys(function('GetEscCodeCSIu'))
1028endfunc
1029
1030func RunTest_mapping_shift(key, func)
1031  call setline(1, '')
1032  if a:key == '|'
1033    exe 'inoremap \| xyz'
1034  else
1035    exe 'inoremap ' .. a:key .. ' xyz'
1036  endif
1037  call feedkeys('a' .. a:func(a:key, 2) .. "\<Esc>", 'Lx!')
1038  call assert_equal("xyz", getline(1))
1039  if a:key == '|'
1040    exe 'iunmap \|'
1041  else
1042    exe 'iunmap ' .. a:key
1043  endif
1044endfunc
1045
1046func RunTest_mapping_works_with_shift(func)
1047  new
1048  set timeoutlen=10
1049
1050  call RunTest_mapping_shift('@', a:func)
1051  call RunTest_mapping_shift('A', a:func)
1052  call RunTest_mapping_shift('Z', a:func)
1053  call RunTest_mapping_shift('^', a:func)
1054  call RunTest_mapping_shift('_', a:func)
1055  call RunTest_mapping_shift('{', a:func)
1056  call RunTest_mapping_shift('|', a:func)
1057  call RunTest_mapping_shift('}', a:func)
1058  call RunTest_mapping_shift('~', a:func)
1059
1060  bwipe!
1061  set timeoutlen&
1062endfunc
1063
1064func Test_mapping_works_with_shift_plain()
1065  call RunTest_mapping_works_with_shift(function('GetEscCodeCSI27'))
1066  call RunTest_mapping_works_with_shift(function('GetEscCodeCSIu'))
1067endfunc
1068
1069func RunTest_mapping_mods(map, key, func, code)
1070  call setline(1, '')
1071  exe 'inoremap ' .. a:map .. ' xyz'
1072  call feedkeys('a' .. a:func(a:key, a:code) .. "\<Esc>", 'Lx!')
1073  call assert_equal("xyz", getline(1))
1074  exe 'iunmap ' .. a:map
1075endfunc
1076
1077func RunTest_mapping_works_with_mods(func, mods, code)
1078  new
1079  set timeoutlen=10
1080
1081  if a:mods !~ 'S'
1082    " Shift by itself has no effect
1083    call RunTest_mapping_mods('<' .. a:mods .. '-@>', '@', a:func, a:code)
1084  endif
1085  call RunTest_mapping_mods('<' .. a:mods .. '-A>', 'A', a:func, a:code)
1086  call RunTest_mapping_mods('<' .. a:mods .. '-Z>', 'Z', a:func, a:code)
1087  if a:mods !~ 'S'
1088    " with Shift code is always upper case
1089    call RunTest_mapping_mods('<' .. a:mods .. '-a>', 'a', a:func, a:code)
1090    call RunTest_mapping_mods('<' .. a:mods .. '-z>', 'z', a:func, a:code)
1091  endif
1092  if a:mods != 'A'
1093    " with Alt code is not in upper case
1094    call RunTest_mapping_mods('<' .. a:mods .. '-a>', 'A', a:func, a:code)
1095    call RunTest_mapping_mods('<' .. a:mods .. '-z>', 'Z', a:func, a:code)
1096  endif
1097  call RunTest_mapping_mods('<' .. a:mods .. '-á>', 'á', a:func, a:code)
1098  if a:mods !~ 'S'
1099    " Shift by itself has no effect
1100    call RunTest_mapping_mods('<' .. a:mods .. '-^>', '^', a:func, a:code)
1101    call RunTest_mapping_mods('<' .. a:mods .. '-_>', '_', a:func, a:code)
1102    call RunTest_mapping_mods('<' .. a:mods .. '-{>', '{', a:func, a:code)
1103    call RunTest_mapping_mods('<' .. a:mods .. '-\|>', '|', a:func, a:code)
1104    call RunTest_mapping_mods('<' .. a:mods .. '-}>', '}', a:func, a:code)
1105    call RunTest_mapping_mods('<' .. a:mods .. '-~>', '~', a:func, a:code)
1106  endif
1107
1108  bwipe!
1109  set timeoutlen&
1110endfunc
1111
1112func Test_mapping_works_with_shift()
1113  call RunTest_mapping_works_with_mods(function('GetEscCodeCSI27'), 'S', 2)
1114  call RunTest_mapping_works_with_mods(function('GetEscCodeCSIu'), 'S', 2)
1115endfunc
1116
1117func Test_mapping_works_with_ctrl()
1118  call RunTest_mapping_works_with_mods(function('GetEscCodeCSI27'), 'C', 5)
1119  call RunTest_mapping_works_with_mods(function('GetEscCodeCSIu'), 'C', 5)
1120endfunc
1121
1122func Test_mapping_works_with_shift_ctrl()
1123  call RunTest_mapping_works_with_mods(function('GetEscCodeCSI27'), 'C-S', 6)
1124  call RunTest_mapping_works_with_mods(function('GetEscCodeCSIu'), 'C-S', 6)
1125endfunc
1126
1127" Below we also test the "u" code with Alt, This works, but libvterm would not
1128" send the Alt key like this but by prefixing an Esc.
1129
1130func Test_mapping_works_with_alt()
1131  call RunTest_mapping_works_with_mods(function('GetEscCodeCSI27'), 'A', 3)
1132  call RunTest_mapping_works_with_mods(function('GetEscCodeCSIu'), 'A', 3)
1133endfunc
1134
1135func Test_mapping_works_with_shift_alt()
1136  call RunTest_mapping_works_with_mods(function('GetEscCodeCSI27'), 'S-A', 4)
1137  call RunTest_mapping_works_with_mods(function('GetEscCodeCSIu'), 'S-A', 4)
1138endfunc
1139
1140func Test_mapping_works_with_ctrl_alt()
1141  call RunTest_mapping_works_with_mods(function('GetEscCodeCSI27'), 'C-A', 7)
1142  call RunTest_mapping_works_with_mods(function('GetEscCodeCSIu'), 'C-A', 7)
1143endfunc
1144
1145func Test_mapping_works_with_shift_ctrl_alt()
1146  call RunTest_mapping_works_with_mods(function('GetEscCodeCSI27'), 'C-S-A', 8)
1147  call RunTest_mapping_works_with_mods(function('GetEscCodeCSIu'), 'C-S-A', 8)
1148endfunc
1149