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    return "\<Esc>[M" .. bytes
42  elseif &ttymouse ==# 'sgr'
43    return printf("\<Esc>[<%d;%d;%d%s", a:code, a:col, a:row, a:m)
44  elseif &ttymouse ==# 'urxvt'
45    return printf("\<Esc>[%d;%d;%dM", a:code + 0x20, a:col, a:row)
46  endif
47endfunc
48
49func DecEscapeCode(code, down, row, col)
50    return printf("\<Esc>[%d;%d;%d;%d&w", a:code, a:down, a:row, a:col)
51endfunc
52
53func NettermEscapeCode(row, col)
54    return printf("\<Esc>}%d,%d\r", a:row, a:col)
55endfunc
56
57func MouseLeftClickCode(row, col)
58  if &ttymouse ==# 'dec'
59    return DecEscapeCode(2, 4, a:row, a:col)
60  elseif &ttymouse ==# 'netterm'
61    return NettermEscapeCode(a:row, a:col)
62  else
63    return TerminalEscapeCode(0, a:row, a:col, 'M')
64  endif
65endfunc
66
67func MouseLeftClick(row, col)
68  call feedkeys(MouseLeftClickCode(a:row, a:col), 'Lx!')
69endfunc
70
71func MouseMiddleClickCode(row, col)
72  if &ttymouse ==# 'dec'
73    return DecEscapeCode(4, 2, a:row, a:col)
74  else
75    return TerminalEscapeCode(1, a:row, a:col, 'M')
76  endif
77endfunc
78
79func MouseMiddleClick(row, col)
80  call feedkeys(MouseMiddleClickCode(a:row, a:col), 'Lx!')
81endfunc
82
83func MouseRightClickCode(row, col)
84  if &ttymouse ==# 'dec'
85    return DecEscapeCode(6, 1, a:row, a:col)
86  else
87    return TerminalEscapeCode(2, a:row, a:col, 'M')
88  endif
89endfunc
90
91func MouseRightClick(row, col)
92  call feedkeys(MouseRightClickCode(a:row, a:col), 'Lx!')
93endfunc
94
95func MouseCtrlLeftClickCode(row, col)
96  let ctrl = 0x10
97  return TerminalEscapeCode(0 + ctrl, a:row, a:col, 'M')
98endfunc
99
100func MouseCtrlLeftClick(row, col)
101  call feedkeys(MouseCtrlLeftClickCode(a:row, a:col), 'Lx!')
102endfunc
103
104func MouseCtrlRightClickCode(row, col)
105  let ctrl = 0x10
106  return TerminalEscapeCode(2 + ctrl, a:row, a:col, 'M')
107endfunc
108
109func MouseCtrlRightClick(row, col)
110  call feedkeys(MouseCtrlRightClickCode(a:row, a:col), 'Lx!')
111endfunc
112
113func MouseLeftReleaseCode(row, col)
114  if &ttymouse ==# 'dec'
115    return DecEscapeCode(3, 0, a:row, a:col)
116  elseif &ttymouse ==# 'netterm'
117    return ''
118  else
119    return TerminalEscapeCode(3, a:row, a:col, 'm')
120  endif
121endfunc
122
123func MouseLeftRelease(row, col)
124  call feedkeys(MouseLeftReleaseCode(a:row, a:col), 'Lx!')
125endfunc
126
127func MouseMiddleReleaseCode(row, col)
128  if &ttymouse ==# 'dec'
129    return DecEscapeCode(5, 0, a:row, a:col)
130  else
131    return TerminalEscapeCode(3, a:row, a:col, 'm')
132  endif
133endfunc
134
135func MouseMiddleRelease(row, col)
136  call feedkeys(MouseMiddleReleaseCode(a:row, a:col), 'Lx!')
137endfunc
138
139func MouseRightReleaseCode(row, col)
140  if &ttymouse ==# 'dec'
141    return DecEscapeCode(7, 0, a:row, a:col)
142  else
143    return TerminalEscapeCode(3, a:row, a:col, 'm')
144  endif
145endfunc
146
147func MouseRightRelease(row, col)
148  call feedkeys(MouseRightReleaseCode(a:row, a:col), 'Lx!')
149endfunc
150
151func MouseLeftDragCode(row, col)
152  if &ttymouse ==# 'dec'
153    return DecEscapeCode(1, 4, a:row, a:col)
154  else
155    return TerminalEscapeCode(0x20, a:row, a:col, 'M')
156  endif
157endfunc
158
159func MouseLeftDrag(row, col)
160  call feedkeys(MouseLeftDragCode(a:row, a:col), 'Lx!')
161endfunc
162
163func MouseWheelUpCode(row, col)
164  return TerminalEscapeCode(0x40, a:row, a:col, 'M')
165endfunc
166
167func MouseWheelUp(row, col)
168  call feedkeys(MouseWheelUpCode(a:row, a:col), 'Lx!')
169endfunc
170
171func MouseWheelDownCode(row, col)
172  return TerminalEscapeCode(0x41, a:row, a:col, 'M')
173endfunc
174
175func MouseWheelDown(row, col)
176  call feedkeys(MouseWheelDownCode(a:row, a:col), 'Lx!')
177endfunc
178
179func Test_term_mouse_left_click()
180  new
181  let save_mouse = &mouse
182  let save_term = &term
183  let save_ttymouse = &ttymouse
184  call test_override('no_query_mouse', 1)
185  set mouse=a term=xterm
186  call setline(1, ['line 1', 'line 2', 'line 3 is a bit longer'])
187
188  for ttymouse_val in s:ttymouse_values + s:ttymouse_dec + s:ttymouse_netterm
189    let msg = 'ttymouse=' .. ttymouse_val
190    exe 'set ttymouse=' .. ttymouse_val
191    go
192    call assert_equal([0, 1, 1, 0], getpos('.'), msg)
193    let row = 2
194    let col = 6
195    call MouseLeftClick(row, col)
196    call MouseLeftRelease(row, col)
197    call assert_equal([0, 2, 6, 0], getpos('.'), msg)
198  endfor
199
200  let &mouse = save_mouse
201  let &term = save_term
202  let &ttymouse = save_ttymouse
203  call test_override('no_query_mouse', 0)
204  bwipe!
205endfunc
206
207func Test_xterm_mouse_right_click_extends_visual()
208  if has('mac')
209    throw "Skipped: test right click in visual mode does not work on macOs (why?)"
210  endif
211  let save_mouse = &mouse
212  let save_term = &term
213  let save_ttymouse = &ttymouse
214  call test_override('no_query_mouse', 1)
215  set mouse=a term=xterm
216
217  for visual_mode in ["v", "V", "\<C-V>"]
218    for ttymouse_val in s:ttymouse_values + s:ttymouse_dec
219      let msg = 'visual=' .. visual_mode .. ' ttymouse=' .. ttymouse_val
220      exe 'set ttymouse=' .. ttymouse_val
221
222      call setline(1, repeat([repeat('-', 7)], 7))
223      call MouseLeftClick(4, 4)
224      call MouseLeftRelease(4, 4)
225      exe  "norm! " .. visual_mode
226
227      " Right click extends top left of visual area.
228      call MouseRightClick(2, 2)
229      call MouseRightRelease(2, 2)
230
231      " Right click extends bottom right of visual area.
232      call MouseRightClick(6, 6)
233      call MouseRightRelease(6, 6)
234      norm! r1gv
235
236      " Right click shrinks top left of visual area.
237      call MouseRightClick(3, 3)
238      call MouseRightRelease(3, 3)
239
240      " Right click shrinks bottom right of visual area.
241      call MouseRightClick(5, 5)
242      call MouseRightRelease(5, 5)
243      norm! r2
244
245      if visual_mode ==# 'v'
246        call assert_equal(['-------',
247              \            '-111111',
248              \            '1122222',
249              \            '2222222',
250              \            '2222211',
251              \            '111111-',
252              \            '-------'], getline(1, '$'), msg)
253      elseif visual_mode ==# 'V'
254        call assert_equal(['-------',
255              \            '1111111',
256              \            '2222222',
257              \            '2222222',
258              \            '2222222',
259              \            '1111111',
260              \            '-------'], getline(1, '$'), msg)
261      else
262        call assert_equal(['-------',
263              \            '-11111-',
264              \            '-12221-',
265              \            '-12221-',
266              \            '-12221-',
267              \            '-11111-',
268              \            '-------'], getline(1, '$'), msg)
269      endif
270    endfor
271  endfor
272
273  let &mouse = save_mouse
274  let &term = save_term
275  let &ttymouse = save_ttymouse
276  call test_override('no_query_mouse', 0)
277  bwipe!
278endfunc
279
280" Test that <C-LeftMouse> jumps to help tag and <C-RightMouse> jumps back.
281func Test_xterm_mouse_ctrl_click()
282  let save_mouse = &mouse
283  let save_term = &term
284  let save_ttymouse = &ttymouse
285  set mouse=a term=xterm
286
287  for ttymouse_val in s:ttymouse_values
288    let msg = 'ttymouse=' .. ttymouse_val
289    exe 'set ttymouse=' .. ttymouse_val
290    help
291    /usr_02.txt
292    norm! zt
293    let row = 1
294    let col = 1
295    call MouseCtrlLeftClick(row, col)
296    call MouseLeftRelease(row, col)
297    call assert_match('usr_02.txt$', bufname('%'), msg)
298    call assert_equal('*usr_02.txt*', expand('<cWORD>'), msg)
299
300    call MouseCtrlRightClick(row, col)
301    call MouseRightRelease(row, col)
302    call assert_match('help.txt$', bufname('%'), msg)
303    call assert_equal('|usr_02.txt|', expand('<cWORD>'), msg)
304
305    helpclose
306  endfor
307
308  let &mouse = save_mouse
309  let &term = save_term
310  let &ttymouse = save_ttymouse
311endfunc
312
313func Test_term_mouse_middle_click()
314  CheckFeature clipboard_working
315
316  new
317  let save_mouse = &mouse
318  let save_term = &term
319  let save_ttymouse = &ttymouse
320  call test_override('no_query_mouse', 1)
321  let save_quotestar = @*
322  let @* = 'abc'
323  set mouse=a term=xterm
324
325  for ttymouse_val in s:ttymouse_values + s:ttymouse_dec
326    let msg = 'ttymouse=' .. ttymouse_val
327    exe 'set ttymouse=' .. ttymouse_val
328    call setline(1, ['123456789', '123456789'])
329
330    " Middle-click in the middle of the line pastes text where clicked.
331    let row = 1
332    let col = 6
333    call MouseMiddleClick(row, col)
334    call MouseMiddleRelease(row, col)
335    call assert_equal(['12345abc6789', '123456789'], getline(1, '$'), msg)
336
337    " Middle-click beyond end of the line pastes text at the end of the line.
338    let col = 20
339    call MouseMiddleClick(row, col)
340    call MouseMiddleRelease(row, col)
341    call assert_equal(['12345abc6789abc', '123456789'], getline(1, '$'), msg)
342
343    " Middle-click beyond the last line pastes in the last line.
344    let row = 5
345    let col = 3
346    call MouseMiddleClick(row, col)
347    call MouseMiddleRelease(row, col)
348    call assert_equal(['12345abc6789abc', '12abc3456789'], getline(1, '$'), msg)
349  endfor
350
351  let &mouse = save_mouse
352  let &term = save_term
353  let &ttymouse = save_ttymouse
354  call test_override('no_query_mouse', 0)
355  let @* = save_quotestar
356  bwipe!
357endfunc
358
359" TODO: for unclear reasons this test fails if it comes after
360" Test_xterm_mouse_ctrl_click()
361func Test_1xterm_mouse_wheel()
362  new
363  let save_mouse = &mouse
364  let save_term = &term
365  let save_ttymouse = &ttymouse
366  set mouse=a term=xterm
367  call setline(1, range(1, 100))
368
369  for ttymouse_val in s:ttymouse_values
370    let msg = 'ttymouse=' .. ttymouse_val
371    exe 'set ttymouse=' .. ttymouse_val
372    go
373    call assert_equal(1, line('w0'), msg)
374    call assert_equal([0, 1, 1, 0], getpos('.'), msg)
375
376    call MouseWheelDown(1, 1)
377    call assert_equal(4, line('w0'), msg)
378    call assert_equal([0, 4, 1, 0], getpos('.'), msg)
379
380    call MouseWheelDown(1, 1)
381    call assert_equal(7, line('w0'), msg)
382    call assert_equal([0, 7, 1, 0], getpos('.'), msg)
383
384    call MouseWheelUp(1, 1)
385    call assert_equal(4, line('w0'), msg)
386    call assert_equal([0, 7, 1, 0], getpos('.'), msg)
387
388    call MouseWheelUp(1, 1)
389    call assert_equal(1, line('w0'), msg)
390    call assert_equal([0, 7, 1, 0], getpos('.'), msg)
391  endfor
392
393  let &mouse = save_mouse
394  let &term = save_term
395  let &ttymouse = save_ttymouse
396  bwipe!
397endfunc
398
399" Test that dragging beyond the window (at the bottom and at the top)
400" scrolls window content by the number of lines beyond the window.
401func Test_term_mouse_drag_beyond_window()
402  let save_mouse = &mouse
403  let save_term = &term
404  let save_ttymouse = &ttymouse
405  call test_override('no_query_mouse', 1)
406  set mouse=a term=xterm
407  let col = 1
408  call setline(1, range(1, 100))
409
410  " Split into 3 windows, and go into the middle window
411  " so we test dragging mouse below and above the window.
412  2split
413  wincmd j
414  2split
415
416  for ttymouse_val in s:ttymouse_values + s:ttymouse_dec
417    let msg = 'ttymouse=' .. ttymouse_val
418    exe 'set ttymouse=' .. ttymouse_val
419
420    " Line #10 at the top.
421    norm! 10zt
422    redraw
423    call assert_equal(10, winsaveview().topline, msg)
424    call assert_equal(2, winheight(0), msg)
425
426    let row = 4
427    call MouseLeftClick(row, col)
428    call assert_equal(10, winsaveview().topline, msg)
429
430    " Drag downwards. We're still in the window so topline should
431    " not change yet.
432    let row += 1
433    call MouseLeftDrag(row, col)
434    call assert_equal(10, winsaveview().topline, msg)
435
436    " We now leave the window at the bottom, so the window content should
437    " scroll by 1 line, then 2 lines (etc) as we drag further away.
438    let row += 1
439    call MouseLeftDrag(row, col)
440    call assert_equal(11, winsaveview().topline, msg)
441
442    let row += 1
443    call MouseLeftDrag(row, col)
444    call assert_equal(13, winsaveview().topline, msg)
445
446    " Now drag upwards.
447    let row -= 1
448    call MouseLeftDrag(row, col)
449    call assert_equal(14, winsaveview().topline, msg)
450
451    " We're now back in the window so the topline should not change.
452    let row -= 1
453    call MouseLeftDrag(row, col)
454    call assert_equal(14, winsaveview().topline, msg)
455
456    let row -= 1
457    call MouseLeftDrag(row, col)
458    call assert_equal(14, winsaveview().topline, msg)
459
460    " We now leave the window at the top so the window content should
461    " scroll by 1 line, then 2, then 3 (etc) in the opposite direction.
462    let row -= 1
463    call MouseLeftDrag(row, col)
464    call assert_equal(13, winsaveview().topline, msg)
465
466    let row -= 1
467    call MouseLeftDrag(row, col)
468    call assert_equal(11, winsaveview().topline, msg)
469
470    let row -= 1
471    call MouseLeftDrag(row, col)
472    call assert_equal(8, winsaveview().topline, msg)
473
474    call MouseLeftRelease(row, col)
475    call assert_equal(8, winsaveview().topline, msg)
476    call assert_equal(2, winheight(0), msg)
477  endfor
478
479  let &mouse = save_mouse
480  let &term = save_term
481  let &ttymouse = save_ttymouse
482  call test_override('no_query_mouse', 0)
483  bwipe!
484endfunc
485
486func Test_term_mouse_drag_window_separator()
487  let save_mouse = &mouse
488  let save_term = &term
489  let save_ttymouse = &ttymouse
490  call test_override('no_query_mouse', 1)
491  set mouse=a term=xterm
492
493  for ttymouse_val in s:ttymouse_values + s:ttymouse_dec
494    let msg = 'ttymouse=' .. ttymouse_val
495    exe 'set ttymouse=' .. ttymouse_val
496
497    " Split horizontally and test dragging the horizontal window separator.
498    split
499    let rowseparator = winheight(0) + 1
500    let row = rowseparator
501    let col = 1
502
503    " When 'ttymouse' is 'xterm2', row/col bigger than 223 are not supported.
504    if ttymouse_val !=# 'xterm2' || row <= 223
505      call MouseLeftClick(row, col)
506      let row -= 1
507      call MouseLeftDrag(row, col)
508      call assert_equal(rowseparator - 1, winheight(0) + 1, msg)
509      let row += 1
510      call MouseLeftDrag(row, col)
511      call assert_equal(rowseparator, winheight(0) + 1, msg)
512      call MouseLeftRelease(row, col)
513      call assert_equal(rowseparator, winheight(0) + 1, msg)
514    endif
515    bwipe!
516
517    " Split vertically and test dragging the vertical window separator.
518    vsplit
519    let colseparator = winwidth(0) + 1
520    let row = 1
521    let col = colseparator
522
523    " When 'ttymouse' is 'xterm2', row/col bigger than 223 are not supported.
524    if ttymouse_val !=# 'xterm2' || col <= 223
525      call MouseLeftClick(row, col)
526      let col -= 1
527      call MouseLeftDrag(row, col)
528      call assert_equal(colseparator - 1, winwidth(0) + 1, msg)
529      let col += 1
530      call MouseLeftDrag(row, col)
531      call assert_equal(colseparator, winwidth(0) + 1, msg)
532      call MouseLeftRelease(row, col)
533      call assert_equal(colseparator, winwidth(0) + 1, msg)
534    endif
535    bwipe!
536  endfor
537
538  let &mouse = save_mouse
539  let &term = save_term
540  let &ttymouse = save_ttymouse
541  call test_override('no_query_mouse', 0)
542endfunc
543
544func Test_term_mouse_drag_statusline()
545  let save_mouse = &mouse
546  let save_term = &term
547  let save_ttymouse = &ttymouse
548  call test_override('no_query_mouse', 1)
549  let save_laststatus = &laststatus
550  set mouse=a term=xterm laststatus=2
551
552  for ttymouse_val in s:ttymouse_values + s:ttymouse_dec
553    let msg = 'ttymouse=' .. ttymouse_val
554    exe 'set ttymouse=' .. ttymouse_val
555
556    call assert_equal(1, &cmdheight, msg)
557    let rowstatusline = winheight(0) + 1
558    let row = rowstatusline
559    let col = 1
560
561    if ttymouse_val ==# 'xterm2' && row > 223
562      " When 'ttymouse' is 'xterm2', row/col bigger than 223 are not supported.
563      continue
564    endif
565
566    call MouseLeftClick(row, col)
567    let row -= 1
568    call MouseLeftDrag(row, col)
569    call assert_equal(2, &cmdheight, msg)
570    call assert_equal(rowstatusline - 1, winheight(0) + 1, msg)
571    let row += 1
572    call MouseLeftDrag(row, col)
573    call assert_equal(1, &cmdheight, msg)
574    call assert_equal(rowstatusline, winheight(0) + 1, msg)
575    call MouseLeftRelease(row, col)
576    call assert_equal(1, &cmdheight, msg)
577    call assert_equal(rowstatusline, winheight(0) + 1, msg)
578  endfor
579
580  let &mouse = save_mouse
581  let &term = save_term
582  let &ttymouse = save_ttymouse
583  call test_override('no_query_mouse', 0)
584  let &laststatus = save_laststatus
585endfunc
586
587func Test_term_mouse_click_tab()
588  let save_mouse = &mouse
589  let save_term = &term
590  let save_ttymouse = &ttymouse
591  call test_override('no_query_mouse', 1)
592  set mouse=a term=xterm
593  let row = 1
594
595  for ttymouse_val in s:ttymouse_values + s:ttymouse_dec + s:ttymouse_netterm
596    let msg = 'ttymouse=' .. ttymouse_val
597    exe 'set ttymouse=' .. ttymouse_val
598    e Xfoo
599    tabnew Xbar
600
601    let a = split(execute(':tabs'), "\n")
602    call assert_equal(['Tab page 1',
603        \              '    Xfoo',
604        \              'Tab page 2',
605        \              '>   Xbar'], a, msg)
606
607    " Test clicking on tab names in the tabline at the top.
608    let col = 2
609    redraw
610    call MouseLeftClick(row, col)
611    call MouseLeftRelease(row, col)
612    let a = split(execute(':tabs'), "\n")
613    call assert_equal(['Tab page 1',
614        \              '>   Xfoo',
615        \              'Tab page 2',
616        \              '    Xbar'], a, msg)
617
618    let col = 9
619    call MouseLeftClick(row, col)
620    call MouseLeftRelease(row, col)
621    let a = split(execute(':tabs'), "\n")
622    call assert_equal(['Tab page 1',
623        \              '    Xfoo',
624        \              'Tab page 2',
625        \              '>   Xbar'], a, msg)
626
627    %bwipe!
628  endfor
629
630  let &mouse = save_mouse
631  let &term = save_term
632  let &ttymouse = save_ttymouse
633  call test_override('no_query_mouse', 0)
634endfunc
635
636func Test_term_mouse_click_X_to_close_tab()
637  let save_mouse = &mouse
638  let save_term = &term
639  let save_ttymouse = &ttymouse
640  call test_override('no_query_mouse', 1)
641  set mouse=a term=xterm
642  let row = 1
643  let col = &columns
644
645  for ttymouse_val in s:ttymouse_values + s:ttymouse_dec + s:ttymouse_netterm
646    if ttymouse_val ==# 'xterm2' && col > 223
647      " When 'ttymouse' is 'xterm2', row/col bigger than 223 are not supported.
648      continue
649    endif
650    let msg = 'ttymouse=' .. ttymouse_val
651    exe 'set ttymouse=' .. ttymouse_val
652    e Xtab1
653    tabnew Xtab2
654    tabnew Xtab3
655    tabn 2
656
657    let a = split(execute(':tabs'), "\n")
658    call assert_equal(['Tab page 1',
659        \              '    Xtab1',
660        \              'Tab page 2',
661        \              '>   Xtab2',
662        \              'Tab page 3',
663        \              '    Xtab3'], a, msg)
664
665    " Click on "X" in tabline to close current tab i.e. Xtab2.
666    redraw
667    call MouseLeftClick(row, col)
668    call MouseLeftRelease(row, col)
669    let a = split(execute(':tabs'), "\n")
670    call assert_equal(['Tab page 1',
671        \              '    Xtab1',
672        \              'Tab page 2',
673        \              '>   Xtab3'], a, msg)
674
675    %bwipe!
676  endfor
677
678  let &mouse = save_mouse
679  let &term = save_term
680  let &ttymouse = save_ttymouse
681  call test_override('no_query_mouse', 0)
682endfunc
683
684func Test_term_mouse_drag_to_move_tab()
685  let save_mouse = &mouse
686  let save_term = &term
687  let save_ttymouse = &ttymouse
688  call test_override('no_query_mouse', 1)
689  " Set 'mousetime' to 1 to avoid recognizing a double-click in the loop
690  set mouse=a term=xterm mousetime=1
691  let row = 1
692
693  for ttymouse_val in s:ttymouse_values + s:ttymouse_dec
694    let msg = 'ttymouse=' .. ttymouse_val
695    exe 'set ttymouse=' .. ttymouse_val
696    e Xtab1
697    tabnew Xtab2
698
699    let a = split(execute(':tabs'), "\n")
700    call assert_equal(['Tab page 1',
701        \              '    Xtab1',
702        \              'Tab page 2',
703        \              '>   Xtab2'], a, msg)
704    redraw
705
706    " Click in tab2 and drag it to tab1.
707    " Check getcharmod() to verify that click is not
708    " interpreted as a spurious double-click.
709    call MouseLeftClick(row, 10)
710    call assert_equal(0, getcharmod(), msg)
711    for col in [9, 8, 7, 6]
712      call MouseLeftDrag(row, col)
713    endfor
714    call MouseLeftRelease(row, col)
715    let a = split(execute(':tabs'), "\n")
716    call assert_equal(['Tab page 1',
717        \              '>   Xtab2',
718        \              'Tab page 2',
719        \              '    Xtab1'], a, msg)
720
721    " Click elsewhere so that click in next iteration is not
722    " interpreted as unwanted double-click.
723    call MouseLeftClick(row, 11)
724    call MouseLeftRelease(row, 11)
725
726    %bwipe!
727  endfor
728
729  let &mouse = save_mouse
730  let &term = save_term
731  let &ttymouse = save_ttymouse
732  call test_override('no_query_mouse', 0)
733  set mousetime&
734endfunc
735
736func Test_term_mouse_double_click_to_create_tab()
737  let save_mouse = &mouse
738  let save_term = &term
739  let save_ttymouse = &ttymouse
740  call test_override('no_query_mouse', 1)
741  " Set 'mousetime' to a small value, so that double-click works but we don't
742  " have to wait long to avoid a triple-click.
743  set mouse=a term=xterm mousetime=200
744  let row = 1
745  let col = 10
746
747  for ttymouse_val in s:ttymouse_values + s:ttymouse_dec
748    let msg = 'ttymouse=' .. ttymouse_val
749    exe 'set ttymouse=' .. ttymouse_val
750    e Xtab1
751    tabnew Xtab2
752
753    let a = split(execute(':tabs'), "\n")
754    call assert_equal(['Tab page 1',
755        \              '    Xtab1',
756        \              'Tab page 2',
757        \              '>   Xtab2'], a, msg)
758
759    redraw
760    call MouseLeftClick(row, col)
761    " Check getcharmod() to verify that first click is not
762    " interpreted as a spurious double-click.
763    call assert_equal(0, getcharmod(), msg)
764    call MouseLeftRelease(row, col)
765    call MouseLeftClick(row, col)
766    call assert_equal(32, getcharmod(), msg) " double-click
767    call MouseLeftRelease(row, col)
768    let a = split(execute(':tabs'), "\n")
769    call assert_equal(['Tab page 1',
770        \              '    Xtab1',
771        \              'Tab page 2',
772        \              '>   [No Name]',
773        \              'Tab page 3',
774        \              '    Xtab2'], a, msg)
775
776    " Click elsewhere so that click in next iteration is not
777    " interpreted as unwanted double click.
778    call MouseLeftClick(row, col + 1)
779    call MouseLeftRelease(row, col + 1)
780
781    %bwipe!
782  endfor
783
784  let &mouse = save_mouse
785  let &term = save_term
786  let &ttymouse = save_ttymouse
787  call test_override('no_query_mouse', 0)
788  set mousetime&
789endfunc
790
791" Test double/triple/quadruple click in normal mode to visually select.
792func Test_term_mouse_multiple_clicks_to_visually_select()
793  let save_mouse = &mouse
794  let save_term = &term
795  let save_ttymouse = &ttymouse
796  call test_override('no_query_mouse', 1)
797  set mouse=a term=xterm mousetime=200
798  new
799
800  for ttymouse_val in s:ttymouse_values + s:ttymouse_dec
801    let msg = 'ttymouse=' .. ttymouse_val
802    exe 'set ttymouse=' .. ttymouse_val
803    call setline(1, ['foo [foo bar] foo', 'foo'])
804
805    " Double-click on word should visually select the word.
806    call MouseLeftClick(1, 2)
807    call assert_equal(0, getcharmod(), msg)
808    call MouseLeftRelease(1, 2)
809    call MouseLeftClick(1, 2)
810    call assert_equal(32, getcharmod(), msg) " double-click
811    call MouseLeftRelease(1, 2)
812    call assert_equal('v', mode(), msg)
813    norm! r1
814    call assert_equal(['111 [foo bar] foo', 'foo'], getline(1, '$'), msg)
815
816    " Double-click on opening square bracket should visually
817    " select the whole [foo bar].
818    call MouseLeftClick(1, 5)
819    call assert_equal(0, getcharmod(), msg)
820    call MouseLeftRelease(1, 5)
821    call MouseLeftClick(1, 5)
822    call assert_equal(32, getcharmod(), msg) " double-click
823    call MouseLeftRelease(1, 5)
824    call assert_equal('v', mode(), msg)
825    norm! r2
826    call assert_equal(['111 222222222 foo', 'foo'], getline(1, '$'), msg)
827
828    " Triple-click should visually select the whole line.
829    call MouseLeftClick(1, 3)
830    call assert_equal(0, getcharmod(), msg)
831    call MouseLeftRelease(1, 3)
832    call MouseLeftClick(1, 3)
833    call assert_equal(32, getcharmod(), msg) " double-click
834    call MouseLeftRelease(1, 3)
835    call MouseLeftClick(1, 3)
836    call assert_equal(64, getcharmod(), msg) " triple-click
837    call MouseLeftRelease(1, 3)
838    call assert_equal('V', mode(), msg)
839    norm! r3
840    call assert_equal(['33333333333333333', 'foo'], getline(1, '$'), msg)
841
842    " Quadruple-click should start visual block select.
843    call MouseLeftClick(1, 2)
844    call assert_equal(0, getcharmod(), msg)
845    call MouseLeftRelease(1, 2)
846    call MouseLeftClick(1, 2)
847    call assert_equal(32, getcharmod(), msg) " double-click
848    call MouseLeftRelease(1, 2)
849    call MouseLeftClick(1, 2)
850    call assert_equal(64, getcharmod(), msg) " triple-click
851    call MouseLeftRelease(1, 2)
852    call MouseLeftClick(1, 2)
853    call assert_equal(96, getcharmod(), msg) " quadruple-click
854    call MouseLeftRelease(1, 2)
855    call assert_equal("\<c-v>", mode(), msg)
856    norm! r4
857    call assert_equal(['34333333333333333', 'foo'], getline(1, '$'), msg)
858  endfor
859
860  let &mouse = save_mouse
861  let &term = save_term
862  let &ttymouse = save_ttymouse
863  set mousetime&
864  call test_override('no_query_mouse', 0)
865  bwipe!
866endfunc
867
868func Test_xterm_mouse_click_in_fold_columns()
869  new
870  let save_mouse = &mouse
871  let save_term = &term
872  let save_ttymouse = &ttymouse
873  let save_foldcolumn = &foldcolumn
874  set mouse=a term=xterm foldcolumn=3 ttymouse=xterm2
875
876  " Create 2 nested folds.
877  call setline(1, range(1, 7))
878  2,6fold
879  norm! zR
880  4,5fold
881  call assert_equal([-1, -1, -1, 4, 4, -1, -1],
882        \           map(range(1, 7), 'foldclosed(v:val)'))
883
884  " Click in "+" of inner fold in foldcolumn should open it.
885  redraw
886  let row = 4
887  let col = 2
888  call MouseLeftClick(row, col)
889  call MouseLeftRelease(row, col)
890  call assert_equal([-1, -1, -1, -1, -1, -1, -1],
891        \           map(range(1, 7), 'foldclosed(v:val)'))
892
893  " Click in "-" of outer fold in foldcolumn should close it.
894  redraw
895  let row = 2
896  let col = 1
897  call MouseLeftClick(row, col)
898  call MouseLeftRelease(row, col)
899  call assert_equal([-1, 2, 2, 2, 2, 2, -1],
900        \           map(range(1, 7), 'foldclosed(v:val)'))
901  norm! zR
902
903  " Click in "|" of inner fold in foldcolumn should close it.
904  redraw
905  let row = 5
906  let col = 2
907  call MouseLeftClick(row, col)
908  call MouseLeftRelease(row, col)
909  call assert_equal([-1, -1, -1, 4, 4, -1, -1],
910        \           map(range(1, 7), 'foldclosed(v:val)'))
911
912  let &foldcolumn = save_foldcolumn
913  let &ttymouse = save_ttymouse
914  let &term = save_term
915  let &mouse = save_mouse
916  bwipe!
917endfunc
918
919" Left or right click in Ex command line sets position of the cursor.
920func Test_term_mouse_click_in_cmdline_to_set_pos()
921  let save_mouse = &mouse
922  let save_term = &term
923  let save_ttymouse = &ttymouse
924  call test_override('no_query_mouse', 1)
925  set mouse=a term=xterm
926  let row = &lines
927
928  for ttymouse_val in s:ttymouse_values + s:ttymouse_dec
929    let msg = 'ttymouse=' .. ttymouse_val
930    exe 'set ttymouse=' .. ttymouse_val
931
932    call feedkeys(':"3456789'
933          \       .. MouseLeftClickCode(row, 7)
934          \       .. MouseLeftReleaseCode(row, 7) .. 'L'
935          \       .. MouseRightClickCode(row, 4)
936          \       .. MouseRightReleaseCode(row, 4) .. 'R'
937          \       .. "\<CR>", 'Lx!')
938    call assert_equal('"3R456L789', @:, msg)
939  endfor
940
941  let &mouse = save_mouse
942  let &term = save_term
943  let &ttymouse = save_ttymouse
944  set mousetime&
945  call test_override('no_query_mouse', 0)
946endfunc
947
948" Middle click in command line pastes at position of cursor.
949func Test_term_mouse_middle_click_in_cmdline_to_paste()
950  CheckFeature clipboard_working
951  let save_mouse = &mouse
952  let save_term = &term
953  let save_ttymouse = &ttymouse
954  call test_override('no_query_mouse', 1)
955  set mouse=a term=xterm
956  let row = &lines
957  " Column values does not matter, paste is done at position of cursor.
958  let col = 1
959  let @* = 'paste'
960
961  for ttymouse_val in s:ttymouse_values + s:ttymouse_dec
962    let msg = 'ttymouse=' .. ttymouse_val
963    exe 'set ttymouse=' .. ttymouse_val
964
965    call feedkeys(":\"->"
966          \       .. MouseMiddleReleaseCode(row, col)
967          \       .. MouseMiddleClickCode(row, col)
968          \       .. "<-"
969          \       .. MouseMiddleReleaseCode(row, col)
970          \       .. MouseMiddleClickCode(row, col)
971          \       .. "\<CR>", 'Lx!')
972    call assert_equal('"->paste<-paste', @:, msg)
973  endfor
974
975  let &mouse = save_mouse
976  let &term = save_term
977  let &ttymouse = save_ttymouse
978  let @* = ''
979  call test_override('no_query_mouse', 0)
980endfunc
981
982" This only checks if the sequence is recognized.
983func Test_term_rgb_response()
984  set t_RF=x
985  set t_RB=y
986
987  " response to t_RF, 4 digits
988  let red = 0x12
989  let green = 0x34
990  let blue = 0x56
991  let seq = printf("\<Esc>]10;rgb:%02x00/%02x00/%02x00\x07", red, green, blue)
992  call feedkeys(seq, 'Lx!')
993  call assert_equal(seq, v:termrfgresp)
994
995  " response to t_RF, 2 digits
996  let red = 0x78
997  let green = 0x9a
998  let blue = 0xbc
999  let seq = printf("\<Esc>]10;rgb:%02x/%02x/%02x\x07", red, green, blue)
1000  call feedkeys(seq, 'Lx!')
1001  call assert_equal(seq, v:termrfgresp)
1002
1003  " response to t_RB, 4 digits, dark
1004  set background=light
1005  eval 'background'->test_option_not_set()
1006  let red = 0x29
1007  let green = 0x4a
1008  let blue = 0x6b
1009  let seq = printf("\<Esc>]11;rgb:%02x00/%02x00/%02x00\x07", red, green, blue)
1010  call feedkeys(seq, 'Lx!')
1011  call assert_equal(seq, v:termrbgresp)
1012  call assert_equal('dark', &background)
1013
1014  " response to t_RB, 4 digits, light
1015  set background=dark
1016  call test_option_not_set('background')
1017  let red = 0x81
1018  let green = 0x63
1019  let blue = 0x65
1020  let seq = printf("\<Esc>]11;rgb:%02x00/%02x00/%02x00\x07", red, green, blue)
1021  call feedkeys(seq, 'Lx!')
1022  call assert_equal(seq, v:termrbgresp)
1023  call assert_equal('light', &background)
1024
1025  " response to t_RB, 2 digits, dark
1026  set background=light
1027  call test_option_not_set('background')
1028  let red = 0x47
1029  let green = 0x59
1030  let blue = 0x5b
1031  let seq = printf("\<Esc>]11;rgb:%02x/%02x/%02x\x07", red, green, blue)
1032  call feedkeys(seq, 'Lx!')
1033  call assert_equal(seq, v:termrbgresp)
1034  call assert_equal('dark', &background)
1035
1036  " response to t_RB, 2 digits, light
1037  set background=dark
1038  call test_option_not_set('background')
1039  let red = 0x83
1040  let green = 0xa4
1041  let blue = 0xc2
1042  let seq = printf("\<Esc>]11;rgb:%02x/%02x/%02x\x07", red, green, blue)
1043  call feedkeys(seq, 'Lx!')
1044  call assert_equal(seq, v:termrbgresp)
1045  call assert_equal('light', &background)
1046
1047  set t_RF= t_RB=
1048endfunc
1049
1050" This only checks if the sequence is recognized.
1051" This must be after other tests, because it has side effects to xterm
1052" properties.
1053func Test_xx01_term_style_response()
1054  " Termresponse is only parsed when t_RV is not empty.
1055  set t_RV=x
1056
1057  " send the termresponse to trigger requesting the XT codes
1058  let seq = "\<Esc>[>41;337;0c"
1059  call feedkeys(seq, 'Lx!')
1060  call assert_equal(seq, v:termresponse)
1061
1062  let seq = "\<Esc>P1$r2 q\<Esc>\\"
1063  call feedkeys(seq, 'Lx!')
1064  call assert_equal(seq, v:termstyleresp)
1065
1066  set t_RV=
1067endfunc
1068
1069" This checks the iTerm2 version response.
1070" This must be after other tests, because it has side effects to xterm
1071" properties.
1072func Test_xx02_iTerm2_response()
1073  " Termresponse is only parsed when t_RV is not empty.
1074  set t_RV=x
1075
1076  " Old versions of iTerm2 used a different style term response.
1077  set ttymouse=xterm
1078  call test_option_not_set('ttymouse')
1079  let seq = "\<Esc>[>0;95;c"
1080  call feedkeys(seq, 'Lx!')
1081  call assert_equal(seq, v:termresponse)
1082  call assert_equal('xterm', &ttymouse)
1083
1084  set ttymouse=xterm
1085  call test_option_not_set('ttymouse')
1086  let seq = "\<Esc>[>0;95;0c"
1087  call feedkeys(seq, 'Lx!')
1088  call assert_equal(seq, v:termresponse)
1089  call assert_equal('sgr', &ttymouse)
1090
1091  set t_RV=
1092endfunc
1093
1094" This checks the libvterm version response.
1095" This must be after other tests, because it has side effects to xterm
1096" properties.
1097func Test_xx03_libvterm_response()
1098  " Termresponse is only parsed when t_RV is not empty.
1099  set t_RV=x
1100
1101  set ttymouse=xterm
1102  call test_option_not_set('ttymouse')
1103  let seq = "\<Esc>[>0;100;0c"
1104  call feedkeys(seq, 'Lx!')
1105  call assert_equal(seq, v:termresponse)
1106  call assert_equal('sgr', &ttymouse)
1107
1108  set t_RV=
1109endfunc
1110
1111" This checks the Mac Terminal.app version response.
1112" This must be after other tests, because it has side effects to xterm
1113" properties.
1114func Test_xx04_Mac_Terminal_response()
1115  " Termresponse is only parsed when t_RV is not empty.
1116  set t_RV=x
1117
1118  set ttymouse=xterm
1119  call test_option_not_set('ttymouse')
1120  let seq = "\<Esc>[>1;95;0c"
1121  call feedkeys(seq, 'Lx!')
1122  call assert_equal(seq, v:termresponse)
1123  call assert_equal('sgr', &ttymouse)
1124
1125  " Reset is_not_xterm and is_mac_terminal.
1126  set t_RV=
1127  set term=xterm
1128  set t_RV=x
1129endfunc
1130
1131" This checks the mintty version response.
1132" This must be after other tests, because it has side effects to xterm
1133" properties.
1134func Test_xx05_mintty_response()
1135  " Termresponse is only parsed when t_RV is not empty.
1136  set t_RV=x
1137
1138  set ttymouse=xterm
1139  call test_option_not_set('ttymouse')
1140  let seq = "\<Esc>[>77;20905;0c"
1141  call feedkeys(seq, 'Lx!')
1142  call assert_equal(seq, v:termresponse)
1143  call assert_equal('sgr', &ttymouse)
1144
1145  set t_RV=
1146endfunc
1147
1148" This checks the screen version response.
1149" This must be after other tests, because it has side effects to xterm
1150" properties.
1151func Test_xx06_screen_response()
1152  " Termresponse is only parsed when t_RV is not empty.
1153  set t_RV=x
1154
1155  " Old versions of screen don't support SGR mouse mode.
1156  set ttymouse=xterm
1157  call test_option_not_set('ttymouse')
1158  let seq = "\<Esc>[>83;40500;0c"
1159  call feedkeys(seq, 'Lx!')
1160  call assert_equal(seq, v:termresponse)
1161  call assert_equal('xterm', &ttymouse)
1162
1163  " screen supports SGR mouse mode starting in version 4.7.
1164  set ttymouse=xterm
1165  call test_option_not_set('ttymouse')
1166  let seq = "\<Esc>[>83;40700;0c"
1167  call feedkeys(seq, 'Lx!')
1168  call assert_equal(seq, v:termresponse)
1169  call assert_equal('sgr', &ttymouse)
1170
1171  set t_RV=
1172endfunc
1173
1174" This checks the xterm version response.
1175" This must be after other tests, because it has side effects to xterm
1176" properties.
1177func Test_xx07_xterm_response()
1178  " Termresponse is only parsed when t_RV is not empty.
1179  set t_RV=x
1180
1181  " Do Terminal.app first to check that is_mac_terminal is reset.
1182  set ttymouse=xterm
1183  call test_option_not_set('ttymouse')
1184  let seq = "\<Esc>[>1;95;0c"
1185  call feedkeys(seq, 'Lx!')
1186  call assert_equal(seq, v:termresponse)
1187  call assert_equal('sgr', &ttymouse)
1188
1189  " xterm < 95: "xterm" (actually unmodified)
1190  set t_RV=
1191  set term=xterm
1192  set t_RV=x
1193  set ttymouse=xterm
1194  call test_option_not_set('ttymouse')
1195  let seq = "\<Esc>[>0;94;0c"
1196  call feedkeys(seq, 'Lx!')
1197  call assert_equal(seq, v:termresponse)
1198  call assert_equal('xterm', &ttymouse)
1199
1200  " xterm >= 95 < 277 "xterm2"
1201  set ttymouse=xterm
1202  call test_option_not_set('ttymouse')
1203  let seq = "\<Esc>[>0;267;0c"
1204  call feedkeys(seq, 'Lx!')
1205  call assert_equal(seq, v:termresponse)
1206  call assert_equal('xterm2', &ttymouse)
1207
1208  " xterm >= 277: "sgr"
1209  set ttymouse=xterm
1210  call test_option_not_set('ttymouse')
1211  let seq = "\<Esc>[>0;277;0c"
1212  call feedkeys(seq, 'Lx!')
1213  call assert_equal(seq, v:termresponse)
1214  call assert_equal('sgr', &ttymouse)
1215
1216  set t_RV=
1217endfunc
1218
1219func Test_get_termcode()
1220  try
1221    let k1 = &t_k1
1222  catch /E113/
1223    throw 'Skipped: Unable to query termcodes'
1224  endtry
1225  set t_k1=
1226  set t_k1&
1227  call assert_equal(k1, &t_k1)
1228
1229  " use external termcap first
1230  set nottybuiltin
1231  set t_k1=
1232  set t_k1&
1233  " when using external termcap may get something else, but it must not be
1234  " empty, since we would fallback to the builtin one.
1235  call assert_notequal('', &t_k1)
1236
1237  if &term =~ 'xterm'
1238    " use internal termcap first
1239    let term_save = &term
1240    let &term = 'builtin_' .. &term
1241    set t_k1=
1242    set t_k1&
1243    call assert_equal(k1, &t_k1)
1244    let &term = term_save
1245  endif
1246
1247  set ttybuiltin
1248endfunc
1249
1250func GetEscCodeCSI27(key, modifier)
1251  let key = printf("%d", char2nr(a:key))
1252  let mod = printf("%d", a:modifier)
1253  return "\<Esc>[27;" .. mod .. ';' .. key .. '~'
1254endfunc
1255
1256func GetEscCodeCSIu(key, modifier)
1257  let key = printf("%d", char2nr(a:key))
1258  let mod = printf("%d", a:modifier)
1259  return "\<Esc>[" .. key .. ';' .. mod .. 'u'
1260endfunc
1261
1262" This checks the CSI sequences when in modifyOtherKeys mode.
1263" The mode doesn't need to be enabled, the codes are always detected.
1264func RunTest_modifyOtherKeys(func)
1265  new
1266  set timeoutlen=10
1267
1268  " Shift-X is sent as 'X' with the shift modifier
1269  call feedkeys('a' .. a:func('X', 2) .. "\<Esc>", 'Lx!')
1270  call assert_equal('X', getline(1))
1271
1272  " Ctrl-i is Tab
1273  call setline(1, '')
1274  call feedkeys('a' .. a:func('i', 5) .. "\<Esc>", 'Lx!')
1275  call assert_equal("\t", getline(1))
1276
1277  " Ctrl-I is also Tab
1278  call setline(1, '')
1279  call feedkeys('a' .. a:func('I', 5) .. "\<Esc>", 'Lx!')
1280  call assert_equal("\t", getline(1))
1281
1282  " Alt-x is ø
1283  call setline(1, '')
1284  call feedkeys('a' .. a:func('x', 3) .. "\<Esc>", 'Lx!')
1285  call assert_equal("ø", getline(1))
1286
1287  " Meta-x is also ø
1288  call setline(1, '')
1289  call feedkeys('a' .. a:func('x', 9) .. "\<Esc>", 'Lx!')
1290  call assert_equal("ø", getline(1))
1291
1292  " Alt-X is Ø
1293  call setline(1, '')
1294  call feedkeys('a' .. a:func('X', 3) .. "\<Esc>", 'Lx!')
1295  call assert_equal("Ø", getline(1))
1296
1297  " Meta-X is ø
1298  call setline(1, '')
1299  call feedkeys('a' .. a:func('X', 9) .. "\<Esc>", 'Lx!')
1300  call assert_equal("Ø", getline(1))
1301
1302  " Ctrl-6 is Ctrl-^
1303  split aaa
1304  edit bbb
1305  call feedkeys(a:func('6', 5), 'Lx!')
1306  call assert_equal("aaa", bufname())
1307  bwipe aaa
1308  bwipe bbb
1309
1310  bwipe!
1311  set timeoutlen&
1312endfunc
1313
1314func Test_modifyOtherKeys_basic()
1315  call RunTest_modifyOtherKeys(function('GetEscCodeCSI27'))
1316  call RunTest_modifyOtherKeys(function('GetEscCodeCSIu'))
1317endfunc
1318
1319func Test_modifyOtherKeys_no_mapping()
1320  set timeoutlen=10
1321
1322  let @a = 'aaa'
1323  call feedkeys(":let x = '" .. GetEscCodeCSI27('R', 5) .. GetEscCodeCSI27('R', 5) .. "a'\<CR>", 'Lx!')
1324  call assert_equal("let x = 'aaa'", @:)
1325
1326  new
1327  call feedkeys("a" .. GetEscCodeCSI27('R', 5) .. GetEscCodeCSI27('R', 5) .. "a\<Esc>", 'Lx!')
1328  call assert_equal("aaa", getline(1))
1329  bwipe!
1330
1331  new
1332  call feedkeys("axx\<CR>yy" .. GetEscCodeCSI27('G', 5) .. GetEscCodeCSI27('K', 5) .. "a\<Esc>", 'Lx!')
1333  call assert_equal("axx", getline(1))
1334  call assert_equal("yy", getline(2))
1335  bwipe!
1336
1337  set timeoutlen&
1338endfunc
1339
1340func RunTest_mapping_shift(key, func)
1341  call setline(1, '')
1342  if a:key == '|'
1343    exe 'inoremap \| xyz'
1344  else
1345    exe 'inoremap ' .. a:key .. ' xyz'
1346  endif
1347  call feedkeys('a' .. a:func(a:key, 2) .. "\<Esc>", 'Lx!')
1348  call assert_equal("xyz", getline(1))
1349  if a:key == '|'
1350    exe 'iunmap \|'
1351  else
1352    exe 'iunmap ' .. a:key
1353  endif
1354endfunc
1355
1356func RunTest_mapping_works_with_shift(func)
1357  new
1358  set timeoutlen=10
1359
1360  call RunTest_mapping_shift('@', a:func)
1361  call RunTest_mapping_shift('A', a:func)
1362  call RunTest_mapping_shift('Z', a:func)
1363  call RunTest_mapping_shift('^', a:func)
1364  call RunTest_mapping_shift('_', a:func)
1365  call RunTest_mapping_shift('{', a:func)
1366  call RunTest_mapping_shift('|', a:func)
1367  call RunTest_mapping_shift('}', a:func)
1368  call RunTest_mapping_shift('~', a:func)
1369
1370  bwipe!
1371  set timeoutlen&
1372endfunc
1373
1374func Test_mapping_works_with_shift_plain()
1375  call RunTest_mapping_works_with_shift(function('GetEscCodeCSI27'))
1376  call RunTest_mapping_works_with_shift(function('GetEscCodeCSIu'))
1377endfunc
1378
1379func RunTest_mapping_mods(map, key, func, code)
1380  call setline(1, '')
1381  exe 'inoremap ' .. a:map .. ' xyz'
1382  call feedkeys('a' .. a:func(a:key, a:code) .. "\<Esc>", 'Lx!')
1383  call assert_equal("xyz", getline(1))
1384  exe 'iunmap ' .. a:map
1385endfunc
1386
1387func RunTest_mapping_works_with_mods(func, mods, code)
1388  new
1389  set timeoutlen=10
1390
1391  if a:mods !~ 'S'
1392    " Shift by itself has no effect
1393    call RunTest_mapping_mods('<' .. a:mods .. '-@>', '@', a:func, a:code)
1394  endif
1395  call RunTest_mapping_mods('<' .. a:mods .. '-A>', 'A', a:func, a:code)
1396  call RunTest_mapping_mods('<' .. a:mods .. '-Z>', 'Z', a:func, a:code)
1397  if a:mods !~ 'S'
1398    " with Shift code is always upper case
1399    call RunTest_mapping_mods('<' .. a:mods .. '-a>', 'a', a:func, a:code)
1400    call RunTest_mapping_mods('<' .. a:mods .. '-z>', 'z', a:func, a:code)
1401  endif
1402  if a:mods != 'A'
1403    " with Alt code is not in upper case
1404    call RunTest_mapping_mods('<' .. a:mods .. '-a>', 'A', a:func, a:code)
1405    call RunTest_mapping_mods('<' .. a:mods .. '-z>', 'Z', a:func, a:code)
1406  endif
1407  call RunTest_mapping_mods('<' .. a:mods .. '-á>', 'á', a:func, a:code)
1408  if a:mods !~ 'S'
1409    " Shift by itself has no effect
1410    call RunTest_mapping_mods('<' .. a:mods .. '-^>', '^', a:func, a:code)
1411    call RunTest_mapping_mods('<' .. a:mods .. '-_>', '_', a:func, a:code)
1412    call RunTest_mapping_mods('<' .. a:mods .. '-{>', '{', a:func, a:code)
1413    call RunTest_mapping_mods('<' .. a:mods .. '-\|>', '|', a:func, a:code)
1414    call RunTest_mapping_mods('<' .. a:mods .. '-}>', '}', a:func, a:code)
1415    call RunTest_mapping_mods('<' .. a:mods .. '-~>', '~', a:func, a:code)
1416  endif
1417
1418  bwipe!
1419  set timeoutlen&
1420endfunc
1421
1422func Test_mapping_works_with_shift()
1423  call RunTest_mapping_works_with_mods(function('GetEscCodeCSI27'), 'S', 2)
1424  call RunTest_mapping_works_with_mods(function('GetEscCodeCSIu'), 'S', 2)
1425endfunc
1426
1427func Test_mapping_works_with_ctrl()
1428  call RunTest_mapping_works_with_mods(function('GetEscCodeCSI27'), 'C', 5)
1429  call RunTest_mapping_works_with_mods(function('GetEscCodeCSIu'), 'C', 5)
1430endfunc
1431
1432func Test_mapping_works_with_shift_ctrl()
1433  call RunTest_mapping_works_with_mods(function('GetEscCodeCSI27'), 'C-S', 6)
1434  call RunTest_mapping_works_with_mods(function('GetEscCodeCSIu'), 'C-S', 6)
1435endfunc
1436
1437" Below we also test the "u" code with Alt, This works, but libvterm would not
1438" send the Alt key like this but by prefixing an Esc.
1439
1440func Test_mapping_works_with_alt()
1441  call RunTest_mapping_works_with_mods(function('GetEscCodeCSI27'), 'A', 3)
1442  call RunTest_mapping_works_with_mods(function('GetEscCodeCSIu'), 'A', 3)
1443endfunc
1444
1445func Test_mapping_works_with_shift_alt()
1446  call RunTest_mapping_works_with_mods(function('GetEscCodeCSI27'), 'S-A', 4)
1447  call RunTest_mapping_works_with_mods(function('GetEscCodeCSIu'), 'S-A', 4)
1448endfunc
1449
1450func Test_mapping_works_with_ctrl_alt()
1451  call RunTest_mapping_works_with_mods(function('GetEscCodeCSI27'), 'C-A', 7)
1452  call RunTest_mapping_works_with_mods(function('GetEscCodeCSIu'), 'C-A', 7)
1453endfunc
1454
1455func Test_mapping_works_with_shift_ctrl_alt()
1456  call RunTest_mapping_works_with_mods(function('GetEscCodeCSI27'), 'C-S-A', 8)
1457  call RunTest_mapping_works_with_mods(function('GetEscCodeCSIu'), 'C-S-A', 8)
1458endfunc
1459
1460func Test_insert_literal()
1461  set timeoutlen=10
1462  new
1463  " CTRL-V CTRL-X inserts a ^X
1464  call feedkeys('a' .. GetEscCodeCSIu('V', '5') .. GetEscCodeCSIu('X', '5') .. "\<Esc>", 'Lx!')
1465  call assert_equal("\<C-X>", getline(1))
1466
1467  call setline(1, '')
1468  call feedkeys('a' .. GetEscCodeCSI27('V', '5') .. GetEscCodeCSI27('X', '5') .. "\<Esc>", 'Lx!')
1469  call assert_equal("\<C-X>", getline(1))
1470
1471  " CTRL-SHIFT-V CTRL-X inserts escape sequence
1472  call setline(1, '')
1473  call feedkeys('a' .. GetEscCodeCSIu('V', '6') .. GetEscCodeCSIu('X', '5') .. "\<Esc>", 'Lx!')
1474  call assert_equal("\<Esc>[88;5u", getline(1))
1475
1476  call setline(1, '')
1477  call feedkeys('a' .. GetEscCodeCSI27('V', '6') .. GetEscCodeCSI27('X', '5') .. "\<Esc>", 'Lx!')
1478  call assert_equal("\<Esc>[27;5;88~", getline(1))
1479
1480  bwipe!
1481  set timeoutlen&
1482endfunc
1483
1484func Test_cmdline_literal()
1485  set timeoutlen=10
1486
1487  " CTRL-V CTRL-Y inserts a ^Y
1488  call feedkeys(':' .. GetEscCodeCSIu('V', '5') .. GetEscCodeCSIu('Y', '5') .. "\<C-B>\"\<CR>", 'Lx!')
1489  call assert_equal("\"\<C-Y>", @:)
1490
1491  call feedkeys(':' .. GetEscCodeCSI27('V', '5') .. GetEscCodeCSI27('Y', '5') .. "\<C-B>\"\<CR>", 'Lx!')
1492  call assert_equal("\"\<C-Y>", @:)
1493
1494  " CTRL-SHIFT-V CTRL-Y inserts escape sequence
1495  call feedkeys(':' .. GetEscCodeCSIu('V', '6') .. GetEscCodeCSIu('Y', '5') .. "\<C-B>\"\<CR>", 'Lx!')
1496  call assert_equal("\"\<Esc>[89;5u", @:)
1497
1498  call setline(1, '')
1499  call feedkeys(':' .. GetEscCodeCSI27('V', '6') .. GetEscCodeCSI27('Y', '5') .. "\<C-B>\"\<CR>", 'Lx!')
1500  call assert_equal("\"\<Esc>[27;5;89~", @:)
1501
1502  set timeoutlen&
1503endfunc
1504