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=eol:\\u21d4,space:\\u2423,multispace:≡\\u2262\\U00002263,nbsp:\\U00002260,tab:←↔\\u2192
292  redraw!
293  call assert_equal(expected, ScreenLines(1, virtcol('$')))
294
295  set listchars+=lead:⇨,trail:⇦
296  let expected = ['⇨⇨⇨⇨⇨⇨⇨⇨a←↔↔↔↔↔→b␣c≠d⇦⇦⇔']
297  redraw!
298  call cursor(1, 1)
299  call assert_equal(expected, ScreenLines(1, virtcol('$')))
300
301  let &encoding=oldencoding
302  enew!
303  set listchars& ff&
304endfunction
305
306func Test_listchars_invalid()
307  enew!
308  set ff=unix
309
310  set listchars&
311  set list
312  set ambiwidth=double
313
314  " No colon
315  call assert_fails('set listchars=x', 'E474:')
316  call assert_fails('set listchars=x', 'E474:')
317  call assert_fails('set listchars=multispace', 'E474:')
318
319  " Too short
320  call assert_fails('set listchars=space:', 'E474:')
321  call assert_fails('set listchars=tab:x', 'E474:')
322  call assert_fails('set listchars=multispace:', 'E474:')
323
324  " One occurrence too short
325  call assert_fails('set listchars=space:,space:x', 'E474:')
326  call assert_fails('set listchars=space:x,space:', 'E474:')
327  call assert_fails('set listchars=tab:x,tab:xx', 'E474:')
328  call assert_fails('set listchars=tab:xx,tab:x', 'E474:')
329  call assert_fails('set listchars=multispace:,multispace:x', 'E474:')
330  call assert_fails('set listchars=multispace:x,multispace:', 'E474:')
331
332  " Too long
333  call assert_fails('set listchars=space:xx', 'E474:')
334  call assert_fails('set listchars=tab:xxxx', 'E474:')
335
336  " Has non-single width character
337  call assert_fails('set listchars=space:·', 'E474:')
338  call assert_fails('set listchars=tab:·x', 'E474:')
339  call assert_fails('set listchars=tab:x·', 'E474:')
340  call assert_fails('set listchars=tab:xx·', 'E474:')
341  call assert_fails('set listchars=multispace:·', 'E474:')
342  call assert_fails('set listchars=multispace:xxx·', 'E474:')
343
344  enew!
345  set ambiwidth& listchars& ff&
346endfunction
347
348" Tests that space characters following composing character won't get replaced
349" by listchars.
350func Test_listchars_composing()
351  enew!
352  let oldencoding=&encoding
353  set encoding=utf-8
354  set ff=unix
355  set list
356
357  set listchars=eol:$,space:_,nbsp:=
358
359  let nbsp1 = nr2char(0xa0)
360  let nbsp2 = nr2char(0x202f)
361  call append(0, [
362        \ "  \u3099\t \u309A" .. nbsp1 .. nbsp1 .. "\u0302" .. nbsp2 .. nbsp2 .. "\u0302",
363        \ ])
364  let expected = [
365        \ "_ \u3099^I \u309A=" .. nbsp1 .. "\u0302=" .. nbsp2 .. "\u0302$"
366        \ ]
367  redraw!
368  call cursor(1, 1)
369  call assert_equal(expected, ScreenLines(1, virtcol('$')))
370  let &encoding=oldencoding
371  enew!
372  set listchars& ff&
373endfunction
374
375" Check for the value of the 'listchars' option
376func s:CheckListCharsValue(expected)
377  call assert_equal(a:expected, &listchars)
378  call assert_equal(a:expected, getwinvar(0, '&listchars'))
379endfunc
380
381" Test for using a window local value for 'listchars'
382func Test_listchars_window_local()
383  %bw!
384  set list listchars&
385  new
386  " set a local value for 'listchars'
387  setlocal listchars=tab:+-,eol:#
388  call s:CheckListCharsValue('tab:+-,eol:#')
389  " When local value is reset, global value should be used
390  setlocal listchars=
391  call s:CheckListCharsValue('eol:$')
392  " Use 'setlocal <' to copy global value
393  setlocal listchars=space:.,extends:>
394  setlocal listchars<
395  call s:CheckListCharsValue('eol:$')
396  " Use 'set <' to copy global value
397  setlocal listchars=space:.,extends:>
398  set listchars<
399  call s:CheckListCharsValue('eol:$')
400  " Changing global setting should not change the local setting
401  setlocal listchars=space:.,extends:>
402  setglobal listchars=tab:+-,eol:#
403  call s:CheckListCharsValue('space:.,extends:>')
404  " when split opening a new window, local value should be copied
405  split
406  call s:CheckListCharsValue('space:.,extends:>')
407  " clearing local value in one window should not change the other window
408  set listchars&
409  call s:CheckListCharsValue('eol:$')
410  close
411  call s:CheckListCharsValue('space:.,extends:>')
412
413  " use different values for 'listchars' items in two different windows
414  call setline(1, ["\t  one  two  "])
415  setlocal listchars=tab:<->,lead:_,space:.,trail:@,eol:#
416  split
417  setlocal listchars=tab:[.],lead:#,space:_,trail:.,eol:&
418  split
419  set listchars=tab:+-+,lead:^,space:>,trail:<,eol:%
420  call assert_equal(['+------+^^one>>two<<%'], ScreenLines(1, virtcol('$')))
421  close
422  call assert_equal(['[......]##one__two..&'], ScreenLines(1, virtcol('$')))
423  close
424  call assert_equal(['<------>__one..two@@#'], ScreenLines(1, virtcol('$')))
425  " changing the global setting should not change the local value
426  setglobal listchars=tab:[.],lead:#,space:_,trail:.,eol:&
427  call assert_equal(['<------>__one..two@@#'], ScreenLines(1, virtcol('$')))
428  set listchars<
429  call assert_equal(['[......]##one__two..&'], ScreenLines(1, virtcol('$')))
430
431  " Using setglobal in a window with local setting should not affect the
432  " window. But should impact other windows using the global setting.
433  enew! | only
434  call setline(1, ["\t  one  two  "])
435  set listchars=tab:[.],lead:#,space:_,trail:.,eol:&
436  split
437  setlocal listchars=tab:+-+,lead:^,space:>,trail:<,eol:%
438  split
439  setlocal listchars=tab:<->,lead:_,space:.,trail:@,eol:#
440  setglobal listchars=tab:{.},lead:-,space:=,trail:#,eol:$
441  call assert_equal(['<------>__one..two@@#'], ScreenLines(1, virtcol('$')))
442  close
443  call assert_equal(['+------+^^one>>two<<%'], ScreenLines(1, virtcol('$')))
444  close
445  call assert_equal(['{......}--one==two##$'], ScreenLines(1, virtcol('$')))
446
447  " Setting the global setting to the default value should not impact a window
448  " using a local setting.
449  split
450  setlocal listchars=tab:<->,lead:_,space:.,trail:@,eol:#
451  setglobal listchars&vim
452  call assert_equal(['<------>__one..two@@#'], ScreenLines(1, virtcol('$')))
453  close
454  call assert_equal(['^I  one  two  $'], ScreenLines(1, virtcol('$')))
455
456  " Setting the local setting to the default value should not impact a window
457  " using a global setting.
458  set listchars=tab:{.},lead:-,space:=,trail:#,eol:$
459  split
460  setlocal listchars=tab:<->,lead:_,space:.,trail:@,eol:#
461  call assert_equal(['<------>__one..two@@#'], ScreenLines(1, virtcol('$')))
462  setlocal listchars&vim
463  call assert_equal(['^I  one  two  $'], ScreenLines(1, virtcol('$')))
464  close
465  call assert_equal(['{......}--one==two##$'], ScreenLines(1, virtcol('$')))
466
467  " Using set in a window with a local setting should change it to use the
468  " global setting and also impact other windows using the global setting.
469  split
470  setlocal listchars=tab:<->,lead:_,space:.,trail:@,eol:#
471  call assert_equal(['<------>__one..two@@#'], ScreenLines(1, virtcol('$')))
472  set listchars=tab:+-+,lead:^,space:>,trail:<,eol:%
473  call assert_equal(['+------+^^one>>two<<%'], ScreenLines(1, virtcol('$')))
474  close
475  call assert_equal(['+------+^^one>>two<<%'], ScreenLines(1, virtcol('$')))
476
477  " Setting invalid value for a local setting should not impact the local and
478  " global settings.
479  split
480  setlocal listchars=tab:<->,lead:_,space:.,trail:@,eol:#
481  let cmd = 'setlocal listchars=tab:{.},lead:-,space:=,trail:#,eol:$,x'
482  call assert_fails(cmd, 'E474:')
483  call assert_equal(['<------>__one..two@@#'], ScreenLines(1, virtcol('$')))
484  close
485  call assert_equal(['+------+^^one>>two<<%'], ScreenLines(1, virtcol('$')))
486
487  " Setting invalid value for a global setting should not impact the local and
488  " global settings.
489  split
490  setlocal listchars=tab:<->,lead:_,space:.,trail:@,eol:#
491  let cmd = 'setglobal listchars=tab:{.},lead:-,space:=,trail:#,eol:$,x'
492  call assert_fails(cmd, 'E474:')
493  call assert_equal(['<------>__one..two@@#'], ScreenLines(1, virtcol('$')))
494  close
495  call assert_equal(['+------+^^one>>two<<%'], ScreenLines(1, virtcol('$')))
496
497  " Closing window with local lcs-multispace should not cause a memory leak.
498  setlocal listchars=multispace:---+
499  split
500  call s:CheckListCharsValue('multispace:---+')
501  close
502
503  %bw!
504  set list& listchars&
505endfunc
506
507func Test_listchars_foldcolumn()
508  CheckScreendump
509
510  let lines =<< trim END
511      call setline(1, ['aaa', '', 'a', 'aaaaaa'])
512      vsplit
513      vsplit
514      windo set signcolumn=yes foldcolumn=1 winminwidth=0 nowrap list listchars=extends:>,precedes:<
515  END
516  call writefile(lines, 'XTest_listchars')
517
518  let buf = RunVimInTerminal('-S XTest_listchars', {'rows': 10, 'cols': 60})
519
520  call term_sendkeys(buf, "13\<C-W>>")
521  call VerifyScreenDump(buf, 'Test_listchars_01', {})
522  call term_sendkeys(buf, "\<C-W>>")
523  call VerifyScreenDump(buf, 'Test_listchars_02', {})
524  call term_sendkeys(buf, "\<C-W>>")
525  call VerifyScreenDump(buf, 'Test_listchars_03', {})
526  call term_sendkeys(buf, "\<C-W>>")
527  call VerifyScreenDump(buf, 'Test_listchars_04', {})
528  call term_sendkeys(buf, "\<C-W>>")
529  call VerifyScreenDump(buf, 'Test_listchars_05', {})
530  call term_sendkeys(buf, "\<C-W>h")
531  call term_sendkeys(buf, ":set nowrap foldcolumn=4\<CR>")
532  call term_sendkeys(buf, "15\<C-W><")
533  call VerifyScreenDump(buf, 'Test_listchars_06', {})
534  call term_sendkeys(buf, "4\<C-W><")
535  call VerifyScreenDump(buf, 'Test_listchars_07', {})
536
537  " clean up
538  call StopVimInTerminal(buf)
539  call delete('XTest_listchars')
540endfunc
541
542
543" vim: shiftwidth=2 sts=2 expandtab
544