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 iTerm2 version response.
886" This must be after other tests, because it has side effects to xterm
887" properties.
888func Test_xx02_iTerm2_response()
889  " Termresponse is only parsed when t_RV is not empty.
890  set t_RV=x
891
892  " Old versions of iTerm2 used a different style term response.
893  set ttymouse=xterm
894  call test_option_not_set('ttymouse')
895  let seq = "\<Esc>[>0;95;c"
896  call feedkeys(seq, 'Lx!')
897  call assert_equal(seq, v:termresponse)
898  call assert_equal('xterm', &ttymouse)
899
900  set ttymouse=xterm
901  call test_option_not_set('ttymouse')
902  let seq = "\<Esc>[>0;95;0c"
903  call feedkeys(seq, 'Lx!')
904  call assert_equal(seq, v:termresponse)
905  call assert_equal('sgr', &ttymouse)
906
907  set t_RV=
908endfunc
909
910" This checks the libvterm version response.
911" This must be after other tests, because it has side effects to xterm
912" properties.
913func Test_xx03_libvterm_response()
914  " Termresponse is only parsed when t_RV is not empty.
915  set t_RV=x
916
917  set ttymouse=xterm
918  call test_option_not_set('ttymouse')
919  let seq = "\<Esc>[>0;100;0c"
920  call feedkeys(seq, 'Lx!')
921  call assert_equal(seq, v:termresponse)
922  call assert_equal('sgr', &ttymouse)
923
924  set t_RV=
925endfunc
926
927" This checks the Mac Terminal.app version response.
928" This must be after other tests, because it has side effects to xterm
929" properties.
930func Test_xx04_Mac_Terminal_response()
931  " Termresponse is only parsed when t_RV is not empty.
932  set t_RV=x
933
934  set ttymouse=xterm
935  call test_option_not_set('ttymouse')
936  let seq = "\<Esc>[>1;95;0c"
937  call feedkeys(seq, 'Lx!')
938  call assert_equal(seq, v:termresponse)
939  call assert_equal('sgr', &ttymouse)
940
941  " Reset is_not_xterm and is_mac_terminal.
942  set t_RV=
943  set term=xterm
944  set t_RV=x
945endfunc
946
947" This checks the mintty version response.
948" This must be after other tests, because it has side effects to xterm
949" properties.
950func Test_xx05_mintty_response()
951  " Termresponse is only parsed when t_RV is not empty.
952  set t_RV=x
953
954  set ttymouse=xterm
955  call test_option_not_set('ttymouse')
956  let seq = "\<Esc>[>77;20905;0c"
957  call feedkeys(seq, 'Lx!')
958  call assert_equal(seq, v:termresponse)
959  call assert_equal('sgr', &ttymouse)
960
961  set t_RV=
962endfunc
963
964" This checks the screen version response.
965" This must be after other tests, because it has side effects to xterm
966" properties.
967func Test_xx06_screen_response()
968  " Termresponse is only parsed when t_RV is not empty.
969  set t_RV=x
970
971  " Old versions of screen don't support SGR mouse mode.
972  set ttymouse=xterm
973  call test_option_not_set('ttymouse')
974  let seq = "\<Esc>[>83;40500;0c"
975  call feedkeys(seq, 'Lx!')
976  call assert_equal(seq, v:termresponse)
977  call assert_equal('xterm', &ttymouse)
978
979  " screen supports SGR mouse mode starting in version 4.7.
980  set ttymouse=xterm
981  call test_option_not_set('ttymouse')
982  let seq = "\<Esc>[>83;40700;0c"
983  call feedkeys(seq, 'Lx!')
984  call assert_equal(seq, v:termresponse)
985  call assert_equal('sgr', &ttymouse)
986
987  set t_RV=
988endfunc
989
990" This checks the xterm version response.
991" This must be after other tests, because it has side effects to xterm
992" properties.
993func Test_xx07_xterm_response()
994  " Termresponse is only parsed when t_RV is not empty.
995  set t_RV=x
996
997  " Do Terminal.app first to check that is_mac_terminal is reset.
998  set ttymouse=xterm
999  call test_option_not_set('ttymouse')
1000  let seq = "\<Esc>[>1;95;0c"
1001  call feedkeys(seq, 'Lx!')
1002  call assert_equal(seq, v:termresponse)
1003  call assert_equal('sgr', &ttymouse)
1004
1005  " xterm < 95: "xterm" (actually unmodified)
1006  set t_RV=
1007  set term=xterm
1008  set t_RV=x
1009  set ttymouse=xterm
1010  call test_option_not_set('ttymouse')
1011  let seq = "\<Esc>[>0;94;0c"
1012  call feedkeys(seq, 'Lx!')
1013  call assert_equal(seq, v:termresponse)
1014  call assert_equal('xterm', &ttymouse)
1015
1016  " xterm >= 95 < 277 "xterm2"
1017  set ttymouse=xterm
1018  call test_option_not_set('ttymouse')
1019  let seq = "\<Esc>[>0;267;0c"
1020  call feedkeys(seq, 'Lx!')
1021  call assert_equal(seq, v:termresponse)
1022  call assert_equal('xterm2', &ttymouse)
1023
1024  " xterm >= 277: "sgr"
1025  set ttymouse=xterm
1026  call test_option_not_set('ttymouse')
1027  let seq = "\<Esc>[>0;277;0c"
1028  call feedkeys(seq, 'Lx!')
1029  call assert_equal(seq, v:termresponse)
1030  call assert_equal('sgr', &ttymouse)
1031
1032  set t_RV=
1033endfunc
1034
1035func Test_get_termcode()
1036  try
1037    let k1 = &t_k1
1038  catch /E113/
1039    throw 'Skipped: Unable to query termcodes'
1040  endtry
1041  set t_k1=
1042  set t_k1&
1043  call assert_equal(k1, &t_k1)
1044
1045  " use external termcap first
1046  set nottybuiltin
1047  set t_k1=
1048  set t_k1&
1049  " when using external termcap may get something else, but it must not be
1050  " empty, since we would fallback to the builtin one.
1051  call assert_notequal('', &t_k1)
1052
1053  if &term =~ 'xterm'
1054    " use internal termcap first
1055    let term_save = &term
1056    let &term = 'builtin_' .. &term
1057    set t_k1=
1058    set t_k1&
1059    call assert_equal(k1, &t_k1)
1060    let &term = term_save
1061  endif
1062
1063  set ttybuiltin
1064endfunc
1065
1066func GetEscCodeCSI27(key, modifier)
1067  let key = printf("%d", char2nr(a:key))
1068  let mod = printf("%d", a:modifier)
1069  return "\<Esc>[27;" .. mod .. ';' .. key .. '~'
1070endfunc
1071
1072func GetEscCodeCSIu(key, modifier)
1073  let key = printf("%d", char2nr(a:key))
1074  let mod = printf("%d", a:modifier)
1075  return "\<Esc>[" .. key .. ';' .. mod .. 'u'
1076endfunc
1077
1078" This checks the CSI sequences when in modifyOtherKeys mode.
1079" The mode doesn't need to be enabled, the codes are always detected.
1080func RunTest_modifyOtherKeys(func)
1081  new
1082  set timeoutlen=10
1083
1084  " Shift-X is send as 'X' with the shift modifier
1085  call feedkeys('a' .. a:func('X', 2) .. "\<Esc>", 'Lx!')
1086  call assert_equal('X', getline(1))
1087
1088  " Ctrl-i is Tab
1089  call setline(1, '')
1090  call feedkeys('a' .. a:func('i', 5) .. "\<Esc>", 'Lx!')
1091  call assert_equal("\t", getline(1))
1092
1093  " Ctrl-I is also Tab
1094  call setline(1, '')
1095  call feedkeys('a' .. a:func('I', 5) .. "\<Esc>", 'Lx!')
1096  call assert_equal("\t", getline(1))
1097
1098  " Alt-x is ø
1099  call setline(1, '')
1100  call feedkeys('a' .. a:func('x', 3) .. "\<Esc>", 'Lx!')
1101  call assert_equal("ø", getline(1))
1102
1103  " Meta-x is also ø
1104  call setline(1, '')
1105  call feedkeys('a' .. a:func('x', 9) .. "\<Esc>", 'Lx!')
1106  call assert_equal("ø", getline(1))
1107
1108  " Alt-X is Ø
1109  call setline(1, '')
1110  call feedkeys('a' .. a:func('X', 3) .. "\<Esc>", 'Lx!')
1111  call assert_equal("Ø", getline(1))
1112
1113  " Meta-X is ø
1114  call setline(1, '')
1115  call feedkeys('a' .. a:func('X', 9) .. "\<Esc>", 'Lx!')
1116  call assert_equal("Ø", getline(1))
1117
1118  bwipe!
1119  set timeoutlen&
1120endfunc
1121
1122func Test_modifyOtherKeys_basic()
1123  call RunTest_modifyOtherKeys(function('GetEscCodeCSI27'))
1124  call RunTest_modifyOtherKeys(function('GetEscCodeCSIu'))
1125endfunc
1126
1127func RunTest_mapping_shift(key, func)
1128  call setline(1, '')
1129  if a:key == '|'
1130    exe 'inoremap \| xyz'
1131  else
1132    exe 'inoremap ' .. a:key .. ' xyz'
1133  endif
1134  call feedkeys('a' .. a:func(a:key, 2) .. "\<Esc>", 'Lx!')
1135  call assert_equal("xyz", getline(1))
1136  if a:key == '|'
1137    exe 'iunmap \|'
1138  else
1139    exe 'iunmap ' .. a:key
1140  endif
1141endfunc
1142
1143func RunTest_mapping_works_with_shift(func)
1144  new
1145  set timeoutlen=10
1146
1147  call RunTest_mapping_shift('@', a:func)
1148  call RunTest_mapping_shift('A', a:func)
1149  call RunTest_mapping_shift('Z', a:func)
1150  call RunTest_mapping_shift('^', a:func)
1151  call RunTest_mapping_shift('_', a:func)
1152  call RunTest_mapping_shift('{', a:func)
1153  call RunTest_mapping_shift('|', a:func)
1154  call RunTest_mapping_shift('}', a:func)
1155  call RunTest_mapping_shift('~', a:func)
1156
1157  bwipe!
1158  set timeoutlen&
1159endfunc
1160
1161func Test_mapping_works_with_shift_plain()
1162  call RunTest_mapping_works_with_shift(function('GetEscCodeCSI27'))
1163  call RunTest_mapping_works_with_shift(function('GetEscCodeCSIu'))
1164endfunc
1165
1166func RunTest_mapping_mods(map, key, func, code)
1167  call setline(1, '')
1168  exe 'inoremap ' .. a:map .. ' xyz'
1169  call feedkeys('a' .. a:func(a:key, a:code) .. "\<Esc>", 'Lx!')
1170  call assert_equal("xyz", getline(1))
1171  exe 'iunmap ' .. a:map
1172endfunc
1173
1174func RunTest_mapping_works_with_mods(func, mods, code)
1175  new
1176  set timeoutlen=10
1177
1178  if a:mods !~ 'S'
1179    " Shift by itself has no effect
1180    call RunTest_mapping_mods('<' .. a:mods .. '-@>', '@', a:func, a:code)
1181  endif
1182  call RunTest_mapping_mods('<' .. a:mods .. '-A>', 'A', a:func, a:code)
1183  call RunTest_mapping_mods('<' .. a:mods .. '-Z>', 'Z', a:func, a:code)
1184  if a:mods !~ 'S'
1185    " with Shift code is always upper case
1186    call RunTest_mapping_mods('<' .. a:mods .. '-a>', 'a', a:func, a:code)
1187    call RunTest_mapping_mods('<' .. a:mods .. '-z>', 'z', a:func, a:code)
1188  endif
1189  if a:mods != 'A'
1190    " with Alt code is not in upper case
1191    call RunTest_mapping_mods('<' .. a:mods .. '-a>', 'A', a:func, a:code)
1192    call RunTest_mapping_mods('<' .. a:mods .. '-z>', 'Z', a:func, a:code)
1193  endif
1194  call RunTest_mapping_mods('<' .. a:mods .. '-á>', 'á', a:func, a:code)
1195  if a:mods !~ 'S'
1196    " Shift by itself has no effect
1197    call RunTest_mapping_mods('<' .. a:mods .. '-^>', '^', a:func, a:code)
1198    call RunTest_mapping_mods('<' .. a:mods .. '-_>', '_', a:func, a:code)
1199    call RunTest_mapping_mods('<' .. a:mods .. '-{>', '{', a:func, a:code)
1200    call RunTest_mapping_mods('<' .. a:mods .. '-\|>', '|', a:func, a:code)
1201    call RunTest_mapping_mods('<' .. a:mods .. '-}>', '}', a:func, a:code)
1202    call RunTest_mapping_mods('<' .. a:mods .. '-~>', '~', a:func, a:code)
1203  endif
1204
1205  bwipe!
1206  set timeoutlen&
1207endfunc
1208
1209func Test_mapping_works_with_shift()
1210  call RunTest_mapping_works_with_mods(function('GetEscCodeCSI27'), 'S', 2)
1211  call RunTest_mapping_works_with_mods(function('GetEscCodeCSIu'), 'S', 2)
1212endfunc
1213
1214func Test_mapping_works_with_ctrl()
1215  call RunTest_mapping_works_with_mods(function('GetEscCodeCSI27'), 'C', 5)
1216  call RunTest_mapping_works_with_mods(function('GetEscCodeCSIu'), 'C', 5)
1217endfunc
1218
1219func Test_mapping_works_with_shift_ctrl()
1220  call RunTest_mapping_works_with_mods(function('GetEscCodeCSI27'), 'C-S', 6)
1221  call RunTest_mapping_works_with_mods(function('GetEscCodeCSIu'), 'C-S', 6)
1222endfunc
1223
1224" Below we also test the "u" code with Alt, This works, but libvterm would not
1225" send the Alt key like this but by prefixing an Esc.
1226
1227func Test_mapping_works_with_alt()
1228  call RunTest_mapping_works_with_mods(function('GetEscCodeCSI27'), 'A', 3)
1229  call RunTest_mapping_works_with_mods(function('GetEscCodeCSIu'), 'A', 3)
1230endfunc
1231
1232func Test_mapping_works_with_shift_alt()
1233  call RunTest_mapping_works_with_mods(function('GetEscCodeCSI27'), 'S-A', 4)
1234  call RunTest_mapping_works_with_mods(function('GetEscCodeCSIu'), 'S-A', 4)
1235endfunc
1236
1237func Test_mapping_works_with_ctrl_alt()
1238  call RunTest_mapping_works_with_mods(function('GetEscCodeCSI27'), 'C-A', 7)
1239  call RunTest_mapping_works_with_mods(function('GetEscCodeCSIu'), 'C-A', 7)
1240endfunc
1241
1242func Test_mapping_works_with_shift_ctrl_alt()
1243  call RunTest_mapping_works_with_mods(function('GetEscCodeCSI27'), 'C-S-A', 8)
1244  call RunTest_mapping_works_with_mods(function('GetEscCodeCSIu'), 'C-S-A', 8)
1245endfunc
1246