aboutsummaryrefslogtreecommitdiff
path: root/test/functional/lua
diff options
context:
space:
mode:
authorJosh Rahm <joshuarahm@gmail.com>2023-11-29 22:39:54 +0000
committerJosh Rahm <joshuarahm@gmail.com>2023-11-29 22:39:54 +0000
commit21cb7d04c387e4198ca8098a884c78b56ffcf4c2 (patch)
tree84fe5690df1551f0bb2bdfe1a13aacd29ebc1de7 /test/functional/lua
parentd9c904f85a23a496df4eb6be42aa43f007b22d50 (diff)
parent4a8bf24ac690004aedf5540fa440e788459e5e34 (diff)
downloadrneovim-colorcolchar.tar.gz
rneovim-colorcolchar.tar.bz2
rneovim-colorcolchar.zip
Merge remote-tracking branch 'upstream/master' into colorcolcharcolorcolchar
Diffstat (limited to 'test/functional/lua')
-rw-r--r--test/functional/lua/api_spec.lua54
-rw-r--r--test/functional/lua/base64_spec.lua105
-rw-r--r--test/functional/lua/buffer_updates_spec.lua110
-rw-r--r--test/functional/lua/command_line_completion_spec.lua15
-rw-r--r--test/functional/lua/commands_spec.lua53
-rw-r--r--test/functional/lua/diagnostic_spec.lua102
-rw-r--r--test/functional/lua/ffi_spec.lua10
-rw-r--r--test/functional/lua/filetype_spec.lua37
-rw-r--r--test/functional/lua/fs_spec.lua58
-rw-r--r--test/functional/lua/help_spec.lua9
-rw-r--r--test/functional/lua/highlight_spec.lua2
-rw-r--r--test/functional/lua/inspector_spec.lua60
-rw-r--r--test/functional/lua/iter_spec.lua402
-rw-r--r--test/functional/lua/json_spec.lua111
-rw-r--r--test/functional/lua/loader_spec.lua56
-rw-r--r--test/functional/lua/loop_spec.lua18
-rw-r--r--test/functional/lua/luaeval_spec.lua18
-rw-r--r--test/functional/lua/overrides_spec.lua57
-rw-r--r--test/functional/lua/runtime_spec.lua216
-rw-r--r--test/functional/lua/secure_spec.lua27
-rw-r--r--test/functional/lua/snippet_spec.lua202
-rw-r--r--test/functional/lua/system_spec.lua100
-rw-r--r--test/functional/lua/text_spec.lua23
-rw-r--r--test/functional/lua/thread_spec.lua42
-rw-r--r--test/functional/lua/ui_event_spec.lua8
-rw-r--r--test/functional/lua/ui_spec.lua27
-rw-r--r--test/functional/lua/version_spec.lua273
-rw-r--r--test/functional/lua/vim_spec.lua487
-rw-r--r--test/functional/lua/watch_spec.lua178
29 files changed, 2501 insertions, 359 deletions
diff --git a/test/functional/lua/api_spec.lua b/test/functional/lua/api_spec.lua
index 03832978a4..d808693a9e 100644
--- a/test/functional/lua/api_spec.lua
+++ b/test/functional/lua/api_spec.lua
@@ -9,6 +9,7 @@ local eval = helpers.eval
local NIL = helpers.NIL
local eq = helpers.eq
local exec_lua = helpers.exec_lua
+local pcall_err = helpers.pcall_err
before_each(clear)
@@ -33,7 +34,7 @@ describe('luaeval(vim.api.…)', function()
describe('with errors', function()
it('transforms API error from nvim_buf_set_lines into lua error', function()
funcs.setline(1, {"abc", "def", "a\nb", "ttt"})
- eq({false, 'String cannot contain newlines'},
+ eq({false, "'replacement string' item contains newlines"},
funcs.luaeval('{pcall(vim.api.nvim_buf_set_lines, 1, 1, 2, false, {"b\\na"})}'))
end)
@@ -103,9 +104,9 @@ describe('luaeval(vim.api.…)', function()
eq(NIL, funcs.luaeval('vim.api.nvim__id(nil)'))
-- API strings from Blobs can work as NUL-terminated C strings
- eq('Vim(call):E5555: API call: Vim:E15: Invalid expression: ',
+ eq('Vim(call):E5555: API call: Vim:E15: Invalid expression: ""',
exc_exec('call nvim_eval(v:_null_blob)'))
- eq('Vim(call):E5555: API call: Vim:E15: Invalid expression: ',
+ eq('Vim(call):E5555: API call: Vim:E15: Invalid expression: ""',
exc_exec('call nvim_eval(0z)'))
eq(1, eval('nvim_eval(0z31)'))
@@ -171,9 +172,32 @@ describe('luaeval(vim.api.…)', function()
eq(4, eval([[type(luaeval('vim.api.nvim__id_dictionary({})'))]]))
end)
+ it('converts booleans in positional args', function()
+ eq({''}, exec_lua [[ return vim.api.nvim_buf_get_lines(0, 0, 10, false) ]])
+ eq({''}, exec_lua [[ return vim.api.nvim_buf_get_lines(0, 0, 10, nil) ]])
+ eq('Index out of bounds', pcall_err(exec_lua, [[ return vim.api.nvim_buf_get_lines(0, 0, 10, true) ]]))
+ eq('Index out of bounds', pcall_err(exec_lua, [[ return vim.api.nvim_buf_get_lines(0, 0, 10, 1) ]]))
+
+ -- this follows lua conventions for bools (not api convention for Boolean)
+ eq('Index out of bounds', pcall_err(exec_lua, [[ return vim.api.nvim_buf_get_lines(0, 0, 10, 0) ]]))
+ eq('Index out of bounds', pcall_err(exec_lua, [[ return vim.api.nvim_buf_get_lines(0, 0, 10, {}) ]]))
+ end)
+
+ it('converts booleans in optional args', function()
+ eq({}, exec_lua [[ return vim.api.nvim_exec2("echo 'foobar'", {output=false}) ]])
+ eq({}, exec_lua [[ return vim.api.nvim_exec2("echo 'foobar'", {}) ]]) -- same as {output=nil}
+
+ -- API conventions (not lua conventions): zero is falsy
+ eq({}, exec_lua [[ return vim.api.nvim_exec2("echo 'foobar'", {output=0}) ]])
+
+ eq({output='foobar'}, exec_lua [[ return vim.api.nvim_exec2("echo 'foobar'", {output=true}) ]])
+ eq({output='foobar'}, exec_lua [[ return vim.api.nvim_exec2("echo 'foobar'", {output=1}) ]])
+ eq([[Invalid 'output': not a boolean]], pcall_err(exec_lua, [[ return vim.api.nvim_exec2("echo 'foobar'", {output={}}) ]]))
+ end)
+
it('errors out correctly when working with API', function()
-- Conversion errors
- eq('Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Cannot convert given lua table',
+ eq([[Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Invalid 'obj': Cannot convert given Lua table]],
remove_trace(exc_exec([[call luaeval("vim.api.nvim__id({1, foo=42})")]])))
-- Errors in number of arguments
eq('Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Expected 1 argument',
@@ -183,32 +207,32 @@ describe('luaeval(vim.api.…)', function()
eq('Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Expected 2 arguments',
remove_trace(exc_exec([[call luaeval("vim.api.nvim_set_var(1, 2, 3)")]])))
-- Error in argument types
- eq('Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Expected lua string',
+ eq([[Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Invalid 'name': Expected Lua string]],
remove_trace(exc_exec([[call luaeval("vim.api.nvim_set_var(1, 2)")]])))
- eq('Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Expected lua number',
+ eq([[Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Invalid 'start': Expected Lua number]],
remove_trace(exc_exec([[call luaeval("vim.api.nvim_buf_get_lines(0, 'test', 1, false)")]])))
- eq('Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Number is not integral',
+ eq([[Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Invalid 'start': Number is not integral]],
remove_trace(exc_exec([[call luaeval("vim.api.nvim_buf_get_lines(0, 1.5, 1, false)")]])))
- eq('Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Expected Lua number',
+ eq([[Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Invalid 'window': Expected Lua number]],
remove_trace(exc_exec([[call luaeval("vim.api.nvim_win_is_valid(nil)")]])))
- eq('Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Expected lua table',
+ eq([[Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Invalid 'flt': Expected Lua number]],
remove_trace(exc_exec([[call luaeval("vim.api.nvim__id_float('test')")]])))
- eq('Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Unexpected type',
+ eq([[Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Invalid 'flt': Expected Float-like Lua table]],
remove_trace(exc_exec([[call luaeval("vim.api.nvim__id_float({[vim.type_idx]=vim.types.dictionary})")]])))
- eq('Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Expected lua table',
+ eq([[Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Invalid 'arr': Expected Lua table]],
remove_trace(exc_exec([[call luaeval("vim.api.nvim__id_array(1)")]])))
- eq('Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Unexpected type',
+ eq([[Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Invalid 'arr': Expected Array-like Lua table]],
remove_trace(exc_exec([[call luaeval("vim.api.nvim__id_array({[vim.type_idx]=vim.types.dictionary})")]])))
- eq('Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Expected lua table',
+ eq([[Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Invalid 'dct': Expected Lua table]],
remove_trace(exc_exec([[call luaeval("vim.api.nvim__id_dictionary(1)")]])))
- eq('Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Unexpected type',
+ eq([[Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Invalid 'dct': Expected Dict-like Lua table]],
remove_trace(exc_exec([[call luaeval("vim.api.nvim__id_dictionary({[vim.type_idx]=vim.types.array})")]])))
- eq('Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Expected lua table',
+ eq([[Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Expected Lua table]],
remove_trace(exc_exec([[call luaeval("vim.api.nvim_set_keymap('', '', '', '')")]])))
-- TODO: check for errors with Tabpage argument
diff --git a/test/functional/lua/base64_spec.lua b/test/functional/lua/base64_spec.lua
new file mode 100644
index 0000000000..f0d112c23e
--- /dev/null
+++ b/test/functional/lua/base64_spec.lua
@@ -0,0 +1,105 @@
+local helpers = require('test.functional.helpers')(after_each)
+local clear = helpers.clear
+local exec_lua = helpers.exec_lua
+local eq = helpers.eq
+local pcall_err = helpers.pcall_err
+local matches = helpers.matches
+
+describe('vim.base64', function()
+ before_each(clear)
+
+ local function encode(s)
+ return exec_lua([[return vim.base64.encode(...)]], s)
+ end
+
+ local function decode(s)
+ return exec_lua([[return vim.base64.decode(...)]], s)
+ end
+
+ it('works', function()
+ local values = {
+ '',
+ 'Many hands make light work.',
+ [[
+ Call me Ishmael. Some years ago—never mind how long precisely—having little or no money in
+ my purse, and nothing particular to interest me on shore, I thought I would sail about a
+ little and see the watery part of the world.
+ ]],
+ [[
+ It is a truth universally acknowledged, that a single man in possession of a good fortune,
+ must be in want of a wife.
+ ]],
+ 'Happy families are all alike; every unhappy family is unhappy in its own way.',
+ 'ЁЂЃЄЅІЇЈЉЊЋЌЍЎЏАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюя',
+ 'ÅÍÎÏ˝ÓÔÒÚÆ☃',
+ '𐐜 𐐔𐐇𐐝𐐀𐐡𐐇𐐓 𐐙𐐊𐐡𐐝𐐓/𐐝𐐇𐐗𐐊𐐤𐐔 𐐒𐐋𐐗 𐐒𐐌 𐐜 𐐡𐐀𐐖𐐇𐐤𐐓𐐝 𐐱𐑂 𐑄 𐐔𐐇𐐝𐐀𐐡𐐇𐐓 𐐏𐐆𐐅𐐤𐐆𐐚𐐊𐐡𐐝𐐆𐐓𐐆',
+ '👨‍👩‍👦 👨‍👩‍👧‍👦 👨‍👨‍👦 👩‍👩‍👧 👨‍👦 👨‍👧‍👦 👩‍👦 👩‍👧‍👦',
+ 'مُنَاقَشَةُ سُبُلِ اِسْتِخْدَامِ اللُّغَةِ فِي النُّظُمِ الْقَائِمَةِ وَفِيم يَخُصَّ التَّطْبِيقَاتُ الْحاسُوبِيَّةُ،',
+ [[
+ Ṱ̺̺̕o͞ ̷i̲̬͇̪͙n̝̗͕v̟̜̘̦͟o̶̙̰̠kè͚̮̺̪̹̱̤ ̖t̝͕̳̣̻̪͞h̼͓̲̦̳̘̲e͇̣̰̦̬͎ ̢̼̻̱̘h͚͎͙̜̣̲ͅi̦̲̣̰̤v̻͍e̺̭̳̪̰-m̢iͅn̖̺̞̲̯̰d̵̼̟͙̩̼̘̳ ̞̥̱̳̭r̛̗̘e͙p͠r̼̞̻̭̗e̺̠̣͟s̘͇̳͍̝͉e͉̥̯̞̲͚̬͜ǹ̬͎͎̟̖͇̤t͍̬̤͓̼̭͘ͅi̪̱n͠g̴͉ ͏͉ͅc̬̟h͡a̫̻̯͘o̫̟̖͍̙̝͉s̗̦̲.̨̹͈̣
+ ̡͓̞ͅI̗̘̦͝n͇͇͙v̮̫ok̲̫̙͈i̖͙̭̹̠̞n̡̻̮̣̺g̲͈͙̭͙̬͎ ̰t͔̦h̞̲e̢̤ ͍̬̲͖f̴̘͕̣è͖ẹ̥̩l͖͔͚i͓͚̦͠n͖͍̗͓̳̮g͍ ̨o͚̪͡f̘̣̬ ̖̘͖̟͙̮c҉͔̫͖͓͇͖ͅh̵̤̣͚͔á̗̼͕ͅo̼̣̥s̱͈̺̖̦̻͢.̛̖̞̠̫̰
+ ̗̺͖̹̯͓Ṯ̤͍̥͇͈h̲́e͏͓̼̗̙̼̣͔ ͇̜̱̠͓͍ͅN͕͠e̗̱z̘̝̜̺͙p̤̺̹͍̯͚e̠̻̠͜r̨̤͍̺̖͔̖̖d̠̟̭̬̝͟i̦͖̩͓͔̤a̠̗̬͉̙n͚͜ ̻̞̰͚ͅh̵͉i̳̞v̢͇ḙ͎͟-҉̭̩̼͔m̤̭̫i͕͇̝̦n̗͙ḍ̟ ̯̲͕͞ǫ̟̯̰̲͙̻̝f ̪̰̰̗̖̭̘͘c̦͍̲̞͍̩̙ḥ͚a̮͎̟̙͜ơ̩̹͎s̤.̝̝ ҉Z̡̖̜͖̰̣͉̜a͖̰͙̬͡l̲̫̳͍̩g̡̟̼̱͚̞̬ͅo̗͜.̟
+ ̦H̬̤̗̤͝e͜ ̜̥̝̻͍̟́w̕h̖̯͓o̝͙̖͎̱̮ ҉̺̙̞̟͈W̷̼̭a̺̪͍į͈͕̭͙̯̜t̶̼̮s̘͙͖̕ ̠̫̠B̻͍͙͉̳ͅe̵h̵̬͇̫͙i̹͓̳̳̮͎̫̕n͟d̴̪̜̖ ̰͉̩͇͙̲͞ͅT͖̼͓̪͢h͏͓̮̻e̬̝̟ͅ ̤̹̝W͙̞̝͔͇͝ͅa͏͓͔̹̼̣l̴͔̰̤̟͔ḽ̫.͕
+ Z̮̞̠͙͔ͅḀ̗̞͈̻̗Ḷ͙͎̯̹̞͓G̻O̭̗̮
+ ]],
+ }
+
+ for _, v in ipairs(values) do
+ eq(v, decode(encode(v)))
+ end
+
+ -- Explicitly check encoded output
+ eq('VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wcyBvdmVyIHRoZSBsYXp5IGRvZwo=', encode('The quick brown fox jumps over the lazy dog\n'))
+
+ -- Test vectors from rfc4648
+ local rfc4648 = {
+ { '', '' },
+ { 'f', 'Zg==', },
+ { 'fo', 'Zm8=' },
+ { 'foo', 'Zm9v' },
+ { 'foob', 'Zm9vYg==' },
+ { 'fooba', 'Zm9vYmE=' },
+ { 'foobar', 'Zm9vYmFy' },
+ }
+
+ for _, v in ipairs(rfc4648) do
+ local input = v[1]
+ local output = v[2]
+ eq(output, encode(input))
+ eq(input, decode(output))
+ end
+ end)
+
+ it('detects invalid input', function()
+ local invalid = {
+ 'A',
+ 'AA',
+ 'AAA',
+ 'A..A',
+ 'AA=A',
+ 'AA/=',
+ 'A/==',
+ 'A===',
+ '====',
+ 'Zm9vYmFyZm9vYmFyA..A',
+ 'Zm9vYmFyZm9vYmFyAA=A',
+ 'Zm9vYmFyZm9vYmFyAA/=',
+ 'Zm9vYmFyZm9vYmFyA/==',
+ 'Zm9vYmFyZm9vYmFyA===',
+ 'A..AZm9vYmFyZm9vYmFy',
+ 'Zm9vYmFyZm9vAA=A',
+ 'Zm9vYmFyZm9vAA/=',
+ 'Zm9vYmFyZm9vA/==',
+ 'Zm9vYmFyZm9vA===',
+ }
+
+ for _, v in ipairs(invalid) do
+ eq('Invalid input', pcall_err(decode, v))
+ end
+
+ eq('Expected 1 argument', pcall_err(encode))
+ eq('Expected 1 argument', pcall_err(decode))
+ matches('expected string', pcall_err(encode, 42))
+ matches('expected string', pcall_err(decode, 42))
+ end)
+end)
diff --git a/test/functional/lua/buffer_updates_spec.lua b/test/functional/lua/buffer_updates_spec.lua
index 2fd44b8b5f..51e4548edb 100644
--- a/test/functional/lua/buffer_updates_spec.lua
+++ b/test/functional/lua/buffer_updates_spec.lua
@@ -1,6 +1,6 @@
-- Test suite for testing interactions with API bindings
local helpers = require('test.functional.helpers')(after_each)
-local lfs = require('lfs')
+local luv = require('luv')
local command = helpers.command
local meths = helpers.meths
@@ -317,7 +317,18 @@ describe('lua buffer event callbacks: on_lines', function()
feed('1G0')
feed('P')
eq(meths.get_var('linesev'), { "lines", 1, 6, 0, 3, 3, 9 })
+ end)
+ it('calling nvim_buf_call() from callback does not cause Normal mode CTRL-A to misbehave #16729', function()
+ exec_lua([[
+ vim.api.nvim_buf_attach(0, false, {
+ on_lines = function(...)
+ vim.api.nvim_buf_call(0, function() end)
+ end,
+ })
+ ]])
+ feed('itest123<Esc><C-A>')
+ eq('test124', meths.get_current_line())
end)
end)
@@ -404,7 +415,7 @@ describe('lua: nvim_buf_attach on_bytes', function()
it('opening lines', function()
local check_events = setup_eventcheck(verify, origlines)
- -- meths.buf_set_option(0, 'autoindent', true)
+ -- meths.set_option_value('autoindent', true, {})
feed 'Go'
check_events {
{ "test1", "bytes", 1, 4, 7, 0, 114, 0, 0, 0, 1, 0, 1 };
@@ -417,7 +428,7 @@ describe('lua: nvim_buf_attach on_bytes', function()
it('opening lines with autoindent', function()
local check_events = setup_eventcheck(verify, origlines)
- meths.buf_set_option(0, 'autoindent', true)
+ meths.set_option_value('autoindent', true, {})
feed 'Go'
check_events {
{ "test1", "bytes", 1, 4, 7, 0, 114, 0, 0, 0, 1, 0, 5 };
@@ -451,8 +462,8 @@ describe('lua: nvim_buf_attach on_bytes', function()
it('continuing comments with fo=or', function()
local check_events = setup_eventcheck(verify, {'// Comment'})
- meths.buf_set_option(0, 'formatoptions', 'ro')
- meths.buf_set_option(0, 'filetype', 'c')
+ meths.set_option_value('formatoptions', 'ro', {})
+ meths.set_option_value('filetype', 'c', {})
feed 'A<CR>'
check_events {
{ "test1", "bytes", 1, 4, 0, 10, 10, 0, 0, 0, 1, 3, 4 };
@@ -592,7 +603,7 @@ describe('lua: nvim_buf_attach on_bytes', function()
it('inccomand=nosplit and substitute', function()
local check_events = setup_eventcheck(verify,
{"abcde", "12345"})
- meths.set_option('inccommand', 'nosplit')
+ meths.set_option_value('inccommand', 'nosplit', {})
-- linewise substitute
feed(':%s/bcd/')
@@ -743,7 +754,8 @@ describe('lua: nvim_buf_attach on_bytes', function()
write_file("Xtest-reload", dedent [[
old line 1
old line 2]])
- lfs.touch("Xtest-reload", os.time() - 10)
+ local atime = os.time() - 10
+ luv.fs_utime("Xtest-reload", atime, atime)
command "e Xtest-reload"
command "set autoread"
@@ -814,53 +826,53 @@ describe('lua: nvim_buf_attach on_bytes', function()
feed("<esc>u")
check_events {
- { "test1", "bytes", 1, 8, 0, 0, 0, 0, 1, 1, 0, 4, 4 },
- { "test1", "bytes", 1, 8, 0, 0, 0, 0, 4, 4, 0, 0, 0 }
+ { "test1", "bytes", 1, 9, 0, 0, 0, 0, 1, 1, 0, 4, 4 },
+ { "test1", "bytes", 1, 9, 0, 0, 0, 0, 4, 4, 0, 0, 0 }
}
-- in REPLACE mode
feed("R<tab><tab>")
check_events {
- { "test1", "bytes", 1, 9, 0, 0, 0, 0, 1, 1, 0, 1, 1 },
- { "test1", "bytes", 1, 10, 0, 1, 1, 0, 0, 0, 0, 1, 1 },
- { "test1", "bytes", 1, 11, 0, 2, 2, 0, 1, 1, 0, 1, 1 },
- { "test1", "bytes", 1, 12, 0, 3, 3, 0, 0, 0, 0, 1, 1 },
- { "test1", "bytes", 1, 13, 0, 0, 0, 0, 4, 4, 0, 1, 1 },
+ { "test1", "bytes", 1, 10, 0, 0, 0, 0, 1, 1, 0, 1, 1 },
+ { "test1", "bytes", 1, 11, 0, 1, 1, 0, 0, 0, 0, 1, 1 },
+ { "test1", "bytes", 1, 12, 0, 2, 2, 0, 1, 1, 0, 1, 1 },
+ { "test1", "bytes", 1, 13, 0, 3, 3, 0, 0, 0, 0, 1, 1 },
+ { "test1", "bytes", 1, 14, 0, 0, 0, 0, 4, 4, 0, 1, 1 },
}
feed("<esc>u")
check_events {
- { "test1", "bytes", 1, 14, 0, 0, 0, 0, 1, 1, 0, 4, 4 },
- { "test1", "bytes", 1, 14, 0, 2, 2, 0, 2, 2, 0, 1, 1 },
- { "test1", "bytes", 1, 14, 0, 0, 0, 0, 2, 2, 0, 1, 1 }
+ { "test1", "bytes", 1, 16, 0, 0, 0, 0, 1, 1, 0, 4, 4 },
+ { "test1", "bytes", 1, 16, 0, 2, 2, 0, 2, 2, 0, 1, 1 },
+ { "test1", "bytes", 1, 16, 0, 0, 0, 0, 2, 2, 0, 1, 1 }
}
-- in VISUALREPLACE mode
feed("gR<tab><tab>")
check_events {
- { "test1", "bytes", 1, 15, 0, 0, 0, 0, 1, 1, 0, 1, 1 };
- { "test1", "bytes", 1, 16, 0, 1, 1, 0, 1, 1, 0, 1, 1 };
- { "test1", "bytes", 1, 17, 0, 2, 2, 0, 1, 1, 0, 1, 1 };
- { "test1", "bytes", 1, 18, 0, 3, 3, 0, 1, 1, 0, 1, 1 };
- { "test1", "bytes", 1, 19, 0, 3, 3, 0, 1, 1, 0, 0, 0 };
- { "test1", "bytes", 1, 20, 0, 3, 3, 0, 0, 0, 0, 1, 1 };
- { "test1", "bytes", 1, 22, 0, 2, 2, 0, 1, 1, 0, 0, 0 };
- { "test1", "bytes", 1, 23, 0, 2, 2, 0, 0, 0, 0, 1, 1 };
- { "test1", "bytes", 1, 25, 0, 1, 1, 0, 1, 1, 0, 0, 0 };
- { "test1", "bytes", 1, 26, 0, 1, 1, 0, 0, 0, 0, 1, 1 };
- { "test1", "bytes", 1, 28, 0, 0, 0, 0, 1, 1, 0, 0, 0 };
- { "test1", "bytes", 1, 29, 0, 0, 0, 0, 0, 0, 0, 1, 1 };
- { "test1", "bytes", 1, 31, 0, 0, 0, 0, 4, 4, 0, 1, 1 };
+ { "test1", "bytes", 1, 17, 0, 0, 0, 0, 1, 1, 0, 1, 1 };
+ { "test1", "bytes", 1, 18, 0, 1, 1, 0, 1, 1, 0, 1, 1 };
+ { "test1", "bytes", 1, 19, 0, 2, 2, 0, 1, 1, 0, 1, 1 };
+ { "test1", "bytes", 1, 20, 0, 3, 3, 0, 1, 1, 0, 1, 1 };
+ { "test1", "bytes", 1, 21, 0, 3, 3, 0, 1, 1, 0, 0, 0 };
+ { "test1", "bytes", 1, 22, 0, 3, 3, 0, 0, 0, 0, 1, 1 };
+ { "test1", "bytes", 1, 24, 0, 2, 2, 0, 1, 1, 0, 0, 0 };
+ { "test1", "bytes", 1, 25, 0, 2, 2, 0, 0, 0, 0, 1, 1 };
+ { "test1", "bytes", 1, 27, 0, 1, 1, 0, 1, 1, 0, 0, 0 };
+ { "test1", "bytes", 1, 28, 0, 1, 1, 0, 0, 0, 0, 1, 1 };
+ { "test1", "bytes", 1, 30, 0, 0, 0, 0, 1, 1, 0, 0, 0 };
+ { "test1", "bytes", 1, 31, 0, 0, 0, 0, 0, 0, 0, 1, 1 };
+ { "test1", "bytes", 1, 33, 0, 0, 0, 0, 4, 4, 0, 1, 1 };
}
-- inserting tab after other tabs
command("set sw=4")
feed("<esc>0a<tab>")
check_events {
- { "test1", "bytes", 1, 32, 0, 1, 1, 0, 0, 0, 0, 1, 1 };
- { "test1", "bytes", 1, 33, 0, 2, 2, 0, 0, 0, 0, 1, 1 };
- { "test1", "bytes", 1, 34, 0, 3, 3, 0, 0, 0, 0, 1, 1 };
- { "test1", "bytes", 1, 35, 0, 4, 4, 0, 0, 0, 0, 1, 1 };
- { "test1", "bytes", 1, 36, 0, 1, 1, 0, 4, 4, 0, 1, 1 };
+ { "test1", "bytes", 1, 34, 0, 1, 1, 0, 0, 0, 0, 1, 1 };
+ { "test1", "bytes", 1, 35, 0, 2, 2, 0, 0, 0, 0, 1, 1 };
+ { "test1", "bytes", 1, 36, 0, 3, 3, 0, 0, 0, 0, 1, 1 };
+ { "test1", "bytes", 1, 37, 0, 4, 4, 0, 0, 0, 0, 1, 1 };
+ { "test1", "bytes", 1, 38, 0, 1, 1, 0, 4, 4, 0, 1, 1 };
}
end)
@@ -986,7 +998,7 @@ describe('lua: nvim_buf_attach on_bytes', function()
it("virtual edit", function ()
local check_events = setup_eventcheck(verify, { "", " " })
- meths.set_option("virtualedit", "all")
+ meths.set_option_value('virtualedit', "all", {})
feed [[<Right><Right>iab<ESC>]]
@@ -1152,12 +1164,36 @@ describe('lua: nvim_buf_attach on_bytes', function()
end)
it("works with accepting spell suggestions", function()
- local check_events = setup_eventcheck(verify, {"hallo"})
+ local check_events = setup_eventcheck(verify, {"hallo world", "hallo world"})
feed("gg0z=4<cr><cr>") -- accepts 'Hello'
check_events {
{ "test1", "bytes", 1, 3, 0, 0, 0, 0, 2, 2, 0, 2, 2 };
}
+
+ command("spellrepall") -- replaces whole words
+ check_events {
+ { "test1", "bytes", 1, 4, 1, 0, 12, 0, 5, 5, 0, 5, 5 };
+ }
+ end)
+
+ it('works with :diffput and :diffget', function()
+ local check_events = setup_eventcheck(verify, {"AAA"})
+ command('diffthis')
+ command('new')
+ command('diffthis')
+ meths.buf_set_lines(0, 0, -1, true, {"AAA", "BBB"})
+ feed('G')
+ command('diffput')
+ check_events {
+ { "test1", "bytes", 1, 3, 1, 0, 4, 0, 0, 0, 1, 0, 4 };
+ }
+ meths.buf_set_lines(0, 0, -1, true, {"AAA", "CCC"})
+ feed('<C-w>pG')
+ command('diffget')
+ check_events {
+ { "test1", "bytes", 1, 4, 1, 0, 4, 1, 0, 4, 1, 0, 4 };
+ }
end)
local function test_lockmarks(mode)
diff --git a/test/functional/lua/command_line_completion_spec.lua b/test/functional/lua/command_line_completion_spec.lua
index 3a5966755e..177e077f4a 100644
--- a/test/functional/lua/command_line_completion_spec.lua
+++ b/test/functional/lua/command_line_completion_spec.lua
@@ -5,7 +5,7 @@ local eq = helpers.eq
local exec_lua = helpers.exec_lua
local get_completions = function(input, env)
- return exec_lua("return {vim._expand_pat(...)}", '^' .. input, env)
+ return exec_lua("return {vim._expand_pat(...)}", input, env)
end
local get_compl_parts = function(parts)
@@ -31,14 +31,12 @@ describe('nlua_expand_pat', function()
eq(
{{
'nvim_buf_set_lines',
- 'nvim_buf_set_option'
}, 8
},
get_completions('vim.api.nvim_buf_', {
vim = {
api = {
nvim_buf_set_lines = true,
- nvim_buf_set_option = true,
nvim_win_doesnt_match = true,
},
other_key = true,
@@ -68,14 +66,12 @@ describe('nlua_expand_pat', function()
eq(
{{
'nvim_buf_set_lines',
- 'nvim_buf_set_option'
}, 11
},
get_completions('vim["api"].nvim_buf_', {
vim = {
api = {
nvim_buf_set_lines = true,
- nvim_buf_set_option = true,
nvim_win_doesnt_match = true,
},
other_key = true,
@@ -88,7 +84,6 @@ describe('nlua_expand_pat', function()
eq(
{{
'nvim_buf_set_lines',
- 'nvim_buf_set_option'
}, 21
},
get_completions('vim["nested"]["api"].nvim_buf_', {
@@ -96,7 +91,6 @@ describe('nlua_expand_pat', function()
nested = {
api = {
nvim_buf_set_lines = true,
- nvim_buf_set_option = true,
nvim_win_doesnt_match = true,
},
},
@@ -107,9 +101,12 @@ describe('nlua_expand_pat', function()
end)
it('should work with lazy submodules of "vim" global', function()
- eq({{ 'inspect' }, 4 },
+ eq({{ 'inspect', 'inspect_pos' }, 4 },
get_completions('vim.inspec'))
+ eq({{ 'treesitter' }, 4 },
+ get_completions('vim.treesi'))
+
eq({{ 'set' }, 11 },
get_completions('vim.keymap.se'))
end)
@@ -118,7 +115,6 @@ describe('nlua_expand_pat', function()
eq(
{{
'nvim_buf_set_lines',
- 'nvim_buf_set_option'
}, 12
},
get_completions('vim[MY_VAR].nvim_buf_', {
@@ -126,7 +122,6 @@ describe('nlua_expand_pat', function()
vim = {
api = {
nvim_buf_set_lines = true,
- nvim_buf_set_option = true,
nvim_win_doesnt_match = true,
},
other_key = true,
diff --git a/test/functional/lua/commands_spec.lua b/test/functional/lua/commands_spec.lua
index b8346df290..fca619348d 100644
--- a/test/functional/lua/commands_spec.lua
+++ b/test/functional/lua/commands_spec.lua
@@ -7,7 +7,10 @@ local NIL = helpers.NIL
local eval = helpers.eval
local feed = helpers.feed
local clear = helpers.clear
+local matches = helpers.matches
local meths = helpers.meths
+local exec_lua = helpers.exec_lua
+local exec_capture = helpers.exec_capture
local funcs = helpers.funcs
local source = helpers.source
local dedent = helpers.dedent
@@ -15,7 +18,6 @@ local command = helpers.command
local exc_exec = helpers.exc_exec
local pcall_err = helpers.pcall_err
local write_file = helpers.write_file
-local exec_capture = helpers.exec_capture
local curbufmeths = helpers.curbufmeths
local remove_trace = helpers.remove_trace
@@ -26,22 +28,27 @@ describe(':lua command', function()
eq('', exec_capture(
'lua vim.api.nvim_buf_set_lines(1, 1, 2, false, {"TEST"})'))
eq({'', 'TEST'}, curbufmeths.get_lines(0, 100, false))
- source(dedent([[
+ source([[
lua << EOF
vim.api.nvim_buf_set_lines(1, 1, 2, false, {"TSET"})
- EOF]]))
+ EOF]])
eq({'', 'TSET'}, curbufmeths.get_lines(0, 100, false))
- source(dedent([[
+ source([[
lua << EOF
- vim.api.nvim_buf_set_lines(1, 1, 2, false, {"SETT"})]]))
+ vim.api.nvim_buf_set_lines(1, 1, 2, false, {"SETT"})]])
eq({'', 'SETT'}, curbufmeths.get_lines(0, 100, false))
- source(dedent([[
+ source([[
lua << EOF
vim.api.nvim_buf_set_lines(1, 1, 2, false, {"ETTS"})
vim.api.nvim_buf_set_lines(1, 2, 3, false, {"TTSE"})
vim.api.nvim_buf_set_lines(1, 3, 4, false, {"STTE"})
- EOF]]))
+ EOF]])
eq({'', 'ETTS', 'TTSE', 'STTE'}, curbufmeths.get_lines(0, 100, false))
+ matches('.*Vim%(lua%):E15: Invalid expression: .*', pcall_err(source, [[
+ lua << eval EOF
+ {}
+ EOF
+ ]]))
end)
it('throws catchable errors', function()
eq([[Vim(lua):E5107: Error loading lua [string ":lua"]:0: unexpected symbol near ')']],
@@ -142,22 +149,30 @@ describe(':lua command', function()
]]}
end)
- it('Can print results of =expr', function()
- helpers.exec_lua("x = 5")
- eq("5", helpers.exec_capture(':lua =x'))
- helpers.exec_lua("function x() return 'hello' end")
- eq([["hello"]], helpers.exec_capture(':lua = x()'))
- helpers.exec_lua("x = {a = 1, b = 2}")
- eq("{\n a = 1,\n b = 2\n}", helpers.exec_capture(':lua =x'))
- helpers.exec_lua([[function x(success)
+ it('prints result of =expr', function()
+ exec_lua("x = 5")
+ eq("5", exec_capture(':lua =x'))
+ eq("5", exec_capture('=x'))
+ exec_lua("function x() return 'hello' end")
+ eq('hello', exec_capture(':lua = x()'))
+ exec_lua("x = {a = 1, b = 2}")
+ eq("{\n a = 1,\n b = 2\n}", exec_capture(':lua =x'))
+ exec_lua([[function x(success)
if success then
return true, "Return value"
else
return false, nil, "Error message"
end
end]])
- eq([[true "Return value"]], helpers.exec_capture(':lua =x(true)'))
- eq([[false nil "Error message"]], helpers.exec_capture(':lua =x(false)'))
+ eq(dedent[[
+ true
+ Return value]],
+ exec_capture(':lua =x(true)'))
+ eq(dedent[[
+ false
+ nil
+ Error message]],
+ exec_capture('=x(false)'))
end)
end)
@@ -183,7 +198,7 @@ describe(':luado command', function()
end)
it('works correctly when changing lines out of range', function()
curbufmeths.set_lines(0, 1, false, {"ABC", "def", "gHi"})
- eq('Vim(luado):E322: line number out of range: 1 past the end',
+ eq('Vim(luado):E322: Line number out of range: 1 past the end',
pcall_err(command, '2,$luado vim.api.nvim_command("%d") return linenr'))
eq({''}, curbufmeths.get_lines(0, -1, false))
end)
@@ -199,7 +214,7 @@ describe(':luado command', function()
end)
it('fails in sandbox when needed', function()
curbufmeths.set_lines(0, 1, false, {"ABC", "def", "gHi"})
- eq('Vim(luado):E48: Not allowed in sandbox',
+ eq('Vim(luado):E48: Not allowed in sandbox: sandbox luado runs = (runs or 0) + 1',
pcall_err(command, 'sandbox luado runs = (runs or 0) + 1'))
eq(NIL, funcs.luaeval('runs'))
end)
diff --git a/test/functional/lua/diagnostic_spec.lua b/test/functional/lua/diagnostic_spec.lua
index d364986ad7..f061fac50a 100644
--- a/test/functional/lua/diagnostic_spec.lua
+++ b/test/functional/lua/diagnostic_spec.lua
@@ -79,13 +79,10 @@ describe('vim.diagnostic', function()
]])
end)
- after_each(function()
- clear()
- end)
-
it('creates highlight groups', function()
command('runtime plugin/diagnostic.vim')
eq({
+ 'DiagnosticDeprecated',
'DiagnosticError',
'DiagnosticFloatingError',
'DiagnosticFloatingHint',
@@ -105,6 +102,7 @@ describe('vim.diagnostic', function()
'DiagnosticUnderlineInfo',
'DiagnosticUnderlineOk',
'DiagnosticUnderlineWarn',
+ 'DiagnosticUnnecessary',
'DiagnosticVirtualTextError',
'DiagnosticVirtualTextHint',
'DiagnosticVirtualTextInfo',
@@ -183,6 +181,18 @@ describe('vim.diagnostic', function()
eq(0, #diagnostics)
end)
+ it('always returns a copy of diagnostic tables', function()
+ local result = exec_lua [[
+ vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, {
+ make_error('Diagnostic #1', 1, 1, 1, 1),
+ })
+ local diag = vim.diagnostic.get()
+ diag[1].col = 10000
+ return vim.diagnostic.get()[1].col == 10000
+ ]]
+ eq(result, false)
+ end)
+
it('resolves buffer number 0 to the current buffer', function()
eq(2, exec_lua [[
vim.api.nvim_set_current_buf(diagnostic_bufnr)
@@ -862,7 +872,7 @@ end)
]])
end)
- it('returns only requested diagnostics when severity is supplied', function()
+ it('returns only requested diagnostics when severity range is supplied', function()
eq({2, 3, 2}, exec_lua [[
vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, {
make_error("Error 1", 1, 1, 1, 5),
@@ -884,6 +894,28 @@ end)
]])
end)
+ it('returns only requested diagnostics when severities are supplied', function()
+ eq({1, 1, 2}, exec_lua [[
+ vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, {
+ make_error("Error 1", 1, 1, 1, 5),
+ make_warning("Warning on Server 1", 1, 1, 2, 3),
+ make_info("Ignored information", 1, 1, 2, 3),
+ make_hint("Here's a hint", 1, 1, 2, 3),
+ })
+
+ return {
+ #vim.diagnostic.get(diagnostic_bufnr, { severity = {vim.diagnostic.severity.WARN} }),
+ #vim.diagnostic.get(diagnostic_bufnr, { severity = {vim.diagnostic.severity.ERROR} }),
+ #vim.diagnostic.get(diagnostic_bufnr, {
+ severity = {
+ vim.diagnostic.severity.INFO,
+ vim.diagnostic.severity.WARN,
+ }
+ }),
+ }
+ ]])
+ end)
+
it('allows filtering by line', function()
eq(1, exec_lua [[
vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, {
@@ -1044,7 +1076,7 @@ end)
local virt_text = get_virt_text_extmarks(diagnostic_ns)[1][4].virt_text
local virt_texts = {}
- for i = 2, #virt_text do
+ for i = 2, #virt_text - 1 do
table.insert(virt_texts, (string.gsub(virt_text[i][2], "DiagnosticVirtualText", "")))
end
@@ -1088,7 +1120,7 @@ end)
})
local extmarks = get_virt_text_extmarks(diagnostic_ns)
- local virt_text = extmarks[1][4].virt_text[2][1]
+ local virt_text = extmarks[1][4].virt_text[3][1]
return virt_text
]]
eq(' source x: Some error', result)
@@ -1103,7 +1135,7 @@ end)
}, diagnostic_ns)
local extmarks = get_virt_text_extmarks(diagnostic_ns)
- local virt_text = extmarks[1][4].virt_text[2][1]
+ local virt_text = extmarks[1][4].virt_text[3][1]
return virt_text
]]
eq(' Some error', result)
@@ -1123,7 +1155,7 @@ end)
})
local extmarks = get_virt_text_extmarks(diagnostic_ns)
- local virt_text = {extmarks[1][4].virt_text[2][1], extmarks[2][4].virt_text[2][1]}
+ local virt_text = {extmarks[1][4].virt_text[3][1], extmarks[2][4].virt_text[3][1]}
return virt_text
]]
eq(' source x: Some error', result[1])
@@ -1153,8 +1185,8 @@ end)
local extmarks = get_virt_text_extmarks(diagnostic_ns)
return {extmarks[1][4].virt_text, extmarks[2][4].virt_text}
]]
- eq(" 👀 Warning", result[1][2][1])
- eq(" 🔥 Error", result[2][2][1])
+ eq(" 👀 Warning", result[1][3][1])
+ eq(" 🔥 Error", result[2][3][1])
end)
it('includes source for formatted diagnostics', function()
@@ -1181,8 +1213,48 @@ end)
local extmarks = get_virt_text_extmarks(diagnostic_ns)
return {extmarks[1][4].virt_text, extmarks[2][4].virt_text}
]]
- eq(" some_linter: 👀 Warning", result[1][2][1])
- eq(" another_linter: 🔥 Error", result[2][2][1])
+ eq(" some_linter: 👀 Warning", result[1][3][1])
+ eq(" another_linter: 🔥 Error", result[2][3][1])
+ end)
+
+ it('can add a prefix to virtual text', function()
+ eq('E Some error', exec_lua [[
+ local diagnostics = {
+ make_error('Some error', 0, 0, 0, 0),
+ }
+
+ vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics, {
+ underline = false,
+ virtual_text = {
+ prefix = 'E',
+ suffix = '',
+ }
+ })
+
+ local extmarks = get_virt_text_extmarks(diagnostic_ns)
+ local prefix = extmarks[1][4].virt_text[2][1]
+ local message = extmarks[1][4].virt_text[3][1]
+ return prefix .. message
+ ]])
+
+ eq('[(1/1) err-code] Some error', exec_lua [[
+ local diagnostics = {
+ make_error('Some error', 0, 0, 0, 0, nil, 'err-code'),
+ }
+
+ vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics, {
+ underline = false,
+ virtual_text = {
+ prefix = function(diag, i, total) return string.format('[(%d/%d) %s]', i, total, diag.code) end,
+ suffix = '',
+ }
+ })
+
+ local extmarks = get_virt_text_extmarks(diagnostic_ns)
+ local prefix = extmarks[1][4].virt_text[2][1]
+ local message = extmarks[1][4].virt_text[3][1]
+ return prefix .. message
+ ]])
end)
it('can add a suffix to virtual text', function()
@@ -1200,7 +1272,7 @@ end)
})
local extmarks = get_virt_text_extmarks(diagnostic_ns)
- local virt_text = extmarks[1][4].virt_text[2][1]
+ local virt_text = extmarks[1][4].virt_text[3][1]
return virt_text
]])
@@ -1218,7 +1290,7 @@ end)
})
local extmarks = get_virt_text_extmarks(diagnostic_ns)
- local virt_text = extmarks[1][4].virt_text[2][1]
+ local virt_text = extmarks[1][4].virt_text[3][1]
return virt_text
]])
end)
diff --git a/test/functional/lua/ffi_spec.lua b/test/functional/lua/ffi_spec.lua
index 18b13a8959..3a37b18cd1 100644
--- a/test/functional/lua/ffi_spec.lua
+++ b/test/functional/lua/ffi_spec.lua
@@ -25,7 +25,6 @@ describe('ffi.cdef', function()
local ffi = require('ffi')
ffi.cdef[[
- typedef unsigned char char_u;
typedef struct window_S win_T;
typedef struct {} stl_hlrec_t;
typedef struct {} StlClickRecord;
@@ -63,5 +62,14 @@ describe('ffi.cdef', function()
nil
)
]=])
+
+ -- Check that extern symbols are exported and accessible
+ eq(true, exec_lua[[
+ local ffi = require('ffi')
+
+ ffi.cdef('uint64_t display_tick;')
+
+ return ffi.C.display_tick >= 0
+ ]])
end)
end)
diff --git a/test/functional/lua/filetype_spec.lua b/test/functional/lua/filetype_spec.lua
index 2a7be53164..b3d95e1c7f 100644
--- a/test/functional/lua/filetype_spec.lua
+++ b/test/functional/lua/filetype_spec.lua
@@ -94,11 +94,46 @@ describe('vim.filetype', function()
return vim.filetype.match({ buf = 0 })
]])
end)
+
+ it('works with contents #22180', function()
+ eq('sh', exec_lua [[
+ -- Needs to be set so detect#sh doesn't fail
+ vim.g.ft_ignore_pat = '\\.\\(Z\\|gz\\|bz2\\|zip\\|tgz\\)$'
+ return vim.filetype.match({ contents = { '#!/usr/bin/env bash' } })
+ ]])
+ end)
+
+ it('considers extension mappings when matching from hashbang', function()
+ eq('fooscript', exec_lua [[
+ vim.filetype.add({
+ extension = {
+ foo = 'fooscript',
+ }
+ })
+ return vim.filetype.match({ contents = { '#!/usr/bin/env foo' } })
+ ]])
+ end)
+
+ it('can get default option values for filetypes via vim.filetype.get_option()', function()
+ command('filetype plugin on')
+
+ for ft, opts in pairs {
+ lua = { commentstring = '-- %s' },
+ vim = { commentstring = '"%s' },
+ man = { tagfunc = 'v:lua.require\'man\'.goto_tag' },
+ xml = { formatexpr = 'xmlformat#Format()' }
+ } do
+ for option, value in pairs(opts) do
+ eq(value, exec_lua([[ return vim.filetype.get_option(...) ]], ft, option))
+ end
+ end
+
+ end)
end)
describe('filetype.lua', function()
it('does not override user autocommands that set filetype #20333', function()
clear({args={'--clean', '--cmd', 'autocmd BufRead *.md set filetype=notmarkdown', 'README.md'}})
- eq('notmarkdown', meths.buf_get_option(0, 'filetype'))
+ eq('notmarkdown', meths.get_option_value('filetype', {}))
end)
end)
diff --git a/test/functional/lua/fs_spec.lua b/test/functional/lua/fs_spec.lua
index 642d36f63a..6bdb9ed79d 100644
--- a/test/functional/lua/fs_spec.lua
+++ b/test/functional/lua/fs_spec.lua
@@ -1,5 +1,5 @@
local helpers = require('test.functional.helpers')(after_each)
-local lfs = require('lfs')
+local uv = require('luv')
local clear = helpers.clear
local exec_lua = helpers.exec_lua
@@ -8,8 +8,10 @@ local mkdir_p = helpers.mkdir_p
local rmdir = helpers.rmdir
local nvim_dir = helpers.nvim_dir
local test_build_dir = helpers.test_build_dir
+local test_source_path = helpers.test_source_path
local nvim_prog = helpers.nvim_prog
local is_os = helpers.is_os
+local mkdir = helpers.mkdir
local nvim_prog_basename = is_os('win') and 'nvim.exe' or 'nvim'
@@ -132,10 +134,10 @@ describe('vim.fs', function()
describe('dir()', function()
before_each(function()
- lfs.mkdir('testd')
- lfs.mkdir('testd/a')
- lfs.mkdir('testd/a/b')
- lfs.mkdir('testd/a/b/c')
+ mkdir('testd')
+ mkdir('testd/a')
+ mkdir('testd/a/b')
+ mkdir('testd/a/b/c')
end)
after_each(function()
@@ -222,7 +224,7 @@ describe('vim.fs', function()
describe('find()', function()
it('works', function()
- eq({test_build_dir}, exec_lua([[
+ eq({test_build_dir .. "/build"}, exec_lua([[
local dir = ...
return vim.fs.find('build', { path = dir, upward = true, type = 'directory' })
]], nvim_dir))
@@ -238,7 +240,7 @@ describe('vim.fs', function()
end)
it('accepts predicate as names', function()
- eq({test_build_dir}, exec_lua([[
+ eq({test_build_dir .. "/build"}, exec_lua([[
local dir = ...
local opts = { path = dir, upward = true, type = 'directory' }
return vim.fs.find(function(x) return x == 'build' end, opts)
@@ -252,6 +254,27 @@ describe('vim.fs', function()
local opts = { path = dir, upward = true, type = 'directory' }
return vim.fs.find(function(x) return x == 'no-match' end, opts)
]], nvim_dir))
+ eq(
+ exec_lua([[
+ local dir = ...
+ return vim.tbl_map(vim.fs.basename, vim.fn.glob(dir..'/contrib/*', false, true))
+ ]], test_source_path),
+ exec_lua([[
+ local dir = ...
+ local opts = { path = dir .. "/contrib", limit = math.huge }
+ return vim.tbl_map(vim.fs.basename, vim.fs.find(function(_, d) return d:match('[\\/]contrib$') end, opts))
+ ]], test_source_path))
+ end)
+ end)
+
+ describe('joinpath()', function()
+ it('works', function()
+ eq('foo/bar/baz', exec_lua([[
+ return vim.fs.joinpath('foo', 'bar', 'baz')
+ ]], nvim_dir))
+ eq('foo/bar/baz', exec_lua([[
+ return vim.fs.joinpath('foo', '/bar/', '/baz')
+ ]], nvim_dir))
end)
end)
@@ -259,11 +282,19 @@ describe('vim.fs', function()
it('works with backward slashes', function()
eq('C:/Users/jdoe', exec_lua [[ return vim.fs.normalize('C:\\Users\\jdoe') ]])
end)
+ it('removes trailing /', function()
+ eq('/home/user', exec_lua [[ return vim.fs.normalize('/home/user/') ]])
+ end)
+ it('works with /', function()
+ eq('/', exec_lua [[ return vim.fs.normalize('/') ]])
+ end)
it('works with ~', function()
- if is_os('win') then
- pending([[$HOME does not exist on Windows ¯\_(ツ)_/¯]])
- end
- eq(os.getenv('HOME') .. '/src/foo', exec_lua [[ return vim.fs.normalize('~/src/foo') ]])
+ eq(
+ exec_lua([[
+ local home = ...
+ return home .. '/src/foo'
+ ]], vim.fs.normalize(uv.os_homedir())),
+ exec_lua [[ return vim.fs.normalize('~/src/foo') ]])
end)
it('works with environment variables', function()
local xdg_config_home = test_build_dir .. '/.config'
@@ -272,5 +303,10 @@ describe('vim.fs', function()
return vim.fs.normalize('$XDG_CONFIG_HOME/nvim')
]], xdg_config_home))
end)
+ if is_os('win') then
+ it('Last slash is not truncated from root drive', function()
+ eq('C:/', exec_lua [[ return vim.fs.normalize('C:/') ]])
+ end)
+ end
end)
end)
diff --git a/test/functional/lua/help_spec.lua b/test/functional/lua/help_spec.lua
index b396e2ba30..ef1b8ebf3f 100644
--- a/test/functional/lua/help_spec.lua
+++ b/test/functional/lua/help_spec.lua
@@ -8,6 +8,8 @@ local exec_lua = helpers.exec_lua
local eq = helpers.eq
local ok = helpers.ok
+if helpers.skip(helpers.is_ci('cirrus'), 'No need to run this on Cirrus') then return end
+
describe(':help docs', function()
before_each(clear)
it('validate', function()
@@ -19,11 +21,12 @@ describe(':help docs', function()
local rv = exec_lua([[return require('scripts.gen_help_html').validate('./build/runtime/doc')]])
-- Check that we actually found helpfiles.
ok(rv.helpfiles > 100, '>100 :help files', rv.helpfiles)
+
+ eq({}, rv.parse_errors, 'no parse errors')
+ eq(0, rv.err_count, 'no parse errors')
eq({}, rv.invalid_links, 'invalid tags in :help docs')
eq({}, rv.invalid_urls, 'invalid URLs in :help docs')
- -- Check that parse errors did not increase wildly.
- -- TODO: Fix all parse errors in :help files.
- ok(rv.err_count < 250, '<250 parse errors', rv.err_count)
+ eq({}, rv.invalid_spelling, 'invalid spelling in :help docs (see spell_dict in scripts/gen_help_html.lua)')
end)
it('gen_help_html.lua generates HTML', function()
diff --git a/test/functional/lua/highlight_spec.lua b/test/functional/lua/highlight_spec.lua
index 60d0ed5017..8e499f1e79 100644
--- a/test/functional/lua/highlight_spec.lua
+++ b/test/functional/lua/highlight_spec.lua
@@ -24,7 +24,7 @@ describe('vim.highlight.on_yank', function()
it('does not close timer twice', function()
exec_lua([[
vim.highlight.on_yank({timeout = 10, on_macro = true, event = {operator = "y"}})
- vim.loop.sleep(10)
+ vim.uv.sleep(10)
vim.schedule(function()
vim.highlight.on_yank({timeout = 0, on_macro = true, event = {operator = "y"}})
end)
diff --git a/test/functional/lua/inspector_spec.lua b/test/functional/lua/inspector_spec.lua
index 5e488bb082..c369956e56 100644
--- a/test/functional/lua/inspector_spec.lua
+++ b/test/functional/lua/inspector_spec.lua
@@ -12,19 +12,62 @@ describe('vim.inspect_pos', function()
it('it returns items', function()
local ret = exec_lua([[
local buf = vim.api.nvim_create_buf(true, false)
+ local buf1 = vim.api.nvim_create_buf(true, false)
+ local ns1 = vim.api.nvim_create_namespace("ns1")
+ local ns2 = vim.api.nvim_create_namespace("")
vim.api.nvim_set_current_buf(buf)
vim.api.nvim_buf_set_lines(0, 0, -1, false, {"local a = 123"})
- vim.api.nvim_buf_set_option(buf, "filetype", "lua")
+ vim.api.nvim_buf_set_lines(buf1, 0, -1, false, {"--commentline"})
+ vim.bo[buf].filetype = 'lua'
+ vim.bo[buf1].filetype = 'lua'
+ vim.api.nvim_buf_set_extmark(buf, ns1, 0, 10, { hl_group = "Normal" })
+ vim.api.nvim_buf_set_extmark(buf, ns2, 0, 10, { hl_group = "Normal" })
vim.cmd("syntax on")
- return {buf, vim.inspect_pos(0, 0, 10)}
+ return {buf, vim.inspect_pos(0, 0, 10), vim.inspect_pos(buf1, 0, 10).syntax }
]])
- local buf, items = unpack(ret)
+ local buf, items, other_buf_syntax = unpack(ret)
+
eq('', eval('v:errmsg'))
eq({
buffer = buf,
col = 10,
row = 0,
- extmarks = {},
+ extmarks = {
+ {
+ col = 10,
+ end_col = 11,
+ end_row = 0,
+ id = 1,
+ ns = 'ns1',
+ ns_id = 1,
+ opts = {
+ hl_eol = false,
+ hl_group = 'Normal',
+ hl_group_link = 'Normal',
+ ns_id = 1,
+ priority = 4096,
+ right_gravity = true
+ },
+ row = 0
+ },
+ {
+ col = 10,
+ end_col = 11,
+ end_row = 0,
+ id = 1,
+ ns = '',
+ ns_id = 2,
+ opts = {
+ hl_eol = false,
+ hl_group = 'Normal',
+ hl_group_link = 'Normal',
+ ns_id = 2,
+ priority = 4096,
+ right_gravity = true
+ },
+ row = 0
+ }
+ },
treesitter = {},
semantic_tokens = {},
syntax = {
@@ -34,6 +77,13 @@ describe('vim.inspect_pos', function()
},
},
}, items)
+
+ eq({
+ {
+ hl_group = 'luaComment',
+ hl_group_link = 'Comment',
+ },
+ }, other_buf_syntax)
end)
end)
@@ -47,7 +97,7 @@ describe('vim.show_pos', function()
local buf = vim.api.nvim_create_buf(true, false)
vim.api.nvim_set_current_buf(buf)
vim.api.nvim_buf_set_lines(0, 0, -1, false, {"local a = 123"})
- vim.api.nvim_buf_set_option(buf, "filetype", "lua")
+ vim.bo[buf].filetype = 'lua'
vim.cmd("syntax on")
return {buf, vim.show_pos(0, 0, 10)}
]])
diff --git a/test/functional/lua/iter_spec.lua b/test/functional/lua/iter_spec.lua
new file mode 100644
index 0000000000..ffa28e7b11
--- /dev/null
+++ b/test/functional/lua/iter_spec.lua
@@ -0,0 +1,402 @@
+local helpers = require('test.functional.helpers')(after_each)
+local eq = helpers.eq
+local matches = helpers.matches
+local pcall_err = helpers.pcall_err
+
+describe('vim.iter', function()
+ it('new() on iterable class instance', function()
+ local rb = vim.ringbuf(3)
+ rb:push("a")
+ rb:push("b")
+
+ local it = vim.iter(rb)
+ eq({"a", "b"}, it:totable())
+ end)
+
+ it('filter()', function()
+ local function odd(v)
+ return v % 2 ~= 0
+ end
+
+ local t = { 1, 2, 3, 4, 5 }
+ eq({ 1, 3, 5 }, vim.iter(t):filter(odd):totable())
+ eq({ 2, 4 }, vim.iter(t):filter(function(v) return not odd(v) end):totable())
+ eq({}, vim.iter(t):filter(function(v) return v > 5 end):totable())
+
+ do
+ local it = vim.iter(ipairs(t))
+ it:filter(function(i, v) return i > 1 and v < 5 end)
+ it:map(function(_, v) return v * 2 end)
+ eq({ 4, 6, 8 }, it:totable())
+ end
+
+ local it = vim.iter(string.gmatch('the quick brown fox', '%w+'))
+ eq({'the', 'fox'}, it:filter(function(s) return #s <= 3 end):totable())
+ end)
+
+ it('map()', function()
+ local t = { 1, 2, 3, 4, 5 }
+ eq(
+ { 2, 4, 6, 8, 10 },
+ vim
+ .iter(t)
+ :map(function(v)
+ return 2 * v
+ end)
+ :totable()
+ )
+
+ local it = vim.gsplit(
+ [[
+ Line 1
+ Line 2
+ Line 3
+ Line 4
+ ]],
+ '\n'
+ )
+
+ eq(
+ { 'Lion 2', 'Lion 4' },
+ vim
+ .iter(it)
+ :map(function(s)
+ local lnum = s:match('(%d+)')
+ if lnum and tonumber(lnum) % 2 == 0 then
+ return vim.trim(s:gsub('Line', 'Lion'))
+ end
+ end)
+ :totable()
+ )
+ end)
+
+ it('for loops', function()
+ local t = {1, 2, 3, 4, 5}
+ local acc = 0
+ for v in vim.iter(t):map(function(v) return v * 3 end) do
+ acc = acc + v
+ end
+ eq(45, acc)
+ end)
+
+ it('totable()', function()
+ do
+ local it = vim.iter({1, 2, 3}):map(function(v) return v, v*v end)
+ eq({{1, 1}, {2, 4}, {3, 9}}, it:totable())
+ end
+
+ do
+ local it = vim.iter(string.gmatch('1,4,lol,17,blah,2,9,3', '%d+')):map(tonumber)
+ eq({1, 4, 17, 2, 9, 3}, it:totable())
+ end
+ end)
+
+ it('next()', function()
+ local it = vim.iter({1, 2, 3}):map(function(v) return 2 * v end)
+ eq(2, it:next())
+ eq(4, it:next())
+ eq(6, it:next())
+ eq(nil, it:next())
+ end)
+
+ it('rev()', function()
+ eq({3, 2, 1}, vim.iter({1, 2, 3}):rev():totable())
+
+ local it = vim.iter(string.gmatch("abc", "%w"))
+ matches('rev%(%) requires a list%-like table', pcall_err(it.rev, it))
+ end)
+
+ it('skip()', function()
+ do
+ local t = {4, 3, 2, 1}
+ eq(t, vim.iter(t):skip(0):totable())
+ eq({3, 2, 1}, vim.iter(t):skip(1):totable())
+ eq({2, 1}, vim.iter(t):skip(2):totable())
+ eq({1}, vim.iter(t):skip(#t - 1):totable())
+ eq({}, vim.iter(t):skip(#t):totable())
+ eq({}, vim.iter(t):skip(#t + 1):totable())
+ end
+
+ do
+ local function skip(n)
+ return vim.iter(vim.gsplit('a|b|c|d', '|')):skip(n):totable()
+ end
+ eq({'a', 'b', 'c', 'd'}, skip(0))
+ eq({'b', 'c', 'd'}, skip(1))
+ eq({'c', 'd'}, skip(2))
+ eq({'d'}, skip(3))
+ eq({}, skip(4))
+ eq({}, skip(5))
+ end
+ end)
+
+ it('skipback()', function()
+ do
+ local t = {4, 3, 2, 1}
+ eq(t, vim.iter(t):skipback(0):totable())
+ eq({4, 3, 2}, vim.iter(t):skipback(1):totable())
+ eq({4, 3}, vim.iter(t):skipback(2):totable())
+ eq({4}, vim.iter(t):skipback(#t - 1):totable())
+ eq({}, vim.iter(t):skipback(#t):totable())
+ eq({}, vim.iter(t):skipback(#t + 1):totable())
+ end
+
+ local it = vim.iter(vim.gsplit('a|b|c|d', '|'))
+ matches('skipback%(%) requires a list%-like table', pcall_err(it.skipback, it, 0))
+ end)
+
+ it('slice()', function()
+ local t = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
+ eq({3, 4, 5, 6, 7}, vim.iter(t):slice(3, 7):totable())
+ eq({}, vim.iter(t):slice(6, 5):totable())
+ eq({}, vim.iter(t):slice(0, 0):totable())
+ eq({1}, vim.iter(t):slice(1, 1):totable())
+ eq({1, 2}, vim.iter(t):slice(1, 2):totable())
+ eq({10}, vim.iter(t):slice(10, 10):totable())
+ eq({8, 9, 10}, vim.iter(t):slice(8, 11):totable())
+
+ local it = vim.iter(vim.gsplit('a|b|c|d', '|'))
+ matches('slice%(%) requires a list%-like table', pcall_err(it.slice, it, 1, 3))
+ end)
+
+ it('nth()', function()
+ do
+ local t = {4, 3, 2, 1}
+ eq(nil, vim.iter(t):nth(0))
+ eq(4, vim.iter(t):nth(1))
+ eq(3, vim.iter(t):nth(2))
+ eq(2, vim.iter(t):nth(3))
+ eq(1, vim.iter(t):nth(4))
+ eq(nil, vim.iter(t):nth(5))
+ end
+
+ do
+ local function nth(n)
+ return vim.iter(vim.gsplit('a|b|c|d', '|')):nth(n)
+ end
+ eq(nil, nth(0))
+ eq('a', nth(1))
+ eq('b', nth(2))
+ eq('c', nth(3))
+ eq('d', nth(4))
+ eq(nil, nth(5))
+ end
+ end)
+
+ it('nthback()', function()
+ do
+ local t = {4, 3, 2, 1}
+ eq(nil, vim.iter(t):nthback(0))
+ eq(1, vim.iter(t):nthback(1))
+ eq(2, vim.iter(t):nthback(2))
+ eq(3, vim.iter(t):nthback(3))
+ eq(4, vim.iter(t):nthback(4))
+ eq(nil, vim.iter(t):nthback(5))
+ end
+
+ local it = vim.iter(vim.gsplit('a|b|c|d', '|'))
+ matches('skipback%(%) requires a list%-like table', pcall_err(it.nthback, it, 1))
+ end)
+
+ it('any()', function()
+ local function odd(v)
+ return v % 2 ~= 0
+ end
+
+ do
+ local t = { 4, 8, 9, 10 }
+ eq(true, vim.iter(t):any(odd))
+ end
+
+ do
+ local t = { 4, 8, 10 }
+ eq(false, vim.iter(t):any(odd))
+ end
+
+ do
+ eq(true, vim.iter(vim.gsplit('a|b|c|d', '|')):any(function(s) return s == 'd' end))
+ eq(false, vim.iter(vim.gsplit('a|b|c|d', '|')):any(function(s) return s == 'e' end))
+ end
+ end)
+
+ it('all()', function()
+ local function odd(v)
+ return v % 2 ~= 0
+ end
+
+ do
+ local t = { 3, 5, 7, 9 }
+ eq(true, vim.iter(t):all(odd))
+ end
+
+ do
+ local t = { 3, 5, 7, 10 }
+ eq(false, vim.iter(t):all(odd))
+ end
+
+ do
+ eq(true, vim.iter(vim.gsplit('a|a|a|a', '|')):all(function(s) return s == 'a' end))
+ eq(false, vim.iter(vim.gsplit('a|a|a|b', '|')):all(function(s) return s == 'a' end))
+ end
+ end)
+
+ it('last()', function()
+ local s = 'abcdefghijklmnopqrstuvwxyz'
+ eq('z', vim.iter(vim.split(s, '')):last())
+ eq('z', vim.iter(vim.gsplit(s, '')):last())
+ end)
+
+ it('enumerate()', function()
+ local it = vim.iter(vim.gsplit('abc', '')):enumerate()
+ eq({1, 'a'}, {it:next()})
+ eq({2, 'b'}, {it:next()})
+ eq({3, 'c'}, {it:next()})
+ eq({}, {it:next()})
+ end)
+
+ it('peek()', function()
+ do
+ local it = vim.iter({ 3, 6, 9, 12 })
+ eq(3, it:peek())
+ eq(3, it:peek())
+ eq(3, it:next())
+ end
+
+ do
+ local it = vim.iter(vim.gsplit('hi', ''))
+ matches('peek%(%) requires a list%-like table', pcall_err(it.peek, it))
+ end
+ end)
+
+ it('find()', function()
+ local t = {3, 6, 9, 12}
+ eq(12, vim.iter(t):find(12))
+ eq(nil, vim.iter(t):find(15))
+ eq(12, vim.iter(t):find(function(v) return v % 4 == 0 end))
+
+ do
+ local it = vim.iter(t)
+ local pred = function(v) return v % 3 == 0 end
+ eq(3, it:find(pred))
+ eq(6, it:find(pred))
+ eq(9, it:find(pred))
+ eq(12, it:find(pred))
+ eq(nil, it:find(pred))
+ end
+
+ do
+ local it = vim.iter(vim.gsplit('AbCdE', ''))
+ local pred = function(s) return s:match('[A-Z]') end
+ eq('A', it:find(pred))
+ eq('C', it:find(pred))
+ eq('E', it:find(pred))
+ eq(nil, it:find(pred))
+ end
+ end)
+
+ it('rfind()', function()
+ local t = {1, 2, 3, 2, 1}
+ do
+ local it = vim.iter(t)
+ eq(1, it:rfind(1))
+ eq(1, it:rfind(1))
+ eq(nil, it:rfind(1))
+ end
+
+ do
+ local it = vim.iter(t):enumerate()
+ local pred = function(i) return i % 2 ~= 0 end
+ eq({5, 1}, {it:rfind(pred)})
+ eq({3, 3}, {it:rfind(pred)})
+ eq({1, 1}, {it:rfind(pred)})
+ eq(nil, it:rfind(pred))
+ end
+
+ do
+ local it = vim.iter(vim.gsplit('AbCdE', ''))
+ matches('rfind%(%) requires a list%-like table', pcall_err(it.rfind, it, 'E'))
+ end
+ end)
+
+ it('nextback()', function()
+ do
+ local it = vim.iter({ 1, 2, 3, 4 })
+ eq(4, it:nextback())
+ eq(3, it:nextback())
+ eq(2, it:nextback())
+ eq(1, it:nextback())
+ eq(nil, it:nextback())
+ eq(nil, it:nextback())
+ end
+
+ do
+ local it = vim.iter(vim.gsplit('hi', ''))
+ matches('nextback%(%) requires a list%-like table', pcall_err(it.nextback, it))
+ end
+ end)
+
+ it('peekback()', function()
+ do
+ local it = vim.iter({ 1, 2, 3, 4 })
+ eq(4, it:peekback())
+ eq(4, it:peekback())
+ eq(4, it:nextback())
+ end
+
+ do
+ local it = vim.iter(vim.gsplit('hi', ''))
+ matches('peekback%(%) requires a list%-like table', pcall_err(it.peekback, it))
+ end
+ end)
+
+ it('fold()', function()
+ local t = {1, 2, 3, 4, 5}
+ eq(115, vim.iter(t):fold(100, function(acc, v) return acc + v end))
+ eq({5, 4, 3, 2, 1}, vim.iter(t):fold({}, function(acc, v)
+ table.insert(acc, 1, v)
+ return acc
+ end))
+ end)
+
+ it('handles map-like tables', function()
+ local it = vim.iter({ a = 1, b = 2, c = 3 }):map(function(k, v)
+ if v % 2 ~= 0 then
+ return k:upper(), v * 2
+ end
+ end)
+
+ local t = it:fold({}, function(t, k, v)
+ t[k] = v
+ return t
+ end)
+ eq({ A = 2, C = 6 }, t)
+ end)
+
+ it('handles table values mid-pipeline', function()
+ local map = {
+ item = {
+ file = 'test',
+ },
+ item_2 = {
+ file = 'test',
+ },
+ item_3 = {
+ file = 'test',
+ },
+ }
+
+ local output = vim.iter(map):map(function(key, value)
+ return { [key] = value.file }
+ end):totable()
+
+ table.sort(output, function(a, b)
+ return next(a) < next(b)
+ end)
+
+ eq({
+ { item = 'test' },
+ { item_2 = 'test' },
+ { item_3 = 'test' },
+ }, output)
+ end)
+end)
diff --git a/test/functional/lua/json_spec.lua b/test/functional/lua/json_spec.lua
index fbb21bfd57..25fdb48eea 100644
--- a/test/functional/lua/json_spec.lua
+++ b/test/functional/lua/json_spec.lua
@@ -1,20 +1,57 @@
local helpers = require('test.functional.helpers')(after_each)
local clear = helpers.clear
-local NIL = helpers.NIL
local exec_lua = helpers.exec_lua
local eq = helpers.eq
+local pcall_err = helpers.pcall_err
-describe('vim.json.decode function', function()
+describe('vim.json.decode()', function()
before_each(function()
clear()
end)
it('parses null, true, false', function()
- eq(NIL, exec_lua([[return vim.json.decode('null')]]))
+ eq(vim.NIL, exec_lua([[return vim.json.decode('null')]]))
eq(true, exec_lua([[return vim.json.decode('true')]]))
eq(false, exec_lua([[return vim.json.decode('false')]]))
end)
+ it('validation', function()
+ eq('Expected object key string but found invalid token at character 2',
+ pcall_err(exec_lua, [[return vim.json.decode('{a:"b"}')]]))
+ end)
+
+ it('options', function()
+ local jsonstr = '{"arr":[1,2,null],"bar":[3,7],"foo":{"a":"b"},"baz":null}'
+ eq({
+ arr = { 1, 2, vim.NIL },
+ bar = { 3, 7 },
+ baz = vim.NIL,
+ foo = { a = 'b' },
+ },
+ exec_lua([[return vim.json.decode(..., {})]], jsonstr))
+ eq({
+ arr = { 1, 2, vim.NIL },
+ bar = { 3, 7 },
+ -- baz = nil,
+ foo = { a = 'b' },
+ },
+ exec_lua([[return vim.json.decode(..., { luanil = { object = true } })]], jsonstr))
+ eq({
+ arr = { 1, 2 },
+ bar = { 3, 7 },
+ baz = vim.NIL,
+ foo = { a = 'b' },
+ },
+ exec_lua([[return vim.json.decode(..., { luanil = { array = true } })]], jsonstr))
+ eq({
+ arr = { 1, 2 },
+ bar = { 3, 7 },
+ -- baz = nil,
+ foo = { a = 'b' },
+ },
+ exec_lua([[return vim.json.decode(..., { luanil = { array = true, object = true } })]], jsonstr))
+ end)
+
it('parses integer numbers', function()
eq(100000, exec_lua([[return vim.json.decode('100000')]]))
eq(-100000, exec_lua([[return vim.json.decode('-100000')]]))
@@ -60,7 +97,7 @@ describe('vim.json.decode function', function()
it('parses containers', function()
eq({1}, exec_lua([[return vim.json.decode('[1]')]]))
- eq({NIL, 1}, exec_lua([[return vim.json.decode('[null, 1]')]]))
+ eq({vim.NIL, 1}, exec_lua([[return vim.json.decode('[null, 1]')]]))
eq({['1']=2}, exec_lua([[return vim.json.decode('{"1": 2}')]]))
eq({['1']=2, ['3']={{['4']={['5']={{}, 1}}}}},
exec_lua([[return vim.json.decode('{"1": 2, "3": [{"4": {"5": [ [], 1]}}]}')]]))
@@ -88,43 +125,43 @@ describe('vim.json.decode function', function()
end)
-describe('vim.json.encode function', function()
+describe('vim.json.encode()', function()
before_each(function()
clear()
end)
- it('dumps strings', function()
- eq('"Test"', exec_lua([[return vim.json.encode('Test')]]))
- eq('""', exec_lua([[return vim.json.encode('')]]))
- eq('"\\t"', exec_lua([[return vim.json.encode('\t')]]))
- eq('"\\n"', exec_lua([[return vim.json.encode('\n')]]))
- -- vim.fn.json_encode return \\u001B
- eq('"\\u001b"', exec_lua([[return vim.json.encode('\27')]]))
- eq('"þÿþ"', exec_lua([[return vim.json.encode('þÿþ')]]))
- end)
-
- it('dumps numbers', function()
- eq('0', exec_lua([[return vim.json.encode(0)]]))
- eq('10', exec_lua([[return vim.json.encode(10)]]))
- eq('-10', exec_lua([[return vim.json.encode(-10)]]))
- end)
-
- it('dumps floats', function()
- eq('10.5', exec_lua([[return vim.json.encode(10.5)]]))
- eq('-10.5', exec_lua([[return vim.json.encode(-10.5)]]))
- eq('-1e-05', exec_lua([[return vim.json.encode(-1e-5)]]))
- end)
-
- it('dumps lists', function()
- eq('[]', exec_lua([[return vim.json.encode({})]]))
- eq('[[]]', exec_lua([[return vim.json.encode({{}})]]))
- eq('[[],[]]', exec_lua([[return vim.json.encode({{}, {}})]]))
- end)
-
- it('dumps dictionaries', function()
- eq('{}', exec_lua([[return vim.json.encode(vim.empty_dict())]]))
- eq('{"d":[]}', exec_lua([[return vim.json.encode({d={}})]]))
- end)
+ it('dumps strings', function()
+ eq('"Test"', exec_lua([[return vim.json.encode('Test')]]))
+ eq('""', exec_lua([[return vim.json.encode('')]]))
+ eq('"\\t"', exec_lua([[return vim.json.encode('\t')]]))
+ eq('"\\n"', exec_lua([[return vim.json.encode('\n')]]))
+ -- vim.fn.json_encode return \\u001B
+ eq('"\\u001b"', exec_lua([[return vim.json.encode('\27')]]))
+ eq('"þÿþ"', exec_lua([[return vim.json.encode('þÿþ')]]))
+ end)
+
+ it('dumps numbers', function()
+ eq('0', exec_lua([[return vim.json.encode(0)]]))
+ eq('10', exec_lua([[return vim.json.encode(10)]]))
+ eq('-10', exec_lua([[return vim.json.encode(-10)]]))
+ end)
+
+ it('dumps floats', function()
+ eq('10.5', exec_lua([[return vim.json.encode(10.5)]]))
+ eq('-10.5', exec_lua([[return vim.json.encode(-10.5)]]))
+ eq('-1e-05', exec_lua([[return vim.json.encode(-1e-5)]]))
+ end)
+
+ it('dumps lists', function()
+ eq('[]', exec_lua([[return vim.json.encode({})]]))
+ eq('[[]]', exec_lua([[return vim.json.encode({{}})]]))
+ eq('[[],[]]', exec_lua([[return vim.json.encode({{}, {}})]]))
+ end)
+
+ it('dumps dictionaries', function()
+ eq('{}', exec_lua([[return vim.json.encode(vim.empty_dict())]]))
+ eq('{"d":[]}', exec_lua([[return vim.json.encode({d={}})]]))
+ end)
it('dumps vim.NIL', function()
eq('null', exec_lua([[return vim.json.encode(vim.NIL)]]))
diff --git a/test/functional/lua/loader_spec.lua b/test/functional/lua/loader_spec.lua
new file mode 100644
index 0000000000..34c36b04ef
--- /dev/null
+++ b/test/functional/lua/loader_spec.lua
@@ -0,0 +1,56 @@
+-- Test suite for testing interactions with API bindings
+local helpers = require('test.functional.helpers')(after_each)
+
+local exec_lua = helpers.exec_lua
+local command = helpers.command
+local eq = helpers.eq
+
+describe('vim.loader', function()
+ before_each(helpers.clear)
+
+ it('handles changing files (#23027)', function()
+ exec_lua[[
+ vim.loader.enable()
+ ]]
+
+ local tmp = helpers.tmpname()
+ command('edit ' .. tmp)
+
+ eq(1, exec_lua([[
+ vim.api.nvim_buf_set_lines(0, 0, -1, true, {'_G.TEST=1'})
+ vim.cmd.write()
+ loadfile(...)()
+ return _G.TEST
+ ]], tmp))
+
+ -- fs latency
+ helpers.sleep(10)
+
+ eq(2, exec_lua([[
+ vim.api.nvim_buf_set_lines(0, 0, -1, true, {'_G.TEST=2'})
+ vim.cmd.write()
+ loadfile(...)()
+ return _G.TEST
+ ]], tmp))
+ end)
+
+ it('handles % signs in modpath (#24491)', function()
+ exec_lua[[
+ vim.loader.enable()
+ ]]
+
+ local tmp1, tmp2 = (function (t)
+ assert(os.remove(t))
+ assert(helpers.mkdir(t))
+ assert(helpers.mkdir(t .. '/%'))
+ return t .. '/%/x', t .. '/%%x'
+ end)(helpers.tmpname())
+
+ helpers.write_file(tmp1, 'return 1', true)
+ helpers.write_file(tmp2, 'return 2', true)
+ vim.uv.fs_utime(tmp1, 0, 0)
+ vim.uv.fs_utime(tmp2, 0, 0)
+ eq(1, exec_lua('return loadfile(...)()', tmp1))
+ eq(2, exec_lua('return loadfile(...)()', tmp2))
+ end)
+end)
diff --git a/test/functional/lua/loop_spec.lua b/test/functional/lua/loop_spec.lua
index 7f3787d7bf..c0924fa0c0 100644
--- a/test/functional/lua/loop_spec.lua
+++ b/test/functional/lua/loop_spec.lua
@@ -14,24 +14,24 @@ local retry = helpers.retry
before_each(clear)
-describe('vim.loop', function()
+describe('vim.uv', function()
it('version', function()
- assert(funcs.luaeval('vim.loop.version()')>=72961, "libuv version too old")
- matches("(%d+)%.(%d+)%.(%d+)", funcs.luaeval('vim.loop.version_string()'))
+ assert(funcs.luaeval('vim.uv.version()')>=72961, "libuv version too old")
+ matches("(%d+)%.(%d+)%.(%d+)", funcs.luaeval('vim.uv.version_string()'))
end)
it('timer', function()
exec_lua('vim.api.nvim_set_var("coroutine_cnt", 0)', {})
local code=[[
- local loop = vim.loop
+ local uv = vim.uv
local touch = 0
local function wait(ms)
local this = coroutine.running()
assert(this)
- local timer = loop.new_timer()
+ local timer = uv.new_timer()
timer:start(ms, 0, vim.schedule_wrap(function ()
timer:close()
touch = touch + 1
@@ -73,7 +73,7 @@ describe('vim.loop', function()
-- deferred API functions are disabled, as their safety can't be guaranteed
exec_lua([[
- local timer = vim.loop.new_timer()
+ local timer = vim.uv.new_timer()
timer:start(20, 0, function ()
_G.is_fast = vim.in_fast_event()
timer:close()
@@ -101,7 +101,7 @@ describe('vim.loop', function()
-- callbacks can be scheduled to be executed in the main event loop
-- where the entire API is available
exec_lua([[
- local timer = vim.loop.new_timer()
+ local timer = vim.uv.new_timer()
timer:start(20, 0, vim.schedule_wrap(function ()
_G.is_fast = vim.in_fast_event()
timer:close()
@@ -127,7 +127,7 @@ describe('vim.loop', function()
-- fast (not deferred) API functions are allowed to be called directly
exec_lua([[
- local timer = vim.loop.new_timer()
+ local timer = vim.uv.new_timer()
timer:start(20, 0, function ()
timer:close()
-- input is queued for processing after the callback returns
@@ -151,6 +151,6 @@ describe('vim.loop', function()
end)
it("is equal to require('luv')", function()
- eq(true, exec_lua("return vim.loop == require('luv')"))
+ eq(true, exec_lua("return vim.uv == require('luv')"))
end)
end)
diff --git a/test/functional/lua/luaeval_spec.lua b/test/functional/lua/luaeval_spec.lua
index 9f313eab9e..dfbd2fb18b 100644
--- a/test/functional/lua/luaeval_spec.lua
+++ b/test/functional/lua/luaeval_spec.lua
@@ -514,7 +514,7 @@ describe('v:lua', function()
[5] = {bold = true, foreground = Screen.colors.SeaGreen4},
})
screen:attach()
- meths.buf_set_option(0, 'omnifunc', 'v:lua.mymod.omni')
+ meths.set_option_value('omnifunc', 'v:lua.mymod.omni', {})
feed('isome st<c-x><c-o>')
screen:expect{grid=[[
some stuff^ |
@@ -526,7 +526,7 @@ describe('v:lua', function()
{1:~ }|
{4:-- Omni completion (^O^N^P) }{5:match 1 of 3} |
]]}
- meths.set_option('operatorfunc', 'v:lua.mymod.noisy')
+ meths.set_option_value('operatorfunc', 'v:lua.mymod.noisy', {})
feed('<Esc>g@g@')
eq("hey line", meths.get_current_line())
end)
@@ -539,13 +539,13 @@ describe('v:lua', function()
end)
it('throw errors for invalid use', function()
- eq('Vim(let):E15: Invalid expression: v:lua.func', pcall_err(command, "let g:Func = v:lua.func"))
- eq('Vim(let):E15: Invalid expression: v:lua', pcall_err(command, "let g:Func = v:lua"))
- eq("Vim(let):E15: Invalid expression: v:['lua']", pcall_err(command, "let g:Func = v:['lua']"))
+ eq([[Vim(let):E15: Invalid expression: "v:lua.func"]], pcall_err(command, "let g:Func = v:lua.func"))
+ eq([[Vim(let):E15: Invalid expression: "v:lua"]], pcall_err(command, "let g:Func = v:lua"))
+ eq([[Vim(let):E15: Invalid expression: "v:['lua']"]], pcall_err(command, "let g:Func = v:['lua']"))
- eq("Vim:E15: Invalid expression: v:['lua'].foo()", pcall_err(eval, "v:['lua'].foo()"))
+ eq([[Vim:E15: Invalid expression: "v:['lua'].foo()"]], pcall_err(eval, "v:['lua'].foo()"))
eq("Vim(call):E461: Illegal variable name: v:['lua']", pcall_err(command, "call v:['lua'].baar()"))
- eq("Vim:E117: Unknown function: v:lua", pcall_err(eval, "v:lua()"))
+ eq("Vim:E1085: Not a callable type: v:lua", pcall_err(eval, "v:lua()"))
eq("Vim(let):E46: Cannot change read-only variable \"v:['lua']\"", pcall_err(command, "let v:['lua'] = 'xx'"))
eq("Vim(let):E46: Cannot change read-only variable \"v:lua\"", pcall_err(command, "let v:lua = 'xx'"))
@@ -553,7 +553,7 @@ describe('v:lua', function()
eq("Vim:E107: Missing parentheses: v:lua.func", pcall_err(eval, "'bad'->v:lua.func"))
eq("Vim:E274: No white space allowed before parenthesis", pcall_err(eval, "'bad'->v:lua.func ()"))
eq("Vim:E107: Missing parentheses: v:lua", pcall_err(eval, "'bad'->v:lua"))
- eq("Vim:E117: Unknown function: v:lua", pcall_err(eval, "'bad'->v:lua()"))
- eq("Vim:E15: Invalid expression: v:lua.()", pcall_err(eval, "'bad'->v:lua.()"))
+ eq("Vim:E1085: Not a callable type: v:lua", pcall_err(eval, "'bad'->v:lua()"))
+ eq([[Vim:E15: Invalid expression: "v:lua.()"]], pcall_err(eval, "'bad'->v:lua.()"))
end)
end)
diff --git a/test/functional/lua/overrides_spec.lua b/test/functional/lua/overrides_spec.lua
index 3f107811ae..c08f3d06a9 100644
--- a/test/functional/lua/overrides_spec.lua
+++ b/test/functional/lua/overrides_spec.lua
@@ -15,8 +15,6 @@ local exec_lua = helpers.exec_lua
local pcall_err = helpers.pcall_err
local is_os = helpers.is_os
-local screen
-
local fname = 'Xtest-functional-lua-overrides-luafile'
before_each(clear)
@@ -56,7 +54,7 @@ describe('print', function()
-- TODO(bfredl): these look weird, print() should not use "E5114:" style errors..
eq('Vim(lua):E5108: Error executing lua E5114: Error while converting print argument #2: [NULL]',
pcall_err(command, 'lua print("foo", v_nilerr, "bar")'))
- eq('Vim(lua):E5108: Error executing lua E5114: Error while converting print argument #2: Xtest-functional-lua-overrides-luafile:0: abc',
+ eq('Vim(lua):E5108: Error executing lua E5114: Error while converting print argument #2: Xtest-functional-lua-overrides-luafile:2: abc',
pcall_err(command, 'lua print("foo", v_abcerr, "bar")'))
eq('Vim(lua):E5108: Error executing lua E5114: Error while converting print argument #2: <Unknown error: lua_tolstring returned NULL for tostring result>',
pcall_err(command, 'lua print("foo", v_tblout, "bar")'))
@@ -86,9 +84,9 @@ describe('print', function()
end
]])
eq('', exec_capture('luafile ' .. fname))
- eq('Vim(lua):E5108: Error executing lua Xtest-functional-lua-overrides-luafile:0: my mistake',
+ eq('Vim(lua):E5108: Error executing lua Xtest-functional-lua-overrides-luafile:1: my mistake',
pcall_err(command, 'lua string_error()'))
- eq('Vim(lua):E5108: Error executing lua Xtest-functional-lua-overrides-luafile:0: 1234',
+ eq('Vim(lua):E5108: Error executing lua Xtest-functional-lua-overrides-luafile:2: 1234',
pcall_err(command, 'lua number_error()'))
eq('Vim(lua):E5108: Error executing lua [NULL]',
pcall_err(command, 'lua nil_error()'))
@@ -100,7 +98,7 @@ describe('print', function()
pcall_err(command, 'lua bad_custom_error()'))
end)
it('prints strings with NULs and NLs correctly', function()
- meths.set_option('more', true)
+ meths.set_option_value('more', true, {})
eq('abc ^@ def\nghi^@^@^@jkl\nTEST\n\n\nT\n',
exec_capture([[lua print("abc \0 def\nghi\0\0\0jkl\nTEST\n\n\nT\n")]]))
eq('abc ^@ def\nghi^@^@^@jkl\nTEST\n\n\nT^@',
@@ -119,7 +117,7 @@ describe('print', function()
exec_lua([[
local cmd = ...
function test()
- local timer = vim.loop.new_timer()
+ local timer = vim.uv.new_timer()
local done = false
timer:start(10, 0, function()
print("very fast")
@@ -130,7 +128,7 @@ describe('print', function()
-- loop until we know for sure the callback has been executed
while not done do
os.execute(cmd)
- vim.loop.run("nowait") -- fake os_breakcheck()
+ vim.uv.run("nowait") -- fake os_breakcheck()
end
print("very slow")
vim.api.nvim_command("sleep 1m") -- force deferred event processing
@@ -138,9 +136,44 @@ describe('print', function()
]], (is_os('win') and "timeout 1") or "sleep 0.1")
eq('very slow\nvery fast', exec_capture('lua test()'))
end)
+
+ 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},
+ [2] = {bold = true, reverse = true},
+ })
+ feed([[:lua print('\na')<CR>]])
+ screen:expect{grid=[[
+ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {2: }|
+ |
+ a |
+ {1:Press ENTER or type command to continue}^ |
+ ]]}
+ feed('<CR>')
+ feed([[:lua print('b\n\nc')<CR>]])
+ screen:expect{grid=[[
+ |
+ {0:~ }|
+ {0:~ }|
+ {2: }|
+ b |
+ |
+ c |
+ {1:Press ENTER or type command to continue}^ |
+ ]]}
+ end)
end)
describe('debug.debug', function()
+ local screen
+
before_each(function()
screen = Screen.new()
screen:attach()
@@ -338,3 +371,11 @@ describe('os.getenv', function()
eq(value, funcs.luaeval('os.getenv("XTEST_1")'))
end)
end)
+
+-- "bit" module is always available, regardless if nvim is built with
+-- luajit or PUC lua 5.1.
+describe('bit module', function()
+ it('works', function()
+ eq (9, exec_lua [[ return require'bit'.band(11,13) ]])
+ end)
+end)
diff --git a/test/functional/lua/runtime_spec.lua b/test/functional/lua/runtime_spec.lua
index b659f2eacb..0b8b2234db 100644
--- a/test/functional/lua/runtime_spec.lua
+++ b/test/functional/lua/runtime_spec.lua
@@ -18,6 +18,11 @@ describe('runtime:', function()
io.open(init, 'w'):close() -- touch init file
clear{args = {'-u', init}}
exec('set rtp+=' .. plug_dir)
+ exec([[
+ set shell=doesnotexist
+ set completeslash=slash
+ set isfname+=(,)
+ ]])
end)
teardown(function()
@@ -35,91 +40,199 @@ describe('runtime:', function()
describe('colors', function()
local colorscheme_folder = plug_dir .. sep .. 'colors'
-
- it('loads lua colorscheme', function()
- local colorscheme_file = colorscheme_folder .. sep .. 'new_colorscheme.lua'
+ before_each(function()
mkdir_p(colorscheme_folder)
+ end)
+
+ it('lua colorschemes work and are included in cmdline completion', function()
+ local colorscheme_file = table.concat({colorscheme_folder, 'new_colorscheme.lua'}, sep)
write_file(colorscheme_file, [[vim.g.lua_colorscheme = 1]])
eq({'new_colorscheme'}, funcs.getcompletion('new_c', 'color'))
+ eq({'colors/new_colorscheme.lua'}, funcs.getcompletion('colors/new_c', 'runtime'))
exec('colorscheme new_colorscheme')
eq(1, eval('g:lua_colorscheme'))
- rmdir(colorscheme_folder)
end)
- it('loads vim colorscheme when both lua and vim version exist', function()
- local colorscheme_file = colorscheme_folder .. sep .. 'new_colorscheme'
- mkdir_p(colorscheme_folder)
- write_file(colorscheme_file..'.vim', [[let g:colorscheme = 'vim']])
- write_file(colorscheme_file..'.lua', [[vim.g.colorscheme = 'lua']])
+ it("'rtp'/'pp' order is respected", function()
+ local pack_dir = 'Test_Pack'
+ mkdir_p(pack_dir)
+ finally(function()
+ rmdir(pack_dir)
+ end)
+ exec('set pp+=' .. pack_dir)
+
+ local pack_opt_dir = table.concat({pack_dir, 'pack', 'some_name', 'opt'}, sep)
+ local colors_opt_dir = table.concat({pack_opt_dir, 'some_pack', 'colors'}, sep)
+ mkdir_p(colors_opt_dir)
+
+ local after_colorscheme_folder = table.concat({plug_dir, 'after', 'colors'}, sep)
+ mkdir_p(after_colorscheme_folder)
+ exec('set rtp+=' .. plug_dir .. '/after')
+
+ write_file(table.concat({colors_opt_dir, 'new_colorscheme.lua'}, sep),
+ [[vim.g.colorscheme = 'lua_pp']])
+ exec('colorscheme new_colorscheme')
+ eq('lua_pp', eval('g:colorscheme'))
+
+ write_file(table.concat({colors_opt_dir, 'new_colorscheme.vim'}, sep),
+ [[let g:colorscheme = 'vim_pp']])
+ exec('colorscheme new_colorscheme')
+ eq('vim_pp', eval('g:colorscheme'))
+ write_file(table.concat({after_colorscheme_folder, 'new_colorscheme.lua'}, sep),
+ [[vim.g.colorscheme = 'lua_rtp_after']])
exec('colorscheme new_colorscheme')
+ eq('lua_rtp_after', eval('g:colorscheme'))
- eq('vim', eval('g:colorscheme'))
- rmdir(colorscheme_folder)
+ write_file(table.concat({after_colorscheme_folder, 'new_colorscheme.vim'}, sep),
+ [[let g:colorscheme = 'vim_rtp_after']])
+ exec('colorscheme new_colorscheme')
+ eq('vim_rtp_after', eval('g:colorscheme'))
+
+ write_file(table.concat({colorscheme_folder, 'new_colorscheme.lua'}, sep),
+ [[vim.g.colorscheme = 'lua_rtp']])
+ exec('colorscheme new_colorscheme')
+ eq('lua_rtp', eval('g:colorscheme'))
+
+ write_file(table.concat({colorscheme_folder, 'new_colorscheme.vim'}, sep),
+ [[let g:colorscheme = 'vim_rtp']])
+ exec('colorscheme new_colorscheme')
+ eq('vim_rtp', eval('g:colorscheme'))
end)
end)
describe('compiler', function()
- local compiler_folder = plug_dir .. sep .. 'compiler'
+ local compiler_folder = table.concat({plug_dir, 'compiler'}, sep)
+ before_each(function()
+ mkdir_p(compiler_folder)
+ end)
- it('loads lua compilers', function()
+ it('lua compilers work and are included in cmdline completion', function()
local compiler_file = compiler_folder .. sep .. 'new_compiler.lua'
- mkdir_p(compiler_folder)
write_file(compiler_file, [[vim.b.lua_compiler = 1]])
eq({'new_compiler'}, funcs.getcompletion('new_c', 'compiler'))
+ eq({'compiler/new_compiler.lua'}, funcs.getcompletion('compiler/new_c', 'runtime'))
exec('compiler new_compiler')
eq(1, eval('b:lua_compiler'))
- rmdir(compiler_folder)
end)
- it('loads vim compilers when both lua and vim version exist', function()
- local compiler_file = compiler_folder .. sep .. 'new_compiler'
- mkdir_p(compiler_folder)
- write_file(compiler_file..'.vim', [[let b:compiler = 'vim']])
- write_file(compiler_file..'.lua', [[vim.b.compiler = 'lua']])
-
+ it("'rtp' order is respected", function()
+ local after_compiler_folder = table.concat({plug_dir, 'after', 'compiler'}, sep)
+ mkdir_p(table.concat({compiler_folder, 'new_compiler'}, sep))
+ mkdir_p(table.concat({after_compiler_folder, 'new_compiler'}, sep))
+ exec('set rtp+=' .. plug_dir .. '/after')
+ exec('let g:seq = ""')
+ -- A .lua file is loaded after a .vim file if they only differ in extension.
+ -- All files in after/compiler/ are loaded after all files in compiler/.
+ write_file(table.concat({compiler_folder, 'new_compiler.vim'}, sep), [[let g:seq ..= 'A']])
+ write_file(table.concat({compiler_folder, 'new_compiler.lua'}, sep), [[vim.g.seq = vim.g.seq .. 'B']])
+ write_file(table.concat({after_compiler_folder, 'new_compiler.vim'}, sep), [[let g:seq ..= 'a']])
+ write_file(table.concat({after_compiler_folder, 'new_compiler.lua'}, sep), [[vim.g.seq = vim.g.seq .. 'b']])
exec('compiler new_compiler')
-
- eq('vim', eval('b:compiler'))
- rmdir(compiler_folder)
+ eq('ABab', eval('g:seq'))
end)
end)
describe('ftplugin', function()
local ftplugin_folder = table.concat({plug_dir, 'ftplugin'}, sep)
- it('loads lua ftplugins', function()
- local ftplugin_file = table.concat({ftplugin_folder , 'new-ft.lua'}, sep)
+ it('lua ftplugins work and are included in cmdline completion', function()
mkdir_p(ftplugin_folder)
+ local ftplugin_file = table.concat({ftplugin_folder , 'new-ft.lua'}, sep)
write_file(ftplugin_file , [[vim.b.lua_ftplugin = 1]])
eq({'new-ft'}, funcs.getcompletion('new-f', 'filetype'))
+ eq({'ftplugin/new-ft.lua'}, funcs.getcompletion('ftplugin/new-f', 'runtime'))
exec [[set filetype=new-ft]]
eq(1, eval('b:lua_ftplugin'))
- rmdir(ftplugin_folder)
+ end)
+
+ it("'rtp' order is respected", function()
+ local after_ftplugin_folder = table.concat({plug_dir, 'after', 'ftplugin'}, sep)
+ mkdir_p(table.concat({ftplugin_folder, 'new-ft'}, sep))
+ mkdir_p(table.concat({after_ftplugin_folder, 'new-ft'}, sep))
+ exec('set rtp+=' .. plug_dir .. '/after')
+ exec('let g:seq = ""')
+ -- A .lua file is loaded after a .vim file if they only differ in extension.
+ -- All files in after/ftplugin/ are loaded after all files in ftplugin/.
+ write_file(table.concat({ftplugin_folder, 'new-ft.vim'}, sep), [[let g:seq ..= 'A']])
+ write_file(table.concat({ftplugin_folder, 'new-ft.lua'}, sep), [[vim.g.seq = vim.g.seq .. 'B']])
+ write_file(table.concat({ftplugin_folder, 'new-ft_a.vim'}, sep), [[let g:seq ..= 'C']])
+ write_file(table.concat({ftplugin_folder, 'new-ft_a.lua'}, sep), [[vim.g.seq = vim.g.seq .. 'D']])
+ write_file(table.concat({ftplugin_folder, 'new-ft', 'a.vim'}, sep), [[let g:seq ..= 'E']])
+ write_file(table.concat({ftplugin_folder, 'new-ft', 'a.lua'}, sep), [[vim.g.seq = vim.g.seq .. 'F']])
+ write_file(table.concat({after_ftplugin_folder, 'new-ft.vim'}, sep), [[let g:seq ..= 'a']])
+ write_file(table.concat({after_ftplugin_folder, 'new-ft.lua'}, sep), [[vim.g.seq = vim.g.seq .. 'b']])
+ write_file(table.concat({after_ftplugin_folder, 'new-ft_a.vim'}, sep), [[let g:seq ..= 'c']])
+ write_file(table.concat({after_ftplugin_folder, 'new-ft_a.lua'}, sep), [[vim.g.seq = vim.g.seq .. 'd']])
+ write_file(table.concat({after_ftplugin_folder, 'new-ft', 'a.vim'}, sep), [[let g:seq ..= 'e']])
+ write_file(table.concat({after_ftplugin_folder, 'new-ft', 'a.lua'}, sep), [[vim.g.seq = vim.g.seq .. 'f']])
+ exec('setfiletype new-ft')
+ eq('ABCDEFabcdef', eval('g:seq'))
+ end)
+
+ it("'rtp' order is respected with 'fileignorecase'", function()
+ exec('set fileignorecase')
+ local after_ftplugin_folder = table.concat({plug_dir, 'after', 'ftplugin'}, sep)
+ mkdir_p(table.concat({ftplugin_folder, 'new-ft'}, sep))
+ mkdir_p(table.concat({after_ftplugin_folder, 'new-ft'}, sep))
+ exec('set rtp+=' .. plug_dir .. '/after')
+ exec('let g:seq = ""')
+ -- A .lua file is loaded after a .vim file if they only differ in extension.
+ -- All files in after/ftplugin/ are loaded after all files in ftplugin/.
+ write_file(table.concat({ftplugin_folder, 'new-ft.VIM'}, sep), [[let g:seq ..= 'A']])
+ write_file(table.concat({ftplugin_folder, 'new-ft.LUA'}, sep), [[vim.g.seq = vim.g.seq .. 'B']])
+ write_file(table.concat({ftplugin_folder, 'new-ft_a.vim'}, sep), [[let g:seq ..= 'C']])
+ write_file(table.concat({ftplugin_folder, 'new-ft_a.lua'}, sep), [[vim.g.seq = vim.g.seq .. 'D']])
+ write_file(table.concat({ftplugin_folder, 'new-ft', 'a.VIM'}, sep), [[let g:seq ..= 'E']])
+ write_file(table.concat({ftplugin_folder, 'new-ft', 'a.LUA'}, sep), [[vim.g.seq = vim.g.seq .. 'F']])
+ write_file(table.concat({after_ftplugin_folder, 'new-ft.vim'}, sep), [[let g:seq ..= 'a']])
+ write_file(table.concat({after_ftplugin_folder, 'new-ft.lua'}, sep), [[vim.g.seq = vim.g.seq .. 'b']])
+ write_file(table.concat({after_ftplugin_folder, 'new-ft_a.VIM'}, sep), [[let g:seq ..= 'c']])
+ write_file(table.concat({after_ftplugin_folder, 'new-ft_a.LUA'}, sep), [[vim.g.seq = vim.g.seq .. 'd']])
+ write_file(table.concat({after_ftplugin_folder, 'new-ft', 'a.vim'}, sep), [[let g:seq ..= 'e']])
+ write_file(table.concat({after_ftplugin_folder, 'new-ft', 'a.lua'}, sep), [[vim.g.seq = vim.g.seq .. 'f']])
+ exec('setfiletype new-ft')
+ eq('ABCDEFabcdef', eval('g:seq'))
end)
end)
describe('indent', function()
local indent_folder = table.concat({plug_dir, 'indent'}, sep)
- it('loads lua indents', function()
- local indent_file = table.concat({indent_folder , 'new-ft.lua'}, sep)
+ it('lua indents work and are included in cmdline completion', function()
mkdir_p(indent_folder)
+ local indent_file = table.concat({indent_folder , 'new-ft.lua'}, sep)
write_file(indent_file , [[vim.b.lua_indent = 1]])
eq({'new-ft'}, funcs.getcompletion('new-f', 'filetype'))
+ eq({'indent/new-ft.lua'}, funcs.getcompletion('indent/new-f', 'runtime'))
exec [[set filetype=new-ft]]
eq(1, eval('b:lua_indent'))
- rmdir(indent_folder)
+ end)
+
+ it("'rtp' order is respected", function()
+ local after_indent_folder = table.concat({plug_dir, 'after', 'indent'}, sep)
+ mkdir_p(table.concat({indent_folder, 'new-ft'}, sep))
+ mkdir_p(table.concat({after_indent_folder, 'new-ft'}, sep))
+ exec('set rtp+=' .. plug_dir .. '/after')
+ exec('let g:seq = ""')
+ -- A .lua file is loaded after a .vim file if they only differ in extension.
+ -- All files in after/indent/ are loaded after all files in indent/.
+ write_file(table.concat({indent_folder, 'new-ft.vim'}, sep), [[let g:seq ..= 'A']])
+ write_file(table.concat({indent_folder, 'new-ft.lua'}, sep), [[vim.g.seq = vim.g.seq .. 'B']])
+ write_file(table.concat({after_indent_folder, 'new-ft.vim'}, sep), [[let g:seq ..= 'a']])
+ write_file(table.concat({after_indent_folder, 'new-ft.lua'}, sep), [[vim.g.seq = vim.g.seq .. 'b']])
+ exec('setfiletype new-ft')
+ eq('ABab', eval('g:seq'))
end)
end)
@@ -127,8 +240,8 @@ describe('runtime:', function()
local syntax_folder = table.concat({plug_dir, 'syntax'}, sep)
before_each(function()
- local syntax_file = table.concat({syntax_folder , 'my-lang.lua'}, sep)
mkdir_p(syntax_folder)
+ local syntax_file = table.concat({syntax_folder , 'my-lang.lua'}, sep)
write_file(syntax_file , [[vim.b.current_syntax = 'my-lang']])
exec([[let b:current_syntax = '']])
end)
@@ -152,8 +265,47 @@ describe('runtime:', function()
it('lua syntaxes are included in cmdline completion', function()
eq({'my-lang'}, funcs.getcompletion('my-l', 'filetype'))
eq({'my-lang'}, funcs.getcompletion('my-l', 'syntax'))
+ eq({'syntax/my-lang.lua'}, funcs.getcompletion('syntax/my-l', 'runtime'))
+ end)
+
+ it("'rtp' order is respected", function()
+ local after_syntax_folder = table.concat({plug_dir, 'after', 'syntax'}, sep)
+ mkdir_p(table.concat({syntax_folder, 'my-lang'}, sep))
+ mkdir_p(table.concat({after_syntax_folder, 'my-lang'}, sep))
+ exec('set rtp+=' .. plug_dir .. '/after')
+ exec('let g:seq = ""')
+ -- A .lua file is loaded after a .vim file if they only differ in extension.
+ -- All files in after/syntax/ are loaded after all files in syntax/.
+ write_file(table.concat({syntax_folder, 'my-lang.vim'}, sep), [[let g:seq ..= 'A']])
+ write_file(table.concat({syntax_folder, 'my-lang.lua'}, sep), [[vim.g.seq = vim.g.seq .. 'B']])
+ write_file(table.concat({syntax_folder, 'my-lang', 'a.vim'}, sep), [[let g:seq ..= 'C']])
+ write_file(table.concat({syntax_folder, 'my-lang', 'a.lua'}, sep), [[vim.g.seq = vim.g.seq .. 'D']])
+ write_file(table.concat({after_syntax_folder, 'my-lang.vim'}, sep), [[let g:seq ..= 'a']])
+ write_file(table.concat({after_syntax_folder, 'my-lang.lua'}, sep), [[vim.g.seq = vim.g.seq .. 'b']])
+ write_file(table.concat({after_syntax_folder, 'my-lang', 'a.vim'}, sep), [[let g:seq ..= 'c']])
+ write_file(table.concat({after_syntax_folder, 'my-lang', 'a.lua'}, sep), [[vim.g.seq = vim.g.seq .. 'd']])
+ exec('setfiletype my-lang')
+ eq('ABCDabcd', eval('g:seq'))
end)
end)
-end)
+ describe('spell', function()
+ it("loads spell/LANG.{vim,lua} respecting 'rtp' order", function()
+ local spell_folder = table.concat({plug_dir, 'spell'}, sep)
+ local after_spell_folder = table.concat({plug_dir, 'after', 'spell'}, sep)
+ mkdir_p(table.concat({spell_folder, 'Xtest'}, sep))
+ mkdir_p(table.concat({after_spell_folder, 'Xtest'}, sep))
+ exec('set rtp+=' .. plug_dir .. '/after')
+ exec('let g:seq = ""')
+ -- A .lua file is loaded after a .vim file if they only differ in extension.
+ -- All files in after/spell/ are loaded after all files in spell/.
+ write_file(table.concat({spell_folder, 'Xtest.vim'}, sep), [[let g:seq ..= 'A']])
+ write_file(table.concat({spell_folder, 'Xtest.lua'}, sep), [[vim.g.seq = vim.g.seq .. 'B']])
+ write_file(table.concat({after_spell_folder, 'Xtest.vim'}, sep), [[let g:seq ..= 'a']])
+ write_file(table.concat({after_spell_folder, 'Xtest.lua'}, sep), [[vim.g.seq = vim.g.seq .. 'b']])
+ exec('set spelllang=Xtest')
+ eq('ABab', eval('g:seq'))
+ end)
+ end)
+end)
diff --git a/test/functional/lua/secure_spec.lua b/test/functional/lua/secure_spec.lua
index 2647b2be46..fc20a06390 100644
--- a/test/functional/lua/secure_spec.lua
+++ b/test/functional/lua/secure_spec.lua
@@ -6,7 +6,7 @@ local clear = helpers.clear
local command = helpers.command
local pathsep = helpers.get_pathsep()
local is_os = helpers.is_os
-local curbufmeths = helpers.curbufmeths
+local meths = helpers.meths
local exec_lua = helpers.exec_lua
local feed_command = helpers.feed_command
local feed = helpers.feed
@@ -19,21 +19,14 @@ describe('vim.secure', function()
local xstate = 'Xstate'
setup(function()
+ clear{env={XDG_STATE_HOME=xstate}}
helpers.mkdir_p(xstate .. pathsep .. (is_os('win') and 'nvim-data' or 'nvim'))
- end)
-
- teardown(function()
- helpers.rmdir(xstate)
- end)
-
- before_each(function()
helpers.write_file('Xfile', [[
let g:foobar = 42
]])
- clear{env={XDG_STATE_HOME=xstate}}
end)
- after_each(function()
+ teardown(function()
os.remove('Xfile')
helpers.rmdir(xstate)
end)
@@ -48,6 +41,7 @@ describe('vim.secure', function()
[4] = {reverse = true},
})
+ --- XXX: screen:expect() may fail if this path is too long.
local cwd = funcs.getcwd()
-- Need to use feed_command instead of exec_lua because of the confirmation prompt
@@ -59,7 +53,7 @@ describe('vim.secure', function()
{1:~ }|
{2: }|
:lua vim.secure.read('Xfile') |
- {3:]] .. cwd .. pathsep .. [[Xfile is untrusted}{MATCH:%s+}|
+ {3:]] .. cwd .. pathsep .. [[Xfile is not trusted.}{MATCH:%s+}|
{3:[i]gnore, (v)iew, (d)eny, (a)llow: }^ |
]]}
feed('d')
@@ -88,7 +82,7 @@ describe('vim.secure', function()
{1:~ }|
{2: }|
:lua vim.secure.read('Xfile') |
- {3:]] .. cwd .. pathsep .. [[Xfile is untrusted}{MATCH:%s+}|
+ {3:]] .. cwd .. pathsep .. [[Xfile is not trusted.}{MATCH:%s+}|
{3:[i]gnore, (v)iew, (d)eny, (a)llow: }^ |
]]}
feed('a')
@@ -118,7 +112,7 @@ describe('vim.secure', function()
{1:~ }|
{2: }|
:lua vim.secure.read('Xfile') |
- {3:]] .. cwd .. pathsep .. [[Xfile is untrusted}{MATCH:%s+}|
+ {3:]] .. cwd .. pathsep .. [[Xfile is not trusted.}{MATCH:%s+}|
{3:[i]gnore, (v)iew, (d)eny, (a)llow: }^ |
]]}
feed('i')
@@ -145,7 +139,7 @@ describe('vim.secure', function()
{1:~ }|
{2: }|
:lua vim.secure.read('Xfile') |
- {3:]] .. cwd .. pathsep .. [[Xfile is untrusted}{MATCH:%s+}|
+ {3:]] .. cwd .. pathsep .. [[Xfile is not trusted.}{MATCH:%s+}|
{3:[i]gnore, (v)iew, (d)eny, (a)llow: }^ |
]]}
feed('v')
@@ -153,7 +147,7 @@ describe('vim.secure', function()
^let g:foobar = 42 |
{1:~ }|
{1:~ }|
- {2:]] .. funcs.fnamemodify(cwd, ':~') .. pathsep .. [[Xfile [RO]{MATCH:%s+}|
+ {2:]] .. funcs.fnamemodify(cwd, ':~') .. pathsep .. [[Xfile [RO]{MATCH:%s+}}|
|
{1:~ }|
{4:[No Name] }|
@@ -166,7 +160,7 @@ describe('vim.secure', function()
-- Cannot write file
pcall_err(command, 'write')
- eq(true, curbufmeths.get_option('readonly'))
+ eq(true, meths.get_option_value('readonly', {}))
end)
end)
@@ -174,6 +168,7 @@ describe('vim.secure', function()
local xstate = 'Xstate'
setup(function()
+ clear{env={XDG_STATE_HOME=xstate}}
helpers.mkdir_p(xstate .. pathsep .. (is_os('win') and 'nvim-data' or 'nvim'))
end)
diff --git a/test/functional/lua/snippet_spec.lua b/test/functional/lua/snippet_spec.lua
new file mode 100644
index 0000000000..f0b3b44139
--- /dev/null
+++ b/test/functional/lua/snippet_spec.lua
@@ -0,0 +1,202 @@
+local helpers = require('test.functional.helpers')(after_each)
+
+local buf_lines = helpers.buf_lines
+local clear = helpers.clear
+local eq = helpers.eq
+local exec_lua = helpers.exec_lua
+local feed = helpers.feed
+local matches = helpers.matches
+local pcall_err = helpers.pcall_err
+local sleep = helpers.sleep
+
+describe('vim.snippet', function()
+ before_each(function()
+ clear()
+
+ exec_lua([[
+ vim.keymap.set({ 'i', 's' }, '<Tab>', function() vim.snippet.jump(1) end, { buffer = true })
+ vim.keymap.set({ 'i', 's' }, '<S-Tab>', function() vim.snippet.jump(-1) end, { buffer = true })
+ ]])
+ end)
+ after_each(clear)
+
+ --- @param snippet string[]
+ --- @param expected string[]
+ --- @param settings? string
+ --- @param prefix? string
+ local function test_expand_success(snippet, expected, settings, prefix)
+ if settings then
+ exec_lua(settings)
+ end
+ if prefix then
+ feed('i' .. prefix)
+ end
+ exec_lua('vim.snippet.expand(...)', table.concat(snippet, '\n'))
+ eq(expected, buf_lines(0))
+ end
+
+ --- @param snippet string
+ --- @param err string
+ local function test_expand_fail(snippet, err)
+ matches(err, pcall_err(exec_lua, string.format('vim.snippet.expand("%s")', snippet)))
+ end
+
+ it('adds base indentation to inserted text', function()
+ test_expand_success(
+ { 'function $1($2)', ' $0', 'end' },
+ { ' function ()', ' ', ' end' },
+ '',
+ ' '
+ )
+ end)
+
+ it('adds indentation based on the start of snippet lines', function()
+ test_expand_success({ 'if $1 then', ' $0', 'end' }, { 'if then', ' ', 'end' })
+ end)
+
+ it('replaces tabs with spaces when expandtab is set', function()
+ test_expand_success(
+ { 'function $1($2)', '\t$0', 'end' },
+ { 'function ()', ' ', 'end' },
+ [[
+ vim.o.expandtab = true
+ vim.o.shiftwidth = 2
+ ]]
+ )
+ end)
+
+ it('respects tabs when expandtab is not set', function()
+ test_expand_success(
+ { 'function $1($2)', '\t$0', 'end' },
+ { 'function ()', '\t', 'end' },
+ 'vim.o.expandtab = false'
+ )
+ end)
+
+ it('inserts known variable value', function()
+ test_expand_success({ '; print($TM_CURRENT_LINE)' }, { 'foo; print(foo)' }, nil, 'foo')
+ end)
+
+ it('uses default when variable is not set', function()
+ test_expand_success({ 'print(${TM_CURRENT_WORD:foo})' }, { 'print(foo)' })
+ end)
+
+ it('replaces unknown variables by placeholders', function()
+ test_expand_success({ 'print($UNKNOWN)' }, { 'print(UNKNOWN)' })
+ end)
+
+ it('does not jump outside snippet range', function()
+ test_expand_success({ 'function $1($2)', ' $0', 'end' }, { 'function ()', ' ', 'end' })
+ eq(false, exec_lua('return vim.snippet.jumpable(-1)'))
+ feed('<Tab><Tab>i')
+ eq(false, exec_lua('return vim.snippet.jumpable(1)'))
+ end)
+
+ it('navigates backwards', function()
+ test_expand_success({ 'function $1($2) end' }, { 'function () end' })
+ feed('<Tab><S-Tab>foo')
+ eq({ 'function foo() end' }, buf_lines(0))
+ end)
+
+ it('visits all tabstops', function()
+ local function cursor()
+ return exec_lua('return vim.api.nvim_win_get_cursor(0)')
+ end
+
+ test_expand_success({ 'function $1($2)', ' $0', 'end' }, { 'function ()', ' ', 'end' })
+ eq({ 1, 9 }, cursor())
+ feed('<Tab>')
+ eq({ 1, 10 }, cursor())
+ feed('<Tab>')
+ eq({ 2, 2 }, cursor())
+ end)
+
+ it('syncs text of tabstops with equal indexes', function()
+ test_expand_success({ 'var double = ${1:x} + ${1:x}' }, { 'var double = x + x' })
+ feed('123')
+ eq({ 'var double = 123 + 123' }, buf_lines(0))
+ end)
+
+ it('cancels session with changes outside the snippet', function()
+ test_expand_success({ 'print($1)' }, { 'print()' })
+ feed('<Esc>O-- A comment')
+ eq(false, exec_lua('return vim.snippet.active()'))
+ eq({ '-- A comment', 'print()' }, buf_lines(0))
+ end)
+
+ it('handles non-consecutive tabstops', function()
+ test_expand_success({ 'class $1($3) {', ' $0', '}' }, { 'class () {', ' ', '}' })
+ feed('Foo') -- First tabstop
+ feed('<Tab><Tab>') -- Jump to $0
+ feed('// Inside') -- Insert text
+ eq({ 'class Foo() {', ' // Inside', '}' }, buf_lines(0))
+ end)
+
+ it('handles multiline placeholders', function()
+ test_expand_success(
+ { 'public void foo() {', ' ${0:// TODO Auto-generated', ' throw;}', '}' },
+ { 'public void foo() {', ' // TODO Auto-generated', ' throw;', '}' }
+ )
+ end)
+
+ it('inserts placeholder in all tabstops when the first tabstop has the placeholder', function()
+ test_expand_success(
+ { 'for (${1:int} ${2:x} = ${3:0}; $2 < ${4:N}; $2++) {', ' $0', '}' },
+ { 'for (int x = 0; x < N; x++) {', ' ', '}' }
+ )
+ end)
+
+ it('inserts placeholder in all tabstops when a later tabstop has the placeholder', function()
+ test_expand_success(
+ { 'for (${1:int} $2 = ${3:0}; ${2:x} < ${4:N}; $2++) {', ' $0', '}' },
+ { 'for (int x = 0; x < N; x++) {', ' ', '}' }
+ )
+ end)
+
+ it('errors with multiple placeholders for the same index', function()
+ test_expand_fail('class ${1:Foo} { void ${1:foo}() {} }', 'multiple placeholders for tabstop $1')
+ end)
+
+ it('errors with multiple $0 tabstops', function()
+ test_expand_fail('function $1() { $0 }$0', 'multiple $0 tabstops')
+ end)
+
+ it('cancels session when deleting the snippet', function()
+ test_expand_success({ 'local function $1()', ' $0', 'end' }, { 'local function ()', ' ', 'end' })
+ feed('<esc>Vjjd')
+ eq(false, exec_lua('return vim.snippet.active()'))
+ end)
+
+ it('cancels session when inserting outside snippet region', function()
+ feed('i<cr>')
+ test_expand_success({ 'local function $1()', ' $0', 'end' }, { '', 'local function ()', ' ', 'end' })
+ feed('<esc>O-- A comment')
+ eq(false, exec_lua('return vim.snippet.active()'))
+ end)
+
+ it('inserts choice', function ()
+ test_expand_success({ 'console.${1|assert,log,error|}()' }, { 'console.()' })
+ sleep(100)
+ feed('<Down><C-y>')
+ eq({ 'console.log()' }, buf_lines(0))
+ end)
+
+ it('closes the choice completion menu when jumping', function ()
+ test_expand_success({ 'console.${1|assert,log,error|}($2)' }, { 'console.()' })
+ sleep(100)
+ exec_lua('vim.snippet.jump(1)')
+ eq(0, exec_lua('return vim.fn.pumvisible()'))
+ end)
+
+ it('jumps to next tabstop after inserting choice', function()
+ test_expand_success(
+ { '${1|public,protected,private|} function ${2:name}() {', '\t$0', '}' },
+ { ' function name() {', '\t', '}' }
+ )
+ sleep(100)
+ feed('<C-y><Tab>')
+ sleep(10)
+ feed('foo')
+ eq({ 'public function foo() {', '\t', '}' }, buf_lines(0))
+ end)
+end)
diff --git a/test/functional/lua/system_spec.lua b/test/functional/lua/system_spec.lua
new file mode 100644
index 0000000000..a988d3f0d7
--- /dev/null
+++ b/test/functional/lua/system_spec.lua
@@ -0,0 +1,100 @@
+local helpers = require('test.functional.helpers')(after_each)
+local clear = helpers.clear
+local exec_lua = helpers.exec_lua
+local eq = helpers.eq
+
+local function system_sync(cmd, opts)
+ return exec_lua([[
+ local cmd, opts = ...
+ local obj = vim.system(...)
+
+ if 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)
+ end
+
+ local res = obj:wait()
+
+ -- Check the process is no longer running
+ local proc = vim.api.nvim_get_proc(obj.pid)
+ assert(not proc, 'process still exists')
+
+ return res
+ ]], cmd, opts)
+end
+
+local function system_async(cmd, opts)
+ return exec_lua([[
+ local cmd, opts = ...
+ _G.done = false
+ local obj = vim.system(cmd, opts, function(obj)
+ _G.done = true
+ _G.ret = obj
+ end)
+
+ local ok = vim.wait(10000, function()
+ return _G.done
+ end)
+
+ assert(ok, 'process did not exit')
+
+ -- Check the process is no longer running
+ local proc = vim.api.nvim_get_proc(obj.pid)
+ assert(not proc, 'process still exists')
+
+ return _G.ret
+ ]], cmd, opts)
+end
+
+describe('vim.system', function()
+ before_each(function()
+ clear()
+ end)
+
+ for name, system in pairs{ sync = system_sync, async = system_async, } do
+ describe('('..name..')', function()
+ it('can run simple commands', function()
+ eq('hello\n', system({'echo', 'hello' }, { text = true }).stdout)
+ end)
+
+ it('handle input', function()
+ eq('hellocat', system({ 'cat' }, { stdin = 'hellocat', text = true }).stdout)
+ end)
+
+ it('supports timeout', function()
+ eq({
+ code = 124,
+ signal = 15,
+ stdout = '',
+ stderr = ''
+ }, system({ 'sleep', '10' }, { timeout = 1000 }))
+ end)
+ end)
+ end
+
+ it('kill processes', function()
+ exec_lua([[
+ local signal
+ local cmd = vim.system({ 'cat', '-' }, { stdin = true }, function(r)
+ signal = r.signal
+ end) -- run forever
+
+ cmd:kill('sigint')
+
+ -- wait for the process not to exist
+ local done = vim.wait(2000, function()
+ return signal ~= nil
+ end)
+
+ assert(done, 'process did not exit')
+
+ -- Check the process is no longer running
+ local proc = vim.api.nvim_get_proc(cmd.pid)
+ assert(not proc, 'process still exists')
+
+ assert(signal == 2)
+ ]])
+ end)
+
+end)
diff --git a/test/functional/lua/text_spec.lua b/test/functional/lua/text_spec.lua
new file mode 100644
index 0000000000..68206557c3
--- /dev/null
+++ b/test/functional/lua/text_spec.lua
@@ -0,0 +1,23 @@
+local helpers = require('test.functional.helpers')(after_each)
+local clear = helpers.clear
+local eq = helpers.eq
+
+describe('vim.text', function()
+ before_each(clear)
+
+ describe('hexencode() and hexdecode()', function()
+ it('works', function()
+ local cases = {
+ { 'Hello world!', '48656C6C6F20776F726C6421' },
+ { '😂', 'F09F9882' },
+ }
+
+ for _, v in ipairs(cases) do
+ local input, output = unpack(v)
+ eq(output, vim.text.hexencode(input))
+ eq(input, vim.text.hexdecode(output))
+ end
+ end)
+ end)
+end)
+
diff --git a/test/functional/lua/thread_spec.lua b/test/functional/lua/thread_spec.lua
index c7f2783cf3..e79d26a641 100644
--- a/test/functional/lua/thread_spec.lua
+++ b/test/functional/lua/thread_spec.lua
@@ -27,10 +27,10 @@ describe('thread', function()
it('entry func is executed in protected mode', function()
exec_lua [[
- local thread = vim.loop.new_thread(function()
+ local thread = vim.uv.new_thread(function()
error('Error in thread entry func')
end)
- vim.loop.thread_join(thread)
+ vim.uv.thread_join(thread)
]]
screen:expect([[
@@ -51,17 +51,17 @@ describe('thread', function()
it('callback is executed in protected mode', function()
exec_lua [[
- local thread = vim.loop.new_thread(function()
- local timer = vim.loop.new_timer()
+ local thread = vim.uv.new_thread(function()
+ local timer = vim.uv.new_timer()
local function ontimeout()
timer:stop()
timer:close()
error('Error in thread callback')
end
timer:start(10, 0, ontimeout)
- vim.loop.run()
+ vim.uv.run()
end)
- vim.loop.thread_join(thread)
+ vim.uv.thread_join(thread)
]]
screen:expect([[
@@ -83,10 +83,10 @@ describe('thread', function()
describe('print', function()
it('works', function()
exec_lua [[
- local thread = vim.loop.new_thread(function()
+ local thread = vim.uv.new_thread(function()
print('print in thread')
end)
- vim.loop.thread_join(thread)
+ vim.uv.thread_join(thread)
]]
screen:expect([[
@@ -105,10 +105,10 @@ describe('thread', function()
it('vim.inspect', function()
exec_lua [[
- local thread = vim.loop.new_thread(function()
+ local thread = vim.uv.new_thread(function()
print(vim.inspect({1,2}))
end)
- vim.loop.thread_join(thread)
+ vim.uv.thread_join(thread)
]]
screen:expect([[
@@ -140,13 +140,13 @@ describe('thread', function()
function Thread_Test:do_test()
local async
local on_async = self.on_async
- async = vim.loop.new_async(function(ret)
+ async = vim.uv.new_async(function(ret)
on_async(ret)
async:close()
end)
local thread =
- vim.loop.new_thread(self.entry_func, async, self.entry_str, self.args)
- vim.loop.thread_join(thread)
+ vim.uv.new_thread(self.entry_func, async, self.entry_str, self.args)
+ vim.uv.thread_join(thread)
end
Thread_Test.new = function(entry, on_async, ...)
@@ -175,10 +175,10 @@ describe('thread', function()
eq({'notification', 'result', {true}}, next_msg())
end)
- it('loop', function()
+ it('uv', function()
exec_lua [[
local entry = function(async)
- async:send(vim.loop.version())
+ async:send(vim.uv.version())
end
local on_async = function(ret)
vim.rpcnotify(1, ret)
@@ -259,7 +259,7 @@ describe('threadpool', function()
local after_work_fn = function(ret)
vim.rpcnotify(1, 'result', ret)
end
- local work = vim.loop.new_work(work_fn, after_work_fn)
+ local work = vim.uv.new_work(work_fn, after_work_fn)
work:queue()
]]
@@ -268,7 +268,7 @@ describe('threadpool', function()
it('with invalid argument', function()
local status = pcall_err(exec_lua, [[
- local work = vim.loop.new_thread(function() end, function() end)
+ local work = vim.uv.new_thread(function() end, function() end)
work:queue({})
]])
@@ -288,7 +288,7 @@ describe('threadpool', function()
})
exec_lua [[
- local work = vim.loop.new_work(function() return {} end, function() end)
+ local work = vim.uv.new_work(function() return {} end, function() end)
work:queue()
]]
@@ -319,7 +319,7 @@ describe('threadpool', function()
function Threadpool_Test:do_test()
local work =
- vim.loop.new_work(self.work_fn, self.after_work)
+ vim.uv.new_work(self.work_fn, self.after_work)
work:queue(self.work_fn_str, self.args)
end
@@ -334,10 +334,10 @@ describe('threadpool', function()
]]
end)
- it('loop', function()
+ it('uv', function()
exec_lua [[
local work_fn = function()
- return vim.loop.version()
+ return vim.uv.version()
end
local after_work_fn = function(ret)
vim.rpcnotify(1, ret)
diff --git a/test/functional/lua/ui_event_spec.lua b/test/functional/lua/ui_event_spec.lua
index 6481da900e..373d45da61 100644
--- a/test/functional/lua/ui_event_spec.lua
+++ b/test/functional/lua/ui_event_spec.lua
@@ -77,13 +77,7 @@ describe('vim.ui_attach', function()
}
feed '<c-y>'
- screen:expect{grid=[[
- foobar^ |
- {1:~ }|
- {1:~ }|
- {1:~ }|
- {2:-- INSERT --} |
- ]], intermediate=true}
+ screen:expect_unchanged()
expect_events {
{ "popupmenu_hide" };
}
diff --git a/test/functional/lua/ui_spec.lua b/test/functional/lua/ui_spec.lua
index 9ee99b4905..d4c150c5f2 100644
--- a/test/functional/lua/ui_spec.lua
+++ b/test/functional/lua/ui_spec.lua
@@ -1,9 +1,12 @@
local helpers = require('test.functional.helpers')(after_each)
local eq = helpers.eq
+local matches = helpers.matches
local exec_lua = helpers.exec_lua
local clear = helpers.clear
local feed = helpers.feed
local eval = helpers.eval
+local is_ci = helpers.is_ci
+local is_os = helpers.is_os
local poke_eventloop = helpers.poke_eventloop
describe('vim.ui', function()
@@ -11,8 +14,7 @@ describe('vim.ui', function()
clear()
end)
-
- describe('select', function()
+ describe('select()', function()
it('can select an item', function()
local result = exec_lua[[
local items = {
@@ -47,7 +49,7 @@ describe('vim.ui', function()
end)
end)
- describe('input', function()
+ describe('input()', function()
it('can input text', function()
local result = exec_lua[[
local opts = {
@@ -130,4 +132,23 @@ describe('vim.ui', function()
end)
end)
+
+ describe('open()', function()
+ it('validation', function()
+ if is_os('win') or not is_ci('github') then
+ exec_lua[[vim.system = function() return { wait=function() return { code=3} end } end]]
+ end
+ if not is_os('bsd') then
+ matches('vim.ui.open: command failed %(%d%): { "[^"]+", .*"non%-existent%-file" }',
+ exec_lua[[local _, err = vim.ui.open('non-existent-file') ; return err]])
+ end
+
+ exec_lua[[
+ vim.fn.has = function() return 0 end
+ vim.fn.executable = function() return 0 end
+ ]]
+ eq('vim.ui.open: no handler found (tried: wslview, xdg-open)',
+ exec_lua[[local _, err = vim.ui.open('foo') ; return err]])
+ end)
+ end)
end)
diff --git a/test/functional/lua/version_spec.lua b/test/functional/lua/version_spec.lua
new file mode 100644
index 0000000000..d1c981c388
--- /dev/null
+++ b/test/functional/lua/version_spec.lua
@@ -0,0 +1,273 @@
+local helpers = require('test.functional.helpers')(after_each)
+local clear = helpers.clear
+local eq = helpers.eq
+local ok = helpers.ok
+local exec_lua = helpers.exec_lua
+local matches = helpers.matches
+local pcall_err = helpers.pcall_err
+
+local function v(ver)
+ return vim.version._version(ver)
+end
+
+describe('version', function()
+
+ it('package', function()
+ clear()
+ eq({ major = 42, minor = 3, patch = 99 }, exec_lua("return vim.version.parse('v42.3.99')"))
+ end)
+
+ it('version() returns Nvim version', function()
+ local expected = exec_lua('return vim.fn.api_info().version')
+ local actual = exec_lua('return vim.version()')
+ eq(expected.major, actual.major)
+ eq(expected.minor, actual.minor)
+ eq(expected.patch, actual.patch)
+ eq(expected.prerelease and 'dev' or nil, actual.prerelease)
+
+ -- tostring() #23863
+ matches([[%d+%.%d+%.%d+]], exec_lua('return tostring(vim.version())'))
+ end)
+
+ describe('_version()', function()
+ local tests = {
+ ['v1.2.3'] = { major = 1, minor = 2, patch = 3 },
+ ['v1.2'] = { major = 1, minor = 2, patch = 0 },
+ ['v1.2.3-prerelease'] = { major = 1, minor = 2, patch = 3, prerelease = 'prerelease' },
+ ['v1.2-prerelease'] = { major = 1, minor = 2, patch = 0, prerelease = 'prerelease' },
+ ['v1.2.3-prerelease+build'] = { major = 1, minor = 2, patch = 3, prerelease = 'prerelease', build = 'build' },
+ ['1.2.3+build'] = { major = 1, minor = 2, patch = 3, build = 'build' },
+ }
+ for input, output in pairs(tests) do
+ it('parses ' .. input, function()
+ eq(output, v(input))
+ end)
+ end
+ end)
+
+ describe('range', function()
+ local tests = {
+ ['1.2.3'] = { from = { 1, 2, 3 }, to = { 1, 2, 4 } },
+ ['1.2'] = { from = { 1, 2, 0 }, to = { 1, 3, 0 } },
+ ['=1.2.3'] = { from = { 1, 2, 3 }, to = { 1, 2, 4 } },
+ ['>1.2.3'] = { from = { 1, 2, 4 } },
+ ['>=1.2.3'] = { from = { 1, 2, 3 } },
+ ['<1.2.3'] = { from = { 0, 0, 0 }, to = { 1, 2, 3 } },
+ ['<=1.2.3'] = { from = { 0, 0, 0 }, to = { 1, 2, 4 } },
+ ['~1.2.3'] = { from = { 1, 2, 3 }, to = { 1, 3, 0 } },
+ ['^1.2.3'] = { from = { 1, 2, 3 }, to = { 2, 0, 0 } },
+ ['^0.2.3'] = { from = { 0, 2, 3 }, to = { 0, 3, 0 } },
+ ['^0.0.1'] = { from = { 0, 0, 1 }, to = { 0, 0, 2 } },
+ ['^1.2'] = { from = { 1, 2, 0 }, to = { 2, 0, 0 } },
+ ['~1.2'] = { from = { 1, 2, 0 }, to = { 1, 3, 0 } },
+ ['~1'] = { from = { 1, 0, 0 }, to = { 2, 0, 0 } },
+ ['^1'] = { from = { 1, 0, 0 }, to = { 2, 0, 0 } },
+ ['1.*'] = { from = { 1, 0, 0 }, to = { 2, 0, 0 } },
+ ['1'] = { from = { 1, 0, 0 }, to = { 2, 0, 0 } },
+ ['1.x'] = { from = { 1, 0, 0 }, to = { 2, 0, 0 } },
+ ['1.2.x'] = { from = { 1, 2, 0 }, to = { 1, 3, 0 } },
+ ['1.2.*'] = { from = { 1, 2, 0 }, to = { 1, 3, 0 } },
+ ['*'] = { from = { 0, 0, 0 } },
+ ['1.2 - 2.3.0'] = { from = { 1, 2, 0 }, to = { 2, 3, 0 } },
+ ['1.2.3 - 2.3.4'] = { from = { 1, 2, 3 }, to = { 2, 3, 4 } },
+ ['1.2.3 - 2'] = { from = { 1, 2, 3 }, to = { 3, 0, 0 } },
+ }
+ for input, output in pairs(tests) do
+ output.from = v(output.from)
+ output.to = output.to and v(output.to)
+ local range = vim.version.range(input)
+
+ it('parses ' .. input, function()
+ eq(output, range)
+ end)
+
+ it('[from] in range ' .. input, function()
+ assert(range:has(output.from))
+ end)
+
+ it('[from-1] not in range ' .. input, function()
+ local lower = vim.deepcopy(range.from)
+ lower.major = lower.major - 1
+ assert(not range:has(lower))
+ end)
+
+ it('[to] not in range ' .. input .. ' to:' .. tostring(range.to), function()
+ if range.to then
+ assert(not (range.to < range.to))
+ assert(not range:has(range.to))
+ end
+ end)
+ end
+
+ it('handles prerelease', function()
+ assert(not vim.version.range('1.2.3'):has('1.2.3-alpha'))
+ assert(vim.version.range('1.2.3-alpha'):has('1.2.3-alpha'))
+ assert(not vim.version.range('1.2.3-alpha'):has('1.2.3-beta'))
+ end)
+ end)
+
+ describe('cmp()', function()
+ local testcases = {
+ { v1 = 'v0.0.99', v2 = 'v9.0.0', want = -1, },
+ { v1 = 'v0.4.0', v2 = 'v0.9.99', want = -1, },
+ { v1 = 'v0.2.8', v2 = 'v1.0.9', want = -1, },
+ { v1 = 'v0.0.0', v2 = 'v0.0.0', want = 0, },
+ { v1 = 'v9.0.0', v2 = 'v0.9.0', want = 1, },
+ { v1 = 'v0.9.0', v2 = 'v0.0.0', want = 1, },
+ { v1 = 'v0.0.9', v2 = 'v0.0.0', want = 1, },
+ { v1 = 'v0.0.9+aaa', v2 = 'v0.0.9+bbb', want = 0, },
+
+ -- prerelease 💩 https://semver.org/#spec-item-11
+ { v1 = 'v1.0.0-alpha', v2 = 'v1.0.0', want = -1, },
+ { v1 = '1.0.0', v2 = '1.0.0-alpha', want = 1, },
+ { v1 = '1.0.0-2', v2 = '1.0.0-1', want = 1, },
+ { v1 = '1.0.0-2', v2 = '1.0.0-9', want = -1, },
+ { v1 = '1.0.0-2', v2 = '1.0.0-2.0', want = -1, },
+ { v1 = '1.0.0-2.0', v2 = '1.0.0-2', want = 1, },
+ { v1 = '1.0.0-2.0', v2 = '1.0.0-2.0', want = 0, },
+ { v1 = '1.0.0-alpha', v2 = '1.0.0-alpha', want = 0, },
+ { v1 = '1.0.0-alpha', v2 = '1.0.0-beta', want = -1, },
+ { v1 = '1.0.0-beta', v2 = '1.0.0-alpha', want = 1, },
+ { v1 = '1.0.0-alpha', v2 = '1.0.0-alpha.1', want = -1, },
+ { v1 = '1.0.0-alpha.1', v2 = '1.0.0-alpha', want = 1, },
+ { v1 = '1.0.0-alpha.beta', v2 = '1.0.0-alpha', want = 1, },
+ { v1 = '1.0.0-alpha', v2 = '1.0.0-alpha.beta', want = -1, },
+ { v1 = '1.0.0-alpha.beta', v2 = '1.0.0-beta', want = -1, },
+ { v1 = '1.0.0-beta.2', v2 = '1.0.0-beta.11', want = -1, },
+ { v1 = '1.0.0-beta.20', v2 = '1.0.0-beta.11', want = 1, },
+ { v1 = '1.0.0-alpha.20', v2 = '1.0.0-beta.11', want = -1, },
+ { v1 = '1.0.0-a.01.x.3', v2 = '1.0.0-a.1.x.003', want = 0, },
+ { v1 = 'v0.9.0-dev-92+9', v2 = 'v0.9.0-dev-120+3', want = -1, },
+ }
+ for _, tc in ipairs(testcases) do
+ local msg = function(s) return ('v1 %s v2'):format(s == 0 and '==' or (s == 1 and '>' or '<')) end
+ it(string.format('(v1 = %s, v2 = %s)', tc.v1, tc.v2),
+ function()
+ local rv = vim.version.cmp(tc.v1, tc.v2, { strict = true })
+ ok(tc.want == rv, msg(tc.want), msg(rv))
+ end
+ )
+ end
+ end)
+
+ describe('parse()', function()
+ describe('strict=true', function()
+ local testcases = {
+ { desc = 'Nvim version', version = 'v0.9.0-dev-1233+g210120dde81e', want = { major = 0, minor = 9, patch = 0, prerelease = 'dev-1233', build = 'g210120dde81e', }, },
+ { desc = 'no v', version = '10.20.123', want = { major = 10, minor = 20, patch = 123, prerelease = nil, build = nil, }, },
+ { desc = 'with v', version = 'v1.2.3', want = { major = 1, minor = 2, patch = 3 }, },
+ { desc = 'prerelease', version = '1.2.3-alpha', want = { major = 1, minor = 2, patch = 3, prerelease = 'alpha' }, },
+ { desc = 'prerelease.x', version = '1.2.3-alpha.1', want = { major = 1, minor = 2, patch = 3, prerelease = 'alpha.1' }, },
+ { desc = 'build.x', version = '1.2.3+build.15', want = { major = 1, minor = 2, patch = 3, build = 'build.15' }, },
+ { desc = 'prerelease and build', version = '1.2.3-rc1+build.15', want = { major = 1, minor = 2, patch = 3, prerelease = 'rc1', build = 'build.15', }, },
+ }
+ for _, tc in ipairs(testcases) do
+ it(
+ string.format('%q: version = %q', tc.desc, tc.version),
+ function()
+ eq(tc.want, vim.version.parse(tc.version))
+ end
+ )
+ end
+ end)
+
+ describe('strict=false', function()
+ local testcases = {
+ { version = '1.2', want = { major = 1, minor = 2, patch = 0 }, },
+ { version = '1', want = { major = 1, minor = 0, patch = 0 }, },
+ { version = '1.1-0', want = { major = 1, minor = 1, patch = 0, prerelease = '0' }, },
+ { version = '1-1.0', want = { major = 1, minor = 0, patch = 0, prerelease = '1.0' }, },
+ { version = 'v1.2.3 ', want = { major = 1, minor = 2, patch = 3 }, },
+ { version = ' v1.2.3', want = { major = 1, minor = 2, patch = 3 }, },
+ { version = 'tmux 3.2a', want = { major = 3, minor = 2, patch = 0, }, },
+ }
+ for _, tc in ipairs(testcases) do
+ it(
+ string.format('version = %q', tc.version),
+ function()
+ eq(tc.want, vim.version.parse(tc.version, { strict = false }))
+ end
+ )
+ end
+ end)
+
+ describe('invalid semver', function()
+ local testcases = {
+ { version = 'foo' },
+ { version = '' },
+ { version = '0.0.0.' },
+ { version = '.0.0.0' },
+ { version = '-1.0.0' },
+ { version = '0.-1.0' },
+ { version = '0.0.-1' },
+ { version = 'foobar1.2.3' },
+ { version = '1.2.3foobar' },
+ { version = '1.2.3-%?' },
+ { version = '1.2.3+%?' },
+ { version = '1.2.3+build.0-rc1' },
+ { version = '3.2a', },
+ { version = 'tmux 3.2a', },
+ }
+
+ local function quote_empty(s)
+ return tostring(s) == '' and '""' or tostring(s)
+ end
+
+ for _, tc in ipairs(testcases) do
+ it(quote_empty(tc.version), function()
+ eq(nil, vim.version.parse(tc.version, { strict = true }))
+ end)
+ end
+ end)
+
+ describe('invalid shape', function()
+ local testcases = {
+ { desc = 'no parameters' },
+ { desc = 'nil', version = nil },
+ { desc = 'number', version = 0 },
+ { desc = 'float', version = 0.01 },
+ { desc = 'table', version = {} },
+ }
+ for _, tc in ipairs(testcases) do
+ it(string.format('(%s): %s', tc.desc, tostring(tc.version)), function()
+ local expected = string.format(type(tc.version) == 'string'
+ and 'invalid version: "%s"' or 'invalid version: %s', tostring(tc.version))
+ matches(expected, pcall_err(vim.version.parse, tc.version, { strict = true }))
+ end)
+ end
+ end)
+ end)
+
+ it('relational metamethods (== < >)', function()
+ assert(v('v1.2.3') == v('1.2.3'))
+ assert(not (v('v1.2.3') < v('1.2.3')))
+ assert(v('v1.2.3') > v('1.2.3-prerelease'))
+ assert(v('v1.2.3-alpha') < v('1.2.3-beta'))
+ assert(v('v1.2.3-prerelease') < v('1.2.3'))
+ assert(v('v1.2.3') >= v('1.2.3'))
+ assert(v('v1.2.3') >= v('1.0.3'))
+ assert(v('v1.2.3') >= v('1.2.2'))
+ assert(v('v1.2.3') > v('1.2.2'))
+ assert(v('v1.2.3') > v('1.0.3'))
+ eq(vim.version.last({ v('1.2.3'), v('2.0.0') }), v('2.0.0'))
+ eq(vim.version.last({ v('2.0.0'), v('1.2.3') }), v('2.0.0'))
+ end)
+
+ it('lt()', function()
+ eq(true, vim.version.lt('1', '2'))
+ eq(false, vim.version.lt({3}, {0, 7, 4}))
+ eq(false, vim.version.lt({major=3, minor=3, patch=0}, {3, 2, 0}))
+ end)
+
+ it('gt()', function()
+ eq(true, vim.version.gt('2', '1'))
+ eq(true, vim.version.gt({3}, {0, 7, 4}))
+ eq(true, vim.version.gt({major=3, minor=3, patch=0}, {3, 2, 0}))
+ end)
+
+ it('eq()', function()
+ eq(true, vim.version.eq('2', '2'))
+ eq(true, vim.version.eq({3, 1, 0}, '3.1.0'))
+ eq(true, vim.version.eq({major=3, minor=3, patch=0}, {3, 3, 0}))
+ end)
+end)
diff --git a/test/functional/lua/vim_spec.lua b/test/functional/lua/vim_spec.lua
index 867f366d06..ebe5fc254e 100644
--- a/test/functional/lua/vim_spec.lua
+++ b/test/functional/lua/vim_spec.lua
@@ -6,10 +6,12 @@ local nvim_prog = helpers.nvim_prog
local funcs = helpers.funcs
local meths = helpers.meths
local command = helpers.command
+local dedent = helpers.dedent
local insert = helpers.insert
local clear = helpers.clear
local eq = helpers.eq
local ok = helpers.ok
+local pesc = helpers.pesc
local eval = helpers.eval
local feed = helpers.feed
local pcall_err = helpers.pcall_err
@@ -126,6 +128,22 @@ describe('lua stdlib', function()
eq(1, funcs.luaeval('vim.stricmp("\\0C\\0", "\\0B\\0")'))
end)
+ it('vim.deprecate', function()
+ -- vim.deprecate(name, alternative, version, plugin, backtrace)
+ eq(dedent[[
+ foo.bar() is deprecated, use zub.wooo{ok=yay} instead. :help deprecated
+ This feature will be removed in Nvim version 2.17]],
+ exec_lua('return vim.deprecate(...)', 'foo.bar()', 'zub.wooo{ok=yay}', '2.17'))
+ -- Same message, skipped.
+ eq(vim.NIL,
+ exec_lua('return vim.deprecate(...)', 'foo.bar()', 'zub.wooo{ok=yay}', '2.17'))
+ -- When `plugin` is specified, don't show ":help deprecated". #22235
+ eq(dedent[[
+ foo.bar() is deprecated, use zub.wooo{ok=yay} instead.
+ This feature will be removed in my-plugin.nvim version 0.3.0]],
+ exec_lua('return vim.deprecate(...)', 'foo.bar()', 'zub.wooo{ok=yay}', '0.3.0', 'my-plugin.nvim', false))
+ end)
+
it('vim.startswith', function()
eq(true, funcs.luaeval('vim.startswith("123", "1")'))
eq(true, funcs.luaeval('vim.startswith("123", "")'))
@@ -259,7 +277,7 @@ describe('lua stdlib', function()
|
]]}
- -- nvim_command causes a vimL exception, check that it is properly caught
+ -- nvim_command causes a Vimscript exception, check that it is properly caught
-- and propagated as an error message in async contexts.. #10809
exec_lua([[
vim.schedule(function()
@@ -275,51 +293,53 @@ describe('lua stdlib', function()
]]}
end)
- it("vim.split", function()
- local split = function(str, sep, kwargs)
- return exec_lua('return vim.split(...)', str, sep, kwargs)
- end
-
+ it('vim.gsplit, vim.split', function()
local tests = {
- { "a,b", ",", false, false, { 'a', 'b' } },
- { ":aa::bb:", ":", false, false, { '', 'aa', '', 'bb', '' } },
- { ":aa::bb:", ":", false, true, { 'aa', '', 'bb' } },
- { "::ee::ff:", ":", false, false, { '', '', 'ee', '', 'ff', '' } },
- { "::ee::ff:", ":", false, true, { 'ee', '', 'ff' } },
- { "ab", ".", false, false, { '', '', '' } },
- { "a1b2c", "[0-9]", false, false, { 'a', 'b', 'c' } },
- { "xy", "", false, false, { 'x', 'y' } },
- { "here be dragons", " ", false, false, { "here", "be", "dragons"} },
- { "axaby", "ab?", false, false, { '', 'x', 'y' } },
- { "f v2v v3v w2w ", "([vw])2%1", false, false, { 'f ', ' v3v ', ' ' } },
- { "", "", false, false, {} },
- { "", "a", false, false, { '' } },
- { "x*yz*oo*l", "*", true, false, { 'x', 'yz', 'oo', 'l' } },
+ -- plain trimempty
+ { 'a,b', ',', false, false, { 'a', 'b' } },
+ { ':aa::::bb:', ':', false, false, { '', 'aa', '', '', '', 'bb', '' } },
+ { ':aa::::bb:', ':', false, true, { 'aa', '', '', '', 'bb' } },
+ { 'aa::::bb:', ':', false, true, { 'aa', '', '', '', 'bb' } },
+ { ':aa::bb:', ':', false, true, { 'aa', '', 'bb' } },
+ { '/a/b:/b/\n', '[:\n]', false, true, { '/a/b', '/b/' } },
+ { '::ee::ff:', ':', false, false, { '', '', 'ee', '', 'ff', '' } },
+ { '::ee::ff::', ':', false, true, { 'ee', '', 'ff' } },
+ { 'ab', '.', false, false, { '', '', '' } },
+ { 'a1b2c', '[0-9]', false, false, { 'a', 'b', 'c' } },
+ { 'xy', '', false, false, { 'x', 'y' } },
+ { 'here be dragons', ' ', false, false, { 'here', 'be', 'dragons'} },
+ { 'axaby', 'ab?', false, false, { '', 'x', 'y' } },
+ { 'f v2v v3v w2w ', '([vw])2%1', false, false, { 'f ', ' v3v ', ' ' } },
+ { '', '', false, false, {} },
+ { '', '', false, true, {} },
+ { '\n', '[:\n]', false, true, {} },
+ { '', 'a', false, false, { '' } },
+ { 'x*yz*oo*l', '*', true, false, { 'x', 'yz', 'oo', 'l' } },
}
for _, t in ipairs(tests) do
- eq(t[5], split(t[1], t[2], {plain=t[3], trimempty=t[4]}))
+ eq(t[5], vim.split(t[1], t[2], {plain=t[3], trimempty=t[4]}), t[1])
end
-- Test old signature
- eq({'x', 'yz', 'oo', 'l'}, split("x*yz*oo*l", "*", true))
+ eq({'x', 'yz', 'oo', 'l'}, vim.split("x*yz*oo*l", "*", true))
local loops = {
{ "abc", ".-" },
}
for _, t in ipairs(loops) do
- matches("Infinite loop detected", pcall_err(split, t[1], t[2]))
+ matches("Infinite loop detected", pcall_err(vim.split, t[1], t[2]))
end
-- Validates args.
- eq(true, pcall(split, 'string', 'string'))
+ eq(true, pcall(vim.split, 'string', 'string'))
matches('s: expected string, got number',
- pcall_err(split, 1, 'string'))
+ pcall_err(vim.split, 1, 'string'))
matches('sep: expected string, got number',
- pcall_err(split, 'string', 1))
- matches('kwargs: expected table, got number',
- pcall_err(split, 'string', 'string', 1))
+ pcall_err(vim.split, 'string', 1))
+ matches('opts: expected table, got number',
+ pcall_err(vim.split, 'string', 'string', 1))
end)
it('vim.trim', function()
@@ -444,6 +464,22 @@ describe('lua stdlib', function()
pcall_err(exec_lua, [[return vim.pesc(2)]]))
end)
+ it('vim.list_contains', function()
+ eq(true, exec_lua("return vim.list_contains({'a','b','c'}, 'c')"))
+ eq(false, exec_lua("return vim.list_contains({'a','b','c'}, 'd')"))
+ end)
+
+ it('vim.tbl_contains', function()
+ eq(true, exec_lua("return vim.tbl_contains({'a','b','c'}, 'c')"))
+ eq(false, exec_lua("return vim.tbl_contains({'a','b','c'}, 'd')"))
+ eq(true, exec_lua("return vim.tbl_contains({[2]='a',foo='b',[5] = 'c'}, 'c')"))
+ eq(true, exec_lua([[
+ return vim.tbl_contains({ 'a', { 'b', 'c' } }, function(v)
+ return vim.deep_equal(v, { 'b', 'c' })
+ end, { predicate = true })
+ ]]))
+ end)
+
it('vim.tbl_keys', function()
eq({}, exec_lua("return vim.tbl_keys({})"))
for _, v in pairs(exec_lua("return vim.tbl_keys({'a', 'b', 'c'})")) do
@@ -488,6 +524,19 @@ describe('lua stdlib', function()
]]))
end)
+ it('vim.tbl_isarray', function()
+ eq(true, exec_lua("return vim.tbl_isarray({})"))
+ eq(false, exec_lua("return vim.tbl_isarray(vim.empty_dict())"))
+ eq(true, exec_lua("return vim.tbl_isarray({'a', 'b', 'c'})"))
+ eq(false, exec_lua("return vim.tbl_isarray({'a', '32', a='hello', b='baz'})"))
+ eq(false, exec_lua("return vim.tbl_isarray({1, a='hello', b='baz'})"))
+ eq(false, exec_lua("return vim.tbl_isarray({a='hello', b='baz', 1})"))
+ eq(false, exec_lua("return vim.tbl_isarray({1, 2, nil, a='hello'})"))
+ eq(true, exec_lua("return vim.tbl_isarray({1, 2, nil, 4})"))
+ eq(true, exec_lua("return vim.tbl_isarray({nil, 2, 3, 4})"))
+ eq(false, exec_lua("return vim.tbl_isarray({1, [1.5]=2, [3]=3})"))
+ end)
+
it('vim.tbl_islist', function()
eq(true, exec_lua("return vim.tbl_islist({})"))
eq(false, exec_lua("return vim.tbl_islist(vim.empty_dict())"))
@@ -496,6 +545,9 @@ describe('lua stdlib', function()
eq(false, exec_lua("return vim.tbl_islist({1, a='hello', b='baz'})"))
eq(false, exec_lua("return vim.tbl_islist({a='hello', b='baz', 1})"))
eq(false, exec_lua("return vim.tbl_islist({1, 2, nil, a='hello'})"))
+ eq(false, exec_lua("return vim.tbl_islist({1, 2, nil, 4})"))
+ eq(false, exec_lua("return vim.tbl_islist({nil, 2, 3, 4})"))
+ eq(false, exec_lua("return vim.tbl_islist({1, [1.5]=2, [3]=3})"))
end)
it('vim.tbl_isempty', function()
@@ -780,7 +832,7 @@ describe('lua stdlib', function()
it('vim.call, vim.fn', function()
eq(true, exec_lua([[return vim.call('sin', 0.0) == 0.0 ]]))
eq(true, exec_lua([[return vim.fn.sin(0.0) == 0.0 ]]))
- -- compat: nvim_call_function uses "special" value for vimL float
+ -- compat: nvim_call_function uses "special" value for Vimscript float
eq(false, exec_lua([[return vim.api.nvim_call_function('sin', {0.0}) == 0.0 ]]))
exec([[
@@ -831,7 +883,7 @@ describe('lua stdlib', function()
it('vim.fn is allowed in "fast" context by some functions #18306', function()
exec_lua([[
- local timer = vim.loop.new_timer()
+ local timer = vim.uv.new_timer()
timer:start(0, 0, function()
timer:close()
assert(vim.in_fast_event())
@@ -897,7 +949,7 @@ describe('lua stdlib', function()
})
screen:attach()
exec_lua([[
- timer = vim.loop.new_timer()
+ timer = vim.uv.new_timer()
timer:start(20, 0, function ()
-- notify ok (executed later when safe)
vim.rpcnotify(chan, 'nvim_set_var', 'yy', {3, vim.NIL})
@@ -1440,14 +1492,68 @@ describe('lua stdlib', function()
eq(NIL, funcs.luaeval "vim.v.null")
matches([[attempt to index .* nil value]],
pcall_err(exec_lua, 'return vim.v[0].progpath'))
+ eq('Key is read-only: count', pcall_err(exec_lua, [[vim.v.count = 42]]))
+ eq('Dictionary is locked', pcall_err(exec_lua, [[vim.v.nosuchvar = 42]]))
+ eq('Key is fixed: errmsg', pcall_err(exec_lua, [[vim.v.errmsg = nil]]))
+ exec_lua([[vim.v.errmsg = 'set by Lua']])
+ eq('set by Lua', eval('v:errmsg'))
+ exec_lua([[vim.v.errmsg = 42]])
+ eq('42', eval('v:errmsg'))
+ exec_lua([[vim.v.oldfiles = { 'one', 'two' }]])
+ eq({ 'one', 'two' }, eval('v:oldfiles'))
+ exec_lua([[vim.v.oldfiles = {}]])
+ eq({}, eval('v:oldfiles'))
+ eq('Setting v:oldfiles to value with wrong type', pcall_err(exec_lua, [[vim.v.oldfiles = 'a']]))
+ eq({}, eval('v:oldfiles'))
+
+ feed('i foo foo foo<Esc>0/foo<CR>')
+ eq({1, 1}, meths.win_get_cursor(0))
+ eq(1, eval('v:searchforward'))
+ feed('n')
+ eq({1, 5}, meths.win_get_cursor(0))
+ exec_lua([[vim.v.searchforward = 0]])
+ eq(0, eval('v:searchforward'))
+ feed('n')
+ eq({1, 1}, meths.win_get_cursor(0))
+ exec_lua([[vim.v.searchforward = 1]])
+ eq(1, eval('v:searchforward'))
+ feed('n')
+ eq({1, 5}, meths.win_get_cursor(0))
+
+ local screen = Screen.new(60, 3)
+ screen:set_default_attr_ids({
+ [0] = {bold = true, foreground = Screen.colors.Blue},
+ [1] = {background = Screen.colors.Yellow},
+ })
+ screen:attach()
+ eq(1, eval('v:hlsearch'))
+ screen:expect{grid=[[
+ {1:foo} {1:^foo} {1:foo} |
+ {0:~ }|
+ |
+ ]]}
+ exec_lua([[vim.v.hlsearch = 0]])
+ eq(0, eval('v:hlsearch'))
+ screen:expect{grid=[[
+ foo ^foo foo |
+ {0:~ }|
+ |
+ ]]}
+ exec_lua([[vim.v.hlsearch = 1]])
+ eq(1, eval('v:hlsearch'))
+ screen:expect{grid=[[
+ {1:foo} {1:^foo} {1:foo} |
+ {0:~ }|
+ |
+ ]]}
end)
it('vim.bo', function()
eq('', funcs.luaeval "vim.bo.filetype")
exec_lua [[
- vim.api.nvim_buf_set_option(0, "filetype", "markdown")
+ vim.api.nvim_set_option_value("filetype", "markdown", {})
BUF = vim.api.nvim_create_buf(false, true)
- vim.api.nvim_buf_set_option(BUF, "modifiable", false)
+ vim.api.nvim_set_option_value("modifiable", false, {buf = BUF})
]]
eq(false, funcs.luaeval "vim.bo.modified")
eq('markdown', funcs.luaeval "vim.bo.filetype")
@@ -1458,9 +1564,9 @@ describe('lua stdlib', function()
]]
eq('', funcs.luaeval "vim.bo.filetype")
eq(true, funcs.luaeval "vim.bo[BUF].modifiable")
- matches("no such option: 'nosuchopt'$",
+ matches("Unknown option 'nosuchopt'$",
pcall_err(exec_lua, 'return vim.bo.nosuchopt'))
- matches("Expected lua string$",
+ matches("Expected Lua string$",
pcall_err(exec_lua, 'return vim.bo[0][0].autoread'))
matches("Invalid buffer id: %-1$",
pcall_err(exec_lua, 'return vim.bo[-1].filetype'))
@@ -1468,9 +1574,9 @@ describe('lua stdlib', function()
it('vim.wo', function()
exec_lua [[
- vim.api.nvim_win_set_option(0, "cole", 2)
+ vim.api.nvim_set_option_value("cole", 2, {})
vim.cmd "split"
- vim.api.nvim_win_set_option(0, "cole", 2)
+ vim.api.nvim_set_option_value("cole", 2, {})
]]
eq(2, funcs.luaeval "vim.wo.cole")
exec_lua [[
@@ -1479,10 +1585,8 @@ describe('lua stdlib', function()
eq(0, funcs.luaeval "vim.wo.cole")
eq(0, funcs.luaeval "vim.wo[0].cole")
eq(0, funcs.luaeval "vim.wo[1001].cole")
- matches("no such option: 'notanopt'$",
+ matches("Unknown option 'notanopt'$",
pcall_err(exec_lua, 'return vim.wo.notanopt'))
- matches("Expected lua string$",
- pcall_err(exec_lua, 'return vim.wo[0][0].list'))
matches("Invalid window id: %-1$",
pcall_err(exec_lua, 'return vim.wo[-1].list'))
eq(2, funcs.luaeval "vim.wo[1000].cole")
@@ -1497,6 +1601,11 @@ describe('lua stdlib', function()
eq(200, funcs.luaeval "vim.wo.scrolloff")
exec_lua [[vim.wo.scrolloff = -1]]
eq(100, funcs.luaeval "vim.wo.scrolloff")
+ exec_lua [[
+ vim.wo[0][0].scrolloff = 200
+ vim.cmd "enew"
+ ]]
+ eq(100, funcs.luaeval "vim.wo.scrolloff")
end)
describe('vim.opt', function()
@@ -1515,8 +1624,8 @@ describe('lua stdlib', function()
local result = exec_lua [[
local result = {}
- table.insert(result, vim.api.nvim_get_option('scrolloff'))
- table.insert(result, vim.api.nvim_win_get_option(0, 'scrolloff'))
+ table.insert(result, vim.api.nvim_get_option_value('scrolloff', {scope='global'}))
+ table.insert(result, vim.api.nvim_get_option_value('scrolloff', {win=0}))
return result
]]
@@ -1580,20 +1689,20 @@ describe('lua stdlib', function()
local result = {}
vim.opt.makeprg = "global-local"
- table.insert(result, vim.api.nvim_get_option('makeprg'))
- table.insert(result, vim.api.nvim_buf_get_option(0, 'makeprg'))
+ table.insert(result, vim.go.makeprg)
+ table.insert(result, vim.api.nvim_get_option_value('makeprg', {buf=0}))
vim.opt_local.mp = "only-local"
- table.insert(result, vim.api.nvim_get_option('makeprg'))
- table.insert(result, vim.api.nvim_buf_get_option(0, 'makeprg'))
+ table.insert(result, vim.go.makeprg)
+ table.insert(result, vim.api.nvim_get_option_value('makeprg', {buf=0}))
vim.opt_global.makeprg = "only-global"
- table.insert(result, vim.api.nvim_get_option('makeprg'))
- table.insert(result, vim.api.nvim_buf_get_option(0, 'makeprg'))
+ table.insert(result, vim.go.makeprg)
+ table.insert(result, vim.api.nvim_get_option_value('makeprg', {buf=0}))
vim.opt.makeprg = "global-local"
- table.insert(result, vim.api.nvim_get_option('makeprg'))
- table.insert(result, vim.api.nvim_buf_get_option(0, 'makeprg'))
+ table.insert(result, vim.go.makeprg)
+ table.insert(result, vim.api.nvim_get_option_value('makeprg', {buf=0}))
return result
]]
@@ -2122,7 +2231,7 @@ describe('lua stdlib', function()
it('can handle isfname ,,,', function()
local result = exec_lua [[
vim.opt.isfname = "a,b,,,c"
- return { vim.opt.isfname:get(), vim.api.nvim_get_option('isfname') }
+ return { vim.opt.isfname:get(), vim.go.isfname }
]]
eq({{",", "a", "b", "c"}, "a,b,,,c"}, result)
@@ -2132,7 +2241,7 @@ describe('lua stdlib', function()
it('can handle isfname ,^,,', function()
local result = exec_lua [[
vim.opt.isfname = "a,b,^,,c"
- return { vim.opt.isfname:get(), vim.api.nvim_get_option('isfname') }
+ return { vim.opt.isfname:get(), vim.go.isfname }
]]
eq({{"^,", "a", "b", "c"}, "a,b,^,,c"}, result)
@@ -2203,8 +2312,8 @@ describe('lua stdlib', function()
end)
end) -- vim.opt
- describe('opt_local', function()
- it('should be able to append to an array list type option', function()
+ describe('vim.opt_local', function()
+ it('appends into global value when changing local option value', function()
eq({ "foo,bar,baz,qux" }, exec_lua [[
local result = {}
@@ -2219,6 +2328,19 @@ describe('lua stdlib', function()
end)
end)
+ describe('vim.opt_global', function()
+ it('gets current global option value', function()
+ eq({ "yes" }, exec_lua [[
+ local result = {}
+
+ vim.cmd "setglobal signcolumn=yes"
+ table.insert(result, vim.opt_global.signcolumn:get())
+
+ return result
+ ]])
+ end)
+ end)
+
it('vim.cmd', function()
exec_lua [[
vim.cmd "autocmd BufNew * ++once lua BUF = vim.fn.expand('<abuf>')"
@@ -2269,17 +2391,36 @@ describe('lua stdlib', function()
describe('vim.region', function()
it('charwise', function()
- insert(helpers.dedent( [[
+ insert(dedent( [[
text tααt tααt text
text tαxt txtα tex
text tαxt tαxt
]]))
- eq({5,15}, exec_lua[[ return vim.region(0,{1,5},{1,14},'v',true)[1] ]])
+ eq({5,13}, exec_lua[[ return vim.region(0,{0,5},{0,13},'v',false)[0] ]])
+ eq({5,15}, exec_lua[[ return vim.region(0,{0,5},{0,13},'v',true)[0] ]])
+ eq({5,15}, exec_lua[[ return vim.region(0,{0,5},{0,14},'v',true)[0] ]])
+ eq({5,15}, exec_lua[[ return vim.region(0,{0,5},{0,15},'v',false)[0] ]])
+ eq({5,17}, exec_lua[[ return vim.region(0,{0,5},{0,15},'v',true)[0] ]])
+ eq({5,17}, exec_lua[[ return vim.region(0,{0,5},{0,16},'v',true)[0] ]])
+ eq({5,17}, exec_lua[[ return vim.region(0,{0,5},{0,17},'v',false)[0] ]])
+ eq({5,18}, exec_lua[[ return vim.region(0,{0,5},{0,17},'v',true)[0] ]])
end)
it('blockwise', function()
insert([[αα]])
eq({0,5}, exec_lua[[ return vim.region(0,{0,0},{0,4},'3',true)[0] ]])
end)
+ it('linewise', function()
+ insert(dedent( [[
+ text tααt tααt text
+ text tαxt txtα tex
+ text tαxt tαxt
+ ]]))
+ eq({0,-1}, exec_lua[[ return vim.region(0,{1,5},{1,14},'V',true)[1] ]])
+ end)
+ it('getpos() input', function()
+ insert('getpos')
+ eq({0,6}, exec_lua[[ return vim.region(0,{0,0},'.','v',true)[0] ]])
+ end)
end)
describe('vim.on_key', function()
@@ -2305,6 +2446,12 @@ describe('lua stdlib', function()
end)
it('allows removing on_key listeners', function()
+ -- Create some unused namespaces
+ meths.create_namespace('unused1')
+ meths.create_namespace('unused2')
+ meths.create_namespace('unused3')
+ meths.create_namespace('unused4')
+
insert([[hello world]])
exec_lua [[
@@ -2420,13 +2567,12 @@ describe('lua stdlib', function()
]])
end)
-
it('should not block other events', function()
eq({time = true, wait_result = true}, exec_lua[[
start_time = get_time()
vim.g.timer_result = false
- timer = vim.loop.new_timer()
+ timer = vim.uv.new_timer()
timer:start(100, 0, vim.schedule_wrap(function()
vim.g.timer_result = true
end))
@@ -2448,7 +2594,7 @@ describe('lua stdlib', function()
start_time = get_time()
vim.g.timer_result = false
- timer = vim.loop.new_timer()
+ timer = vim.uv.new_timer()
timer:start(100, 0, vim.schedule_wrap(function()
vim.g.timer_result = true
end))
@@ -2462,6 +2608,7 @@ describe('lua stdlib', function()
}
]])
end)
+
it('should work with vim.defer_fn', function()
eq({time = true, wait_result = true}, exec_lua[[
start_time = get_time()
@@ -2491,17 +2638,17 @@ describe('lua stdlib', function()
it('should allow waiting with no callback, explicit', function()
eq(true, exec_lua [[
- local start_time = vim.loop.hrtime()
+ local start_time = vim.uv.hrtime()
vim.wait(50, nil)
- return vim.loop.hrtime() - start_time > 25000
+ return vim.uv.hrtime() - start_time > 25000
]])
end)
it('should allow waiting with no callback, implicit', function()
eq(true, exec_lua [[
- local start_time = vim.loop.hrtime()
+ local start_time = vim.uv.hrtime()
vim.wait(50)
- return vim.loop.hrtime() - start_time > 25000
+ return vim.uv.hrtime() - start_time > 25000
]])
end)
@@ -2623,6 +2770,23 @@ describe('lua stdlib', function()
eq({'notification', 'wait', {-2}}, next_msg(500))
end)
end)
+
+ it('should not run 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()
+ timer:close()
+ vim.wait(100, function() end)
+ end)
+ ]])
+ screen:expect({
+ any = pesc('E5560: vim.wait must not be called in a lua loop callback'),
+ })
+ feed('<CR>')
+ assert_alive()
+ end)
end)
it('vim.notify_once', function()
@@ -2679,14 +2843,14 @@ describe('lua stdlib', function()
describe('vim.api.nvim_buf_call', function()
it('can access buf options', function()
- local buf1 = meths.get_current_buf()
+ local buf1 = meths.get_current_buf().id
local buf2 = exec_lua [[
buf2 = vim.api.nvim_create_buf(false, true)
return buf2
]]
- eq(false, meths.buf_get_option(buf1, 'autoindent'))
- eq(false, meths.buf_get_option(buf2, 'autoindent'))
+ eq(false, meths.get_option_value('autoindent', {buf=buf1}))
+ eq(false, meths.get_option_value('autoindent', {buf=buf2}))
local val = exec_lua [[
return vim.api.nvim_buf_call(buf2, function()
@@ -2695,20 +2859,20 @@ describe('lua stdlib', function()
end)
]]
- eq(false, meths.buf_get_option(buf1, 'autoindent'))
- eq(true, meths.buf_get_option(buf2, 'autoindent'))
- eq(buf1, meths.get_current_buf())
+ eq(false, meths.get_option_value('autoindent', {buf=buf1}))
+ eq(true, meths.get_option_value('autoindent', {buf=buf2}))
+ eq(buf1, meths.get_current_buf().id)
eq(buf2, val)
end)
it('does not cause ml_get errors with invalid visual selection', function()
-- Should be fixed by vim-patch:8.2.4028.
exec_lua [[
- local a = vim.api
- local t = function(s) return a.nvim_replace_termcodes(s, true, true, true) end
- a.nvim_buf_set_lines(0, 0, -1, true, {"a", "b", "c"})
- a.nvim_feedkeys(t "G<C-V>", "txn", false)
- a.nvim_buf_call(a.nvim_create_buf(false, true), function() vim.cmd "redraw" end)
+ local api = vim.api
+ local t = function(s) return api.nvim_replace_termcodes(s, true, true, true) end
+ api.nvim_buf_set_lines(0, 0, -1, true, {"a", "b", "c"})
+ api.nvim_feedkeys(t "G<C-V>", "txn", false)
+ api.nvim_buf_call(api.nvim_create_buf(false, true), function() vim.cmd "redraw" end)
]]
end)
@@ -2716,10 +2880,10 @@ describe('lua stdlib', function()
eq(true, exec_lua([[
local function scratch_buf_call(fn)
local buf = vim.api.nvim_create_buf(false, true)
- vim.api.nvim_buf_set_option(buf, 'cindent', true)
+ vim.api.nvim_set_option_value('cindent', true, {buf = buf})
return vim.api.nvim_buf_call(buf, function()
return vim.api.nvim_get_current_buf() == buf
- and vim.api.nvim_buf_get_option(buf, 'cindent')
+ and vim.api.nvim_get_option_value('cindent', {buf = buf})
and fn()
end) and vim.api.nvim_buf_delete(buf, {}) == nil
end
@@ -2756,7 +2920,7 @@ describe('lua stdlib', function()
describe('vim.api.nvim_win_call', function()
it('can access window options', function()
command('vsplit')
- local win1 = meths.get_current_win()
+ local win1 = meths.get_current_win().id
command('wincmd w')
local win2 = exec_lua [[
win2 = vim.api.nvim_get_current_win()
@@ -2764,8 +2928,8 @@ describe('lua stdlib', function()
]]
command('wincmd p')
- eq('', meths.win_get_option(win1, 'winhighlight'))
- eq('', meths.win_get_option(win2, 'winhighlight'))
+ eq('', meths.get_option_value('winhighlight', {win=win1}))
+ eq('', meths.get_option_value('winhighlight', {win=win2}))
local val = exec_lua [[
return vim.api.nvim_win_call(win2, function()
@@ -2774,38 +2938,38 @@ describe('lua stdlib', function()
end)
]]
- eq('', meths.win_get_option(win1, 'winhighlight'))
- eq('Normal:Normal', meths.win_get_option(win2, 'winhighlight'))
- eq(win1, meths.get_current_win())
+ eq('', meths.get_option_value('winhighlight', {win=win1}))
+ eq('Normal:Normal', meths.get_option_value('winhighlight', {win=win2}))
+ eq(win1, meths.get_current_win().id)
eq(win2, val)
end)
it('does not cause ml_get errors with invalid visual selection', function()
-- Add lines to the current buffer and make another window looking into an empty buffer.
exec_lua [[
- _G.a = vim.api
- _G.t = function(s) return a.nvim_replace_termcodes(s, true, true, true) end
- _G.win_lines = a.nvim_get_current_win()
+ _G.api = vim.api
+ _G.t = function(s) return api.nvim_replace_termcodes(s, true, true, true) end
+ _G.win_lines = api.nvim_get_current_win()
vim.cmd "new"
- _G.win_empty = a.nvim_get_current_win()
- a.nvim_set_current_win(win_lines)
- a.nvim_buf_set_lines(0, 0, -1, true, {"a", "b", "c"})
+ _G.win_empty = api.nvim_get_current_win()
+ api.nvim_set_current_win(win_lines)
+ api.nvim_buf_set_lines(0, 0, -1, true, {"a", "b", "c"})
]]
-- Start Visual in current window, redraw in other window with fewer lines.
-- Should be fixed by vim-patch:8.2.4018.
exec_lua [[
- a.nvim_feedkeys(t "G<C-V>", "txn", false)
- a.nvim_win_call(win_empty, function() vim.cmd "redraw" end)
+ api.nvim_feedkeys(t "G<C-V>", "txn", false)
+ api.nvim_win_call(win_empty, function() vim.cmd "redraw" end)
]]
-- Start Visual in current window, extend it in other window with more lines.
-- Fixed for win_execute by vim-patch:8.2.4026, but nvim_win_call should also not be affected.
exec_lua [[
- a.nvim_feedkeys(t "<Esc>gg", "txn", false)
- a.nvim_set_current_win(win_empty)
- a.nvim_feedkeys(t "gg<C-V>", "txn", false)
- a.nvim_win_call(win_lines, function() a.nvim_feedkeys(t "G<C-V>", "txn", false) end)
+ api.nvim_feedkeys(t "<Esc>gg", "txn", false)
+ api.nvim_set_current_win(win_empty)
+ api.nvim_feedkeys(t "gg<C-V>", "txn", false)
+ api.nvim_win_call(win_lines, function() api.nvim_feedkeys(t "G<C-V>", "txn", false) end)
vim.cmd "redraw"
]]
end)
@@ -2819,14 +2983,14 @@ describe('lua stdlib', function()
}
screen:attach()
exec_lua [[
- _G.a = vim.api
+ _G.api = vim.api
vim.opt.ruler = true
local lines = {}
for i = 0, 499 do lines[#lines + 1] = tostring(i) end
- a.nvim_buf_set_lines(0, 0, -1, true, lines)
- a.nvim_win_set_cursor(0, {20, 0})
+ api.nvim_buf_set_lines(0, 0, -1, true, lines)
+ api.nvim_win_set_cursor(0, {20, 0})
vim.cmd "split"
- _G.win = a.nvim_get_current_win()
+ _G.win = api.nvim_get_current_win()
vim.cmd "wincmd w | redraw"
]]
screen:expect [[
@@ -2837,7 +3001,7 @@ describe('lua stdlib', function()
|
]]
exec_lua [[
- a.nvim_win_call(win, function() a.nvim_win_set_cursor(0, {100, 0}) end)
+ api.nvim_win_call(win, function() api.nvim_win_set_cursor(0, {100, 0}) end)
vim.cmd "redraw"
]]
screen:expect [[
@@ -2898,8 +3062,133 @@ describe('lua stdlib', function()
return a
]])
end)
+
+ it('accepts the key name', function()
+ eq({ b = 'b', c = 'c' }, exec_lua [[
+ local a = vim.defaulttable(function(k) return k end)
+ local _ = a.b
+ local _ = a.c
+ return a
+ ]])
+ end)
end)
+ it('vim.lua_omnifunc', function()
+ local screen = Screen.new(60,5)
+ screen:set_default_attr_ids {
+ [1] = {foreground = Screen.colors.Blue1, bold = true};
+ [2] = {background = Screen.colors.WebGray};
+ [3] = {background = Screen.colors.LightMagenta};
+ [4] = {bold = true};
+ [5] = {foreground = Screen.colors.SeaGreen, bold = true};
+ }
+ screen:attach()
+ command [[ set omnifunc=v:lua.vim.lua_omnifunc ]]
+
+ -- Note: the implementation is shared with lua command line completion.
+ -- More tests for completion in lua/command_line_completion_spec.lua
+ feed [[ivim.insp<c-x><c-o>]]
+ screen:expect{grid=[[
+ vim.inspect^ |
+ {1:~ }{2: inspect }{1: }|
+ {1:~ }{3: inspect_pos }{1: }|
+ {1:~ }|
+ {4:-- Omni completion (^O^N^P) }{5:match 1 of 2} |
+ ]]}
+ end)
+
+ it('vim.print', function()
+ -- vim.print() returns its args.
+ eq({42, 'abc', { a = { b = 77 }}},
+ exec_lua[[return {vim.print(42, 'abc', { a = { b = 77 }})}]])
+
+ -- vim.print() pretty-prints the args.
+ eq(dedent[[
+
+ 42
+ abc
+ {
+ a = {
+ b = 77
+ }
+ }]],
+ eval[[execute('lua vim.print(42, "abc", { a = { b = 77 }})')]])
+ end)
+
+ it('vim.F.if_nil', function()
+ local function if_nil(...)
+ return exec_lua([[
+ local args = {...}
+ local nargs = select('#', ...)
+ for i = 1, nargs do
+ if args[i] == vim.NIL then
+ args[i] = nil
+ end
+ end
+ return vim.F.if_nil(unpack(args, 1, nargs))
+ ]], ...)
+ end
+
+ local a = NIL
+ local b = NIL
+ local c = 42
+ local d = false
+ eq(42, if_nil(a, c))
+ eq(false, if_nil(d, b))
+ eq(42, if_nil(a, b, c, d))
+ eq(false, if_nil(d))
+ eq(false, if_nil(d, c))
+ eq(NIL, if_nil(a))
+ end)
+
+ it('lpeg', function()
+ eq(5, exec_lua [[
+ local m = vim.lpeg
+ return m.match(m.R'09'^1, '4504ab')
+ ]])
+
+ eq(4, exec_lua [[ return vim.re.match("abcde", '[a-c]+') ]])
+ end)
+
+ it("vim.ringbuf", function()
+ local results = exec_lua([[
+ local ringbuf = vim.ringbuf(3)
+ ringbuf:push("a") -- idx: 0
+ local peeka1 = ringbuf:peek()
+ local peeka2 = ringbuf:peek()
+ local popa = ringbuf:pop()
+ local popnil = ringbuf:pop()
+ ringbuf:push("a") -- idx: 1
+ ringbuf:push("b") -- idx: 2
+
+ -- doesn't read last added item, but uses separate read index
+ local pop_after_add_b = ringbuf:pop()
+
+ ringbuf:push("c") -- idx: 3 wraps around, overrides idx: 0 "a"
+ ringbuf:push("d") -- idx: 4 wraps around, overrides idx: 1 "a"
+ return {
+ peeka1 = peeka1,
+ peeka2 = peeka2,
+ pop1 = popa,
+ pop2 = popnil,
+ pop3 = ringbuf:pop(),
+ pop4 = ringbuf:pop(),
+ pop5 = ringbuf:pop(),
+ pop_after_add_b = pop_after_add_b,
+ }
+ ]])
+ local expected = {
+ peeka1 = "a",
+ peeka2 = "a",
+ pop1 = "a",
+ pop2 = nil,
+ pop3 = "b",
+ pop4 = "c",
+ pop5 = "d",
+ pop_after_add_b = "a",
+ }
+ eq(expected, results)
+ end)
end)
describe('lua: builtin modules', function()
diff --git a/test/functional/lua/watch_spec.lua b/test/functional/lua/watch_spec.lua
new file mode 100644
index 0000000000..cdcef08a1a
--- /dev/null
+++ b/test/functional/lua/watch_spec.lua
@@ -0,0 +1,178 @@
+local helpers = require('test.functional.helpers')(after_each)
+local eq = helpers.eq
+local exec_lua = helpers.exec_lua
+local clear = helpers.clear
+local is_os = helpers.is_os
+local skip = helpers.skip
+
+describe('vim._watch', function()
+ before_each(function()
+ clear()
+ end)
+
+ describe('watch', function()
+ it('detects file changes', function()
+ skip(is_os('bsd'), "Stopped working on bsd after 3ca967387c49c754561c3b11a574797504d40f38")
+ local root_dir = vim.uv.fs_mkdtemp(vim.fs.dirname(helpers.tmpname()) .. '/nvim_XXXXXXXXXX')
+
+ local result = exec_lua(
+ [[
+ local root_dir = ...
+
+ local events = {}
+
+ local expected_events = 0
+ local function wait_for_events()
+ assert(vim.wait(100, function() return #events == expected_events end), 'Timed out waiting for expected number of events. Current events seen so far: ' .. vim.inspect(events))
+ end
+
+ local stop = vim._watch.watch(root_dir, {}, function(path, change_type)
+ table.insert(events, { path = path, change_type = change_type })
+ end)
+
+ -- Only BSD seems to need some extra time for the watch to be ready to respond to events
+ if vim.fn.has('bsd') then
+ vim.wait(50)
+ end
+
+ local watched_path = root_dir .. '/file'
+ local watched, err = io.open(watched_path, 'w')
+ assert(not err, err)
+
+ expected_events = expected_events + 1
+ wait_for_events()
+
+ watched:close()
+ os.remove(watched_path)
+
+ expected_events = expected_events + 1
+ wait_for_events()
+
+ stop()
+ -- No events should come through anymore
+
+ local watched_path = root_dir .. '/file'
+ local watched, err = io.open(watched_path, 'w')
+ assert(not err, err)
+
+ vim.wait(50)
+
+ watched:close()
+ os.remove(watched_path)
+
+ vim.wait(50)
+
+ return events
+ ]],
+ root_dir
+ )
+
+ local expected = {
+ {
+ change_type = exec_lua([[return vim._watch.FileChangeType.Created]]),
+ path = root_dir .. '/file',
+ },
+ {
+ change_type = exec_lua([[return vim._watch.FileChangeType.Deleted]]),
+ path = root_dir .. '/file',
+ },
+ }
+
+ -- kqueue only reports events on the watched path itself, so creating a file within a
+ -- watched directory results in a "rename" libuv event on the directory.
+ if is_os('bsd') then
+ expected = {
+ {
+ change_type = exec_lua([[return vim._watch.FileChangeType.Created]]),
+ path = root_dir,
+ },
+ {
+ change_type = exec_lua([[return vim._watch.FileChangeType.Created]]),
+ path = root_dir,
+ },
+ }
+ end
+
+ eq(expected, result)
+ end)
+ end)
+
+ describe('poll', function()
+ it('detects file changes', function()
+ skip(is_os('bsd'), "bsd only reports rename on folders if file inside change")
+ local root_dir = vim.uv.fs_mkdtemp(vim.fs.dirname(helpers.tmpname()) .. '/nvim_XXXXXXXXXX')
+
+ local result = exec_lua(
+ [[
+ local root_dir = ...
+ local lpeg = vim.lpeg
+
+ local events = {}
+
+ local debounce = 100
+ local wait_ms = debounce + 200
+
+ local expected_events = 0
+ local function wait_for_events()
+ assert(vim.wait(wait_ms, function() return #events == expected_events end), 'Timed out waiting for expected number of events. Current events seen so far: ' .. vim.inspect(events))
+ end
+
+ local incl = lpeg.P(root_dir) * lpeg.P("/file")^-1
+ local excl = lpeg.P(root_dir..'/file.unwatched')
+ local stop = vim._watch.poll(root_dir, {
+ debounce = debounce,
+ include_pattern = incl,
+ exclude_pattern = excl,
+ }, function(path, change_type)
+ table.insert(events, { path = path, change_type = change_type })
+ end)
+
+ local watched_path = root_dir .. '/file'
+ local watched, err = io.open(watched_path, 'w')
+ assert(not err, err)
+ local unwatched_path = root_dir .. '/file.unwatched'
+ local unwatched, err = io.open(unwatched_path, 'w')
+ assert(not err, err)
+
+ expected_events = expected_events + 1
+ wait_for_events()
+
+ watched:close()
+ os.remove(watched_path)
+ unwatched:close()
+ os.remove(unwatched_path)
+
+ expected_events = expected_events + 1
+ wait_for_events()
+
+ stop()
+ -- No events should come through anymore
+
+ local watched_path = root_dir .. '/file'
+ local watched, err = io.open(watched_path, 'w')
+ assert(not err, err)
+
+ watched:close()
+ os.remove(watched_path)
+
+ return events
+ ]],
+ root_dir
+ )
+
+ local created = exec_lua([[return vim._watch.FileChangeType.Created]])
+ local deleted = exec_lua([[return vim._watch.FileChangeType.Deleted]])
+ local expected = {
+ {
+ change_type = created,
+ path = root_dir .. "/file",
+ },
+ {
+ change_type = deleted,
+ path = root_dir .. "/file",
+ }
+ }
+ eq(expected, result)
+ end)
+ end)
+end)