local helpers = require('test.functional.helpers')(after_each) local lfs = require('lfs') local eq = helpers.eq local clear = helpers.clear local funcs = helpers.funcs local meths = helpers.meths local command = helpers.command local exc_exec = helpers.exc_exec local bufmeths = helpers.bufmeths local winmeths = helpers.winmeths local curbufmeths = helpers.curbufmeths local curwinmeths = helpers.curwinmeths local curtabmeths = helpers.curtabmeths local get_pathsep = helpers.get_pathsep local rmdir = helpers.rmdir local pcall_err = helpers.pcall_err local fname = 'Xtest-functional-eval-buf_functions' local fname2 = fname .. '.2' local dirname = fname .. '.d' before_each(clear) for _, func in ipairs({'bufname(%s)', 'bufnr(%s)', 'bufwinnr(%s)', 'getbufline(%s, 1)', 'getbufvar(%s, "changedtick")', 'setbufvar(%s, "f", 0)'}) do local funcname = func:match('%w+') describe(funcname .. '() function', function() it('errors out when receives v:true/v:false/v:null', function() -- Not compatible with Vim: in Vim it always results in buffer not found -- without any error messages. for _, var in ipairs({'v:true', 'v:false'}) do eq('Vim(call):E5299: Expected a Number or a String, Boolean found', exc_exec('call ' .. func:format(var))) end eq('Vim(call):E5300: Expected a Number or a String', exc_exec('call ' .. func:format('v:null'))) end) it('errors out when receives invalid argument', function() eq('Vim(call):E745: Expected a Number or a String, List found', exc_exec('call ' .. func:format('[]'))) eq('Vim(call):E728: Expected a Number or a String, Dictionary found', exc_exec('call ' .. func:format('{}'))) eq('Vim(call):E805: Expected a Number or a String, Float found', exc_exec('call ' .. func:format('0.0'))) eq('Vim(call):E703: Expected a Number or a String, Funcref found', exc_exec('call ' .. func:format('function("tr")'))) end) end) end describe('bufname() function', function() it('returns empty string when buffer was not found', function() command('file ' .. fname) eq('', funcs.bufname(2)) eq('', funcs.bufname('non-existent-buffer')) eq('', funcs.bufname('#')) command('edit ' .. fname2) eq(2, funcs.bufnr('%')) eq('', funcs.bufname('X')) end) before_each(function() lfs.mkdir(dirname) end) after_each(function() rmdir(dirname) end) it('returns expected buffer name', function() eq('', funcs.bufname('%')) -- Buffer has no name yet command('file ' .. fname) local wd = lfs.currentdir() local sep = get_pathsep() local curdirname = funcs.fnamemodify(wd, ':t') for _, arg in ipairs({'%', 1, 'X', wd}) do eq(fname, funcs.bufname(arg)) meths.set_current_dir('..') eq(curdirname .. sep .. fname, funcs.bufname(arg)) meths.set_current_dir(curdirname) meths.set_current_dir(dirname) eq(wd .. sep .. fname, funcs.bufname(arg)) meths.set_current_dir('..') eq(fname, funcs.bufname(arg)) command('enew') end eq('', funcs.bufname('%')) eq('', funcs.bufname('$')) eq(2, funcs.bufnr('%')) end) end) describe('bufnr() function', function() it('returns -1 when buffer was not found', function() command('file ' .. fname) eq(-1, funcs.bufnr(2)) eq(-1, funcs.bufnr('non-existent-buffer')) eq(-1, funcs.bufnr('#')) command('edit ' .. fname2) eq(2, funcs.bufnr('%')) eq(-1, funcs.bufnr('X')) end) it('returns expected buffer number', function() eq(1, funcs.bufnr('%')) command('file ' .. fname) local wd = lfs.currentdir() local curdirname = funcs.fnamemodify(wd, ':t') eq(1, funcs.bufnr(fname)) eq(1, funcs.bufnr(wd)) eq(1, funcs.bufnr(curdirname)) eq(1, funcs.bufnr('X')) end) it('returns number of last buffer with "$"', function() eq(1, funcs.bufnr('$')) command('new') eq(2, funcs.bufnr('$')) command('new') eq(3, funcs.bufnr('$')) command('only') eq(3, funcs.bufnr('$')) eq(3, funcs.bufnr('%')) command('buffer 1') eq(3, funcs.bufnr('$')) eq(1, funcs.bufnr('%')) command('bwipeout 2') eq(3, funcs.bufnr('$')) eq(1, funcs.bufnr('%')) command('bwipeout 3') eq(1, funcs.bufnr('$')) eq(1, funcs.bufnr('%')) command('new') eq(4, funcs.bufnr('$')) end) end) describe('bufwinnr() function', function() it('returns -1 when buffer was not found', function() command('file ' .. fname) eq(-1, funcs.bufwinnr(2)) eq(-1, funcs.bufwinnr('non-existent-buffer')) eq(-1, funcs.bufwinnr('#')) command('split ' .. fname2) -- It would be OK if there was one window eq(2, funcs.bufnr('%')) eq(-1, funcs.bufwinnr('X')) end) before_each(function() lfs.mkdir(dirname) end) after_each(function() rmdir(dirname) end) it('returns expected window number', function() eq(1, funcs.bufwinnr('%')) command('file ' .. fname) command('vsplit') command('split ' .. fname2) eq(2, funcs.bufwinnr(fname)) eq(1, funcs.bufwinnr(fname2)) eq(-1, funcs.bufwinnr(fname:sub(1, #fname - 1))) meths.set_current_dir(dirname) eq(2, funcs.bufwinnr(fname)) eq(1, funcs.bufwinnr(fname2)) eq(-1, funcs.bufwinnr(fname:sub(1, #fname - 1))) eq(1, funcs.bufwinnr('%')) eq(2, funcs.bufwinnr(1)) eq(1, funcs.bufwinnr(2)) eq(-1, funcs.bufwinnr(3)) eq(1, funcs.bufwinnr('$')) end) end) describe('getbufline() function', function() it('returns empty list when buffer was not found', function() command('file ' .. fname) eq({}, funcs.getbufline(2, 1)) eq({}, funcs.getbufline('non-existent-buffer', 1)) eq({}, funcs.getbufline('#', 1)) command('edit ' .. fname2) eq(2, funcs.bufnr('%')) eq({}, funcs.getbufline('X', 1)) end) it('returns empty list when range is invalid', function() eq({}, funcs.getbufline(1, 0)) curbufmeths.set_lines(0, 1, false, {'foo', 'bar', 'baz'}) eq({}, funcs.getbufline(1, 2, 1)) eq({}, funcs.getbufline(1, -10, -20)) eq({}, funcs.getbufline(1, -2, -1)) eq({}, funcs.getbufline(1, -1, 9999)) end) it('returns expected lines', function() meths.set_option('hidden', true) command('file ' .. fname) curbufmeths.set_lines(0, 1, false, {'foo\0', '\0bar', 'baz'}) command('edit ' .. fname2) curbufmeths.set_lines(0, 1, false, {'abc\0', '\0def', 'ghi'}) eq({'foo\n', '\nbar', 'baz'}, funcs.getbufline(1, 1, 9999)) eq({'abc\n', '\ndef', 'ghi'}, funcs.getbufline(2, 1, 9999)) eq({'foo\n', '\nbar', 'baz'}, funcs.getbufline(1, 1, '$')) eq({'baz'}, funcs.getbufline(1, '$', '$')) eq({'baz'}, funcs.getbufline(1, '$', 9999)) end) end) describe('getbufvar() function', function() it('returns empty list when buffer was not found', function() command('file ' .. fname) eq('', funcs.getbufvar(2, '&autoindent')) eq('', funcs.getbufvar('non-existent-buffer', '&autoindent')) eq('', funcs.getbufvar('#', '&autoindent')) command('edit ' .. fname2) eq(2, funcs.bufnr('%')) eq('', funcs.getbufvar('X', '&autoindent')) end) it('returns empty list when variable/option/etc was not found', function() command('file ' .. fname) eq('', funcs.getbufvar(1, '&autondent')) eq('', funcs.getbufvar(1, 'changedtic')) end) it('returns expected option value', function() eq(0, funcs.getbufvar(1, '&autoindent')) eq(0, funcs.getbufvar(1, '&l:autoindent')) eq(0, funcs.getbufvar(1, '&g:autoindent')) -- Also works with global-only options eq(1, funcs.getbufvar(1, '&hidden')) eq(1, funcs.getbufvar(1, '&l:hidden')) eq(1, funcs.getbufvar(1, '&g:hidden')) -- Also works with window-local options eq(0, funcs.getbufvar(1, '&number')) eq(0, funcs.getbufvar(1, '&l:number')) eq(0, funcs.getbufvar(1, '&g:number')) command('new') -- But with window-local options it probably does not what you expect command("setl number") -- (note that current window’s buffer is 2, but getbufvar() receives 1) eq({id=2}, curwinmeths.get_buf()) eq(1, funcs.getbufvar(1, '&number')) eq(1, funcs.getbufvar(1, '&l:number')) -- You can get global value though, if you find this useful. eq(0, funcs.getbufvar(1, '&g:number')) end) it('returns expected variable value', function() eq(2, funcs.getbufvar(1, 'changedtick')) curbufmeths.set_lines(0, 1, false, {'abc\0', '\0def', 'ghi'}) eq(3, funcs.getbufvar(1, 'changedtick')) curbufmeths.set_var('test', true) eq(true, funcs.getbufvar(1, 'test')) eq({test=true, changedtick=3}, funcs.getbufvar(1, '')) command('new') eq(3, funcs.getbufvar(1, 'changedtick')) eq(true, funcs.getbufvar(1, 'test')) eq({test=true, changedtick=3}, funcs.getbufvar(1, '')) end) end) describe('setbufvar() function', function() it('throws the error or ignores the input when buffer was not found', function() command('file ' .. fname) eq(0, exc_exec('call setbufvar(2, "&autoindent", 0)')) eq('Vim(call):E94: No matching buffer for non-existent-buffer', exc_exec('call setbufvar("non-existent-buffer", "&autoindent", 0)')) eq(0, exc_exec('call setbufvar("#", "&autoindent", 0)')) command('edit ' .. fname2) eq(2, funcs.bufnr('%')) eq('Vim(call):E93: More than one match for X', exc_exec('call setbufvar("X", "&autoindent", 0)')) end) it('may set options, including window-local and global values', function() local buf1 = meths.get_current_buf() eq(false, curwinmeths.get_option('number')) command('split') command('new') eq(2, bufmeths.get_number(curwinmeths.get_buf())) funcs.setbufvar(1, '&number', true) local windows = curtabmeths.list_wins() eq(false, winmeths.get_option(windows[1], 'number')) eq(true, winmeths.get_option(windows[2], 'number')) eq(false, winmeths.get_option(windows[3], 'number')) eq(false, winmeths.get_option(meths.get_current_win(), 'number')) eq(true, meths.get_option('hidden')) funcs.setbufvar(1, '&hidden', 0) eq(false, meths.get_option('hidden')) eq(false, bufmeths.get_option(buf1, 'autoindent')) funcs.setbufvar(1, '&autoindent', true) eq(true, bufmeths.get_option(buf1, 'autoindent')) eq('Vim(call):E355: Unknown option: xxx', exc_exec('call setbufvar(1, "&xxx", 0)')) end) it('may set variables', function() local buf1 = meths.get_current_buf() command('split') command('new') eq(2, curbufmeths.get_number()) funcs.setbufvar(1, 'number', true) eq(true, bufmeths.get_var(buf1, 'number')) eq('Vim(call):E461: Illegal variable name: b:', exc_exec('call setbufvar(1, "", 0)')) eq(true, bufmeths.get_var(buf1, 'number')) eq('Vim:E46: Cannot change read-only variable "b:changedtick"', pcall_err(funcs.setbufvar, 1, 'changedtick', true)) eq(2, funcs.getbufvar(1, 'changedtick')) end) end)