buf_functions_spec.lua 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  1. local t = require('test.testutil')
  2. local n = require('test.functional.testnvim')()
  3. local eq = t.eq
  4. local clear = n.clear
  5. local fn = n.fn
  6. local api = n.api
  7. local command = n.command
  8. local exc_exec = n.exc_exec
  9. local get_pathsep = n.get_pathsep
  10. local rmdir = n.rmdir
  11. local pcall_err = t.pcall_err
  12. local mkdir = t.mkdir
  13. local fname = 'Xtest-functional-eval-buf_functions'
  14. local fname2 = fname .. '.2'
  15. local dirname = fname .. '.d'
  16. before_each(clear)
  17. for _, func in ipairs({
  18. 'bufname(%s)',
  19. 'bufnr(%s)',
  20. 'bufwinnr(%s)',
  21. 'getbufline(%s, 1)',
  22. 'getbufvar(%s, "changedtick")',
  23. 'setbufvar(%s, "f", 0)',
  24. }) do
  25. local funcname = func:match('%w+')
  26. describe(funcname .. '() function', function()
  27. it('errors out when receives v:true/v:false/v:null', function()
  28. -- Not compatible with Vim: in Vim it always results in buffer not found
  29. -- without any error messages.
  30. for _, var in ipairs({ 'v:true', 'v:false' }) do
  31. eq(
  32. 'Vim(call):E5299: Expected a Number or a String, Boolean found',
  33. exc_exec('call ' .. func:format(var))
  34. )
  35. end
  36. eq(
  37. 'Vim(call):E5300: Expected a Number or a String',
  38. exc_exec('call ' .. func:format('v:null'))
  39. )
  40. end)
  41. it('errors out when receives invalid argument', function()
  42. eq(
  43. 'Vim(call):E745: Expected a Number or a String, List found',
  44. exc_exec('call ' .. func:format('[]'))
  45. )
  46. eq(
  47. 'Vim(call):E728: Expected a Number or a String, Dictionary found',
  48. exc_exec('call ' .. func:format('{}'))
  49. )
  50. eq(
  51. 'Vim(call):E805: Expected a Number or a String, Float found',
  52. exc_exec('call ' .. func:format('0.0'))
  53. )
  54. eq(
  55. 'Vim(call):E703: Expected a Number or a String, Funcref found',
  56. exc_exec('call ' .. func:format('function("tr")'))
  57. )
  58. end)
  59. end)
  60. end
  61. describe('bufname() function', function()
  62. it('returns empty string when buffer was not found', function()
  63. command('file ' .. fname)
  64. eq('', fn.bufname(2))
  65. eq('', fn.bufname('non-existent-buffer'))
  66. eq('', fn.bufname('#'))
  67. command('edit ' .. fname2)
  68. eq(2, fn.bufnr('%'))
  69. eq('', fn.bufname('X'))
  70. end)
  71. before_each(function()
  72. mkdir(dirname)
  73. end)
  74. after_each(function()
  75. rmdir(dirname)
  76. end)
  77. it('returns expected buffer name', function()
  78. eq('', fn.bufname('%')) -- Buffer has no name yet
  79. command('file ' .. fname)
  80. local wd = vim.uv.cwd()
  81. local sep = get_pathsep()
  82. local curdirname = fn.fnamemodify(wd, ':t')
  83. for _, arg in ipairs({ '%', 1, 'X', wd }) do
  84. eq(fname, fn.bufname(arg))
  85. api.nvim_set_current_dir('..')
  86. eq(curdirname .. sep .. fname, fn.bufname(arg))
  87. api.nvim_set_current_dir(curdirname)
  88. api.nvim_set_current_dir(dirname)
  89. eq(wd .. sep .. fname, fn.bufname(arg))
  90. api.nvim_set_current_dir('..')
  91. eq(fname, fn.bufname(arg))
  92. command('enew')
  93. end
  94. eq('', fn.bufname('%'))
  95. eq('', fn.bufname('$'))
  96. eq(2, fn.bufnr('%'))
  97. end)
  98. end)
  99. describe('bufnr() function', function()
  100. it('returns -1 when buffer was not found', function()
  101. command('file ' .. fname)
  102. eq(-1, fn.bufnr(2))
  103. eq(-1, fn.bufnr('non-existent-buffer'))
  104. eq(-1, fn.bufnr('#'))
  105. command('edit ' .. fname2)
  106. eq(2, fn.bufnr('%'))
  107. eq(-1, fn.bufnr('X'))
  108. end)
  109. it('returns expected buffer number', function()
  110. eq(1, fn.bufnr('%'))
  111. command('file ' .. fname)
  112. local wd = vim.uv.cwd()
  113. local curdirname = fn.fnamemodify(wd, ':t')
  114. eq(1, fn.bufnr(fname))
  115. eq(1, fn.bufnr(wd))
  116. eq(1, fn.bufnr(curdirname))
  117. eq(1, fn.bufnr('X'))
  118. end)
  119. it('returns number of last buffer with "$"', function()
  120. eq(1, fn.bufnr('$'))
  121. command('new')
  122. eq(2, fn.bufnr('$'))
  123. command('new')
  124. eq(3, fn.bufnr('$'))
  125. command('only')
  126. eq(3, fn.bufnr('$'))
  127. eq(3, fn.bufnr('%'))
  128. command('buffer 1')
  129. eq(3, fn.bufnr('$'))
  130. eq(1, fn.bufnr('%'))
  131. command('bwipeout 2')
  132. eq(3, fn.bufnr('$'))
  133. eq(1, fn.bufnr('%'))
  134. command('bwipeout 3')
  135. eq(1, fn.bufnr('$'))
  136. eq(1, fn.bufnr('%'))
  137. command('new')
  138. eq(4, fn.bufnr('$'))
  139. end)
  140. end)
  141. describe('bufwinnr() function', function()
  142. it('returns -1 when buffer was not found', function()
  143. command('file ' .. fname)
  144. eq(-1, fn.bufwinnr(2))
  145. eq(-1, fn.bufwinnr('non-existent-buffer'))
  146. eq(-1, fn.bufwinnr('#'))
  147. command('split ' .. fname2) -- It would be OK if there was one window
  148. eq(2, fn.bufnr('%'))
  149. eq(-1, fn.bufwinnr('X'))
  150. end)
  151. before_each(function()
  152. mkdir(dirname)
  153. end)
  154. after_each(function()
  155. rmdir(dirname)
  156. end)
  157. it('returns expected window number', function()
  158. eq(1, fn.bufwinnr('%'))
  159. command('file ' .. fname)
  160. command('vsplit')
  161. command('split ' .. fname2)
  162. eq(2, fn.bufwinnr(fname))
  163. eq(1, fn.bufwinnr(fname2))
  164. eq(-1, fn.bufwinnr(fname:sub(1, #fname - 1)))
  165. api.nvim_set_current_dir(dirname)
  166. eq(2, fn.bufwinnr(fname))
  167. eq(1, fn.bufwinnr(fname2))
  168. eq(-1, fn.bufwinnr(fname:sub(1, #fname - 1)))
  169. eq(1, fn.bufwinnr('%'))
  170. eq(2, fn.bufwinnr(1))
  171. eq(1, fn.bufwinnr(2))
  172. eq(-1, fn.bufwinnr(3))
  173. eq(1, fn.bufwinnr('$'))
  174. end)
  175. end)
  176. describe('getbufline() function', function()
  177. it('returns empty list when buffer was not found', function()
  178. command('file ' .. fname)
  179. eq({}, fn.getbufline(2, 1))
  180. eq({}, fn.getbufline('non-existent-buffer', 1))
  181. eq({}, fn.getbufline('#', 1))
  182. command('edit ' .. fname2)
  183. eq(2, fn.bufnr('%'))
  184. eq({}, fn.getbufline('X', 1))
  185. end)
  186. it('returns empty list when range is invalid', function()
  187. eq({}, fn.getbufline(1, 0))
  188. api.nvim_buf_set_lines(0, 0, 1, false, { 'foo', 'bar', 'baz' })
  189. eq({}, fn.getbufline(1, 2, 1))
  190. eq({}, fn.getbufline(1, -10, -20))
  191. eq({}, fn.getbufline(1, -2, -1))
  192. eq({}, fn.getbufline(1, -1, 9999))
  193. end)
  194. it('returns expected lines', function()
  195. api.nvim_set_option_value('hidden', true, {})
  196. command('file ' .. fname)
  197. api.nvim_buf_set_lines(0, 0, 1, false, { 'foo\0', '\0bar', 'baz' })
  198. command('edit ' .. fname2)
  199. api.nvim_buf_set_lines(0, 0, 1, false, { 'abc\0', '\0def', 'ghi' })
  200. eq({ 'foo\n', '\nbar', 'baz' }, fn.getbufline(1, 1, 9999))
  201. eq({ 'abc\n', '\ndef', 'ghi' }, fn.getbufline(2, 1, 9999))
  202. eq({ 'foo\n', '\nbar', 'baz' }, fn.getbufline(1, 1, '$'))
  203. eq({ 'baz' }, fn.getbufline(1, '$', '$'))
  204. eq({ 'baz' }, fn.getbufline(1, '$', 9999))
  205. end)
  206. end)
  207. describe('getbufvar() function', function()
  208. it('returns empty list when buffer was not found', function()
  209. command('file ' .. fname)
  210. eq('', fn.getbufvar(2, '&autoindent'))
  211. eq('', fn.getbufvar('non-existent-buffer', '&autoindent'))
  212. eq('', fn.getbufvar('#', '&autoindent'))
  213. command('edit ' .. fname2)
  214. eq(2, fn.bufnr('%'))
  215. eq('', fn.getbufvar('X', '&autoindent'))
  216. end)
  217. it('returns empty list when variable/option/etc was not found', function()
  218. command('file ' .. fname)
  219. eq('', fn.getbufvar(1, '&autondent'))
  220. eq('', fn.getbufvar(1, 'changedtic'))
  221. end)
  222. it('returns expected option value', function()
  223. eq(0, fn.getbufvar(1, '&autoindent'))
  224. eq(0, fn.getbufvar(1, '&l:autoindent'))
  225. eq(0, fn.getbufvar(1, '&g:autoindent'))
  226. -- Also works with global-only options
  227. eq(1, fn.getbufvar(1, '&hidden'))
  228. eq(1, fn.getbufvar(1, '&l:hidden'))
  229. eq(1, fn.getbufvar(1, '&g:hidden'))
  230. -- Also works with window-local options
  231. eq(0, fn.getbufvar(1, '&number'))
  232. eq(0, fn.getbufvar(1, '&l:number'))
  233. eq(0, fn.getbufvar(1, '&g:number'))
  234. command('new')
  235. -- But with window-local options it probably does not what you expect
  236. command('setl number')
  237. -- (note that current window’s buffer is 2, but getbufvar() receives 1)
  238. eq(2, api.nvim_win_get_buf(0))
  239. eq(1, fn.getbufvar(1, '&number'))
  240. eq(1, fn.getbufvar(1, '&l:number'))
  241. -- You can get global value though, if you find this useful.
  242. eq(0, fn.getbufvar(1, '&g:number'))
  243. end)
  244. it('returns expected variable value', function()
  245. eq(2, fn.getbufvar(1, 'changedtick'))
  246. api.nvim_buf_set_lines(0, 0, 1, false, { 'abc\0', '\0def', 'ghi' })
  247. eq(3, fn.getbufvar(1, 'changedtick'))
  248. api.nvim_buf_set_var(0, 'test', true)
  249. eq(true, fn.getbufvar(1, 'test'))
  250. eq({ test = true, changedtick = 3 }, fn.getbufvar(1, ''))
  251. command('new')
  252. eq(3, fn.getbufvar(1, 'changedtick'))
  253. eq(true, fn.getbufvar(1, 'test'))
  254. eq({ test = true, changedtick = 3 }, fn.getbufvar(1, ''))
  255. end)
  256. end)
  257. describe('setbufvar() function', function()
  258. it('throws the error or ignores the input when buffer was not found', function()
  259. command('file ' .. fname)
  260. eq(0, exc_exec('call setbufvar(2, "&autoindent", 0)'))
  261. eq(
  262. 'Vim(call):E94: No matching buffer for non-existent-buffer',
  263. exc_exec('call setbufvar("non-existent-buffer", "&autoindent", 0)')
  264. )
  265. eq(0, exc_exec('call setbufvar("#", "&autoindent", 0)'))
  266. command('edit ' .. fname2)
  267. eq(2, fn.bufnr('%'))
  268. eq(
  269. 'Vim(call):E93: More than one match for X',
  270. exc_exec('call setbufvar("X", "&autoindent", 0)')
  271. )
  272. end)
  273. it('may set options, including window-local and global values', function()
  274. local buf1 = api.nvim_get_current_buf()
  275. eq(false, api.nvim_get_option_value('number', {}))
  276. command('split')
  277. command('new')
  278. eq(2, api.nvim_buf_get_number(api.nvim_win_get_buf(0)))
  279. fn.setbufvar(1, '&number', true)
  280. local windows = api.nvim_tabpage_list_wins(0)
  281. eq(false, api.nvim_get_option_value('number', { win = windows[1] }))
  282. eq(true, api.nvim_get_option_value('number', { win = windows[2] }))
  283. eq(false, api.nvim_get_option_value('number', { win = windows[3] }))
  284. eq(false, api.nvim_get_option_value('number', { win = api.nvim_get_current_win() }))
  285. eq(true, api.nvim_get_option_value('hidden', {}))
  286. fn.setbufvar(1, '&hidden', 0)
  287. eq(false, api.nvim_get_option_value('hidden', {}))
  288. eq(false, api.nvim_get_option_value('autoindent', { buf = buf1 }))
  289. fn.setbufvar(1, '&autoindent', true)
  290. eq(true, api.nvim_get_option_value('autoindent', { buf = buf1 }))
  291. eq('Vim(call):E355: Unknown option: xxx', exc_exec('call setbufvar(1, "&xxx", 0)'))
  292. end)
  293. it('may set variables', function()
  294. local buf1 = api.nvim_get_current_buf()
  295. command('split')
  296. command('new')
  297. eq(2, api.nvim_buf_get_number(0))
  298. fn.setbufvar(1, 'number', true)
  299. eq(true, api.nvim_buf_get_var(buf1, 'number'))
  300. eq('Vim(call):E461: Illegal variable name: b:', exc_exec('call setbufvar(1, "", 0)'))
  301. eq(true, api.nvim_buf_get_var(buf1, 'number'))
  302. eq(
  303. 'Vim:E46: Cannot change read-only variable "b:changedtick"',
  304. pcall_err(fn.setbufvar, 1, 'changedtick', true)
  305. )
  306. eq(2, fn.getbufvar(1, 'changedtick'))
  307. end)
  308. it('throws error when setting a string option to a boolean value vim-patch:9.0.0090', function()
  309. eq('Vim:E928: String required', pcall_err(fn.setbufvar, '', '&errorformat', true))
  310. end)
  311. end)