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