1" Tests for 'listchars' display with 'list' and :list
2
3source check.vim
4source view_util.vim
5source screendump.vim
6
7func Test_listchars()
8  enew!
9  set ff=unix
10  set list
11
12  set listchars+=tab:>-,space:.,trail:<
13  call append(0, [
14	      \ '	aa	',
15	      \ '  bb	  ',
16	      \ '   cccc	 ',
17	      \ 'dd        ee  	',
18	      \ ' '
19	      \ ])
20  let expected = [
21	      \ '>-------aa>-----$',
22	      \ '..bb>---<<$',
23	      \ '...cccc><$',
24	      \ 'dd........ee<<>-$',
25	      \ '<$'
26	      \ ]
27  redraw!
28  for i in range(1, 5)
29    call cursor(i, 1)
30    call assert_equal([expected[i - 1]], ScreenLines(i, '$'->virtcol()))
31  endfor
32
33  set listchars-=trail:<
34  let expected = [
35	      \ '>-------aa>-----$',
36	      \ '..bb>---..$',
37	      \ '...cccc>.$',
38	      \ 'dd........ee..>-$',
39	      \ '.$'
40	      \ ]
41  redraw!
42  for i in range(1, 5)
43    call cursor(i, 1)
44    call assert_equal([expected[i - 1]], ScreenLines(i, virtcol('$')))
45  endfor
46
47  " tab with 3rd character.
48  set listchars-=tab:>-
49  set listchars+=tab:<=>,trail:-
50  let expected = [
51	      \ '<======>aa<====>$',
52	      \ '..bb<==>--$',
53	      \ '...cccc>-$',
54	      \ 'dd........ee--<>$',
55	      \ '-$'
56	      \ ]
57  redraw!
58  for i in range(1, 5)
59    call cursor(i, 1)
60    call assert_equal([expected[i - 1]], ScreenLines(i, virtcol('$')))
61  endfor
62
63  " tab with 3rd character and linebreak set
64  set listchars-=tab:<=>
65  set listchars+=tab:<·>
66  set linebreak
67  let expected = [
68	      \ '<······>aa<····>$',
69	      \ '..bb<··>--$',
70	      \ '...cccc>-$',
71	      \ 'dd........ee--<>$',
72	      \ '-$'
73	      \ ]
74  redraw!
75  for i in range(1, 5)
76    call cursor(i, 1)
77    call assert_equal([expected[i - 1]], ScreenLines(i, virtcol('$')))
78  endfor
79  set nolinebreak
80  set listchars-=tab:<·>
81  set listchars+=tab:<=>
82
83  set listchars-=trail:-
84  let expected = [
85	      \ '<======>aa<====>$',
86	      \ '..bb<==>..$',
87	      \ '...cccc>.$',
88	      \ 'dd........ee..<>$',
89	      \ '.$'
90	      \ ]
91  redraw!
92  for i in range(1, 5)
93    call cursor(i, 1)
94    call assert_equal([expected[i - 1]], ScreenLines(i, virtcol('$')))
95  endfor
96
97  set listchars-=tab:<=>
98  set listchars+=tab:>-
99  set listchars+=trail:<
100  set nolist
101  normal ggdG
102  call append(0, [
103	      \ '  fff	  ',
104	      \ '	gg	',
105	      \ '     h	',
106	      \ 'iii    	  ',
107	      \ ])
108  let l = split(execute("%list"), "\n")
109  call assert_equal([
110	      \ '..fff>--<<$',
111	      \ '>-------gg>-----$',
112	      \ '.....h>-$',
113	      \ 'iii<<<<><<$', '$'], l)
114
115  " Test lead and trail
116  normal ggdG
117  set listchars&
118  set listchars+=lead:>,trail:<,space:x
119  set list
120
121  call append(0, [
122	      \ '    ffff    ',
123	      \ '          gg',
124	      \ 'h           ',
125	      \ '            ',
126	      \ '    0  0    ',
127	      \ ])
128
129  let expected = [
130	      \ '>>>>ffff<<<<$',
131	      \ '>>>>>>>>>>gg$',
132	      \ 'h<<<<<<<<<<<$',
133	      \ '<<<<<<<<<<<<$',
134	      \ '>>>>0xx0<<<<$',
135              \ '$'
136	      \ ]
137  redraw!
138  for i in range(1, 5)
139    call cursor(i, 1)
140    call assert_equal([expected[i - 1]], ScreenLines(i, virtcol('$')))
141  endfor
142
143  call assert_equal(expected, split(execute("%list"), "\n"))
144
145  " Test multispace
146  normal ggdG
147  set listchars&
148  set listchars+=multispace:yYzZ
149  set list
150
151  call append(0, [
152	      \ '    ffff    ',
153	      \ '  i i     gg',
154	      \ ' h          ',
155	      \ '          j ',
156	      \ '    0  0    ',
157	      \ ])
158
159  let expected = [
160	      \ 'yYzZffffyYzZ$',
161	      \ 'yYi iyYzZygg$',
162	      \ ' hyYzZyYzZyY$',
163	      \ 'yYzZyYzZyYj $',
164	      \ 'yYzZ0yY0yYzZ$',
165              \ '$'
166	      \ ]
167  redraw!
168  for i in range(1, 5)
169    call cursor(i, 1)
170    call assert_equal([expected[i - 1]], ScreenLines(i, virtcol('$')))
171  endfor
172
173  call assert_equal(expected, split(execute("%list"), "\n"))
174
175  " the last occurrence of 'multispace:' is used
176  set listchars+=space:x,multispace:XyY
177
178  let expected = [
179	      \ 'XyYXffffXyYX$',
180	      \ 'XyixiXyYXygg$',
181	      \ 'xhXyYXyYXyYX$',
182	      \ 'XyYXyYXyYXjx$',
183	      \ 'XyYX0Xy0XyYX$',
184              \ '$'
185	      \ ]
186  redraw!
187  for i in range(1, 5)
188    call cursor(i, 1)
189    call assert_equal([expected[i - 1]], ScreenLines(i, virtcol('$')))
190  endfor
191
192  call assert_equal(expected, split(execute("%list"), "\n"))
193
194  set listchars+=lead:>,trail:<
195
196  let expected = [
197	      \ '>>>>ffff<<<<$',
198	      \ '>>ixiXyYXygg$',
199	      \ '>h<<<<<<<<<<$',
200	      \ '>>>>>>>>>>j<$',
201	      \ '>>>>0Xy0<<<<$',
202              \ '$'
203	      \ ]
204  redraw!
205  for i in range(1, 5)
206    call cursor(i, 1)
207    call assert_equal([expected[i - 1]], ScreenLines(i, virtcol('$')))
208  endfor
209
210  call assert_equal(expected, split(execute("%list"), "\n"))
211
212  " removing 'multispace:'
213  set listchars-=multispace:XyY
214  set listchars-=multispace:yYzZ
215
216  let expected = [
217	      \ '>>>>ffff<<<<$',
218	      \ '>>ixixxxxxgg$',
219	      \ '>h<<<<<<<<<<$',
220	      \ '>>>>>>>>>>j<$',
221	      \ '>>>>0xx0<<<<$',
222              \ '$'
223	      \ ]
224  redraw!
225  for i in range(1, 5)
226    call cursor(i, 1)
227    call assert_equal([expected[i - 1]], ScreenLines(i, virtcol('$')))
228  endfor
229
230  call assert_equal(expected, split(execute("%list"), "\n"))
231
232  " test nbsp
233  normal ggdG
234  set listchars=nbsp:X,trail:Y
235  set list
236  " Non-breaking space
237  let nbsp = nr2char(0xa0)
238  call append(0, [ ">" .. nbsp .. "<" ])
239
240  let expected = '>X< '
241
242  redraw!
243  call cursor(1, 1)
244  call assert_equal([expected], ScreenLines(1, virtcol('$')))
245
246  set listchars=nbsp:X
247  redraw!
248  call cursor(1, 1)
249  call assert_equal([expected], ScreenLines(1, virtcol('$')))
250
251  " test extends
252  normal ggdG
253  set listchars=extends:Z
254  set nowrap
255  set nolist
256  call append(0, [ repeat('A', &columns + 1) ])
257
258  let expected = repeat('A', &columns)
259
260  redraw!
261  call cursor(1, 1)
262  call assert_equal([expected], ScreenLines(1, &columns))
263
264  set list
265  let expected = expected[:-2] . 'Z'
266  redraw!
267  call cursor(1, 1)
268  call assert_equal([expected], ScreenLines(1, &columns))
269
270  enew!
271  set listchars& ff&
272endfunc
273
274" Test that unicode listchars characters get properly inserted
275func Test_listchars_unicode()
276  enew!
277  let oldencoding=&encoding
278  set encoding=utf-8
279  set ff=unix
280
281  set listchars=eol:⇔,space:␣,multispace:≡≢≣,nbsp:≠,tab:←↔→
282  set list
283
284  let nbsp = nr2char(0xa0)
285  call append(0, ["        a\tb c" .. nbsp .. "d  "])
286  let expected = ['≡≢≣≡≢≣≡≢a←↔↔↔↔↔→b␣c≠d≡≢⇔']
287  redraw!
288  call cursor(1, 1)
289  call assert_equal(expected, ScreenLines(1, virtcol('$')))
290
291  set listchars+=lead:⇨,trail:⇦
292  let expected = ['⇨⇨⇨⇨⇨⇨⇨⇨a←↔↔↔↔↔→b␣c≠d⇦⇦⇔']
293  redraw!
294  call cursor(1, 1)
295  call assert_equal(expected, ScreenLines(1, virtcol('$')))
296
297  let &encoding=oldencoding
298  enew!
299  set listchars& ff&
300endfunction
301
302func Test_listchars_invalid()
303  enew!
304  set ff=unix
305
306  set listchars&
307  set list
308  set ambiwidth=double
309
310  " No colon
311  call assert_fails('set listchars=x', 'E474:')
312  call assert_fails('set listchars=x', 'E474:')
313  call assert_fails('set listchars=multispace', 'E474:')
314
315  " Too short
316  call assert_fails('set listchars=space:', 'E474:')
317  call assert_fails('set listchars=tab:x', 'E474:')
318  call assert_fails('set listchars=multispace:', 'E474:')
319
320  " One occurrence too short
321  call assert_fails('set listchars=space:,space:x', 'E474:')
322  call assert_fails('set listchars=space:x,space:', 'E474:')
323  call assert_fails('set listchars=tab:x,tab:xx', 'E474:')
324  call assert_fails('set listchars=tab:xx,tab:x', 'E474:')
325  call assert_fails('set listchars=multispace:,multispace:x', 'E474:')
326  call assert_fails('set listchars=multispace:x,multispace:', 'E474:')
327
328  " Too long
329  call assert_fails('set listchars=space:xx', 'E474:')
330  call assert_fails('set listchars=tab:xxxx', 'E474:')
331
332  " Has non-single width character
333  call assert_fails('set listchars=space:·', 'E474:')
334  call assert_fails('set listchars=tab:·x', 'E474:')
335  call assert_fails('set listchars=tab:x·', 'E474:')
336  call assert_fails('set listchars=tab:xx·', 'E474:')
337  call assert_fails('set listchars=multispace:·', 'E474:')
338  call assert_fails('set listchars=multispace:xxx·', 'E474:')
339
340  enew!
341  set ambiwidth& listchars& ff&
342endfunction
343
344" Tests that space characters following composing character won't get replaced
345" by listchars.
346func Test_listchars_composing()
347  enew!
348  let oldencoding=&encoding
349  set encoding=utf-8
350  set ff=unix
351  set list
352
353  set listchars=eol:$,space:_,nbsp:=
354
355  let nbsp1 = nr2char(0xa0)
356  let nbsp2 = nr2char(0x202f)
357  call append(0, [
358        \ "  \u3099\t \u309A" .. nbsp1 .. nbsp1 .. "\u0302" .. nbsp2 .. nbsp2 .. "\u0302",
359        \ ])
360  let expected = [
361        \ "_ \u3099^I \u309A=" .. nbsp1 .. "\u0302=" .. nbsp2 .. "\u0302$"
362        \ ]
363  redraw!
364  call cursor(1, 1)
365  call assert_equal(expected, ScreenLines(1, virtcol('$')))
366  let &encoding=oldencoding
367  enew!
368  set listchars& ff&
369endfunction
370
371" Check for the value of the 'listchars' option
372func s:CheckListCharsValue(expected)
373  call assert_equal(a:expected, &listchars)
374  call assert_equal(a:expected, getwinvar(0, '&listchars'))
375endfunc
376
377" Test for using a window local value for 'listchars'
378func Test_listchars_window_local()
379  %bw!
380  set list listchars&
381  new
382  " set a local value for 'listchars'
383  setlocal listchars=tab:+-,eol:#
384  call s:CheckListCharsValue('tab:+-,eol:#')
385  " When local value is reset, global value should be used
386  setlocal listchars=
387  call s:CheckListCharsValue('eol:$')
388  " Use 'setlocal <' to copy global value
389  setlocal listchars=space:.,extends:>
390  setlocal listchars<
391  call s:CheckListCharsValue('eol:$')
392  " Use 'set <' to copy global value
393  setlocal listchars=space:.,extends:>
394  set listchars<
395  call s:CheckListCharsValue('eol:$')
396  " Changing global setting should not change the local setting
397  setlocal listchars=space:.,extends:>
398  setglobal listchars=tab:+-,eol:#
399  call s:CheckListCharsValue('space:.,extends:>')
400  " when split opening a new window, local value should be copied
401  split
402  call s:CheckListCharsValue('space:.,extends:>')
403  " clearing local value in one window should not change the other window
404  set listchars&
405  call s:CheckListCharsValue('eol:$')
406  close
407  call s:CheckListCharsValue('space:.,extends:>')
408
409  " use different values for 'listchars' items in two different windows
410  call setline(1, ["\t  one  two  "])
411  setlocal listchars=tab:<->,lead:_,space:.,trail:@,eol:#
412  split
413  setlocal listchars=tab:[.],lead:#,space:_,trail:.,eol:&
414  split
415  set listchars=tab:+-+,lead:^,space:>,trail:<,eol:%
416  call assert_equal(['+------+^^one>>two<<%'], ScreenLines(1, virtcol('$')))
417  close
418  call assert_equal(['[......]##one__two..&'], ScreenLines(1, virtcol('$')))
419  close
420  call assert_equal(['<------>__one..two@@#'], ScreenLines(1, virtcol('$')))
421  " changing the global setting should not change the local value
422  setglobal listchars=tab:[.],lead:#,space:_,trail:.,eol:&
423  call assert_equal(['<------>__one..two@@#'], ScreenLines(1, virtcol('$')))
424  set listchars<
425  call assert_equal(['[......]##one__two..&'], ScreenLines(1, virtcol('$')))
426
427  " Using setglobal in a window with local setting should not affect the
428  " window. But should impact other windows using the global setting.
429  enew! | only
430  call setline(1, ["\t  one  two  "])
431  set listchars=tab:[.],lead:#,space:_,trail:.,eol:&
432  split
433  setlocal listchars=tab:+-+,lead:^,space:>,trail:<,eol:%
434  split
435  setlocal listchars=tab:<->,lead:_,space:.,trail:@,eol:#
436  setglobal listchars=tab:{.},lead:-,space:=,trail:#,eol:$
437  call assert_equal(['<------>__one..two@@#'], ScreenLines(1, virtcol('$')))
438  close
439  call assert_equal(['+------+^^one>>two<<%'], ScreenLines(1, virtcol('$')))
440  close
441  call assert_equal(['{......}--one==two##$'], ScreenLines(1, virtcol('$')))
442
443  " Setting the global setting to the default value should not impact a window
444  " using a local setting
445  split
446  setlocal listchars=tab:<->,lead:_,space:.,trail:@,eol:#
447  setglobal listchars&vim
448  call assert_equal(['<------>__one..two@@#'], ScreenLines(1, virtcol('$')))
449  close
450  call assert_equal(['^I  one  two  $'], ScreenLines(1, virtcol('$')))
451
452  " Setting the local setting to the default value should not impact a window
453  " using a global setting
454  set listchars=tab:{.},lead:-,space:=,trail:#,eol:$
455  split
456  setlocal listchars=tab:<->,lead:_,space:.,trail:@,eol:#
457  call assert_equal(['<------>__one..two@@#'], ScreenLines(1, virtcol('$')))
458  setlocal listchars&vim
459  call assert_equal(['^I  one  two  $'], ScreenLines(1, virtcol('$')))
460  close
461  call assert_equal(['{......}--one==two##$'], ScreenLines(1, virtcol('$')))
462
463  " Using set in a window with a local setting should change it to use the
464  " global setting and also impact other windows using the global setting
465  split
466  setlocal listchars=tab:<->,lead:_,space:.,trail:@,eol:#
467  call assert_equal(['<------>__one..two@@#'], ScreenLines(1, virtcol('$')))
468  set listchars=tab:+-+,lead:^,space:>,trail:<,eol:%
469  call assert_equal(['+------+^^one>>two<<%'], ScreenLines(1, virtcol('$')))
470  close
471  call assert_equal(['+------+^^one>>two<<%'], ScreenLines(1, virtcol('$')))
472
473  " Setting invalid value for a local setting should not impact the local and
474  " global settings
475  split
476  setlocal listchars=tab:<->,lead:_,space:.,trail:@,eol:#
477  let cmd = 'setlocal listchars=tab:{.},lead:-,space:=,trail:#,eol:$,x'
478  call assert_fails(cmd, 'E474:')
479  call assert_equal(['<------>__one..two@@#'], ScreenLines(1, virtcol('$')))
480  close
481  call assert_equal(['+------+^^one>>two<<%'], ScreenLines(1, virtcol('$')))
482
483  " Setting invalid value for a global setting should not impact the local and
484  " global settings
485  split
486  setlocal listchars=tab:<->,lead:_,space:.,trail:@,eol:#
487  let cmd = 'setglobal listchars=tab:{.},lead:-,space:=,trail:#,eol:$,x'
488  call assert_fails(cmd, 'E474:')
489  call assert_equal(['<------>__one..two@@#'], ScreenLines(1, virtcol('$')))
490  close
491  call assert_equal(['+------+^^one>>two<<%'], ScreenLines(1, virtcol('$')))
492
493  %bw!
494  set list& listchars&
495endfunc
496
497func Test_listchars_foldcolumn()
498  CheckScreendump
499
500  let lines =<< trim END
501      call setline(1, ['aaa', '', 'a', 'aaaaaa'])
502      vsplit
503      vsplit
504      windo set signcolumn=yes foldcolumn=1 winminwidth=0 nowrap list listchars=extends:>,precedes:<
505  END
506  call writefile(lines, 'XTest_listchars')
507
508  let buf = RunVimInTerminal('-S XTest_listchars', {'rows': 10, 'cols': 60})
509
510  call term_sendkeys(buf, "13\<C-W>>")
511  call VerifyScreenDump(buf, 'Test_listchars_01', {})
512  call term_sendkeys(buf, "\<C-W>>")
513  call VerifyScreenDump(buf, 'Test_listchars_02', {})
514  call term_sendkeys(buf, "\<C-W>>")
515  call VerifyScreenDump(buf, 'Test_listchars_03', {})
516  call term_sendkeys(buf, "\<C-W>>")
517  call VerifyScreenDump(buf, 'Test_listchars_04', {})
518  call term_sendkeys(buf, "\<C-W>>")
519  call VerifyScreenDump(buf, 'Test_listchars_05', {})
520  call term_sendkeys(buf, "\<C-W>h")
521  call term_sendkeys(buf, ":set nowrap foldcolumn=4\<CR>")
522  call term_sendkeys(buf, "15\<C-W><")
523  call VerifyScreenDump(buf, 'Test_listchars_06', {})
524  call term_sendkeys(buf, "4\<C-W><")
525  call VerifyScreenDump(buf, 'Test_listchars_07', {})
526
527  " clean up
528  call StopVimInTerminal(buf)
529  call delete('XTest_listchars')
530endfunc
531
532
533" vim: shiftwidth=2 sts=2 expandtab
534