1" Test 'statusline'
2"
3" Not tested yet:
4"   %N
5
6source view_util.vim
7source check.vim
8source screendump.vim
9
10func s:get_statusline()
11  return ScreenLines(&lines - 1, &columns)[0]
12endfunc
13
14func StatuslineWithCaughtError()
15  let s:func_in_statusline_called = 1
16  try
17    call eval('unknown expression')
18  catch
19  endtry
20  return ''
21endfunc
22
23func StatuslineWithError()
24  let s:func_in_statusline_called = 1
25  call eval('unknown expression')
26  return ''
27endfunc
28
29" Function used to display syntax group.
30func SyntaxItem()
31  call assert_equal(s:expected_curbuf, g:actual_curbuf)
32  call assert_equal(s:expected_curwin, g:actual_curwin)
33  return synIDattr(synID(line("."), col("."),1), "name")
34endfunc
35
36func Test_caught_error_in_statusline()
37  let s:func_in_statusline_called = 0
38  set laststatus=2
39  let statusline = '%{StatuslineWithCaughtError()}'
40  let &statusline = statusline
41  redrawstatus
42  call assert_true(s:func_in_statusline_called)
43  call assert_equal(statusline, &statusline)
44  set statusline=
45endfunc
46
47func Test_statusline_will_be_disabled_with_error()
48  let s:func_in_statusline_called = 0
49  set laststatus=2
50  let statusline = '%{StatuslineWithError()}'
51  try
52    let &statusline = statusline
53    redrawstatus
54  catch
55  endtry
56  call assert_true(s:func_in_statusline_called)
57  call assert_equal('', &statusline)
58  set statusline=
59endfunc
60
61func Test_statusline()
62  CheckFeature quickfix
63
64  " %a: Argument list ({current} of {max})
65  set statusline=%a
66  call assert_match('^\s*$', s:get_statusline())
67  arglocal a1 a2
68  rewind
69  call assert_match('^ (1 of 2)\s*$', s:get_statusline())
70  next
71  call assert_match('^ (2 of 2)\s*$', s:get_statusline())
72  e Xstatusline
73  call assert_match('^ ((2) of 2)\s*$', s:get_statusline())
74
75  only
76  set laststatus=2
77  set splitbelow
78  call setline(1, range(1, 10000))
79
80  " %b: Value of character under cursor.
81  " %B: As above, in hexadecimal.
82  call cursor(9000, 1)
83  set statusline=%b,%B
84  call assert_match('^57,39\s*$', s:get_statusline())
85
86  " %o: Byte number in file of byte under cursor, first byte is 1.
87  " %O: As above, in hexadecimal.
88  set statusline=%o,%O
89  set fileformat=dos
90  call assert_match('^52888,CE98\s*$', s:get_statusline())
91  set fileformat=mac
92  call assert_match('^43889,AB71\s*$', s:get_statusline())
93  set fileformat=unix
94  call assert_match('^43889,AB71\s*$', s:get_statusline())
95  set fileformat&
96
97  " %f: Path to the file in the buffer, as typed or relative to current dir.
98  set statusline=%f
99  call assert_match('^Xstatusline\s*$', s:get_statusline())
100
101  " %F: Full path to the file in the buffer.
102  set statusline=%F
103  call assert_match('/testdir/Xstatusline\s*$', s:get_statusline())
104
105  " Test for min and max width with %(. For some reason, if this test is moved
106  " after the below test for the help buffer flag, then the code to truncate
107  " the string is not executed.
108  set statusline=%015(%f%)
109  call assert_match('^    Xstatusline\s*$', s:get_statusline())
110  set statusline=%.6(%f%)
111  call assert_match('^<sline\s*$', s:get_statusline())
112  set statusline=%14f
113  call assert_match('^   Xstatusline\s*$', s:get_statusline())
114  set statusline=%.4L
115  call assert_match('^10>3\s*$', s:get_statusline())
116
117  " %h: Help buffer flag, text is "[help]".
118  " %H: Help buffer flag, text is ",HLP".
119  set statusline=%h,%H
120  call assert_match('^,\s*$', s:get_statusline())
121  help
122  call assert_match('^\[Help\],HLP\s*$', s:get_statusline())
123  helpclose
124
125  " %k: Value of "b:keymap_name" or 'keymap'
126  "     when :lmap mappings are being used: <keymap>"
127  set statusline=%k
128  if has('keymap')
129    set keymap=esperanto
130    call assert_match('^<Eo>\s*$', s:get_statusline())
131    set keymap&
132  else
133    call assert_match('^\s*$', s:get_statusline())
134  endif
135
136  " %l: Line number.
137  " %L: Number of line in buffer.
138  " %c: Column number.
139  set statusline=%l/%L,%c
140  call assert_match('^9000/10000,1\s*$', s:get_statusline())
141
142  " %m: Modified flag, text is "[+]", "[-]" if 'modifiable' is off.
143  " %M: Modified flag, text is ",+" or ",-".
144  set statusline=%m%M
145  call assert_match('^\[+\],+\s*$', s:get_statusline())
146  set nomodifiable
147  call assert_match('^\[+-\],+-\s*$', s:get_statusline())
148  write
149  call assert_match('^\[-\],-\s*$', s:get_statusline())
150  set modifiable&
151  call assert_match('^\s*$', s:get_statusline())
152
153  " %n: Buffer number.
154  set statusline=%n
155  call assert_match('^'.bufnr('%').'\s*$', s:get_statusline())
156
157  " %p: Percentage through file in lines as in CTRL-G.
158  " %P: Percentage through file of displayed window.
159  set statusline=%p,%P
160  0
161  call assert_match('^0,Top\s*$', s:get_statusline())
162  norm G
163  call assert_match('^100,Bot\s*$', s:get_statusline())
164  9000
165  " Don't check the exact percentage as it depends on the window size
166  call assert_match('^90,\(Top\|Bot\|\d\+%\)\s*$', s:get_statusline())
167
168  " %q: "[Quickfix List]", "[Location List]" or empty.
169  set statusline=%q
170  call assert_match('^\s*$', s:get_statusline())
171  copen
172  call assert_match('^\[Quickfix List\]\s*$', s:get_statusline())
173  cclose
174  lexpr getline(1, 2)
175  lopen
176  call assert_match('^\[Location List\]\s*$', s:get_statusline())
177  lclose
178
179  " %r: Readonly flag, text is "[RO]".
180  " %R: Readonly flag, text is ",RO".
181  set statusline=%r,%R
182  call assert_match('^,\s*$', s:get_statusline())
183  help
184  call assert_match('^\[RO\],RO\s*$', s:get_statusline())
185  helpclose
186
187  " %t: File name (tail) of file in the buffer.
188  set statusline=%t
189  call assert_match('^Xstatusline\s*$', s:get_statusline())
190
191  " %v: Virtual column number.
192  " %V: Virtual column number as -{num}. Not displayed if equal to 'c'.
193  call cursor(9000, 2)
194  set statusline=%v,%V
195  call assert_match('^2,\s*$', s:get_statusline())
196  set virtualedit=all
197  norm 10|
198  call assert_match('^10,-10\s*$', s:get_statusline())
199  set virtualedit&
200
201  " %w: Preview window flag, text is "[Preview]".
202  " %W: Preview window flag, text is ",PRV".
203  set statusline=%w%W
204  call assert_match('^\s*$', s:get_statusline())
205  pedit
206  wincmd j
207  call assert_match('^\[Preview\],PRV\s*$', s:get_statusline())
208  pclose
209
210  " %y: Type of file in the buffer, e.g., "[vim]". See 'filetype'.
211  " %Y: Type of file in the buffer, e.g., ",VIM". See 'filetype'.
212  set statusline=%y\ %Y
213  call assert_match('^\s*$', s:get_statusline())
214  setfiletype vim
215  call assert_match('^\[vim\] VIM\s*$', s:get_statusline())
216
217  " %=: Separation point between left and right aligned items.
218  set statusline=foo%=bar
219  call assert_match('^foo\s\+bar\s*$', s:get_statusline())
220
221  " Test min/max width, leading zeroes, left/right justify.
222  set statusline=%04B
223  call cursor(9000, 1)
224  call assert_match('^0039\s*$', s:get_statusline())
225  set statusline=#%4B#
226  call assert_match('^#  39#\s*$', s:get_statusline())
227  set statusline=#%-4B#
228  call assert_match('^#39  #\s*$', s:get_statusline())
229  set statusline=%.6f
230  call assert_match('^<sline\s*$', s:get_statusline())
231
232  " %<: Where to truncate.
233  " First check with when %< should not truncate with many columns
234  exe 'set statusline=a%<b' . repeat('c', &columns - 3) . 'd'
235  call assert_match('^abc\+d$', s:get_statusline())
236  exe 'set statusline=a' . repeat('b', &columns - 2) . '%<c'
237  call assert_match('^ab\+c$', s:get_statusline())
238  " Then check when %< should truncate when there with too few columns.
239  exe 'set statusline=a%<b' . repeat('c', &columns - 2) . 'd'
240  call assert_match('^a<c\+d$', s:get_statusline())
241  exe 'set statusline=a' . repeat('b', &columns - 1) . '%<c'
242  call assert_match('^ab\+>$', s:get_statusline())
243
244  "%{: Evaluate expression between '%{' and '}' and substitute result.
245  syntax on
246  let s:expected_curbuf = string(bufnr(''))
247  let s:expected_curwin = string(win_getid())
248  set statusline=%{SyntaxItem()}
249  call assert_match('^vimNumber\s*$', s:get_statusline())
250  s/^/"/
251  call assert_match('^vimLineComment\s*$', s:get_statusline())
252  syntax off
253
254  "%{%expr%}: evaluates enxpressions present in result of expr
255  func! Inner_eval()
256    return '%n some other text'
257  endfunc
258  func! Outer_eval()
259    return 'some text %{%Inner_eval()%}'
260  endfunc
261  set statusline=%{%Outer_eval()%}
262  call assert_match('^some text ' . bufnr() . ' some other text\s*$', s:get_statusline())
263  delfunc Inner_eval
264  delfunc Outer_eval
265
266  "%{%expr%}: Doesn't get stuck in recursion
267  func! Recurse_eval()
268    return '%{%Recurse_eval()%}'
269  endfunc
270  set statusline=%{%Recurse_eval()%}
271  call assert_match('^%{%Recurse_eval()%}\s*$', s:get_statusline())
272  delfunc Recurse_eval
273
274  "%(: Start of item group.
275  set statusline=ab%(cd%q%)de
276  call assert_match('^abde\s*$', s:get_statusline())
277  copen
278  call assert_match('^abcd\[Quickfix List]de\s*$', s:get_statusline())
279  cclose
280
281  " %#: Set highlight group. The name must follow and then a # again.
282  set statusline=ab%#Todo#cd%#Error#ef
283  call assert_match('^abcdef\s*$', s:get_statusline())
284  let sa1=screenattr(&lines - 1, 1)
285  let sa2=screenattr(&lines - 1, 3)
286  let sa3=screenattr(&lines - 1, 5)
287  call assert_notequal(sa1, sa2)
288  call assert_notequal(sa1, sa3)
289  call assert_notequal(sa2, sa3)
290  call assert_equal(sa1, screenattr(&lines - 1, 2))
291  call assert_equal(sa2, screenattr(&lines - 1, 4))
292  call assert_equal(sa3, screenattr(&lines - 1, 6))
293  call assert_equal(sa3, screenattr(&lines - 1, 7))
294
295  " %*: Set highlight group to User{N}
296  set statusline=a%1*b%0*c
297  call assert_match('^abc\s*$', s:get_statusline())
298  let sa1=screenattr(&lines - 1, 1)
299  let sa2=screenattr(&lines - 1, 2)
300  let sa3=screenattr(&lines - 1, 3)
301  call assert_equal(sa1, sa3)
302  call assert_notequal(sa1, sa2)
303
304  " An empty group that contains highlight changes
305  let g:a = ''
306  set statusline=ab%(cd%1*%{g:a}%*%)de
307  call assert_match('^abde\s*$', s:get_statusline())
308  let sa1=screenattr(&lines - 1, 1)
309  let sa2=screenattr(&lines - 1, 4)
310  call assert_equal(sa1, sa2)
311  let g:a = 'X'
312  call assert_match('^abcdXde\s*$', s:get_statusline())
313  let sa1=screenattr(&lines - 1, 1)
314  let sa2=screenattr(&lines - 1, 5)
315  let sa3=screenattr(&lines - 1, 7)
316  call assert_equal(sa1, sa3)
317  call assert_notequal(sa1, sa2)
318
319  let g:a = ''
320  set statusline=ab%1*%(cd%*%{g:a}%1*%)de
321  call assert_match('^abde\s*$', s:get_statusline())
322  let sa1=screenattr(&lines - 1, 1)
323  let sa2=screenattr(&lines - 1, 4)
324  call assert_notequal(sa1, sa2)
325  let g:a = 'X'
326  call assert_match('^abcdXde\s*$', s:get_statusline())
327  let sa1=screenattr(&lines - 1, 1)
328  let sa2=screenattr(&lines - 1, 3)
329  let sa3=screenattr(&lines - 1, 5)
330  let sa4=screenattr(&lines - 1, 7)
331  call assert_notequal(sa1, sa2)
332  call assert_equal(sa1, sa3)
333  call assert_equal(sa2, sa4)
334
335  " An empty group that contains highlight changes and doesn't reset them
336  let g:a = ''
337  set statusline=ab%(cd%1*%{g:a}%)de
338  call assert_match('^abcdde\s*$', s:get_statusline())
339  let sa1=screenattr(&lines - 1, 1)
340  let sa2=screenattr(&lines - 1, 5)
341  call assert_notequal(sa1, sa2)
342  let g:a = 'X'
343  call assert_match('^abcdXde\s*$', s:get_statusline())
344  let sa1=screenattr(&lines - 1, 1)
345  let sa2=screenattr(&lines - 1, 5)
346  let sa3=screenattr(&lines - 1, 7)
347  call assert_notequal(sa1, sa2)
348  call assert_equal(sa2, sa3)
349
350  let g:a = ''
351  set statusline=ab%1*%(cd%*%{g:a}%)de
352  call assert_match('^abcdde\s*$', s:get_statusline())
353  let sa1=screenattr(&lines - 1, 1)
354  let sa2=screenattr(&lines - 1, 3)
355  let sa3=screenattr(&lines - 1, 5)
356  call assert_notequal(sa1, sa2)
357  call assert_equal(sa1, sa3)
358  let g:a = 'X'
359  call assert_match('^abcdXde\s*$', s:get_statusline())
360  let sa1=screenattr(&lines - 1, 1)
361  let sa2=screenattr(&lines - 1, 3)
362  let sa3=screenattr(&lines - 1, 5)
363  let sa4=screenattr(&lines - 1, 7)
364  call assert_notequal(sa1, sa2)
365  call assert_equal(sa1, sa3)
366  call assert_equal(sa1, sa4)
367
368  let g:a = ''
369  set statusline=%#Error#{%(\ %{g:a}\ %)}
370  call assert_match('^{}\s*$', s:get_statusline())
371  let g:a = 'X'
372  call assert_match('^{ X }\s*$', s:get_statusline())
373
374  " %%: a percent sign.
375  set statusline=10%%
376  call assert_match('^10%\s*$', s:get_statusline())
377
378  " %!: evaluated expression is used as the option value
379  set statusline=%!2*3+1
380  call assert_match('7\s*$', s:get_statusline())
381
382  func GetNested()
383    call assert_equal(string(win_getid()), g:actual_curwin)
384    call assert_equal(string(bufnr('')), g:actual_curbuf)
385    return 'nested'
386  endfunc
387  func GetStatusLine()
388    call assert_equal(win_getid(), g:statusline_winid)
389    return 'the %{GetNested()} line'
390  endfunc
391  set statusline=%!GetStatusLine()
392  call assert_match('the nested line', s:get_statusline())
393  call assert_false(exists('g:actual_curwin'))
394  call assert_false(exists('g:actual_curbuf'))
395  call assert_false(exists('g:statusline_winid'))
396  delfunc GetNested
397  delfunc GetStatusLine
398
399  " Test statusline works with 80+ items
400  function! StatusLabel()
401    redrawstatus
402    return '[label]'
403  endfunc
404  let statusline = '%{StatusLabel()}'
405  for i in range(150)
406    let statusline .= '%#TabLine' . (i % 2 == 0 ? 'Fill' : 'Sel') . '#' . string(i)[0]
407  endfor
408  let &statusline = statusline
409  redrawstatus
410  set statusline&
411  delfunc StatusLabel
412
413
414  " Check statusline in current and non-current window
415  " with the 'fillchars' option.
416  set fillchars=stl:^,stlnc:=,vert:\|,fold:-,diff:-
417  vsplit
418  set statusline=x%=y
419  call assert_match('^x^\+y^x=\+y$', s:get_statusline())
420  set fillchars&
421  close
422
423  %bw!
424  call delete('Xstatusline')
425  set statusline&
426  set laststatus&
427  set splitbelow&
428endfunc
429
430func Test_statusline_visual()
431  func CallWordcount()
432    call wordcount()
433  endfunc
434  new x1
435  setl statusline=count=%{CallWordcount()}
436  " buffer must not be empty
437  call setline(1, 'hello')
438
439  " window with more lines than x1
440  new x2
441  call setline(1, range(10))
442  $
443  " Visual mode in line below liast line in x1 should not give ml_get error
444  call feedkeys("\<C-V>", "xt")
445  redraw
446
447  delfunc CallWordcount
448  bwipe! x1
449  bwipe! x2
450endfunc
451
452func Test_statusline_removed_group()
453  CheckScreendump
454
455  let lines =<< trim END
456    scriptencoding utf-8
457    set laststatus=2
458    let &statusline = '%#StatColorHi2#%(✓%#StatColorHi2#%) Q≡'
459  END
460  call writefile(lines, 'XTest_statusline')
461
462  let buf = RunVimInTerminal('-S XTest_statusline', {'rows': 10, 'cols': 50})
463  call TermWait(buf, 50)
464  call VerifyScreenDump(buf, 'Test_statusline_1', {})
465
466  " clean up
467  call StopVimInTerminal(buf)
468  call delete('XTest_statusline')
469endfunc
470
471func Test_statusline_using_mode()
472  CheckScreendump
473
474  let lines =<< trim END
475    setlocal statusline=-%{mode()}-
476    split
477    setlocal statusline=+%{mode()}+
478  END
479  call writefile(lines, 'XTest_statusline')
480
481  let buf = RunVimInTerminal('-S XTest_statusline', {'rows': 7, 'cols': 50})
482  call VerifyScreenDump(buf, 'Test_statusline_mode_1', {})
483
484  call term_sendkeys(buf, ":")
485  call VerifyScreenDump(buf, 'Test_statusline_mode_2', {})
486
487  " clean up
488  call term_sendkeys(buf, "close\<CR>")
489  call StopVimInTerminal(buf)
490  call delete('XTest_statusline')
491endfunc
492
493func Test_statusline_after_split_vsplit()
494  only
495
496  " Make the status line of each window show the window number.
497  set ls=2 stl=%{winnr()}
498
499  split | redraw
500  vsplit | redraw
501
502  " The status line of the third window should read '3' here.
503  call assert_equal('3', nr2char(screenchar(&lines - 1, 1)))
504
505  only
506  set ls& stl&
507endfunc
508
509" Test using a multibyte character for 'stl' and 'stlnc' items in 'fillchars'
510" with a custom 'statusline'
511func Test_statusline_mbyte_fillchar()
512  only
513  set laststatus=2
514  set fillchars=vert:\|,fold:-,stl:━,stlnc:═
515  set statusline=a%=b
516  call assert_match('^a\+━\+b$', s:get_statusline())
517  vnew
518  call assert_match('^a\+━\+b━a\+═\+b$', s:get_statusline())
519  wincmd w
520  call assert_match('^a\+═\+b═a\+━\+b$', s:get_statusline())
521  set statusline& fillchars& laststatus&
522  %bw!
523endfunc
524
525" Used to write beyond allocated memory.  This assumes MAXPATHL is 4096 bytes.
526func Test_statusline_verylong_filename()
527  let fname = repeat('x', 4090)
528  exe "new " .. fname
529  set buftype=help
530  set previewwindow
531  redraw
532  bwipe!
533endfunc
534
535" vim: shiftwidth=2 sts=2 expandtab
536