statusline_spec.lua 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472
  1. local t = require('test.unit.testutil')
  2. local itp = t.gen_itp(it)
  3. local to_cstr = t.to_cstr
  4. local get_str = t.ffi.string
  5. local eq = t.eq
  6. local NULL = t.NULL
  7. local buffer = t.cimport('./src/nvim/buffer.h')
  8. local globals = t.cimport('./src/nvim/globals.h')
  9. local stl = t.cimport('./src/nvim/statusline.h')
  10. local grid = t.cimport('./src/nvim/grid.h')
  11. describe('build_stl_str_hl', function()
  12. local buffer_byte_size = 100
  13. local STL_INITIAL_ITEMS = 20
  14. local output_buffer = ''
  15. -- This function builds the statusline
  16. --
  17. -- @param arg Optional arguments are:
  18. -- .pat The statusline format string
  19. -- .fillchar The fill character used in the statusline
  20. -- .maximum_cell_count The number of cells available in the statusline
  21. local function build_stl_str_hl(arg)
  22. output_buffer = to_cstr(string.rep(' ', buffer_byte_size))
  23. local pat = arg.pat or ''
  24. local fillchar = arg.fillchar or ' '
  25. local maximum_cell_count = arg.maximum_cell_count or buffer_byte_size
  26. if type(fillchar) == type('') then
  27. fillchar = grid.schar_from_str(fillchar)
  28. end
  29. return stl.build_stl_str_hl(
  30. globals.curwin,
  31. output_buffer,
  32. buffer_byte_size,
  33. to_cstr(pat),
  34. -1,
  35. 0,
  36. fillchar,
  37. maximum_cell_count,
  38. NULL,
  39. NULL,
  40. NULL,
  41. NULL
  42. )
  43. end
  44. -- Use this function to simplify testing the comparison between
  45. -- the format string and the resulting statusline.
  46. --
  47. -- @param description The description of what the test should be doing
  48. -- @param statusline_cell_count The number of cells available in the statusline
  49. -- @param input_stl The format string for the statusline
  50. -- @param expected_stl The expected result string for the statusline
  51. --
  52. -- @param arg Options can be placed in an optional dict as the last parameter
  53. -- .expected_cell_count The expected number of cells build_stl_str_hl will return
  54. -- .expected_byte_length The expected byte length of the string (defaults to byte length of expected_stl)
  55. -- .file_name The name of the file to be tested (useful in %f type tests)
  56. -- .fillchar The character that will be used to fill any 'extra' space in the stl
  57. local function statusline_test(description, statusline_cell_count, input_stl, expected_stl, arg)
  58. -- arg is the optional parameter
  59. -- so we either fill in option with arg or an empty dict
  60. local option = arg or {}
  61. local fillchar = option.fillchar or ' '
  62. local expected_cell_count = option.expected_cell_count or statusline_cell_count
  63. local expected_byte_length = option.expected_byte_length or #expected_stl
  64. itp(description, function()
  65. if option.file_name then
  66. buffer.setfname(globals.curbuf, to_cstr(option.file_name), NULL, 1)
  67. else
  68. buffer.setfname(globals.curbuf, nil, NULL, 1)
  69. end
  70. local result_cell_count = build_stl_str_hl {
  71. pat = input_stl,
  72. maximum_cell_count = statusline_cell_count,
  73. fillchar = fillchar,
  74. }
  75. eq(expected_stl, get_str(output_buffer, expected_byte_length))
  76. eq(expected_cell_count, result_cell_count)
  77. end)
  78. end
  79. -- expression testing
  80. statusline_test('Should expand expression', 2, '%!expand(20+1)', '21')
  81. statusline_test('Should expand broken expression to itself', 11, '%!expand(20+1', 'expand(20+1')
  82. -- file name testing
  83. statusline_test('should print no file name', 10, '%f', '[No Name]', { expected_cell_count = 9 })
  84. statusline_test(
  85. 'should print the relative file name',
  86. 30,
  87. '%f',
  88. 'test/unit/buffer_spec.lua',
  89. { file_name = 'test/unit/buffer_spec.lua', expected_cell_count = 25 }
  90. )
  91. statusline_test(
  92. 'should print the full file name',
  93. 40,
  94. '%F',
  95. '/test/unit/buffer_spec.lua',
  96. { file_name = '/test/unit/buffer_spec.lua', expected_cell_count = 26 }
  97. )
  98. -- fillchar testing
  99. statusline_test(
  100. 'should handle `!` as a fillchar',
  101. 10,
  102. 'abcde%=',
  103. 'abcde!!!!!',
  104. { fillchar = '!' }
  105. )
  106. statusline_test(
  107. 'should handle `~` as a fillchar',
  108. 10,
  109. '%=abcde',
  110. '~~~~~abcde',
  111. { fillchar = '~' }
  112. )
  113. statusline_test(
  114. 'should put fillchar `!` in between text',
  115. 10,
  116. 'abc%=def',
  117. 'abc!!!!def',
  118. { fillchar = '!' }
  119. )
  120. statusline_test(
  121. 'should put fillchar `~` in between text',
  122. 10,
  123. 'abc%=def',
  124. 'abc~~~~def',
  125. { fillchar = '~' }
  126. )
  127. statusline_test(
  128. 'should put fillchar `━` in between text',
  129. 10,
  130. 'abc%=def',
  131. 'abc━━━━def',
  132. { fillchar = '━' }
  133. )
  134. statusline_test(
  135. 'should handle zero-fillchar as a space',
  136. 10,
  137. 'abcde%=',
  138. 'abcde ',
  139. { fillchar = 0 }
  140. )
  141. statusline_test(
  142. 'should print the tail file name',
  143. 80,
  144. '%t',
  145. 'buffer_spec.lua',
  146. { file_name = 'test/unit/buffer_spec.lua', expected_cell_count = 15 }
  147. )
  148. -- standard text testing
  149. statusline_test(
  150. 'should copy plain text',
  151. 80,
  152. 'this is a test',
  153. 'this is a test',
  154. { expected_cell_count = 14 }
  155. )
  156. -- line number testing
  157. statusline_test('should print the buffer number', 80, '%n', '1', { expected_cell_count = 1 })
  158. statusline_test(
  159. 'should print the current line number in the buffer',
  160. 80,
  161. '%l',
  162. '0',
  163. { expected_cell_count = 1 }
  164. )
  165. statusline_test(
  166. 'should print the number of lines in the buffer',
  167. 80,
  168. '%L',
  169. '1',
  170. { expected_cell_count = 1 }
  171. )
  172. -- truncation testing
  173. statusline_test(
  174. 'should truncate when standard text pattern is too long',
  175. 10,
  176. '0123456789abcde',
  177. '<6789abcde'
  178. )
  179. statusline_test('should truncate when using =', 10, 'abcdef%=ghijkl', 'abcdef<jkl')
  180. statusline_test(
  181. 'should truncate centered text when using ==',
  182. 10,
  183. 'abcde%=gone%=fghij',
  184. 'abcde<ghij'
  185. )
  186. statusline_test('should respect the `<` marker', 10, 'abc%<defghijkl', 'abc<ghijkl')
  187. statusline_test(
  188. 'should truncate at `<` with one `=`, test 1',
  189. 10,
  190. 'abc%<def%=ghijklmno',
  191. 'abc<jklmno'
  192. )
  193. statusline_test(
  194. 'should truncate at `<` with one `=`, test 2',
  195. 10,
  196. 'abcdef%=ghijkl%<mno',
  197. 'abcdefghi>'
  198. )
  199. statusline_test(
  200. 'should truncate at `<` with one `=`, test 3',
  201. 10,
  202. 'abc%<def%=ghijklmno',
  203. 'abc<jklmno'
  204. )
  205. statusline_test('should truncate at `<` with one `=`, test 4', 10, 'abc%<def%=ghij', 'abcdefghij')
  206. statusline_test(
  207. 'should truncate at `<` with one `=`, test 4',
  208. 10,
  209. 'abc%<def%=ghijk',
  210. 'abc<fghijk'
  211. )
  212. statusline_test(
  213. 'should truncate at `<` with many `=`, test 4',
  214. 10,
  215. 'ab%<cdef%=g%=h%=ijk',
  216. 'ab<efghijk'
  217. )
  218. statusline_test('should truncate at the first `<`', 10, 'abc%<def%<ghijklm', 'abc<hijklm')
  219. statusline_test('should ignore trailing %', 3, 'abc%', 'abc')
  220. -- alignment testing with fillchar
  221. local function statusline_test_align(
  222. description,
  223. statusline_cell_count,
  224. input_stl,
  225. expected_stl,
  226. arg
  227. )
  228. arg = arg or {}
  229. statusline_test(
  230. description .. ' without fillchar',
  231. statusline_cell_count,
  232. input_stl,
  233. expected_stl:gsub('%~', ' '),
  234. arg
  235. )
  236. arg.fillchar = '!'
  237. statusline_test(
  238. description .. ' with fillchar `!`',
  239. statusline_cell_count,
  240. input_stl,
  241. expected_stl:gsub('%~', '!'),
  242. arg
  243. )
  244. arg.fillchar = '━'
  245. statusline_test(
  246. description .. ' with fillchar `━`',
  247. statusline_cell_count,
  248. input_stl,
  249. expected_stl:gsub('%~', '━'),
  250. arg
  251. )
  252. end
  253. statusline_test_align('should right align when using =', 20, 'neo%=vim', 'neo~~~~~~~~~~~~~~vim')
  254. statusline_test_align(
  255. 'should, when possible, center text when using %=text%=',
  256. 20,
  257. 'abc%=neovim%=def',
  258. 'abc~~~~neovim~~~~def'
  259. )
  260. statusline_test_align(
  261. 'should handle uneven spacing in the buffer when using %=text%=',
  262. 20,
  263. 'abc%=neo_vim%=def',
  264. 'abc~~~neo_vim~~~~def'
  265. )
  266. statusline_test_align(
  267. 'should have equal spaces even with non-equal sides when using =',
  268. 20,
  269. 'foobar%=test%=baz',
  270. 'foobar~~~test~~~~baz'
  271. )
  272. statusline_test_align(
  273. 'should have equal spaces even with longer right side when using =',
  274. 20,
  275. 'a%=test%=longtext',
  276. 'a~~~test~~~~longtext'
  277. )
  278. statusline_test_align(
  279. 'should handle an empty left side when using ==',
  280. 20,
  281. '%=test%=baz',
  282. '~~~~~~test~~~~~~~baz'
  283. )
  284. statusline_test_align(
  285. 'should handle an empty right side when using ==',
  286. 20,
  287. 'foobar%=test%=',
  288. 'foobar~~~~~test~~~~~'
  289. )
  290. statusline_test_align(
  291. 'should handle consecutive empty ==',
  292. 20,
  293. '%=%=test%=',
  294. '~~~~~~~~~~test~~~~~~'
  295. )
  296. statusline_test_align('should handle an = alone', 20, '%=', '~~~~~~~~~~~~~~~~~~~~')
  297. statusline_test_align(
  298. 'should right align text when it is alone with =',
  299. 20,
  300. '%=foo',
  301. '~~~~~~~~~~~~~~~~~foo'
  302. )
  303. statusline_test_align(
  304. 'should left align text when it is alone with =',
  305. 20,
  306. 'foo%=',
  307. 'foo~~~~~~~~~~~~~~~~~'
  308. )
  309. statusline_test_align(
  310. 'should approximately center text when using %=text%=',
  311. 21,
  312. 'abc%=neovim%=def',
  313. 'abc~~~~neovim~~~~~def'
  314. )
  315. statusline_test_align(
  316. 'should completely fill the buffer when using %=text%=',
  317. 21,
  318. 'abc%=neo_vim%=def',
  319. 'abc~~~~neo_vim~~~~def'
  320. )
  321. statusline_test_align(
  322. 'should have equal spacing even with non-equal sides when using =',
  323. 21,
  324. 'foobar%=test%=baz',
  325. 'foobar~~~~test~~~~baz'
  326. )
  327. statusline_test_align(
  328. 'should have equal spacing even with longer right side when using =',
  329. 21,
  330. 'a%=test%=longtext',
  331. 'a~~~~test~~~~longtext'
  332. )
  333. statusline_test_align(
  334. 'should handle an empty left side when using ==',
  335. 21,
  336. '%=test%=baz',
  337. '~~~~~~~test~~~~~~~baz'
  338. )
  339. statusline_test_align(
  340. 'should handle an empty right side when using ==',
  341. 21,
  342. 'foobar%=test%=',
  343. 'foobar~~~~~test~~~~~~'
  344. )
  345. statusline_test_align(
  346. 'should quadrant the text when using 3 %=',
  347. 40,
  348. 'abcd%=n%=eovim%=ef',
  349. 'abcd~~~~~~~~~n~~~~~~~~~eovim~~~~~~~~~~ef'
  350. )
  351. statusline_test_align(
  352. 'should work well with %t',
  353. 40,
  354. '%t%=right_aligned',
  355. 'buffer_spec.lua~~~~~~~~~~~~right_aligned',
  356. { file_name = 'test/unit/buffer_spec.lua' }
  357. )
  358. statusline_test_align(
  359. 'should work well with %t and regular text',
  360. 40,
  361. 'l%=m_l %t m_r%=r',
  362. 'l~~~~~~~m_l buffer_spec.lua m_r~~~~~~~~r',
  363. { file_name = 'test/unit/buffer_spec.lua' }
  364. )
  365. statusline_test_align(
  366. 'should work well with %=, %t, %L, and %l',
  367. 40,
  368. '%t %= %L %= %l',
  369. 'buffer_spec.lua ~~~~~~~~~ 1 ~~~~~~~~~~ 0',
  370. { file_name = 'test/unit/buffer_spec.lua' }
  371. )
  372. statusline_test_align(
  373. 'should quadrant the text when using 3 %=',
  374. 41,
  375. 'abcd%=n%=eovim%=ef',
  376. 'abcd~~~~~~~~~n~~~~~~~~~eovim~~~~~~~~~~~ef'
  377. )
  378. statusline_test_align(
  379. 'should work well with %t',
  380. 41,
  381. '%t%=right_aligned',
  382. 'buffer_spec.lua~~~~~~~~~~~~~right_aligned',
  383. { file_name = 'test/unit/buffer_spec.lua' }
  384. )
  385. statusline_test_align(
  386. 'should work well with %t and regular text',
  387. 41,
  388. 'l%=m_l %t m_r%=r',
  389. 'l~~~~~~~~m_l buffer_spec.lua m_r~~~~~~~~r',
  390. { file_name = 'test/unit/buffer_spec.lua' }
  391. )
  392. statusline_test_align(
  393. 'should work well with %=, %t, %L, and %l',
  394. 41,
  395. '%t %= %L %= %l',
  396. 'buffer_spec.lua ~~~~~~~~~~ 1 ~~~~~~~~~~ 0',
  397. { file_name = 'test/unit/buffer_spec.lua' }
  398. )
  399. statusline_test_align(
  400. 'should work with 10 %=',
  401. 50,
  402. 'aaaa%=b%=c%=d%=e%=fg%=hi%=jk%=lmnop%=qrstuv%=wxyz',
  403. 'aaaa~~b~~c~~d~~e~~fg~~hi~~jk~~lmnop~~qrstuv~~~wxyz'
  404. )
  405. -- stl item testing
  406. local tabline = ''
  407. for i = 1, 1000 do
  408. tabline = tabline .. (i % 2 == 0 and '%#TabLineSel#' or '%#TabLineFill#') .. tostring(i % 2)
  409. end
  410. statusline_test('should handle a large amount of any items', 20, tabline, '<1010101010101010101') -- Should not show any error
  411. statusline_test(
  412. 'should handle a larger amount of = than stl initial item',
  413. 20,
  414. ('%='):rep(STL_INITIAL_ITEMS * 5),
  415. ' '
  416. ) -- Should not show any error
  417. statusline_test(
  418. 'should handle many extra characters',
  419. 20,
  420. 'a' .. ('a'):rep(STL_INITIAL_ITEMS * 5),
  421. '<aaaaaaaaaaaaaaaaaaa'
  422. ) -- Does not show any error
  423. statusline_test(
  424. 'should handle many extra characters and flags',
  425. 20,
  426. 'a' .. ('%=a'):rep(STL_INITIAL_ITEMS * 2),
  427. 'a<aaaaaaaaaaaaaaaaaa'
  428. ) -- Should not show any error
  429. -- multi-byte testing
  430. statusline_test('should handle multibyte characters', 10, 'Ĉ%=x', 'Ĉ x')
  431. statusline_test(
  432. 'should handle multibyte characters and different fillchars',
  433. 10,
  434. 'Ą%=mid%=end',
  435. 'Ą@mid@@end',
  436. { fillchar = '@' }
  437. )
  438. -- escaping % testing
  439. statusline_test('should handle escape of %', 4, 'abc%%', 'abc%')
  440. statusline_test('case where escaped % does not fit', 3, 'abc%%abcabc', '<bc')
  441. statusline_test('escaped % is first', 1, '%%', '%')
  442. end)