1" Tests for decoding escape sequences sent by the terminal.
2
3" This only works for Unix in a terminal
4if has('gui_running') || !has('unix')
5  finish
6endif
7
8source shared.vim
9
10" Helper function to emit a terminal escape code.
11func TerminalEscapeCode(code_xterm, code_sgr, row, col, m)
12  if &ttymouse ==# 'xterm2'
13    " need to use byte encoding here.
14    let str = list2str([a:code_xterm, a:col + 0x20, a:row + 0x20])
15    if has('iconv')
16      let bytes = iconv(str, 'utf-8', 'latin1')
17    else
18      " Hopefully the numbers are not too big.
19      let bytes = str
20    endif
21    call feedkeys("\<Esc>[M" .. bytes, 'Lx!')
22  elseif &ttymouse ==# 'sgr'
23    call feedkeys(printf("\<Esc>[<%d;%d;%d%s", a:code_sgr, a:col, a:row, a:m), 'Lx!')
24  endif
25endfunc
26
27func MouseLeftClick(row, col)
28  call TerminalEscapeCode(0x20, 0, a:row, a:col, 'M')
29endfunc
30
31func MouseMiddleClick(row, col)
32  call TerminalEscapeCode(0x21, 1, a:row, a:col, 'M')
33endfunc
34
35func MouseLeftRelease(row, col)
36  call TerminalEscapeCode(0x23, 3, a:row, a:col, 'm')
37endfunc
38
39func MouseMiddleRelease(row, col)
40  call TerminalEscapeCode(0x23, 3, a:row, a:col, 'm')
41endfunc
42
43func MouseLeftDrag(row, col)
44  call TerminalEscapeCode(0x43, 0x20, a:row, a:col, 'M')
45endfunc
46
47func MouseWheelUp(row, col)
48  call TerminalEscapeCode(0x40, 0x40, a:row, a:col, 'M')
49endfunc
50
51func MouseWheelDown(row, col)
52  call TerminalEscapeCode(0x41, 0x41, a:row, a:col, 'M')
53endfunc
54
55func Test_xterm_mouse_left_click()
56  new
57  let save_mouse = &mouse
58  let save_term = &term
59  let save_ttymouse = &ttymouse
60  set mouse=a term=xterm
61  call setline(1, ['line 1', 'line 2', 'line 3 is a bit longer'])
62
63  for ttymouse_val in ['xterm2', 'sgr']
64    let msg = 'ttymouse=' .. ttymouse_val
65    exe 'set ttymouse=' . ttymouse_val
66    go
67    call assert_equal([0, 1, 1, 0], getpos('.'), msg)
68    let row = 2
69    let col = 6
70    call MouseLeftClick(row, col)
71    call MouseLeftRelease(row, col)
72    call assert_equal([0, 2, 6, 0], getpos('.'), msg)
73  endfor
74
75  let &mouse = save_mouse
76  let &term = save_term
77  let &ttymouse = save_ttymouse
78  bwipe!
79endfunc
80
81func Test_xterm_mouse_middle_click()
82  if !WorkingClipboard()
83    throw 'Skipped: No working clipboard'
84  endif
85
86  new
87  let save_mouse = &mouse
88  let save_term = &term
89  let save_ttymouse = &ttymouse
90  let save_quotestar = @*
91  let @* = 'abc'
92  set mouse=a term=xterm
93
94  for ttymouse_val in ['xterm2', 'sgr']
95    let msg = 'ttymouse=' .. ttymouse_val
96    exe 'set ttymouse=' . ttymouse_val
97    call setline(1, ['123456789', '123456789'])
98
99    " Middle-click in the middle of the line pastes text where clicked.
100    let row = 1
101    let col = 6
102    call MouseMiddleClick(row, col)
103    call MouseMiddleRelease(row, col)
104    call assert_equal(['12345abc6789', '123456789'], getline(1, '$'), msg)
105
106    " Middle-click beyond end of the line pastes text at the end of the line.
107    let col = 20
108    call MouseMiddleClick(row, col)
109    call MouseMiddleRelease(row, col)
110    call assert_equal(['12345abc6789abc', '123456789'], getline(1, '$'), msg)
111
112    " Middle-click beyond the last line pastes in the last line.
113    let row = 5
114    let col = 3
115    call MouseMiddleClick(row, col)
116    call MouseMiddleRelease(row, col)
117    call assert_equal(['12345abc6789abc', '12abc3456789'], getline(1, '$'), msg)
118  endfor
119
120  let &mouse = save_mouse
121  let &term = save_term
122  let &ttymouse = save_ttymouse
123  let @* = save_quotestar
124  bwipe!
125endfunc
126
127func Test_xterm_mouse_wheel()
128  new
129  let save_mouse = &mouse
130  let save_term = &term
131  let save_ttymouse = &ttymouse
132  set mouse=a term=xterm
133  call setline(1, range(1, 100))
134
135  for ttymouse_val in ['xterm2', 'sgr']
136    let msg = 'ttymouse=' .. ttymouse_val
137    exe 'set ttymouse=' . ttymouse_val
138    go
139    call assert_equal(1, line('w0'), msg)
140    call assert_equal([0, 1, 1, 0], getpos('.'), msg)
141
142    call MouseWheelDown(1, 1)
143    call assert_equal(4, line('w0'), msg)
144    call assert_equal([0, 4, 1, 0], getpos('.'), msg)
145
146    call MouseWheelDown(1, 1)
147    call assert_equal(7, line('w0'), msg)
148    call assert_equal([0, 7, 1, 0], getpos('.'), msg)
149
150    call MouseWheelUp(1, 1)
151    call assert_equal(4, line('w0'), msg)
152    call assert_equal([0, 7, 1, 0], getpos('.'), msg)
153
154    call MouseWheelUp(1, 1)
155    call assert_equal(1, line('w0'), msg)
156    call assert_equal([0, 7, 1, 0], getpos('.'), msg)
157  endfor
158
159  let &mouse = save_mouse
160  let &term = save_term
161  let &ttymouse = save_ttymouse
162  bwipe!
163endfunc
164
165func Test_xterm_mouse_drag_window_separator()
166  let save_mouse = &mouse
167  let save_term = &term
168  let save_ttymouse = &ttymouse
169  set mouse=a term=xterm
170
171  for ttymouse_val in ['xterm2', 'sgr']
172    let msg = 'ttymouse=' .. ttymouse_val
173    exe 'set ttymouse=' . ttymouse_val
174
175    " Split horizontally and test dragging the horizontal window separator.
176    split
177    let rowseparator = winheight(0) + 1
178    let row = rowseparator
179    let col = 1
180
181    " When 'ttymouse' is 'xterm2', row/col bigger than 223 are not supported.
182    if ttymouse_val !=# 'xterm2' || row <= 223
183      call MouseLeftClick(row, col)
184      let row -= 1
185      call MouseLeftDrag(row, col)
186      call assert_equal(rowseparator - 1, winheight(0) + 1, msg)
187      let row += 1
188      call MouseLeftDrag(row, col)
189      call assert_equal(rowseparator, winheight(0) + 1, msg)
190      call MouseLeftRelease(row, col)
191      call assert_equal(rowseparator, winheight(0) + 1, msg)
192    endif
193    bwipe!
194
195    " Split vertically and test dragging the vertical window separator.
196    vsplit
197    let colseparator = winwidth(0) + 1
198    let row = 1
199    let col = colseparator
200
201    " When 'ttymouse' is 'xterm2', row/col bigger than 223 are not supported.
202    if ttymouse_val !=# 'xterm2' || col <= 223
203      call MouseLeftClick(row, col)
204      let col -= 1
205      call MouseLeftDrag(row, col)
206      call assert_equal(colseparator - 1, winwidth(0) + 1, msg)
207      let col += 1
208      call MouseLeftDrag(row, col)
209      call assert_equal(colseparator, winwidth(0) + 1, msg)
210      call MouseLeftRelease(row, col)
211      call assert_equal(colseparator, winwidth(0) + 1, msg)
212    endif
213    bwipe!
214  endfor
215
216  let &mouse = save_mouse
217  let &term = save_term
218  let &ttymouse = save_ttymouse
219endfunc
220
221func Test_xterm_mouse_drag_statusline()
222  let save_mouse = &mouse
223  let save_term = &term
224  let save_ttymouse = &ttymouse
225  let save_laststatus = &laststatus
226  set mouse=a term=xterm laststatus=2
227
228  for ttymouse_val in ['xterm2', 'sgr']
229    let msg = 'ttymouse=' .. ttymouse_val
230    exe 'set ttymouse=' . ttymouse_val
231
232    call assert_equal(1, &cmdheight, msg)
233    let rowstatusline = winheight(0) + 1
234    let row = rowstatusline
235    let col = 1
236
237    if ttymouse_val ==# 'xterm2' && row > 223
238      " When 'ttymouse' is 'xterm2', row/col bigger than 223 are not supported.
239      continue
240    endif
241
242    call MouseLeftClick(row, col)
243    let row -= 1
244    call MouseLeftDrag(row, col)
245    call assert_equal(2, &cmdheight, msg)
246    call assert_equal(rowstatusline - 1, winheight(0) + 1, msg)
247    let row += 1
248    call MouseLeftDrag(row, col)
249    call assert_equal(1, &cmdheight, msg)
250    call assert_equal(rowstatusline, winheight(0) + 1, msg)
251    call MouseLeftRelease(row, col)
252    call assert_equal(1, &cmdheight, msg)
253    call assert_equal(rowstatusline, winheight(0) + 1, msg)
254  endfor
255
256  let &mouse = save_mouse
257  let &term = save_term
258  let &ttymouse = save_ttymouse
259  let &laststatus = save_laststatus
260endfunc
261
262func Test_xterm_mouse_click_tab()
263  let save_mouse = &mouse
264  let save_term = &term
265  let save_ttymouse = &ttymouse
266  set mouse=a term=xterm
267  let row = 1
268
269  for ttymouse_val in ['xterm2', 'sgr']
270    let msg = 'ttymouse=' .. ttymouse_val
271    exe 'set ttymouse=' . ttymouse_val
272    e Xfoo
273    tabnew Xbar
274
275    let a = split(execute(':tabs'), "\n")
276    call assert_equal(['Tab page 1',
277        \              '    Xfoo',
278        \              'Tab page 2',
279        \              '>   Xbar'], a, msg)
280
281    " Test clicking on tab names in the tabline at the top.
282    let col = 2
283    redraw
284    call MouseLeftClick(row, col)
285    call MouseLeftRelease(row, col)
286    let a = split(execute(':tabs'), "\n")
287    call assert_equal(['Tab page 1',
288        \              '>   Xfoo',
289        \              'Tab page 2',
290        \              '    Xbar'], a, msg)
291
292    let col = 9
293    call MouseLeftClick(row, col)
294    call MouseLeftRelease(row, col)
295    let a = split(execute(':tabs'), "\n")
296    call assert_equal(['Tab page 1',
297        \              '    Xfoo',
298        \              'Tab page 2',
299        \              '>   Xbar'], a, msg)
300
301    %bwipe!
302  endfor
303
304  let &mouse = save_mouse
305  let &term = save_term
306  let &ttymouse = save_ttymouse
307endfunc
308
309func Test_xterm_mouse_click_X_to_close_tab()
310  let save_mouse = &mouse
311  let save_term = &term
312  let save_ttymouse = &ttymouse
313  set mouse=a term=xterm
314  let row = 1
315  let col = &columns
316
317  for ttymouse_val in ['xterm2', 'sgr']
318    if ttymouse_val ==# 'xterm2' && col > 223
319      " When 'ttymouse' is 'xterm2', row/col bigger than 223 are not supported.
320      continue
321    endif
322    let msg = 'ttymouse=' .. ttymouse_val
323    exe 'set ttymouse=' . ttymouse_val
324    e Xtab1
325    tabnew Xtab2
326    tabnew Xtab3
327    tabn 2
328
329    let a = split(execute(':tabs'), "\n")
330    call assert_equal(['Tab page 1',
331        \              '    Xtab1',
332        \              'Tab page 2',
333        \              '>   Xtab2',
334        \              'Tab page 3',
335        \              '    Xtab3'], a, msg)
336
337    " Click on "X" in tabline to close current tab i.e. Xtab2.
338    redraw
339    call MouseLeftClick(row, col)
340    call MouseLeftRelease(row, col)
341    let a = split(execute(':tabs'), "\n")
342    call assert_equal(['Tab page 1',
343        \              '    Xtab1',
344        \              'Tab page 2',
345        \              '>   Xtab3'], a, msg)
346
347    %bwipe!
348  endfor
349
350  let &mouse = save_mouse
351  let &term = save_term
352  let &ttymouse = save_ttymouse
353endfunc
354
355func Test_xterm_mouse_drag_to_move_tab()
356  let save_mouse = &mouse
357  let save_term = &term
358  let save_ttymouse = &ttymouse
359  " Set 'mousetime' to 1 to avoid recognizing a double-click in the loop
360  set mouse=a term=xterm mousetime=1
361  let row = 1
362
363  for ttymouse_val in ['xterm2', 'sgr']
364    let msg = 'ttymouse=' .. ttymouse_val
365    exe 'set ttymouse=' . ttymouse_val
366    e Xtab1
367    tabnew Xtab2
368
369    let a = split(execute(':tabs'), "\n")
370    call assert_equal(['Tab page 1',
371        \              '    Xtab1',
372        \              'Tab page 2',
373        \              '>   Xtab2'], a, msg)
374    redraw
375
376    " Click in tab2 and drag it to tab1.
377    " Check getcharmod() to verify that click is not
378    " interpreted as a spurious double-click.
379    call MouseLeftClick(row, 10)
380    call assert_equal(0, getcharmod(), msg)
381    for col in [9, 8, 7, 6]
382      call MouseLeftDrag(row, col)
383    endfor
384    call MouseLeftRelease(row, col)
385    let a = split(execute(':tabs'), "\n")
386    call assert_equal(['Tab page 1',
387        \              '>   Xtab2',
388        \              'Tab page 2',
389        \              '    Xtab1'], a, msg)
390
391    " brief sleep to avoid causing a double-click
392    sleep 20m
393    %bwipe!
394  endfor
395
396  let &mouse = save_mouse
397  let &term = save_term
398  let &ttymouse = save_ttymouse
399  set mousetime&
400endfunc
401
402func Test_xterm_mouse_double_click_to_create_tab()
403  let save_mouse = &mouse
404  let save_term = &term
405  let save_ttymouse = &ttymouse
406  " Set 'mousetime' to a small value, so that double-click works but we don't
407  " have to wait long to avoid a triple-click.
408  set mouse=a term=xterm mousetime=100
409  let row = 1
410  let col = 10
411
412  for ttymouse_val in ['xterm2', 'sgr']
413    let msg = 'ttymouse=' .. ttymouse_val
414    exe 'set ttymouse=' . ttymouse_val
415    e Xtab1
416    tabnew Xtab2
417
418    let a = split(execute(':tabs'), "\n")
419    call assert_equal(['Tab page 1',
420        \              '    Xtab1',
421        \              'Tab page 2',
422        \              '>   Xtab2'], a, msg)
423
424    redraw
425    call MouseLeftClick(row, col)
426    " Check getcharmod() to verify that first click is not
427    " interpreted as a spurious double-click.
428    call assert_equal(0, getcharmod(), msg)
429    call MouseLeftRelease(row, col)
430    call MouseLeftClick(row, col)
431    call assert_equal(32, getcharmod(), msg) " double-click
432    call MouseLeftRelease(row, col)
433    let a = split(execute(':tabs'), "\n")
434    call assert_equal(['Tab page 1',
435        \              '    Xtab1',
436        \              'Tab page 2',
437        \              '>   [No Name]',
438        \              'Tab page 3',
439        \              '    Xtab2'], a, msg)
440
441    if ttymouse_val !=# 'sgr'
442      " We need to sleep, or else MouseLeftClick() in next loop
443      " iteration will be interpreted as a spurious triple-click.
444      sleep 100m
445    endif
446    %bwipe!
447  endfor
448
449  let &mouse = save_mouse
450  let &term = save_term
451  let &ttymouse = save_ttymouse
452  set mousetime&
453endfunc
454
455func Test_xterm_mouse_click_in_fold_columns()
456  new
457  let save_mouse = &mouse
458  let save_term = &term
459  let save_ttymouse = &ttymouse
460  let save_foldcolumn = &foldcolumn
461  set mouse=a term=xterm foldcolumn=3 ttymouse=xterm2
462
463  " Create 2 nested folds.
464  call setline(1, range(1, 7))
465  2,6fold
466  norm! zR
467  4,5fold
468  call assert_equal([-1, -1, -1, 4, 4, -1, -1],
469        \           map(range(1, 7), 'foldclosed(v:val)'))
470
471  " Click in "+" of inner fold in foldcolumn should open it.
472  redraw
473  let row = 4
474  let col = 2
475  call MouseLeftClick(row, col)
476  call MouseLeftRelease(row, col)
477  call assert_equal([-1, -1, -1, -1, -1, -1, -1],
478        \           map(range(1, 7), 'foldclosed(v:val)'))
479
480  " Click in "-" of outer fold in foldcolumn should close it.
481  redraw
482  let row = 2
483  let col = 1
484  call MouseLeftClick(row, col)
485  call MouseLeftRelease(row, col)
486  call assert_equal([-1, 2, 2, 2, 2, 2, -1],
487        \           map(range(1, 7), 'foldclosed(v:val)'))
488  norm! zR
489
490  " Click in "|" of inner fold in foldcolumn should close it.
491  redraw
492  let row = 5
493  let col = 2
494  call MouseLeftClick(row, col)
495  call MouseLeftRelease(row, col)
496  call assert_equal([-1, -1, -1, 4, 4, -1, -1],
497        \           map(range(1, 7), 'foldclosed(v:val)'))
498
499  let &foldcolumn = save_foldcolumn
500  let &ttymouse = save_ttymouse
501  let &term = save_term
502  let &mouse = save_mouse
503  bwipe!
504endfunc
505