From 0e42c81c7fd429529d89458349c7cdde254d5406 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 12 Oct 2024 08:07:05 +0800 Subject: fix(lua): avoid recursive vim.on_key() callback (#30753) --- test/functional/lua/vim_spec.lua | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) (limited to 'test/functional/lua') diff --git a/test/functional/lua/vim_spec.lua b/test/functional/lua/vim_spec.lua index 3c65ec664e..793cf7cfd7 100644 --- a/test/functional/lua/vim_spec.lua +++ b/test/functional/lua/vim_spec.lua @@ -3223,6 +3223,45 @@ describe('lua stdlib', function() feed('') eq('/', exec_lua([[return _G.ctrl_c_cmdtype]])) end) + + it('callback is not invoked recursively #30752', function() + local screen = Screen.new(60, 10) + screen:attach() + exec_lua([[ + vim.on_key(function(key, typed) + vim.api.nvim_echo({ + { 'key_cb\n' }, + { ("KEYCB: key '%s', typed '%s'\n"):format(key, typed) }, + }, false, {}) + end) + ]]) + feed('^') + screen:expect([[ + | + {1:~ }|*5 + {3: }| + key_cb | + KEYCB: key '^', typed '^' | + {6:Press ENTER or type command to continue}^ | + ]]) + feed('') + screen:expect([[ + | + {1:~ }|*3 + {3: }| + key_cb | + KEYCB: key '^', typed '^' | + key_cb | + KEYCB: key '{18:^C}', typed '{18:^C}' | + {6:Press ENTER or type command to continue}^ | + ]]) + feed('') + screen:expect([[ + ^ | + {1:~ }|*8 + | + ]]) + end) end) describe('vim.wait', function() -- cgit From f4b620c4e6430ea8cbc5632d6853b24da09d7dfc Mon Sep 17 00:00:00 2001 From: Uthman Mohamed <83053931+uthmanmoh@users.noreply.github.com> Date: Fri, 18 Oct 2024 05:22:15 -0400 Subject: feat(vim.ui.open): support lemonade #30845 --- test/functional/lua/ui_spec.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'test/functional/lua') diff --git a/test/functional/lua/ui_spec.lua b/test/functional/lua/ui_spec.lua index d5eede2885..5c727b3347 100644 --- a/test/functional/lua/ui_spec.lua +++ b/test/functional/lua/ui_spec.lua @@ -153,7 +153,7 @@ describe('vim.ui', function() vim.fn.executable = function() return 0 end ]] eq( - 'vim.ui.open: no handler found (tried: wslview, explorer.exe, xdg-open)', + 'vim.ui.open: no handler found (tried: wslview, explorer.exe, xdg-open, lemonade)', exec_lua [[local _, err = vim.ui.open('foo') ; return err]] ) end) -- cgit From 960fdc775a88389d8b19389b8c74c98921b9acac Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Thu, 17 Oct 2024 17:58:13 +0200 Subject: fix(lua): vim.deprecate does not support major>0 --- test/functional/lua/vim_spec.lua | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) (limited to 'test/functional/lua') diff --git a/test/functional/lua/vim_spec.lua b/test/functional/lua/vim_spec.lua index 793cf7cfd7..ccb45a9d26 100644 --- a/test/functional/lua/vim_spec.lua +++ b/test/functional/lua/vim_spec.lua @@ -155,10 +155,10 @@ describe('lua stdlib', function() end) it('plugin=nil, no error if soft-deprecated', function() - eq( - vim.NIL, - exec_lua('return vim.deprecate(...)', 'foo.baz()', 'foo.better_baz()', '0.99.0') - ) + eq(vim.NIL, exec_lua [[return vim.deprecate('old1', 'new1', '0.99.0')]]) + -- Major version > current Nvim major is always "soft-deprecated". + -- XXX: This is also a reminder to update the hardcoded `nvim_major`, when Nvim reaches 1.0. + eq(vim.NIL, exec_lua [[return vim.deprecate('old2', 'new2', '1.0.0')]]) end) it('plugin=nil, show error if hard-deprecated', function() @@ -175,13 +175,6 @@ describe('lua stdlib', function() ) end) - it('plugin=nil, to be deleted in the next major version (1.0)', function() - eq( - [[foo.baz() is deprecated. Run ":checkhealth vim.deprecated" for more information]], - exec_lua [[ return vim.deprecate('foo.baz()', nil, '1.0') ]] - ) - end) - it('plugin specified', function() -- When `plugin` is specified, don't show ":help deprecated". #22235 eq( -- cgit From 18b43c331d8a0ed87d7cbefe2a18543b8e4ad360 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Thu, 17 Oct 2024 11:16:16 +0200 Subject: refactor: rename vim.highlight => vim.hl Problem: - `vim.highlight` module does not follow `:help dev-name-common`, which documents the name for "highlight" as "hl". - Shorter names are usually preferred. Solution: Rename `vim.highlight` to `vim.hl`. This is not a breaking change until 2.0 (or maybe never). --- test/functional/lua/highlight_spec.lua | 175 --------------------------------- test/functional/lua/hl_spec.lua | 175 +++++++++++++++++++++++++++++++++ 2 files changed, 175 insertions(+), 175 deletions(-) delete mode 100644 test/functional/lua/highlight_spec.lua create mode 100644 test/functional/lua/hl_spec.lua (limited to 'test/functional/lua') diff --git a/test/functional/lua/highlight_spec.lua b/test/functional/lua/highlight_spec.lua deleted file mode 100644 index c048949df8..0000000000 --- a/test/functional/lua/highlight_spec.lua +++ /dev/null @@ -1,175 +0,0 @@ -local t = require('test.testutil') -local n = require('test.functional.testnvim')() -local Screen = require('test.functional.ui.screen') - -local exec_lua = n.exec_lua -local eq = t.eq -local eval = n.eval -local command = n.command -local clear = n.clear -local api = n.api - -describe('vim.highlight.range', function() - local screen - - before_each(function() - clear() - screen = Screen.new(60, 6) - screen:add_extra_attr_ids({ - [100] = { foreground = Screen.colors.Blue, background = Screen.colors.Yellow, bold = true }, - }) - screen:attach() - api.nvim_set_option_value('list', true, {}) - api.nvim_set_option_value('listchars', 'eol:$', {}) - api.nvim_buf_set_lines(0, 0, -1, true, { - 'asdfghjkl', - '«口=口»', - 'qwertyuiop', - '口口=口口', - 'zxcvbnm', - }) - end) - - it('works with charwise selection', function() - exec_lua(function() - local ns = vim.api.nvim_create_namespace('') - vim.highlight.range(0, ns, 'Search', { 1, 5 }, { 3, 10 }) - end) - screen:expect([[ - ^asdfghjkl{1:$} | - «口{10:=口»}{100:$} | - {10:qwertyuiop}{100:$} | - {10:口口=口}口{1:$} | - zxcvbnm{1:$} | - | - ]]) - end) - - it('works with linewise selection', function() - exec_lua(function() - local ns = vim.api.nvim_create_namespace('') - vim.highlight.range(0, ns, 'Search', { 0, 0 }, { 4, 0 }, { regtype = 'V' }) - end) - screen:expect([[ - {10:^asdfghjkl}{100:$} | - {10:«口=口»}{100:$} | - {10:qwertyuiop}{100:$} | - {10:口口=口口}{100:$} | - {10:zxcvbnm}{100:$} | - | - ]]) - end) - - it('works with blockwise selection', function() - exec_lua(function() - local ns = vim.api.nvim_create_namespace('') - vim.highlight.range(0, ns, 'Search', { 0, 0 }, { 4, 4 }, { regtype = '\022' }) - end) - screen:expect([[ - {10:^asdf}ghjkl{1:$} | - {10:«口=}口»{1:$} | - {10:qwer}tyuiop{1:$} | - {10:口口}=口口{1:$} | - {10:zxcv}bnm{1:$} | - | - ]]) - end) - - it('works with blockwise selection with width', function() - exec_lua(function() - local ns = vim.api.nvim_create_namespace('') - vim.highlight.range(0, ns, 'Search', { 0, 4 }, { 4, 7 }, { regtype = '\0226' }) - end) - screen:expect([[ - ^asdf{10:ghjkl}{1:$} | - «口={10:口»}{1:$} | - qwer{10:tyuiop}{1:$} | - 口口{10:=口口}{1:$} | - zxcv{10:bnm}{1:$} | - | - ]]) - end) - - it('can use -1 or v:maxcol to indicate end of line', function() - exec_lua(function() - local ns = vim.api.nvim_create_namespace('') - vim.highlight.range(0, ns, 'Search', { 0, 4 }, { 1, -1 }, {}) - vim.highlight.range(0, ns, 'Search', { 2, 6 }, { 3, vim.v.maxcol }, {}) - end) - screen:expect([[ - ^asdf{10:ghjkl}{100:$} | - {10:«口=口»}{100:$} | - qwerty{10:uiop}{100:$} | - {10:口口=口口}{1:$} | - zxcvbnm{1:$} | - | - ]]) - end) -end) - -describe('vim.highlight.on_yank', function() - before_each(function() - clear() - end) - - it('does not show errors even if buffer is wiped before timeout', function() - command('new') - exec_lua(function() - vim.highlight.on_yank({ - timeout = 10, - on_macro = true, - event = { operator = 'y', regtype = 'v' }, - }) - vim.cmd('bwipeout!') - end) - vim.uv.sleep(10) - n.feed('') -- avoid hang if error message exists - eq('', eval('v:errmsg')) - end) - - it('does not close timer twice', function() - exec_lua(function() - vim.highlight.on_yank({ timeout = 10, on_macro = true, event = { operator = 'y' } }) - vim.uv.sleep(10) - vim.schedule(function() - vim.highlight.on_yank({ timeout = 0, on_macro = true, event = { operator = 'y' } }) - end) - end) - eq('', eval('v:errmsg')) - end) - - it('does not show in another window', function() - command('vsplit') - exec_lua(function() - vim.api.nvim_buf_set_mark(0, '[', 1, 1, {}) - vim.api.nvim_buf_set_mark(0, ']', 1, 1, {}) - vim.highlight.on_yank({ timeout = math.huge, on_macro = true, event = { operator = 'y' } }) - end) - local ns = api.nvim_create_namespace('hlyank') - local win = api.nvim_get_current_win() - eq({ win }, api.nvim__ns_get(ns).wins) - command('wincmd w') - eq({ win }, api.nvim__ns_get(ns).wins) - end) - - it('removes old highlight if new one is created before old one times out', function() - command('vnew') - exec_lua(function() - vim.api.nvim_buf_set_mark(0, '[', 1, 1, {}) - vim.api.nvim_buf_set_mark(0, ']', 1, 1, {}) - vim.highlight.on_yank({ timeout = math.huge, on_macro = true, event = { operator = 'y' } }) - end) - local ns = api.nvim_create_namespace('hlyank') - eq(api.nvim_get_current_win(), api.nvim__ns_get(ns).wins[1]) - command('wincmd w') - exec_lua(function() - vim.api.nvim_buf_set_mark(0, '[', 1, 1, {}) - vim.api.nvim_buf_set_mark(0, ']', 1, 1, {}) - vim.highlight.on_yank({ timeout = math.huge, on_macro = true, event = { operator = 'y' } }) - end) - local win = api.nvim_get_current_win() - eq({ win }, api.nvim__ns_get(ns).wins) - command('wincmd w') - eq({ win }, api.nvim__ns_get(ns).wins) - end) -end) diff --git a/test/functional/lua/hl_spec.lua b/test/functional/lua/hl_spec.lua new file mode 100644 index 0000000000..a123c79ffb --- /dev/null +++ b/test/functional/lua/hl_spec.lua @@ -0,0 +1,175 @@ +local t = require('test.testutil') +local n = require('test.functional.testnvim')() +local Screen = require('test.functional.ui.screen') + +local exec_lua = n.exec_lua +local eq = t.eq +local eval = n.eval +local command = n.command +local clear = n.clear +local api = n.api + +describe('vim.hl.range', function() + local screen + + before_each(function() + clear() + screen = Screen.new(60, 6) + screen:add_extra_attr_ids({ + [100] = { foreground = Screen.colors.Blue, background = Screen.colors.Yellow, bold = true }, + }) + screen:attach() + api.nvim_set_option_value('list', true, {}) + api.nvim_set_option_value('listchars', 'eol:$', {}) + api.nvim_buf_set_lines(0, 0, -1, true, { + 'asdfghjkl', + '«口=口»', + 'qwertyuiop', + '口口=口口', + 'zxcvbnm', + }) + end) + + it('works with charwise selection', function() + exec_lua(function() + local ns = vim.api.nvim_create_namespace('') + vim.hl.range(0, ns, 'Search', { 1, 5 }, { 3, 10 }) + end) + screen:expect([[ + ^asdfghjkl{1:$} | + «口{10:=口»}{100:$} | + {10:qwertyuiop}{100:$} | + {10:口口=口}口{1:$} | + zxcvbnm{1:$} | + | + ]]) + end) + + it('works with linewise selection', function() + exec_lua(function() + local ns = vim.api.nvim_create_namespace('') + vim.hl.range(0, ns, 'Search', { 0, 0 }, { 4, 0 }, { regtype = 'V' }) + end) + screen:expect([[ + {10:^asdfghjkl}{100:$} | + {10:«口=口»}{100:$} | + {10:qwertyuiop}{100:$} | + {10:口口=口口}{100:$} | + {10:zxcvbnm}{100:$} | + | + ]]) + end) + + it('works with blockwise selection', function() + exec_lua(function() + local ns = vim.api.nvim_create_namespace('') + vim.hl.range(0, ns, 'Search', { 0, 0 }, { 4, 4 }, { regtype = '\022' }) + end) + screen:expect([[ + {10:^asdf}ghjkl{1:$} | + {10:«口=}口»{1:$} | + {10:qwer}tyuiop{1:$} | + {10:口口}=口口{1:$} | + {10:zxcv}bnm{1:$} | + | + ]]) + end) + + it('works with blockwise selection with width', function() + exec_lua(function() + local ns = vim.api.nvim_create_namespace('') + vim.hl.range(0, ns, 'Search', { 0, 4 }, { 4, 7 }, { regtype = '\0226' }) + end) + screen:expect([[ + ^asdf{10:ghjkl}{1:$} | + «口={10:口»}{1:$} | + qwer{10:tyuiop}{1:$} | + 口口{10:=口口}{1:$} | + zxcv{10:bnm}{1:$} | + | + ]]) + end) + + it('can use -1 or v:maxcol to indicate end of line', function() + exec_lua(function() + local ns = vim.api.nvim_create_namespace('') + vim.hl.range(0, ns, 'Search', { 0, 4 }, { 1, -1 }, {}) + vim.hl.range(0, ns, 'Search', { 2, 6 }, { 3, vim.v.maxcol }, {}) + end) + screen:expect([[ + ^asdf{10:ghjkl}{100:$} | + {10:«口=口»}{100:$} | + qwerty{10:uiop}{100:$} | + {10:口口=口口}{1:$} | + zxcvbnm{1:$} | + | + ]]) + end) +end) + +describe('vim.hl.on_yank', function() + before_each(function() + clear() + end) + + it('does not show errors even if buffer is wiped before timeout', function() + command('new') + exec_lua(function() + vim.hl.on_yank({ + timeout = 10, + on_macro = true, + event = { operator = 'y', regtype = 'v' }, + }) + vim.cmd('bwipeout!') + end) + vim.uv.sleep(10) + n.feed('') -- avoid hang if error message exists + eq('', eval('v:errmsg')) + end) + + it('does not close timer twice', function() + exec_lua(function() + vim.hl.on_yank({ timeout = 10, on_macro = true, event = { operator = 'y' } }) + vim.uv.sleep(10) + vim.schedule(function() + vim.hl.on_yank({ timeout = 0, on_macro = true, event = { operator = 'y' } }) + end) + end) + eq('', eval('v:errmsg')) + end) + + it('does not show in another window', function() + command('vsplit') + exec_lua(function() + vim.api.nvim_buf_set_mark(0, '[', 1, 1, {}) + vim.api.nvim_buf_set_mark(0, ']', 1, 1, {}) + vim.hl.on_yank({ timeout = math.huge, on_macro = true, event = { operator = 'y' } }) + end) + local ns = api.nvim_create_namespace('hlyank') + local win = api.nvim_get_current_win() + eq({ win }, api.nvim__ns_get(ns).wins) + command('wincmd w') + eq({ win }, api.nvim__ns_get(ns).wins) + end) + + it('removes old highlight if new one is created before old one times out', function() + command('vnew') + exec_lua(function() + vim.api.nvim_buf_set_mark(0, '[', 1, 1, {}) + vim.api.nvim_buf_set_mark(0, ']', 1, 1, {}) + vim.hl.on_yank({ timeout = math.huge, on_macro = true, event = { operator = 'y' } }) + end) + local ns = api.nvim_create_namespace('hlyank') + eq(api.nvim_get_current_win(), api.nvim__ns_get(ns).wins[1]) + command('wincmd w') + exec_lua(function() + vim.api.nvim_buf_set_mark(0, '[', 1, 1, {}) + vim.api.nvim_buf_set_mark(0, ']', 1, 1, {}) + vim.hl.on_yank({ timeout = math.huge, on_macro = true, event = { operator = 'y' } }) + end) + local win = api.nvim_get_current_win() + eq({ win }, api.nvim__ns_get(ns).wins) + command('wincmd w') + eq({ win }, api.nvim__ns_get(ns).wins) + end) +end) -- cgit From 6fd13eeddaf5db89c1b81cc7d3d3f1a7da5401a7 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Sat, 19 Oct 2024 13:25:43 +0200 Subject: test(rpc): retry flaky 'vim.rpcrequest and vim.rpcnotify' test Problem: 'vim.rpcrequest and vim.rpcnotify' is flaky on Windows. Solution: retry it. --- test/functional/lua/vim_spec.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'test/functional/lua') diff --git a/test/functional/lua/vim_spec.lua b/test/functional/lua/vim_spec.lua index ccb45a9d26..cba6c89c16 100644 --- a/test/functional/lua/vim_spec.lua +++ b/test/functional/lua/vim_spec.lua @@ -1319,7 +1319,9 @@ describe('lua stdlib', function() ]], } feed('') - eq({ 3, NIL }, api.nvim_get_var('yy')) + retry(10, nil, function() + eq({ 3, NIL }, api.nvim_get_var('yy')) + end) exec_lua([[timer:close()]]) end) -- cgit From 3572319b4cb1a4163624a5fe328886f1928dbc4a Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Fri, 18 Oct 2024 11:33:12 +0100 Subject: feat(vim.validate): improve fast form and deprecate spec form Problem: `vim.validate()` takes two forms when it only needs one. Solution: - Teach the fast form all the features of the spec form. - Deprecate the spec form. - General optimizations for both forms. - Add a `message` argument which can be used alongside or in place of the `optional` argument. --- test/functional/lua/vim_spec.lua | 105 ++++++++++++++++++++++++++++++--------- 1 file changed, 82 insertions(+), 23 deletions(-) (limited to 'test/functional/lua') diff --git a/test/functional/lua/vim_spec.lua b/test/functional/lua/vim_spec.lua index cba6c89c16..b32712860a 100644 --- a/test/functional/lua/vim_spec.lua +++ b/test/functional/lua/vim_spec.lua @@ -1358,7 +1358,79 @@ describe('lua stdlib', function() eq('{"a": {}, "b": []}', exec_lua([[ return vim.fn.json_encode({a=vim.empty_dict(), b={}}) ]])) end) - it('vim.validate', function() + it('vim.validate (fast form)', function() + exec_lua("vim.validate('arg1', {}, 'table')") + exec_lua("vim.validate('arg1', nil, 'table', true)") + exec_lua("vim.validate('arg1', { foo='foo' }, 'table')") + exec_lua("vim.validate('arg1', { 'foo' }, 'table')") + exec_lua("vim.validate('arg1', 'foo', 'string')") + exec_lua("vim.validate('arg1', nil, 'string', true)") + exec_lua("vim.validate('arg1', 1, 'number')") + exec_lua("vim.validate('arg1', 0, 'number')") + exec_lua("vim.validate('arg1', 0.1, 'number')") + exec_lua("vim.validate('arg1', nil, 'number', true)") + exec_lua("vim.validate('arg1', true, 'boolean')") + exec_lua("vim.validate('arg1', false, 'boolean')") + exec_lua("vim.validate('arg1', nil, 'boolean', true)") + exec_lua("vim.validate('arg1', function()end, 'function')") + exec_lua("vim.validate('arg1', nil, 'function', true)") + exec_lua("vim.validate('arg1', nil, 'nil')") + exec_lua("vim.validate('arg1', nil, 'nil', true)") + exec_lua("vim.validate('arg1', coroutine.create(function()end), 'thread')") + exec_lua("vim.validate('arg1', nil, 'thread', true)") + exec_lua("vim.validate('arg1', 2, function(a) return (a % 2) == 0 end, 'even number')") + exec_lua("vim.validate('arg1', 5, {'number', 'string'})") + exec_lua("vim.validate('arg2', 'foo', {'number', 'string'})") + + matches('arg1: expected number, got nil', pcall_err(vim.validate, 'arg1', nil, 'number')) + matches('arg1: expected string, got nil', pcall_err(vim.validate, 'arg1', nil, 'string')) + matches('arg1: expected table, got nil', pcall_err(vim.validate, 'arg1', nil, 'table')) + matches('arg1: expected function, got nil', pcall_err(vim.validate, 'arg1', nil, 'function')) + matches('arg1: expected string, got number', pcall_err(vim.validate, 'arg1', 5, 'string')) + matches('arg1: expected table, got number', pcall_err(vim.validate, 'arg1', 5, 'table')) + matches('arg1: expected function, got number', pcall_err(vim.validate, 'arg1', 5, 'function')) + matches('arg1: expected number, got string', pcall_err(vim.validate, 'arg1', '5', 'number')) + matches('arg1: expected x, got number', pcall_err(exec_lua, "vim.validate('arg1', 1, 'x')")) + matches('invalid validator: 1', pcall_err(exec_lua, "vim.validate('arg1', 1, 1)")) + matches('invalid arguments', pcall_err(exec_lua, "vim.validate('arg1', { 1 })")) + + -- Validated parameters are required by default. + matches( + 'arg1: expected string, got nil', + pcall_err(exec_lua, "vim.validate('arg1', nil, 'string')") + ) + -- Explicitly required. + matches( + 'arg1: expected string, got nil', + pcall_err(exec_lua, "vim.validate('arg1', nil, 'string', false)") + ) + + matches( + 'arg1: expected table, got number', + pcall_err(exec_lua, "vim.validate('arg1', 1, 'table')") + ) + + matches( + 'arg1: expected even number, got 3', + pcall_err(exec_lua, "vim.validate('arg1', 3, function(a) return a == 1 end, 'even number')") + ) + matches( + 'arg1: expected %?, got 3', + pcall_err(exec_lua, "vim.validate('arg1', 3, function(a) return a == 1 end)") + ) + matches( + 'arg1: expected number|string, got nil', + pcall_err(exec_lua, "vim.validate('arg1', nil, {'number', 'string'})") + ) + + -- Pass an additional message back. + matches( + 'arg1: expected %?, got 3. Info: TEST_MSG', + pcall_err(exec_lua, "vim.validate('arg1', 3, function(a) return a == 1, 'TEST_MSG' end)") + ) + end) + + it('vim.validate (spec form)', function() exec_lua("vim.validate{arg1={{}, 'table' }}") exec_lua("vim.validate{arg1={{}, 't' }}") exec_lua("vim.validate{arg1={nil, 't', true }}") @@ -1387,29 +1459,11 @@ describe('lua stdlib', function() exec_lua("vim.validate{arg1={{}, 't' }, arg2={ 'foo', 's' }}") exec_lua("vim.validate{arg1={2, function(a) return (a % 2) == 0 end, 'even number' }}") exec_lua("vim.validate{arg1={5, {'n', 's'} }, arg2={ 'foo', {'n', 's'} }}") - vim.validate('arg1', 5, 'number') - vim.validate('arg1', '5', 'string') - vim.validate('arg1', { 5 }, 'table') - vim.validate('arg1', function() - return 5 - end, 'function') - vim.validate('arg1', nil, 'number', true) - vim.validate('arg1', nil, 'string', true) - vim.validate('arg1', nil, 'table', true) - vim.validate('arg1', nil, 'function', true) - matches('arg1: expected number, got nil', pcall_err(vim.validate, 'arg1', nil, 'number')) - matches('arg1: expected string, got nil', pcall_err(vim.validate, 'arg1', nil, 'string')) - matches('arg1: expected table, got nil', pcall_err(vim.validate, 'arg1', nil, 'table')) - matches('arg1: expected function, got nil', pcall_err(vim.validate, 'arg1', nil, 'function')) - matches('arg1: expected string, got number', pcall_err(vim.validate, 'arg1', 5, 'string')) - matches('arg1: expected table, got number', pcall_err(vim.validate, 'arg1', 5, 'table')) - matches('arg1: expected function, got number', pcall_err(vim.validate, 'arg1', 5, 'function')) - matches('arg1: expected number, got string', pcall_err(vim.validate, 'arg1', '5', 'number')) matches('expected table, got number', pcall_err(exec_lua, "vim.validate{ 1, 'x' }")) - matches('invalid type name: x', pcall_err(exec_lua, "vim.validate{ arg1={ 1, 'x' }}")) - matches('invalid type name: 1', pcall_err(exec_lua, 'vim.validate{ arg1={ 1, 1 }}')) - matches('invalid type name: nil', pcall_err(exec_lua, 'vim.validate{ arg1={ 1 }}')) + matches('arg1: expected x, got number', pcall_err(exec_lua, "vim.validate{ arg1={ 1, 'x' }}")) + matches('invalid validator: 1', pcall_err(exec_lua, 'vim.validate{ arg1={ 1, 1 }}')) + matches('invalid validator: nil', pcall_err(exec_lua, 'vim.validate{ arg1={ 1 }}')) -- Validated parameters are required by default. matches( @@ -4094,10 +4148,15 @@ describe('vim.keymap', function() ) matches( - 'opts: expected table, got function', + 'rhs: expected string|function, got number', pcall_err(exec_lua, [[vim.keymap.set({}, 'x', 42, function() end)]]) ) + matches( + 'opts: expected table, got function', + pcall_err(exec_lua, [[vim.keymap.set({}, 'x', 'x', function() end)]]) + ) + matches( 'rhs: expected string|function, got number', pcall_err(exec_lua, [[vim.keymap.set('z', 'x', 42)]]) -- cgit From 230b0c7f021a57647a658edce27fe115343f083f Mon Sep 17 00:00:00 2001 From: Tristan Knight Date: Wed, 23 Oct 2024 14:33:57 +0100 Subject: feat(stdlib): overload vim.str_byteindex, vim.str_utfindex #30735 PROBLEM: There are several limitations to vim.str_byteindex, vim.str_utfindex: 1. They throw given out-of-range indexes. An invalid (often user/lsp-provided) index doesn't feel exceptional and should be handled by the caller. `:help dev-error-patterns` suggests that `retval, errmsg` is the preferred way to handle this kind of failure. 2. They cannot accept an encoding. So LSP needs wrapper functions. #25272 3. The current signatures are not extensible. * Calling: The function currently uses a fairly opaque boolean value to indicate to identify the encoding. * Returns: The fact it can throw requires wrapping in pcall. 4. The current name doesn't follow suggestions in `:h dev-naming` and I think `get` would be suitable. SOLUTION: - Because these are performance-sensitive, don't introduce `opts`. - Introduce an "overload" that accepts `encoding:string` and `strict_indexing:bool` params. ```lua local col = vim.str_utfindex(line, encoding, [index, [no_out_of_range]]) ``` Support the old versions by dispatching on the type of argument 2, and deprecate that form. ```lua vim.str_utfindex(line) -- (utf-32 length, utf-16 length), deprecated vim.str_utfindex(line, index) -- (utf-32 index, utf-16 index), deprecated vim.str_utfindex(line, 'utf-16') -- utf-16 length vim.str_utfindex(line, 'utf-16', index) -- utf-16 index vim.str_utfindex(line, 'utf-16', math.huge) -- error: index out of range vim.str_utfindex(line, 'utf-16', math.huge, false) -- utf-16 length ``` --- test/functional/lua/vim_spec.lua | 113 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 109 insertions(+), 4 deletions(-) (limited to 'test/functional/lua') diff --git a/test/functional/lua/vim_spec.lua b/test/functional/lua/vim_spec.lua index b32712860a..13e146a9da 100644 --- a/test/functional/lua/vim_spec.lua +++ b/test/functional/lua/vim_spec.lua @@ -312,21 +312,106 @@ describe('lua stdlib', function() 49, 51, } + local indices8 = { + [0] = 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39, + 40, + 41, + 42, + 43, + 44, + 45, + 46, + 47, + 48, + 49, + 50, + 51, + } for i, k in pairs(indices32) do eq(k, exec_lua('return vim.str_byteindex(_G.test_text, ...)', i), i) + eq(k, exec_lua('return vim.str_byteindex(_G.test_text, ..., false)', i), i) + eq(k, exec_lua('return vim.str_byteindex(_G.test_text, "utf-32", ...)', i), i) end for i, k in pairs(indices16) do eq(k, exec_lua('return vim.str_byteindex(_G.test_text, ..., true)', i), i) + eq(k, exec_lua('return vim.str_byteindex(_G.test_text, "utf-16", ...)', i), i) end - eq( + for i, k in pairs(indices8) do + eq(k, exec_lua('return vim.str_byteindex(_G.test_text, "utf-8", ...)', i), i) + end + matches( 'index out of range', pcall_err(exec_lua, 'return vim.str_byteindex(_G.test_text, ...)', #indices32 + 1) ) - eq( + matches( 'index out of range', pcall_err(exec_lua, 'return vim.str_byteindex(_G.test_text, ..., true)', #indices16 + 1) ) - local i32, i16 = 0, 0 + matches( + 'index out of range', + pcall_err(exec_lua, 'return vim.str_byteindex(_G.test_text, "utf-16", ...)', #indices16 + 1) + ) + matches( + 'index out of range', + pcall_err(exec_lua, 'return vim.str_byteindex(_G.test_text, "utf-32", ...)', #indices32 + 1) + ) + matches( + 'invalid encoding', + pcall_err(exec_lua, 'return vim.str_byteindex("hello", "madeupencoding", 1)') + ) + eq( + indices32[#indices32], + exec_lua('return vim.str_byteindex(_G.test_text, "utf-32", 99999, false)') + ) + eq( + indices16[#indices16], + exec_lua('return vim.str_byteindex(_G.test_text, "utf-16", 99999, false)') + ) + eq( + indices8[#indices8], + exec_lua('return vim.str_byteindex(_G.test_text, "utf-8", 99999, false)') + ) + eq(2, exec_lua('return vim.str_byteindex("é", "utf-16", 2, false)')) + local i32, i16, i8 = 0, 0, 0 local len = 51 for k = 0, len do if indices32[i32] < k then @@ -338,9 +423,29 @@ describe('lua stdlib', function() i16 = i16 + 1 end end + if indices8[i8] < k then + i8 = i8 + 1 + end eq({ i32, i16 }, exec_lua('return {vim.str_utfindex(_G.test_text, ...)}', k), k) + eq({ i32 }, exec_lua('return {vim.str_utfindex(_G.test_text, "utf-32", ...)}', k), k) + eq({ i16 }, exec_lua('return {vim.str_utfindex(_G.test_text, "utf-16", ...)}', k), k) + eq({ i8 }, exec_lua('return {vim.str_utfindex(_G.test_text, "utf-8", ...)}', k), k) end - eq( + + eq({ #indices32, #indices16 }, exec_lua('return {vim.str_utfindex(_G.test_text)}')) + + eq(#indices32, exec_lua('return vim.str_utfindex(_G.test_text, "utf-32", math.huge, false)')) + eq(#indices16, exec_lua('return vim.str_utfindex(_G.test_text, "utf-16", math.huge, false)')) + eq(#indices8, exec_lua('return vim.str_utfindex(_G.test_text, "utf-8", math.huge, false)')) + + eq(#indices32, exec_lua('return vim.str_utfindex(_G.test_text, "utf-32")')) + eq(#indices16, exec_lua('return vim.str_utfindex(_G.test_text, "utf-16")')) + eq(#indices8, exec_lua('return vim.str_utfindex(_G.test_text, "utf-8")')) + matches( + 'invalid encoding', + pcall_err(exec_lua, 'return vim.str_utfindex(_G.test_text, "madeupencoding", ...)', 1) + ) + matches( 'index out of range', pcall_err(exec_lua, 'return vim.str_utfindex(_G.test_text, ...)', len + 1) ) -- cgit From a9e725b26e08f494a9300d8f63acd3efa9c2da82 Mon Sep 17 00:00:00 2001 From: Donatas Date: Sun, 27 Oct 2024 18:36:39 +0200 Subject: feat(diagnostics)!: sort underline severity_sort (#30898) feat(diagnostics)!: sort underline with severity_sort BREAKING CHANGE: underline will be applied with a higher value than `vim.hl.priorities.diagnostics` --- test/functional/lua/diagnostic_spec.lua | 34 +++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) (limited to 'test/functional/lua') diff --git a/test/functional/lua/diagnostic_spec.lua b/test/functional/lua/diagnostic_spec.lua index 4ae1146703..eb1ac3e6a1 100644 --- a/test/functional/lua/diagnostic_spec.lua +++ b/test/functional/lua/diagnostic_spec.lua @@ -111,6 +111,19 @@ describe('vim.diagnostic', function() { details = true } ) end + + ---@param ns integer + function _G.get_underline_extmarks(ns) + ---@type integer + local underline_ns = vim.diagnostic.get_namespace(ns).user_data.underline_ns + return vim.api.nvim_buf_get_extmarks( + _G.diagnostic_bufnr, + underline_ns, + 0, + -1, + { details = true } + ) + end end) exec_lua(function() @@ -1813,6 +1826,21 @@ describe('vim.diagnostic', function() _G.make_info('Info', 4, 4, 4, 4), }) + function _G.get_highest_underline_hl(severity_sort) + vim.diagnostic.config({ + underline = true, + severity_sort = severity_sort, + }) + + local extmarks = _G.get_underline_extmarks(_G.diagnostic_ns) + + table.sort(extmarks, function(a, b) + return a[4].priority > b[4].priority + end) + + return extmarks[1][4].hl_group + end + function _G.get_virt_text_and_signs(severity_sort) vim.diagnostic.config({ severity_sort = severity_sort, @@ -1864,6 +1892,12 @@ describe('vim.diagnostic', function() result = exec_lua [[return _G.get_virt_text_and_signs({ reverse = true })]] eq({ 'Error', 'Warn', 'Info' }, result[1]) eq({ 'Info', 'Warn', 'Error' }, result[2]) + + local underline_hl = exec_lua [[return _G.get_highest_underline_hl(true)]] + eq('DiagnosticUnderlineError', underline_hl) + + underline_hl = exec_lua [[return _G.get_highest_underline_hl({ reverse = true })]] + eq('DiagnosticUnderlineInfo', underline_hl) end) it('can show diagnostic sources in virtual text', function() -- cgit From b34e137e43d359c8db4fb76028dea3b410842aff Mon Sep 17 00:00:00 2001 From: errael Date: Thu, 31 Oct 2024 18:11:15 -0700 Subject: feat(lua): allow vim.on_key() callback to consume the key (#30939) --- test/functional/lua/vim_spec.lua | 65 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) (limited to 'test/functional/lua') diff --git a/test/functional/lua/vim_spec.lua b/test/functional/lua/vim_spec.lua index 13e146a9da..55ca489a9a 100644 --- a/test/functional/lua/vim_spec.lua +++ b/test/functional/lua/vim_spec.lua @@ -28,6 +28,7 @@ local rmdir = n.rmdir local write_file = t.write_file local poke_eventloop = n.poke_eventloop local assert_alive = n.assert_alive +local expect = n.expect describe('lua stdlib', function() before_each(clear) @@ -3416,6 +3417,70 @@ describe('lua stdlib', function() | ]]) end) + + it('can discard input', function() + clear() + -- discard every other normal 'x' command + exec_lua [[ + n_key = 0 + + vim.on_key(function(buf, typed_buf) + if typed_buf == 'x' then + n_key = n_key + 1 + end + return (n_key % 2 == 0) and "" or nil + end) + ]] + + api.nvim_buf_set_lines(0, 0, -1, true, { '54321' }) + + feed('x') + expect('4321') + feed('x') + expect('4321') + feed('x') + expect('321') + feed('x') + expect('321') + end) + + it('callback invalid return', function() + clear() + -- second key produces an error which removes the callback + exec_lua [[ + n_call = 0 + vim.on_key(function(buf, typed_buf) + if typed_buf == 'x' then + n_call = n_call + 1 + end + return n_call >= 2 and '!' or nil + end) + ]] + + api.nvim_buf_set_lines(0, 0, -1, true, { '54321' }) + + local function cleanup_msg(msg) + return (remove_trace(msg):gsub('^Error.*\n *Messages: ', '')) + end + + feed('x') + eq(1, exec_lua [[ return n_call ]]) + + eq(1, exec_lua [[ return vim.on_key(nil, nil) ]]) + + eq('', cleanup_msg(eval('v:errmsg'))) + feed('x') + eq(2, exec_lua [[ return n_call ]]) + eq('return string must be empty', cleanup_msg(eval('v:errmsg'))) + command('let v:errmsg = ""') + + eq(0, exec_lua [[ return vim.on_key(nil, nil) ]]) + + feed('x') + eq(2, exec_lua [[ return n_call ]]) + expect('21') + eq('', cleanup_msg(eval('v:errmsg'))) + end) end) describe('vim.wait', function() -- cgit From 3688a333544251c887d78e6501eec55f0fb685f8 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 2 Nov 2024 10:11:06 +0800 Subject: fix(lua): show stacktrace for error in vim.on_key() callback (#31021) --- test/functional/lua/vim_spec.lua | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) (limited to 'test/functional/lua') diff --git a/test/functional/lua/vim_spec.lua b/test/functional/lua/vim_spec.lua index 55ca489a9a..9fd7184cb0 100644 --- a/test/functional/lua/vim_spec.lua +++ b/test/functional/lua/vim_spec.lua @@ -3312,10 +3312,17 @@ describe('lua stdlib', function() eq('inext lines', exec_lua [[return table.concat(keys, '')]]) end) - it('skips any function that caused an error', function() + it('skips any function that caused an error and shows stacktrace', function() insert([[hello world]]) exec_lua [[ + local function ErrF2() + error("Dumb Error") + end + local function ErrF1() + ErrF2() + end + keys = {} return vim.on_key(function(buf) @@ -3326,7 +3333,7 @@ describe('lua stdlib', function() table.insert(keys, buf) if buf == 'l' then - error("Dumb Error") + ErrF1() end end) ]] @@ -3336,6 +3343,19 @@ describe('lua stdlib', function() -- Only the first letter gets added. After that we remove the callback eq('inext l', exec_lua [[ return table.concat(keys, '') ]]) + + local errmsg = api.nvim_get_vvar('errmsg') + matches( + [[ +^Error executing vim%.on%_key%(%) callbacks:.* +With ns%_id %d+: .*: Dumb Error +stack traceback: +.*: in function 'error' +.*: in function 'ErrF2' +.*: in function 'ErrF1' +.*]], + errmsg + ) end) it('argument 1 is keys after mapping, argument 2 is typed keys', function() @@ -3449,6 +3469,7 @@ describe('lua stdlib', function() -- second key produces an error which removes the callback exec_lua [[ n_call = 0 + vim.on_key(function(buf, typed_buf) if typed_buf == 'x' then n_call = n_call + 1 @@ -3460,7 +3481,7 @@ describe('lua stdlib', function() api.nvim_buf_set_lines(0, 0, -1, true, { '54321' }) local function cleanup_msg(msg) - return (remove_trace(msg):gsub('^Error.*\n *Messages: ', '')) + return msg:gsub('^Error .*\nWith ns%_id %d+: ', '') end feed('x') -- cgit From a27419f3fc540f66567f4559a796cd6758f1bb1f Mon Sep 17 00:00:00 2001 From: Famiu Haque Date: Mon, 4 Nov 2024 19:00:12 +0600 Subject: feat(options)!: disallow setting hidden options #28400 Problem: There are three different ways of marking an option as hidden, `enable_if = false`, `hidden = true` and `immutable = true`. These also have different behaviors. Options hidden with `enable_if = false` can't have their value fetched using Vim script or the API, but options hidden with `hidden = true` or `immutable = true` can. On the other hand, options with `hidden = true` do not error when trying to set their value, but options with `immutable = true` do. Solution: Remove `enable_if = false`, remove the `hidden` property for options, and use `immutable = true` to mark an option as hidden instead. Also make hidden option variable pointers always point to the default value, which allows fetching the value of every hidden option using Vim script and the API. This does also mean that trying to set a hidden option will now give an error instead of just being ignored. --- test/functional/lua/runtime_spec.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'test/functional/lua') diff --git a/test/functional/lua/runtime_spec.lua b/test/functional/lua/runtime_spec.lua index f63363d6d9..6705dff847 100644 --- a/test/functional/lua/runtime_spec.lua +++ b/test/functional/lua/runtime_spec.lua @@ -21,7 +21,9 @@ describe('runtime:', function() exec('set rtp+=' .. plug_dir) exec([[ set shell=doesnotexist - set completeslash=slash + if exists('+completeslash') + set completeslash=slash + endif set isfname+=(,) ]]) end) -- cgit From ad3472e291694b6c589d8a664459b03962eaac95 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Thu, 7 Nov 2024 16:21:49 +0000 Subject: fix(vim.system): resolve executable paths on windows Fixes #31107 --- test/functional/lua/system_spec.lua | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'test/functional/lua') diff --git a/test/functional/lua/system_spec.lua b/test/functional/lua/system_spec.lua index 482bfcf1a9..afbada007d 100644 --- a/test/functional/lua/system_spec.lua +++ b/test/functional/lua/system_spec.lua @@ -9,7 +9,7 @@ local function system_sync(cmd, opts) return exec_lua(function() local obj = vim.system(cmd, opts) - if opts.timeout then + if opts and opts.timeout then -- Minor delay before calling wait() so the timeout uv timer can have a headstart over the -- internal call to vim.wait() in wait(). vim.wait(10) @@ -75,7 +75,7 @@ describe('vim.system', function() it('kill processes', function() exec_lua(function() - local signal + local signal --- @type integer? local cmd = vim.system({ 'cat', '-' }, { stdin = true }, function(r) signal = r.signal end) -- run forever @@ -112,4 +112,12 @@ describe('vim.system', function() ) eq(true, exec_lua([[return _G.processed]])) end) + + if t.is_os('win') then + it('can resolve windows command extentions.', function() + t.write_file('test.bat', 'echo hello world') + system_sync({ 'chmod', '+x', 'test.bat' }) + system_sync({ './test' }) + end) + end end) -- cgit From 4f9260d06a48216862ebb34fc33744486b058f58 Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Fri, 8 Mar 2024 14:44:58 +0100 Subject: feat(ext_messages): add hl_id to ext_messages chunks Problem: Ext_messages chunks only contain the highlight attr id, which is not very useful for vim.ui_attach() consumers. Solotion: Add highlight group id to message chunks, which can easily be used to highlight text in the TUI through nvim_buf_set_extmark(): hl_group = synIDattr(id, "name"). --- test/functional/lua/ui_event_spec.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'test/functional/lua') diff --git a/test/functional/lua/ui_event_spec.lua b/test/functional/lua/ui_event_spec.lua index 0a6deaa41c..52045b76ff 100644 --- a/test/functional/lua/ui_event_spec.lua +++ b/test/functional/lua/ui_event_spec.lua @@ -151,9 +151,9 @@ describe('vim.ui_attach', function() { 'msg_history_show', { - { 'echomsg', { { 0, 'message1' } } }, - { '', { { 0, 'message2' } } }, - { 'echomsg', { { 0, 'message3' } } }, + { 'echomsg', { { 0, 'message1', 0 } } }, + { '', { { 0, 'message2', 0 } } }, + { 'echomsg', { { 0, 'message3', 0 } } }, }, }, }, actual, vim.inspect(actual)) -- cgit From e61228a214ebda9845db9462dad0a8c362d3963f Mon Sep 17 00:00:00 2001 From: bfredl Date: Mon, 11 Nov 2024 22:15:19 +0100 Subject: fix(tests): needing two calls to setup a screen is cringe Before calling "attach" a screen object is just a dummy container for (row, col) values whose purpose is to be sent as part of the "attach" function call anyway. Just create the screen in an attached state directly. Keep the complete (row, col, options) config together. It is still completely valid to later detach and re-attach as needed, including to another session. --- test/functional/lua/buffer_updates_spec.lua | 1 - test/functional/lua/commands_spec.lua | 2 -- test/functional/lua/hl_spec.lua | 1 - test/functional/lua/loop_spec.lua | 1 - test/functional/lua/luaeval_spec.lua | 1 - test/functional/lua/overrides_spec.lua | 2 -- test/functional/lua/secure_spec.lua | 1 - test/functional/lua/thread_spec.lua | 2 -- test/functional/lua/ui_event_spec.lua | 4 +--- test/functional/lua/vim_spec.lua | 8 -------- test/functional/lua/with_spec.lua | 2 -- 11 files changed, 1 insertion(+), 24 deletions(-) (limited to 'test/functional/lua') diff --git a/test/functional/lua/buffer_updates_spec.lua b/test/functional/lua/buffer_updates_spec.lua index f277868b1c..7d034222c8 100644 --- a/test/functional/lua/buffer_updates_spec.lua +++ b/test/functional/lua/buffer_updates_spec.lua @@ -344,7 +344,6 @@ describe('lua buffer event callbacks: on_lines', function() it('setting extmark in on_lines callback works', function() local screen = Screen.new(40, 6) - screen:attach() api.nvim_buf_set_lines(0, 0, -1, true, { 'aaa', 'bbb', 'ccc' }) exec_lua(function() diff --git a/test/functional/lua/commands_spec.lua b/test/functional/lua/commands_spec.lua index 456ee13da2..1fd01cfd5a 100644 --- a/test/functional/lua/commands_spec.lua +++ b/test/functional/lua/commands_spec.lua @@ -111,7 +111,6 @@ describe(':lua', function() it('can show multiline error messages', function() local screen = Screen.new(40, 10) - screen:attach() screen:set_default_attr_ids({ [1] = { bold = true, foreground = Screen.colors.Blue1 }, [2] = { bold = true, reverse = true }, @@ -204,7 +203,6 @@ describe(':lua', function() it('with range', function() local screen = Screen.new(40, 10) - screen:attach() api.nvim_buf_set_lines(0, 0, 0, 0, { 'nonsense', 'function x() print "hello" end', 'x()' }) -- ":{range}lua" fails on invalid Lua code. diff --git a/test/functional/lua/hl_spec.lua b/test/functional/lua/hl_spec.lua index a123c79ffb..89881973bf 100644 --- a/test/functional/lua/hl_spec.lua +++ b/test/functional/lua/hl_spec.lua @@ -18,7 +18,6 @@ describe('vim.hl.range', function() screen:add_extra_attr_ids({ [100] = { foreground = Screen.colors.Blue, background = Screen.colors.Yellow, bold = true }, }) - screen:attach() api.nvim_set_option_value('list', true, {}) api.nvim_set_option_value('listchars', 'eol:$', {}) api.nvim_buf_set_lines(0, 0, -1, true, { diff --git a/test/functional/lua/loop_spec.lua b/test/functional/lua/loop_spec.lua index de8200a5f1..0c72843fad 100644 --- a/test/functional/lua/loop_spec.lua +++ b/test/functional/lua/loop_spec.lua @@ -65,7 +65,6 @@ describe('vim.uv', function() it('is API safe', function() local screen = Screen.new(50, 10) - screen:attach() screen:set_default_attr_ids({ [1] = { bold = true, foreground = Screen.colors.Blue1 }, [2] = { bold = true, reverse = true }, diff --git a/test/functional/lua/luaeval_spec.lua b/test/functional/lua/luaeval_spec.lua index 2b23f72c7d..5fea79141c 100644 --- a/test/functional/lua/luaeval_spec.lua +++ b/test/functional/lua/luaeval_spec.lua @@ -510,7 +510,6 @@ describe('v:lua', function() it('works in func options', function() local screen = Screen.new(60, 8) - screen:attach() api.nvim_set_option_value('omnifunc', 'v:lua.mymod.omni', {}) feed('isome st') screen:expect{grid=[[ diff --git a/test/functional/lua/overrides_spec.lua b/test/functional/lua/overrides_spec.lua index 33a2813200..2f332d2c10 100644 --- a/test/functional/lua/overrides_spec.lua +++ b/test/functional/lua/overrides_spec.lua @@ -158,7 +158,6 @@ describe('print', function() it('blank line in message works', function() local screen = Screen.new(40, 8) - screen:attach() screen:set_default_attr_ids({ [0] = { bold = true, foreground = Screen.colors.Blue }, [1] = { bold = true, foreground = Screen.colors.SeaGreen }, @@ -196,7 +195,6 @@ describe('debug.debug', function() before_each(function() screen = Screen.new() - screen:attach() screen:set_default_attr_ids { [0] = { bold = true, foreground = 255 }, [1] = { bold = true, reverse = true }, diff --git a/test/functional/lua/secure_spec.lua b/test/functional/lua/secure_spec.lua index c58fd689b7..b40b084ef9 100644 --- a/test/functional/lua/secure_spec.lua +++ b/test/functional/lua/secure_spec.lua @@ -39,7 +39,6 @@ describe('vim.secure', function() it('works', function() local screen = Screen.new(80, 8) - screen:attach() screen:set_default_attr_ids({ [1] = { bold = true, foreground = Screen.colors.Blue1 }, [2] = { bold = true, reverse = true }, diff --git a/test/functional/lua/thread_spec.lua b/test/functional/lua/thread_spec.lua index cbf23517dc..310705fd97 100644 --- a/test/functional/lua/thread_spec.lua +++ b/test/functional/lua/thread_spec.lua @@ -17,7 +17,6 @@ describe('thread', function() before_each(function() clear() screen = Screen.new(50, 10) - screen:attach() end) it('entry func is executed in protected mode', function() @@ -257,7 +256,6 @@ describe('threadpool', function() it('with invalid return value', function() local screen = Screen.new(50, 10) - screen:attach() exec_lua [[ local work = vim.uv.new_work(function() return {} end, function() end) diff --git a/test/functional/lua/ui_event_spec.lua b/test/functional/lua/ui_event_spec.lua index 52045b76ff..714332bc2f 100644 --- a/test/functional/lua/ui_event_spec.lua +++ b/test/functional/lua/ui_event_spec.lua @@ -41,7 +41,6 @@ describe('vim.ui_attach', function() [6] = { reverse = true, bold = true }, [7] = { background = Screen.colors.Yellow1 }, }) - screen:attach() end) local function expect_events(expected) @@ -251,8 +250,7 @@ describe('vim.ui_attach', function() it('error in callback is logged', function() clear({ env = { NVIM_LOG_FILE = testlog } }) - local screen = Screen.new() - screen:attach() + local _ = Screen.new() exec_lua([[ local ns = vim.api.nvim_create_namespace('testspace') vim.ui_attach(ns, { ext_popupmenu = true }, function() error(42) end) diff --git a/test/functional/lua/vim_spec.lua b/test/functional/lua/vim_spec.lua index 9fd7184cb0..0578d88e66 100644 --- a/test/functional/lua/vim_spec.lua +++ b/test/functional/lua/vim_spec.lua @@ -637,7 +637,6 @@ describe('lua stdlib', function() matches('big failure\nvery async', remove_trace(eval('v:errmsg'))) local screen = Screen.new(60, 5) - screen:attach() screen:expect { grid = [[ ^ | @@ -1403,7 +1402,6 @@ describe('lua stdlib', function() end) local screen = Screen.new(50, 7) - screen:attach() exec_lua([[ timer = vim.uv.new_timer() timer:start(20, 0, function () @@ -2130,7 +2128,6 @@ describe('lua stdlib', function() eq({ 1, 5 }, api.nvim_win_get_cursor(0)) local screen = Screen.new(60, 3) - screen:attach() eq(1, eval('v:hlsearch')) screen:expect { grid = [[ @@ -3401,7 +3398,6 @@ stack traceback: it('callback is not invoked recursively #30752', function() local screen = Screen.new(60, 10) - screen:attach() exec_lua([[ vim.on_key(function(key, typed) vim.api.nvim_echo({ @@ -3779,7 +3775,6 @@ stack traceback: it('fails in fast callbacks #26122', function() local screen = Screen.new(80, 10) - screen:attach() exec_lua([[ local timer = vim.uv.new_timer() timer:start(0, 0, function() @@ -3797,7 +3792,6 @@ stack traceback: it('vim.notify_once', function() local screen = Screen.new(60, 5) - screen:attach() screen:expect { grid = [[ ^ | @@ -3994,7 +3988,6 @@ stack traceback: it('updates ruler if cursor moved', function() -- Fixed for win_execute in vim-patch:8.1.2124, but should've applied to nvim_win_call too! local screen = Screen.new(30, 5) - screen:attach() exec_lua [[ _G.api = vim.api vim.opt.ruler = true @@ -4137,7 +4130,6 @@ stack traceback: it('vim.lua_omnifunc', function() local screen = Screen.new(60, 5) - screen:attach() command [[ set omnifunc=v:lua.vim.lua_omnifunc ]] -- Note: the implementation is shared with lua command line completion. diff --git a/test/functional/lua/with_spec.lua b/test/functional/lua/with_spec.lua index 99b80ef749..6127e83619 100644 --- a/test/functional/lua/with_spec.lua +++ b/test/functional/lua/with_spec.lua @@ -1029,7 +1029,6 @@ describe('vim._with', function() [1] = { bold = true, reverse = true }, [2] = { bold = true, foreground = Screen.colors.Blue }, } - screen:attach() exec_lua [[ vim._with({ silent = true }, function() vim.cmd.echo('"ccc"') end) ]] screen:expect [[ ^ | @@ -1178,7 +1177,6 @@ describe('vim._with', function() [1] = { reverse = true }, [2] = { bold = true, reverse = true }, } - screen:attach() exec_lua [[ vim.opt.ruler = true local lines = {} -- cgit From de48fbbd5f8800bd7f1909a6fb41e53e871cf74c Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Thu, 20 Jun 2024 14:48:06 +0200 Subject: fix(messages)!: vim.ui_attach message callbacks are unsafe Problem: Lua callbacks for "msg_show" events with vim.ui_attach() are executed when it is not safe. Solution: Disallow non-fast API calls for "msg_show" event callbacks. Automatically detach callback after excessive errors. Make sure fast APIs do not modify Nvim state. --- test/functional/lua/loop_spec.lua | 4 +- test/functional/lua/ui_event_spec.lua | 155 ++++++++++++++++++++++++++++------ test/functional/lua/vim_spec.lua | 4 +- 3 files changed, 135 insertions(+), 28 deletions(-) (limited to 'test/functional/lua') diff --git a/test/functional/lua/loop_spec.lua b/test/functional/lua/loop_spec.lua index 0c72843fad..52a8fec0bf 100644 --- a/test/functional/lua/loop_spec.lua +++ b/test/functional/lua/loop_spec.lua @@ -87,9 +87,9 @@ describe('vim.uv', function() screen:expect([[ | {2: }| - {3:Error executing luv callback:} | + {3:Error executing callback:} | {3:[string ""]:5: E5560: nvim_set_var must not }| - {3:be called in a lua loop callback} | + {3:be called in a fast event context} | {3:stack traceback:} | {3: [C]: in function 'nvim_set_var'} | {3: [string ""]:5: in function <[string }| diff --git a/test/functional/lua/ui_event_spec.lua b/test/functional/lua/ui_event_spec.lua index 714332bc2f..f78fced14e 100644 --- a/test/functional/lua/ui_event_spec.lua +++ b/test/functional/lua/ui_event_spec.lua @@ -32,15 +32,6 @@ describe('vim.ui_attach', function() ]] screen = Screen.new(40, 5) - screen:set_default_attr_ids({ - [1] = { bold = true, foreground = Screen.colors.Blue1 }, - [2] = { bold = true }, - [3] = { background = Screen.colors.Grey }, - [4] = { background = Screen.colors.LightMagenta }, - [5] = { reverse = true }, - [6] = { reverse = true, bold = true }, - [7] = { background = Screen.colors.Yellow1 }, - }) end) local function expect_events(expected) @@ -55,7 +46,7 @@ describe('vim.ui_attach', function() grid = [[ fo^ | {1:~ }|*3 - {2:-- INSERT --} | + {5:-- INSERT --} | ]], } @@ -64,7 +55,7 @@ describe('vim.ui_attach', function() grid = [[ food^ | {1:~ }|*3 - {2:-- INSERT --} | + {5:-- INSERT --} | ]], } expect_events { @@ -83,7 +74,7 @@ describe('vim.ui_attach', function() grid = [[ foobar^ | {1:~ }|*3 - {2:-- INSERT --} | + {5:-- INSERT --} | ]], } expect_events { @@ -105,10 +96,10 @@ describe('vim.ui_attach', function() screen:expect { grid = [[ food^ | - {3:food }{1: }| + {12:food }{1: }| {4:foobar }{1: }| {4:foo }{1: }| - {2:-- INSERT --} | + {5:-- INSERT --} | ]], } expect_events {} @@ -180,12 +171,17 @@ describe('vim.ui_attach', function() exec_lua([[ _G.cmdline = 0 vim.ui_attach(ns, { ext_messages = true }, function(ev) - vim.cmd.redraw() + if ev == 'msg_show' then + vim.schedule(function() vim.cmd.redraw() end) + else + vim.cmd.redraw() + end _G.cmdline = _G.cmdline + (ev == 'cmdline_show' and 1 or 0) end )]]) feed(':') - eq(1, exec_lua('return _G.cmdline')) + n.assert_alive() + eq(2, exec_lua('return _G.cmdline')) n.assert_alive() feed('versionv') n.assert_alive() @@ -211,9 +207,9 @@ describe('vim.ui_attach', function() screen:expect({ grid = [[ cmdline | - {5:cmdline [+] }| + {2:cmdline [+] }| fooba^r | - {6:[No Name] [+] }| + {3:[No Name] [+] }| | ]], }) @@ -222,9 +218,9 @@ describe('vim.ui_attach', function() screen:expect({ grid = [[ foo | - {5:cmdline [+] }| - {5:foo}ba^r | - {6:[No Name] [+] }| + {2:cmdline [+] }| + {2:foo}ba^r | + {3:[No Name] [+] }| | ]], }) @@ -233,13 +229,124 @@ describe('vim.ui_attach', function() screen:expect({ grid = [[ %s/bar/baz | - {5:cmdline [+] }| - foo{7:ba^z} | - {6:[No Name] [+] }| + {2:cmdline [+] }| + foo{10:ba^z} | + {3:[No Name] [+] }| | ]], }) end) + + it('aborts :function on error with ext_messages', function() + exec_lua([[ + vim.ui_attach(ns, { ext_messages = true }, function(event, _, content) + if event == "msg_show" then + -- "fast-api" does not prevent aborting :function + vim.api.nvim_get_runtime_file("foo", false) + -- non-"fast-api" is not allowed in msg_show callback and should be scheduled + local _, err = pcall(vim.api.nvim_buf_set_lines, 0, -2, -1, false, { content[1][2] }) + vim.schedule(function() + vim.api.nvim_buf_set_lines(0, -2, -1, false, { content[1][2], err }) + end) + end + end) + ]]) + feed(':func Foo()barendf:func Foo()') + screen:expect({ + grid = [[ + ^E122: Function Foo already exists, add !| + to replace it | + E5560: nvim_buf_set_lines must not be ca| + lled in a fast event context | + {1:~ }| + ]], + messages = { + { + content = { { 'E122: Function Foo already exists, add ! to replace it', 9, 7 } }, + kind = 'emsg', + }, + }, + }) + end) + + it('detaches after excessive errors', function() + screen:add_extra_attr_ids({ [100] = { bold = true, foreground = Screen.colors.SeaGreen } }) + exec_lua([[ + vim.ui_attach(vim.api.nvim_create_namespace(''), { ext_messages = true }, function() + vim.api.nvim_buf_set_lines(0, -2, -1, false, { err[1] }) + end) + ]]) + screen:expect({ + grid = [[ + ^ | + {1:~ }|*4 + ]], + }) + feed('ifoo') + screen:expect({ + grid = [[ + foo^ | + {1:~ }|*4 + ]], + showmode = { { '-- INSERT --', 5, 12 } }, + }) + feed(':1mes clear:mes') + screen:expect({ + grid = [[ + foo | + {3: }| + {9:Excessive errors in vim.ui_attach() call}| + {9:back from ns: 2.} | + {100:Press ENTER or type command to continue}^ | + ]], + }) + feed('') + -- Also when scheduled + exec_lua([[ + vim.ui_attach(vim.api.nvim_create_namespace(''), { ext_messages = true }, function() + vim.schedule(function() vim.api.nvim_buf_set_lines(0, -2, -1, false, { err[1] }) end) + end) + ]]) + screen:expect({ + any = 'fo^o', + messages = { + { + content = { + { + 'Error executing vim.schedule lua callback: [string ""]:2: attempt to index global \'err\' (a nil value)\nstack traceback:\n\t[string ""]:2: in function <[string ""]:2>', + 9, + 7, + }, + }, + kind = 'lua_error', + }, + { + content = { + { + 'Error executing vim.schedule lua callback: [string ""]:2: attempt to index global \'err\' (a nil value)\nstack traceback:\n\t[string ""]:2: in function <[string ""]:2>', + 9, + 7, + }, + }, + kind = 'lua_error', + }, + { + content = { { 'Press ENTER or type command to continue', 100, 19 } }, + kind = 'return_prompt', + }, + }, + }) + feed(':1mes clear:mes') + screen:expect({ + grid = [[ + foo | + {3: }| + {9:Excessive errors in vim.ui_attach() call}| + {9:back from ns: 3.} | + {100:Press ENTER or type command to continue}^ | + ]], + }) + end) end) describe('vim.ui_attach', function() diff --git a/test/functional/lua/vim_spec.lua b/test/functional/lua/vim_spec.lua index 0578d88e66..3cfbfe167a 100644 --- a/test/functional/lua/vim_spec.lua +++ b/test/functional/lua/vim_spec.lua @@ -1414,7 +1414,7 @@ describe('lua stdlib', function() screen:expect { grid = [[ {9:[string ""]:6: E5560: rpcrequest must not be}| - {9: called in a lua loop callback} | + {9: called in a fast event context} | {9:stack traceback:} | {9: [C]: in function 'rpcrequest'} | {9: [string ""]:6: in function <[string }| @@ -3783,7 +3783,7 @@ stack traceback: end) ]]) screen:expect({ - any = pesc('E5560: vim.wait must not be called in a lua loop callback'), + any = pesc('E5560: vim.wait must not be called in a fast event context'), }) feed('') assert_alive() -- cgit From 6e4df18b457e9743c34068fd6e0a89fd04d3526c Mon Sep 17 00:00:00 2001 From: luukvbaal Date: Fri, 15 Nov 2024 23:34:42 +0100 Subject: fix(ui): no fast context for prompt message kinds #31224 Problem: No longer able to show prompt messages with vim.ui_attach(). Solution: Do not execute callback in fast context for prompt message kinds. These events must be safe to show the incoming message so the event itself serves to indicate that the message should be shown immediately. --- test/functional/lua/ui_event_spec.lua | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) (limited to 'test/functional/lua') diff --git a/test/functional/lua/ui_event_spec.lua b/test/functional/lua/ui_event_spec.lua index f78fced14e..0580442d0a 100644 --- a/test/functional/lua/ui_event_spec.lua +++ b/test/functional/lua/ui_event_spec.lua @@ -237,20 +237,21 @@ describe('vim.ui_attach', function() }) end) - it('aborts :function on error with ext_messages', function() + it('msg_show in fast context', function() exec_lua([[ vim.ui_attach(ns, { ext_messages = true }, function(event, _, content) if event == "msg_show" then - -- "fast-api" does not prevent aborting :function vim.api.nvim_get_runtime_file("foo", false) -- non-"fast-api" is not allowed in msg_show callback and should be scheduled local _, err = pcall(vim.api.nvim_buf_set_lines, 0, -2, -1, false, { content[1][2] }) + pcall(vim.api.nvim__redraw, { flush = true }) vim.schedule(function() vim.api.nvim_buf_set_lines(0, -2, -1, false, { content[1][2], err }) end) end end) ]]) + -- "fast-api" does not prevent aborting :function feed(':func Foo()barendf:func Foo()') screen:expect({ grid = [[ @@ -267,6 +268,23 @@ describe('vim.ui_attach', function() }, }, }) + -- No fast context for prompt message kinds + feed(':%s/Function/Replacement/c') + screen:expect({ + grid = [[ + ^E122: {10:Function} Foo already exists, add !| + to replace it | + replace with Replacement (y/n/a/q/l/^E/^| + Y)? | + {1:~ }| + ]], + messages = { + { + content = { { 'replace with Replacement (y/n/a/q/l/^E/^Y)?', 6, 19 } }, + kind = 'confirm_sub', + }, + }, + }) end) it('detaches after excessive errors', function() -- cgit From 2eb227f5b6e4debf2d5c2320168d27b4c25c3eb3 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 16 Nov 2024 08:56:42 +0800 Subject: test(lua/ui_event_spec): avoid polluting log file (#31229) --- test/functional/lua/ui_event_spec.lua | 44 +++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 20 deletions(-) (limited to 'test/functional/lua') diff --git a/test/functional/lua/ui_event_spec.lua b/test/functional/lua/ui_event_spec.lua index 0580442d0a..6d4be4d1f2 100644 --- a/test/functional/lua/ui_event_spec.lua +++ b/test/functional/lua/ui_event_spec.lua @@ -286,6 +286,28 @@ describe('vim.ui_attach', function() }, }) end) +end) + +describe('vim.ui_attach', function() + local screen + before_each(function() + clear({ env = { NVIM_LOG_FILE = testlog } }) + screen = Screen.new(40, 5) + end) + + after_each(function() + check_close() + os.remove(testlog) + end) + + it('error in callback is logged', function() + exec_lua([[ + local ns = vim.api.nvim_create_namespace('testspace') + vim.ui_attach(ns, { ext_popupmenu = true }, function() error(42) end) + ]]) + feed('ifoofoobarfo') + assert_log('Error executing UI event callback: Error executing lua: .*: 42', testlog, 100) + end) it('detaches after excessive errors', function() screen:add_extra_attr_ids({ [100] = { bold = true, foreground = Screen.colors.SeaGreen } }) @@ -314,7 +336,7 @@ describe('vim.ui_attach', function() foo | {3: }| {9:Excessive errors in vim.ui_attach() call}| - {9:back from ns: 2.} | + {9:back from ns: 1.} | {100:Press ENTER or type command to continue}^ | ]], }) @@ -360,27 +382,9 @@ describe('vim.ui_attach', function() foo | {3: }| {9:Excessive errors in vim.ui_attach() call}| - {9:back from ns: 3.} | + {9:back from ns: 2.} | {100:Press ENTER or type command to continue}^ | ]], }) end) end) - -describe('vim.ui_attach', function() - after_each(function() - check_close() - os.remove(testlog) - end) - - it('error in callback is logged', function() - clear({ env = { NVIM_LOG_FILE = testlog } }) - local _ = Screen.new() - exec_lua([[ - local ns = vim.api.nvim_create_namespace('testspace') - vim.ui_attach(ns, { ext_popupmenu = true }, function() error(42) end) - ]]) - feed('ifoofoobarfo') - assert_log('Error executing UI event callback: Error executing lua: .*: 42', testlog, 100) - end) -end) -- cgit From e025f5a5b30a1ef92e88fed0f0c548d2240d30c0 Mon Sep 17 00:00:00 2001 From: luukvbaal Date: Sun, 17 Nov 2024 19:21:50 +0100 Subject: fix(messages): proper multiline Lua print() messages #31205 Problem: Separate message emitted for each newline present in Lua print() arguments. Solution: Make msg_multiline() handle NUL bytes. Refactor print() to use msg_multiline(). Refactor vim.print() to use print(). --- test/functional/lua/ui_event_spec.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'test/functional/lua') diff --git a/test/functional/lua/ui_event_spec.lua b/test/functional/lua/ui_event_spec.lua index 6d4be4d1f2..c8616e3e11 100644 --- a/test/functional/lua/ui_event_spec.lua +++ b/test/functional/lua/ui_event_spec.lua @@ -142,7 +142,7 @@ describe('vim.ui_attach', function() 'msg_history_show', { { 'echomsg', { { 0, 'message1', 0 } } }, - { '', { { 0, 'message2', 0 } } }, + { 'lua_print', { { 0, 'message2', 0 } } }, { 'echomsg', { { 0, 'message3', 0 } } }, }, }, -- cgit From e2ad251c8d01726ecd54d925b5280ab32b448c13 Mon Sep 17 00:00:00 2001 From: altermo <107814000+altermo@users.noreply.github.com> Date: Mon, 18 Nov 2024 15:40:51 +0100 Subject: fix(api): nvim_get_option_value does not clean up on FileType error #31219 Problem: If there's an error in `FileType` autocmd, the filetype get-opt buffer doesn't get cleaned up. Solution: Call `aucmd_restbuf`. --- test/functional/lua/filetype_spec.lua | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'test/functional/lua') diff --git a/test/functional/lua/filetype_spec.lua b/test/functional/lua/filetype_spec.lua index 574c837f92..b6011d5268 100644 --- a/test/functional/lua/filetype_spec.lua +++ b/test/functional/lua/filetype_spec.lua @@ -161,6 +161,18 @@ describe('vim.filetype', function() end end end) + + it('.get_option() cleans up buffer on error', function() + api.nvim_create_autocmd('FileType', { pattern = 'foo', command = 'lua error()' }) + + local buf = api.nvim_get_current_buf() + + exec_lua(function() + pcall(vim.filetype.get_option, 'foo', 'lisp') + end) + + eq(buf, api.nvim_get_current_buf()) + end) end) describe('filetype.lua', function() -- cgit