From e47622f26b40d88bb8582c391df30474a64a082c Mon Sep 17 00:00:00 2001 From: Jakob Schnitzer Date: Sun, 26 Mar 2017 13:20:44 +0200 Subject: options: setlocal should only set local value For 'iminsert' and 'imsearch' the global value was always changed. --- .../functional/options/setlocal_setglobal_spec.lua | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 test/functional/options/setlocal_setglobal_spec.lua (limited to 'test') diff --git a/test/functional/options/setlocal_setglobal_spec.lua b/test/functional/options/setlocal_setglobal_spec.lua new file mode 100644 index 0000000000..6902437403 --- /dev/null +++ b/test/functional/options/setlocal_setglobal_spec.lua @@ -0,0 +1,22 @@ +-- Tests for :setlocal and :setglobal + +local helpers = require('test.functional.helpers')(after_each) +local clear, execute, eval, eq, nvim = + helpers.clear, helpers.execute, helpers.eval, helpers.eq, helpers.nvim + +local function get_num_option_global(opt) + return nvim('command_output', 'setglobal ' .. opt .. '?'):match('%d+') +end + +describe(':setlocal', function() + before_each(clear) + + it('setlocal sets only local value', function() + eq('0', get_num_option_global('iminsert')) + execute('setlocal iminsert=1') + eq('0', get_num_option_global('iminsert')) + eq('0', get_num_option_global('imsearch')) + execute('setlocal imsearch=1') + eq('0', get_num_option_global('imsearch')) + end) +end) -- cgit From 628d0335b8e402008b3c03db9f5d2a109d5e8ef2 Mon Sep 17 00:00:00 2001 From: Jakob Schnitzer Date: Mon, 27 Mar 2017 09:51:14 +0200 Subject: options: add some tests --- test/functional/options/num_options_spec.lua | 42 ++++++++++++++++++++++ .../functional/options/setlocal_setglobal_spec.lua | 22 ------------ 2 files changed, 42 insertions(+), 22 deletions(-) create mode 100644 test/functional/options/num_options_spec.lua delete mode 100644 test/functional/options/setlocal_setglobal_spec.lua (limited to 'test') diff --git a/test/functional/options/num_options_spec.lua b/test/functional/options/num_options_spec.lua new file mode 100644 index 0000000000..c37bbeffc3 --- /dev/null +++ b/test/functional/options/num_options_spec.lua @@ -0,0 +1,42 @@ +-- Tests for :setlocal and :setglobal + +local helpers = require('test.functional.helpers')(after_each) +local clear, execute, eval, eq, nvim = + helpers.clear, helpers.execute, helpers.eval, helpers.eq, helpers.nvim + +local function get_num_option_global(opt) + return nvim('command_output', 'setglobal ' .. opt .. '?'):match('%d+') +end + +local function should_fail(opt, value, errmsg) + execute('let v:errmsg = ""') + execute('setglobal ' .. opt .. '=' .. value) + eq(errmsg, eval("v:errmsg"):match("E%d*:")) + execute('let v:errmsg = ""') + execute('setlocal ' .. opt .. '=' .. value) + eq(errmsg, eval("v:errmsg"):match("E%d*:")) + execute('let v:errmsg = ""') +end + +describe(':setlocal', function() + before_each(clear) + + it('setlocal sets only local value', function() + eq('0', get_num_option_global('iminsert')) + execute('setlocal iminsert=1') + eq('0', get_num_option_global('iminsert')) + eq('0', get_num_option_global('imsearch')) + execute('setlocal imsearch=1') + eq('0', get_num_option_global('imsearch')) + end) +end) + +describe(':set validation', function() + before_each(clear) + + it('setlocal and setglobal validate values', function() + should_fail('shiftwidth', -10, 'E487') + should_fail('tabstop', -10, 'E487') + should_fail('winheight', -10, 'E487') + end) +end) diff --git a/test/functional/options/setlocal_setglobal_spec.lua b/test/functional/options/setlocal_setglobal_spec.lua deleted file mode 100644 index 6902437403..0000000000 --- a/test/functional/options/setlocal_setglobal_spec.lua +++ /dev/null @@ -1,22 +0,0 @@ --- Tests for :setlocal and :setglobal - -local helpers = require('test.functional.helpers')(after_each) -local clear, execute, eval, eq, nvim = - helpers.clear, helpers.execute, helpers.eval, helpers.eq, helpers.nvim - -local function get_num_option_global(opt) - return nvim('command_output', 'setglobal ' .. opt .. '?'):match('%d+') -end - -describe(':setlocal', function() - before_each(clear) - - it('setlocal sets only local value', function() - eq('0', get_num_option_global('iminsert')) - execute('setlocal iminsert=1') - eq('0', get_num_option_global('iminsert')) - eq('0', get_num_option_global('imsearch')) - execute('setlocal imsearch=1') - eq('0', get_num_option_global('imsearch')) - end) -end) -- cgit From 44f039a1c8bf1a3111825f2d3385730e31536d9a Mon Sep 17 00:00:00 2001 From: Jakob Schnitzer Date: Mon, 27 Mar 2017 19:50:04 +0200 Subject: options: fix setglobal for buf-local number options --- test/functional/options/num_options_spec.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'test') diff --git a/test/functional/options/num_options_spec.lua b/test/functional/options/num_options_spec.lua index c37bbeffc3..9b66d4f862 100644 --- a/test/functional/options/num_options_spec.lua +++ b/test/functional/options/num_options_spec.lua @@ -11,10 +11,10 @@ end local function should_fail(opt, value, errmsg) execute('let v:errmsg = ""') execute('setglobal ' .. opt .. '=' .. value) - eq(errmsg, eval("v:errmsg"):match("E%d*:")) + eq(errmsg, eval("v:errmsg"):match("E%d*")) execute('let v:errmsg = ""') execute('setlocal ' .. opt .. '=' .. value) - eq(errmsg, eval("v:errmsg"):match("E%d*:")) + eq(errmsg, eval("v:errmsg"):match("E%d*")) execute('let v:errmsg = ""') end @@ -37,6 +37,6 @@ describe(':set validation', function() it('setlocal and setglobal validate values', function() should_fail('shiftwidth', -10, 'E487') should_fail('tabstop', -10, 'E487') - should_fail('winheight', -10, 'E487') + should_fail('winheight', -10, 'E591') end) end) -- cgit From db095f65636664afb4b09a3920571bf0565c7763 Mon Sep 17 00:00:00 2001 From: Jakob Schnitzer Date: Tue, 28 Mar 2017 16:17:53 +0200 Subject: options: more tests; check first set later; stricter validation --- test/functional/options/num_options_spec.lua | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) (limited to 'test') diff --git a/test/functional/options/num_options_spec.lua b/test/functional/options/num_options_spec.lua index 9b66d4f862..d0b63d3f91 100644 --- a/test/functional/options/num_options_spec.lua +++ b/test/functional/options/num_options_spec.lua @@ -37,6 +37,25 @@ describe(':set validation', function() it('setlocal and setglobal validate values', function() should_fail('shiftwidth', -10, 'E487') should_fail('tabstop', -10, 'E487') - should_fail('winheight', -10, 'E591') + should_fail('winheight', -10, 'E487') + should_fail('helpheight', -10, 'E487') + should_fail('maxcombine', 10, 'E474') + should_fail('history', 1000000, 'E474') + should_fail('regexpengine', 3, 'E474') + + should_fail('foldlevel', -5, 'E487') + should_fail('foldcolumn', 100, 'E474') + should_fail('conceallevel', 4, 'E474') + should_fail('numberwidth', 20, 'E474') + end) + + it('set wmh/wh wmw/wiw checks', function() + execute('set winheight=2') + execute('set winminheight=3') + eq('E591', eval("v:errmsg"):match("E%d*")) + + execute('set winwidth=2') + execute('set winminwidth=3') + eq('E592', eval("v:errmsg"):match("E%d*")) end) end) -- cgit From 8a55f9b1c8bab2cf22e266d7e1eecde85119d75d Mon Sep 17 00:00:00 2001 From: Jakob Schnitzer Date: Fri, 31 Mar 2017 18:30:06 +0200 Subject: update for changes in master; fix 'window'; tests --- test/functional/options/num_options_spec.lua | 57 +++++++++++++++++++++------- 1 file changed, 43 insertions(+), 14 deletions(-) (limited to 'test') diff --git a/test/functional/options/num_options_spec.lua b/test/functional/options/num_options_spec.lua index d0b63d3f91..620e758141 100644 --- a/test/functional/options/num_options_spec.lua +++ b/test/functional/options/num_options_spec.lua @@ -1,12 +1,8 @@ -- Tests for :setlocal and :setglobal local helpers = require('test.functional.helpers')(after_each) -local clear, execute, eval, eq, nvim = - helpers.clear, helpers.execute, helpers.eval, helpers.eq, helpers.nvim - -local function get_num_option_global(opt) - return nvim('command_output', 'setglobal ' .. opt .. '?'):match('%d+') -end +local clear, execute, eval, eq, meths = + helpers.clear, helpers.execute, helpers.eval, helpers.eq, helpers.meths local function should_fail(opt, value, errmsg) execute('let v:errmsg = ""') @@ -18,16 +14,23 @@ local function should_fail(opt, value, errmsg) execute('let v:errmsg = ""') end +local function should_succeed(opt, value) + execute('setglobal ' .. opt .. '=' .. value) + eq('', eval("v:errmsg")) + execute('setlocal ' .. opt .. '=' .. value) + eq('', eval("v:errmsg")) +end + describe(':setlocal', function() before_each(clear) it('setlocal sets only local value', function() - eq('0', get_num_option_global('iminsert')) + eq(0, meths.get_option('iminsert')) execute('setlocal iminsert=1') - eq('0', get_num_option_global('iminsert')) - eq('0', get_num_option_global('imsearch')) + eq(0, meths.get_option('iminsert')) + eq(0, meths.get_option('imsearch')) execute('setlocal imsearch=1') - eq('0', get_num_option_global('imsearch')) + eq(0, meths.get_option('imsearch')) end) end) @@ -36,17 +39,43 @@ describe(':set validation', function() it('setlocal and setglobal validate values', function() should_fail('shiftwidth', -10, 'E487') + should_succeed('shiftwidth', 0) should_fail('tabstop', -10, 'E487') should_fail('winheight', -10, 'E487') - should_fail('helpheight', -10, 'E487') - should_fail('maxcombine', 10, 'E474') + should_fail('winheight', 0, 'E487') + should_fail('winminheight', -1, 'E487') + should_succeed('winminheight', 0) + should_fail('winwidth', 0, 'E487') + should_fail('helpheight', -1, 'E487') + should_fail('maxcombine', 7, 'E474') + should_fail('iminsert', 3, 'E474') + should_fail('imsearch', 3, 'E474') + should_fail('titlelen', -1, 'E487') + should_fail('cmdheight', 0, 'E487') + should_fail('updatecount', -1, 'E487') + should_fail('textwidth', -1, 'E487') + should_fail('tabstop', 0, 'E487') + should_fail('timeoutlen', -1, 'E487') should_fail('history', 1000000, 'E474') + should_fail('regexpengine', -1, 'E474') should_fail('regexpengine', 3, 'E474') + should_succeed('regexpengine', 2) + should_fail('report', -1, 'E487') + should_succeed('report', 0) + should_fail('scrolloff', -1, 'E49') + should_fail('sidescrolloff', -1, 'E487') + should_fail('sidescroll', -1, 'E487') + should_fail('cmdwinheight', 0, 'E487') + should_fail('updatetime', -1, 'E487') should_fail('foldlevel', -5, 'E487') - should_fail('foldcolumn', 100, 'E474') + should_fail('foldcolumn', 13, 'E474') should_fail('conceallevel', 4, 'E474') - should_fail('numberwidth', 20, 'E474') + should_fail('numberwidth', 11, 'E474') + should_fail('numberwidth', 0, 'E487') + + -- If smaller than one this one is set to 'lines'-1 + should_succeed('window', -10) end) it('set wmh/wh wmw/wiw checks', function() -- cgit From 4049492b6d7b8805686b14dbacb3b729abd03308 Mon Sep 17 00:00:00 2001 From: Jakob Schnitzer Date: Sat, 1 Apr 2017 12:26:58 +0200 Subject: also test set_option --- test/functional/options/num_options_spec.lua | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) (limited to 'test') diff --git a/test/functional/options/num_options_spec.lua b/test/functional/options/num_options_spec.lua index 620e758141..42af4cb9b8 100644 --- a/test/functional/options/num_options_spec.lua +++ b/test/functional/options/num_options_spec.lua @@ -5,19 +5,23 @@ local clear, execute, eval, eq, meths = helpers.clear, helpers.execute, helpers.eval, helpers.eq, helpers.meths local function should_fail(opt, value, errmsg) - execute('let v:errmsg = ""') execute('setglobal ' .. opt .. '=' .. value) eq(errmsg, eval("v:errmsg"):match("E%d*")) execute('let v:errmsg = ""') execute('setlocal ' .. opt .. '=' .. value) eq(errmsg, eval("v:errmsg"):match("E%d*")) execute('let v:errmsg = ""') + local status, err = pcall(meths.set_option, opt, value) + eq(status, false) + eq(errmsg, err:match("E%d*")) + eq('', eval("v:errmsg")) end local function should_succeed(opt, value) execute('setglobal ' .. opt .. '=' .. value) - eq('', eval("v:errmsg")) execute('setlocal ' .. opt .. '=' .. value) + meths.set_option(opt, value) + eq(value, meths.get_option(opt)) eq('', eval("v:errmsg")) end @@ -74,8 +78,11 @@ describe(':set validation', function() should_fail('numberwidth', 11, 'E474') should_fail('numberwidth', 0, 'E487') - -- If smaller than one this one is set to 'lines'-1 - should_succeed('window', -10) + -- If smaller than 1 this one is set to 'lines'-1 + execute('setglobal window=-10') + meths.set_option('window', -10) + eq(23, meths.get_option('window')) + eq('', eval("v:errmsg")) end) it('set wmh/wh wmw/wiw checks', function() -- cgit From 4403864da3c48412595d439f36458d1e6ccfc49f Mon Sep 17 00:00:00 2001 From: Jakob Schnitzer Date: Wed, 28 Jun 2017 20:21:44 +0200 Subject: update tests --- test/functional/options/num_options_spec.lua | 30 ++++++++++++++-------------- 1 file changed, 15 insertions(+), 15 deletions(-) (limited to 'test') diff --git a/test/functional/options/num_options_spec.lua b/test/functional/options/num_options_spec.lua index 42af4cb9b8..ed17ffdd3c 100644 --- a/test/functional/options/num_options_spec.lua +++ b/test/functional/options/num_options_spec.lua @@ -1,16 +1,16 @@ -- Tests for :setlocal and :setglobal local helpers = require('test.functional.helpers')(after_each) -local clear, execute, eval, eq, meths = - helpers.clear, helpers.execute, helpers.eval, helpers.eq, helpers.meths +local clear, feed_command, eval, eq, meths = + helpers.clear, helpers.feed_command, helpers.eval, helpers.eq, helpers.meths local function should_fail(opt, value, errmsg) - execute('setglobal ' .. opt .. '=' .. value) + feed_command('setglobal ' .. opt .. '=' .. value) eq(errmsg, eval("v:errmsg"):match("E%d*")) - execute('let v:errmsg = ""') - execute('setlocal ' .. opt .. '=' .. value) + feed_command('let v:errmsg = ""') + feed_command('setlocal ' .. opt .. '=' .. value) eq(errmsg, eval("v:errmsg"):match("E%d*")) - execute('let v:errmsg = ""') + feed_command('let v:errmsg = ""') local status, err = pcall(meths.set_option, opt, value) eq(status, false) eq(errmsg, err:match("E%d*")) @@ -18,8 +18,8 @@ local function should_fail(opt, value, errmsg) end local function should_succeed(opt, value) - execute('setglobal ' .. opt .. '=' .. value) - execute('setlocal ' .. opt .. '=' .. value) + feed_command('setglobal ' .. opt .. '=' .. value) + feed_command('setlocal ' .. opt .. '=' .. value) meths.set_option(opt, value) eq(value, meths.get_option(opt)) eq('', eval("v:errmsg")) @@ -30,10 +30,10 @@ describe(':setlocal', function() it('setlocal sets only local value', function() eq(0, meths.get_option('iminsert')) - execute('setlocal iminsert=1') + feed_command('setlocal iminsert=1') eq(0, meths.get_option('iminsert')) eq(0, meths.get_option('imsearch')) - execute('setlocal imsearch=1') + feed_command('setlocal imsearch=1') eq(0, meths.get_option('imsearch')) end) end) @@ -79,19 +79,19 @@ describe(':set validation', function() should_fail('numberwidth', 0, 'E487') -- If smaller than 1 this one is set to 'lines'-1 - execute('setglobal window=-10') + feed_command('setglobal window=-10') meths.set_option('window', -10) eq(23, meths.get_option('window')) eq('', eval("v:errmsg")) end) it('set wmh/wh wmw/wiw checks', function() - execute('set winheight=2') - execute('set winminheight=3') + feed_command('set winheight=2') + feed_command('set winminheight=3') eq('E591', eval("v:errmsg"):match("E%d*")) - execute('set winwidth=2') - execute('set winminwidth=3') + feed_command('set winwidth=2') + feed_command('set winminwidth=3') eq('E592', eval("v:errmsg"):match("E%d*")) end) end) -- cgit From 520c0b91a528e7943bc6163a65de7adde387869b Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 29 Sep 2017 00:57:23 +0300 Subject: test/helpers: Add format_string and format_luav First intended to provide %r functionality like in Python (and also support for %*.*s, but this was not checked), second adds nice table formatting for use in cases similar to screen:snapshot_util(). --- test/helpers.lua | 79 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) (limited to 'test') diff --git a/test/helpers.lua b/test/helpers.lua index 260f10002e..d5356416af 100644 --- a/test/helpers.lua +++ b/test/helpers.lua @@ -307,6 +307,83 @@ local function dedent(str, leave_indent) return str end +local SUBTBL = { + '\\000', '\\001', '\\002', '\\003', '\\004', + '\\005', '\\006', '\\007', '\\008', '\\t', + '\\n', '\\011', '\\012', '\\r', '\\014', + '\\015', '\\016', '\\017', '\\018', '\\019', + '\\020', '\\021', '\\022', '\\023', '\\024', + '\\025', '\\026', '\\027', '\\028', '\\029', + '\\030', '\\031', +} + +local format_luav + +format_luav = function(v, indent) + local linesep = '\n' + local next_indent = nil + if indent == nil then + indent = '' + linesep = '' + else + next_indent = indent .. ' ' + end + local ret = '' + if type(v) == 'string' then + ret = '\'' .. tostring(v):gsub('[\'\\]', '\\%0'):gsub('[%z\1-\31]', function(match) + return SUBTBL[match:byte()] + end) .. '\'' + elseif type(v) == 'table' then + local processed_keys = {} + ret = '{' .. linesep + for i, subv in ipairs(v) do + ret = ret .. (next_indent or '') .. format_luav(subv, next_indent) .. ',\n' + processed_keys[i] = true + end + for k, subv in pairs(v) do + if not processed_keys[k] then + if type(k) == 'string' and k:match('^[a-zA-Z_][a-zA-Z0-9_]*$') then + ret = ret .. next_indent .. k .. ' = ' + else + ret = ret .. next_indent .. '[' .. format_luav(k) .. '] = ' + end + ret = ret .. format_luav(subv, next_indent) .. ',\n' + end + end + ret = ret .. indent .. '}' + else + -- Not implemented yet + assert(false) + end + return ret +end + +local function format_string(fmt, ...) + local i = 0 + local args = {...} + local function getarg() + i = i + 1 + return args[i] + end + local ret = fmt:gsub('%%[0-9*]*%.?[0-9*]*[cdEefgGiouXxqsr%%]', function(match) + local subfmt = match:gsub('%*', function(match) + return getarg() + end) + local arg = nil + if subfmt:sub(-1) ~= '%' then + arg = getarg() + end + if subfmt:sub(-1) == 'r' then + -- %r is like %q, but it is supposed to single-quote strings and not + -- double-quote them, and also work not only for strings. + subfmt = subfmt:sub(1, -2) .. 's' + arg = format_luav(arg) + end + return subfmt:format(arg) + end) + return ret +end + return { eq = eq, neq = neq, @@ -323,4 +400,6 @@ return { shallowcopy = shallowcopy, concat_tables = concat_tables, dedent = dedent, + format_luav = format_luav, + format_string = format_string, } -- cgit From 190c8516f534499f89d610fc78a20c025386b2f2 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 26 Sep 2017 23:13:55 +0300 Subject: unittests: Add a way to print trace on regular error --- test/README.md | 3 +++ test/unit/helpers.lua | 9 ++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) (limited to 'test') diff --git a/test/README.md b/test/README.md index 01db5960cd..15041b74e8 100644 --- a/test/README.md +++ b/test/README.md @@ -110,3 +110,6 @@ disables tracing (the fastest, but you get no data if tests crash and there was no core dump generated), `1` or empty/undefined leaves only C function cals and returns in the trace (faster then recording everything), `2` records all function calls, returns and lua source lines exuecuted. + +`NVIM_TEST_TRACE_ON_ERROR` (U) (1): makes unit tests yield trace on error in +addition to regular error message. diff --git a/test/unit/helpers.lua b/test/unit/helpers.lua index 4b9f185156..a629fca9a2 100644 --- a/test/unit/helpers.lua +++ b/test/unit/helpers.lua @@ -698,7 +698,14 @@ local function check_child_err(rd) local len_s = sc.read(rd, 5) local len = tonumber(len_s) neq(0, len) - local err = sc.read(rd, len + 1) + local err = '' + if os.getenv('NVIM_TEST_TRACE_ON_ERROR') == '1' and #trace ~= 0 then + err = '\nTest failed, trace:\n' .. tracehelp + for _, traceline in ipairs(trace) do + err = err .. traceline + end + end + err = err .. sc.read(rd, len + 1) assert.just_fail(err) end -- cgit From 0300c4d10991fb6ce218d45c4fe6d71a73f07d62 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 20 Aug 2017 18:40:22 +0300 Subject: viml/expressions: Add lexer with some basic tests --- test/unit/viml/expressions/lexer_spec.lua | 337 ++++++++++++++++++++++++++++++ 1 file changed, 337 insertions(+) create mode 100644 test/unit/viml/expressions/lexer_spec.lua (limited to 'test') diff --git a/test/unit/viml/expressions/lexer_spec.lua b/test/unit/viml/expressions/lexer_spec.lua new file mode 100644 index 0000000000..bf5afe4eeb --- /dev/null +++ b/test/unit/viml/expressions/lexer_spec.lua @@ -0,0 +1,337 @@ +local helpers = require('test.unit.helpers')(after_each) +local itp = helpers.gen_itp(it) + +local child_call_once = helpers.child_call_once +local cimport = helpers.cimport +local ffi = helpers.ffi +local eq = helpers.eq + +local lib = cimport('./src/nvim/viml/parser/expressions.h') + +local eltkn_type_tab, eltkn_cmp_type_tab, ccs_tab, eltkn_mul_type_tab +local eltkn_opt_scope_tab +child_call_once(function() + eltkn_type_tab = { + [tonumber(lib.kExprLexInvalid)] = 'Invalid', + [tonumber(lib.kExprLexMissing)] = 'Missing', + [tonumber(lib.kExprLexSpacing)] = 'Spacing', + [tonumber(lib.kExprLexEOC)] = 'EOC', + + [tonumber(lib.kExprLexQuestion)] = 'Question', + [tonumber(lib.kExprLexColon)] = 'Colon', + [tonumber(lib.kExprLexOr)] = 'Or', + [tonumber(lib.kExprLexAnd)] = 'And', + [tonumber(lib.kExprLexComparison)] = 'Comparison', + [tonumber(lib.kExprLexPlus)] = 'Plus', + [tonumber(lib.kExprLexMinus)] = 'Minus', + [tonumber(lib.kExprLexDot)] = 'Dot', + [tonumber(lib.kExprLexMultiplication)] = 'Multiplication', + + [tonumber(lib.kExprLexNot)] = 'Not', + + [tonumber(lib.kExprLexNumber)] = 'Number', + [tonumber(lib.kExprLexSingleQuotedString)] = 'SingleQuotedString', + [tonumber(lib.kExprLexDoubleQuotedString)] = 'DoubleQuotedString', + [tonumber(lib.kExprLexOption)] = 'Option', + [tonumber(lib.kExprLexRegister)] = 'Register', + [tonumber(lib.kExprLexEnv)] = 'Env', + [tonumber(lib.kExprLexPlainIdentifier)] = 'PlainIdentifier', + + [tonumber(lib.kExprLexBracket)] = 'Bracket', + [tonumber(lib.kExprLexFigureBrace)] = 'FigureBrace', + [tonumber(lib.kExprLexParenthesis)] = 'Parenthesis', + [tonumber(lib.kExprLexComma)] = 'Comma', + [tonumber(lib.kExprLexArrow)] = 'Arrow', + } + + eltkn_cmp_type_tab = { + [tonumber(lib.kExprLexCmpEqual)] = 'Equal', + [tonumber(lib.kExprLexCmpMatches)] = 'Matches', + [tonumber(lib.kExprLexCmpGreater)] = 'Greater', + [tonumber(lib.kExprLexCmpGreaterOrEqual)] = 'GreaterOrEqual', + [tonumber(lib.kExprLexCmpIdentical)] = 'Identical', + } + + ccs_tab = { + [tonumber(lib.kCCStrategyUseOption)] = 'UseOption', + [tonumber(lib.kCCStrategyMatchCase)] = 'MatchCase', + [tonumber(lib.kCCStrategyIgnoreCase)] = 'IgnoreCase', + } + + eltkn_mul_type_tab = { + [tonumber(lib.kExprLexMulMul)] = 'Mul', + [tonumber(lib.kExprLexMulDiv)] = 'Div', + [tonumber(lib.kExprLexMulMod)] = 'Mod', + } + + eltkn_opt_scope_tab = { + [tonumber(lib.kExprLexOptUnspecified)] = 'Unspecified', + [tonumber(lib.kExprLexOptGlobal)] = 'Global', + [tonumber(lib.kExprLexOptLocal)] = 'Local', + } +end) + +local function array_size(arr) + return ffi.sizeof(arr) / ffi.sizeof(arr[0]) +end + +local function kvi_size(kvi) + return array_size(kvi.init_array) +end + +local function kvi_init(kvi) + kvi.capacity = kvi_size(kvi) + kvi.items = kvi.init_array + return kvi +end + +local function kvi_new(ct) + return kvi_init(ffi.new(ct)) +end + +local function new_pstate(strings) + local strings_idx = 0 + local function get_line(_, ret_pline) + strings_idx = strings_idx + 1 + local str = strings[strings_idx] + local data, size + if type(str) == 'string' then + data = str + size = #str + elseif type(str) == 'nil' then + data = nil + size = 0 + elseif type(str) == 'table' then + data = str.data + size = str.size + elseif type(str) == 'function' then + data, size = str() + size = size or 0 + end + ret_pline.data = data + ret_pline.size = size + end + local pline_init = { + data = nil, + size = 0, + } + local state = { + reader = { + get_line = get_line, + cookie = nil, + }, + pos = { line = 0, col = 0 }, + colors = kvi_new('ParserHighlight'), + can_continuate = false, + } + local ret = ffi.new('ParserState', state) + kvi_init(ret.reader.lines) + kvi_init(ret.stack) + return ret +end + +local function conv_enum(etab, eval) + local n = tonumber(eval) + return etab[n] or n +end + +local function conv_eltkn_type(typ) + return conv_enum(eltkn_type_tab, typ) +end + +local function pline2lua(pline) + return ffi.string(pline.data, pline.size) +end + +local bracket_types = { + Bracket = true, + FigureBrace = true, + Parenthesis = true, +} + +local function intchar2lua(ch) + ch = tonumber(ch) + return (20 <= ch and ch < 127) and ('%c'):format(ch) or ch +end + +local function eltkn2lua(pstate, tkn) + local ret = { + type = conv_eltkn_type(tkn.type), + len = tonumber(tkn.len), + start = { line = tonumber(tkn.start.line), col = tonumber(tkn.start.col) }, + } + if ret.start.line < pstate.reader.lines.size then + local pstr = pline2lua(pstate.reader.lines.items[ret.start.line]) + if ret.start.col >= #pstr then + ret.error = 'start.col >= #pstr' + else + ret.str = pstr:sub(ret.start.col + 1, ret.start.col + ret.len) + if #(ret.str) ~= ret.len then + ret.error = '#str /= len' + end + end + else + ret.error = 'start.line >= pstate.reader.lines.size' + end + if ret.type == 'Comparison' then + ret.data = { + type = conv_enum(eltkn_cmp_type_tab, tkn.data.cmp.type), + ccs = conv_enum(ccs_tab, tkn.data.cmp.ccs), + inv = (not not tkn.data.cmp.inv), + } + elseif ret.type == 'Multiplication' then + ret.data = { type = conv_enum(eltkn_mul_type_tab, tkn.data.mul.type) } + elseif bracket_types[ret.type] then + ret.data = { closing = (not not tkn.data.brc.closing) } + elseif ret.type == 'Register' then + ret.data = { name = intchar2lua(tkn.data.reg.name) } + elseif (ret.type == 'SingleQuotedString' + or ret.type == 'DoubleQuotedString') then + ret.data = { closed = (not not tkn.data.str.closed) } + elseif ret.type == 'Option' then + ret.data = { + scope = conv_enum(eltkn_opt_scope_tab, tkn.data.opt.scope), + name = ffi.string(tkn.data.opt.name, tkn.data.opt.len), + } + elseif ret.type == 'PlainIdentifier' then + ret.data = { + scope = intchar2lua(tkn.data.var.scope), + autoload = (not not tkn.data.var.autoload), + } + elseif ret.type == 'Invalid' then + ret.data = { error = ffi.string(tkn.data.err.msg) } + end + return ret, tkn +end + +local function next_eltkn(pstate) + return eltkn2lua(pstate, lib.viml_pexpr_next_token(pstate, false)) +end + +describe('Expressions lexer', function() + itp('works (single tokens)', function() + local function singl_eltkn_test(typ, str, data) + local pstate = new_pstate({str}) + eq({data=data, len=#str, start={col=0, line=0}, str=str, type=typ}, + next_eltkn(pstate)) + if not ( + typ == 'Spacing' + or (typ == 'Register' and str == '@') + or ((typ == 'SingleQuotedString' or typ == 'DoubleQuotedString') + and not data.closed) + ) then + pstate = new_pstate({str .. ' '}) + eq({data=data, len=#str, start={col=0, line=0}, str=str, type=typ}, + next_eltkn(pstate)) + end + pstate = new_pstate({'x' .. str}) + pstate.pos.col = 1 + eq({data=data, len=#str, start={col=1, line=0}, str=str, type=typ}, + next_eltkn(pstate)) + end + singl_eltkn_test('Parenthesis', '(', {closing=false}) + singl_eltkn_test('Parenthesis', ')', {closing=true}) + singl_eltkn_test('Bracket', '[', {closing=false}) + singl_eltkn_test('Bracket', ']', {closing=true}) + singl_eltkn_test('FigureBrace', '{', {closing=false}) + singl_eltkn_test('FigureBrace', '}', {closing=true}) + singl_eltkn_test('Question', '?') + singl_eltkn_test('Colon', ':') + singl_eltkn_test('Dot', '.') + singl_eltkn_test('Plus', '+') + singl_eltkn_test('Comma', ',') + singl_eltkn_test('Multiplication', '*', {type='Mul'}) + singl_eltkn_test('Multiplication', '/', {type='Div'}) + singl_eltkn_test('Multiplication', '%', {type='Mod'}) + singl_eltkn_test('Spacing', ' \t\t \t\t') + singl_eltkn_test('Spacing', ' ') + singl_eltkn_test('Spacing', '\t') + singl_eltkn_test('Invalid', '\x01\x02\x03', {error='E15: Invalid control character present in input: %.*s'}) + singl_eltkn_test('Number', '0123') + singl_eltkn_test('Number', '0') + singl_eltkn_test('Number', '9') + singl_eltkn_test('Env', '$abc') + singl_eltkn_test('Env', '$') + singl_eltkn_test('PlainIdentifier', 'test', {autoload=false, scope=0}) + singl_eltkn_test('PlainIdentifier', '_test', {autoload=false, scope=0}) + singl_eltkn_test('PlainIdentifier', '_test_foo', {autoload=false, scope=0}) + singl_eltkn_test('PlainIdentifier', 't', {autoload=false, scope=0}) + singl_eltkn_test('PlainIdentifier', 'test5', {autoload=false, scope=0}) + singl_eltkn_test('PlainIdentifier', 't0', {autoload=false, scope=0}) + singl_eltkn_test('PlainIdentifier', 'test#var', {autoload=true, scope=0}) + singl_eltkn_test('PlainIdentifier', 'test#var#val###', {autoload=true, scope=0}) + singl_eltkn_test('PlainIdentifier', 't#####', {autoload=true, scope=0}) + local function scope_test(scope) + singl_eltkn_test('PlainIdentifier', scope .. ':test#var', {autoload=true, scope=scope}) + singl_eltkn_test('PlainIdentifier', scope .. ':', {autoload=false, scope=scope}) + end + scope_test('s') + scope_test('g') + scope_test('v') + scope_test('b') + scope_test('w') + scope_test('t') + scope_test('l') + scope_test('a') + local function comparison_test(op, inv_op, cmp_type) + singl_eltkn_test('Comparison', op, {type=cmp_type, inv=false, ccs='UseOption'}) + singl_eltkn_test('Comparison', inv_op, {type=cmp_type, inv=true, ccs='UseOption'}) + singl_eltkn_test('Comparison', op .. '#', {type=cmp_type, inv=false, ccs='MatchCase'}) + singl_eltkn_test('Comparison', inv_op .. '#', {type=cmp_type, inv=true, ccs='MatchCase'}) + singl_eltkn_test('Comparison', op .. '?', {type=cmp_type, inv=false, ccs='IgnoreCase'}) + singl_eltkn_test('Comparison', inv_op .. '?', {type=cmp_type, inv=true, ccs='IgnoreCase'}) + end + comparison_test('is', 'isnot', 'Identical') + singl_eltkn_test('And', '&&') + singl_eltkn_test('Invalid', '&', {error='E112: Option name missing: %.*s'}) + singl_eltkn_test('Option', '&opt', {scope='Unspecified', name='opt'}) + singl_eltkn_test('Option', '&t_xx', {scope='Unspecified', name='t_xx'}) + singl_eltkn_test('Option', '&t_\r\r', {scope='Unspecified', name='t_\r\r'}) + singl_eltkn_test('Option', '&t_\t\t', {scope='Unspecified', name='t_\t\t'}) + singl_eltkn_test('Option', '&t_ ', {scope='Unspecified', name='t_ '}) + singl_eltkn_test('Option', '&g:opt', {scope='Global', name='opt'}) + singl_eltkn_test('Option', '&l:opt', {scope='Local', name='opt'}) + singl_eltkn_test('Invalid', '&l:', {error='E112: Option name missing: %.*s'}) + singl_eltkn_test('Invalid', '&g:', {error='E112: Option name missing: %.*s'}) + singl_eltkn_test('Register', '@', {name=-1}) + singl_eltkn_test('Register', '@a', {name='a'}) + singl_eltkn_test('Register', '@\r', {name=13}) + singl_eltkn_test('Register', '@ ', {name=' '}) + singl_eltkn_test('Register', '@\t', {name=9}) + singl_eltkn_test('SingleQuotedString', '\'test', {closed=false}) + singl_eltkn_test('SingleQuotedString', '\'test\'', {closed=true}) + singl_eltkn_test('SingleQuotedString', '\'\'\'\'', {closed=true}) + singl_eltkn_test('SingleQuotedString', '\'x\'\'\'', {closed=true}) + singl_eltkn_test('SingleQuotedString', '\'\'\'x\'', {closed=true}) + singl_eltkn_test('SingleQuotedString', '\'\'\'', {closed=false}) + singl_eltkn_test('SingleQuotedString', '\'x\'\'', {closed=false}) + singl_eltkn_test('SingleQuotedString', '\'\'\'x', {closed=false}) + singl_eltkn_test('DoubleQuotedString', '"test', {closed=false}) + singl_eltkn_test('DoubleQuotedString', '"test"', {closed=true}) + singl_eltkn_test('DoubleQuotedString', '"\\""', {closed=true}) + singl_eltkn_test('DoubleQuotedString', '"x\\""', {closed=true}) + singl_eltkn_test('DoubleQuotedString', '"\\"x"', {closed=true}) + singl_eltkn_test('DoubleQuotedString', '"\\"', {closed=false}) + singl_eltkn_test('DoubleQuotedString', '"x\\"', {closed=false}) + singl_eltkn_test('DoubleQuotedString', '"\\"x', {closed=false}) + singl_eltkn_test('Not', '!') + singl_eltkn_test('Invalid', '=', {error='E15: Expected == or =~: %.*s'}) + comparison_test('==', '!=', 'Equal') + comparison_test('=~', '!~', 'Matches') + comparison_test('>', '<=', 'Greater') + comparison_test('>=', '<', 'GreaterOrEqual') + singl_eltkn_test('Minus', '-') + singl_eltkn_test('Arrow', '->') + singl_eltkn_test('EOC', '\0') + singl_eltkn_test('EOC', '\n') + singl_eltkn_test('Invalid', '~', {error='E15: Unidentified character: %.*s'}) + + local pstate = new_pstate({{data=nil, size=0}}) + eq({len=0, error='start.col >= #pstr', start={col=0, line=0}, type='EOC'}, + next_eltkn(pstate)) + + local pstate = new_pstate({''}) + eq({len=0, error='start.col >= #pstr', start={col=0, line=0}, type='EOC'}, + next_eltkn(pstate)) + end) +end) -- cgit From 2d8b9937deae3731143f4ea44e5c41715fe1363a Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 20 Aug 2017 20:40:59 +0300 Subject: viml/parser: Handle encoding conversions --- test/unit/viml/expressions/lexer_spec.lua | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'test') diff --git a/test/unit/viml/expressions/lexer_spec.lua b/test/unit/viml/expressions/lexer_spec.lua index bf5afe4eeb..c877ce4bbf 100644 --- a/test/unit/viml/expressions/lexer_spec.lua +++ b/test/unit/viml/expressions/lexer_spec.lua @@ -110,15 +110,22 @@ local function new_pstate(strings) end ret_pline.data = data ret_pline.size = size + ret_pline.allocated = false end local pline_init = { data = nil, size = 0, + allocated = false, } local state = { reader = { get_line = get_line, cookie = nil, + conv = { + vc_type = 0, + vc_factor = 1, + vc_fail = false, + }, }, pos = { line = 0, col = 0 }, colors = kvi_new('ParserHighlight'), -- cgit From 919223c23ae3c8c904f35e7d605b1cf14d44a5f0 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 3 Sep 2017 19:57:24 +0300 Subject: unittests: Move some functions into helpers modules --- test/unit/helpers.lua | 28 ++++++++ test/unit/viml/expressions/lexer_spec.lua | 104 +++--------------------------- test/unit/viml/helpers.lua | 97 ++++++++++++++++++++++++++++ 3 files changed, 135 insertions(+), 94 deletions(-) create mode 100644 test/unit/viml/helpers.lua (limited to 'test') diff --git a/test/unit/helpers.lua b/test/unit/helpers.lua index a629fca9a2..a5ca7b069b 100644 --- a/test/unit/helpers.lua +++ b/test/unit/helpers.lua @@ -760,6 +760,29 @@ end cimport('./src/nvim/types.h', './src/nvim/main.h', './src/nvim/os/time.h') +local function conv_enum(etab, eval) + local n = tonumber(eval) + return etab[n] or n +end + +local function array_size(arr) + return ffi.sizeof(arr) / ffi.sizeof(arr[0]) +end + +local function kvi_size(kvi) + return array_size(kvi.init_array) +end + +local function kvi_init(kvi) + kvi.capacity = kvi_size(kvi) + kvi.items = kvi.init_array + return kvi +end + +local function kvi_new(ct) + return kvi_init(ffi.new(ct)) +end + local module = { cimport = cimport, cppimport = cppimport, @@ -780,6 +803,11 @@ local module = { child_call_once = child_call_once, child_cleanup_once = child_cleanup_once, sc = sc, + conv_enum = conv_enum, + array_size = array_sive, + kvi_size = kvi_size, + kvi_init = kvi_init, + kvi_new = kvi_new, } return function() return module diff --git a/test/unit/viml/expressions/lexer_spec.lua b/test/unit/viml/expressions/lexer_spec.lua index c877ce4bbf..32182f650d 100644 --- a/test/unit/viml/expressions/lexer_spec.lua +++ b/test/unit/viml/expressions/lexer_spec.lua @@ -1,11 +1,18 @@ local helpers = require('test.unit.helpers')(after_each) +local viml_helpers = require('test.unit.viml.helpers') local itp = helpers.gen_itp(it) local child_call_once = helpers.child_call_once +local conv_enum = helpers.conv_enum local cimport = helpers.cimport local ffi = helpers.ffi local eq = helpers.eq +local pline2lua = viml_helpers.pline2lua +local new_pstate = viml_helpers.new_pstate +local intchar2lua = viml_helpers.intchar2lua +local pstate_set_str = viml_helpers.pstate_set_str + local lib = cimport('./src/nvim/viml/parser/expressions.h') local eltkn_type_tab, eltkn_cmp_type_tab, ccs_tab, eltkn_mul_type_tab @@ -71,114 +78,23 @@ child_call_once(function() } end) -local function array_size(arr) - return ffi.sizeof(arr) / ffi.sizeof(arr[0]) -end - -local function kvi_size(kvi) - return array_size(kvi.init_array) -end - -local function kvi_init(kvi) - kvi.capacity = kvi_size(kvi) - kvi.items = kvi.init_array - return kvi -end - -local function kvi_new(ct) - return kvi_init(ffi.new(ct)) -end - -local function new_pstate(strings) - local strings_idx = 0 - local function get_line(_, ret_pline) - strings_idx = strings_idx + 1 - local str = strings[strings_idx] - local data, size - if type(str) == 'string' then - data = str - size = #str - elseif type(str) == 'nil' then - data = nil - size = 0 - elseif type(str) == 'table' then - data = str.data - size = str.size - elseif type(str) == 'function' then - data, size = str() - size = size or 0 - end - ret_pline.data = data - ret_pline.size = size - ret_pline.allocated = false - end - local pline_init = { - data = nil, - size = 0, - allocated = false, - } - local state = { - reader = { - get_line = get_line, - cookie = nil, - conv = { - vc_type = 0, - vc_factor = 1, - vc_fail = false, - }, - }, - pos = { line = 0, col = 0 }, - colors = kvi_new('ParserHighlight'), - can_continuate = false, - } - local ret = ffi.new('ParserState', state) - kvi_init(ret.reader.lines) - kvi_init(ret.stack) - return ret -end - -local function conv_enum(etab, eval) - local n = tonumber(eval) - return etab[n] or n -end - local function conv_eltkn_type(typ) return conv_enum(eltkn_type_tab, typ) end -local function pline2lua(pline) - return ffi.string(pline.data, pline.size) -end - local bracket_types = { Bracket = true, FigureBrace = true, Parenthesis = true, } -local function intchar2lua(ch) - ch = tonumber(ch) - return (20 <= ch and ch < 127) and ('%c'):format(ch) or ch -end - local function eltkn2lua(pstate, tkn) local ret = { type = conv_eltkn_type(tkn.type), - len = tonumber(tkn.len), - start = { line = tonumber(tkn.start.line), col = tonumber(tkn.start.col) }, } - if ret.start.line < pstate.reader.lines.size then - local pstr = pline2lua(pstate.reader.lines.items[ret.start.line]) - if ret.start.col >= #pstr then - ret.error = 'start.col >= #pstr' - else - ret.str = pstr:sub(ret.start.col + 1, ret.start.col + ret.len) - if #(ret.str) ~= ret.len then - ret.error = '#str /= len' - end - end - else - ret.error = 'start.line >= pstate.reader.lines.size' + pstate_set_str(pstate, tkn.start, tkn.len, ret) + if not ret.error and (#(ret.str) ~= ret.len) then + ret.error = '#str /= len' end if ret.type == 'Comparison' then ret.data = { diff --git a/test/unit/viml/helpers.lua b/test/unit/viml/helpers.lua new file mode 100644 index 0000000000..2cb60499eb --- /dev/null +++ b/test/unit/viml/helpers.lua @@ -0,0 +1,97 @@ +local helpers = require('test.unit.helpers')(nil) + +local ffi = helpers.ffi +local kvi_new = helpers.kvi_new +local kvi_init = helpers.kvi_init + +local function new_pstate(strings) + local strings_idx = 0 + local function get_line(_, ret_pline) + strings_idx = strings_idx + 1 + local str = strings[strings_idx] + local data, size + if type(str) == 'string' then + data = str + size = #str + elseif type(str) == 'nil' then + data = nil + size = 0 + elseif type(str) == 'table' then + data = str.data + size = str.size + elseif type(str) == 'function' then + data, size = str() + size = size or 0 + end + ret_pline.data = data + ret_pline.size = size + ret_pline.allocated = false + end + local pline_init = { + data = nil, + size = 0, + allocated = false, + } + local state = { + reader = { + get_line = get_line, + cookie = nil, + conv = { + vc_type = 0, + vc_factor = 1, + vc_fail = false, + }, + }, + pos = { line = 0, col = 0 }, + colors = kvi_new('ParserHighlight'), + can_continuate = false, + } + local ret = ffi.new('ParserState', state) + kvi_init(ret.reader.lines) + kvi_init(ret.stack) + return ret +end + +local function intchar2lua(ch) + ch = tonumber(ch) + return (20 <= ch and ch < 127) and ('%c'):format(ch) or ch +end + +local function pline2lua(pline) + return ffi.string(pline.data, pline.size) +end + +local function pstate_str(pstate, start, len) + local str = nil + local err = nil + if start.line < pstate.reader.lines.size then + local pstr = pline2lua(pstate.reader.lines.items[start.line]) + if start.col >= #pstr then + err = 'start.col >= #pstr' + else + str = pstr:sub(tonumber(start.col) + 1, tonumber(start.col + len)) + end + else + err = 'start.line >= pstate.reader.lines.size' + end + return str, err +end + +local function pstate_set_str(pstate, start, len, ret) + ret = ret or {} + ret.start = { + line = tonumber(start.line), + col = tonumber(start.col) + } + ret.len = tonumber(len) + ret.str, ret.error = pstate_str(pstate, start, len) + return ret +end + +return { + pline2lua = pline2lua, + pstate_str = pstate_str, + new_pstate = new_pstate, + intchar2lua = intchar2lua, + pstate_set_str = pstate_set_str, +} -- cgit From 430e516d3ac1235c1ee3009a8a36089bf278440e Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 3 Sep 2017 21:58:16 +0300 Subject: viml/parser/expressions: Start creating expressions parser Currently supported nodes: - Register as it is one of the simplest value nodes (even numbers are not that simple with that dot handling). - Plus, both unary and binary. - Parenthesis, both nesting and calling. Note regarding unit tests: it stores data for AST in highlighting in strings in place of tables because luassert fails to do a good job at representing big tables. Squashing a bunch of data into a single string simply yields more readable result. --- test/unit/eval/helpers.lua | 5 +- test/unit/helpers.lua | 28 + test/unit/viml/expressions/parser_spec.lua | 887 +++++++++++++++++++++++++++++ 3 files changed, 916 insertions(+), 4 deletions(-) create mode 100644 test/unit/viml/expressions/parser_spec.lua (limited to 'test') diff --git a/test/unit/eval/helpers.lua b/test/unit/eval/helpers.lua index 5bc482216e..d7399182f7 100644 --- a/test/unit/eval/helpers.lua +++ b/test/unit/eval/helpers.lua @@ -1,5 +1,6 @@ local helpers = require('test.unit.helpers')(nil) +local ptr2key = helpers.ptr2key local cimport = helpers.cimport local to_cstr = helpers.to_cstr local ffi = helpers.ffi @@ -91,10 +92,6 @@ local function populate_partial(pt, lua_pt, processed) return pt end -local ptr2key = function(ptr) - return tostring(ptr) -end - local lst2tbl local dct2tbl diff --git a/test/unit/helpers.lua b/test/unit/helpers.lua index a5ca7b069b..d3d14a5ca2 100644 --- a/test/unit/helpers.lua +++ b/test/unit/helpers.lua @@ -783,6 +783,31 @@ local function kvi_new(ct) return kvi_init(ffi.new(ct)) end +local function make_enum_conv_tab(lib, values, skip_pref, set_cb) + child_call_once(function() + local ret = {} + for _, v in ipairs(values) do + local str_v = v + if v:sub(1, #skip_pref) == skip_pref then + str_v = v:sub(#skip_pref + 1) + end + ret[tonumber(lib[v])] = str_v + end + set_cb(ret) + end) +end + +local function ptr2addr(ptr) + return tonumber(ffi.cast('intptr_t', ffi.cast('void *', ptr))) +end + +local s = ffi.new('char[64]', {0}) + +local function ptr2key(ptr) + ffi.C.snprintf(s, ffi.sizeof(s), '%p', ffi.cast('void *', ptr)) + return ffi.string(s) +end + local module = { cimport = cimport, cppimport = cppimport, @@ -808,6 +833,9 @@ local module = { kvi_size = kvi_size, kvi_init = kvi_init, kvi_new = kvi_new, + make_enum_conv_tab = make_enum_conv_tab, + ptr2addr = ptr2addr, + ptr2key = ptr2key, } return function() return module diff --git a/test/unit/viml/expressions/parser_spec.lua b/test/unit/viml/expressions/parser_spec.lua new file mode 100644 index 0000000000..c4bb067391 --- /dev/null +++ b/test/unit/viml/expressions/parser_spec.lua @@ -0,0 +1,887 @@ +local helpers = require('test.unit.helpers')(after_each) +local viml_helpers = require('test.unit.viml.helpers') +local itp = helpers.gen_itp(it) + +local make_enum_conv_tab = helpers.make_enum_conv_tab +local child_call_once = helpers.child_call_once +local conv_enum = helpers.conv_enum +local ptr2key = helpers.ptr2key +local cimport = helpers.cimport +local ffi = helpers.ffi +local eq = helpers.eq + +local pline2lua = viml_helpers.pline2lua +local new_pstate = viml_helpers.new_pstate +local intchar2lua = viml_helpers.intchar2lua +local pstate_set_str = viml_helpers.pstate_set_str + +local lib = cimport('./src/nvim/viml/parser/expressions.h') + +local east_node_type_tab +make_enum_conv_tab(lib, { + 'kExprNodeMissing', + 'kExprNodeOpMissing', + 'kExprNodeTernary', + 'kExprNodeRegister', + 'kExprNodeSubscript', + 'kExprNodeListLiteral', + 'kExprNodeUnaryPlus', + 'kExprNodeBinaryPlus', + 'kExprNodeNested', + 'kExprNodeCall', + 'kExprNodePlainIdentifier', + 'kExprNodeComplexIdentifier', +}, 'kExprNode', function(ret) east_node_type_tab = ret end) + +local function conv_east_node_type(typ) + return conv_enum(east_node_type_tab, typ) +end + +local eastnodelist2lua + +local function eastnode2lua(pstate, eastnode, checked_nodes) + local key = ptr2key(eastnode) + if checked_nodes[key] then + checked_nodes[key].duplicate_key = key + return { duplicate = key } + end + local typ = conv_east_node_type(eastnode.type) + local ret = {} + checked_nodes[key] = ret + ret.children = eastnodelist2lua(pstate, eastnode.children, checked_nodes) + local str = pstate_set_str(pstate, eastnode.start, eastnode.len) + local ret_str + if str.error then + ret_str = 'error:' .. str.error + else + ret_str = ('%u:%u:%s'):format(str.start.line, str.start.col, str.str) + end + if typ == 'Register' then + typ = typ .. ('(name=%s)'):format( + tostring(intchar2lua(eastnode.data.reg.name))) + end + ret_str = typ .. ':' .. ret_str + local can_simplify = true + for k, v in pairs(ret) do + can_simplify = false + end + if can_simplify then + ret = ret_str + else + ret[1] = ret_str + end + return ret +end + +eastnodelist2lua = function(pstate, eastnode, checked_nodes) + local ret = {} + while eastnode ~= nil do + ret[#ret + 1] = eastnode2lua(pstate, eastnode, checked_nodes) + eastnode = eastnode.next + end + if #ret == 0 then + ret = nil + end + return ret +end + +local function east2lua(pstate, east) + local checked_nodes = {} + return { + err = (not east.correct) and { + msg = ffi.string(east.err.msg), + arg = ('%u:%s'):format( + tonumber(east.err.arg_len), + ffi.string(east.err.arg, east.err.arg_len)), + } or nil, + ast = eastnodelist2lua(pstate, east.root, checked_nodes), + } +end + +local function phl2lua(pstate) + local ret = {} + for i = 0, (tonumber(pstate.colors.size) - 1) do + local chunk = pstate.colors.items[i] + local chunk_tbl = pstate_set_str( + pstate, chunk.start, chunk.end_col - chunk.start.col, { + group = ffi.string(chunk.group), + }) + chunk_str = ('%s:%u:%u:%s'):format( + chunk_tbl.group, + chunk_tbl.start.line, + chunk_tbl.start.col, + chunk_tbl.str) + ret[i + 1] = chunk_str + end + return ret +end + +child_call_once(function() + assert:set_parameter('TableFormatLevel', 1000000) +end) + +describe('Expressions parser', function() + itp('works', function() + local function check_parsing(str, flags, exp_ast, exp_highlighting_fs) + local pstate = new_pstate({str}) + local east = lib.viml_pexpr_parse(pstate, flags) + local ast = east2lua(pstate, east) + eq(exp_ast, ast) + if exp_highlighting_fs then + local exp_highlighting = {} + local next_col = 0 + for i, h in ipairs(exp_highlighting_fs) do + exp_highlighting[i], next_col = h(next_col) + end + eq(exp_highlighting, phl2lua(pstate)) + end + end + local function hl(group, str, shift) + return function(next_col) + local col = next_col + (shift or 0) + return (('%s:%u:%u:%s'):format( + 'NVim' .. group, + 0, + col, + str)), (col + #str) + end + end + check_parsing('@a', 0, { + ast = { + 'Register(name=a):0:0:@a', + }, + }, { + hl('Register', '@a'), + }) + check_parsing('+@a', 0, { + ast = { + { + 'UnaryPlus:0:0:+', + children = { + 'Register(name=a):0:1:@a', + }, + }, + }, + }, { + hl('UnaryPlus', '+'), + hl('Register', '@a'), + }) + check_parsing('@a+@b', 0, { + ast = { + { + 'BinaryPlus:0:2:+', + children = { + 'Register(name=a):0:0:@a', + 'Register(name=b):0:3:@b', + }, + }, + }, + }, { + hl('Register', '@a'), + hl('BinaryPlus', '+'), + hl('Register', '@b'), + }) + check_parsing('@a+@b+@c', 0, { + ast = { + { + 'BinaryPlus:0:5:+', + children = { + { + 'BinaryPlus:0:2:+', + children = { + 'Register(name=a):0:0:@a', + 'Register(name=b):0:3:@b', + }, + }, + 'Register(name=c):0:6:@c', + }, + }, + }, + }, { + hl('Register', '@a'), + hl('BinaryPlus', '+'), + hl('Register', '@b'), + hl('BinaryPlus', '+'), + hl('Register', '@c'), + }) + check_parsing('+@a+@b', 0, { + ast = { + { + 'BinaryPlus:0:3:+', + children = { + { + 'UnaryPlus:0:0:+', + children = { + 'Register(name=a):0:1:@a', + }, + }, + 'Register(name=b):0:4:@b', + }, + }, + }, + }, { + hl('UnaryPlus', '+'), + hl('Register', '@a'), + hl('BinaryPlus', '+'), + hl('Register', '@b'), + }) + check_parsing('+@a++@b', 0, { + ast = { + { + 'BinaryPlus:0:3:+', + children = { + { + 'UnaryPlus:0:0:+', + children = { + 'Register(name=a):0:1:@a', + }, + }, + { + 'UnaryPlus:0:4:+', + children = { + 'Register(name=b):0:5:@b', + }, + }, + }, + }, + }, + }, { + hl('UnaryPlus', '+'), + hl('Register', '@a'), + hl('BinaryPlus', '+'), + hl('UnaryPlus', '+'), + hl('Register', '@b'), + }) + check_parsing('@a@b', 0, { + ast = { + { + 'OpMissing:0:2:', + children = { + 'Register(name=a):0:0:@a', + 'Register(name=b):0:2:@b', + }, + }, + }, + err = { + arg = '2:@b', + msg = 'E15: Missing operator: %.*s', + }, + }, { + hl('Register', '@a'), + hl('InvalidRegister', '@b'), + }) + check_parsing(' @a \t @b', 0, { + ast = { + { + 'OpMissing:0:3:', + children = { + 'Register(name=a):0:0: @a', + 'Register(name=b):0:3: \t @b', + }, + }, + }, + err = { + arg = '2:@b', + msg = 'E15: Missing operator: %.*s', + }, + }, { + hl('Register', '@a', 1), + hl('InvalidSpacing', ' \t '), + hl('Register', '@b'), + }) + check_parsing('+', 0, { + ast = { + 'UnaryPlus:0:0:+', + }, + err = { + arg = '0:', + msg = 'E15: Expected value: %.*s', + }, + }, { + hl('UnaryPlus', '+'), + }) + check_parsing(' +', 0, { + ast = { + 'UnaryPlus:0:0: +', + }, + err = { + arg = '0:', + msg = 'E15: Expected value: %.*s', + }, + }, { + hl('UnaryPlus', '+', 1), + }) + check_parsing('@a+ ', 0, { + ast = { + { + 'BinaryPlus:0:2:+', + children = { + 'Register(name=a):0:0:@a', + }, + }, + }, + err = { + arg = '0:', + msg = 'E15: Expected value: %.*s', + }, + }, { + hl('Register', '@a'), + hl('BinaryPlus', '+'), + }) + check_parsing('(@a)', 0, { + ast = { + { + 'Nested:0:0:(', + children = { + 'Register(name=a):0:1:@a', + }, + }, + }, + }, { + hl('NestingParenthesis', '('), + hl('Register', '@a'), + hl('NestingParenthesis', ')'), + }) + check_parsing('()', 0, { + ast = { + { + 'Nested:0:0:(', + children = { + 'Missing:0:1:', + }, + }, + }, + err = { + arg = '1:)', + msg = 'E15: Expected value: %.*s', + }, + }, { + hl('NestingParenthesis', '('), + hl('InvalidNestingParenthesis', ')'), + }) + check_parsing(')', 0, { + ast = { + { + 'Nested:0:0:', + children = { + 'Missing:0:0:', + }, + }, + }, + err = { + arg = '1:)', + msg = 'E15: Expected value: %.*s', + }, + }, { + hl('InvalidNestingParenthesis', ')'), + }) + check_parsing('+)', 0, { + ast = { + { + 'Nested:0:1:', + children = { + { + 'UnaryPlus:0:0:+', + children = { + 'Missing:0:1:', + }, + }, + }, + }, + }, + err = { + arg = '1:)', + msg = 'E15: Expected value: %.*s', + }, + }, { + hl('UnaryPlus', '+'), + hl('InvalidNestingParenthesis', ')'), + }) + check_parsing('+@a(@b)', 0, { + ast = { + { + 'UnaryPlus:0:0:+', + children = { + { + 'Call:0:3:(', + children = { + 'Register(name=a):0:1:@a', + 'Register(name=b):0:4:@b', + }, + }, + }, + }, + }, + }, { + hl('UnaryPlus', '+'), + hl('Register', '@a'), + hl('CallingParenthesis', '('), + hl('Register', '@b'), + hl('CallingParenthesis', ')'), + }) + check_parsing('@a+@b(@c)', 0, { + ast = { + { + 'BinaryPlus:0:2:+', + children = { + 'Register(name=a):0:0:@a', + { + 'Call:0:5:(', + children = { + 'Register(name=b):0:3:@b', + 'Register(name=c):0:6:@c', + }, + }, + }, + }, + }, + }, { + hl('Register', '@a'), + hl('BinaryPlus', '+'), + hl('Register', '@b'), + hl('CallingParenthesis', '('), + hl('Register', '@c'), + hl('CallingParenthesis', ')'), + }) + check_parsing('@a()', 0, { + ast = { + { + 'Call:0:2:(', + children = { + 'Register(name=a):0:0:@a', + }, + }, + }, + }, { + hl('Register', '@a'), + hl('CallingParenthesis', '('), + hl('CallingParenthesis', ')'), + }) + check_parsing('@a ()', 0, { + ast = { + { + 'OpMissing:0:2:', + children = { + 'Register(name=a):0:0:@a', + { + 'Nested:0:2: (', + children = { + 'Missing:0:4:', + }, + }, + }, + }, + }, + err = { + arg = '2:()', + msg = 'E15: Missing operator: %.*s', + }, + }, { + hl('Register', '@a'), + hl('InvalidSpacing', ' '), + hl('NestingParenthesis', '('), + hl('InvalidNestingParenthesis', ')'), + }) + check_parsing( + '@a + (@b)', 0, { + ast = { + { + 'BinaryPlus:0:2: +', + children = { + 'Register(name=a):0:0:@a', + { + 'Nested:0:4: (', + children = { + 'Register(name=b):0:6:@b', + }, + }, + }, + }, + }, + }, { + hl('Register', '@a'), + hl('BinaryPlus', '+', 1), + hl('NestingParenthesis', '(', 1), + hl('Register', '@b'), + hl('NestingParenthesis', ')'), + }) + check_parsing( + '@a + (+@b)', 0, { + ast = { + { + 'BinaryPlus:0:2: +', + children = { + 'Register(name=a):0:0:@a', + { + 'Nested:0:4: (', + children = { + { + 'UnaryPlus:0:6:+', + children = { + 'Register(name=b):0:7:@b', + }, + }, + }, + }, + }, + }, + }, + }, { + hl('Register', '@a'), + hl('BinaryPlus', '+', 1), + hl('NestingParenthesis', '(', 1), + hl('UnaryPlus', '+'), + hl('Register', '@b'), + hl('NestingParenthesis', ')'), + }) + check_parsing( + '@a + (@b + @c)', 0, { + ast = { + { + 'BinaryPlus:0:2: +', + children = { + 'Register(name=a):0:0:@a', + { + 'Nested:0:4: (', + children = { + { + 'BinaryPlus:0:8: +', + children = { + 'Register(name=b):0:6:@b', + 'Register(name=c):0:10: @c', + }, + }, + }, + }, + }, + }, + }, + }, { + hl('Register', '@a'), + hl('BinaryPlus', '+', 1), + hl('NestingParenthesis', '(', 1), + hl('Register', '@b'), + hl('BinaryPlus', '+', 1), + hl('Register', '@c', 1), + hl('NestingParenthesis', ')'), + }) + check_parsing('(@a)+@b', 0, { + ast = { + { + 'BinaryPlus:0:4:+', + children = { + { + 'Nested:0:0:(', + children = { + 'Register(name=a):0:1:@a', + }, + }, + 'Register(name=b):0:5:@b', + }, + }, + }, + }, { + hl('NestingParenthesis', '('), + hl('Register', '@a'), + hl('NestingParenthesis', ')'), + hl('BinaryPlus', '+'), + hl('Register', '@b'), + }) + check_parsing('@a+(@b)(@c)', 0, { + -- 01234567890 + ast = { + { + 'BinaryPlus:0:2:+', + children = { + 'Register(name=a):0:0:@a', + { + 'Call:0:7:(', + children = { + { + 'Nested:0:3:(', + children = { 'Register(name=b):0:4:@b' }, + }, + 'Register(name=c):0:8:@c', + }, + }, + }, + }, + }, + }, { + hl('Register', '@a'), + hl('BinaryPlus', '+'), + hl('NestingParenthesis', '('), + hl('Register', '@b'), + hl('NestingParenthesis', ')'), + hl('CallingParenthesis', '('), + hl('Register', '@c'), + hl('CallingParenthesis', ')'), + }) + check_parsing('@a+((@b))(@c)', 0, { + -- 01234567890123456890123456789 + -- 0 1 2 + ast = { + { + 'BinaryPlus:0:2:+', + children = { + 'Register(name=a):0:0:@a', + { + 'Call:0:9:(', + children = { + { + 'Nested:0:3:(', + children = { + { + 'Nested:0:4:(', + children = { 'Register(name=b):0:5:@b' } + }, + }, + }, + 'Register(name=c):0:10:@c', + }, + }, + }, + }, + }, + }, { + hl('Register', '@a'), + hl('BinaryPlus', '+'), + hl('NestingParenthesis', '('), + hl('NestingParenthesis', '('), + hl('Register', '@b'), + hl('NestingParenthesis', ')'), + hl('NestingParenthesis', ')'), + hl('CallingParenthesis', '('), + hl('Register', '@c'), + hl('CallingParenthesis', ')'), + }) + check_parsing('@a+((@b))+@c', 0, { + -- 01234567890123456890123456789 + -- 0 1 2 + ast = { + { + 'BinaryPlus:0:9:+', + children = { + { + 'BinaryPlus:0:2:+', + children = { + 'Register(name=a):0:0:@a', + { + 'Nested:0:3:(', + children = { + { + 'Nested:0:4:(', + children = { 'Register(name=b):0:5:@b' } + }, + }, + }, + }, + }, + 'Register(name=c):0:10:@c', + }, + }, + }, + }, { + hl('Register', '@a'), + hl('BinaryPlus', '+'), + hl('NestingParenthesis', '('), + hl('NestingParenthesis', '('), + hl('Register', '@b'), + hl('NestingParenthesis', ')'), + hl('NestingParenthesis', ')'), + hl('BinaryPlus', '+'), + hl('Register', '@c'), + }) + check_parsing( + '@a + (@b + @c) + @d(@e) + (+@f) + ((+@g(@h))(@j)(@k))(@l)', 0, {--[[ + | | | | | | | | || | | || | | ||| || || || || + 000000000011111111112222222222333333333344444444445555555 + 012345678901234567890123456789012345678901234567890123456 + ]] + ast = {{ + 'BinaryPlus:0:31: +', + children = { + { + 'BinaryPlus:0:23: +', + children = { + { + 'BinaryPlus:0:14: +', + children = { + { + 'BinaryPlus:0:2: +', + children = { + 'Register(name=a):0:0:@a', + { + 'Nested:0:4: (', + children = { + { + 'BinaryPlus:0:8: +', + children = { + 'Register(name=b):0:6:@b', + 'Register(name=c):0:10: @c', + }, + }, + }, + }, + }, + }, + { + 'Call:0:19:(', + children = { + 'Register(name=d):0:16: @d', + 'Register(name=e):0:20:@e', + }, + }, + }, + }, + { + 'Nested:0:25: (', + children = { + { + 'UnaryPlus:0:27:+', + children = { + 'Register(name=f):0:28:@f', + }, + }, + }, + }, + }, + }, + { + 'Call:0:53:(', + children = { + { + 'Nested:0:33: (', + children = { + { + 'Call:0:48:(', + children = { + { + 'Call:0:44:(', + children = { + { + 'Nested:0:35:(', + children = { + { + 'UnaryPlus:0:36:+', + children = { + { + 'Call:0:39:(', + children = { + 'Register(name=g):0:37:@g', + 'Register(name=h):0:40:@h', + }, + }, + }, + }, + }, + }, + 'Register(name=j):0:45:@j', + }, + }, + 'Register(name=k):0:49:@k', + }, + }, + }, + }, + 'Register(name=l):0:54:@l', + }, + }, + }, + }}, + }, { + hl('Register', '@a'), + hl('BinaryPlus', '+', 1), + hl('NestingParenthesis', '(', 1), + hl('Register', '@b'), + hl('BinaryPlus', '+', 1), + hl('Register', '@c', 1), + hl('NestingParenthesis', ')'), + hl('BinaryPlus', '+', 1), + hl('Register', '@d', 1), + hl('CallingParenthesis', '('), + hl('Register', '@e'), + hl('CallingParenthesis', ')'), + hl('BinaryPlus', '+', 1), + hl('NestingParenthesis', '(', 1), + hl('UnaryPlus', '+'), + hl('Register', '@f'), + hl('NestingParenthesis', ')'), + hl('BinaryPlus', '+', 1), + hl('NestingParenthesis', '(', 1), + hl('NestingParenthesis', '('), + hl('UnaryPlus', '+'), + hl('Register', '@g'), + hl('CallingParenthesis', '('), + hl('Register', '@h'), + hl('CallingParenthesis', ')'), + hl('NestingParenthesis', ')'), + hl('CallingParenthesis', '('), + hl('Register', '@j'), + hl('CallingParenthesis', ')'), + hl('CallingParenthesis', '('), + hl('Register', '@k'), + hl('CallingParenthesis', ')'), + hl('NestingParenthesis', ')'), + hl('CallingParenthesis', '('), + hl('Register', '@l'), + hl('CallingParenthesis', ')'), + }) + check_parsing('@a)', 0, { + -- 012 + ast = { + { + 'Nested:0:2:', + children = { + 'Register(name=a):0:0:@a', + }, + }, + }, + err = { + arg = '1:)', + msg = 'E15: Unexpected closing parenthesis: %.*s', + }, + }, { + hl('Register', '@a'), + hl('InvalidNestingParenthesis', ')'), + }) + check_parsing('(@a', 0, { + -- 012 + ast = { + { + 'Nested:0:0:(', + children = { + 'Register(name=a):0:1:@a', + }, + }, + }, + err = { + arg = '3:(@a', + msg = 'E110: Missing closing parenthesis for nested expression: %.*s', + }, + }, { + hl('NestingParenthesis', '('), + hl('Register', '@a'), + }) + check_parsing('@a(@b', 0, { + -- 01234 + ast = { + { + 'Call:0:2:(', + children = { + 'Register(name=a):0:0:@a', + 'Register(name=b):0:3:@b', + }, + }, + }, + err = { + arg = '3:(@b', + msg = 'E116: Missing closing parenthesis for function call: %.*s', + }, + }, { + hl('Register', '@a'), + hl('CallingParenthesis', '('), + hl('Register', '@b'), + }) + end) +end) -- cgit From 7c97f783935ec122fbf0d7d070c00804738abd6a Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 11 Sep 2017 01:27:46 +0300 Subject: klee: Start preparing for klee tests First stage: something compiling without klee, but with a buch of dirty hacks - done. Second stage: something running under klee, able to emit useful results, but still using dirty hacks - done. Third stage: make CMake care about clang argumnets - not done, may be omitted if proves to be too hard. Not that klee can be run on CI in any case. --- test/symbolic/klee/nvim/charset.c | 10 ++ test/symbolic/klee/nvim/garray.c | 195 +++++++++++++++++++++++++++ test/symbolic/klee/nvim/gettext.c | 4 + test/symbolic/klee/nvim/mbyte.c | 18 +++ test/symbolic/klee/nvim/memory.c | 90 +++++++++++++ test/symbolic/klee/run.sh | 69 ++++++++++ test/symbolic/klee/viml_expressions_lexer.c | 86 ++++++++++++ test/symbolic/klee/viml_expressions_parser.c | 102 ++++++++++++++ 8 files changed, 574 insertions(+) create mode 100644 test/symbolic/klee/nvim/charset.c create mode 100644 test/symbolic/klee/nvim/garray.c create mode 100644 test/symbolic/klee/nvim/gettext.c create mode 100644 test/symbolic/klee/nvim/mbyte.c create mode 100644 test/symbolic/klee/nvim/memory.c create mode 100755 test/symbolic/klee/run.sh create mode 100644 test/symbolic/klee/viml_expressions_lexer.c create mode 100644 test/symbolic/klee/viml_expressions_parser.c (limited to 'test') diff --git a/test/symbolic/klee/nvim/charset.c b/test/symbolic/klee/nvim/charset.c new file mode 100644 index 0000000000..a40488920e --- /dev/null +++ b/test/symbolic/klee/nvim/charset.c @@ -0,0 +1,10 @@ +#include + +#include "nvim/ascii.h" +#include "nvim/macros.h" +#include "nvim/charset.h" + +bool vim_isIDc(int c) +{ + return ASCII_ISALNUM(c); +} diff --git a/test/symbolic/klee/nvim/garray.c b/test/symbolic/klee/nvim/garray.c new file mode 100644 index 0000000000..260870c3c2 --- /dev/null +++ b/test/symbolic/klee/nvim/garray.c @@ -0,0 +1,195 @@ +// This is an open source non-commercial project. Dear PVS-Studio, please check +// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com + +/// @file garray.c +/// +/// Functions for handling growing arrays. + +#include +#include + +#include "nvim/vim.h" +#include "nvim/ascii.h" +#include "nvim/log.h" +#include "nvim/memory.h" +#include "nvim/path.h" +#include "nvim/garray.h" +#include "nvim/strings.h" + +// #include "nvim/globals.h" +#include "nvim/memline.h" + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "garray.c.generated.h" +#endif + +/// Clear an allocated growing array. +void ga_clear(garray_T *gap) +{ + xfree(gap->ga_data); + + // Initialize growing array without resetting itemsize or growsize + gap->ga_data = NULL; + gap->ga_maxlen = 0; + gap->ga_len = 0; +} + +/// Clear a growing array that contains a list of strings. +/// +/// @param gap +void ga_clear_strings(garray_T *gap) +{ + GA_DEEP_CLEAR_PTR(gap); +} + +/// Initialize a growing array. +/// +/// @param gap +/// @param itemsize +/// @param growsize +void ga_init(garray_T *gap, int itemsize, int growsize) +{ + gap->ga_data = NULL; + gap->ga_maxlen = 0; + gap->ga_len = 0; + gap->ga_itemsize = itemsize; + ga_set_growsize(gap, growsize); +} + +/// A setter for the growsize that guarantees it will be at least 1. +/// +/// @param gap +/// @param growsize +void ga_set_growsize(garray_T *gap, int growsize) +{ + if (growsize < 1) { + WLOG("trying to set an invalid ga_growsize: %d", growsize); + gap->ga_growsize = 1; + } else { + gap->ga_growsize = growsize; + } +} + +/// Make room in growing array "gap" for at least "n" items. +/// +/// @param gap +/// @param n +void ga_grow(garray_T *gap, int n) +{ + if (gap->ga_maxlen - gap->ga_len >= n) { + // the garray still has enough space, do nothing + return; + } + + if (gap->ga_growsize < 1) { + WLOG("ga_growsize(%d) is less than 1", gap->ga_growsize); + } + + // the garray grows by at least growsize + if (n < gap->ga_growsize) { + n = gap->ga_growsize; + } + int new_maxlen = gap->ga_len + n; + + size_t new_size = (size_t)(gap->ga_itemsize * new_maxlen); + size_t old_size = (size_t)(gap->ga_itemsize * gap->ga_maxlen); + + // reallocate and clear the new memory + char *pp = xrealloc(gap->ga_data, new_size); + memset(pp + old_size, 0, new_size - old_size); + + gap->ga_maxlen = new_maxlen; + gap->ga_data = pp; +} + +/// For a growing array that contains a list of strings: concatenate all the +/// strings with sep as separator. +/// +/// @param gap +/// @param sep +/// +/// @returns the concatenated strings +char_u *ga_concat_strings_sep(const garray_T *gap, const char *sep) + FUNC_ATTR_NONNULL_RET +{ + const size_t nelem = (size_t) gap->ga_len; + const char **strings = gap->ga_data; + + if (nelem == 0) { + return (char_u *) xstrdup(""); + } + + size_t len = 0; + for (size_t i = 0; i < nelem; i++) { + len += strlen(strings[i]); + } + + // add some space for the (num - 1) separators + len += (nelem - 1) * strlen(sep); + char *const ret = xmallocz(len); + + char *s = ret; + for (size_t i = 0; i < nelem - 1; i++) { + s = xstpcpy(s, strings[i]); + s = xstpcpy(s, sep); + } + strcpy(s, strings[nelem - 1]); + + return (char_u *) ret; +} + +/// For a growing array that contains a list of strings: concatenate all the +/// strings with a separating comma. +/// +/// @param gap +/// +/// @returns the concatenated strings +char_u* ga_concat_strings(const garray_T *gap) FUNC_ATTR_NONNULL_RET +{ + return ga_concat_strings_sep(gap, ","); +} + +/// Concatenate a string to a growarray which contains characters. +/// When "s" is NULL does not do anything. +/// +/// WARNING: +/// - Does NOT copy the NUL at the end! +/// - The parameter may not overlap with the growing array +/// +/// @param gap +/// @param s +void ga_concat(garray_T *gap, const char_u *restrict s) +{ + if (s == NULL) { + return; + } + + ga_concat_len(gap, (const char *restrict) s, strlen((char *) s)); +} + +/// Concatenate a string to a growarray which contains characters +/// +/// @param[out] gap Growarray to modify. +/// @param[in] s String to concatenate. +/// @param[in] len String length. +void ga_concat_len(garray_T *const gap, const char *restrict s, + const size_t len) + FUNC_ATTR_NONNULL_ALL +{ + if (len) { + ga_grow(gap, (int) len); + char *data = gap->ga_data; + memcpy(data + gap->ga_len, s, len); + gap->ga_len += (int) len; + } +} + +/// Append one byte to a growarray which contains bytes. +/// +/// @param gap +/// @param c +void ga_append(garray_T *gap, char c) +{ + GA_APPEND(char, gap, c); +} + diff --git a/test/symbolic/klee/nvim/gettext.c b/test/symbolic/klee/nvim/gettext.c new file mode 100644 index 0000000000..b9cc98d770 --- /dev/null +++ b/test/symbolic/klee/nvim/gettext.c @@ -0,0 +1,4 @@ +char *gettext(const char *s) +{ + return (char *)s; +} diff --git a/test/symbolic/klee/nvim/mbyte.c b/test/symbolic/klee/nvim/mbyte.c new file mode 100644 index 0000000000..394d17b700 --- /dev/null +++ b/test/symbolic/klee/nvim/mbyte.c @@ -0,0 +1,18 @@ +#include + +#include "nvim/types.h" +#include "nvim/mbyte.h" +#include "nvim/ascii.h" + +char_u *string_convert(const vimconv_T *conv, char_u *data, size_t *size) +{ + return NULL; +} + +int utfc_ptr2len_len(const char_u *p, int size) +{ + if (size < 1 || *p == NUL) { + return 0; + } + return 1; +} diff --git a/test/symbolic/klee/nvim/memory.c b/test/symbolic/klee/nvim/memory.c new file mode 100644 index 0000000000..7e924a410a --- /dev/null +++ b/test/symbolic/klee/nvim/memory.c @@ -0,0 +1,90 @@ +#include +#include + +#include "nvim/lib/ringbuf.h" + +enum { RB_SIZE = 1024 }; + +typedef struct { + void *ptr; + size_t size; +} AllocRecord; + +RINGBUF_TYPEDEF(AllocRecords, AllocRecord) +RINGBUF_INIT(AllocRecords, arecs, AllocRecord, RINGBUF_DUMMY_FREE) +RINGBUF_STATIC(static, AllocRecords, AllocRecord, arecs, RB_SIZE) + +size_t allocated_memory = 0; + +void *xmalloc(const size_t size) +{ + void *ret = malloc(size); + allocated_memory += size; + assert(arecs_rb_length(&arecs) < RB_SIZE); + arecs_rb_push(&arecs, (AllocRecord) { + .ptr = ret, + .size = size, + }); + return ret; +} + +void xfree(void *const p) +{ + RINGBUF_FORALL(&arecs, AllocRecord, arec) { + if (arec->ptr == p) { + allocated_memory -= arec->size; + arecs_rb_remove(&arecs, arecs_rb_find_idx(&arecs, arec)); + return; + } + } + assert(false); +} + +void *xrealloc(void *const p, size_t new_size) +{ + void *ret = realloc(p, new_size); + RINGBUF_FORALL(&arecs, AllocRecord, arec) { + if (arec->ptr == p) { + allocated_memory -= arec->size; + allocated_memory += new_size; + arec->ptr = ret; + arec->size = new_size; + return ret; + } + } + assert(false); + return (void *)(intptr_t)1; +} + +char *xstrdup(const char *str) + FUNC_ATTR_MALLOC FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_RET + FUNC_ATTR_NONNULL_ALL +{ + return xmemdupz(str, strlen(str)); +} + +void *xmallocz(size_t size) + FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_RET FUNC_ATTR_WARN_UNUSED_RESULT +{ + size_t total_size = size + 1; + assert(total_size > size); + + void *ret = xmalloc(total_size); + ((char *)ret)[size] = 0; + + return ret; +} + +char *xstpcpy(char *restrict dst, const char *restrict src) + FUNC_ATTR_NONNULL_RET FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL +{ + const size_t len = strlen(src); + return (char *)memcpy(dst, src, len + 1) + len; +} + +void *xmemdupz(const void *data, size_t len) + FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_RET FUNC_ATTR_WARN_UNUSED_RESULT + FUNC_ATTR_NONNULL_ALL +{ + return memcpy(xmallocz(len), data, len); +} diff --git a/test/symbolic/klee/run.sh b/test/symbolic/klee/run.sh new file mode 100755 index 0000000000..388903c234 --- /dev/null +++ b/test/symbolic/klee/run.sh @@ -0,0 +1,69 @@ +#!/bin/sh + +set -e +set -x +test -z "$POSH_VERSION" && set -u + +PROJECT_SOURCE_DIR=. +PROJECT_BINARY_DIR="$PROJECT_SOURCE_DIR/build" +KLEE_TEST_DIR="$PROJECT_SOURCE_DIR/test/symbolic/klee" +KLEE_BIN_DIR="$PROJECT_BINARY_DIR/klee" +KLEE_OUT_DIR="$KLEE_BIN_DIR/out" + +main() { + local compile= + if test "$1" = "-c" ; then + compile=1 + shift + fi + local test="$1" ; shift + + local includes= + includes="$includes -I$KLEE_TEST_DIR" + includes="$includes -I/home/klee/klee_src/include" + includes="$includes -I$PROJECT_SOURCE_DIR/src" + includes="$includes -I$PROJECT_BINARY_DIR/src/nvim/auto" + includes="$includes -I$PROJECT_BINARY_DIR/include" + includes="$includes -I$PROJECT_BINARY_DIR/config" + includes="$includes -I/host-includes" + + local defines= + defines="$defines -DMIN_LOG_LEVEL=9999" + defines="$defines -DINCLUDE_GENERATED_DECLARATIONS" + + test -z "$compile" && defines="$defines -DUSE_KLEE" + + test -d "$KLEE_BIN_DIR" || mkdir -p "$KLEE_BIN_DIR" + + if test -z "$compile" ; then + test -d "$KLEE_OUT_DIR" && rm -r "$KLEE_OUT_DIR" + local line1='cd /image' + line1="$line1 && $(echo clang \ + $includes $defines \ + -o "$KLEE_BIN_DIR/a.bc" -emit-llvm -g -c \ + "$KLEE_TEST_DIR/$test.c")" + line1="$line1 && klee --libc=uclibc --posix-runtime " + line1="$line1 '--output-dir=$KLEE_OUT_DIR' '$KLEE_BIN_DIR/a.bc'" + local line2="for t in '$KLEE_OUT_DIR'/*.err" + line2="$line2 ; do ktest-tool --write-ints" + line2="$line2 \"\$(printf '%s' \"\$t\" | sed -e 's@.[^/]*\$@.out@')\"" + line2="$line2 ; done" + printf '%s\n%s\n' "$line1" "$line2" | \ + docker run \ + --volume "$(cd "$PROJECT_SOURCE_DIR" && pwd)":/image \ + --volume "/usr/include":/host-includes \ + --interactive \ + --rm \ + --ulimit='stack=-1:-1' \ + klee/klee \ + /bin/sh -x + else + clang \ + $includes $defines \ + -o "$KLEE_BIN_DIR/$test" \ + -O0 -g \ + "$KLEE_TEST_DIR/$test.c" + fi +} + +main "$@" diff --git a/test/symbolic/klee/viml_expressions_lexer.c b/test/symbolic/klee/viml_expressions_lexer.c new file mode 100644 index 0000000000..e3b5aa80cc --- /dev/null +++ b/test/symbolic/klee/viml_expressions_lexer.c @@ -0,0 +1,86 @@ +#ifdef USE_KLEE +# include +#else +# include +#endif +#include +#include +#include + +#include "nvim/viml/parser/expressions.h" +#include "nvim/viml/parser/parser.h" +#include "nvim/mbyte.h" + +#include "nvim/memory.c" +#include "nvim/mbyte.c" +#include "nvim/charset.c" +#include "nvim/garray.c" +#include "nvim/gettext.c" +#include "nvim/viml/parser/expressions.c" + +#define INPUT_SIZE 7 + +uint8_t avoid_optimizing_out; + +void simple_get_line(void *cookie, ParserLine *ret_pline) +{ + ParserLine **plines_p = (ParserLine **)cookie; + *ret_pline = **plines_p; + (*plines_p)++; +} + +int main(const int argc, const char *const *const argv, + const char *const *const environ) +{ + char input[INPUT_SIZE]; + uint8_t shift; + const bool peek = false; + avoid_optimizing_out = argc; + +#ifdef USE_KLEE + klee_make_symbolic(input, sizeof(input), "input"); + klee_make_symbolic(&shift, sizeof(shift), "shift"); + klee_assume(shift < INPUT_SIZE); +#endif + + ParserLine plines[] = { + { +#ifdef USE_KLEE + .data = &input[shift], + .size = sizeof(input) - shift, +#else + .data = (const char *)&argv[1], + .size = strlen(argv[1]), +#endif + .allocated = false, + }, + { + .data = NULL, + .size = 0, + .allocated = false, + }, + }; +#ifdef USE_KLEE + assert(plines[0].size <= INPUT_SIZE); + assert((plines[0].data[0] != 5) | (plines[0].data[0] != argc)); +#endif + ParserLine *cur_pline = &plines[0]; + + ParserState pstate = { + .reader = { + .get_line = simple_get_line, + .cookie = &cur_pline, + .lines = KV_INITIAL_VALUE, + .conv.vc_type = CONV_NONE, + }, + .pos = { 0, 0 }, + .colors = NULL, + .can_continuate = false, + }; + kvi_init(pstate.reader.lines); + + LexExprToken token = viml_pexpr_next_token(&pstate, peek); + assert((pstate.pos.line == 0) + ? (pstate.pos.col > 0) + : (pstate.pos.line == 1 && pstate.pos.col == 0)); +} diff --git a/test/symbolic/klee/viml_expressions_parser.c b/test/symbolic/klee/viml_expressions_parser.c new file mode 100644 index 0000000000..2bad1adc53 --- /dev/null +++ b/test/symbolic/klee/viml_expressions_parser.c @@ -0,0 +1,102 @@ +#ifdef USE_KLEE +# error UNFINISHED +# include +#else +# include +#endif +#include +#include +#include + +#include "nvim/viml/parser/expressions.h" +#include "nvim/viml/parser/parser.h" +#include "nvim/mbyte.h" + +#include "nvim/memory.c" +#include "nvim/mbyte.c" +#include "nvim/charset.c" +#include "nvim/garray.c" +#include "nvim/gettext.c" +#include "nvim/viml/parser/expressions.c" + +#define INPUT_SIZE 50 + +uint8_t avoid_optimizing_out; + +void simple_get_line(void *cookie, ParserLine *ret_pline) +{ + ParserLine **plines_p = (ParserLine **)cookie; + *ret_pline = **plines_p; + (*plines_p)++; +} + +int main(const int argc, const char *const *const argv, + const char *const *const environ) +{ + char input[INPUT_SIZE]; + uint8_t shift; + int flags; + const bool peek = false; + avoid_optimizing_out = argc; + +#ifndef USE_KLEE + sscanf(argv[2], "%d", &flags); +#endif + +#ifdef USE_KLEE + klee_make_symbolic(input, sizeof(input), "input"); + klee_make_symbolic(&shift, sizeof(shift), "shift"); + klee_make_symbolic(&flags, sizeof{flags}, "flags"); + klee_assume(shift < INPUT_SIZE); + klee_assume( + flags <= kExprFlagsMulti|kExprFlagsDisallowEOC|kExprFlagsPrintError); +#endif + + ParserLine plines[] = { + { +#ifdef USE_KLEE + .data = &input[shift], + .size = sizeof(input) - shift, +#else + .data = argv[1], + .size = strlen(argv[1]), +#endif + .allocated = false, + }, + { + .data = NULL, + .size = 0, + .allocated = false, + }, + }; +#ifdef USE_KLEE + assert(plines[0].size <= INPUT_SIZE); + assert((plines[0].data[0] != 5) | (plines[0].data[0] != argc)); +#endif + ParserLine *cur_pline = &plines[0]; + + ParserState pstate = { + .reader = { + .get_line = simple_get_line, + .cookie = &cur_pline, + .lines = KV_INITIAL_VALUE, + .conv.vc_type = CONV_NONE, + }, + .pos = { 0, 0 }, + .colors = NULL, + .can_continuate = false, + }; + kvi_init(pstate.reader.lines); + + const ExprAST ast = viml_pexpr_parse(&pstate, flags); + assert(ast.root != NULL + || plines[0].size == 0); + assert(ast.root != NULL || !ast.correct); + assert(ast.correct + || (ast.err.msg != NULL + && ast.err.arg != NULL + && ast.err.arg >= plines[0].data + && ((size_t)(ast.err.arg - plines[0].data) + ast.err.arg_len + <= plines[0].size))); + // FIXME: free memory and assert no memory leaks +} -- cgit From 7980614650f0aedb39bf88466e5bd3ce90429cc1 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 17 Sep 2017 17:33:03 +0300 Subject: viml/parser/expressions: Add support for figure braces (three kinds) --- test/unit/viml/expressions/parser_spec.lua | 1018 ++++++++++++++++++++++++++-- 1 file changed, 976 insertions(+), 42 deletions(-) (limited to 'test') diff --git a/test/unit/viml/expressions/parser_spec.lua b/test/unit/viml/expressions/parser_spec.lua index c4bb067391..63b8baa8a8 100644 --- a/test/unit/viml/expressions/parser_spec.lua +++ b/test/unit/viml/expressions/parser_spec.lua @@ -31,6 +31,13 @@ make_enum_conv_tab(lib, { 'kExprNodeCall', 'kExprNodePlainIdentifier', 'kExprNodeComplexIdentifier', + 'kExprNodeUnknownFigure', + 'kExprNodeLambda', + 'kExprNodeDictLiteral', + 'kExprNodeCurlyBracesIdentifier', + 'kExprNodeComma', + 'kExprNodeColon', + 'kExprNodeArrow', }, 'kExprNode', function(ret) east_node_type_tab = ret end) local function conv_east_node_type(typ) @@ -59,6 +66,16 @@ local function eastnode2lua(pstate, eastnode, checked_nodes) if typ == 'Register' then typ = typ .. ('(name=%s)'):format( tostring(intchar2lua(eastnode.data.reg.name))) + elseif typ == 'PlainIdentifier' then + typ = typ .. ('(scope=%s,ident=%s)'):format( + tostring(intchar2lua(eastnode.data.var.scope)), + ffi.string(eastnode.data.var.ident, eastnode.data.var.ident_len)) + elseif (typ == 'UnknownFigure' or typ == 'DictLiteral' + or typ == 'CurlyBracesIdentifier' or typ == 'Lambda') then + typ = typ .. ('(%s)'):format( + (eastnode.data.fig.type_guesses.allow_lambda and '\\' or '-') + .. (eastnode.data.fig.type_guesses.allow_dict and 'd' or '-') + .. (eastnode.data.fig.type_guesses.allow_ident and 'i' or '-')) end ret_str = typ .. ':' .. ret_str local can_simplify = true @@ -90,8 +107,7 @@ local function east2lua(pstate, east) return { err = (not east.correct) and { msg = ffi.string(east.err.msg), - arg = ('%u:%s'):format( - tonumber(east.err.arg_len), + arg = ('%s'):format( ffi.string(east.err.arg, east.err.arg_len)), } or nil, ast = eastnodelist2lua(pstate, east.root, checked_nodes), @@ -121,31 +137,31 @@ child_call_once(function() end) describe('Expressions parser', function() - itp('works', function() - local function check_parsing(str, flags, exp_ast, exp_highlighting_fs) - local pstate = new_pstate({str}) - local east = lib.viml_pexpr_parse(pstate, flags) - local ast = east2lua(pstate, east) - eq(exp_ast, ast) - if exp_highlighting_fs then - local exp_highlighting = {} - local next_col = 0 - for i, h in ipairs(exp_highlighting_fs) do - exp_highlighting[i], next_col = h(next_col) - end - eq(exp_highlighting, phl2lua(pstate)) + local function check_parsing(str, flags, exp_ast, exp_highlighting_fs) + local pstate = new_pstate({str}) + local east = lib.viml_pexpr_parse(pstate, flags) + local ast = east2lua(pstate, east) + eq(exp_ast, ast) + if exp_highlighting_fs then + local exp_highlighting = {} + local next_col = 0 + for i, h in ipairs(exp_highlighting_fs) do + exp_highlighting[i], next_col = h(next_col) end + eq(exp_highlighting, phl2lua(pstate)) end - local function hl(group, str, shift) - return function(next_col) - local col = next_col + (shift or 0) - return (('%s:%u:%u:%s'):format( - 'NVim' .. group, - 0, - col, - str)), (col + #str) - end + end + local function hl(group, str, shift) + return function(next_col) + local col = next_col + (shift or 0) + return (('%s:%u:%u:%s'):format( + 'NVim' .. group, + 0, + col, + str)), (col + #str) end + end + itp('works with + and @a', function() check_parsing('@a', 0, { ast = { 'Register(name=a):0:0:@a', @@ -263,7 +279,7 @@ describe('Expressions parser', function() }, }, err = { - arg = '2:@b', + arg = '@b', msg = 'E15: Missing operator: %.*s', }, }, { @@ -281,7 +297,7 @@ describe('Expressions parser', function() }, }, err = { - arg = '2:@b', + arg = '@b', msg = 'E15: Missing operator: %.*s', }, }, { @@ -294,8 +310,8 @@ describe('Expressions parser', function() 'UnaryPlus:0:0:+', }, err = { - arg = '0:', - msg = 'E15: Expected value: %.*s', + arg = '', + msg = 'E15: Expected value, got EOC: %.*s', }, }, { hl('UnaryPlus', '+'), @@ -305,8 +321,8 @@ describe('Expressions parser', function() 'UnaryPlus:0:0: +', }, err = { - arg = '0:', - msg = 'E15: Expected value: %.*s', + arg = '', + msg = 'E15: Expected value, got EOC: %.*s', }, }, { hl('UnaryPlus', '+', 1), @@ -321,13 +337,15 @@ describe('Expressions parser', function() }, }, err = { - arg = '0:', - msg = 'E15: Expected value: %.*s', + arg = '', + msg = 'E15: Expected value, got EOC: %.*s', }, }, { hl('Register', '@a'), hl('BinaryPlus', '+'), }) + end) + itp('works with @a, + and parenthesis', function() check_parsing('(@a)', 0, { ast = { { @@ -352,8 +370,8 @@ describe('Expressions parser', function() }, }, err = { - arg = '1:)', - msg = 'E15: Expected value: %.*s', + arg = ')', + msg = 'E15: Expected value, got parenthesis: %.*s', }, }, { hl('NestingParenthesis', '('), @@ -369,8 +387,8 @@ describe('Expressions parser', function() }, }, err = { - arg = '1:)', - msg = 'E15: Expected value: %.*s', + arg = ')', + msg = 'E15: Expected value, got parenthesis: %.*s', }, }, { hl('InvalidNestingParenthesis', ')'), @@ -390,8 +408,8 @@ describe('Expressions parser', function() }, }, err = { - arg = '1:)', - msg = 'E15: Expected value: %.*s', + arg = ')', + msg = 'E15: Expected value, got parenthesis: %.*s', }, }, { hl('UnaryPlus', '+'), @@ -473,7 +491,7 @@ describe('Expressions parser', function() }, }, err = { - arg = '2:()', + arg = '()', msg = 'E15: Missing operator: %.*s', }, }, { @@ -838,7 +856,7 @@ describe('Expressions parser', function() }, }, err = { - arg = '1:)', + arg = ')', msg = 'E15: Unexpected closing parenthesis: %.*s', }, }, { @@ -856,7 +874,7 @@ describe('Expressions parser', function() }, }, err = { - arg = '3:(@a', + arg = '(@a', msg = 'E110: Missing closing parenthesis for nested expression: %.*s', }, }, { @@ -875,7 +893,7 @@ describe('Expressions parser', function() }, }, err = { - arg = '3:(@b', + arg = '(@b', msg = 'E116: Missing closing parenthesis for function call: %.*s', }, }, { @@ -884,4 +902,920 @@ describe('Expressions parser', function() hl('Register', '@b'), }) end) + itp('works with identifiers', function() + check_parsing('var', 0, { + ast = { + 'PlainIdentifier(scope=0,ident=var):0:0:var', + }, + }, { + hl('Identifier', 'var'), + }) + check_parsing('g:var', 0, { + ast = { + 'PlainIdentifier(scope=g,ident=var):0:0:g:var', + }, + }, { + hl('IdentifierScope', 'g'), + hl('IdentifierScopeDelimiter', ':'), + hl('Identifier', 'var'), + }) + check_parsing('g:', 0, { + ast = { + 'PlainIdentifier(scope=g,ident=):0:0:g:', + }, + }, { + hl('IdentifierScope', 'g'), + hl('IdentifierScopeDelimiter', ':'), + }) + end) + itp('works with curly braces', function() + check_parsing('{}', 0, { + ast = { + 'DictLiteral(-di):0:0:{', + }, + }, { + hl('Dict', '{'), + hl('Dict', '}'), + }) + check_parsing('{a}', 0, { + -- 012 + ast = { + { + 'CurlyBracesIdentifier(-di):0:0:{', + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + }, + }, + }, + }, { + hl('Curly', '{'), + hl('Identifier', 'a'), + hl('Curly', '}'), + }) + check_parsing('{a:b}', 0, { + -- 012 + ast = { + { + 'CurlyBracesIdentifier(-di):0:0:{', + children = { + 'PlainIdentifier(scope=a,ident=b):0:1:a:b', + }, + }, + }, + }, { + hl('Curly', '{'), + hl('IdentifierScope', 'a'), + hl('IdentifierScopeDelimiter', ':'), + hl('Identifier', 'b'), + hl('Curly', '}'), + }) + check_parsing('{a:@b}', 0, { + -- 012345 + ast = { + { + 'CurlyBracesIdentifier(-di):0:0:{', + children = { + { + 'OpMissing:0:3:', + children={ + 'PlainIdentifier(scope=a,ident=):0:1:a:', + 'Register(name=b):0:3:@b', + }, + }, + }, + }, + }, + err = { + arg = '@b}', + msg = 'E15: Missing operator: %.*s', + }, + }, { + hl('Curly', '{'), + hl('IdentifierScope', 'a'), + hl('IdentifierScopeDelimiter', ':'), + hl('InvalidRegister', '@b'), + hl('Curly', '}'), + }) + check_parsing('{@a}', 0, { + ast = { + { + 'CurlyBracesIdentifier(-di):0:0:{', + children = { + 'Register(name=a):0:1:@a', + }, + }, + }, + }, { + hl('Curly', '{'), + hl('Register', '@a'), + hl('Curly', '}'), + }) + check_parsing('{->@a}', 0, { + ast = { + { + 'Lambda(\\di):0:0:{', + children = { + { + 'Arrow:0:1:->', + children = { + 'Register(name=a):0:3:@a', + }, + }, + }, + }, + }, + }, { + hl('Lambda', '{'), + hl('Arrow', '->'), + hl('Register', '@a'), + hl('Lambda', '}'), + }) + check_parsing('{->@a+@b}', 0, { + -- 012345678 + ast = { + { + 'Lambda(\\di):0:0:{', + children = { + { + 'Arrow:0:1:->', + children = { + { + 'BinaryPlus:0:5:+', + children = { + 'Register(name=a):0:3:@a', + 'Register(name=b):0:6:@b', + }, + }, + }, + }, + }, + }, + }, + }, { + hl('Lambda', '{'), + hl('Arrow', '->'), + hl('Register', '@a'), + hl('BinaryPlus', '+'), + hl('Register', '@b'), + hl('Lambda', '}'), + }) + check_parsing('{a->@a}', 0, { + -- 012345678 + ast = { + { + 'Lambda(\\di):0:0:{', + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + { + 'Arrow:0:2:->', + children = { + 'Register(name=a):0:4:@a', + }, + }, + }, + }, + }, + }, { + hl('Lambda', '{'), + hl('Identifier', 'a'), + hl('Arrow', '->'), + hl('Register', '@a'), + hl('Lambda', '}'), + }) + check_parsing('{a,b->@a}', 0, { + -- 012345678 + ast = { + { + 'Lambda(\\di):0:0:{', + children = { + { + 'Comma:0:2:,', + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + 'PlainIdentifier(scope=0,ident=b):0:3:b', + }, + }, + { + 'Arrow:0:4:->', + children = { + 'Register(name=a):0:6:@a', + }, + }, + }, + }, + }, + }, { + hl('Lambda', '{'), + hl('Identifier', 'a'), + hl('Comma', ','), + hl('Identifier', 'b'), + hl('Arrow', '->'), + hl('Register', '@a'), + hl('Lambda', '}'), + }) + check_parsing('{a,b,c->@a}', 0, { + -- 01234567890 + ast = { + { + 'Lambda(\\di):0:0:{', + children = { + { + 'Comma:0:2:,', + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + { + 'Comma:0:4:,', + children = { + 'PlainIdentifier(scope=0,ident=b):0:3:b', + 'PlainIdentifier(scope=0,ident=c):0:5:c', + }, + }, + }, + }, + { + 'Arrow:0:6:->', + children = { + 'Register(name=a):0:8:@a', + }, + }, + }, + }, + }, + }, { + hl('Lambda', '{'), + hl('Identifier', 'a'), + hl('Comma', ','), + hl('Identifier', 'b'), + hl('Comma', ','), + hl('Identifier', 'c'), + hl('Arrow', '->'), + hl('Register', '@a'), + hl('Lambda', '}'), + }) + check_parsing('{a,b,c,d->@a}', 0, { + -- 0123456789012 + ast = { + { + 'Lambda(\\di):0:0:{', + children = { + { + 'Comma:0:2:,', + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + { + 'Comma:0:4:,', + children = { + 'PlainIdentifier(scope=0,ident=b):0:3:b', + { + 'Comma:0:6:,', + children = { + 'PlainIdentifier(scope=0,ident=c):0:5:c', + 'PlainIdentifier(scope=0,ident=d):0:7:d', + }, + }, + }, + }, + }, + }, + { + 'Arrow:0:8:->', + children = { + 'Register(name=a):0:10:@a', + }, + }, + }, + }, + }, + }, { + hl('Lambda', '{'), + hl('Identifier', 'a'), + hl('Comma', ','), + hl('Identifier', 'b'), + hl('Comma', ','), + hl('Identifier', 'c'), + hl('Comma', ','), + hl('Identifier', 'd'), + hl('Arrow', '->'), + hl('Register', '@a'), + hl('Lambda', '}'), + }) + check_parsing('{a,b,c,d,->@a}', 0, { + -- 01234567890123 + ast = { + { + 'Lambda(\\di):0:0:{', + children = { + { + 'Comma:0:2:,', + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + { + 'Comma:0:4:,', + children = { + 'PlainIdentifier(scope=0,ident=b):0:3:b', + { + 'Comma:0:6:,', + children = { + 'PlainIdentifier(scope=0,ident=c):0:5:c', + { + 'Comma:0:8:,', + children = { + 'PlainIdentifier(scope=0,ident=d):0:7:d', + }, + }, + }, + }, + }, + }, + }, + }, + { + 'Arrow:0:9:->', + children = { + 'Register(name=a):0:11:@a', + }, + }, + }, + }, + }, + }, { + hl('Lambda', '{'), + hl('Identifier', 'a'), + hl('Comma', ','), + hl('Identifier', 'b'), + hl('Comma', ','), + hl('Identifier', 'c'), + hl('Comma', ','), + hl('Identifier', 'd'), + hl('Comma', ','), + hl('Arrow', '->'), + hl('Register', '@a'), + hl('Lambda', '}'), + }) + check_parsing('{a,b->{c,d->{e,f->@a}}}', 0, { + -- 01234567890123456789012 + -- 0 1 2 + ast = { + { + 'Lambda(\\di):0:0:{', + children = { + { + 'Comma:0:2:,', + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + 'PlainIdentifier(scope=0,ident=b):0:3:b', + }, + }, + { + 'Arrow:0:4:->', + children = { + { + 'Lambda(\\di):0:6:{', + children = { + { + 'Comma:0:8:,', + children = { + 'PlainIdentifier(scope=0,ident=c):0:7:c', + 'PlainIdentifier(scope=0,ident=d):0:9:d', + }, + }, + { + 'Arrow:0:10:->', + children = { + { + 'Lambda(\\di):0:12:{', + children = { + { + 'Comma:0:14:,', + children = { + 'PlainIdentifier(scope=0,ident=e):0:13:e', + 'PlainIdentifier(scope=0,ident=f):0:15:f', + }, + }, + { + 'Arrow:0:16:->', + children = { + 'Register(name=a):0:18:@a', + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, { + hl('Lambda', '{'), + hl('Identifier', 'a'), + hl('Comma', ','), + hl('Identifier', 'b'), + hl('Arrow', '->'), + hl('Lambda', '{'), + hl('Identifier', 'c'), + hl('Comma', ','), + hl('Identifier', 'd'), + hl('Arrow', '->'), + hl('Lambda', '{'), + hl('Identifier', 'e'), + hl('Comma', ','), + hl('Identifier', 'f'), + hl('Arrow', '->'), + hl('Register', '@a'), + hl('Lambda', '}'), + hl('Lambda', '}'), + hl('Lambda', '}'), + }) + check_parsing('{a,b->c,d}', 0, { + -- 0123456789 + ast = { + { + 'Lambda(\\di):0:0:{', + children = { + { + 'Comma:0:2:,', + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + 'PlainIdentifier(scope=0,ident=b):0:3:b', + }, + }, + { + 'Arrow:0:4:->', + children = { + { + 'Comma:0:7:,', + children = { + 'PlainIdentifier(scope=0,ident=c):0:6:c', + 'PlainIdentifier(scope=0,ident=d):0:8:d', + }, + }, + }, + }, + }, + }, + }, + err = { + arg = ',d}', + msg = 'E15: Comma outside of call, lambda or literal: %.*s', + }, + }, { + hl('Lambda', '{'), + hl('Identifier', 'a'), + hl('Comma', ','), + hl('Identifier', 'b'), + hl('Arrow', '->'), + hl('Identifier', 'c'), + hl('InvalidComma', ','), + hl('Identifier', 'd'), + hl('Lambda', '}'), + }) + check_parsing('a,b,c,d', 0, { + -- 0123456789 + ast = { + { + 'Comma:0:1:,', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + { + 'Comma:0:3:,', + children = { + 'PlainIdentifier(scope=0,ident=b):0:2:b', + { + 'Comma:0:5:,', + children = { + 'PlainIdentifier(scope=0,ident=c):0:4:c', + 'PlainIdentifier(scope=0,ident=d):0:6:d', + }, + }, + }, + }, + }, + }, + }, + err = { + arg = ',b,c,d', + msg = 'E15: Comma outside of call, lambda or literal: %.*s', + }, + }, { + hl('Identifier', 'a'), + hl('InvalidComma', ','), + hl('Identifier', 'b'), + hl('InvalidComma', ','), + hl('Identifier', 'c'), + hl('InvalidComma', ','), + hl('Identifier', 'd'), + }) + check_parsing('a,b,c,d,', 0, { + -- 0123456789 + ast = { + { + 'Comma:0:1:,', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + { + 'Comma:0:3:,', + children = { + 'PlainIdentifier(scope=0,ident=b):0:2:b', + { + 'Comma:0:5:,', + children = { + 'PlainIdentifier(scope=0,ident=c):0:4:c', + { + 'Comma:0:7:,', + children = { + 'PlainIdentifier(scope=0,ident=d):0:6:d', + }, + }, + }, + }, + }, + }, + }, + }, + }, + err = { + arg = ',b,c,d,', + msg = 'E15: Comma outside of call, lambda or literal: %.*s', + }, + }, { + hl('Identifier', 'a'), + hl('InvalidComma', ','), + hl('Identifier', 'b'), + hl('InvalidComma', ','), + hl('Identifier', 'c'), + hl('InvalidComma', ','), + hl('Identifier', 'd'), + hl('InvalidComma', ','), + }) + check_parsing(',', 0, { + -- 0123456789 + ast = { + { + 'Comma:0:0:,', + children = { + 'Missing:0:0:', + }, + }, + }, + err = { + arg = ',', + msg = 'E15: Expected value, got comma: %.*s', + }, + }, { + hl('InvalidComma', ','), + }) + check_parsing('{,a->@a}', 0, { + -- 0123456789 + ast = { + { + 'CurlyBracesIdentifier(-di):0:0:{', + children = { + { + 'Arrow:0:3:->', + children = { + { + 'Comma:0:1:,', + children = { + 'Missing:0:1:', + 'PlainIdentifier(scope=0,ident=a):0:2:a', + }, + }, + 'Register(name=a):0:5:@a', + }, + }, + }, + }, + }, + err = { + arg = ',a->@a}', + msg = 'E15: Expected value, got comma: %.*s', + }, + }, { + hl('Curly', '{'), + hl('InvalidComma', ','), + hl('Identifier', 'a'), + hl('InvalidArrow', '->'), + hl('Register', '@a'), + hl('Curly', '}'), + }) + check_parsing('}', 0, { + -- 0123456789 + ast = { + 'UnknownFigure(---):0:0:', + }, + err = { + arg = '}', + msg = 'E15: Unexpected closing figure brace: %.*s', + }, + }, { + hl('InvalidFigureBrace', '}'), + }) + check_parsing('{->}', 0, { + -- 0123456789 + ast = { + { + 'Lambda(\\di):0:0:{', + children = { + 'Arrow:0:1:->', + }, + }, + }, + err = { + arg = '}', + msg = 'E15: Expected value, got closing figure brace: %.*s', + }, + }, { + hl('Lambda', '{'), + hl('Arrow', '->'), + hl('InvalidLambda', '}'), + }) + check_parsing('{a,b}', 0, { + -- 0123456789 + ast = { + { + 'Lambda(-di):0:0:{', + children = { + { + 'Comma:0:2:,', + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + 'PlainIdentifier(scope=0,ident=b):0:3:b', + }, + }, + }, + }, + }, + err = { + arg = '}', + msg = 'E15: Expected lambda arguments list or arrow: %.*s', + }, + }, { + hl('Lambda', '{'), + hl('Identifier', 'a'), + hl('Comma', ','), + hl('Identifier', 'b'), + hl('InvalidLambda', '}'), + }) + check_parsing('{a,}', 0, { + -- 0123456789 + ast = { + { + 'Lambda(-di):0:0:{', + children = { + { + 'Comma:0:2:,', + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + }, + }, + }, + }, + }, + err = { + arg = '}', + msg = 'E15: Expected lambda arguments list or arrow: %.*s', + }, + }, { + hl('Lambda', '{'), + hl('Identifier', 'a'), + hl('Comma', ','), + hl('InvalidLambda', '}'), + }) + check_parsing('{@a:@b}', 0, { + -- 0123456789 + ast = { + { + 'DictLiteral(-di):0:0:{', + children = { + { + 'Colon:0:3::', + children = { + 'Register(name=a):0:1:@a', + 'Register(name=b):0:4:@b', + }, + }, + }, + }, + }, + }, { + hl('Dict', '{'), + hl('Register', '@a'), + hl('Colon', ':'), + hl('Register', '@b'), + hl('Dict', '}'), + }) + check_parsing('{@a:@b,@c:@d}', 0, { + -- 0123456789012 + -- 0 1 + ast = { + { + 'DictLiteral(-di):0:0:{', + children = { + { + 'Comma:0:6:,', + children = { + { + 'Colon:0:3::', + children = { + 'Register(name=a):0:1:@a', + 'Register(name=b):0:4:@b', + }, + }, + { + 'Colon:0:9::', + children = { + 'Register(name=c):0:7:@c', + 'Register(name=d):0:10:@d', + }, + }, + }, + }, + }, + }, + }, + }, { + hl('Dict', '{'), + hl('Register', '@a'), + hl('Colon', ':'), + hl('Register', '@b'), + hl('Comma', ','), + hl('Register', '@c'), + hl('Colon', ':'), + hl('Register', '@d'), + hl('Dict', '}'), + }) + check_parsing('{@a:@b,@c:@d,@e:@f,}', 0, { + -- 01234567890123456789 + -- 0 1 + ast = { + { + 'DictLiteral(-di):0:0:{', + children = { + { + 'Comma:0:6:,', + children = { + { + 'Colon:0:3::', + children = { + 'Register(name=a):0:1:@a', + 'Register(name=b):0:4:@b', + }, + }, + { + 'Comma:0:12:,', + children = { + { + 'Colon:0:9::', + children = { + 'Register(name=c):0:7:@c', + 'Register(name=d):0:10:@d', + }, + }, + { + 'Comma:0:18:,', + children = { + { + 'Colon:0:15::', + children = { + 'Register(name=e):0:13:@e', + 'Register(name=f):0:16:@f', + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, { + hl('Dict', '{'), + hl('Register', '@a'), + hl('Colon', ':'), + hl('Register', '@b'), + hl('Comma', ','), + hl('Register', '@c'), + hl('Colon', ':'), + hl('Register', '@d'), + hl('Comma', ','), + hl('Register', '@e'), + hl('Colon', ':'), + hl('Register', '@f'), + hl('Comma', ','), + hl('Dict', '}'), + }) + check_parsing('{@a:@b,@c:@d,@e:@f,@g:}', 0, { + -- 01234567890123456789012 + -- 0 1 2 + ast = { + { + 'DictLiteral(-di):0:0:{', + children = { + { + 'Comma:0:6:,', + children = { + { + 'Colon:0:3::', + children = { + 'Register(name=a):0:1:@a', + 'Register(name=b):0:4:@b', + }, + }, + { + 'Comma:0:12:,', + children = { + { + 'Colon:0:9::', + children = { + 'Register(name=c):0:7:@c', + 'Register(name=d):0:10:@d', + }, + }, + { + 'Comma:0:18:,', + children = { + { + 'Colon:0:15::', + children = { + 'Register(name=e):0:13:@e', + 'Register(name=f):0:16:@f', + }, + }, + { + 'Colon:0:21::', + children = { + 'Register(name=g):0:19:@g', + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + err = { + arg = '}', + msg = 'E15: Expected value, got closing figure brace: %.*s', + }, + }, { + hl('Dict', '{'), + hl('Register', '@a'), + hl('Colon', ':'), + hl('Register', '@b'), + hl('Comma', ','), + hl('Register', '@c'), + hl('Colon', ':'), + hl('Register', '@d'), + hl('Comma', ','), + hl('Register', '@e'), + hl('Colon', ':'), + hl('Register', '@f'), + hl('Comma', ','), + hl('Register', '@g'), + hl('Colon', ':'), + hl('InvalidDict', '}'), + }) + check_parsing('{@a:@b,}', 0, { + -- 01234567890123 + -- 0 1 + ast = { + { + 'DictLiteral(-di):0:0:{', + children = { + { + 'Comma:0:6:,', + children = { + { + 'Colon:0:3::', + children = { + 'Register(name=a):0:1:@a', + 'Register(name=b):0:4:@b', + }, + }, + }, + }, + }, + }, + }, + }, { + hl('Dict', '{'), + hl('Register', '@a'), + hl('Colon', ':'), + hl('Register', '@b'), + hl('Comma', ','), + hl('Dict', '}'), + }) + end) + -- FIXME: Test sequence of arrows inside and outside lambdas. + -- FIXME: Test multiple arguments calling. + -- FIXME: Test autoload character and scope in lambda arguments. end) -- cgit From d4782fb1ca05e76095086bdcbc8dcea47f532d00 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 26 Sep 2017 00:28:34 +0300 Subject: viml/parser/expressions: Make commas actually work when calling --- test/unit/viml/expressions/parser_spec.lua | 42 ++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) (limited to 'test') diff --git a/test/unit/viml/expressions/parser_spec.lua b/test/unit/viml/expressions/parser_spec.lua index 63b8baa8a8..4d4a5f1007 100644 --- a/test/unit/viml/expressions/parser_spec.lua +++ b/test/unit/viml/expressions/parser_spec.lua @@ -901,6 +901,48 @@ describe('Expressions parser', function() hl('CallingParenthesis', '('), hl('Register', '@b'), }) + check_parsing('@a(@b, @c, @d, @e)', 0, { + -- 012345678901234567 + -- 0 1 + ast = { + { + 'Call:0:2:(', + children = { + 'Register(name=a):0:0:@a', + { + 'Comma:0:5:,', + children = { + 'Register(name=b):0:3:@b', + { + 'Comma:0:9:,', + children = { + 'Register(name=c):0:6: @c', + { + 'Comma:0:13:,', + children = { + 'Register(name=d):0:10: @d', + 'Register(name=e):0:14: @e', + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, { + hl('Register', '@a'), + hl('CallingParenthesis', '('), + hl('Register', '@b'), + hl('Comma', ','), + hl('Register', '@c', 1), + hl('Comma', ','), + hl('Register', '@d', 1), + hl('Comma', ','), + hl('Register', '@e', 1), + hl('CallingParenthesis', ')'), + }) end) itp('works with identifiers', function() check_parsing('var', 0, { -- cgit From 3cc65ac054976ef7520f0247b430ebef2f9537b7 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 26 Sep 2017 00:52:40 +0300 Subject: viml/parser/expressions: Make commas actually work when calling --- test/unit/viml/expressions/parser_spec.lua | 115 +++++++++++++++++++++++++++++ 1 file changed, 115 insertions(+) (limited to 'test') diff --git a/test/unit/viml/expressions/parser_spec.lua b/test/unit/viml/expressions/parser_spec.lua index 4d4a5f1007..b747a40e27 100644 --- a/test/unit/viml/expressions/parser_spec.lua +++ b/test/unit/viml/expressions/parser_spec.lua @@ -943,6 +943,121 @@ describe('Expressions parser', function() hl('Register', '@e', 1), hl('CallingParenthesis', ')'), }) + check_parsing('@a(@b(@c))', 0, { + -- 01234567890123456789012345678901234567 + -- 0 1 2 3 + ast = { + { + 'Call:0:2:(', + children = { + 'Register(name=a):0:0:@a', + { + 'Call:0:5:(', + children = { + 'Register(name=b):0:3:@b', + 'Register(name=c):0:6:@c', + }, + }, + }, + }, + }, + }, { + hl('Register', '@a'), + hl('CallingParenthesis', '('), + hl('Register', '@b'), + hl('CallingParenthesis', '('), + hl('Register', '@c'), + hl('CallingParenthesis', ')'), + hl('CallingParenthesis', ')'), + }) + check_parsing('@a(@b(@c(@d(@e), @f(@g(@h), @i(@j)))))', 0, { + -- 01234567890123456789012345678901234567 + -- 0 1 2 3 + ast = { + { + 'Call:0:2:(', + children = { + 'Register(name=a):0:0:@a', + { + 'Call:0:5:(', + children = { + 'Register(name=b):0:3:@b', + { + 'Call:0:8:(', + children = { + 'Register(name=c):0:6:@c', + { + 'Comma:0:15:,', + children = { + { + 'Call:0:11:(', + children = { + 'Register(name=d):0:9:@d', + 'Register(name=e):0:12:@e', + }, + }, + { + 'Call:0:19:(', + children = { + 'Register(name=f):0:16: @f', + { + 'Comma:0:26:,', + children = { + { + 'Call:0:22:(', + children = { + 'Register(name=g):0:20:@g', + 'Register(name=h):0:23:@h', + }, + }, + { + 'Call:0:30:(', + children = { + 'Register(name=i):0:27: @i', + 'Register(name=j):0:31:@j', + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, { + hl('Register', '@a'), + hl('CallingParenthesis', '('), + hl('Register', '@b'), + hl('CallingParenthesis', '('), + hl('Register', '@c'), + hl('CallingParenthesis', '('), + hl('Register', '@d'), + hl('CallingParenthesis', '('), + hl('Register', '@e'), + hl('CallingParenthesis', ')'), + hl('Comma', ','), + hl('Register', '@f', 1), + hl('CallingParenthesis', '('), + hl('Register', '@g'), + hl('CallingParenthesis', '('), + hl('Register', '@h'), + hl('CallingParenthesis', ')'), + hl('Comma', ','), + hl('Register', '@i', 1), + hl('CallingParenthesis', '('), + hl('Register', '@j'), + hl('CallingParenthesis', ')'), + hl('CallingParenthesis', ')'), + hl('CallingParenthesis', ')'), + hl('CallingParenthesis', ')'), + hl('CallingParenthesis', ')'), + }) end) itp('works with identifiers', function() check_parsing('var', 0, { -- cgit From 0987d3b10f36202e9f0289b50298e69aaf2fa4d2 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 26 Sep 2017 01:22:13 +0300 Subject: viml/parser/expressions: Make curly braces name actually work --- test/unit/viml/expressions/parser_spec.lua | 384 ++++++++++++++++++++++++++++- 1 file changed, 372 insertions(+), 12 deletions(-) (limited to 'test') diff --git a/test/unit/viml/expressions/parser_spec.lua b/test/unit/viml/expressions/parser_spec.lua index b747a40e27..eec4cb5bd9 100644 --- a/test/unit/viml/expressions/parser_spec.lua +++ b/test/unit/viml/expressions/parser_spec.lua @@ -1059,7 +1059,7 @@ describe('Expressions parser', function() hl('CallingParenthesis', ')'), }) end) - itp('works with identifiers', function() + itp('works with variable names, including curly braces ones', function() check_parsing('var', 0, { ast = { 'PlainIdentifier(scope=0,ident=var):0:0:var', @@ -1084,16 +1084,6 @@ describe('Expressions parser', function() hl('IdentifierScope', 'g'), hl('IdentifierScopeDelimiter', ':'), }) - end) - itp('works with curly braces', function() - check_parsing('{}', 0, { - ast = { - 'DictLiteral(-di):0:0:{', - }, - }, { - hl('Dict', '{'), - hl('Dict', '}'), - }) check_parsing('{a}', 0, { -- 012 ast = { @@ -1167,6 +1157,209 @@ describe('Expressions parser', function() hl('Register', '@a'), hl('Curly', '}'), }) + check_parsing('{@a}{@b}', 0, { + -- 01234567 + ast = { + { + 'ComplexIdentifier:0:4:', + children = { + { + 'CurlyBracesIdentifier(-di):0:0:{', + children = { + 'Register(name=a):0:1:@a', + }, + }, + { + 'CurlyBracesIdentifier(--i):0:4:{', + children = { + 'Register(name=b):0:5:@b', + }, + }, + }, + }, + }, + }, { + hl('Curly', '{'), + hl('Register', '@a'), + hl('Curly', '}'), + hl('Curly', '{'), + hl('Register', '@b'), + hl('Curly', '}'), + }) + check_parsing('g:{@a}', 0, { + -- 01234567 + ast = { + { + 'ComplexIdentifier:0:2:', + children = { + 'PlainIdentifier(scope=g,ident=):0:0:g:', + { + 'CurlyBracesIdentifier(--i):0:2:{', + children = { + 'Register(name=a):0:3:@a', + }, + }, + }, + }, + }, + }, { + hl('IdentifierScope', 'g'), + hl('IdentifierScopeDelimiter', ':'), + hl('Curly', '{'), + hl('Register', '@a'), + hl('Curly', '}'), + }) + check_parsing('{@a}_test', 0, { + -- 012345678 + ast = { + { + 'ComplexIdentifier:0:4:', + children = { + { + 'CurlyBracesIdentifier(-di):0:0:{', + children = { + 'Register(name=a):0:1:@a', + }, + }, + 'PlainIdentifier(scope=0,ident=_test):0:4:_test', + }, + }, + }, + }, { + hl('Curly', '{'), + hl('Register', '@a'), + hl('Curly', '}'), + hl('Identifier', '_test'), + }) + check_parsing('g:{@a}_test', 0, { + -- 01234567890 + ast = { + { + 'ComplexIdentifier:0:2:', + children = { + 'PlainIdentifier(scope=g,ident=):0:0:g:', + { + 'ComplexIdentifier:0:6:', + children = { + { + 'CurlyBracesIdentifier(--i):0:2:{', + children = { + 'Register(name=a):0:3:@a', + }, + }, + 'PlainIdentifier(scope=0,ident=_test):0:6:_test', + }, + }, + }, + }, + }, + }, { + hl('IdentifierScope', 'g'), + hl('IdentifierScopeDelimiter', ':'), + hl('Curly', '{'), + hl('Register', '@a'), + hl('Curly', '}'), + hl('Identifier', '_test'), + }) + check_parsing('g:{@a}_test()', 0, { + -- 0123456789012 + ast = { + { + 'Call:0:11:(', + children = { + { + 'ComplexIdentifier:0:2:', + children = { + 'PlainIdentifier(scope=g,ident=):0:0:g:', + { + 'ComplexIdentifier:0:6:', + children = { + { + 'CurlyBracesIdentifier(--i):0:2:{', + children = { + 'Register(name=a):0:3:@a', + }, + }, + 'PlainIdentifier(scope=0,ident=_test):0:6:_test', + }, + }, + }, + }, + }, + }, + }, + }, { + hl('IdentifierScope', 'g'), + hl('IdentifierScopeDelimiter', ':'), + hl('Curly', '{'), + hl('Register', '@a'), + hl('Curly', '}'), + hl('Identifier', '_test'), + hl('CallingParenthesis', '('), + hl('CallingParenthesis', ')'), + }) + check_parsing('{@a} ()', 0, { + -- 0123456789012 + ast = { + { + 'Call:0:4: (', + children = { + { + 'CurlyBracesIdentifier(-di):0:0:{', + children = { + 'Register(name=a):0:1:@a', + }, + }, + }, + }, + }, + }, { + hl('Curly', '{'), + hl('Register', '@a'), + hl('Curly', '}'), + hl('CallingParenthesis', '(', 1), + hl('CallingParenthesis', ')'), + }) + check_parsing('g:{@a} ()', 0, { + -- 0123456789012 + ast = { + { + 'Call:0:6: (', + children = { + { + 'ComplexIdentifier:0:2:', + children = { + 'PlainIdentifier(scope=g,ident=):0:0:g:', + { + 'CurlyBracesIdentifier(--i):0:2:{', + children = { + 'Register(name=a):0:3:@a', + }, + }, + }, + }, + }, + }, + }, + }, { + hl('IdentifierScope', 'g'), + hl('IdentifierScopeDelimiter', ':'), + hl('Curly', '{'), + hl('Register', '@a'), + hl('Curly', '}'), + hl('CallingParenthesis', '(', 1), + hl('CallingParenthesis', ')'), + }) + end) + itp('works with lambdas and dictionaries', function() + check_parsing('{}', 0, { + ast = { + 'DictLiteral(-di):0:0:{', + }, + }, { + hl('Dict', '{'), + hl('Dict', '}'), + }) check_parsing('{->@a}', 0, { ast = { { @@ -1971,8 +2164,175 @@ describe('Expressions parser', function() hl('Comma', ','), hl('Dict', '}'), }) + check_parsing('{({f -> g})(@h)(@i)}', 0, { + -- 01234567890123456789 + -- 0 1 + ast = { + { + 'CurlyBracesIdentifier(-di):0:0:{', + children = { + { + 'Call:0:15:(', + children = { + { + 'Call:0:11:(', + children = { + { + 'Nested:0:1:(', + children = { + { + 'Lambda(\\di):0:2:{', + children = { + 'PlainIdentifier(scope=0,ident=f):0:3:f', + { + 'Arrow:0:4: ->', + children = { + 'PlainIdentifier(scope=0,ident=g):0:7: g', + }, + }, + }, + }, + }, + }, + 'Register(name=h):0:12:@h', + }, + }, + 'Register(name=i):0:16:@i', + }, + }, + }, + }, + }, + }, { + hl('Curly', '{'), + hl('NestingParenthesis', '('), + hl('Lambda', '{'), + hl('Identifier', 'f'), + hl('Arrow', '->', 1), + hl('Identifier', 'g', 1), + hl('Lambda', '}'), + hl('NestingParenthesis', ')'), + hl('CallingParenthesis', '('), + hl('Register', '@h'), + hl('CallingParenthesis', ')'), + hl('CallingParenthesis', '('), + hl('Register', '@i'), + hl('CallingParenthesis', ')'), + hl('Curly', '}'), + }) + -- FIXME the below should not crash + check_parsing('a:{{b, c -> @d + @e + ({f -> g})(@h)}(@i)}j', 0, { + -- 01234567890123456789012345678901234567890123456 + -- 0 1 2 3 4 + ast = { + { + 'ComplexIdentifier:0:2:', + children = { + 'PlainIdentifier(scope=a,ident=):0:0:g:', + { + 'ComplexIdentifier:0:6:', + children = { + { + 'CurlyBracesIdentifier(--i):0:2:{', + children = { + { + 'Call:0:37:(', + children = { + { + 'Lambda(\\di):0:3:{', + children = { + { + 'Comma:0:5:,', + children = { + 'PlainIdentifier(scope=0,ident=b):0:4:b', + 'PlainIdentifier(scope=0,ident=c):0:6: c', + }, + }, + { + 'Arrow:0:8: ->', + children = { + { + 'BinaryPlus:0:19: +', + children = { + { + 'BinaryPlus:0:14: +', + children = { + 'Register(name=d):0:11: @d', + 'Register(name=e):0:16: @e', + }, + }, + { + 'Call:0:32:(', + children = { + { + 'NestingParenthesis:0:21: (', + children = { + { + 'Lambda(\\di):0:23:{', + children = { + 'PlainIdentifier(scope=0,ident=f):0:24:f', + { + 'Arrow:0:25: ->', + children = { + 'PlainIdentifier(scope=0,ident=g):0:28: g', + }, + }, + }, + }, + }, + }, + 'Register(name=h):0:33:@h', + }, + }, + }, + }, + }, + }, + }, + }, + 'Register(name=i):0:38:@i', + }, + }, + 'PlainIdentifier(scope=0,ident=j):0:42:j', + }, + }, + 'PlainIdentifier(scope=0,ident=_test):0:42:_test', + }, + }, + }, + }, + }, + }, { + hl('IdentifierScope', 'a'), + hl('IdentifierScopeDelimiter', ':'), + hl('Curly', '{'), + hl('Lambda', '{'), + hl('Identifier', 'b'), + hl('Comma', ','), + hl('Identifier', 'c', 1), + hl('Arrow', '->', 1), + hl('Register', '@d', 1), + hl('BinaryPlus', '+', 1), + hl('Register', '@e', 1), + hl('BinaryPlus', '+', 1), + hl('NestingParenthesis', '('), + hl('Lambda', '{'), + hl('Identifier', 'f'), + hl('Arrow', '->', 1), + hl('Identifier', 'g', 1), + hl('Lambda', '}'), + hl('NestingParenthesis', ')'), + hl('CallingParenthesis', '('), + hl('Register', '@h'), + hl('CallingParenthesis', ')'), + hl('Lambda', '}'), + hl('CallingParenthesis', '('), + hl('Register', '@i'), + hl('CallingParenthesis', ')'), + hl('Curly', '}'), + hl('Identifier', 'j'), + }) end) -- FIXME: Test sequence of arrows inside and outside lambdas. - -- FIXME: Test multiple arguments calling. -- FIXME: Test autoload character and scope in lambda arguments. end) -- cgit From 9fa8f7fc0a24371f7956450d840bdae8a2fc9a51 Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 28 Sep 2017 00:40:25 +0300 Subject: viml/parser/expressions: Add a way to adjust lexer It also adds support for kExprLexOr which for some reason was forgotten. It was only made sure that KLEE test compiles in non-KLEE mode, not that something works or that KLEE is able to run tests. --- test/symbolic/klee/nvim/memory.c | 8 + test/symbolic/klee/viml_expressions_lexer.c | 24 ++- test/unit/viml/expressions/lexer_spec.lua | 247 ++++++++++++++++++++++------ 3 files changed, 223 insertions(+), 56 deletions(-) (limited to 'test') diff --git a/test/symbolic/klee/nvim/memory.c b/test/symbolic/klee/nvim/memory.c index 7e924a410a..1f9cdce6c0 100644 --- a/test/symbolic/klee/nvim/memory.c +++ b/test/symbolic/klee/nvim/memory.c @@ -15,11 +15,16 @@ RINGBUF_INIT(AllocRecords, arecs, AllocRecord, RINGBUF_DUMMY_FREE) RINGBUF_STATIC(static, AllocRecords, AllocRecord, arecs, RB_SIZE) size_t allocated_memory = 0; +size_t ever_allocated_memory = 0; + +size_t allocated_memory_limit = SIZE_MAX; void *xmalloc(const size_t size) { void *ret = malloc(size); allocated_memory += size; + ever_allocated_memory += size; + assert(allocated_memory <= allocated_memory_limit); assert(arecs_rb_length(&arecs) < RB_SIZE); arecs_rb_push(&arecs, (AllocRecord) { .ptr = ret, @@ -47,6 +52,9 @@ void *xrealloc(void *const p, size_t new_size) if (arec->ptr == p) { allocated_memory -= arec->size; allocated_memory += new_size; + if (new_size > arec->size) { + ever_allocated_memory += (new_size - arec->size); + } arec->ptr = ret; arec->size = new_size; return ret; diff --git a/test/symbolic/klee/viml_expressions_lexer.c b/test/symbolic/klee/viml_expressions_lexer.c index e3b5aa80cc..67f3eb7faa 100644 --- a/test/symbolic/klee/viml_expressions_lexer.c +++ b/test/symbolic/klee/viml_expressions_lexer.c @@ -34,13 +34,20 @@ int main(const int argc, const char *const *const argv, { char input[INPUT_SIZE]; uint8_t shift; - const bool peek = false; + int flags; avoid_optimizing_out = argc; +#ifndef USE_KLEE + sscanf(argv[2], "%d", &flags); +#endif + #ifdef USE_KLEE klee_make_symbolic(input, sizeof(input), "input"); klee_make_symbolic(&shift, sizeof(shift), "shift"); + klee_make_symbolic(&flags, sizeof(flags), "flags"); klee_assume(shift < INPUT_SIZE); + klee_assume(flags <= (kELFlagPeek|kELFlagAllowFloat|kELFlagForbidEOC + |kELFlagForbidScope|kELFlagIsNotCmp)); #endif ParserLine plines[] = { @@ -79,8 +86,15 @@ int main(const int argc, const char *const *const argv, }; kvi_init(pstate.reader.lines); - LexExprToken token = viml_pexpr_next_token(&pstate, peek); - assert((pstate.pos.line == 0) - ? (pstate.pos.col > 0) - : (pstate.pos.line == 1 && pstate.pos.col == 0)); + allocated_memory_limit = 0; + LexExprToken token = viml_pexpr_next_token(&pstate, flags); + if (flags & kELFlagPeek) { + assert(pstate.pos.line == 0 && pstate.pos.col == 0); + } else { + assert((pstate.pos.line == 0) + ? (pstate.pos.col > 0) + : (pstate.pos.line == 1 && pstate.pos.col == 0)); + } + assert(allocated_memory == 0); + assert(ever_allocated_memory == 0); } diff --git a/test/unit/viml/expressions/lexer_spec.lua b/test/unit/viml/expressions/lexer_spec.lua index 32182f650d..972478c2e5 100644 --- a/test/unit/viml/expressions/lexer_spec.lua +++ b/test/unit/viml/expressions/lexer_spec.lua @@ -1,5 +1,6 @@ local helpers = require('test.unit.helpers')(after_each) local viml_helpers = require('test.unit.viml.helpers') +local global_helpers = require('test.helpers') local itp = helpers.gen_itp(it) local child_call_once = helpers.child_call_once @@ -13,6 +14,8 @@ local new_pstate = viml_helpers.new_pstate local intchar2lua = viml_helpers.intchar2lua local pstate_set_str = viml_helpers.pstate_set_str +local shallowcopy = global_helpers.shallowcopy + local lib = cimport('./src/nvim/viml/parser/expressions.h') local eltkn_type_tab, eltkn_cmp_type_tab, ccs_tab, eltkn_mul_type_tab @@ -121,37 +124,81 @@ local function eltkn2lua(pstate, tkn) scope = intchar2lua(tkn.data.var.scope), autoload = (not not tkn.data.var.autoload), } + elseif ret.type == 'Number' then + ret.data = { + is_float = (not not tkn.data.num.is_float), + } elseif ret.type == 'Invalid' then ret.data = { error = ffi.string(tkn.data.err.msg) } end return ret, tkn end -local function next_eltkn(pstate) - return eltkn2lua(pstate, lib.viml_pexpr_next_token(pstate, false)) +local function next_eltkn(pstate, flags) + return eltkn2lua(pstate, lib.viml_pexpr_next_token(pstate, flags)) end describe('Expressions lexer', function() - itp('works (single tokens)', function() - local function singl_eltkn_test(typ, str, data) - local pstate = new_pstate({str}) - eq({data=data, len=#str, start={col=0, line=0}, str=str, type=typ}, - next_eltkn(pstate)) - if not ( - typ == 'Spacing' - or (typ == 'Register' and str == '@') - or ((typ == 'SingleQuotedString' or typ == 'DoubleQuotedString') - and not data.closed) - ) then - pstate = new_pstate({str .. ' '}) - eq({data=data, len=#str, start={col=0, line=0}, str=str, type=typ}, - next_eltkn(pstate)) + local flags = 0 + local should_advance = true + local function check_advance(pstate, bytes_to_advance, initial_col) + local tgt = initial_col + bytes_to_advance + if should_advance then + if pstate.reader.lines.items[0].size == tgt then + eq(1, pstate.pos.line) + eq(0, pstate.pos.col) + else + eq(0, pstate.pos.line) + eq(tgt, pstate.pos.col) end - pstate = new_pstate({'x' .. str}) - pstate.pos.col = 1 - eq({data=data, len=#str, start={col=1, line=0}, str=str, type=typ}, - next_eltkn(pstate)) + else + eq(0, pstate.pos.line) + eq(initial_col, pstate.pos.col) end + end + local function singl_eltkn_test(typ, str, data) + local pstate = new_pstate({str}) + eq({data=data, len=#str, start={col=0, line=0}, str=str, type=typ}, + next_eltkn(pstate, flags)) + check_advance(pstate, #str, 0) + if not ( + typ == 'Spacing' + or (typ == 'Register' and str == '@') + or ((typ == 'SingleQuotedString' or typ == 'DoubleQuotedString') + and not data.closed) + ) then + pstate = new_pstate({str .. ' '}) + eq({data=data, len=#str, start={col=0, line=0}, str=str, type=typ}, + next_eltkn(pstate, flags)) + check_advance(pstate, #str, 0) + end + pstate = new_pstate({'x' .. str}) + pstate.pos.col = 1 + eq({data=data, len=#str, start={col=1, line=0}, str=str, type=typ}, + next_eltkn(pstate, flags)) + check_advance(pstate, #str, 1) + end + local function scope_test(scope) + singl_eltkn_test('PlainIdentifier', scope .. ':test#var', {autoload=true, scope=scope}) + singl_eltkn_test('PlainIdentifier', scope .. ':', {autoload=false, scope=scope}) + end + local function comparison_test(op, inv_op, cmp_type) + singl_eltkn_test('Comparison', op, {type=cmp_type, inv=false, ccs='UseOption'}) + singl_eltkn_test('Comparison', inv_op, {type=cmp_type, inv=true, ccs='UseOption'}) + singl_eltkn_test('Comparison', op .. '#', {type=cmp_type, inv=false, ccs='MatchCase'}) + singl_eltkn_test('Comparison', inv_op .. '#', {type=cmp_type, inv=true, ccs='MatchCase'}) + singl_eltkn_test('Comparison', op .. '?', {type=cmp_type, inv=false, ccs='IgnoreCase'}) + singl_eltkn_test('Comparison', inv_op .. '?', {type=cmp_type, inv=true, ccs='IgnoreCase'}) + end + local function simple_test(pstate_arg, exp_type, exp_len, exp) + local pstate = new_pstate(pstate_arg) + local exp = shallowcopy(exp) + exp.type = exp_type + exp.len = exp_len or #(pstate_arg[0]) + exp.start = { col = 0, line = 0 } + eq(exp, next_eltkn(pstate, flags)) + end + local function stable_tests() singl_eltkn_test('Parenthesis', '(', {closing=false}) singl_eltkn_test('Parenthesis', ')', {closing=true}) singl_eltkn_test('Bracket', '[', {closing=false}) @@ -170,9 +217,9 @@ describe('Expressions lexer', function() singl_eltkn_test('Spacing', ' ') singl_eltkn_test('Spacing', '\t') singl_eltkn_test('Invalid', '\x01\x02\x03', {error='E15: Invalid control character present in input: %.*s'}) - singl_eltkn_test('Number', '0123') - singl_eltkn_test('Number', '0') - singl_eltkn_test('Number', '9') + singl_eltkn_test('Number', '0123', {is_float=false}) + singl_eltkn_test('Number', '0', {is_float=false}) + singl_eltkn_test('Number', '9', {is_float=false}) singl_eltkn_test('Env', '$abc') singl_eltkn_test('Env', '$') singl_eltkn_test('PlainIdentifier', 'test', {autoload=false, scope=0}) @@ -184,28 +231,8 @@ describe('Expressions lexer', function() singl_eltkn_test('PlainIdentifier', 'test#var', {autoload=true, scope=0}) singl_eltkn_test('PlainIdentifier', 'test#var#val###', {autoload=true, scope=0}) singl_eltkn_test('PlainIdentifier', 't#####', {autoload=true, scope=0}) - local function scope_test(scope) - singl_eltkn_test('PlainIdentifier', scope .. ':test#var', {autoload=true, scope=scope}) - singl_eltkn_test('PlainIdentifier', scope .. ':', {autoload=false, scope=scope}) - end - scope_test('s') - scope_test('g') - scope_test('v') - scope_test('b') - scope_test('w') - scope_test('t') - scope_test('l') - scope_test('a') - local function comparison_test(op, inv_op, cmp_type) - singl_eltkn_test('Comparison', op, {type=cmp_type, inv=false, ccs='UseOption'}) - singl_eltkn_test('Comparison', inv_op, {type=cmp_type, inv=true, ccs='UseOption'}) - singl_eltkn_test('Comparison', op .. '#', {type=cmp_type, inv=false, ccs='MatchCase'}) - singl_eltkn_test('Comparison', inv_op .. '#', {type=cmp_type, inv=true, ccs='MatchCase'}) - singl_eltkn_test('Comparison', op .. '?', {type=cmp_type, inv=false, ccs='IgnoreCase'}) - singl_eltkn_test('Comparison', inv_op .. '?', {type=cmp_type, inv=true, ccs='IgnoreCase'}) - end - comparison_test('is', 'isnot', 'Identical') singl_eltkn_test('And', '&&') + singl_eltkn_test('Or', '||') singl_eltkn_test('Invalid', '&', {error='E112: Option name missing: %.*s'}) singl_eltkn_test('Option', '&opt', {scope='Unspecified', name='opt'}) singl_eltkn_test('Option', '&t_xx', {scope='Unspecified', name='t_xx'}) @@ -245,16 +272,134 @@ describe('Expressions lexer', function() comparison_test('>=', '<', 'GreaterOrEqual') singl_eltkn_test('Minus', '-') singl_eltkn_test('Arrow', '->') + singl_eltkn_test('Invalid', '~', {error='E15: Unidentified character: %.*s'}) + simple_test({{data=nil, size=0}}, 'EOC', 0, {error='start.col >= #pstr'}) + simple_test({''}, 'EOC', 0, {error='start.col >= #pstr'}) + simple_test({'2.'}, 'Number', 1, {data={is_float=false}, str='2'}) + simple_test({'2.x'}, 'Number', 1, {data={is_float=false}, str='2'}) + end + + local function regular_scope_tests() + scope_test('s') + scope_test('g') + scope_test('v') + scope_test('b') + scope_test('w') + scope_test('t') + scope_test('l') + scope_test('a') + + simple_test({'g:'}, 'PlainIdentifier', 2, {data={scope='g', autoload=false}, str='g:'}) + simple_test({'g:is#foo'}, 'PlainIdentifier', 8, {data={scope='g', autoload=true}, str='g:is#foo'}) + simple_test({'g:isnot#foo'}, 'PlainIdentifier', 11, {data={scope='g', autoload=true}, str='g:isnot#foo'}) + end + + local function regular_is_tests() + comparison_test('is', 'isnot', 'Identical') + + simple_test({'is'}, 'Comparison', 2, {data={type='Identical', inv=false, ccs='UseOption'}, str='is'}) + simple_test({'isnot'}, 'Comparison', 5, {data={type='Identical', inv=true, ccs='UseOption'}, str='isnot'}) + simple_test({'is?'}, 'Comparison', 3, {data={type='Identical', inv=false, ccs='IgnoreCase'}, str='is?'}) + simple_test({'isnot?'}, 'Comparison', 6, {data={type='Identical', inv=true, ccs='IgnoreCase'}, str='isnot?'}) + simple_test({'is#'}, 'Comparison', 3, {data={type='Identical', inv=false, ccs='MatchCase'}, str='is#'}) + simple_test({'isnot#'}, 'Comparison', 6, {data={type='Identical', inv=true, ccs='MatchCase'}, str='isnot#'}) + simple_test({'is#foo'}, 'Comparison', 3, {data={type='Identical', inv=false, ccs='MatchCase'}, str='is#'}) + simple_test({'isnot#foo'}, 'Comparison', 6, {data={type='Identical', inv=true, ccs='MatchCase'}, str='isnot#'}) + end + + local function regular_number_tests() + simple_test({'2.0'}, 'Number', 1, {data={is_float=false}, str='2'}) + simple_test({'2.0x'}, 'Number', 1, {data={is_float=false}, str='2'}) + simple_test({'2.0e'}, 'Number', 1, {data={is_float=false}, str='2'}) + simple_test({'2.0e+'}, 'Number', 1, {data={is_float=false}, str='2'}) + simple_test({'2.0e-'}, 'Number', 1, {data={is_float=false}, str='2'}) + simple_test({'2.0e+x'}, 'Number', 1, {data={is_float=false}, str='2'}) + simple_test({'2.0e-x'}, 'Number', 1, {data={is_float=false}, str='2'}) + simple_test({'2.0e5'}, 'Number', 1, {data={is_float=false}, str='2'}) + simple_test({'2.0e+5'}, 'Number', 1, {data={is_float=false}, str='2'}) + simple_test({'2.0e-5'}, 'Number', 1, {data={is_float=false}, str='2'}) + end + + local function regular_eoc_tests() + singl_eltkn_test('EOC', '|') singl_eltkn_test('EOC', '\0') singl_eltkn_test('EOC', '\n') - singl_eltkn_test('Invalid', '~', {error='E15: Unidentified character: %.*s'}) + end + + itp('works (single tokens, zero flags)', function() + stable_tests() + + regular_eoc_tests() + regular_scope_tests() + regular_is_tests() + regular_number_tests() + end) + itp('peeks', function() + flags = tonumber(lib.kELFlagPeek) + should_advance = false + stable_tests() + + regular_eoc_tests() + regular_scope_tests() + regular_is_tests() + regular_number_tests() + end) + itp('forbids scope', function() + flags = tonumber(lib.kELFlagForbidScope) + stable_tests() + + regular_eoc_tests() + regular_is_tests() + regular_number_tests() + + simple_test({'g:'}, 'PlainIdentifier', 1, {data={scope=0, autoload=false}, str='g'}) + end) + itp('allows floats', function() + flags = tonumber(lib.kELFlagAllowFloat) + stable_tests() + + regular_eoc_tests() + regular_scope_tests() + regular_is_tests() + + simple_test({'2.0'}, 'Number', 3, {data={is_float=true}, str='2.0'}) + simple_test({'2.0x'}, 'Number', 3, {data={is_float=true}, str='2.0'}) + simple_test({'2.0e'}, 'Number', 3, {data={is_float=true}, str='2.0'}) + simple_test({'2.0e+'}, 'Number', 3, {data={is_float=true}, str='2.0'}) + simple_test({'2.0e-'}, 'Number', 3, {data={is_float=true}, str='2.0'}) + simple_test({'2.0e+x'}, 'Number', 3, {data={is_float=true}, str='2.0'}) + simple_test({'2.0e-x'}, 'Number', 3, {data={is_float=true}, str='2.0'}) + simple_test({'2.0e5'}, 'Number', 5, {data={is_float=true}, str='2.0e5'}) + simple_test({'2.0e+5'}, 'Number', 6, {data={is_float=true}, str='2.0e+5'}) + simple_test({'2.0e-5'}, 'Number', 6, {data={is_float=true}, str='2.0e-5'}) + end) + itp('treats `is` as an identifier', function() + flags = tonumber(lib.kELFlagIsNotCmp) + stable_tests() + + regular_eoc_tests() + regular_scope_tests() + regular_number_tests() + + simple_test({'is'}, 'PlainIdentifier', 2, {data={scope=0, autoload=false}, str='is'}) + simple_test({'isnot'}, 'PlainIdentifier', 5, {data={scope=0, autoload=false}, str='isnot'}) + simple_test({'is?'}, 'PlainIdentifier', 2, {data={scope=0, autoload=false}, str='is'}) + simple_test({'isnot?'}, 'PlainIdentifier', 5, {data={scope=0, autoload=false}, str='isnot'}) + simple_test({'is#'}, 'PlainIdentifier', 3, {data={scope=0, autoload=true}, str='is#'}) + simple_test({'isnot#'}, 'PlainIdentifier', 6, {data={scope=0, autoload=true}, str='isnot#'}) + simple_test({'is#foo'}, 'PlainIdentifier', 6, {data={scope=0, autoload=true}, str='is#foo'}) + simple_test({'isnot#foo'}, 'PlainIdentifier', 9, {data={scope=0, autoload=true}, str='isnot#foo'}) + end) + itp('forbids EOC', function() + flags = tonumber(lib.kELFlagForbidEOC) + stable_tests() - local pstate = new_pstate({{data=nil, size=0}}) - eq({len=0, error='start.col >= #pstr', start={col=0, line=0}, type='EOC'}, - next_eltkn(pstate)) + regular_scope_tests() + regular_is_tests() + regular_number_tests() - local pstate = new_pstate({''}) - eq({len=0, error='start.col >= #pstr', start={col=0, line=0}, type='EOC'}, - next_eltkn(pstate)) + singl_eltkn_test('Invalid', '|', {error='E15: Unexpected EOC character: %.*s'}) + singl_eltkn_test('Invalid', '\0', {error='E15: Unexpected EOC character: %.*s'}) + singl_eltkn_test('Invalid', '\n', {error='E15: Unexpected EOC character: %.*s'}) end) end) -- cgit From f2650660819ddeae97c26114a97aff9608bd226e Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 29 Sep 2017 01:13:35 +0300 Subject: unittests: Add support for dumping “expected” state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Purpose is similar to that of `screen:snapshot_util()`, but in different domain. --- test/unit/viml/expressions/parser_spec.lua | 53 ++++++++++++++++++++++++++++-- 1 file changed, 51 insertions(+), 2 deletions(-) (limited to 'test') diff --git a/test/unit/viml/expressions/parser_spec.lua b/test/unit/viml/expressions/parser_spec.lua index eec4cb5bd9..51db2dd2d9 100644 --- a/test/unit/viml/expressions/parser_spec.lua +++ b/test/unit/viml/expressions/parser_spec.lua @@ -1,5 +1,6 @@ local helpers = require('test.unit.helpers')(after_each) local viml_helpers = require('test.unit.viml.helpers') +local global_helpers = require('test.helpers') local itp = helpers.gen_itp(it) local make_enum_conv_tab = helpers.make_enum_conv_tab @@ -15,8 +16,51 @@ local new_pstate = viml_helpers.new_pstate local intchar2lua = viml_helpers.intchar2lua local pstate_set_str = viml_helpers.pstate_set_str +local format_string = global_helpers.format_string +local format_luav = global_helpers.format_luav + local lib = cimport('./src/nvim/viml/parser/expressions.h') +local function format_check(expr, flags, ast, hls) + -- That forces specific order. + print( format_string('\ncheck_parsing(%r, %u, {', expr, flags)) + local digits = ' -- ' + local digits2 = ' -- ' + for i = 0, #expr - 1 do + if i % 10 == 0 then + digits2 = ('%s%10u'):format(digits2, i / 10) + end + digits = ('%s%u'):format(digits, i % 10) + end + print(digits) + if #expr > 10 then + print(digits2) + end + print(' ast = ' .. format_luav(ast.ast, ' ') .. ',') + if ast.err then + print(' err = {') + print(' arg = ' .. format_luav(ast.err.arg) .. ',') + print(' msg = ' .. format_luav(ast.err.msg) .. ',') + print(' },') + end + print('}, {') + local next_col = 0 + for _, v in ipairs(hls) do + local group, line, col, str = v:match('NVim([a-zA-Z]+):(%d+):(%d+):(.*)') + col = tonumber(col) + line = tonumber(line) + assert(line == 0) + local col_shift = col - next_col + assert(col_shift >= 0) + next_col = col + #str + print(format_string(' hl(%r, %r%s),', + group, + str, + (col_shift == 0 and '' or (', %u'):format(col_shift)))) + end + print('})') +end + local east_node_type_tab make_enum_conv_tab(lib, { 'kExprNodeMissing', @@ -137,10 +181,15 @@ child_call_once(function() end) describe('Expressions parser', function() - local function check_parsing(str, flags, exp_ast, exp_highlighting_fs) + local function check_parsing(str, flags, exp_ast, exp_highlighting_fs, + print_exp) local pstate = new_pstate({str}) local east = lib.viml_pexpr_parse(pstate, flags) local ast = east2lua(pstate, east) + local hls = phl2lua(pstate) + if print_exp then + format_check(str, flags, ast, hls) + end eq(exp_ast, ast) if exp_highlighting_fs then local exp_highlighting = {} @@ -148,7 +197,7 @@ describe('Expressions parser', function() for i, h in ipairs(exp_highlighting_fs) do exp_highlighting[i], next_col = h(next_col) end - eq(exp_highlighting, phl2lua(pstate)) + eq(exp_highlighting, hls) end end local function hl(group, str, shift) -- cgit From 3735537a508c5690c4622ebe450e6f3f15706670 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 1 Oct 2017 15:54:46 +0300 Subject: viml/parser/expressions: Fix call inside nested parenthesis MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It may have incorrectly tried to call everything because of essentially “value” nodes being treated as not such. --- test/unit/viml/expressions/parser_spec.lua | 49 +++++++++++++++++++++++++----- 1 file changed, 42 insertions(+), 7 deletions(-) (limited to 'test') diff --git a/test/unit/viml/expressions/parser_spec.lua b/test/unit/viml/expressions/parser_spec.lua index 51db2dd2d9..a2b76ccf8d 100644 --- a/test/unit/viml/expressions/parser_spec.lua +++ b/test/unit/viml/expressions/parser_spec.lua @@ -2269,7 +2269,43 @@ describe('Expressions parser', function() hl('CallingParenthesis', ')'), hl('Curly', '}'), }) - -- FIXME the below should not crash + check_parsing('a:{b()}c', 0, { + -- 01234567 + ast = { + { + 'ComplexIdentifier:0:2:', + children = { + 'PlainIdentifier(scope=a,ident=):0:0:a:', + { + 'ComplexIdentifier:0:7:', + children = { + { + 'CurlyBracesIdentifier(--i):0:2:{', + children = { + { + 'Call:0:4:(', + children = { + 'PlainIdentifier(scope=0,ident=b):0:3:b', + }, + }, + }, + }, + 'PlainIdentifier(scope=0,ident=c):0:7:c', + }, + }, + }, + }, + }, + }, { + hl('IdentifierScope', 'a'), + hl('IdentifierScopeDelimiter', ':'), + hl('Curly', '{'), + hl('Identifier', 'b'), + hl('CallingParenthesis', '('), + hl('CallingParenthesis', ')'), + hl('Curly', '}'), + hl('Identifier', 'c'), + }) check_parsing('a:{{b, c -> @d + @e + ({f -> g})(@h)}(@i)}j', 0, { -- 01234567890123456789012345678901234567890123456 -- 0 1 2 3 4 @@ -2277,9 +2313,9 @@ describe('Expressions parser', function() { 'ComplexIdentifier:0:2:', children = { - 'PlainIdentifier(scope=a,ident=):0:0:g:', + 'PlainIdentifier(scope=a,ident=):0:0:a:', { - 'ComplexIdentifier:0:6:', + 'ComplexIdentifier:0:42:', children = { { 'CurlyBracesIdentifier(--i):0:2:{', @@ -2314,7 +2350,7 @@ describe('Expressions parser', function() 'Call:0:32:(', children = { { - 'NestingParenthesis:0:21: (', + 'Nested:0:21: (', children = { { 'Lambda(\\di):0:23:{', @@ -2342,10 +2378,9 @@ describe('Expressions parser', function() 'Register(name=i):0:38:@i', }, }, - 'PlainIdentifier(scope=0,ident=j):0:42:j', }, }, - 'PlainIdentifier(scope=0,ident=_test):0:42:_test', + 'PlainIdentifier(scope=0,ident=j):0:42:j', }, }, }, @@ -2364,7 +2399,7 @@ describe('Expressions parser', function() hl('BinaryPlus', '+', 1), hl('Register', '@e', 1), hl('BinaryPlus', '+', 1), - hl('NestingParenthesis', '('), + hl('NestingParenthesis', '(', 1), hl('Lambda', '{'), hl('Identifier', 'f'), hl('Arrow', '->', 1), -- cgit From 9e721031d597bfa435da03597939191970f7a918 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 1 Oct 2017 16:50:46 +0300 Subject: viml/parser/expressions: Fix determining invalid commas/colons --- test/unit/viml/expressions/parser_spec.lua | 78 ++++++++++++++++++++++++++++-- 1 file changed, 75 insertions(+), 3 deletions(-) (limited to 'test') diff --git a/test/unit/viml/expressions/parser_spec.lua b/test/unit/viml/expressions/parser_spec.lua index a2b76ccf8d..1f734c3c2a 100644 --- a/test/unit/viml/expressions/parser_spec.lua +++ b/test/unit/viml/expressions/parser_spec.lua @@ -181,14 +181,14 @@ child_call_once(function() end) describe('Expressions parser', function() - local function check_parsing(str, flags, exp_ast, exp_highlighting_fs, - print_exp) + local function check_parsing(str, flags, exp_ast, exp_highlighting_fs) local pstate = new_pstate({str}) local east = lib.viml_pexpr_parse(pstate, flags) local ast = east2lua(pstate, east) local hls = phl2lua(pstate) - if print_exp then + if exp_ast == nil then format_check(str, flags, ast, hls) + return end eq(exp_ast, ast) if exp_highlighting_fs then @@ -2416,6 +2416,78 @@ describe('Expressions parser', function() hl('Curly', '}'), hl('Identifier', 'j'), }) + check_parsing('{@a + @b : @c + @d, @e + @f : @g + @i}', 0, { + -- 01234567890123456789012345678901234567 + -- 0 1 2 3 + ast = { + { + 'DictLiteral(-di):0:0:{', + children = { + { + 'Comma:0:18:,', + children = { + { + 'Colon:0:8: :', + children = { + { + 'BinaryPlus:0:3: +', + children = { + 'Register(name=a):0:1:@a', + 'Register(name=b):0:5: @b', + }, + }, + { + 'BinaryPlus:0:13: +', + children = { + 'Register(name=c):0:10: @c', + 'Register(name=d):0:15: @d', + }, + }, + }, + }, + { + 'Colon:0:27: :', + children = { + { + 'BinaryPlus:0:22: +', + children = { + 'Register(name=e):0:19: @e', + 'Register(name=f):0:24: @f', + }, + }, + { + 'BinaryPlus:0:32: +', + children = { + 'Register(name=g):0:29: @g', + 'Register(name=i):0:34: @i', + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, { + hl('Dict', '{'), + hl('Register', '@a'), + hl('BinaryPlus', '+', 1), + hl('Register', '@b', 1), + hl('Colon', ':', 1), + hl('Register', '@c', 1), + hl('BinaryPlus', '+', 1), + hl('Register', '@d', 1), + hl('Comma', ','), + hl('Register', '@e', 1), + hl('BinaryPlus', '+', 1), + hl('Register', '@f', 1), + hl('Colon', ':', 1), + hl('Register', '@g', 1), + hl('BinaryPlus', '+', 1), + hl('Register', '@i', 1), + hl('Dict', '}'), + }) end) -- FIXME: Test sequence of arrows inside and outside lambdas. -- FIXME: Test autoload character and scope in lambda arguments. -- cgit From 6144e26eb920a90b0db22bd7afcac0b9e0734ed6 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 1 Oct 2017 22:35:41 +0300 Subject: viml/parser/expressions: Add support for ternary operator --- test/unit/viml/expressions/parser_spec.lua | 651 ++++++++++++++++++++++++++++- 1 file changed, 649 insertions(+), 2 deletions(-) (limited to 'test') diff --git a/test/unit/viml/expressions/parser_spec.lua b/test/unit/viml/expressions/parser_spec.lua index 1f734c3c2a..ea37d64662 100644 --- a/test/unit/viml/expressions/parser_spec.lua +++ b/test/unit/viml/expressions/parser_spec.lua @@ -66,6 +66,7 @@ make_enum_conv_tab(lib, { 'kExprNodeMissing', 'kExprNodeOpMissing', 'kExprNodeTernary', + 'kExprNodeTernaryValue', 'kExprNodeRegister', 'kExprNodeSubscript', 'kExprNodeListLiteral', @@ -2489,6 +2490,652 @@ describe('Expressions parser', function() hl('Dict', '}'), }) end) - -- FIXME: Test sequence of arrows inside and outside lambdas. - -- FIXME: Test autoload character and scope in lambda arguments. + itp('works with ternary operator', function() + check_parsing('a ? b : c', 0, { + -- 012345678 + ast = { + { + 'Ternary:0:1: ?', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + { + 'TernaryValue:0:5: :', + children = { + 'PlainIdentifier(scope=0,ident=b):0:3: b', + 'PlainIdentifier(scope=0,ident=c):0:7: c', + }, + }, + }, + }, + }, + }, { + hl('Identifier', 'a'), + hl('Ternary', '?', 1), + hl('Identifier', 'b', 1), + hl('TernaryColon', ':', 1), + hl('Identifier', 'c', 1), + }) + check_parsing('@a?@b?@c:@d:@e', 0, { + -- 01234567890123 + -- 0 1 + ast = { + { + 'Ternary:0:2:?', + children = { + 'Register(name=a):0:0:@a', + { + 'TernaryValue:0:11::', + children = { + { + 'Ternary:0:5:?', + children = { + 'Register(name=b):0:3:@b', + { + 'TernaryValue:0:8::', + children = { + 'Register(name=c):0:6:@c', + 'Register(name=d):0:9:@d', + }, + }, + }, + }, + 'Register(name=e):0:12:@e', + }, + }, + }, + }, + }, + }, { + hl('Register', '@a'), + hl('Ternary', '?'), + hl('Register', '@b'), + hl('Ternary', '?'), + hl('Register', '@c'), + hl('TernaryColon', ':'), + hl('Register', '@d'), + hl('TernaryColon', ':'), + hl('Register', '@e'), + }) + check_parsing('@a?@b:@c?@d:@e', 0, { + -- 01234567890123 + -- 0 1 + ast = { + { + 'Ternary:0:2:?', + children = { + 'Register(name=a):0:0:@a', + { + 'TernaryValue:0:5::', + children = { + 'Register(name=b):0:3:@b', + { + 'Ternary:0:8:?', + children = { + 'Register(name=c):0:6:@c', + { + 'TernaryValue:0:11::', + children = { + 'Register(name=d):0:9:@d', + 'Register(name=e):0:12:@e', + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, { + hl('Register', '@a'), + hl('Ternary', '?'), + hl('Register', '@b'), + hl('TernaryColon', ':'), + hl('Register', '@c'), + hl('Ternary', '?'), + hl('Register', '@d'), + hl('TernaryColon', ':'), + hl('Register', '@e'), + }) + check_parsing('@a?@b?@c?@d:@e?@f:@g:@h?@i:@j:@k', 0, { + -- 01234567890123456789012345678901 + -- 0 1 2 3 + ast = { + { + 'Ternary:0:2:?', + children = { + 'Register(name=a):0:0:@a', + { + 'TernaryValue:0:29::', + children = { + { + 'Ternary:0:5:?', + children = { + 'Register(name=b):0:3:@b', + { + 'TernaryValue:0:20::', + children = { + { + 'Ternary:0:8:?', + children = { + 'Register(name=c):0:6:@c', + { + 'TernaryValue:0:11::', + children = { + 'Register(name=d):0:9:@d', + { + 'Ternary:0:14:?', + children = { + 'Register(name=e):0:12:@e', + { + 'TernaryValue:0:17::', + children = { + 'Register(name=f):0:15:@f', + 'Register(name=g):0:18:@g', + }, + }, + }, + }, + }, + }, + }, + }, + { + 'Ternary:0:23:?', + children = { + 'Register(name=h):0:21:@h', + { + 'TernaryValue:0:26::', + children = { + 'Register(name=i):0:24:@i', + 'Register(name=j):0:27:@j', + }, + }, + }, + }, + }, + }, + }, + }, + 'Register(name=k):0:30:@k', + }, + }, + }, + }, + }, + }, { + hl('Register', '@a'), + hl('Ternary', '?'), + hl('Register', '@b'), + hl('Ternary', '?'), + hl('Register', '@c'), + hl('Ternary', '?'), + hl('Register', '@d'), + hl('TernaryColon', ':'), + hl('Register', '@e'), + hl('Ternary', '?'), + hl('Register', '@f'), + hl('TernaryColon', ':'), + hl('Register', '@g'), + hl('TernaryColon', ':'), + hl('Register', '@h'), + hl('Ternary', '?'), + hl('Register', '@i'), + hl('TernaryColon', ':'), + hl('Register', '@j'), + hl('TernaryColon', ':'), + hl('Register', '@k'), + }) + check_parsing('?', 0, { + -- 0 + ast = { + { + 'Ternary:0:0:?', + children = { + 'Missing:0:0:', + 'TernaryValue:0:0:?', + }, + }, + }, + err = { + arg = '?', + msg = 'E15: Expected value, got question mark: %.*s', + }, + }, { + hl('InvalidTernary', '?'), + }) + + check_parsing('?:', 0, { + -- 01 + ast = { + { + 'Ternary:0:0:?', + children = { + 'Missing:0:0:', + { + 'TernaryValue:0:1::', + children = { + 'Missing:0:1:', + }, + }, + }, + }, + }, + err = { + arg = '?:', + msg = 'E15: Expected value, got question mark: %.*s', + }, + }, { + hl('InvalidTernary', '?'), + hl('InvalidTernaryColon', ':'), + }) + + check_parsing('?::', 0, { + -- 012 + ast = { + { + 'Colon:0:2::', + children = { + { + 'Ternary:0:0:?', + children = { + 'Missing:0:0:', + { + 'TernaryValue:0:1::', + children = { + 'Missing:0:1:', + 'Missing:0:2:', + }, + }, + }, + }, + }, + }, + }, + err = { + arg = '?::', + msg = 'E15: Expected value, got question mark: %.*s', + }, + }, { + hl('InvalidTernary', '?'), + hl('InvalidTernaryColon', ':'), + hl('InvalidColon', ':'), + }) + + check_parsing('a?b', 0, { + -- 012 + ast = { + { + 'Ternary:0:1:?', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + { + 'TernaryValue:0:1:?', + children = { + 'PlainIdentifier(scope=0,ident=b):0:2:b', + }, + }, + }, + }, + }, + err = { + arg = '?b', + msg = 'E109: Missing \':\' after \'?\': %.*s', + }, + }, { + hl('Identifier', 'a'), + hl('Ternary', '?'), + hl('Identifier', 'b'), + }) + check_parsing('a?b:', 0, { + -- 0123 + ast = { + { + 'Ternary:0:1:?', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + { + 'TernaryValue:0:1:?', + children = { + 'PlainIdentifier(scope=b,ident=):0:2:b:', + }, + }, + }, + }, + }, + err = { + arg = '?b:', + msg = 'E109: Missing \':\' after \'?\': %.*s', + }, + }, { + hl('Identifier', 'a'), + hl('Ternary', '?'), + hl('IdentifierScope', 'b'), + hl('IdentifierScopeDelimiter', ':'), + }) + + check_parsing('a?b::c', 0, { + -- 012345 + ast = { + { + 'Ternary:0:1:?', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + { + 'TernaryValue:0:4::', + children = { + 'PlainIdentifier(scope=b,ident=):0:2:b:', + 'PlainIdentifier(scope=0,ident=c):0:5:c', + }, + }, + }, + }, + }, + }, { + hl('Identifier', 'a'), + hl('Ternary', '?'), + hl('IdentifierScope', 'b'), + hl('IdentifierScopeDelimiter', ':'), + hl('TernaryColon', ':'), + hl('Identifier', 'c'), + }) + + check_parsing('a?b :', 0, { + -- 01234 + ast = { + { + 'Ternary:0:1:?', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + { + 'TernaryValue:0:3: :', + children = { + 'PlainIdentifier(scope=0,ident=b):0:2:b', + }, + }, + }, + }, + }, + err = { + arg = '', + msg = 'E15: Expected value, got EOC: %.*s', + }, + }, { + hl('Identifier', 'a'), + hl('Ternary', '?'), + hl('Identifier', 'b'), + hl('TernaryColon', ':', 1), + }) + + check_parsing('(@a?@b:@c)?@d:@e', 0, { + -- 0123456789012345 + -- 0 1 + ast = { + { + 'Ternary:0:10:?', + children = { + { + 'Nested:0:0:(', + children = { + { + 'Ternary:0:3:?', + children = { + 'Register(name=a):0:1:@a', + { + 'TernaryValue:0:6::', + children = { + 'Register(name=b):0:4:@b', + 'Register(name=c):0:7:@c', + }, + }, + }, + }, + }, + }, + { + 'TernaryValue:0:13::', + children = { + 'Register(name=d):0:11:@d', + 'Register(name=e):0:14:@e', + }, + }, + }, + }, + }, + }, { + hl('NestingParenthesis', '('), + hl('Register', '@a'), + hl('Ternary', '?'), + hl('Register', '@b'), + hl('TernaryColon', ':'), + hl('Register', '@c'), + hl('NestingParenthesis', ')'), + hl('Ternary', '?'), + hl('Register', '@d'), + hl('TernaryColon', ':'), + hl('Register', '@e'), + }) + + check_parsing('(@a?@b:@c)?(@d?@e:@f):(@g?@h:@i)', 0, { + -- 01234567890123456789012345678901 + -- 0 1 2 3 + ast = { + { + 'Ternary:0:10:?', + children = { + { + 'Nested:0:0:(', + children = { + { + 'Ternary:0:3:?', + children = { + 'Register(name=a):0:1:@a', + { + 'TernaryValue:0:6::', + children = { + 'Register(name=b):0:4:@b', + 'Register(name=c):0:7:@c', + }, + }, + }, + }, + }, + }, + { + 'TernaryValue:0:21::', + children = { + { + 'Nested:0:11:(', + children = { + { + 'Ternary:0:14:?', + children = { + 'Register(name=d):0:12:@d', + { + 'TernaryValue:0:17::', + children = { + 'Register(name=e):0:15:@e', + 'Register(name=f):0:18:@f', + }, + }, + }, + }, + }, + }, + { + 'Nested:0:22:(', + children = { + { + 'Ternary:0:25:?', + children = { + 'Register(name=g):0:23:@g', + { + 'TernaryValue:0:28::', + children = { + 'Register(name=h):0:26:@h', + 'Register(name=i):0:29:@i', + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, { + hl('NestingParenthesis', '('), + hl('Register', '@a'), + hl('Ternary', '?'), + hl('Register', '@b'), + hl('TernaryColon', ':'), + hl('Register', '@c'), + hl('NestingParenthesis', ')'), + hl('Ternary', '?'), + hl('NestingParenthesis', '('), + hl('Register', '@d'), + hl('Ternary', '?'), + hl('Register', '@e'), + hl('TernaryColon', ':'), + hl('Register', '@f'), + hl('NestingParenthesis', ')'), + hl('TernaryColon', ':'), + hl('NestingParenthesis', '('), + hl('Register', '@g'), + hl('Ternary', '?'), + hl('Register', '@h'), + hl('TernaryColon', ':'), + hl('Register', '@i'), + hl('NestingParenthesis', ')'), + }) + + check_parsing('(@a?@b:@c)?@d?@e:@f:@g?@h:@i', 0, { + -- 0123456789012345678901234567 + -- 0 1 2 + ast = { + { + 'Ternary:0:10:?', + children = { + { + 'Nested:0:0:(', + children = { + { + 'Ternary:0:3:?', + children = { + 'Register(name=a):0:1:@a', + { + 'TernaryValue:0:6::', + children = { + 'Register(name=b):0:4:@b', + 'Register(name=c):0:7:@c', + }, + }, + }, + }, + }, + }, + { + 'TernaryValue:0:19::', + children = { + { + 'Ternary:0:13:?', + children = { + 'Register(name=d):0:11:@d', + { + 'TernaryValue:0:16::', + children = { + 'Register(name=e):0:14:@e', + 'Register(name=f):0:17:@f', + }, + }, + }, + }, + { + 'Ternary:0:22:?', + children = { + 'Register(name=g):0:20:@g', + { + 'TernaryValue:0:25::', + children = { + 'Register(name=h):0:23:@h', + 'Register(name=i):0:26:@i', + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, { + hl('NestingParenthesis', '('), + hl('Register', '@a'), + hl('Ternary', '?'), + hl('Register', '@b'), + hl('TernaryColon', ':'), + hl('Register', '@c'), + hl('NestingParenthesis', ')'), + hl('Ternary', '?'), + hl('Register', '@d'), + hl('Ternary', '?'), + hl('Register', '@e'), + hl('TernaryColon', ':'), + hl('Register', '@f'), + hl('TernaryColon', ':'), + hl('Register', '@g'), + hl('Ternary', '?'), + hl('Register', '@h'), + hl('TernaryColon', ':'), + hl('Register', '@i'), + }) + check_parsing('a?b{cdef}g:h', 0, { + -- 012345678901 + -- 0 1 + ast = { + { + 'Ternary:0:1:?', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + { + 'TernaryValue:0:10::', + children = { + { + 'ComplexIdentifier:0:3:', + children = { + 'PlainIdentifier(scope=0,ident=b):0:2:b', + { + 'ComplexIdentifier:0:9:', + children = { + { + 'CurlyBracesIdentifier(--i):0:3:{', + children = { + 'PlainIdentifier(scope=0,ident=cdef):0:4:cdef', + }, + }, + 'PlainIdentifier(scope=0,ident=g):0:9:g', + }, + }, + }, + }, + 'PlainIdentifier(scope=0,ident=h):0:11:h', + }, + }, + }, + }, + }, + }, { + hl('Identifier', 'a'), + hl('Ternary', '?'), + hl('Identifier', 'b'), + hl('Curly', '{'), + hl('Identifier', 'cdef'), + hl('Curly', '}'), + hl('Identifier', 'g'), + hl('TernaryColon', ':'), + hl('Identifier', 'h'), + }) + end) end) -- cgit From 6791c574209c83570746c139d93f8e6a6b9cd135 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 2 Oct 2017 01:22:35 +0300 Subject: viml/parser/expressions: Make sure that arrows outside lambda throw --- test/unit/viml/expressions/parser_spec.lua | 190 +++++++++++++++++++++++++++++ 1 file changed, 190 insertions(+) (limited to 'test') diff --git a/test/unit/viml/expressions/parser_spec.lua b/test/unit/viml/expressions/parser_spec.lua index ea37d64662..2c80b437dc 100644 --- a/test/unit/viml/expressions/parser_spec.lua +++ b/test/unit/viml/expressions/parser_spec.lua @@ -2489,6 +2489,196 @@ describe('Expressions parser', function() hl('Register', '@i', 1), hl('Dict', '}'), }) + check_parsing('-> -> ->', 0, { + -- 01234567 + ast = { + { + 'Arrow:0:0:->', + children = { + 'Missing:0:0:', + { + 'Arrow:0:2: ->', + children = { + 'Missing:0:2:', + { + 'Arrow:0:5: ->', + children = { + 'Missing:0:5:', + }, + }, + }, + }, + }, + }, + }, + err = { + arg = '-> -> ->', + msg = 'E15: Unexpected arrow: %.*s', + }, + }, { + hl('InvalidArrow', '->'), + hl('InvalidArrow', '->', 1), + hl('InvalidArrow', '->', 1), + }) + check_parsing('a -> b -> c -> d', 0, { + -- 0123456789012345 + -- 0 1 + ast = { + { + 'Arrow:0:1: ->', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + { + 'Arrow:0:6: ->', + children = { + 'PlainIdentifier(scope=0,ident=b):0:4: b', + { + 'Arrow:0:11: ->', + children = { + 'PlainIdentifier(scope=0,ident=c):0:9: c', + 'PlainIdentifier(scope=0,ident=d):0:14: d', + }, + }, + }, + }, + }, + }, + }, + err = { + arg = '-> b -> c -> d', + msg = 'E15: Arrow outside of lambda: %.*s', + }, + }, { + hl('Identifier', 'a'), + hl('InvalidArrow', '->', 1), + hl('Identifier', 'b', 1), + hl('InvalidArrow', '->', 1), + hl('Identifier', 'c', 1), + hl('InvalidArrow', '->', 1), + hl('Identifier', 'd', 1), + }) + check_parsing('{a -> b -> c}', 0, { + -- 0123456789012 + -- 0 1 + ast = { + { + 'Lambda(\\di):0:0:{', + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + { + 'Arrow:0:2: ->', + children = { + { + 'Arrow:0:7: ->', + children = { + 'PlainIdentifier(scope=0,ident=b):0:5: b', + 'PlainIdentifier(scope=0,ident=c):0:10: c', + }, + }, + }, + }, + }, + }, + }, + err = { + arg = '-> c}', + msg = 'E15: Arrow outside of lambda: %.*s', + }, + }, { + hl('Lambda', '{'), + hl('Identifier', 'a'), + hl('Arrow', '->', 1), + hl('Identifier', 'b', 1), + hl('InvalidArrow', '->', 1), + hl('Identifier', 'c', 1), + hl('Lambda', '}'), + }) + check_parsing('{a: -> b}', 0, { + -- 012345678 + ast = { + { + 'CurlyBracesIdentifier(-di):0:0:{', + children = { + { + 'Arrow:0:3: ->', + children = { + 'PlainIdentifier(scope=a,ident=):0:1:a:', + 'PlainIdentifier(scope=0,ident=b):0:6: b', + }, + }, + }, + }, + }, + err = { + arg = '-> b}', + msg = 'E15: Arrow outside of lambda: %.*s', + }, + }, { + hl('Curly', '{'), + hl('IdentifierScope', 'a'), + hl('IdentifierScopeDelimiter', ':'), + hl('InvalidArrow', '->', 1), + hl('Identifier', 'b', 1), + hl('Curly', '}'), + }) + + check_parsing('{a:b -> b}', 0, { + -- 0123456789 + ast = { + { + 'CurlyBracesIdentifier(-di):0:0:{', + children = { + { + 'Arrow:0:4: ->', + children = { + 'PlainIdentifier(scope=a,ident=b):0:1:a:b', + 'PlainIdentifier(scope=0,ident=b):0:7: b', + }, + }, + }, + }, + }, + err = { + arg = '-> b}', + msg = 'E15: Arrow outside of lambda: %.*s', + }, + }, { + hl('Curly', '{'), + hl('IdentifierScope', 'a'), + hl('IdentifierScopeDelimiter', ':'), + hl('Identifier', 'b'), + hl('InvalidArrow', '->', 1), + hl('Identifier', 'b', 1), + hl('Curly', '}'), + }) + + check_parsing('{a#b -> b}', 0, { + -- 0123456789 + ast = { + { + 'CurlyBracesIdentifier(-di):0:0:{', + children = { + { + 'Arrow:0:4: ->', + children = { + 'PlainIdentifier(scope=0,ident=a#b):0:1:a#b', + 'PlainIdentifier(scope=0,ident=b):0:7: b', + }, + }, + }, + }, + }, + err = { + arg = '-> b}', + msg = 'E15: Arrow outside of lambda: %.*s', + }, + }, { + hl('Curly', '{'), + hl('Identifier', 'a#b'), + hl('InvalidArrow', '->', 1), + hl('Identifier', 'b', 1), + hl('Curly', '}'), + }) end) itp('works with ternary operator', function() check_parsing('a ? b : c', 0, { -- cgit From 6168e1127c1c80a3810854649b0776146545043b Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 2 Oct 2017 02:41:55 +0300 Subject: viml/parser/expressions: Add support for comparison operators --- test/symbolic/klee/viml_expressions_parser.c | 8 +- test/unit/viml/expressions/lexer_spec.lua | 25 +-- test/unit/viml/expressions/parser_spec.lua | 325 ++++++++++++++++++++++++++- test/unit/viml/helpers.lua | 31 +++ 4 files changed, 361 insertions(+), 28 deletions(-) (limited to 'test') diff --git a/test/symbolic/klee/viml_expressions_parser.c b/test/symbolic/klee/viml_expressions_parser.c index 2bad1adc53..5ad592b99f 100644 --- a/test/symbolic/klee/viml_expressions_parser.c +++ b/test/symbolic/klee/viml_expressions_parser.c @@ -91,12 +91,6 @@ int main(const int argc, const char *const *const argv, const ExprAST ast = viml_pexpr_parse(&pstate, flags); assert(ast.root != NULL || plines[0].size == 0); - assert(ast.root != NULL || !ast.correct); - assert(ast.correct - || (ast.err.msg != NULL - && ast.err.arg != NULL - && ast.err.arg >= plines[0].data - && ((size_t)(ast.err.arg - plines[0].data) + ast.err.arg_len - <= plines[0].size))); + assert(ast.root != NULL || ast.err.msg); // FIXME: free memory and assert no memory leaks } diff --git a/test/unit/viml/expressions/lexer_spec.lua b/test/unit/viml/expressions/lexer_spec.lua index 972478c2e5..d201d54526 100644 --- a/test/unit/viml/expressions/lexer_spec.lua +++ b/test/unit/viml/expressions/lexer_spec.lua @@ -1,7 +1,7 @@ local helpers = require('test.unit.helpers')(after_each) -local viml_helpers = require('test.unit.viml.helpers') local global_helpers = require('test.helpers') local itp = helpers.gen_itp(it) +local viml_helpers = require('test.unit.viml.helpers') local child_call_once = helpers.child_call_once local conv_enum = helpers.conv_enum @@ -9,17 +9,18 @@ local cimport = helpers.cimport local ffi = helpers.ffi local eq = helpers.eq +local conv_ccs = viml_helpers.conv_ccs local pline2lua = viml_helpers.pline2lua local new_pstate = viml_helpers.new_pstate local intchar2lua = viml_helpers.intchar2lua +local conv_cmp_type = viml_helpers.conv_cmp_type local pstate_set_str = viml_helpers.pstate_set_str local shallowcopy = global_helpers.shallowcopy local lib = cimport('./src/nvim/viml/parser/expressions.h') -local eltkn_type_tab, eltkn_cmp_type_tab, ccs_tab, eltkn_mul_type_tab -local eltkn_opt_scope_tab +local eltkn_type_tab, eltkn_mul_type_tab, eltkn_opt_scope_tab child_call_once(function() eltkn_type_tab = { [tonumber(lib.kExprLexInvalid)] = 'Invalid', @@ -54,20 +55,6 @@ child_call_once(function() [tonumber(lib.kExprLexArrow)] = 'Arrow', } - eltkn_cmp_type_tab = { - [tonumber(lib.kExprLexCmpEqual)] = 'Equal', - [tonumber(lib.kExprLexCmpMatches)] = 'Matches', - [tonumber(lib.kExprLexCmpGreater)] = 'Greater', - [tonumber(lib.kExprLexCmpGreaterOrEqual)] = 'GreaterOrEqual', - [tonumber(lib.kExprLexCmpIdentical)] = 'Identical', - } - - ccs_tab = { - [tonumber(lib.kCCStrategyUseOption)] = 'UseOption', - [tonumber(lib.kCCStrategyMatchCase)] = 'MatchCase', - [tonumber(lib.kCCStrategyIgnoreCase)] = 'IgnoreCase', - } - eltkn_mul_type_tab = { [tonumber(lib.kExprLexMulMul)] = 'Mul', [tonumber(lib.kExprLexMulDiv)] = 'Div', @@ -101,8 +88,8 @@ local function eltkn2lua(pstate, tkn) end if ret.type == 'Comparison' then ret.data = { - type = conv_enum(eltkn_cmp_type_tab, tkn.data.cmp.type), - ccs = conv_enum(ccs_tab, tkn.data.cmp.ccs), + type = conv_cmp_type(tkn.data.cmp.type), + ccs = conv_ccs(tkn.data.cmp.ccs), inv = (not not tkn.data.cmp.inv), } elseif ret.type == 'Multiplication' then diff --git a/test/unit/viml/expressions/parser_spec.lua b/test/unit/viml/expressions/parser_spec.lua index 2c80b437dc..efa88455e4 100644 --- a/test/unit/viml/expressions/parser_spec.lua +++ b/test/unit/viml/expressions/parser_spec.lua @@ -1,7 +1,7 @@ local helpers = require('test.unit.helpers')(after_each) -local viml_helpers = require('test.unit.viml.helpers') local global_helpers = require('test.helpers') local itp = helpers.gen_itp(it) +local viml_helpers = require('test.unit.viml.helpers') local make_enum_conv_tab = helpers.make_enum_conv_tab local child_call_once = helpers.child_call_once @@ -11,9 +11,11 @@ local cimport = helpers.cimport local ffi = helpers.ffi local eq = helpers.eq +local conv_ccs = viml_helpers.conv_ccs local pline2lua = viml_helpers.pline2lua local new_pstate = viml_helpers.new_pstate local intchar2lua = viml_helpers.intchar2lua +local conv_cmp_type = viml_helpers.conv_cmp_type local pstate_set_str = viml_helpers.pstate_set_str local format_string = global_helpers.format_string @@ -83,6 +85,7 @@ make_enum_conv_tab(lib, { 'kExprNodeComma', 'kExprNodeColon', 'kExprNodeArrow', + 'kExprNodeComparison', }, 'kExprNode', function(ret) east_node_type_tab = ret end) local function conv_east_node_type(typ) @@ -121,6 +124,10 @@ local function eastnode2lua(pstate, eastnode, checked_nodes) (eastnode.data.fig.type_guesses.allow_lambda and '\\' or '-') .. (eastnode.data.fig.type_guesses.allow_dict and 'd' or '-') .. (eastnode.data.fig.type_guesses.allow_ident and 'i' or '-')) + elseif typ == 'Comparison' then + typ = typ .. ('(type=%s,inv=%u,ccs=%s)'):format( + conv_cmp_type(eastnode.data.cmp.type), eastnode.data.cmp.inv and 1 or 0, + conv_ccs(eastnode.data.cmp.ccs)) end ret_str = typ .. ':' .. ret_str local can_simplify = true @@ -150,7 +157,7 @@ end local function east2lua(pstate, east) local checked_nodes = {} return { - err = (not east.correct) and { + err = east.err.msg ~= nil and { msg = ffi.string(east.err.msg), arg = ('%s'):format( ffi.string(east.err.arg, east.err.arg_len)), @@ -3328,4 +3335,318 @@ describe('Expressions parser', function() hl('Identifier', 'h'), }) end) + itp('works with comparison operators', function() + check_parsing('a == b', 0, { + -- 012345 + ast = { + { + 'Comparison(type=Equal,inv=0,ccs=UseOption):0:1: ==', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + 'PlainIdentifier(scope=0,ident=b):0:4: b', + }, + }, + }, + }, { + hl('Identifier', 'a'), + hl('ComparisonOperator', '==', 1), + hl('Identifier', 'b', 1), + }) + + check_parsing('a ==? b', 0, { + -- 0123456 + ast = { + { + 'Comparison(type=Equal,inv=0,ccs=IgnoreCase):0:1: ==?', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + 'PlainIdentifier(scope=0,ident=b):0:5: b', + }, + }, + }, + }, { + hl('Identifier', 'a'), + hl('ComparisonOperator', '==', 1), + hl('ComparisonOperatorModifier', '?'), + hl('Identifier', 'b', 1), + }) + + check_parsing('a ==# b', 0, { + -- 0123456 + ast = { + { + 'Comparison(type=Equal,inv=0,ccs=MatchCase):0:1: ==#', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + 'PlainIdentifier(scope=0,ident=b):0:5: b', + }, + }, + }, + }, { + hl('Identifier', 'a'), + hl('ComparisonOperator', '==', 1), + hl('ComparisonOperatorModifier', '#'), + hl('Identifier', 'b', 1), + }) + + check_parsing('a !=# b', 0, { + -- 0123456 + ast = { + { + 'Comparison(type=Equal,inv=1,ccs=MatchCase):0:1: !=#', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + 'PlainIdentifier(scope=0,ident=b):0:5: b', + }, + }, + }, + }, { + hl('Identifier', 'a'), + hl('ComparisonOperator', '!=', 1), + hl('ComparisonOperatorModifier', '#'), + hl('Identifier', 'b', 1), + }) + + check_parsing('a <=# b', 0, { + -- 0123456 + ast = { + { + 'Comparison(type=Greater,inv=1,ccs=MatchCase):0:1: <=#', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + 'PlainIdentifier(scope=0,ident=b):0:5: b', + }, + }, + }, + }, { + hl('Identifier', 'a'), + hl('ComparisonOperator', '<=', 1), + hl('ComparisonOperatorModifier', '#'), + hl('Identifier', 'b', 1), + }) + + check_parsing('a >=# b', 0, { + -- 0123456 + ast = { + { + 'Comparison(type=GreaterOrEqual,inv=0,ccs=MatchCase):0:1: >=#', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + 'PlainIdentifier(scope=0,ident=b):0:5: b', + }, + }, + }, + }, { + hl('Identifier', 'a'), + hl('ComparisonOperator', '>=', 1), + hl('ComparisonOperatorModifier', '#'), + hl('Identifier', 'b', 1), + }) + + check_parsing('a ># b', 0, { + -- 012345 + ast = { + { + 'Comparison(type=Greater,inv=0,ccs=MatchCase):0:1: >#', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + 'PlainIdentifier(scope=0,ident=b):0:4: b', + }, + }, + }, + }, { + hl('Identifier', 'a'), + hl('ComparisonOperator', '>', 1), + hl('ComparisonOperatorModifier', '#'), + hl('Identifier', 'b', 1), + }) + + check_parsing('a <# b', 0, { + -- 012345 + ast = { + { + 'Comparison(type=GreaterOrEqual,inv=1,ccs=MatchCase):0:1: <#', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + 'PlainIdentifier(scope=0,ident=b):0:4: b', + }, + }, + }, + }, { + hl('Identifier', 'a'), + hl('ComparisonOperator', '<', 1), + hl('ComparisonOperatorModifier', '#'), + hl('Identifier', 'b', 1), + }) + + check_parsing('a is#b', 0, { + -- 012345 + ast = { + { + 'Comparison(type=Identical,inv=0,ccs=MatchCase):0:1: is#', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + 'PlainIdentifier(scope=0,ident=b):0:5:b', + }, + }, + }, + }, { + hl('Identifier', 'a'), + hl('ComparisonOperator', 'is', 1), + hl('ComparisonOperatorModifier', '#'), + hl('Identifier', 'b'), + }) + + check_parsing('a is?b', 0, { + -- 012345 + ast = { + { + 'Comparison(type=Identical,inv=0,ccs=IgnoreCase):0:1: is?', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + 'PlainIdentifier(scope=0,ident=b):0:5:b', + }, + }, + }, + }, { + hl('Identifier', 'a'), + hl('ComparisonOperator', 'is', 1), + hl('ComparisonOperatorModifier', '?'), + hl('Identifier', 'b'), + }) + + check_parsing('a isnot b', 0, { + -- 012345678 + ast = { + { + 'Comparison(type=Identical,inv=1,ccs=UseOption):0:1: isnot', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + 'PlainIdentifier(scope=0,ident=b):0:7: b', + }, + }, + }, + }, { + hl('Identifier', 'a'), + hl('ComparisonOperator', 'isnot', 1), + hl('Identifier', 'b', 1), + }) + + check_parsing('a < b < c', 0, { + -- 012345678 + ast = { + { + 'Comparison(type=GreaterOrEqual,inv=1,ccs=UseOption):0:1: <', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + { + 'Comparison(type=GreaterOrEqual,inv=1,ccs=UseOption):0:5: <', + children = { + 'PlainIdentifier(scope=0,ident=b):0:3: b', + 'PlainIdentifier(scope=0,ident=c):0:7: c', + }, + }, + }, + }, + }, + err = { + arg = ' < c', + msg = 'E15: Operator is not associative: %.*s', + }, + }, { + hl('Identifier', 'a'), + hl('ComparisonOperator', '<', 1), + hl('Identifier', 'b', 1), + hl('InvalidComparisonOperator', '<', 1), + hl('Identifier', 'c', 1), + }) + check_parsing('a += b', 0, { + -- 012345 + ast = { + { + 'Comparison(type=Equal,inv=0,ccs=UseOption):0:3:=', + children = { + { + 'BinaryPlus:0:1: +', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + 'Missing:0:3:', + }, + }, + 'PlainIdentifier(scope=0,ident=b):0:4: b', + }, + }, + }, + err = { + arg = '= b', + msg = 'E15: Expected == or =~: %.*s', + }, + }, { + hl('Identifier', 'a'), + hl('BinaryPlus', '+', 1), + hl('InvalidComparisonOperator', '='), + hl('Identifier', 'b', 1), + }) + check_parsing('a + b == c + d', 0, { + -- 01234567890123 + -- 0 1 + ast = { + { + 'Comparison(type=Equal,inv=0,ccs=UseOption):0:5: ==', + children = { + { + 'BinaryPlus:0:1: +', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + 'PlainIdentifier(scope=0,ident=b):0:3: b', + }, + }, + { + 'BinaryPlus:0:10: +', + children = { + 'PlainIdentifier(scope=0,ident=c):0:8: c', + 'PlainIdentifier(scope=0,ident=d):0:12: d', + }, + }, + }, + }, + }, + }, { + hl('Identifier', 'a'), + hl('BinaryPlus', '+', 1), + hl('Identifier', 'b', 1), + hl('ComparisonOperator', '==', 1), + hl('Identifier', 'c', 1), + hl('BinaryPlus', '+', 1), + hl('Identifier', 'd', 1), + }) + check_parsing('+ a == + b', 0, { + -- 0123456789 + ast = { + { + 'Comparison(type=Equal,inv=0,ccs=UseOption):0:3: ==', + children = { + { + 'UnaryPlus:0:0:+', + children = { + 'PlainIdentifier(scope=0,ident=a):0:1: a', + }, + }, + { + 'UnaryPlus:0:6: +', + children = { + 'PlainIdentifier(scope=0,ident=b):0:8: b', + }, + }, + }, + }, + }, + }, { + hl('UnaryPlus', '+'), + hl('Identifier', 'a', 1), + hl('ComparisonOperator', '==', 1), + hl('UnaryPlus', '+', 1), + hl('Identifier', 'b', 1), + }) + end) end) diff --git a/test/unit/viml/helpers.lua b/test/unit/viml/helpers.lua index 2cb60499eb..0b92be2654 100644 --- a/test/unit/viml/helpers.lua +++ b/test/unit/viml/helpers.lua @@ -1,8 +1,13 @@ local helpers = require('test.unit.helpers')(nil) local ffi = helpers.ffi +local cimport = helpers.cimport local kvi_new = helpers.kvi_new local kvi_init = helpers.kvi_init +local conv_enum = helpers.conv_enum +local make_enum_conv_tab = helpers.make_enum_conv_tab + +local lib = cimport('./src/nvim/viml/parser/expressions.h') local function new_pstate(strings) local strings_idx = 0 @@ -88,10 +93,36 @@ local function pstate_set_str(pstate, start, len, ret) return ret end +local eltkn_cmp_type_tab +make_enum_conv_tab(lib, { + 'kExprCmpEqual', + 'kExprCmpMatches', + 'kExprCmpGreater', + 'kExprCmpGreaterOrEqual', + 'kExprCmpIdentical', +}, 'kExprCmp', function(ret) eltkn_cmp_type_tab = ret end) + +local function conv_cmp_type(typ) + return conv_enum(eltkn_cmp_type_tab, typ) +end + +local ccs_tab +make_enum_conv_tab(lib, { + 'kCCStrategyUseOption', + 'kCCStrategyMatchCase', + 'kCCStrategyIgnoreCase', +}, 'kCCStrategy', function(ret) ccs_tab = ret end) + +local function conv_ccs(ccs) + return conv_enum(ccs_tab, ccs) +end + return { + conv_ccs = conv_ccs, pline2lua = pline2lua, pstate_str = pstate_str, new_pstate = new_pstate, intchar2lua = intchar2lua, + conv_cmp_type = conv_cmp_type, pstate_set_str = pstate_set_str, } -- cgit From 0bc4e2237960712426da3774c1430f5874c49aea Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 3 Oct 2017 00:39:40 +0300 Subject: viml/parser/expressions: Forbid dot or alpha characters after a float This is basically what Vim already does, in addition to forbidding floats should there be a concat immediately before it. --- test/unit/viml/expressions/lexer_spec.lua | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) (limited to 'test') diff --git a/test/unit/viml/expressions/lexer_spec.lua b/test/unit/viml/expressions/lexer_spec.lua index d201d54526..bd8045632e 100644 --- a/test/unit/viml/expressions/lexer_spec.lua +++ b/test/unit/viml/expressions/lexer_spec.lua @@ -264,6 +264,15 @@ describe('Expressions lexer', function() simple_test({''}, 'EOC', 0, {error='start.col >= #pstr'}) simple_test({'2.'}, 'Number', 1, {data={is_float=false}, str='2'}) simple_test({'2.x'}, 'Number', 1, {data={is_float=false}, str='2'}) + simple_test({'2.2.'}, 'Number', 1, {data={is_float=false}, str='2'}) + simple_test({'2.0x'}, 'Number', 1, {data={is_float=false}, str='2'}) + simple_test({'2.0e'}, 'Number', 1, {data={is_float=false}, str='2'}) + simple_test({'2.0e+'}, 'Number', 1, {data={is_float=false}, str='2'}) + simple_test({'2.0e-'}, 'Number', 1, {data={is_float=false}, str='2'}) + simple_test({'2.0e+x'}, 'Number', 1, {data={is_float=false}, str='2'}) + simple_test({'2.0e-x'}, 'Number', 1, {data={is_float=false}, str='2'}) + simple_test({'2.0e+1a'}, 'Number', 1, {data={is_float=false}, str='2'}) + simple_test({'2.0e-1a'}, 'Number', 1, {data={is_float=false}, str='2'}) end local function regular_scope_tests() @@ -296,12 +305,6 @@ describe('Expressions lexer', function() local function regular_number_tests() simple_test({'2.0'}, 'Number', 1, {data={is_float=false}, str='2'}) - simple_test({'2.0x'}, 'Number', 1, {data={is_float=false}, str='2'}) - simple_test({'2.0e'}, 'Number', 1, {data={is_float=false}, str='2'}) - simple_test({'2.0e+'}, 'Number', 1, {data={is_float=false}, str='2'}) - simple_test({'2.0e-'}, 'Number', 1, {data={is_float=false}, str='2'}) - simple_test({'2.0e+x'}, 'Number', 1, {data={is_float=false}, str='2'}) - simple_test({'2.0e-x'}, 'Number', 1, {data={is_float=false}, str='2'}) simple_test({'2.0e5'}, 'Number', 1, {data={is_float=false}, str='2'}) simple_test({'2.0e+5'}, 'Number', 1, {data={is_float=false}, str='2'}) simple_test({'2.0e-5'}, 'Number', 1, {data={is_float=false}, str='2'}) @@ -350,12 +353,6 @@ describe('Expressions lexer', function() regular_is_tests() simple_test({'2.0'}, 'Number', 3, {data={is_float=true}, str='2.0'}) - simple_test({'2.0x'}, 'Number', 3, {data={is_float=true}, str='2.0'}) - simple_test({'2.0e'}, 'Number', 3, {data={is_float=true}, str='2.0'}) - simple_test({'2.0e+'}, 'Number', 3, {data={is_float=true}, str='2.0'}) - simple_test({'2.0e-'}, 'Number', 3, {data={is_float=true}, str='2.0'}) - simple_test({'2.0e+x'}, 'Number', 3, {data={is_float=true}, str='2.0'}) - simple_test({'2.0e-x'}, 'Number', 3, {data={is_float=true}, str='2.0'}) simple_test({'2.0e5'}, 'Number', 5, {data={is_float=true}, str='2.0e5'}) simple_test({'2.0e+5'}, 'Number', 6, {data={is_float=true}, str='2.0e+5'}) simple_test({'2.0e-5'}, 'Number', 6, {data={is_float=true}, str='2.0e-5'}) -- cgit From 163792e9b9854fe046ada3233dec0fd0f6c55737 Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 6 Oct 2017 01:19:43 +0300 Subject: viml/parser/expressions: Make lexer parse numbers, support non-decimal --- test/symbolic/klee/nvim/charset.c | 165 ++++++++++++++++++++++++++++ test/symbolic/klee/viml_expressions_lexer.c | 6 +- test/unit/viml/expressions/lexer_spec.lua | 73 ++++++++---- 3 files changed, 221 insertions(+), 23 deletions(-) (limited to 'test') diff --git a/test/symbolic/klee/nvim/charset.c b/test/symbolic/klee/nvim/charset.c index a40488920e..409d7d443c 100644 --- a/test/symbolic/klee/nvim/charset.c +++ b/test/symbolic/klee/nvim/charset.c @@ -3,8 +3,173 @@ #include "nvim/ascii.h" #include "nvim/macros.h" #include "nvim/charset.h" +#include "nvim/eval/typval.h" +#include "nvim/vim.h" bool vim_isIDc(int c) { return ASCII_ISALNUM(c); } + +int hex2nr(int c) +{ + if ((c >= 'a') && (c <= 'f')) { + return c - 'a' + 10; + } + + if ((c >= 'A') && (c <= 'F')) { + return c - 'A' + 10; + } + return c - '0'; +} + +void vim_str2nr(const char_u *const start, int *const prep, int *const len, + const int what, varnumber_T *const nptr, + uvarnumber_T *const unptr, const int maxlen) +{ + const char_u *ptr = start; + int pre = 0; // default is decimal + bool negative = false; + uvarnumber_T un = 0; + + if (ptr[0] == '-') { + negative = true; + ptr++; + } + + // Recognize hex, octal and bin. + if ((ptr[0] == '0') && (ptr[1] != '8') && (ptr[1] != '9') + && (maxlen == 0 || maxlen > 1)) { + pre = ptr[1]; + + if ((what & STR2NR_HEX) + && ((pre == 'X') || (pre == 'x')) + && ascii_isxdigit(ptr[2]) + && (maxlen == 0 || maxlen > 2)) { + // hexadecimal + ptr += 2; + } else if ((what & STR2NR_BIN) + && ((pre == 'B') || (pre == 'b')) + && ascii_isbdigit(ptr[2]) + && (maxlen == 0 || maxlen > 2)) { + // binary + ptr += 2; + } else { + // decimal or octal, default is decimal + pre = 0; + + if (what & STR2NR_OCT) { + // Don't interpret "0", "08" or "0129" as octal. + for (int n = 1; ascii_isdigit(ptr[n]); ++n) { + if (ptr[n] > '7') { + // can't be octal + pre = 0; + break; + } + if (ptr[n] >= '0') { + // assume octal + pre = '0'; + } + if (n == maxlen) { + break; + } + } + } + } + } + + // Do the string-to-numeric conversion "manually" to avoid sscanf quirks. + int n = 1; + if ((pre == 'B') || (pre == 'b') || what == STR2NR_BIN + STR2NR_FORCE) { + // bin + if (pre != 0) { + n += 2; // skip over "0b" + } + while ('0' <= *ptr && *ptr <= '1') { + // avoid ubsan error for overflow + if (un < UVARNUMBER_MAX / 2) { + un = 2 * un + (uvarnumber_T)(*ptr - '0'); + } else { + un = UVARNUMBER_MAX; + } + ptr++; + if (n++ == maxlen) { + break; + } + } + } else if ((pre == '0') || what == STR2NR_OCT + STR2NR_FORCE) { + // octal + while ('0' <= *ptr && *ptr <= '7') { + // avoid ubsan error for overflow + if (un < UVARNUMBER_MAX / 8) { + un = 8 * un + (uvarnumber_T)(*ptr - '0'); + } else { + un = UVARNUMBER_MAX; + } + ptr++; + if (n++ == maxlen) { + break; + } + } + } else if ((pre == 'X') || (pre == 'x') + || what == STR2NR_HEX + STR2NR_FORCE) { + // hex + if (pre != 0) { + n += 2; // skip over "0x" + } + while (ascii_isxdigit(*ptr)) { + // avoid ubsan error for overflow + if (un < UVARNUMBER_MAX / 16) { + un = 16 * un + (uvarnumber_T)hex2nr(*ptr); + } else { + un = UVARNUMBER_MAX; + } + ptr++; + if (n++ == maxlen) { + break; + } + } + } else { + // decimal + while (ascii_isdigit(*ptr)) { + // avoid ubsan error for overflow + if (un < UVARNUMBER_MAX / 10) { + un = 10 * un + (uvarnumber_T)(*ptr - '0'); + } else { + un = UVARNUMBER_MAX; + } + ptr++; + if (n++ == maxlen) { + break; + } + } + } + + if (prep != NULL) { + *prep = pre; + } + + if (len != NULL) { + *len = (int)(ptr - start); + } + + if (nptr != NULL) { + if (negative) { // account for leading '-' for decimal numbers + // avoid ubsan error for overflow + if (un > VARNUMBER_MAX) { + *nptr = VARNUMBER_MIN; + } else { + *nptr = -(varnumber_T)un; + } + } else { + if (un > VARNUMBER_MAX) { + un = VARNUMBER_MAX; + } + *nptr = (varnumber_T)un; + } + } + + if (unptr != NULL) { + *unptr = un; + } +} diff --git a/test/symbolic/klee/viml_expressions_lexer.c b/test/symbolic/klee/viml_expressions_lexer.c index 67f3eb7faa..cddc1cb2f1 100644 --- a/test/symbolic/klee/viml_expressions_lexer.c +++ b/test/symbolic/klee/viml_expressions_lexer.c @@ -2,6 +2,7 @@ # include #else # include +# include #endif #include #include @@ -56,7 +57,7 @@ int main(const int argc, const char *const *const argv, .data = &input[shift], .size = sizeof(input) - shift, #else - .data = (const char *)&argv[1], + .data = (const char *)argv[1], .size = strlen(argv[1]), #endif .allocated = false, @@ -97,4 +98,7 @@ int main(const int argc, const char *const *const argv, } assert(allocated_memory == 0); assert(ever_allocated_memory == 0); +#ifndef USE_KLEE + fprintf(stderr, "tkn: %s\n", viml_pexpr_repr_token(&pstate, token, NULL)); +#endif } diff --git a/test/unit/viml/expressions/lexer_spec.lua b/test/unit/viml/expressions/lexer_spec.lua index bd8045632e..f180d8ceff 100644 --- a/test/unit/viml/expressions/lexer_spec.lua +++ b/test/unit/viml/expressions/lexer_spec.lua @@ -114,7 +114,11 @@ local function eltkn2lua(pstate, tkn) elseif ret.type == 'Number' then ret.data = { is_float = (not not tkn.data.num.is_float), + base = tonumber(tkn.data.num.base), } + ret.data.val = tonumber(tkn.data.num.is_float + and tkn.data.num.val.floating + or tkn.data.num.val.integer) elseif ret.type == 'Invalid' then ret.data = { error = ffi.string(tkn.data.err.msg) } end @@ -204,9 +208,20 @@ describe('Expressions lexer', function() singl_eltkn_test('Spacing', ' ') singl_eltkn_test('Spacing', '\t') singl_eltkn_test('Invalid', '\x01\x02\x03', {error='E15: Invalid control character present in input: %.*s'}) - singl_eltkn_test('Number', '0123', {is_float=false}) - singl_eltkn_test('Number', '0', {is_float=false}) - singl_eltkn_test('Number', '9', {is_float=false}) + singl_eltkn_test('Number', '0123', {is_float=false, base=8, val=83}) + singl_eltkn_test('Number', '01234567', {is_float=false, base=8, val=342391}) + singl_eltkn_test('Number', '012345678', {is_float=false, base=10, val=12345678}) + singl_eltkn_test('Number', '0x123', {is_float=false, base=16, val=291}) + singl_eltkn_test('Number', '0x56FF', {is_float=false, base=16, val=22271}) + singl_eltkn_test('Number', '0xabcdef', {is_float=false, base=16, val=11259375}) + singl_eltkn_test('Number', '0xABCDEF', {is_float=false, base=16, val=11259375}) + singl_eltkn_test('Number', '0x0', {is_float=false, base=16, val=0}) + singl_eltkn_test('Number', '00', {is_float=false, base=8, val=0}) + singl_eltkn_test('Number', '0b0', {is_float=false, base=2, val=0}) + singl_eltkn_test('Number', '0b010111', {is_float=false, base=2, val=23}) + singl_eltkn_test('Number', '0b100111', {is_float=false, base=2, val=39}) + singl_eltkn_test('Number', '0', {is_float=false, base=10, val=0}) + singl_eltkn_test('Number', '9', {is_float=false, base=10, val=9}) singl_eltkn_test('Env', '$abc') singl_eltkn_test('Env', '$') singl_eltkn_test('PlainIdentifier', 'test', {autoload=false, scope=0}) @@ -262,17 +277,21 @@ describe('Expressions lexer', function() singl_eltkn_test('Invalid', '~', {error='E15: Unidentified character: %.*s'}) simple_test({{data=nil, size=0}}, 'EOC', 0, {error='start.col >= #pstr'}) simple_test({''}, 'EOC', 0, {error='start.col >= #pstr'}) - simple_test({'2.'}, 'Number', 1, {data={is_float=false}, str='2'}) - simple_test({'2.x'}, 'Number', 1, {data={is_float=false}, str='2'}) - simple_test({'2.2.'}, 'Number', 1, {data={is_float=false}, str='2'}) - simple_test({'2.0x'}, 'Number', 1, {data={is_float=false}, str='2'}) - simple_test({'2.0e'}, 'Number', 1, {data={is_float=false}, str='2'}) - simple_test({'2.0e+'}, 'Number', 1, {data={is_float=false}, str='2'}) - simple_test({'2.0e-'}, 'Number', 1, {data={is_float=false}, str='2'}) - simple_test({'2.0e+x'}, 'Number', 1, {data={is_float=false}, str='2'}) - simple_test({'2.0e-x'}, 'Number', 1, {data={is_float=false}, str='2'}) - simple_test({'2.0e+1a'}, 'Number', 1, {data={is_float=false}, str='2'}) - simple_test({'2.0e-1a'}, 'Number', 1, {data={is_float=false}, str='2'}) + simple_test({'2.'}, 'Number', 1, {data={is_float=false, base=10, val=2}, str='2'}) + simple_test({'2e5'}, 'Number', 1, {data={is_float=false, base=10, val=2}, str='2'}) + simple_test({'2.x'}, 'Number', 1, {data={is_float=false, base=10, val=2}, str='2'}) + simple_test({'2.2.'}, 'Number', 1, {data={is_float=false, base=10, val=2}, str='2'}) + simple_test({'2.0x'}, 'Number', 1, {data={is_float=false, base=10, val=2}, str='2'}) + simple_test({'2.0e'}, 'Number', 1, {data={is_float=false, base=10, val=2}, str='2'}) + simple_test({'2.0e+'}, 'Number', 1, {data={is_float=false, base=10, val=2}, str='2'}) + simple_test({'2.0e-'}, 'Number', 1, {data={is_float=false, base=10, val=2}, str='2'}) + simple_test({'2.0e+x'}, 'Number', 1, {data={is_float=false, base=10, val=2}, str='2'}) + simple_test({'2.0e-x'}, 'Number', 1, {data={is_float=false, base=10, val=2}, str='2'}) + simple_test({'2.0e+1a'}, 'Number', 1, {data={is_float=false, base=10, val=2}, str='2'}) + simple_test({'2.0e-1a'}, 'Number', 1, {data={is_float=false, base=10, val=2}, str='2'}) + simple_test({'0b102'}, 'Number', 4, {data={is_float=false, base=2, val=2}, str='0b10'}) + simple_test({'10F'}, 'Number', 2, {data={is_float=false, base=10, val=10}, str='10'}) + simple_test({'0x0123456789ABCDEFG'}, 'Number', 18, {data={is_float=false, base=16, val=81985529216486895}, str='0x0123456789ABCDEF'}) end local function regular_scope_tests() @@ -304,10 +323,10 @@ describe('Expressions lexer', function() end local function regular_number_tests() - simple_test({'2.0'}, 'Number', 1, {data={is_float=false}, str='2'}) - simple_test({'2.0e5'}, 'Number', 1, {data={is_float=false}, str='2'}) - simple_test({'2.0e+5'}, 'Number', 1, {data={is_float=false}, str='2'}) - simple_test({'2.0e-5'}, 'Number', 1, {data={is_float=false}, str='2'}) + simple_test({'2.0'}, 'Number', 1, {data={is_float=false, base=10, val=2}, str='2'}) + simple_test({'2.0e5'}, 'Number', 1, {data={is_float=false, base=10, val=2}, str='2'}) + simple_test({'2.0e+5'}, 'Number', 1, {data={is_float=false, base=10, val=2}, str='2'}) + simple_test({'2.0e-5'}, 'Number', 1, {data={is_float=false, base=10, val=2}, str='2'}) end local function regular_eoc_tests() @@ -352,10 +371,20 @@ describe('Expressions lexer', function() regular_scope_tests() regular_is_tests() - simple_test({'2.0'}, 'Number', 3, {data={is_float=true}, str='2.0'}) - simple_test({'2.0e5'}, 'Number', 5, {data={is_float=true}, str='2.0e5'}) - simple_test({'2.0e+5'}, 'Number', 6, {data={is_float=true}, str='2.0e+5'}) - simple_test({'2.0e-5'}, 'Number', 6, {data={is_float=true}, str='2.0e-5'}) + simple_test({'2.2'}, 'Number', 3, {data={is_float=true, base=10, val=2.2}, str='2.2'}) + simple_test({'2.0e5'}, 'Number', 5, {data={is_float=true, base=10, val=2e5}, str='2.0e5'}) + simple_test({'2.0e+5'}, 'Number', 6, {data={is_float=true, base=10, val=2e5}, str='2.0e+5'}) + simple_test({'2.0e-5'}, 'Number', 6, {data={is_float=true, base=10, val=2e-5}, str='2.0e-5'}) + simple_test({'2.500000e-5'}, 'Number', 11, {data={is_float=true, base=10, val=2.5e-5}, str='2.500000e-5'}) + simple_test({'2.5555e2'}, 'Number', 8, {data={is_float=true, base=10, val=2.5555e2}, str='2.5555e2'}) + simple_test({'2.5555e+2'}, 'Number', 9, {data={is_float=true, base=10, val=2.5555e2}, str='2.5555e+2'}) + simple_test({'2.5555e-2'}, 'Number', 9, {data={is_float=true, base=10, val=2.5555e-2}, str='2.5555e-2'}) + simple_test({{data='2.5e-5', size=3}}, + 'Number', 3, {data={is_float=true, base=10, val=2.5}, str='2.5'}) + simple_test({{data='2.5e5', size=4}}, + 'Number', 1, {data={is_float=false, base=10, val=2}, str='2'}) + simple_test({{data='2.5e-50', size=6}}, + 'Number', 6, {data={is_float=true, base=10, val=2.5e-5}, str='2.5e-5'}) end) itp('treats `is` as an identifier', function() flags = tonumber(lib.kELFlagIsNotCmp) -- cgit From 21a5ce033c5a853bed3204ea9f0f7a3cfc1d164f Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 3 Oct 2017 01:30:02 +0300 Subject: viml/parser/expressions: Add support for the dot operator and numbers --- test/unit/viml/expressions/parser_spec.lua | 294 +++++++++++++++++++++++++++++ 1 file changed, 294 insertions(+) (limited to 'test') diff --git a/test/unit/viml/expressions/parser_spec.lua b/test/unit/viml/expressions/parser_spec.lua index efa88455e4..8d96c29db7 100644 --- a/test/unit/viml/expressions/parser_spec.lua +++ b/test/unit/viml/expressions/parser_spec.lua @@ -77,6 +77,7 @@ make_enum_conv_tab(lib, { 'kExprNodeNested', 'kExprNodeCall', 'kExprNodePlainIdentifier', + 'kExprNodePlainKey', 'kExprNodeComplexIdentifier', 'kExprNodeUnknownFigure', 'kExprNodeLambda', @@ -86,6 +87,10 @@ make_enum_conv_tab(lib, { 'kExprNodeColon', 'kExprNodeArrow', 'kExprNodeComparison', + 'kExprNodeConcat', + 'kExprNodeConcatOrSubscript', + 'kExprNodeInteger', + 'kExprNodeFloat', }, 'kExprNode', function(ret) east_node_type_tab = ret end) local function conv_east_node_type(typ) @@ -118,6 +123,9 @@ local function eastnode2lua(pstate, eastnode, checked_nodes) typ = typ .. ('(scope=%s,ident=%s)'):format( tostring(intchar2lua(eastnode.data.var.scope)), ffi.string(eastnode.data.var.ident, eastnode.data.var.ident_len)) + elseif typ == 'PlainKey' then + typ = typ .. ('(key=%s)'):format( + ffi.string(eastnode.data.var.ident, eastnode.data.var.ident_len)) elseif (typ == 'UnknownFigure' or typ == 'DictLiteral' or typ == 'CurlyBracesIdentifier' or typ == 'Lambda') then typ = typ .. ('(%s)'):format( @@ -128,6 +136,10 @@ local function eastnode2lua(pstate, eastnode, checked_nodes) typ = typ .. ('(type=%s,inv=%u,ccs=%s)'):format( conv_cmp_type(eastnode.data.cmp.type), eastnode.data.cmp.inv and 1 or 0, conv_ccs(eastnode.data.cmp.ccs)) + elseif typ == 'Integer' then + typ = typ .. ('(val=%u)'):format(tonumber(eastnode.data.num.value)) + elseif typ == 'Float' then + typ = typ .. ('(val=%e)'):format(tonumber(eastnode.data.flt.value)) end ret_str = typ .. ':' .. ret_str local can_simplify = true @@ -190,6 +202,8 @@ end) describe('Expressions parser', function() local function check_parsing(str, flags, exp_ast, exp_highlighting_fs) + flags = flags or 0 + local pstate = new_pstate({str}) local east = lib.viml_pexpr_parse(pstate, flags) local ast = east2lua(pstate, east) @@ -3649,4 +3663,284 @@ describe('Expressions parser', function() hl('Identifier', 'b', 1), }) end) + itp('works with concat/subscript', function() + check_parsing('.', 0, { + -- 0 + ast = { + { + 'ConcatOrSubscript:0:0:.', + children = { + 'Missing:0:0:', + }, + }, + }, + err = { + arg = '.', + msg = 'E15: Unexpected dot: %.*s', + }, + }, { + hl('InvalidConcatOrSubscript', '.'), + }) + + check_parsing('a.', 0, { + -- 01 + ast = { + { + 'ConcatOrSubscript:0:1:.', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + }, + }, + }, + err = { + arg = '', + msg = 'E15: Expected value, got EOC: %.*s', + }, + }, { + hl('Identifier', 'a'), + hl('ConcatOrSubscript', '.'), + }) + + check_parsing('a.b', 0, { + -- 012 + ast = { + { + 'ConcatOrSubscript:0:1:.', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + 'PlainKey(key=b):0:2:b', + }, + }, + }, + }, { + hl('Identifier', 'a'), + hl('ConcatOrSubscript', '.'), + hl('IdentifierKey', 'b'), + }) + + check_parsing('1.2', 0, { + -- 012 + ast = { + 'Float(val=1.200000e+00):0:0:1.2', + }, + }, { + hl('Float', '1.2'), + }) + + check_parsing('1.2 + 1.3e-5', 0, { + -- 012345678901 + -- 0 1 + ast = { + { + 'BinaryPlus:0:3: +', + children = { + 'Float(val=1.200000e+00):0:0:1.2', + 'Float(val=1.300000e-05):0:5: 1.3e-5', + }, + }, + }, + }, { + hl('Float', '1.2'), + hl('BinaryPlus', '+', 1), + hl('Float', '1.3e-5', 1), + }) + + check_parsing('a . 1.2 + 1.3e-5', 0, { + -- 0123456789012345 + -- 0 1 + ast = { + { + 'BinaryPlus:0:7: +', + children = { + { + 'Concat:0:1: .', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + { + 'ConcatOrSubscript:0:5:.', + children = { + 'Integer(val=1):0:3: 1', + 'PlainKey(key=2):0:6:2', + }, + }, + }, + }, + 'Float(val=1.300000e-05):0:9: 1.3e-5', + }, + }, + }, + }, { + hl('Identifier', 'a'), + hl('Concat', '.', 1), + hl('Number', '1', 1), + hl('ConcatOrSubscript', '.'), + hl('IdentifierKey', '2'), + hl('BinaryPlus', '+', 1), + hl('Float', '1.3e-5', 1), + }) + + check_parsing('1.3e-5 + 1.2 . a', 0, { + -- 0123456789012345 + -- 0 1 + ast = { + { + 'Concat:0:12: .', + children = { + { + 'BinaryPlus:0:6: +', + children = { + 'Float(val=1.300000e-05):0:0:1.3e-5', + 'Float(val=1.200000e+00):0:8: 1.2', + }, + }, + 'PlainIdentifier(scope=0,ident=a):0:14: a', + }, + }, + }, + }, { + hl('Float', '1.3e-5'), + hl('BinaryPlus', '+', 1), + hl('Float', '1.2', 1), + hl('Concat', '.', 1), + hl('Identifier', 'a', 1), + }) + + check_parsing('1.3e-5 + a . 1.2', 0, { + -- 0123456789012345 + -- 0 1 + ast = { + { + 'Concat:0:10: .', + children = { + { + 'BinaryPlus:0:6: +', + children = { + 'Float(val=1.300000e-05):0:0:1.3e-5', + 'PlainIdentifier(scope=0,ident=a):0:8: a', + }, + }, + { + 'ConcatOrSubscript:0:14:.', + children = { + 'Integer(val=1):0:12: 1', + 'PlainKey(key=2):0:15:2', + }, + }, + }, + }, + }, + }, { + hl('Float', '1.3e-5'), + hl('BinaryPlus', '+', 1), + hl('Identifier', 'a', 1), + hl('Concat', '.', 1), + hl('Number', '1', 1), + hl('ConcatOrSubscript', '.'), + hl('IdentifierKey', '2'), + }) + + check_parsing('1.2.3', 0, { + -- 01234 + ast = { + { + 'ConcatOrSubscript:0:3:.', + children = { + { + 'ConcatOrSubscript:0:1:.', + children = { + 'Integer(val=1):0:0:1', + 'PlainKey(key=2):0:2:2', + }, + }, + 'PlainKey(key=3):0:4:3', + }, + }, + }, + }, { + hl('Number', '1'), + hl('ConcatOrSubscript', '.'), + hl('IdentifierKey', '2'), + hl('ConcatOrSubscript', '.'), + hl('IdentifierKey', '3'), + }) + + check_parsing('a.1.2', 0, { + -- 01234 + ast = { + { + 'ConcatOrSubscript:0:3:.', + children = { + { + 'ConcatOrSubscript:0:1:.', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + 'PlainKey(key=1):0:2:1', + }, + }, + 'PlainKey(key=2):0:4:2', + }, + }, + }, + }, { + hl('Identifier', 'a'), + hl('ConcatOrSubscript', '.'), + hl('IdentifierKey', '1'), + hl('ConcatOrSubscript', '.'), + hl('IdentifierKey', '2'), + }) + + check_parsing('a . 1.2', 0, { + -- 0123456 + ast = { + { + 'Concat:0:1: .', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + { + 'ConcatOrSubscript:0:5:.', + children = { + 'Integer(val=1):0:3: 1', + 'PlainKey(key=2):0:6:2', + }, + }, + }, + }, + }, + }, { + hl('Identifier', 'a'), + hl('Concat', '.', 1), + hl('Number', '1', 1), + hl('ConcatOrSubscript', '.'), + hl('IdentifierKey', '2'), + }) + + check_parsing('+a . +b', 0, { + -- 0123456 + ast = { + { + 'Concat:0:2: .', + children = { + { + 'UnaryPlus:0:0:+', + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + }, + }, + { + 'UnaryPlus:0:4: +', + children = { + 'PlainIdentifier(scope=0,ident=b):0:6:b', + }, + }, + }, + }, + }, + }, { + hl('UnaryPlus', '+'), + hl('Identifier', 'a'), + hl('Concat', '.', 1), + hl('UnaryPlus', '+', 1), + hl('Identifier', 'b'), + }) + end) end) -- cgit From e45e519495832e3d1d0fde1e32723d4140c5fc65 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 8 Oct 2017 01:19:58 +0300 Subject: viml/parser/expressions: Error out on multiple colons in a row --- test/unit/viml/expressions/parser_spec.lua | 73 ++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) (limited to 'test') diff --git a/test/unit/viml/expressions/parser_spec.lua b/test/unit/viml/expressions/parser_spec.lua index 8d96c29db7..46e3328184 100644 --- a/test/unit/viml/expressions/parser_spec.lua +++ b/test/unit/viml/expressions/parser_spec.lua @@ -2700,6 +2700,42 @@ describe('Expressions parser', function() hl('Identifier', 'b', 1), hl('Curly', '}'), }) + check_parsing('{a : b : c}', 0, { + -- 01234567890 + -- 0 1 + ast = { + { + 'DictLiteral(-di):0:0:{', + children = { + { + 'Colon:0:2: :', + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + { + 'Colon:0:6: :', + children = { + 'PlainIdentifier(scope=0,ident=b):0:4: b', + 'PlainIdentifier(scope=0,ident=c):0:8: c', + }, + }, + }, + }, + }, + }, + }, + err = { + arg = ': c}', + msg = 'E15: Colon outside of dictionary or ternary operator: %.*s', + }, + }, { + hl('Dict', '{'), + hl('Identifier', 'a'), + hl('Colon', ':', 1), + hl('Identifier', 'b', 1), + hl('InvalidColon', ':', 1), + hl('Identifier', 'c', 1), + hl('Dict', '}'), + }) end) itp('works with ternary operator', function() check_parsing('a ? b : c', 0, { @@ -3348,6 +3384,43 @@ describe('Expressions parser', function() hl('TernaryColon', ':'), hl('Identifier', 'h'), }) + check_parsing('a ? b : c : d', 0, { + -- 0123456789012 + -- 0 1 + ast = { + { + 'Colon:0:9: :', + children = { + { + 'Ternary:0:1: ?', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + { + 'TernaryValue:0:5: :', + children = { + 'PlainIdentifier(scope=0,ident=b):0:3: b', + 'PlainIdentifier(scope=0,ident=c):0:7: c', + }, + }, + }, + }, + 'PlainIdentifier(scope=0,ident=d):0:11: d', + }, + }, + }, + err = { + arg = ': d', + msg = 'E15: Colon outside of dictionary or ternary operator: %.*s', + }, + }, { + hl('Identifier', 'a'), + hl('Ternary', '?', 1), + hl('Identifier', 'b', 1), + hl('TernaryColon', ':', 1), + hl('Identifier', 'c', 1), + hl('InvalidColon', ':', 1), + hl('Identifier', 'd', 1), + }) end) itp('works with comparison operators', function() check_parsing('a == b', 0, { -- cgit From bd3a4166b25a64dbe406be09b3140955cf694477 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 8 Oct 2017 02:17:05 +0300 Subject: viml/parser/expressions: Add support for subscript and list literals --- test/unit/viml/expressions/parser_spec.lua | 533 +++++++++++++++++++++++++++++ 1 file changed, 533 insertions(+) (limited to 'test') diff --git a/test/unit/viml/expressions/parser_spec.lua b/test/unit/viml/expressions/parser_spec.lua index 46e3328184..41261cf7c9 100644 --- a/test/unit/viml/expressions/parser_spec.lua +++ b/test/unit/viml/expressions/parser_spec.lua @@ -91,6 +91,8 @@ make_enum_conv_tab(lib, { 'kExprNodeConcatOrSubscript', 'kExprNodeInteger', 'kExprNodeFloat', + 'kExprNodeSingleQuotedString', + 'kExprNodeDoubleQuotedString', }, 'kExprNode', function(ret) east_node_type_tab = ret end) local function conv_east_node_type(typ) @@ -204,6 +206,10 @@ describe('Expressions parser', function() local function check_parsing(str, flags, exp_ast, exp_highlighting_fs) flags = flags or 0 + if os.getenv('NVIM_TEST_PARSER_SPEC_PRINT_TEST_CASE') == '1' then + print(str, flags) + end + local pstate = new_pstate({str}) local east = lib.viml_pexpr_parse(pstate, flags) local ast = east2lua(pstate, east) @@ -4016,4 +4022,531 @@ describe('Expressions parser', function() hl('Identifier', 'b'), }) end) + itp('works with bracket subscripts', function() + check_parsing(':', 0, { + -- 0 + ast = { + { + 'Colon:0:0::', + children = { + 'Missing:0:0:', + }, + }, + }, + err = { + arg = ':', + msg = 'E15: Colon outside of dictionary or ternary operator: %.*s', + }, + }, { + hl('InvalidColon', ':'), + }) + check_parsing('a[]', 0, { + -- 012 + ast = { + { + 'Subscript:0:1:[', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + }, + }, + }, + err = { + arg = ']', + msg = 'E15: Expected value, got closing bracket: %.*s', + }, + }, { + hl('Identifier', 'a'), + hl('Subscript', '['), + hl('InvalidSubscript', ']'), + }) + check_parsing('a[b:]', 0, { + -- 01234 + ast = { + { + 'Subscript:0:1:[', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + 'PlainIdentifier(scope=b,ident=):0:2:b:', + }, + }, + }, + }, { + hl('Identifier', 'a'), + hl('Subscript', '['), + hl('IdentifierScope', 'b'), + hl('IdentifierScopeDelimiter', ':'), + hl('Subscript', ']'), + }) + + check_parsing('a[b:c]', 0, { + -- 012345 + ast = { + { + 'Subscript:0:1:[', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + 'PlainIdentifier(scope=b,ident=c):0:2:b:c', + }, + }, + }, + }, { + hl('Identifier', 'a'), + hl('Subscript', '['), + hl('IdentifierScope', 'b'), + hl('IdentifierScopeDelimiter', ':'), + hl('Identifier', 'c'), + hl('Subscript', ']'), + }) + check_parsing('a[b : c]', 0, { + -- 01234567 + ast = { + { + 'Subscript:0:1:[', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + { + 'Colon:0:3: :', + children = { + 'PlainIdentifier(scope=0,ident=b):0:2:b', + 'PlainIdentifier(scope=0,ident=c):0:5: c', + }, + }, + }, + }, + }, + }, { + hl('Identifier', 'a'), + hl('Subscript', '['), + hl('Identifier', 'b'), + hl('SubscriptColon', ':', 1), + hl('Identifier', 'c', 1), + hl('Subscript', ']'), + }) + + check_parsing('a[: b]', 0, { + -- 012345 + ast = { + { + 'Subscript:0:1:[', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + { + 'Colon:0:2::', + children = { + 'Missing:0:2:', + 'PlainIdentifier(scope=0,ident=b):0:3: b', + }, + }, + }, + }, + }, + }, { + hl('Identifier', 'a'), + hl('Subscript', '['), + hl('SubscriptColon', ':'), + hl('Identifier', 'b', 1), + hl('Subscript', ']'), + }) + + check_parsing('a[b :]', 0, { + -- 012345 + ast = { + { + 'Subscript:0:1:[', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + { + 'Colon:0:3: :', + children = { + 'PlainIdentifier(scope=0,ident=b):0:2:b', + }, + }, + }, + }, + }, + }, { + hl('Identifier', 'a'), + hl('Subscript', '['), + hl('Identifier', 'b'), + hl('SubscriptColon', ':', 1), + hl('Subscript', ']'), + }) + check_parsing('a[b][c][d](e)(f)(g)', 0, { + -- 0123456789012345678 + -- 0 1 + ast = { + { + 'Call:0:16:(', + children = { + { + 'Call:0:13:(', + children = { + { + 'Call:0:10:(', + children = { + { + 'Subscript:0:7:[', + children = { + { + 'Subscript:0:4:[', + children = { + { + 'Subscript:0:1:[', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + 'PlainIdentifier(scope=0,ident=b):0:2:b', + }, + }, + 'PlainIdentifier(scope=0,ident=c):0:5:c', + }, + }, + 'PlainIdentifier(scope=0,ident=d):0:8:d', + }, + }, + 'PlainIdentifier(scope=0,ident=e):0:11:e', + }, + }, + 'PlainIdentifier(scope=0,ident=f):0:14:f', + }, + }, + 'PlainIdentifier(scope=0,ident=g):0:17:g', + }, + }, + }, + }, { + hl('Identifier', 'a'), + hl('Subscript', '['), + hl('Identifier', 'b'), + hl('Subscript', ']'), + hl('Subscript', '['), + hl('Identifier', 'c'), + hl('Subscript', ']'), + hl('Subscript', '['), + hl('Identifier', 'd'), + hl('Subscript', ']'), + hl('CallingParenthesis', '('), + hl('Identifier', 'e'), + hl('CallingParenthesis', ')'), + hl('CallingParenthesis', '('), + hl('Identifier', 'f'), + hl('CallingParenthesis', ')'), + hl('CallingParenthesis', '('), + hl('Identifier', 'g'), + hl('CallingParenthesis', ')'), + }) + check_parsing('{a}{b}{c}[d][e][f]', 0, { + -- 012345678901234567 + -- 0 1 + ast = { + { + 'Subscript:0:15:[', + children = { + { + 'Subscript:0:12:[', + children = { + { + 'Subscript:0:9:[', + children = { + { + 'ComplexIdentifier:0:3:', + children = { + { + 'CurlyBracesIdentifier(-di):0:0:{', + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + }, + }, + { + 'ComplexIdentifier:0:6:', + children = { + { + 'CurlyBracesIdentifier(--i):0:3:{', + children = { + 'PlainIdentifier(scope=0,ident=b):0:4:b', + }, + }, + { + 'CurlyBracesIdentifier(--i):0:6:{', + children = { + 'PlainIdentifier(scope=0,ident=c):0:7:c', + }, + }, + }, + }, + }, + }, + 'PlainIdentifier(scope=0,ident=d):0:10:d', + }, + }, + 'PlainIdentifier(scope=0,ident=e):0:13:e', + }, + }, + 'PlainIdentifier(scope=0,ident=f):0:16:f', + }, + }, + }, + }, { + hl('Curly', '{'), + hl('Identifier', 'a'), + hl('Curly', '}'), + hl('Curly', '{'), + hl('Identifier', 'b'), + hl('Curly', '}'), + hl('Curly', '{'), + hl('Identifier', 'c'), + hl('Curly', '}'), + hl('Subscript', '['), + hl('Identifier', 'd'), + hl('Subscript', ']'), + hl('Subscript', '['), + hl('Identifier', 'e'), + hl('Subscript', ']'), + hl('Subscript', '['), + hl('Identifier', 'f'), + hl('Subscript', ']'), + }) + end) + itp('supports list literals', function() + check_parsing('[]', 0, { + -- 01 + ast = { + 'ListLiteral:0:0:[', + }, + }, { + hl('List', '['), + hl('List', ']'), + }) + + check_parsing('[a]', 0, { + -- 012 + ast = { + { + 'ListLiteral:0:0:[', + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + }, + }, + }, + }, { + hl('List', '['), + hl('Identifier', 'a'), + hl('List', ']'), + }) + + check_parsing('[a, b]', 0, { + -- 012345 + ast = { + { + 'ListLiteral:0:0:[', + children = { + { + 'Comma:0:2:,', + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + 'PlainIdentifier(scope=0,ident=b):0:3: b', + }, + }, + }, + }, + }, + }, { + hl('List', '['), + hl('Identifier', 'a'), + hl('Comma', ','), + hl('Identifier', 'b', 1), + hl('List', ']'), + }) + + check_parsing('[a, b, c]', 0, { + -- 012345678 + ast = { + { + 'ListLiteral:0:0:[', + children = { + { + 'Comma:0:2:,', + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + { + 'Comma:0:5:,', + children = { + 'PlainIdentifier(scope=0,ident=b):0:3: b', + 'PlainIdentifier(scope=0,ident=c):0:6: c', + }, + }, + }, + }, + }, + }, + }, + }, { + hl('List', '['), + hl('Identifier', 'a'), + hl('Comma', ','), + hl('Identifier', 'b', 1), + hl('Comma', ','), + hl('Identifier', 'c', 1), + hl('List', ']'), + }) + + check_parsing('[a, b, c, ]', 0, { + -- 01234567890 + -- 0 1 + ast = { + { + 'ListLiteral:0:0:[', + children = { + { + 'Comma:0:2:,', + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + { + 'Comma:0:5:,', + children = { + 'PlainIdentifier(scope=0,ident=b):0:3: b', + { + 'Comma:0:8:,', + children = { + 'PlainIdentifier(scope=0,ident=c):0:6: c', + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, { + hl('List', '['), + hl('Identifier', 'a'), + hl('Comma', ','), + hl('Identifier', 'b', 1), + hl('Comma', ','), + hl('Identifier', 'c', 1), + hl('Comma', ','), + hl('List', ']', 1), + }) + + check_parsing('[a : b, c : d]', 0, { + -- 01234567890123 + -- 0 1 + ast = { + { + 'ListLiteral:0:0:[', + children = { + { + 'Comma:0:6:,', + children = { + { + 'Colon:0:2: :', + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + 'PlainIdentifier(scope=0,ident=b):0:4: b', + }, + }, + { + 'Colon:0:9: :', + children = { + 'PlainIdentifier(scope=0,ident=c):0:7: c', + 'PlainIdentifier(scope=0,ident=d):0:11: d', + }, + }, + }, + }, + }, + }, + }, + err = { + arg = ': b, c : d]', + msg = 'E15: Colon outside of dictionary or ternary operator: %.*s', + }, + }, { + hl('List', '['), + hl('Identifier', 'a'), + hl('InvalidColon', ':', 1), + hl('Identifier', 'b', 1), + hl('Comma', ','), + hl('Identifier', 'c', 1), + hl('InvalidColon', ':', 1), + hl('Identifier', 'd', 1), + hl('List', ']'), + }) + + check_parsing(']', 0, { + -- 0 + ast = { + 'ListLiteral:0:0:', + }, + err = { + arg = ']', + msg = 'E15: Unexpected closing figure brace: %.*s', + }, + }, { + hl('InvalidList', ']'), + }) + + check_parsing('a]', 0, { + -- 01 + ast = { + { + 'ListLiteral:0:1:', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + }, + }, + }, + err = { + arg = ']', + msg = 'E15: Unexpected closing figure brace: %.*s', + }, + }, { + hl('Identifier', 'a'), + hl('InvalidList', ']'), + }) + + check_parsing('[] []', 0, { + -- 01234 + ast = { + { + 'OpMissing:0:2:', + children = { + 'ListLiteral:0:0:[', + 'ListLiteral:0:2: [', + }, + }, + }, + err = { + arg = '[]', + msg = 'E15: Missing operator: %.*s', + }, + }, { + hl('List', '['), + hl('List', ']'), + hl('InvalidSpacing', ' '), + hl('List', '['), + hl('List', ']'), + }) + + check_parsing('[][]', 0, { + -- 0123 + ast = { + { + 'Subscript:0:2:[', + children = { + 'ListLiteral:0:0:[', + }, + }, + }, + err = { + arg = ']', + msg = 'E15: Expected value, got closing bracket: %.*s', + }, + }, { + hl('List', '['), + hl('List', ']'), + hl('Subscript', '['), + hl('InvalidSubscript', ']'), + }) + end) end) -- cgit From af38cea133f5ebb67208cedd289e408cd1dad15a Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 8 Oct 2017 21:52:38 +0300 Subject: viml/parser/expressions: Add support for string parsing --- test/helpers.lua | 7 +- test/symbolic/klee/nvim/keymap.c | 540 ++++++++++++++++++ test/symbolic/klee/nvim/mbyte.c | 193 ++++++- test/symbolic/klee/viml_expressions_parser.c | 1 + test/unit/viml/expressions/parser_spec.lua | 825 +++++++++++++++++++++++++++ 5 files changed, 1561 insertions(+), 5 deletions(-) create mode 100644 test/symbolic/klee/nvim/keymap.c (limited to 'test') diff --git a/test/helpers.lua b/test/helpers.lua index d5356416af..e7d8c185ba 100644 --- a/test/helpers.lua +++ b/test/helpers.lua @@ -330,9 +330,10 @@ format_luav = function(v, indent) end local ret = '' if type(v) == 'string' then - ret = '\'' .. tostring(v):gsub('[\'\\]', '\\%0'):gsub('[%z\1-\31]', function(match) - return SUBTBL[match:byte()] - end) .. '\'' + ret = tostring(v):gsub('[\'\\]', '\\%0'):gsub('[%z\1-\31]', function(match) + return SUBTBL[match:byte() + 1] + end) + ret = '\'' .. ret .. '\'' elseif type(v) == 'table' then local processed_keys = {} ret = '{' .. linesep diff --git a/test/symbolic/klee/nvim/keymap.c b/test/symbolic/klee/nvim/keymap.c new file mode 100644 index 0000000000..c59d8d4d6e --- /dev/null +++ b/test/symbolic/klee/nvim/keymap.c @@ -0,0 +1,540 @@ +#include + +#include "nvim/types.h" +#include "nvim/keymap.h" +#include "nvim/eval/typval.h" + +#define MOD_KEYS_ENTRY_SIZE 5 + +static char_u modifier_keys_table[] = +{ + MOD_MASK_SHIFT, '&', '9', '@', '1', + MOD_MASK_SHIFT, '&', '0', '@', '2', + MOD_MASK_SHIFT, '*', '1', '@', '4', + MOD_MASK_SHIFT, '*', '2', '@', '5', + MOD_MASK_SHIFT, '*', '3', '@', '6', + MOD_MASK_SHIFT, '*', '4', 'k', 'D', + MOD_MASK_SHIFT, '*', '5', 'k', 'L', + MOD_MASK_SHIFT, '*', '7', '@', '7', + MOD_MASK_CTRL, KS_EXTRA, (int)KE_C_END, '@', '7', + MOD_MASK_SHIFT, '*', '9', '@', '9', + MOD_MASK_SHIFT, '*', '0', '@', '0', + MOD_MASK_SHIFT, '#', '1', '%', '1', + MOD_MASK_SHIFT, '#', '2', 'k', 'h', + MOD_MASK_CTRL, KS_EXTRA, (int)KE_C_HOME, 'k', 'h', + MOD_MASK_SHIFT, '#', '3', 'k', 'I', + MOD_MASK_SHIFT, '#', '4', 'k', 'l', + MOD_MASK_CTRL, KS_EXTRA, (int)KE_C_LEFT, 'k', 'l', + MOD_MASK_SHIFT, '%', 'a', '%', '3', + MOD_MASK_SHIFT, '%', 'b', '%', '4', + MOD_MASK_SHIFT, '%', 'c', '%', '5', + MOD_MASK_SHIFT, '%', 'd', '%', '7', + MOD_MASK_SHIFT, '%', 'e', '%', '8', + MOD_MASK_SHIFT, '%', 'f', '%', '9', + MOD_MASK_SHIFT, '%', 'g', '%', '0', + MOD_MASK_SHIFT, '%', 'h', '&', '3', + MOD_MASK_SHIFT, '%', 'i', 'k', 'r', + MOD_MASK_CTRL, KS_EXTRA, (int)KE_C_RIGHT, 'k', 'r', + MOD_MASK_SHIFT, '%', 'j', '&', '5', + MOD_MASK_SHIFT, '!', '1', '&', '6', + MOD_MASK_SHIFT, '!', '2', '&', '7', + MOD_MASK_SHIFT, '!', '3', '&', '8', + MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_UP, 'k', 'u', + MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_DOWN, 'k', 'd', + + MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_XF1, KS_EXTRA, (int)KE_XF1, + MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_XF2, KS_EXTRA, (int)KE_XF2, + MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_XF3, KS_EXTRA, (int)KE_XF3, + MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_XF4, KS_EXTRA, (int)KE_XF4, + + MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F1, 'k', '1', + MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F2, 'k', '2', + MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F3, 'k', '3', + MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F4, 'k', '4', + MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F5, 'k', '5', + MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F6, 'k', '6', + MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F7, 'k', '7', + MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F8, 'k', '8', + MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F9, 'k', '9', + MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F10, 'k', ';', + + MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F11, 'F', '1', + MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F12, 'F', '2', + MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F13, 'F', '3', + MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F14, 'F', '4', + MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F15, 'F', '5', + MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F16, 'F', '6', + MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F17, 'F', '7', + MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F18, 'F', '8', + MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F19, 'F', '9', + MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F20, 'F', 'A', + + MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F21, 'F', 'B', + MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F22, 'F', 'C', + MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F23, 'F', 'D', + MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F24, 'F', 'E', + MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F25, 'F', 'F', + MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F26, 'F', 'G', + MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F27, 'F', 'H', + MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F28, 'F', 'I', + MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F29, 'F', 'J', + MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F30, 'F', 'K', + + MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F31, 'F', 'L', + MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F32, 'F', 'M', + MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F33, 'F', 'N', + MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F34, 'F', 'O', + MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F35, 'F', 'P', + MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F36, 'F', 'Q', + MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F37, 'F', 'R', + + MOD_MASK_SHIFT, 'k', 'B', KS_EXTRA, (int)KE_TAB, + + NUL +}; + +int simplify_key(const int key, int *modifiers) +{ + if (*modifiers & (MOD_MASK_SHIFT | MOD_MASK_CTRL | MOD_MASK_ALT)) { + // TAB is a special case. + if (key == TAB && (*modifiers & MOD_MASK_SHIFT)) { + *modifiers &= ~MOD_MASK_SHIFT; + return K_S_TAB; + } + const int key0 = KEY2TERMCAP0(key); + const int key1 = KEY2TERMCAP1(key); + for (int i = 0; modifier_keys_table[i] != NUL; i += MOD_KEYS_ENTRY_SIZE) { + if (key0 == modifier_keys_table[i + 3] + && key1 == modifier_keys_table[i + 4] + && (*modifiers & modifier_keys_table[i])) { + *modifiers &= ~modifier_keys_table[i]; + return TERMCAP2KEY(modifier_keys_table[i + 1], + modifier_keys_table[i + 2]); + } + } + } + return key; +} + +int handle_x_keys(const int key) + FUNC_ATTR_CONST FUNC_ATTR_WARN_UNUSED_RESULT +{ + switch (key) { + case K_XUP: return K_UP; + case K_XDOWN: return K_DOWN; + case K_XLEFT: return K_LEFT; + case K_XRIGHT: return K_RIGHT; + case K_XHOME: return K_HOME; + case K_ZHOME: return K_HOME; + case K_XEND: return K_END; + case K_ZEND: return K_END; + case K_XF1: return K_F1; + case K_XF2: return K_F2; + case K_XF3: return K_F3; + case K_XF4: return K_F4; + case K_S_XF1: return K_S_F1; + case K_S_XF2: return K_S_F2; + case K_S_XF3: return K_S_F3; + case K_S_XF4: return K_S_F4; + } + return key; +} + +static const struct key_name_entry { + int key; ///< Special key code or ASCII value. + const char *name; ///< Name of the key +} key_names_table[] = { + {' ', "Space"}, + {TAB, "Tab"}, + {K_TAB, "Tab"}, + {NL, "NL"}, + {NL, "NewLine"}, // Alternative name + {NL, "LineFeed"}, // Alternative name + {NL, "LF"}, // Alternative name + {CAR, "CR"}, + {CAR, "Return"}, // Alternative name + {CAR, "Enter"}, // Alternative name + {K_BS, "BS"}, + {K_BS, "BackSpace"}, // Alternative name + {ESC, "Esc"}, + {CSI, "CSI"}, + {K_CSI, "xCSI"}, + {'|', "Bar"}, + {'\\', "Bslash"}, + {K_DEL, "Del"}, + {K_DEL, "Delete"}, // Alternative name + {K_KDEL, "kDel"}, + {K_UP, "Up"}, + {K_DOWN, "Down"}, + {K_LEFT, "Left"}, + {K_RIGHT, "Right"}, + {K_XUP, "xUp"}, + {K_XDOWN, "xDown"}, + {K_XLEFT, "xLeft"}, + {K_XRIGHT, "xRight"}, + + {K_F1, "F1"}, + {K_F2, "F2"}, + {K_F3, "F3"}, + {K_F4, "F4"}, + {K_F5, "F5"}, + {K_F6, "F6"}, + {K_F7, "F7"}, + {K_F8, "F8"}, + {K_F9, "F9"}, + {K_F10, "F10"}, + + {K_F11, "F11"}, + {K_F12, "F12"}, + {K_F13, "F13"}, + {K_F14, "F14"}, + {K_F15, "F15"}, + {K_F16, "F16"}, + {K_F17, "F17"}, + {K_F18, "F18"}, + {K_F19, "F19"}, + {K_F20, "F20"}, + + {K_F21, "F21"}, + {K_F22, "F22"}, + {K_F23, "F23"}, + {K_F24, "F24"}, + {K_F25, "F25"}, + {K_F26, "F26"}, + {K_F27, "F27"}, + {K_F28, "F28"}, + {K_F29, "F29"}, + {K_F30, "F30"}, + + {K_F31, "F31"}, + {K_F32, "F32"}, + {K_F33, "F33"}, + {K_F34, "F34"}, + {K_F35, "F35"}, + {K_F36, "F36"}, + {K_F37, "F37"}, + + {K_XF1, "xF1"}, + {K_XF2, "xF2"}, + {K_XF3, "xF3"}, + {K_XF4, "xF4"}, + + {K_HELP, "Help"}, + {K_UNDO, "Undo"}, + {K_INS, "Insert"}, + {K_INS, "Ins"}, // Alternative name + {K_KINS, "kInsert"}, + {K_HOME, "Home"}, + {K_KHOME, "kHome"}, + {K_XHOME, "xHome"}, + {K_ZHOME, "zHome"}, + {K_END, "End"}, + {K_KEND, "kEnd"}, + {K_XEND, "xEnd"}, + {K_ZEND, "zEnd"}, + {K_PAGEUP, "PageUp"}, + {K_PAGEDOWN, "PageDown"}, + {K_KPAGEUP, "kPageUp"}, + {K_KPAGEDOWN, "kPageDown"}, + + {K_KPLUS, "kPlus"}, + {K_KMINUS, "kMinus"}, + {K_KDIVIDE, "kDivide"}, + {K_KMULTIPLY, "kMultiply"}, + {K_KENTER, "kEnter"}, + {K_KPOINT, "kPoint"}, + + {K_K0, "k0"}, + {K_K1, "k1"}, + {K_K2, "k2"}, + {K_K3, "k3"}, + {K_K4, "k4"}, + {K_K5, "k5"}, + {K_K6, "k6"}, + {K_K7, "k7"}, + {K_K8, "k8"}, + {K_K9, "k9"}, + + {'<', "lt"}, + + {K_MOUSE, "Mouse"}, + {K_LEFTMOUSE, "LeftMouse"}, + {K_LEFTMOUSE_NM, "LeftMouseNM"}, + {K_LEFTDRAG, "LeftDrag"}, + {K_LEFTRELEASE, "LeftRelease"}, + {K_LEFTRELEASE_NM, "LeftReleaseNM"}, + {K_MIDDLEMOUSE, "MiddleMouse"}, + {K_MIDDLEDRAG, "MiddleDrag"}, + {K_MIDDLERELEASE, "MiddleRelease"}, + {K_RIGHTMOUSE, "RightMouse"}, + {K_RIGHTDRAG, "RightDrag"}, + {K_RIGHTRELEASE, "RightRelease"}, + {K_MOUSEDOWN, "ScrollWheelUp"}, + {K_MOUSEUP, "ScrollWheelDown"}, + {K_MOUSELEFT, "ScrollWheelRight"}, + {K_MOUSERIGHT, "ScrollWheelLeft"}, + {K_MOUSEDOWN, "MouseDown"}, // OBSOLETE: Use ScrollWheelXXX instead + {K_MOUSEUP, "MouseUp"}, // Same + {K_X1MOUSE, "X1Mouse"}, + {K_X1DRAG, "X1Drag"}, + {K_X1RELEASE, "X1Release"}, + {K_X2MOUSE, "X2Mouse"}, + {K_X2DRAG, "X2Drag"}, + {K_X2RELEASE, "X2Release"}, + {K_DROP, "Drop"}, + {K_ZERO, "Nul"}, + {K_SNR, "SNR"}, + {K_PLUG, "Plug"}, + {K_PASTE, "Paste"}, + {K_FOCUSGAINED, "FocusGained"}, + {K_FOCUSLOST, "FocusLost"}, + {0, NULL} +}; + +int get_special_key_code(const char_u *name) +{ + for (int i = 0; key_names_table[i].name != NULL; i++) { + const char *const table_name = key_names_table[i].name; + int j; + for (j = 0; vim_isIDc(name[j]) && table_name[j] != NUL; j++) { + if (TOLOWER_ASC(table_name[j]) != TOLOWER_ASC(name[j])) { + break; + } + } + if (!vim_isIDc(name[j]) && table_name[j] == NUL) { + return key_names_table[i].key; + } + } + + return 0; +} + + +static const struct modmasktable { + short mod_mask; ///< Bit-mask for particular key modifier. + short mod_flag; ///< Bit(s) for particular key modifier. + char_u name; ///< Single letter name of modifier. +} mod_mask_table[] = { + {MOD_MASK_ALT, MOD_MASK_ALT, (char_u)'M'}, + {MOD_MASK_META, MOD_MASK_META, (char_u)'T'}, + {MOD_MASK_CTRL, MOD_MASK_CTRL, (char_u)'C'}, + {MOD_MASK_SHIFT, MOD_MASK_SHIFT, (char_u)'S'}, + {MOD_MASK_MULTI_CLICK, MOD_MASK_2CLICK, (char_u)'2'}, + {MOD_MASK_MULTI_CLICK, MOD_MASK_3CLICK, (char_u)'3'}, + {MOD_MASK_MULTI_CLICK, MOD_MASK_4CLICK, (char_u)'4'}, + {MOD_MASK_CMD, MOD_MASK_CMD, (char_u)'D'}, + // 'A' must be the last one + {MOD_MASK_ALT, MOD_MASK_ALT, (char_u)'A'}, + {0, 0, NUL} +}; + +int name_to_mod_mask(int c) +{ + c = TOUPPER_ASC(c); + for (size_t i = 0; mod_mask_table[i].mod_mask != 0; i++) { + if (c == mod_mask_table[i].name) { + return mod_mask_table[i].mod_flag; + } + } + return 0; +} + +static int extract_modifiers(int key, int *modp) +{ + int modifiers = *modp; + + if (!(modifiers & MOD_MASK_CMD)) { // Command-key is special + if ((modifiers & MOD_MASK_SHIFT) && ASCII_ISALPHA(key)) { + key = TOUPPER_ASC(key); + modifiers &= ~MOD_MASK_SHIFT; + } + } + if ((modifiers & MOD_MASK_CTRL) + && ((key >= '?' && key <= '_') || ASCII_ISALPHA(key))) { + key = Ctrl_chr(key); + modifiers &= ~MOD_MASK_CTRL; + if (key == 0) { // is + key = K_ZERO; + } + } + + *modp = modifiers; + return key; +} + +int find_special_key(const char_u **srcp, const size_t src_len, int *const modp, + const bool keycode, const bool keep_x_key, + const bool in_string) +{ + const char_u *last_dash; + const char_u *end_of_name; + const char_u *src; + const char_u *bp; + const char_u *const end = *srcp + src_len - 1; + int modifiers; + int bit; + int key; + uvarnumber_T n; + int l; + + if (src_len == 0) { + return 0; + } + + src = *srcp; + if (src[0] != '<') { + return 0; + } + + // Find end of modifier list + last_dash = src; + for (bp = src + 1; bp <= end && (*bp == '-' || vim_isIDc(*bp)); bp++) { + if (*bp == '-') { + last_dash = bp; + if (bp + 1 <= end) { + l = utfc_ptr2len_len(bp + 1, (int)(end - bp) + 1); + // Anything accepted, like . + // or are not special in strings as " is + // the string delimiter. With a backslash it works: + if (end - bp > l && !(in_string && bp[1] == '"') && bp[2] == '>') { + bp += l; + } else if (end - bp > 2 && in_string && bp[1] == '\\' + && bp[2] == '"' && bp[3] == '>') { + bp += 2; + } + } + } + if (end - bp > 3 && bp[0] == 't' && bp[1] == '_') { + bp += 3; // skip t_xx, xx may be '-' or '>' + } else if (end - bp > 4 && STRNICMP(bp, "char-", 5) == 0) { + vim_str2nr(bp + 5, NULL, &l, STR2NR_ALL, NULL, NULL, 0); + bp += l + 5; + break; + } + } + + if (bp <= end && *bp == '>') { // found matching '>' + end_of_name = bp + 1; + + /* Which modifiers are given? */ + modifiers = 0x0; + for (bp = src + 1; bp < last_dash; bp++) { + if (*bp != '-') { + bit = name_to_mod_mask(*bp); + if (bit == 0x0) { + break; // Illegal modifier name + } + modifiers |= bit; + } + } + + // Legal modifier name. + if (bp >= last_dash) { + if (STRNICMP(last_dash + 1, "char-", 5) == 0 + && ascii_isdigit(last_dash[6])) { + // or or + vim_str2nr(last_dash + 6, NULL, NULL, STR2NR_ALL, NULL, &n, 0); + key = (int)n; + } else { + int off = 1; + + // Modifier with single letter, or special key name. + if (in_string && last_dash[1] == '\\' && last_dash[2] == '"') { + off = 2; + } + l = mb_ptr2len(last_dash + 1); + if (modifiers != 0 && last_dash[l + 1] == '>') { + key = PTR2CHAR(last_dash + off); + } else { + key = get_special_key_code(last_dash + off); + if (!keep_x_key) { + key = handle_x_keys(key); + } + } + } + + // get_special_key_code() may return NUL for invalid + // special key name. + if (key != NUL) { + // Only use a modifier when there is no special key code that + // includes the modifier. + key = simplify_key(key, &modifiers); + + if (!keycode) { + // don't want keycode, use single byte code + if (key == K_BS) { + key = BS; + } else if (key == K_DEL || key == K_KDEL) { + key = DEL; + } + } + + // Normal Key with modifier: + // Try to make a single byte code (except for Alt/Meta modifiers). + if (!IS_SPECIAL(key)) { + key = extract_modifiers(key, &modifiers); + } + + *modp = modifiers; + *srcp = end_of_name; + return key; + } + } + } + return 0; +} + +char_u *add_char2buf(int c, char_u *s) +{ + char_u temp[MB_MAXBYTES + 1]; + const int len = utf_char2bytes(c, temp); + for (int i = 0; i < len; ++i) { + c = temp[i]; + // Need to escape K_SPECIAL and CSI like in the typeahead buffer. + if (c == K_SPECIAL) { + *s++ = K_SPECIAL; + *s++ = KS_SPECIAL; + *s++ = KE_FILLER; + } else { + *s++ = c; + } + } + return s; +} + +unsigned int trans_special(const char_u **srcp, const size_t src_len, + char_u *const dst, const bool keycode, + const bool in_string) +{ + int modifiers = 0; + int key; + unsigned int dlen = 0; + + key = find_special_key(srcp, src_len, &modifiers, keycode, false, in_string); + if (key == 0) { + return 0; + } + + // Put the appropriate modifier in a string. + if (modifiers != 0) { + dst[dlen++] = K_SPECIAL; + dst[dlen++] = KS_MODIFIER; + dst[dlen++] = (char_u)modifiers; + } + + if (IS_SPECIAL(key)) { + dst[dlen++] = K_SPECIAL; + dst[dlen++] = (char_u)KEY2TERMCAP0(key); + dst[dlen++] = KEY2TERMCAP1(key); + } else if (has_mbyte && !keycode) { + dlen += (unsigned int)(*mb_char2bytes)(key, dst + dlen); + } else if (keycode) { + char_u *after = add_char2buf(key, dst + dlen); + assert(after >= dst && (uintmax_t)(after - dst) <= UINT_MAX); + dlen = (unsigned int)(after - dst); + } else { + dst[dlen++] = (char_u)key; + } + + return dlen; +} diff --git a/test/symbolic/klee/nvim/mbyte.c b/test/symbolic/klee/nvim/mbyte.c index 394d17b700..bfc191b1b7 100644 --- a/test/symbolic/klee/nvim/mbyte.c +++ b/test/symbolic/klee/nvim/mbyte.c @@ -1,9 +1,89 @@ #include +#include +#include +#include #include "nvim/types.h" #include "nvim/mbyte.h" #include "nvim/ascii.h" +const uint8_t utf8len_tab_zero[] = { + //1 2 3 4 5 6 7 8 9 A B C D E F 0 1 2 3 4 5 6 7 8 9 A B C D E F + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 2 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 4 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 6 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 8 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // A + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, // C + 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,6,6,0,0, // E +}; + +const uint8_t utf8len_tab[] = { + // ?1 ?2 ?3 ?4 ?5 ?6 ?7 ?8 ?9 ?A ?B ?C ?D ?E ?F + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0? + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1? + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2? + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3? + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4? + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5? + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6? + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7? + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8? + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9? + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A? + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B? + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // C? + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // D? + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // E? + 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 1, 1, // F? +}; + +int utf_ptr2char(const char_u *const p) +{ + if (p[0] < 0x80) { // Be quick for ASCII. + return p[0]; + } + + const uint8_t len = utf8len_tab_zero[p[0]]; + if (len > 1 && (p[1] & 0xc0) == 0x80) { + if (len == 2) { + return ((p[0] & 0x1f) << 6) + (p[1] & 0x3f); + } + if ((p[2] & 0xc0) == 0x80) { + if (len == 3) { + return (((p[0] & 0x0f) << 12) + ((p[1] & 0x3f) << 6) + + (p[2] & 0x3f)); + } + if ((p[3] & 0xc0) == 0x80) { + if (len == 4) { + return (((p[0] & 0x07) << 18) + ((p[1] & 0x3f) << 12) + + ((p[2] & 0x3f) << 6) + (p[3] & 0x3f)); + } + if ((p[4] & 0xc0) == 0x80) { + if (len == 5) { + return (((p[0] & 0x03) << 24) + ((p[1] & 0x3f) << 18) + + ((p[2] & 0x3f) << 12) + ((p[3] & 0x3f) << 6) + + (p[4] & 0x3f)); + } + if ((p[5] & 0xc0) == 0x80 && len == 6) { + return (((p[0] & 0x01) << 30) + ((p[1] & 0x3f) << 24) + + ((p[2] & 0x3f) << 18) + ((p[3] & 0x3f) << 12) + + ((p[4] & 0x3f) << 6) + (p[5] & 0x3f)); + } + } + } + } + } + // Illegal value: just return the first byte. + return p[0]; +} + +bool utf_composinglike(const char_u *p1, const char_u *p2) +{ + return false; +} + char_u *string_convert(const vimconv_T *conv, char_u *data, size_t *size) { return NULL; @@ -11,8 +91,117 @@ char_u *string_convert(const vimconv_T *conv, char_u *data, size_t *size) int utfc_ptr2len_len(const char_u *p, int size) { - if (size < 1 || *p == NUL) { + assert(false); + return 0; +} + +int utf_char2len(const int c) +{ + if (c < 0x80) { + return 1; + } else if (c < 0x800) { + return 2; + } else if (c < 0x10000) { + return 3; + } else if (c < 0x200000) { + return 4; + } else if (c < 0x4000000) { + return 5; + } else { + return 6; + } +} + +int utf_char2bytes(const int c, char_u *const buf) +{ + if (c < 0x80) { // 7 bits + buf[0] = c; + return 1; + } else if (c < 0x800) { // 11 bits + buf[0] = 0xc0 + ((unsigned)c >> 6); + buf[1] = 0x80 + (c & 0x3f); + return 2; + } else if (c < 0x10000) { // 16 bits + buf[0] = 0xe0 + ((unsigned)c >> 12); + buf[1] = 0x80 + (((unsigned)c >> 6) & 0x3f); + buf[2] = 0x80 + (c & 0x3f); + return 3; + } else if (c < 0x200000) { // 21 bits + buf[0] = 0xf0 + ((unsigned)c >> 18); + buf[1] = 0x80 + (((unsigned)c >> 12) & 0x3f); + buf[2] = 0x80 + (((unsigned)c >> 6) & 0x3f); + buf[3] = 0x80 + (c & 0x3f); + return 4; + } else if (c < 0x4000000) { // 26 bits + buf[0] = 0xf8 + ((unsigned)c >> 24); + buf[1] = 0x80 + (((unsigned)c >> 18) & 0x3f); + buf[2] = 0x80 + (((unsigned)c >> 12) & 0x3f); + buf[3] = 0x80 + (((unsigned)c >> 6) & 0x3f); + buf[4] = 0x80 + (c & 0x3f); + return 5; + } else { // 31 bits + buf[0] = 0xfc + ((unsigned)c >> 30); + buf[1] = 0x80 + (((unsigned)c >> 24) & 0x3f); + buf[2] = 0x80 + (((unsigned)c >> 18) & 0x3f); + buf[3] = 0x80 + (((unsigned)c >> 12) & 0x3f); + buf[4] = 0x80 + (((unsigned)c >> 6) & 0x3f); + buf[5] = 0x80 + (c & 0x3f); + return 6; + } +} + +int utf_ptr2len(const char_u *const p) +{ + if (*p == NUL) { + return 0; + } + const int len = utf8len_tab[*p]; + for (int i = 1; i < len; i++) { + if ((p[i] & 0xc0) != 0x80) { + return 1; + } + } + return len; +} + +int utfc_ptr2len(const char_u *const p) +{ + uint8_t b0 = (uint8_t)(*p); + + if (b0 == NUL) { return 0; } - return 1; + if (b0 < 0x80 && p[1] < 0x80) { // be quick for ASCII + return 1; + } + + // Skip over first UTF-8 char, stopping at a NUL byte. + int len = utf_ptr2len(p); + + // Check for illegal byte. + if (len == 1 && b0 >= 0x80) { + return 1; + } + + // Check for composing characters. We can handle only the first six, but + // skip all of them (otherwise the cursor would get stuck). + int prevlen = 0; + for (;;) { + if (p[len] < 0x80 || !UTF_COMPOSINGLIKE(p + prevlen, p + len)) { + return len; + } + + // Skip over composing char. + prevlen = len; + len += utf_ptr2len(p + len); + } +} + +void mb_copy_char(const char_u **fp, char_u **tp) +{ + const size_t l = utfc_ptr2len(*fp); + + memmove(*tp, *fp, (size_t)l); + *tp += l; + *fp += l; } diff --git a/test/symbolic/klee/viml_expressions_parser.c b/test/symbolic/klee/viml_expressions_parser.c index 5ad592b99f..e1cea2d990 100644 --- a/test/symbolic/klee/viml_expressions_parser.c +++ b/test/symbolic/klee/viml_expressions_parser.c @@ -18,6 +18,7 @@ #include "nvim/garray.c" #include "nvim/gettext.c" #include "nvim/viml/parser/expressions.c" +#include "nvim/keymap.c" #define INPUT_SIZE 50 diff --git a/test/unit/viml/expressions/parser_spec.lua b/test/unit/viml/expressions/parser_spec.lua index 41261cf7c9..ed77a7cba4 100644 --- a/test/unit/viml/expressions/parser_spec.lua +++ b/test/unit/viml/expressions/parser_spec.lua @@ -142,6 +142,13 @@ local function eastnode2lua(pstate, eastnode, checked_nodes) typ = typ .. ('(val=%u)'):format(tonumber(eastnode.data.num.value)) elseif typ == 'Float' then typ = typ .. ('(val=%e)'):format(tonumber(eastnode.data.flt.value)) + elseif typ == 'SingleQuotedString' or typ == 'DoubleQuotedString' then + if eastnode.data.str.value == nil then + typ = typ .. '(val=NULL)' + else + local s = ffi.string(eastnode.data.str.value, eastnode.data.str.size) + typ = format_string('%s(val=%q)', typ, s) + end end ret_str = typ .. ':' .. ret_str local can_simplify = true @@ -4549,4 +4556,822 @@ describe('Expressions parser', function() hl('InvalidSubscript', ']'), }) end) + itp('works with strings', function() + check_parsing('\'abc\'', 0, { + -- 01234 + ast = { + 'SingleQuotedString(val="abc"):0:0:\'abc\'', + }, + }, { + hl('SingleQuotedString', '\''), + hl('SingleQuotedBody', 'abc'), + hl('SingleQuotedString', '\''), + }) + check_parsing('"abc"', 0, { + -- 01234 + ast = { + 'DoubleQuotedString(val="abc"):0:0:"abc"', + }, + }, { + hl('DoubleQuotedString', '"'), + hl('DoubleQuotedBody', 'abc'), + hl('DoubleQuotedString', '"'), + }) + check_parsing('\'\'', 0, { + -- 01 + ast = { + 'SingleQuotedString(val=NULL):0:0:\'\'', + }, + }, { + hl('SingleQuotedString', '\''), + hl('SingleQuotedString', '\''), + }) + check_parsing('""', 0, { + -- 01 + ast = { + 'DoubleQuotedString(val=NULL):0:0:""', + }, + }, { + hl('DoubleQuotedString', '"'), + hl('DoubleQuotedString', '"'), + }) + check_parsing('"', 0, { + -- 0 + ast = { + 'DoubleQuotedString(val=NULL):0:0:"', + }, + err = { + arg = '"', + msg = 'E114: Missing double quote: %.*s', + }, + }, { + hl('InvalidDoubleQuotedString', '"'), + }) + check_parsing('\'', 0, { + -- 0 + ast = { + 'SingleQuotedString(val=NULL):0:0:\'', + }, + err = { + arg = '\'', + msg = 'E115: Missing single quote: %.*s', + }, + }, { + hl('InvalidSingleQuotedString', '\''), + }) + check_parsing('"a', 0, { + -- 01 + ast = { + 'DoubleQuotedString(val="a"):0:0:"a', + }, + err = { + arg = '"a', + msg = 'E114: Missing double quote: %.*s', + }, + }, { + hl('InvalidDoubleQuotedString', '"'), + hl('InvalidDoubleQuotedBody', 'a'), + }) + check_parsing('\'a', 0, { + -- 01 + ast = { + 'SingleQuotedString(val="a"):0:0:\'a', + }, + err = { + arg = '\'a', + msg = 'E115: Missing single quote: %.*s', + }, + }, { + hl('InvalidSingleQuotedString', '\''), + hl('InvalidSingleQuotedBody', 'a'), + }) + check_parsing('\'abc\'\'def\'', 0, { + -- 0123456789 + ast = { + 'SingleQuotedString(val="abc\'def"):0:0:\'abc\'\'def\'', + }, + }, { + hl('SingleQuotedString', '\''), + hl('SingleQuotedBody', 'abc'), + hl('SingleQuotedQuote', '\'\''), + hl('SingleQuotedBody', 'def'), + hl('SingleQuotedString', '\''), + }) + check_parsing('\'abc\'\'', 0, { + -- 012345 + ast = { + 'SingleQuotedString(val="abc\'"):0:0:\'abc\'\'', + }, + err = { + arg = '\'abc\'\'', + msg = 'E115: Missing single quote: %.*s', + }, + }, { + hl('InvalidSingleQuotedString', '\''), + hl('InvalidSingleQuotedBody', 'abc'), + hl('InvalidSingleQuotedQuote', '\'\''), + }) + check_parsing('\'\'\'\'\'\'\'\'', 0, { + -- 01234567 + ast = { + 'SingleQuotedString(val="\'\'\'"):0:0:\'\'\'\'\'\'\'\'', + }, + }, { + hl('SingleQuotedString', '\''), + hl('SingleQuotedQuote', '\'\''), + hl('SingleQuotedQuote', '\'\''), + hl('SingleQuotedQuote', '\'\''), + hl('SingleQuotedString', '\''), + }) + check_parsing('\'\'\'a\'\'\'\'bc\'', 0, { + -- 01234567890 + -- 0 1 + ast = { + 'SingleQuotedString(val="\'a\'\'bc"):0:0:\'\'\'a\'\'\'\'bc\'', + }, + }, { + hl('SingleQuotedString', '\''), + hl('SingleQuotedQuote', '\'\''), + hl('SingleQuotedBody', 'a'), + hl('SingleQuotedQuote', '\'\''), + hl('SingleQuotedQuote', '\'\''), + hl('SingleQuotedBody', 'bc'), + hl('SingleQuotedString', '\''), + }) + check_parsing('"\\"\\"\\"\\""', 0, { + -- 0123456789 + ast = { + 'DoubleQuotedString(val="\\"\\"\\"\\""):0:0:"\\"\\"\\"\\""', + }, + }, { + hl('DoubleQuotedString', '"'), + hl('DoubleQuotedEscape', '\\"'), + hl('DoubleQuotedEscape', '\\"'), + hl('DoubleQuotedEscape', '\\"'), + hl('DoubleQuotedEscape', '\\"'), + hl('DoubleQuotedString', '"'), + }) + check_parsing('"abc\\"def\\"ghi\\"jkl\\"mno"', 0, { + -- 0123456789012345678901234 + -- 0 1 2 + ast = { + 'DoubleQuotedString(val="abc\\"def\\"ghi\\"jkl\\"mno"):0:0:"abc\\"def\\"ghi\\"jkl\\"mno"', + }, + }, { + hl('DoubleQuotedString', '"'), + hl('DoubleQuotedBody', 'abc'), + hl('DoubleQuotedEscape', '\\"'), + hl('DoubleQuotedBody', 'def'), + hl('DoubleQuotedEscape', '\\"'), + hl('DoubleQuotedBody', 'ghi'), + hl('DoubleQuotedEscape', '\\"'), + hl('DoubleQuotedBody', 'jkl'), + hl('DoubleQuotedEscape', '\\"'), + hl('DoubleQuotedBody', 'mno'), + hl('DoubleQuotedString', '"'), + }) + check_parsing('"\\b\\e\\f\\r\\t\\\\"', 0, { + -- 0123456789012345 + -- 0 1 + ast = { + [[DoubleQuotedString(val="\8\27\12\13\9\\"):0:0:"\b\e\f\r\t\\"]], + }, + }, { + hl('DoubleQuotedString', '"'), + hl('DoubleQuotedEscape', '\\b'), + hl('DoubleQuotedEscape', '\\e'), + hl('DoubleQuotedEscape', '\\f'), + hl('DoubleQuotedEscape', '\\r'), + hl('DoubleQuotedEscape', '\\t'), + hl('DoubleQuotedEscape', '\\\\'), + hl('DoubleQuotedString', '"'), + }) + check_parsing('"\\n\n"', 0, { + -- 01234 + ast = { + 'DoubleQuotedString(val="\\\n\\\n"):0:0:"\\n\n"', + }, + }, { + hl('DoubleQuotedString', '"'), + hl('DoubleQuotedEscape', '\\n'), + hl('DoubleQuotedBody', '\n'), + hl('DoubleQuotedString', '"'), + }) + check_parsing('"\\x00"', 0, { + -- 012345 + ast = { + 'DoubleQuotedString(val="\\0"):0:0:"\\x00"', + }, + }, { + hl('DoubleQuotedString', '"'), + hl('DoubleQuotedEscape', '\\x00'), + hl('DoubleQuotedString', '"'), + }) + check_parsing('"\\xFF"', 0, { + -- 012345 + ast = { + 'DoubleQuotedString(val="\255"):0:0:"\\xFF"', + }, + }, { + hl('DoubleQuotedString', '"'), + hl('DoubleQuotedEscape', '\\xFF'), + hl('DoubleQuotedString', '"'), + }) + check_parsing('"\\xF"', 0, { + -- 012345 + ast = { + 'DoubleQuotedString(val="\\15"):0:0:"\\xF"', + }, + }, { + hl('DoubleQuotedString', '"'), + hl('DoubleQuotedEscape', '\\xF'), + hl('DoubleQuotedString', '"'), + }) + check_parsing('"\\u00AB"', 0, { + -- 01234567 + ast = { + 'DoubleQuotedString(val="«"):0:0:"\\u00AB"', + }, + }, { + hl('DoubleQuotedString', '"'), + hl('DoubleQuotedEscape', '\\u00AB'), + hl('DoubleQuotedString', '"'), + }) + check_parsing('"\\U000000AB"', 0, { + -- 01234567 + ast = { + 'DoubleQuotedString(val="«"):0:0:"\\U000000AB"', + }, + }, { + hl('DoubleQuotedString', '"'), + hl('DoubleQuotedEscape', '\\U000000AB'), + hl('DoubleQuotedString', '"'), + }) + check_parsing('"\\x"', 0, { + -- 0123 + ast = { + 'DoubleQuotedString(val="x"):0:0:"\\x"', + }, + }, { + hl('DoubleQuotedString', '"'), + hl('DoubleQuotedUnknownEscape', '\\x'), + hl('DoubleQuotedString', '"'), + }) + + check_parsing('"\\x', 0, { + -- 012 + ast = { + 'DoubleQuotedString(val="x"):0:0:"\\x', + }, + err = { + arg = '"\\x', + msg = 'E114: Missing double quote: %.*s', + }, + }, { + hl('InvalidDoubleQuotedString', '"'), + hl('InvalidDoubleQuotedUnknownEscape', '\\x'), + }) + + check_parsing('"\\xF', 0, { + -- 0123 + ast = { + 'DoubleQuotedString(val="\\15"):0:0:"\\xF', + }, + err = { + arg = '"\\xF', + msg = 'E114: Missing double quote: %.*s', + }, + }, { + hl('InvalidDoubleQuotedString', '"'), + hl('InvalidDoubleQuotedEscape', '\\xF'), + }) + + check_parsing('"\\u"', 0, { + -- 0123 + ast = { + 'DoubleQuotedString(val="u"):0:0:"\\u"', + }, + }, { + hl('DoubleQuotedString', '"'), + hl('DoubleQuotedUnknownEscape', '\\u'), + hl('DoubleQuotedString', '"'), + }) + + check_parsing('"\\u', 0, { + -- 012 + ast = { + 'DoubleQuotedString(val="u"):0:0:"\\u', + }, + err = { + arg = '"\\u', + msg = 'E114: Missing double quote: %.*s', + }, + }, { + hl('InvalidDoubleQuotedString', '"'), + hl('InvalidDoubleQuotedUnknownEscape', '\\u'), + }) + + check_parsing('"\\U', 0, { + -- 012 + ast = { + 'DoubleQuotedString(val="U"):0:0:"\\U', + }, + err = { + arg = '"\\U', + msg = 'E114: Missing double quote: %.*s', + }, + }, { + hl('InvalidDoubleQuotedString', '"'), + hl('InvalidDoubleQuotedUnknownEscape', '\\U'), + }) + + check_parsing('"\\U"', 0, { + -- 0123 + ast = { + 'DoubleQuotedString(val="U"):0:0:"\\U"', + }, + }, { + hl('DoubleQuotedString', '"'), + hl('DoubleQuotedUnknownEscape', '\\U'), + hl('DoubleQuotedString', '"'), + }) + + check_parsing('"\\xFX"', 0, { + -- 012345 + ast = { + 'DoubleQuotedString(val="\\15X"):0:0:"\\xFX"', + }, + }, { + hl('DoubleQuotedString', '"'), + hl('DoubleQuotedEscape', '\\xF'), + hl('DoubleQuotedBody', 'X'), + hl('DoubleQuotedString', '"'), + }) + + check_parsing('"\\XFX"', 0, { + -- 012345 + ast = { + 'DoubleQuotedString(val="\\15X"):0:0:"\\XFX"', + }, + }, { + hl('DoubleQuotedString', '"'), + hl('DoubleQuotedEscape', '\\XF'), + hl('DoubleQuotedBody', 'X'), + hl('DoubleQuotedString', '"'), + }) + + check_parsing('"\\xX"', 0, { + -- 01234 + ast = { + 'DoubleQuotedString(val="xX"):0:0:"\\xX"', + }, + }, { + hl('DoubleQuotedString', '"'), + hl('DoubleQuotedUnknownEscape', '\\x'), + hl('DoubleQuotedBody', 'X'), + hl('DoubleQuotedString', '"'), + }) + + check_parsing('"\\XX"', 0, { + -- 01234 + ast = { + 'DoubleQuotedString(val="XX"):0:0:"\\XX"', + }, + }, { + hl('DoubleQuotedString', '"'), + hl('DoubleQuotedUnknownEscape', '\\X'), + hl('DoubleQuotedBody', 'X'), + hl('DoubleQuotedString', '"'), + }) + + check_parsing('"\\uX"', 0, { + -- 01234 + ast = { + 'DoubleQuotedString(val="uX"):0:0:"\\uX"', + }, + }, { + hl('DoubleQuotedString', '"'), + hl('DoubleQuotedUnknownEscape', '\\u'), + hl('DoubleQuotedBody', 'X'), + hl('DoubleQuotedString', '"'), + }) + + check_parsing('"\\UX"', 0, { + -- 01234 + ast = { + 'DoubleQuotedString(val="UX"):0:0:"\\UX"', + }, + }, { + hl('DoubleQuotedString', '"'), + hl('DoubleQuotedUnknownEscape', '\\U'), + hl('DoubleQuotedBody', 'X'), + hl('DoubleQuotedString', '"'), + }) + + check_parsing('"\\x0X"', 0, { + -- 012345 + ast = { + 'DoubleQuotedString(val="\\0X"):0:0:"\\x0X"', + }, + }, { + hl('DoubleQuotedString', '"'), + hl('DoubleQuotedEscape', '\\x0'), + hl('DoubleQuotedBody', 'X'), + hl('DoubleQuotedString', '"'), + }) + + check_parsing('"\\X0X"', 0, { + -- 012345 + ast = { + 'DoubleQuotedString(val="\\0X"):0:0:"\\X0X"', + }, + }, { + hl('DoubleQuotedString', '"'), + hl('DoubleQuotedEscape', '\\X0'), + hl('DoubleQuotedBody', 'X'), + hl('DoubleQuotedString', '"'), + }) + + check_parsing('"\\u0X"', 0, { + -- 012345 + ast = { + 'DoubleQuotedString(val="\\0X"):0:0:"\\u0X"', + }, + }, { + hl('DoubleQuotedString', '"'), + hl('DoubleQuotedEscape', '\\u0'), + hl('DoubleQuotedBody', 'X'), + hl('DoubleQuotedString', '"'), + }) + + check_parsing('"\\U0X"', 0, { + -- 012345 + ast = { + 'DoubleQuotedString(val="\\0X"):0:0:"\\U0X"', + }, + }, { + hl('DoubleQuotedString', '"'), + hl('DoubleQuotedEscape', '\\U0'), + hl('DoubleQuotedBody', 'X'), + hl('DoubleQuotedString', '"'), + }) + + check_parsing('"\\x00X"', 0, { + -- 0123456 + ast = { + 'DoubleQuotedString(val="\\0X"):0:0:"\\x00X"', + }, + }, { + hl('DoubleQuotedString', '"'), + hl('DoubleQuotedEscape', '\\x00'), + hl('DoubleQuotedBody', 'X'), + hl('DoubleQuotedString', '"'), + }) + + check_parsing('"\\X00X"', 0, { + -- 0123456 + ast = { + 'DoubleQuotedString(val="\\0X"):0:0:"\\X00X"', + }, + }, { + hl('DoubleQuotedString', '"'), + hl('DoubleQuotedEscape', '\\X00'), + hl('DoubleQuotedBody', 'X'), + hl('DoubleQuotedString', '"'), + }) + + check_parsing('"\\u00X"', 0, { + -- 0123456 + ast = { + 'DoubleQuotedString(val="\\0X"):0:0:"\\u00X"', + }, + }, { + hl('DoubleQuotedString', '"'), + hl('DoubleQuotedEscape', '\\u00'), + hl('DoubleQuotedBody', 'X'), + hl('DoubleQuotedString', '"'), + }) + + check_parsing('"\\U00X"', 0, { + -- 0123456 + ast = { + 'DoubleQuotedString(val="\\0X"):0:0:"\\U00X"', + }, + }, { + hl('DoubleQuotedString', '"'), + hl('DoubleQuotedEscape', '\\U00'), + hl('DoubleQuotedBody', 'X'), + hl('DoubleQuotedString', '"'), + }) + + check_parsing('"\\u000X"', 0, { + -- 01234567 + ast = { + 'DoubleQuotedString(val="\\0X"):0:0:"\\u000X"', + }, + }, { + hl('DoubleQuotedString', '"'), + hl('DoubleQuotedEscape', '\\u000'), + hl('DoubleQuotedBody', 'X'), + hl('DoubleQuotedString', '"'), + }) + + check_parsing('"\\U000X"', 0, { + -- 01234567 + ast = { + 'DoubleQuotedString(val="\\0X"):0:0:"\\U000X"', + }, + }, { + hl('DoubleQuotedString', '"'), + hl('DoubleQuotedEscape', '\\U000'), + hl('DoubleQuotedBody', 'X'), + hl('DoubleQuotedString', '"'), + }) + + check_parsing('"\\u0000X"', 0, { + -- 012345678 + ast = { + 'DoubleQuotedString(val="\\0X"):0:0:"\\u0000X"', + }, + }, { + hl('DoubleQuotedString', '"'), + hl('DoubleQuotedEscape', '\\u0000'), + hl('DoubleQuotedBody', 'X'), + hl('DoubleQuotedString', '"'), + }) + + check_parsing('"\\U0000X"', 0, { + -- 012345678 + ast = { + 'DoubleQuotedString(val="\\0X"):0:0:"\\U0000X"', + }, + }, { + hl('DoubleQuotedString', '"'), + hl('DoubleQuotedEscape', '\\U0000'), + hl('DoubleQuotedBody', 'X'), + hl('DoubleQuotedString', '"'), + }) + + check_parsing('"\\U00000X"', 0, { + -- 0123456789 + ast = { + 'DoubleQuotedString(val="\\0X"):0:0:"\\U00000X"', + }, + }, { + hl('DoubleQuotedString', '"'), + hl('DoubleQuotedEscape', '\\U00000'), + hl('DoubleQuotedBody', 'X'), + hl('DoubleQuotedString', '"'), + }) + + check_parsing('"\\U000000X"', 0, { + -- 01234567890 + -- 0 1 + ast = { + 'DoubleQuotedString(val="\\0X"):0:0:"\\U000000X"', + }, + }, { + hl('DoubleQuotedString', '"'), + hl('DoubleQuotedEscape', '\\U000000'), + hl('DoubleQuotedBody', 'X'), + hl('DoubleQuotedString', '"'), + }) + + check_parsing('"\\U0000000X"', 0, { + -- 012345678901 + -- 0 1 + ast = { + 'DoubleQuotedString(val="\\0X"):0:0:"\\U0000000X"', + }, + }, { + hl('DoubleQuotedString', '"'), + hl('DoubleQuotedEscape', '\\U0000000'), + hl('DoubleQuotedBody', 'X'), + hl('DoubleQuotedString', '"'), + }) + + check_parsing('"\\U00000000X"', 0, { + -- 0123456789012 + -- 0 1 + ast = { + 'DoubleQuotedString(val="\\0X"):0:0:"\\U00000000X"', + }, + }, { + hl('DoubleQuotedString', '"'), + hl('DoubleQuotedEscape', '\\U00000000'), + hl('DoubleQuotedBody', 'X'), + hl('DoubleQuotedString', '"'), + }) + + check_parsing('"\\x000X"', 0, { + -- 01234567 + ast = { + 'DoubleQuotedString(val="\\0000X"):0:0:"\\x000X"', + }, + }, { + hl('DoubleQuotedString', '"'), + hl('DoubleQuotedEscape', '\\x00'), + hl('DoubleQuotedBody', '0X'), + hl('DoubleQuotedString', '"'), + }) + + check_parsing('"\\X000X"', 0, { + -- 01234567 + ast = { + 'DoubleQuotedString(val="\\0000X"):0:0:"\\X000X"', + }, + }, { + hl('DoubleQuotedString', '"'), + hl('DoubleQuotedEscape', '\\X00'), + hl('DoubleQuotedBody', '0X'), + hl('DoubleQuotedString', '"'), + }) + + check_parsing('"\\u00000X"', 0, { + -- 0123456789 + ast = { + 'DoubleQuotedString(val="\\0000X"):0:0:"\\u00000X"', + }, + }, { + hl('DoubleQuotedString', '"'), + hl('DoubleQuotedEscape', '\\u0000'), + hl('DoubleQuotedBody', '0X'), + hl('DoubleQuotedString', '"'), + }) + + check_parsing('"\\U000000000X"', 0, { + -- 01234567890123 + -- 0 1 + ast = { + 'DoubleQuotedString(val="\\0000X"):0:0:"\\U000000000X"', + }, + }, { + hl('DoubleQuotedString', '"'), + hl('DoubleQuotedEscape', '\\U00000000'), + hl('DoubleQuotedBody', '0X'), + hl('DoubleQuotedString', '"'), + }) + + check_parsing('"\\0"', 0, { + -- 0123 + ast = { + 'DoubleQuotedString(val="\\0"):0:0:"\\0"', + }, + }, { + hl('DoubleQuotedString', '"'), + hl('DoubleQuotedEscape', '\\0'), + hl('DoubleQuotedString', '"'), + }) + + check_parsing('"\\00"', 0, { + -- 01234 + ast = { + 'DoubleQuotedString(val="\\0"):0:0:"\\00"', + }, + }, { + hl('DoubleQuotedString', '"'), + hl('DoubleQuotedEscape', '\\00'), + hl('DoubleQuotedString', '"'), + }) + + check_parsing('"\\000"', 0, { + -- 012345 + ast = { + 'DoubleQuotedString(val="\\0"):0:0:"\\000"', + }, + }, { + hl('DoubleQuotedString', '"'), + hl('DoubleQuotedEscape', '\\000'), + hl('DoubleQuotedString', '"'), + }) + + check_parsing('"\\0000"', 0, { + -- 0123456 + ast = { + 'DoubleQuotedString(val="\\0000"):0:0:"\\0000"', + }, + }, { + hl('DoubleQuotedString', '"'), + hl('DoubleQuotedEscape', '\\000'), + hl('DoubleQuotedBody', '0'), + hl('DoubleQuotedString', '"'), + }) + + check_parsing('"\\8"', 0, { + -- 0123 + ast = { + 'DoubleQuotedString(val="8"):0:0:"\\8"', + }, + }, { + hl('DoubleQuotedString', '"'), + hl('DoubleQuotedUnknownEscape', '\\8'), + hl('DoubleQuotedString', '"'), + }) + + check_parsing('"\\08"', 0, { + -- 01234 + ast = { + 'DoubleQuotedString(val="\\0008"):0:0:"\\08"', + }, + }, { + hl('DoubleQuotedString', '"'), + hl('DoubleQuotedEscape', '\\0'), + hl('DoubleQuotedBody', '8'), + hl('DoubleQuotedString', '"'), + }) + + check_parsing('"\\008"', 0, { + -- 012345 + ast = { + 'DoubleQuotedString(val="\\0008"):0:0:"\\008"', + }, + }, { + hl('DoubleQuotedString', '"'), + hl('DoubleQuotedEscape', '\\00'), + hl('DoubleQuotedBody', '8'), + hl('DoubleQuotedString', '"'), + }) + + check_parsing('"\\0008"', 0, { + -- 0123456 + ast = { + 'DoubleQuotedString(val="\\0008"):0:0:"\\0008"', + }, + }, { + hl('DoubleQuotedString', '"'), + hl('DoubleQuotedEscape', '\\000'), + hl('DoubleQuotedBody', '8'), + hl('DoubleQuotedString', '"'), + }) + + check_parsing('"\\777"', 0, { + -- 012345 + ast = { + 'DoubleQuotedString(val="\255"):0:0:"\\777"', + }, + }, { + hl('DoubleQuotedString', '"'), + hl('DoubleQuotedEscape', '\\777'), + hl('DoubleQuotedString', '"'), + }) + + check_parsing('"\\050"', 0, { + -- 012345 + ast = { + 'DoubleQuotedString(val="\40"):0:0:"\\050"', + }, + }, { + hl('DoubleQuotedString', '"'), + hl('DoubleQuotedEscape', '\\050'), + hl('DoubleQuotedString', '"'), + }) + + check_parsing('"\\"', 0, { + -- 012345 + ast = { + 'DoubleQuotedString(val="\\21"):0:0:"\\"', + }, + }, { + hl('DoubleQuotedString', '"'), + hl('DoubleQuotedEscape', '\\'), + hl('DoubleQuotedString', '"'), + }) + + check_parsing('"\\<', 0, { + -- 012 + ast = { + 'DoubleQuotedString(val="<"):0:0:"\\<', + }, + err = { + arg = '"\\<', + msg = 'E114: Missing double quote: %.*s', + }, + }, { + hl('InvalidDoubleQuotedString', '"'), + hl('InvalidDoubleQuotedUnknownEscape', '\\<'), + }) + + check_parsing('"\\<"', 0, { + -- 0123 + ast = { + 'DoubleQuotedString(val="<"):0:0:"\\<"', + }, + }, { + hl('DoubleQuotedString', '"'), + hl('DoubleQuotedUnknownEscape', '\\<'), + hl('DoubleQuotedString', '"'), + }) + + check_parsing('"\\ Date: Mon, 9 Oct 2017 02:55:56 +0300 Subject: viml/parser/expressions: Finish parser MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Note: formatc.lua was unable to swallow some newer additions to ExprASTNodeType (specifically `kExprNodeOr = '|'` and probably something else), so all `= …` were dropped: in any case they only were there in order to not bother updating viml_pexpr_debug_print_ast_node and since it is now known all nodes which will be present it is not much of an issue. --- test/symbolic/klee/viml_expressions_lexer.c | 1 + test/unit/viml/expressions/lexer_spec.lua | 6 +++--- test/unit/viml/expressions/parser_spec.lua | 19 +++++++++++++++++++ test/unit/viml/helpers.lua | 1 + 4 files changed, 24 insertions(+), 3 deletions(-) (limited to 'test') diff --git a/test/symbolic/klee/viml_expressions_lexer.c b/test/symbolic/klee/viml_expressions_lexer.c index cddc1cb2f1..ee7dc312e9 100644 --- a/test/symbolic/klee/viml_expressions_lexer.c +++ b/test/symbolic/klee/viml_expressions_lexer.c @@ -17,6 +17,7 @@ #include "nvim/charset.c" #include "nvim/garray.c" #include "nvim/gettext.c" +#include "nvim/keymap.c" #include "nvim/viml/parser/expressions.c" #define INPUT_SIZE 7 diff --git a/test/unit/viml/expressions/lexer_spec.lua b/test/unit/viml/expressions/lexer_spec.lua index f180d8ceff..674b1b37db 100644 --- a/test/unit/viml/expressions/lexer_spec.lua +++ b/test/unit/viml/expressions/lexer_spec.lua @@ -62,9 +62,9 @@ child_call_once(function() } eltkn_opt_scope_tab = { - [tonumber(lib.kExprLexOptUnspecified)] = 'Unspecified', - [tonumber(lib.kExprLexOptGlobal)] = 'Global', - [tonumber(lib.kExprLexOptLocal)] = 'Local', + [tonumber(lib.kExprOptScopeUnspecified)] = 'Unspecified', + [tonumber(lib.kExprOptScopeGlobal)] = 'Global', + [tonumber(lib.kExprOptScopeLocal)] = 'Local', } end) diff --git a/test/unit/viml/expressions/parser_spec.lua b/test/unit/viml/expressions/parser_spec.lua index ed77a7cba4..5041708a3e 100644 --- a/test/unit/viml/expressions/parser_spec.lua +++ b/test/unit/viml/expressions/parser_spec.lua @@ -93,6 +93,16 @@ make_enum_conv_tab(lib, { 'kExprNodeFloat', 'kExprNodeSingleQuotedString', 'kExprNodeDoubleQuotedString', + 'kExprNodeOr', + 'kExprNodeAnd', + 'kExprNodeUnaryMinus', + 'kExprNodeBinaryMinus', + 'kExprNodeNot', + 'kExprNodeMultiplication', + 'kExprNodeDivision', + 'kExprNodeMod', + 'kExprNodeOption', + 'kExprNodeEnvironment', }, 'kExprNode', function(ret) east_node_type_tab = ret end) local function conv_east_node_type(typ) @@ -149,6 +159,15 @@ local function eastnode2lua(pstate, eastnode, checked_nodes) local s = ffi.string(eastnode.data.str.value, eastnode.data.str.size) typ = format_string('%s(val=%q)', typ, s) end + elseif typ == 'Option' then + typ = ('%s(scope=%s,ident=%s)'):format( + typ, + tostring(intchar2lua(eastnode.data.opt.scope)), + ffi.string(eastnode.data.opt.ident, eastnode.data.opt.ident_len)) + elseif typ == 'Environment' then + typ = ('%s(ident=%s)'):format( + typ, + ffi.string(eastnode.data.env.ident, eastnode.data.env.ident_len)) end ret_str = typ .. ':' .. ret_str local can_simplify = true diff --git a/test/unit/viml/helpers.lua b/test/unit/viml/helpers.lua index 0b92be2654..c965cacb29 100644 --- a/test/unit/viml/helpers.lua +++ b/test/unit/viml/helpers.lua @@ -5,6 +5,7 @@ local cimport = helpers.cimport local kvi_new = helpers.kvi_new local kvi_init = helpers.kvi_init local conv_enum = helpers.conv_enum +local child_call_once = helpers.child_call_once local make_enum_conv_tab = helpers.make_enum_conv_tab local lib = cimport('./src/nvim/viml/parser/expressions.h') -- cgit From 8178ba2871bb427e03419a2f68c0fb119e44e717 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 14 Oct 2017 00:26:52 +0300 Subject: klee: Fix some errors made in …parser.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/symbolic/klee/viml_expressions_parser.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'test') diff --git a/test/symbolic/klee/viml_expressions_parser.c b/test/symbolic/klee/viml_expressions_parser.c index e1cea2d990..a4fbdb6bf4 100644 --- a/test/symbolic/klee/viml_expressions_parser.c +++ b/test/symbolic/klee/viml_expressions_parser.c @@ -1,5 +1,4 @@ #ifdef USE_KLEE -# error UNFINISHED # include #else # include @@ -47,10 +46,10 @@ int main(const int argc, const char *const *const argv, #ifdef USE_KLEE klee_make_symbolic(input, sizeof(input), "input"); klee_make_symbolic(&shift, sizeof(shift), "shift"); - klee_make_symbolic(&flags, sizeof{flags}, "flags"); + klee_make_symbolic(&flags, sizeof(flags), "flags"); klee_assume(shift < INPUT_SIZE); klee_assume( - flags <= kExprFlagsMulti|kExprFlagsDisallowEOC|kExprFlagsPrintError); + flags <= (kExprFlagsMulti|kExprFlagsDisallowEOC|kExprFlagsPrintError)); #endif ParserLine plines[] = { @@ -93,5 +92,6 @@ int main(const int argc, const char *const *const argv, assert(ast.root != NULL || plines[0].size == 0); assert(ast.root != NULL || ast.err.msg); + // FIXME: check for AST recursiveness // FIXME: free memory and assert no memory leaks } -- cgit From c286155bfa53c828ebe5479fd81a544740a92403 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 15 Oct 2017 19:06:41 +0300 Subject: viml/parser/expressions: Create tests for latest additions --- test/helpers.lua | 7 + test/symbolic/klee/nvim/mbyte.c | 63 +- test/unit/viml/expressions/parser_spec.lua | 1430 ++++++++++++++++++++++++++++ 3 files changed, 1498 insertions(+), 2 deletions(-) (limited to 'test') diff --git a/test/helpers.lua b/test/helpers.lua index e7d8c185ba..6a42963d7f 100644 --- a/test/helpers.lua +++ b/test/helpers.lua @@ -352,7 +352,14 @@ format_luav = function(v, indent) end end ret = ret .. indent .. '}' + elseif type(v) == 'number' then + if v % 1 == 0 then + ret = ('%d'):format(v) + else + ret = ('%e'):format(v) + end else + print(type(v)) -- Not implemented yet assert(false) end diff --git a/test/symbolic/klee/nvim/mbyte.c b/test/symbolic/klee/nvim/mbyte.c index bfc191b1b7..f98a531206 100644 --- a/test/symbolic/klee/nvim/mbyte.c +++ b/test/symbolic/klee/nvim/mbyte.c @@ -89,10 +89,69 @@ char_u *string_convert(const vimconv_T *conv, char_u *data, size_t *size) return NULL; } +int utf_ptr2len_len(const char_u *p, int size) +{ + int len; + int i; + int m; + + len = utf8len_tab[*p]; + if (len == 1) + return 1; /* NUL, ascii or illegal lead byte */ + if (len > size) + m = size; /* incomplete byte sequence. */ + else + m = len; + for (i = 1; i < m; ++i) + if ((p[i] & 0xc0) != 0x80) + return 1; + return len; +} + int utfc_ptr2len_len(const char_u *p, int size) { - assert(false); - return 0; + int len; + int prevlen; + + if (size < 1 || *p == NUL) + return 0; + if (p[0] < 0x80 && (size == 1 || p[1] < 0x80)) /* be quick for ASCII */ + return 1; + + /* Skip over first UTF-8 char, stopping at a NUL byte. */ + len = utf_ptr2len_len(p, size); + + /* Check for illegal byte and incomplete byte sequence. */ + if ((len == 1 && p[0] >= 0x80) || len > size) + return 1; + + /* + * Check for composing characters. We can handle only the first six, but + * skip all of them (otherwise the cursor would get stuck). + */ + prevlen = 0; + while (len < size) { + int len_next_char; + + if (p[len] < 0x80) + break; + + /* + * Next character length should not go beyond size to ensure that + * UTF_COMPOSINGLIKE(...) does not read beyond size. + */ + len_next_char = utf_ptr2len_len(p + len, size - len); + if (len_next_char > size - len) + break; + + if (!UTF_COMPOSINGLIKE(p + prevlen, p + len)) + break; + + /* Skip over composing char */ + prevlen = len; + len += len_next_char; + } + return len; } int utf_char2len(const int c) diff --git a/test/unit/viml/expressions/parser_spec.lua b/test/unit/viml/expressions/parser_spec.lua index 5041708a3e..d95eaca79b 100644 --- a/test/unit/viml/expressions/parser_spec.lua +++ b/test/unit/viml/expressions/parser_spec.lua @@ -1453,6 +1453,24 @@ describe('Expressions parser', function() hl('CallingParenthesis', '(', 1), hl('CallingParenthesis', ')'), }) + check_parsing('{@a', 0, { + -- 012 + ast = { + { + 'UnknownFigure(-di):0:0:{', + children = { + 'Register(name=a):0:1:@a', + }, + }, + }, + err = { + arg = '{@a', + msg = 'E15: Missing closing figure brace: %.*s', + }, + }, { + hl('FigureBrace', '{'), + hl('Register', '@a'), + }) end) itp('works with lambdas and dictionaries', function() check_parsing('{}', 0, { @@ -2768,6 +2786,182 @@ describe('Expressions parser', function() hl('Identifier', 'c', 1), hl('Dict', '}'), }) + check_parsing('{', 0, { + -- 0 + ast = { + 'UnknownFigure(\\di):0:0:{', + }, + err = { + arg = '{', + msg = 'E15: Missing closing figure brace: %.*s', + }, + }, { + hl('FigureBrace', '{'), + }) + check_parsing('{a', 0, { + -- 01 + ast = { + { + 'UnknownFigure(\\di):0:0:{', + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + }, + }, + }, + err = { + arg = '{a', + msg = 'E15: Missing closing figure brace: %.*s', + }, + }, { + hl('FigureBrace', '{'), + hl('Identifier', 'a'), + }) + check_parsing('{a,b', 0, { + -- 0123 + ast = { + { + 'Lambda(\\di):0:0:{', + children = { + { + 'Comma:0:2:,', + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + 'PlainIdentifier(scope=0,ident=b):0:3:b', + }, + }, + }, + }, + }, + err = { + arg = '{a,b', + msg = 'E15: Missing closing figure brace for lambda: %.*s', + }, + }, { + hl('Lambda', '{'), + hl('Identifier', 'a'), + hl('Comma', ','), + hl('Identifier', 'b'), + }) + check_parsing('{a,b->', 0, { + -- 012345 + ast = { + { + 'Lambda(\\di):0:0:{', + children = { + { + 'Comma:0:2:,', + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + 'PlainIdentifier(scope=0,ident=b):0:3:b', + }, + }, + 'Arrow:0:4:->', + }, + }, + }, + err = { + arg = '', + msg = 'E15: Expected value, got EOC: %.*s', + }, + }, { + hl('Lambda', '{'), + hl('Identifier', 'a'), + hl('Comma', ','), + hl('Identifier', 'b'), + hl('Arrow', '->'), + }) + check_parsing('{a,b->c', 0, { + -- 0123456 + ast = { + { + 'Lambda(\\di):0:0:{', + children = { + { + 'Comma:0:2:,', + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + 'PlainIdentifier(scope=0,ident=b):0:3:b', + }, + }, + { + 'Arrow:0:4:->', + children = { + 'PlainIdentifier(scope=0,ident=c):0:6:c', + }, + }, + }, + }, + }, + err = { + arg = '{a,b->c', + msg = 'E15: Missing closing figure brace for lambda: %.*s', + }, + }, { + hl('Lambda', '{'), + hl('Identifier', 'a'), + hl('Comma', ','), + hl('Identifier', 'b'), + hl('Arrow', '->'), + hl('Identifier', 'c'), + }) + check_parsing('{a : b', 0, { + -- 012345 + ast = { + { + 'DictLiteral(-di):0:0:{', + children = { + { + 'Colon:0:2: :', + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + 'PlainIdentifier(scope=0,ident=b):0:4: b', + }, + }, + }, + }, + }, + err = { + arg = '{a : b', + msg = 'E723: Missing end of Dictionary \'}\': %.*s', + }, + }, { + hl('Dict', '{'), + hl('Identifier', 'a'), + hl('Colon', ':', 1), + hl('Identifier', 'b', 1), + }) + check_parsing('{a : b,', 0, { + -- 0123456 + ast = { + { + 'DictLiteral(-di):0:0:{', + children = { + { + 'Comma:0:6:,', + children = { + { + 'Colon:0:2: :', + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + 'PlainIdentifier(scope=0,ident=b):0:4: b', + }, + }, + }, + }, + }, + }, + }, + err = { + arg = '', + msg = 'E15: Expected value, got EOC: %.*s', + }, + }, { + hl('Dict', '{'), + hl('Identifier', 'a'), + hl('Colon', ':', 1), + hl('Identifier', 'b', 1), + hl('Comma', ','), + }) end) itp('works with ternary operator', function() check_parsing('a ? b : c', 0, { @@ -4574,6 +4768,38 @@ describe('Expressions parser', function() hl('Subscript', '['), hl('InvalidSubscript', ']'), }) + + check_parsing('[', 0, { + -- 0 + ast = { + 'ListLiteral:0:0:[', + }, + err = { + arg = '', + msg = 'E15: Expected value, got EOC: %.*s', + }, + }, { + hl('List', '['), + }) + + check_parsing('[1', 0, { + -- 01 + ast = { + { + 'ListLiteral:0:0:[', + children = { + 'Integer(val=1):0:1:1', + }, + }, + }, + err = { + arg = '[1', + msg = 'E697: Missing end of List \']\': %.*s', + }, + }, { + hl('List', '['), + hl('Number', '1'), + }) end) itp('works with strings', function() check_parsing('\'abc\'', 0, { @@ -5393,4 +5619,1208 @@ describe('Expressions parser', function() hl('DoubleQuotedString', '"'), }) end) + itp('works with multiplication-like operators', function() + check_parsing('2+2*2', 0, { + -- 01234 + ast = { + { + 'BinaryPlus:0:1:+', + children = { + 'Integer(val=2):0:0:2', + { + 'Multiplication:0:3:*', + children = { + 'Integer(val=2):0:2:2', + 'Integer(val=2):0:4:2', + }, + }, + }, + }, + }, + }, { + hl('Number', '2'), + hl('BinaryPlus', '+'), + hl('Number', '2'), + hl('Multiplication', '*'), + hl('Number', '2'), + }) + + check_parsing('2+2*', 0, { + -- 0123 + ast = { + { + 'BinaryPlus:0:1:+', + children = { + 'Integer(val=2):0:0:2', + { + 'Multiplication:0:3:*', + children = { + 'Integer(val=2):0:2:2', + }, + }, + }, + }, + }, + err = { + arg = '', + msg = 'E15: Expected value, got EOC: %.*s', + }, + }, { + hl('Number', '2'), + hl('BinaryPlus', '+'), + hl('Number', '2'), + hl('Multiplication', '*'), + }) + + check_parsing('2+*2', 0, { + -- 0123 + ast = { + { + 'BinaryPlus:0:1:+', + children = { + 'Integer(val=2):0:0:2', + { + 'Multiplication:0:2:*', + children = { + 'Missing:0:2:', + 'Integer(val=2):0:3:2', + }, + }, + }, + }, + }, + err = { + arg = '*2', + msg = 'E15: Unexpected multiplication-like operator: %.*s', + }, + }, { + hl('Number', '2'), + hl('BinaryPlus', '+'), + hl('InvalidMultiplication', '*'), + hl('Number', '2'), + }) + + check_parsing('2+2/2', 0, { + -- 01234 + ast = { + { + 'BinaryPlus:0:1:+', + children = { + 'Integer(val=2):0:0:2', + { + 'Division:0:3:/', + children = { + 'Integer(val=2):0:2:2', + 'Integer(val=2):0:4:2', + }, + }, + }, + }, + }, + }, { + hl('Number', '2'), + hl('BinaryPlus', '+'), + hl('Number', '2'), + hl('Division', '/'), + hl('Number', '2'), + }) + + check_parsing('2+2/', 0, { + -- 0123 + ast = { + { + 'BinaryPlus:0:1:+', + children = { + 'Integer(val=2):0:0:2', + { + 'Division:0:3:/', + children = { + 'Integer(val=2):0:2:2', + }, + }, + }, + }, + }, + err = { + arg = '', + msg = 'E15: Expected value, got EOC: %.*s', + }, + }, { + hl('Number', '2'), + hl('BinaryPlus', '+'), + hl('Number', '2'), + hl('Division', '/'), + }) + + check_parsing('2+/2', 0, { + -- 0123 + ast = { + { + 'BinaryPlus:0:1:+', + children = { + 'Integer(val=2):0:0:2', + { + 'Division:0:2:/', + children = { + 'Missing:0:2:', + 'Integer(val=2):0:3:2', + }, + }, + }, + }, + }, + err = { + arg = '/2', + msg = 'E15: Unexpected multiplication-like operator: %.*s', + }, + }, { + hl('Number', '2'), + hl('BinaryPlus', '+'), + hl('InvalidDivision', '/'), + hl('Number', '2'), + }) + + check_parsing('2+2%2', 0, { + -- 01234 + ast = { + { + 'BinaryPlus:0:1:+', + children = { + 'Integer(val=2):0:0:2', + { + 'Mod:0:3:%', + children = { + 'Integer(val=2):0:2:2', + 'Integer(val=2):0:4:2', + }, + }, + }, + }, + }, + }, { + hl('Number', '2'), + hl('BinaryPlus', '+'), + hl('Number', '2'), + hl('Mod', '%'), + hl('Number', '2'), + }) + + check_parsing('2+2%', 0, { + -- 0123 + ast = { + { + 'BinaryPlus:0:1:+', + children = { + 'Integer(val=2):0:0:2', + { + 'Mod:0:3:%', + children = { + 'Integer(val=2):0:2:2', + }, + }, + }, + }, + }, + err = { + arg = '', + msg = 'E15: Expected value, got EOC: %.*s', + }, + }, { + hl('Number', '2'), + hl('BinaryPlus', '+'), + hl('Number', '2'), + hl('Mod', '%'), + }) + + check_parsing('2+%2', 0, { + -- 0123 + ast = { + { + 'BinaryPlus:0:1:+', + children = { + 'Integer(val=2):0:0:2', + { + 'Mod:0:2:%', + children = { + 'Missing:0:2:', + 'Integer(val=2):0:3:2', + }, + }, + }, + }, + }, + err = { + arg = '%2', + msg = 'E15: Unexpected multiplication-like operator: %.*s', + }, + }, { + hl('Number', '2'), + hl('BinaryPlus', '+'), + hl('InvalidMod', '%'), + hl('Number', '2'), + }) + end) + itp('works with -', function() + check_parsing('@a', 0, { + ast = { + 'Register(name=a):0:0:@a', + }, + }, { + hl('Register', '@a'), + }) + check_parsing('-@a', 0, { + ast = { + { + 'UnaryMinus:0:0:-', + children = { + 'Register(name=a):0:1:@a', + }, + }, + }, + }, { + hl('UnaryMinus', '-'), + hl('Register', '@a'), + }) + check_parsing('@a-@b', 0, { + ast = { + { + 'BinaryMinus:0:2:-', + children = { + 'Register(name=a):0:0:@a', + 'Register(name=b):0:3:@b', + }, + }, + }, + }, { + hl('Register', '@a'), + hl('BinaryMinus', '-'), + hl('Register', '@b'), + }) + check_parsing('@a-@b-@c', 0, { + ast = { + { + 'BinaryMinus:0:5:-', + children = { + { + 'BinaryMinus:0:2:-', + children = { + 'Register(name=a):0:0:@a', + 'Register(name=b):0:3:@b', + }, + }, + 'Register(name=c):0:6:@c', + }, + }, + }, + }, { + hl('Register', '@a'), + hl('BinaryMinus', '-'), + hl('Register', '@b'), + hl('BinaryMinus', '-'), + hl('Register', '@c'), + }) + check_parsing('-@a-@b', 0, { + ast = { + { + 'BinaryMinus:0:3:-', + children = { + { + 'UnaryMinus:0:0:-', + children = { + 'Register(name=a):0:1:@a', + }, + }, + 'Register(name=b):0:4:@b', + }, + }, + }, + }, { + hl('UnaryMinus', '-'), + hl('Register', '@a'), + hl('BinaryMinus', '-'), + hl('Register', '@b'), + }) + check_parsing('-@a--@b', 0, { + ast = { + { + 'BinaryMinus:0:3:-', + children = { + { + 'UnaryMinus:0:0:-', + children = { + 'Register(name=a):0:1:@a', + }, + }, + { + 'UnaryMinus:0:4:-', + children = { + 'Register(name=b):0:5:@b', + }, + }, + }, + }, + }, + }, { + hl('UnaryMinus', '-'), + hl('Register', '@a'), + hl('BinaryMinus', '-'), + hl('UnaryMinus', '-'), + hl('Register', '@b'), + }) + check_parsing('@a@b', 0, { + ast = { + { + 'OpMissing:0:2:', + children = { + 'Register(name=a):0:0:@a', + 'Register(name=b):0:2:@b', + }, + }, + }, + err = { + arg = '@b', + msg = 'E15: Missing operator: %.*s', + }, + }, { + hl('Register', '@a'), + hl('InvalidRegister', '@b'), + }) + check_parsing(' @a \t @b', 0, { + ast = { + { + 'OpMissing:0:3:', + children = { + 'Register(name=a):0:0: @a', + 'Register(name=b):0:3: \t @b', + }, + }, + }, + err = { + arg = '@b', + msg = 'E15: Missing operator: %.*s', + }, + }, { + hl('Register', '@a', 1), + hl('InvalidSpacing', ' \t '), + hl('Register', '@b'), + }) + check_parsing('-', 0, { + ast = { + 'UnaryMinus:0:0:-', + }, + err = { + arg = '', + msg = 'E15: Expected value, got EOC: %.*s', + }, + }, { + hl('UnaryMinus', '-'), + }) + check_parsing(' -', 0, { + ast = { + 'UnaryMinus:0:0: -', + }, + err = { + arg = '', + msg = 'E15: Expected value, got EOC: %.*s', + }, + }, { + hl('UnaryMinus', '-', 1), + }) + check_parsing('@a- ', 0, { + ast = { + { + 'BinaryMinus:0:2:-', + children = { + 'Register(name=a):0:0:@a', + }, + }, + }, + err = { + arg = '', + msg = 'E15: Expected value, got EOC: %.*s', + }, + }, { + hl('Register', '@a'), + hl('BinaryMinus', '-'), + }) + end) + itp('works with logical operators', function() + check_parsing('a && b || c && d', 0, { + -- 0123456789012345 + -- 0 1 + ast = { + { + 'Or:0:6: ||', + children = { + { + 'And:0:1: &&', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + 'PlainIdentifier(scope=0,ident=b):0:4: b', + }, + }, + { + 'And:0:11: &&', + children = { + 'PlainIdentifier(scope=0,ident=c):0:9: c', + 'PlainIdentifier(scope=0,ident=d):0:14: d', + }, + }, + }, + }, + }, + }, { + hl('Identifier', 'a'), + hl('And', '&&', 1), + hl('Identifier', 'b', 1), + hl('Or', '||', 1), + hl('Identifier', 'c', 1), + hl('And', '&&', 1), + hl('Identifier', 'd', 1), + }) + + check_parsing('&& a', 0, { + -- 0123 + ast = { + { + 'And:0:0:&&', + children = { + 'Missing:0:0:', + 'PlainIdentifier(scope=0,ident=a):0:2: a', + }, + }, + }, + err = { + arg = '&& a', + msg = 'E15: Unexpected and operator: %.*s', + }, + }, { + hl('InvalidAnd', '&&'), + hl('Identifier', 'a', 1), + }) + + check_parsing('|| a', 0, { + -- 0123 + ast = { + { + 'Or:0:0:||', + children = { + 'Missing:0:0:', + 'PlainIdentifier(scope=0,ident=a):0:2: a', + }, + }, + }, + err = { + arg = '|| a', + msg = 'E15: Unexpected or operator: %.*s', + }, + }, { + hl('InvalidOr', '||'), + hl('Identifier', 'a', 1), + }) + + check_parsing('a||', 0, { + -- 012 + ast = { + { + 'Or:0:1:||', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + }, + }, + }, + err = { + arg = '', + msg = 'E15: Expected value, got EOC: %.*s', + }, + }, { + hl('Identifier', 'a'), + hl('Or', '||'), + }) + + check_parsing('a&&', 0, { + -- 012 + ast = { + { + 'And:0:1:&&', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + }, + }, + }, + err = { + arg = '', + msg = 'E15: Expected value, got EOC: %.*s', + }, + }, { + hl('Identifier', 'a'), + hl('And', '&&'), + }) + + check_parsing('(&&)', 0, { + -- 0123 + ast = { + { + 'Nested:0:0:(', + children = { + { + 'And:0:1:&&', + children = { + 'Missing:0:1:', + 'Missing:0:3:', + }, + }, + }, + }, + }, + err = { + arg = '&&)', + msg = 'E15: Unexpected and operator: %.*s', + }, + }, { + hl('NestingParenthesis', '('), + hl('InvalidAnd', '&&'), + hl('InvalidNestingParenthesis', ')'), + }) + + check_parsing('(||)', 0, { + -- 0123 + ast = { + { + 'Nested:0:0:(', + children = { + { + 'Or:0:1:||', + children = { + 'Missing:0:1:', + 'Missing:0:3:', + }, + }, + }, + }, + }, + err = { + arg = '||)', + msg = 'E15: Unexpected or operator: %.*s', + }, + }, { + hl('NestingParenthesis', '('), + hl('InvalidOr', '||'), + hl('InvalidNestingParenthesis', ')'), + }) + + check_parsing('(a||)', 0, { + -- 01234 + ast = { + { + 'Nested:0:0:(', + children = { + { + 'Or:0:2:||', + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + 'Missing:0:4:', + }, + }, + }, + }, + }, + err = { + arg = ')', + msg = 'E15: Expected value, got parenthesis: %.*s', + }, + }, { + hl('NestingParenthesis', '('), + hl('Identifier', 'a'), + hl('Or', '||'), + hl('InvalidNestingParenthesis', ')'), + }) + + check_parsing('(a&&)', 0, { + -- 01234 + ast = { + { + 'Nested:0:0:(', + children = { + { + 'And:0:2:&&', + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + 'Missing:0:4:', + }, + }, + }, + }, + }, + err = { + arg = ')', + msg = 'E15: Expected value, got parenthesis: %.*s', + }, + }, { + hl('NestingParenthesis', '('), + hl('Identifier', 'a'), + hl('And', '&&'), + hl('InvalidNestingParenthesis', ')'), + }) + + check_parsing('(&&a)', 0, { + -- 01234 + ast = { + { + 'Nested:0:0:(', + children = { + { + 'And:0:1:&&', + children = { + 'Missing:0:1:', + 'PlainIdentifier(scope=0,ident=a):0:3:a', + }, + }, + }, + }, + }, + err = { + arg = '&&a)', + msg = 'E15: Unexpected and operator: %.*s', + }, + }, { + hl('NestingParenthesis', '('), + hl('InvalidAnd', '&&'), + hl('Identifier', 'a'), + hl('NestingParenthesis', ')'), + }) + + check_parsing('(||a)', 0, { + -- 01234 + ast = { + { + 'Nested:0:0:(', + children = { + { + 'Or:0:1:||', + children = { + 'Missing:0:1:', + 'PlainIdentifier(scope=0,ident=a):0:3:a', + }, + }, + }, + }, + }, + err = { + arg = '||a)', + msg = 'E15: Unexpected or operator: %.*s', + }, + }, { + hl('NestingParenthesis', '('), + hl('InvalidOr', '||'), + hl('Identifier', 'a'), + hl('NestingParenthesis', ')'), + }) + end) + itp('works with &opt', function() + check_parsing('&', 0, { + -- 0 + ast = { + 'Option(scope=0,ident=):0:0:&', + }, + err = { + arg = '&', + msg = 'E112: Option name missing: %.*s', + }, + }, { + hl('InvalidOptionSigil', '&'), + }) + + check_parsing('&opt', 0, { + -- 0123 + ast = { + 'Option(scope=0,ident=opt):0:0:&opt', + }, + }, { + hl('OptionSigil', '&'), + hl('Option', 'opt'), + }) + + check_parsing('&l:opt', 0, { + -- 012345 + ast = { + 'Option(scope=l,ident=opt):0:0:&l:opt', + }, + }, { + hl('OptionSigil', '&'), + hl('OptionScope', 'l'), + hl('OptionScopeDelimiter', ':'), + hl('Option', 'opt'), + }) + + check_parsing('&g:opt', 0, { + -- 012345 + ast = { + 'Option(scope=g,ident=opt):0:0:&g:opt', + }, + }, { + hl('OptionSigil', '&'), + hl('OptionScope', 'g'), + hl('OptionScopeDelimiter', ':'), + hl('Option', 'opt'), + }) + + check_parsing('&s:opt', 0, { + -- 012345 + ast = { + { + 'Colon:0:2::', + children = { + 'Option(scope=0,ident=s):0:0:&s', + 'PlainIdentifier(scope=0,ident=opt):0:3:opt', + }, + }, + }, + err = { + arg = ':opt', + msg = 'E15: Colon outside of dictionary or ternary operator: %.*s', + }, + }, { + hl('OptionSigil', '&'), + hl('Option', 's'), + hl('InvalidColon', ':'), + hl('Identifier', 'opt'), + }) + + check_parsing('& ', 0, { + -- 01 + ast = { + 'Option(scope=0,ident=):0:0:&', + }, + err = { + arg = '& ', + msg = 'E112: Option name missing: %.*s', + }, + }, { + hl('InvalidOptionSigil', '&'), + }) + + check_parsing('&-', 0, { + -- 01 + ast = { + { + 'BinaryMinus:0:1:-', + children = { + 'Option(scope=0,ident=):0:0:&', + }, + }, + }, + err = { + arg = '&-', + msg = 'E112: Option name missing: %.*s', + }, + }, { + hl('InvalidOptionSigil', '&'), + hl('BinaryMinus', '-'), + }) + + check_parsing('&A', 0, { + -- 01 + ast = { + 'Option(scope=0,ident=A):0:0:&A', + }, + }, { + hl('OptionSigil', '&'), + hl('Option', 'A'), + }) + + check_parsing('&xxx_yyy', 0, { + -- 01234567 + ast = { + { + 'OpMissing:0:4:', + children = { + 'Option(scope=0,ident=xxx):0:0:&xxx', + 'PlainIdentifier(scope=0,ident=_yyy):0:4:_yyy', + }, + }, + }, + err = { + arg = '_yyy', + msg = 'E15: Missing operator: %.*s', + }, + }, { + hl('OptionSigil', '&'), + hl('Option', 'xxx'), + hl('InvalidIdentifier', '_yyy'), + }) + + check_parsing('(1+&)', 0, { + -- 01234 + ast = { + { + 'Nested:0:0:(', + children = { + { + 'BinaryPlus:0:2:+', + children = { + 'Integer(val=1):0:1:1', + 'Option(scope=0,ident=):0:3:&', + }, + }, + }, + }, + }, + err = { + arg = '&)', + msg = 'E112: Option name missing: %.*s', + }, + }, { + hl('NestingParenthesis', '('), + hl('Number', '1'), + hl('BinaryPlus', '+'), + hl('InvalidOptionSigil', '&'), + hl('NestingParenthesis', ')'), + }) + + check_parsing('(&+1)', 0, { + -- 01234 + ast = { + { + 'Nested:0:0:(', + children = { + { + 'BinaryPlus:0:2:+', + children = { + 'Option(scope=0,ident=):0:1:&', + 'Integer(val=1):0:3:1', + }, + }, + }, + }, + }, + err = { + arg = '&+1)', + msg = 'E112: Option name missing: %.*s', + }, + }, { + hl('NestingParenthesis', '('), + hl('InvalidOptionSigil', '&'), + hl('BinaryPlus', '+'), + hl('Number', '1'), + hl('NestingParenthesis', ')'), + }) + end) + itp('works with $ENV', function() + check_parsing('$', 0, { + -- 0 + ast = { + 'Environment(ident=):0:0:$', + }, + err = { + arg = '$', + msg = 'E15: Environment variable name missing', + }, + }, { + hl('InvalidEnvironmentSigil', '$'), + }) + + check_parsing('$g:A', 0, { + -- 0123 + ast = { + { + 'Colon:0:2::', + children = { + 'Environment(ident=g):0:0:$g', + 'PlainIdentifier(scope=0,ident=A):0:3:A', + }, + }, + }, + err = { + arg = ':A', + msg = 'E15: Colon outside of dictionary or ternary operator: %.*s', + }, + }, { + hl('EnvironmentSigil', '$'), + hl('Environment', 'g'), + hl('InvalidColon', ':'), + hl('Identifier', 'A'), + }) + + check_parsing('$A', 0, { + -- 01 + ast = { + 'Environment(ident=A):0:0:$A', + }, + }, { + hl('EnvironmentSigil', '$'), + hl('Environment', 'A'), + }) + + check_parsing('$ABC', 0, { + -- 0123 + ast = { + 'Environment(ident=ABC):0:0:$ABC', + }, + }, { + hl('EnvironmentSigil', '$'), + hl('Environment', 'ABC'), + }) + + check_parsing('(1+$)', 0, { + -- 01234 + ast = { + { + 'Nested:0:0:(', + children = { + { + 'BinaryPlus:0:2:+', + children = { + 'Integer(val=1):0:1:1', + 'Environment(ident=):0:3:$', + }, + }, + }, + }, + }, + err = { + arg = '$)', + msg = 'E15: Environment variable name missing', + }, + }, { + hl('NestingParenthesis', '('), + hl('Number', '1'), + hl('BinaryPlus', '+'), + hl('InvalidEnvironmentSigil', '$'), + hl('NestingParenthesis', ')'), + }) + + check_parsing('($+1)', 0, { + -- 01234 + ast = { + { + 'Nested:0:0:(', + children = { + { + 'BinaryPlus:0:2:+', + children = { + 'Environment(ident=):0:1:$', + 'Integer(val=1):0:3:1', + }, + }, + }, + }, + }, + err = { + arg = '$+1)', + msg = 'E15: Environment variable name missing', + }, + }, { + hl('NestingParenthesis', '('), + hl('InvalidEnvironmentSigil', '$'), + hl('BinaryPlus', '+'), + hl('Number', '1'), + hl('NestingParenthesis', ')'), + }) + + check_parsing('$_ABC', 0, { + -- 01234 + ast = { + 'Environment(ident=_ABC):0:0:$_ABC', + }, + }, { + hl('EnvironmentSigil', '$'), + hl('Environment', '_ABC'), + }) + + check_parsing('$_', 0, { + -- 01 + ast = { + 'Environment(ident=_):0:0:$_', + }, + }, { + hl('EnvironmentSigil', '$'), + hl('Environment', '_'), + }) + + check_parsing('$ABC_DEF', 0, { + -- 01234567 + ast = { + 'Environment(ident=ABC_DEF):0:0:$ABC_DEF', + }, + }, { + hl('EnvironmentSigil', '$'), + hl('Environment', 'ABC_DEF'), + }) + end) + itp('works with unary !', function() + check_parsing('!', 0, { + -- 0 + ast = { + 'Not:0:0:!', + }, + err = { + arg = '', + msg = 'E15: Expected value, got EOC: %.*s', + }, + }, { + hl('Not', '!'), + }) + + check_parsing('!!', 0, { + -- 01 + ast = { + { + 'Not:0:0:!', + children = { + 'Not:0:1:!', + }, + }, + }, + err = { + arg = '', + msg = 'E15: Expected value, got EOC: %.*s', + }, + }, { + hl('Not', '!'), + hl('Not', '!'), + }) + + check_parsing('!!1', 0, { + -- 012 + ast = { + { + 'Not:0:0:!', + children = { + { + 'Not:0:1:!', + children = { + 'Integer(val=1):0:2:1', + }, + }, + }, + }, + }, + }, { + hl('Not', '!'), + hl('Not', '!'), + hl('Number', '1'), + }) + + check_parsing('!1', 0, { + -- 01 + ast = { + { + 'Not:0:0:!', + children = { + 'Integer(val=1):0:1:1', + }, + }, + }, + }, { + hl('Not', '!'), + hl('Number', '1'), + }) + + check_parsing('(!1)', 0, { + -- 0123 + ast = { + { + 'Nested:0:0:(', + children = { + { + 'Not:0:1:!', + children = { + 'Integer(val=1):0:2:1', + }, + }, + }, + }, + }, + }, { + hl('NestingParenthesis', '('), + hl('Not', '!'), + hl('Number', '1'), + hl('NestingParenthesis', ')'), + }) + + check_parsing('(!)', 0, { + -- 012 + ast = { + { + 'Nested:0:0:(', + children = { + { + 'Not:0:1:!', + children = { + 'Missing:0:2:', + }, + }, + }, + }, + }, + err = { + arg = ')', + msg = 'E15: Expected value, got parenthesis: %.*s', + }, + }, { + hl('NestingParenthesis', '('), + hl('Not', '!'), + hl('InvalidNestingParenthesis', ')'), + }) + + check_parsing('(1!2)', 0, { + -- 01234 + ast = { + { + 'Nested:0:0:(', + children = { + { + 'OpMissing:0:2:', + children = { + 'Integer(val=1):0:1:1', + { + 'Not:0:2:!', + children = { + 'Integer(val=2):0:3:2', + }, + }, + }, + }, + }, + }, + }, + err = { + arg = '!2)', + msg = 'E15: Missing operator: %.*s', + }, + }, { + hl('NestingParenthesis', '('), + hl('Number', '1'), + hl('InvalidNot', '!'), + hl('Number', '2'), + hl('NestingParenthesis', ')'), + }) + + check_parsing('1!2', 0, { + -- 012 + ast = { + { + 'OpMissing:0:1:', + children = { + 'Integer(val=1):0:0:1', + { + 'Not:0:1:!', + children = { + 'Integer(val=2):0:2:2', + }, + }, + }, + }, + }, + err = { + arg = '!2', + msg = 'E15: Missing operator: %.*s', + }, + }, { + hl('Number', '1'), + hl('InvalidNot', '!'), + hl('Number', '2'), + }) + end) end) -- cgit From 206f7ae76a4a06c6bac10402649e515dd3fb2365 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 15 Oct 2017 19:18:17 +0300 Subject: unittests: Test some edge cases --- test/unit/viml/expressions/parser_spec.lua | 44 ++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) (limited to 'test') diff --git a/test/unit/viml/expressions/parser_spec.lua b/test/unit/viml/expressions/parser_spec.lua index d95eaca79b..407114ff33 100644 --- a/test/unit/viml/expressions/parser_spec.lua +++ b/test/unit/viml/expressions/parser_spec.lua @@ -4241,6 +4241,40 @@ describe('Expressions parser', function() hl('UnaryPlus', '+', 1), hl('Identifier', 'b'), }) + + check_parsing('a. b', 0, { + -- 0123 + ast = { + { + 'Concat:0:1:.', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + 'PlainIdentifier(scope=0,ident=b):0:2: b', + }, + }, + }, + }, { + hl('Identifier', 'a'), + hl('ConcatOrSubscript', '.'), + hl('Identifier', 'b', 1), + }) + + check_parsing('a. 1', 0, { + -- 0123 + ast = { + { + 'Concat:0:1:.', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + 'Integer(val=1):0:2: 1', + }, + }, + }, + }, { + hl('Identifier', 'a'), + hl('ConcatOrSubscript', '.'), + hl('Number', '1', 1), + }) end) itp('works with bracket subscripts', function() check_parsing(':', 0, { @@ -6823,4 +6857,14 @@ describe('Expressions parser', function() hl('Number', '2'), }) end) + itp('works (KLEE tests)', function() + check_parsing('\0002&A:\000', 0, { + ast = nil, + err = { + arg = '', + msg = 'E15: Expected value, got EOC: %.*s', + }, + }, { + }) + end) end) -- cgit From 6c19cbef2611c389da6f3e06d8c8eb635d065774 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 15 Oct 2017 20:05:35 +0300 Subject: viml/parser/expressions,tests: Add AST freeing, with sanity checks --- test/helpers.lua | 2 ++ test/symbolic/klee/viml_expressions_parser.c | 4 ++-- test/unit/viml/expressions/parser_spec.lua | 1 + 3 files changed, 5 insertions(+), 2 deletions(-) (limited to 'test') diff --git a/test/helpers.lua b/test/helpers.lua index 6a42963d7f..83e78ba059 100644 --- a/test/helpers.lua +++ b/test/helpers.lua @@ -358,6 +358,8 @@ format_luav = function(v, indent) else ret = ('%e'):format(v) end + elseif type(v) == 'nil' then + ret = 'nil' else print(type(v)) -- Not implemented yet diff --git a/test/symbolic/klee/viml_expressions_parser.c b/test/symbolic/klee/viml_expressions_parser.c index a4fbdb6bf4..c0cedceb21 100644 --- a/test/symbolic/klee/viml_expressions_parser.c +++ b/test/symbolic/klee/viml_expressions_parser.c @@ -92,6 +92,6 @@ int main(const int argc, const char *const *const argv, assert(ast.root != NULL || plines[0].size == 0); assert(ast.root != NULL || ast.err.msg); - // FIXME: check for AST recursiveness - // FIXME: free memory and assert no memory leaks + viml_pexpr_free_ast(ast); + assert(allocated_memory == 0); } diff --git a/test/unit/viml/expressions/parser_spec.lua b/test/unit/viml/expressions/parser_spec.lua index 407114ff33..9624ced022 100644 --- a/test/unit/viml/expressions/parser_spec.lua +++ b/test/unit/viml/expressions/parser_spec.lua @@ -253,6 +253,7 @@ describe('Expressions parser', function() end eq(exp_highlighting, hls) end + lib.viml_pexpr_free_ast(east) end local function hl(group, str, shift) return function(next_col) -- cgit From 3aa2c0d63ae488e302a89fdcdd650404cb2670fd Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 15 Oct 2017 21:11:00 +0300 Subject: viml/parser/expressions,klee: Fix some problems found by KLEE run --- test/symbolic/klee/nvim/charset.c | 20 ++++++++++---------- test/symbolic/klee/run.sh | 2 +- test/symbolic/klee/viml_expressions_parser.c | 2 -- test/unit/viml/expressions/parser_spec.lua | 8 ++++++++ 4 files changed, 19 insertions(+), 13 deletions(-) (limited to 'test') diff --git a/test/symbolic/klee/nvim/charset.c b/test/symbolic/klee/nvim/charset.c index 409d7d443c..59fe6a1430 100644 --- a/test/symbolic/klee/nvim/charset.c +++ b/test/symbolic/klee/nvim/charset.c @@ -38,20 +38,21 @@ void vim_str2nr(const char_u *const start, int *const prep, int *const len, } // Recognize hex, octal and bin. - if ((ptr[0] == '0') && (ptr[1] != '8') && (ptr[1] != '9') - && (maxlen == 0 || maxlen > 1)) { + if ((what & (STR2NR_HEX|STR2NR_OCT|STR2NR_BIN)) + && (maxlen == 0 || maxlen > 1) + && (ptr[0] == '0') && (ptr[1] != '8') && (ptr[1] != '9')) { pre = ptr[1]; if ((what & STR2NR_HEX) + && (maxlen == 0 || maxlen > 2) && ((pre == 'X') || (pre == 'x')) - && ascii_isxdigit(ptr[2]) - && (maxlen == 0 || maxlen > 2)) { + && ascii_isxdigit(ptr[2])) { // hexadecimal ptr += 2; } else if ((what & STR2NR_BIN) + && (maxlen == 0 || maxlen > 2) && ((pre == 'B') || (pre == 'b')) - && ascii_isbdigit(ptr[2]) - && (maxlen == 0 || maxlen > 2)) { + && ascii_isbdigit(ptr[2])) { // binary ptr += 2; } else { @@ -80,7 +81,7 @@ void vim_str2nr(const char_u *const start, int *const prep, int *const len, // Do the string-to-numeric conversion "manually" to avoid sscanf quirks. int n = 1; - if ((pre == 'B') || (pre == 'b') || what == STR2NR_BIN + STR2NR_FORCE) { + if (pre == 'B' || pre == 'b' || what == (STR2NR_BIN|STR2NR_FORCE)) { // bin if (pre != 0) { n += 2; // skip over "0b" @@ -97,7 +98,7 @@ void vim_str2nr(const char_u *const start, int *const prep, int *const len, break; } } - } else if ((pre == '0') || what == STR2NR_OCT + STR2NR_FORCE) { + } else if (pre == '0' || what == (STR2NR_OCT|STR2NR_FORCE)) { // octal while ('0' <= *ptr && *ptr <= '7') { // avoid ubsan error for overflow @@ -111,8 +112,7 @@ void vim_str2nr(const char_u *const start, int *const prep, int *const len, break; } } - } else if ((pre == 'X') || (pre == 'x') - || what == STR2NR_HEX + STR2NR_FORCE) { + } else if (pre == 'X' || pre == 'x' || what == (STR2NR_HEX|STR2NR_FORCE)) { // hex if (pre != 0) { n += 2; // skip over "0x" diff --git a/test/symbolic/klee/run.sh b/test/symbolic/klee/run.sh index 388903c234..c143cd0624 100755 --- a/test/symbolic/klee/run.sh +++ b/test/symbolic/klee/run.sh @@ -46,7 +46,7 @@ main() { line1="$line1 '--output-dir=$KLEE_OUT_DIR' '$KLEE_BIN_DIR/a.bc'" local line2="for t in '$KLEE_OUT_DIR'/*.err" line2="$line2 ; do ktest-tool --write-ints" - line2="$line2 \"\$(printf '%s' \"\$t\" | sed -e 's@.[^/]*\$@.out@')\"" + line2="$line2 \"\$(printf '%s' \"\$t\" | sed -e 's@\\.[^/]*\$@.ktest@')\"" line2="$line2 ; done" printf '%s\n%s\n' "$line1" "$line2" | \ docker run \ diff --git a/test/symbolic/klee/viml_expressions_parser.c b/test/symbolic/klee/viml_expressions_parser.c index c0cedceb21..ed280adb22 100644 --- a/test/symbolic/klee/viml_expressions_parser.c +++ b/test/symbolic/klee/viml_expressions_parser.c @@ -89,8 +89,6 @@ int main(const int argc, const char *const *const argv, kvi_init(pstate.reader.lines); const ExprAST ast = viml_pexpr_parse(&pstate, flags); - assert(ast.root != NULL - || plines[0].size == 0); assert(ast.root != NULL || ast.err.msg); viml_pexpr_free_ast(ast); assert(allocated_memory == 0); diff --git a/test/unit/viml/expressions/parser_spec.lua b/test/unit/viml/expressions/parser_spec.lua index 9624ced022..22263af8e2 100644 --- a/test/unit/viml/expressions/parser_spec.lua +++ b/test/unit/viml/expressions/parser_spec.lua @@ -6867,5 +6867,13 @@ describe('Expressions parser', function() }, }, { }) + check_parsing('0', 0, { + -- 0 + ast = { + 'Integer(val=0):0:0:0', + }, + }, { + hl('Number', '0'), + }) end) end) -- cgit From 1a3635304b80b48625bcd9d48f4f38778b42e4af Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 16 Oct 2017 00:07:32 +0300 Subject: charset: Avoid overflow in vim_str2nr --- test/symbolic/klee/nvim/charset.c | 60 +++++++++++------------------- test/unit/viml/expressions/lexer_spec.lua | 3 ++ test/unit/viml/expressions/parser_spec.lua | 10 ++++- 3 files changed, 32 insertions(+), 41 deletions(-) (limited to 'test') diff --git a/test/symbolic/klee/nvim/charset.c b/test/symbolic/klee/nvim/charset.c index 59fe6a1430..7dcf6868bf 100644 --- a/test/symbolic/klee/nvim/charset.c +++ b/test/symbolic/klee/nvim/charset.c @@ -25,12 +25,15 @@ int hex2nr(int c) void vim_str2nr(const char_u *const start, int *const prep, int *const len, const int what, varnumber_T *const nptr, - uvarnumber_T *const unptr, const int maxlen) + uvarnumber_T *const unptr, int maxlen) { - const char_u *ptr = start; + const char *ptr = (const char *)start; + if (maxlen == 0) { + maxlen = (int)strlen(ptr); + } + const char *const e = ptr + maxlen; int pre = 0; // default is decimal bool negative = false; - uvarnumber_T un = 0; if (ptr[0] == '-') { negative = true; @@ -39,19 +42,19 @@ void vim_str2nr(const char_u *const start, int *const prep, int *const len, // Recognize hex, octal and bin. if ((what & (STR2NR_HEX|STR2NR_OCT|STR2NR_BIN)) - && (maxlen == 0 || maxlen > 1) - && (ptr[0] == '0') && (ptr[1] != '8') && (ptr[1] != '9')) { + && maxlen > 1 + && ptr[0] == '0' && ptr[1] != '8' && ptr[1] != '9') { pre = ptr[1]; if ((what & STR2NR_HEX) - && (maxlen == 0 || maxlen > 2) - && ((pre == 'X') || (pre == 'x')) + && maxlen > 2 + && (pre == 'X' || pre == 'x') && ascii_isxdigit(ptr[2])) { // hexadecimal ptr += 2; } else if ((what & STR2NR_BIN) - && (maxlen == 0 || maxlen > 2) - && ((pre == 'B') || (pre == 'b')) + && maxlen > 2 + && (pre == 'B' || pre == 'b') && ascii_isbdigit(ptr[2])) { // binary ptr += 2; @@ -61,32 +64,26 @@ void vim_str2nr(const char_u *const start, int *const prep, int *const len, if (what & STR2NR_OCT) { // Don't interpret "0", "08" or "0129" as octal. - for (int n = 1; ascii_isdigit(ptr[n]); ++n) { - if (ptr[n] > '7') { + for (int i = 1; i < maxlen && ascii_isdigit(ptr[i]); i++) { + if (ptr[i] > '7') { // can't be octal pre = 0; break; } - if (ptr[n] >= '0') { + if (ptr[i] >= '0') { // assume octal pre = '0'; } - if (n == maxlen) { - break; - } } } } } // Do the string-to-numeric conversion "manually" to avoid sscanf quirks. - int n = 1; + uvarnumber_T un = 0; if (pre == 'B' || pre == 'b' || what == (STR2NR_BIN|STR2NR_FORCE)) { // bin - if (pre != 0) { - n += 2; // skip over "0b" - } - while ('0' <= *ptr && *ptr <= '1') { + while (ptr < e && '0' <= *ptr && *ptr <= '1') { // avoid ubsan error for overflow if (un < UVARNUMBER_MAX / 2) { un = 2 * un + (uvarnumber_T)(*ptr - '0'); @@ -94,13 +91,10 @@ void vim_str2nr(const char_u *const start, int *const prep, int *const len, un = UVARNUMBER_MAX; } ptr++; - if (n++ == maxlen) { - break; - } } } else if (pre == '0' || what == (STR2NR_OCT|STR2NR_FORCE)) { // octal - while ('0' <= *ptr && *ptr <= '7') { + while (ptr < e && '0' <= *ptr && *ptr <= '7') { // avoid ubsan error for overflow if (un < UVARNUMBER_MAX / 8) { un = 8 * un + (uvarnumber_T)(*ptr - '0'); @@ -108,16 +102,10 @@ void vim_str2nr(const char_u *const start, int *const prep, int *const len, un = UVARNUMBER_MAX; } ptr++; - if (n++ == maxlen) { - break; - } } } else if (pre == 'X' || pre == 'x' || what == (STR2NR_HEX|STR2NR_FORCE)) { // hex - if (pre != 0) { - n += 2; // skip over "0x" - } - while (ascii_isxdigit(*ptr)) { + while (ptr < e && ascii_isxdigit(*ptr)) { // avoid ubsan error for overflow if (un < UVARNUMBER_MAX / 16) { un = 16 * un + (uvarnumber_T)hex2nr(*ptr); @@ -125,13 +113,10 @@ void vim_str2nr(const char_u *const start, int *const prep, int *const len, un = UVARNUMBER_MAX; } ptr++; - if (n++ == maxlen) { - break; - } } } else { // decimal - while (ascii_isdigit(*ptr)) { + while (ptr < e && ascii_isdigit(*ptr)) { // avoid ubsan error for overflow if (un < UVARNUMBER_MAX / 10) { un = 10 * un + (uvarnumber_T)(*ptr - '0'); @@ -139,9 +124,6 @@ void vim_str2nr(const char_u *const start, int *const prep, int *const len, un = UVARNUMBER_MAX; } ptr++; - if (n++ == maxlen) { - break; - } } } @@ -150,7 +132,7 @@ void vim_str2nr(const char_u *const start, int *const prep, int *const len, } if (len != NULL) { - *len = (int)(ptr - start); + *len = (int)(ptr - (const char *)start); } if (nptr != NULL) { diff --git a/test/unit/viml/expressions/lexer_spec.lua b/test/unit/viml/expressions/lexer_spec.lua index 674b1b37db..5910468017 100644 --- a/test/unit/viml/expressions/lexer_spec.lua +++ b/test/unit/viml/expressions/lexer_spec.lua @@ -292,6 +292,9 @@ describe('Expressions lexer', function() simple_test({'0b102'}, 'Number', 4, {data={is_float=false, base=2, val=2}, str='0b10'}) simple_test({'10F'}, 'Number', 2, {data={is_float=false, base=10, val=10}, str='10'}) simple_test({'0x0123456789ABCDEFG'}, 'Number', 18, {data={is_float=false, base=16, val=81985529216486895}, str='0x0123456789ABCDEF'}) + simple_test({{data='00', size=2}}, 'Number', 2, {data={is_float=false, base=8, val=0}, str='00'}) + simple_test({{data='009', size=2}}, 'Number', 2, {data={is_float=false, base=8, val=0}, str='00'}) + simple_test({{data='01', size=1}}, 'Number', 1, {data={is_float=false, base=10, val=0}, str='0'}) end local function regular_scope_tests() diff --git a/test/unit/viml/expressions/parser_spec.lua b/test/unit/viml/expressions/parser_spec.lua index 22263af8e2..36af875bd2 100644 --- a/test/unit/viml/expressions/parser_spec.lua +++ b/test/unit/viml/expressions/parser_spec.lua @@ -6867,13 +6867,19 @@ describe('Expressions parser', function() }, }, { }) - check_parsing('0', 0, { - -- 0 + check_parsing({data='01', size=1}, 0, { ast = { 'Integer(val=0):0:0:0', }, }, { hl('Number', '0'), }) + check_parsing({data='001', size=2}, 0, { + ast = { + 'Integer(val=0):0:0:00', + }, + }, { + hl('Number', '00'), + }) end) end) -- cgit From 5e92ee6565233c56e7a33b12ef6a61f05eae91aa Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 16 Oct 2017 00:19:02 +0300 Subject: charset: Do not call strlen() from vim_str2nr --- test/symbolic/klee/nvim/charset.c | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) (limited to 'test') diff --git a/test/symbolic/klee/nvim/charset.c b/test/symbolic/klee/nvim/charset.c index 7dcf6868bf..c584bd72ef 100644 --- a/test/symbolic/klee/nvim/charset.c +++ b/test/symbolic/klee/nvim/charset.c @@ -25,13 +25,11 @@ int hex2nr(int c) void vim_str2nr(const char_u *const start, int *const prep, int *const len, const int what, varnumber_T *const nptr, - uvarnumber_T *const unptr, int maxlen) + uvarnumber_T *const unptr, const int maxlen) { const char *ptr = (const char *)start; - if (maxlen == 0) { - maxlen = (int)strlen(ptr); - } - const char *const e = ptr + maxlen; +#define STRING_ENDED(ptr) \ + (!(maxlen == 0 || (int)((ptr) - (const char *)start) < maxlen)) int pre = 0; // default is decimal bool negative = false; @@ -42,18 +40,18 @@ void vim_str2nr(const char_u *const start, int *const prep, int *const len, // Recognize hex, octal and bin. if ((what & (STR2NR_HEX|STR2NR_OCT|STR2NR_BIN)) - && maxlen > 1 + && !STRING_ENDED(ptr + 1) && ptr[0] == '0' && ptr[1] != '8' && ptr[1] != '9') { pre = ptr[1]; if ((what & STR2NR_HEX) - && maxlen > 2 + && !STRING_ENDED(ptr + 2) && (pre == 'X' || pre == 'x') && ascii_isxdigit(ptr[2])) { // hexadecimal ptr += 2; } else if ((what & STR2NR_BIN) - && maxlen > 2 + && !STRING_ENDED(ptr + 2) && (pre == 'B' || pre == 'b') && ascii_isbdigit(ptr[2])) { // binary @@ -64,7 +62,7 @@ void vim_str2nr(const char_u *const start, int *const prep, int *const len, if (what & STR2NR_OCT) { // Don't interpret "0", "08" or "0129" as octal. - for (int i = 1; i < maxlen && ascii_isdigit(ptr[i]); i++) { + for (int i = 1; !STRING_ENDED(ptr + i) && ascii_isdigit(ptr[i]); i++) { if (ptr[i] > '7') { // can't be octal pre = 0; @@ -83,7 +81,7 @@ void vim_str2nr(const char_u *const start, int *const prep, int *const len, uvarnumber_T un = 0; if (pre == 'B' || pre == 'b' || what == (STR2NR_BIN|STR2NR_FORCE)) { // bin - while (ptr < e && '0' <= *ptr && *ptr <= '1') { + while (!STRING_ENDED(ptr) && '0' <= *ptr && *ptr <= '1') { // avoid ubsan error for overflow if (un < UVARNUMBER_MAX / 2) { un = 2 * un + (uvarnumber_T)(*ptr - '0'); @@ -94,7 +92,7 @@ void vim_str2nr(const char_u *const start, int *const prep, int *const len, } } else if (pre == '0' || what == (STR2NR_OCT|STR2NR_FORCE)) { // octal - while (ptr < e && '0' <= *ptr && *ptr <= '7') { + while (!STRING_ENDED(ptr) && '0' <= *ptr && *ptr <= '7') { // avoid ubsan error for overflow if (un < UVARNUMBER_MAX / 8) { un = 8 * un + (uvarnumber_T)(*ptr - '0'); @@ -105,7 +103,7 @@ void vim_str2nr(const char_u *const start, int *const prep, int *const len, } } else if (pre == 'X' || pre == 'x' || what == (STR2NR_HEX|STR2NR_FORCE)) { // hex - while (ptr < e && ascii_isxdigit(*ptr)) { + while (!STRING_ENDED(ptr) && ascii_isxdigit(*ptr)) { // avoid ubsan error for overflow if (un < UVARNUMBER_MAX / 16) { un = 16 * un + (uvarnumber_T)hex2nr(*ptr); @@ -116,7 +114,7 @@ void vim_str2nr(const char_u *const start, int *const prep, int *const len, } } else { // decimal - while (ptr < e && ascii_isdigit(*ptr)) { + while (!STRING_ENDED(ptr) && ascii_isdigit(*ptr)) { // avoid ubsan error for overflow if (un < UVARNUMBER_MAX / 10) { un = 10 * un + (uvarnumber_T)(*ptr - '0'); @@ -154,4 +152,5 @@ void vim_str2nr(const char_u *const start, int *const prep, int *const len, if (unptr != NULL) { *unptr = un; } +#undef STRING_ENDED } -- cgit From fe81380bf5d4d161187998088aa9cff948b7c891 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 16 Oct 2017 00:30:55 +0300 Subject: viml/parser/expressions: Highlight prefix separately from number Should make accidental octals more visible. --- test/unit/viml/expressions/parser_spec.lua | 113 ++++++++++++++++++++++++++++- 1 file changed, 112 insertions(+), 1 deletion(-) (limited to 'test') diff --git a/test/unit/viml/expressions/parser_spec.lua b/test/unit/viml/expressions/parser_spec.lua index 36af875bd2..54fd54aea8 100644 --- a/test/unit/viml/expressions/parser_spec.lua +++ b/test/unit/viml/expressions/parser_spec.lua @@ -6858,6 +6858,116 @@ describe('Expressions parser', function() hl('Number', '2'), }) end) + itp('highlights numbers with prefix', function() + check_parsing('0xABCDEF', 0, { + -- 01234567 + ast = { + 'Integer(val=11259375):0:0:0xABCDEF', + }, + }, { + hl('NumberPrefix', '0x'), + hl('Number', 'ABCDEF'), + }) + + check_parsing('0Xabcdef', 0, { + -- 01234567 + ast = { + 'Integer(val=11259375):0:0:0Xabcdef', + }, + }, { + hl('NumberPrefix', '0X'), + hl('Number', 'abcdef'), + }) + + check_parsing('0XABCDEF', 0, { + -- 01234567 + ast = { + 'Integer(val=11259375):0:0:0XABCDEF', + }, + }, { + hl('NumberPrefix', '0X'), + hl('Number', 'ABCDEF'), + }) + + check_parsing('0xabcdef', 0, { + -- 01234567 + ast = { + 'Integer(val=11259375):0:0:0xabcdef', + }, + }, { + hl('NumberPrefix', '0x'), + hl('Number', 'abcdef'), + }) + + check_parsing('0b001', 0, { + -- 01234 + ast = { + 'Integer(val=1):0:0:0b001', + }, + }, { + hl('NumberPrefix', '0b'), + hl('Number', '001'), + }) + + check_parsing('0B001', 0, { + -- 01234 + ast = { + 'Integer(val=1):0:0:0B001', + }, + }, { + hl('NumberPrefix', '0B'), + hl('Number', '001'), + }) + + check_parsing('0B00', 0, { + -- 0123 + ast = { + 'Integer(val=0):0:0:0B00', + }, + }, { + hl('NumberPrefix', '0B'), + hl('Number', '00'), + }) + + check_parsing('00', 0, { + -- 01 + ast = { + 'Integer(val=0):0:0:00', + }, + }, { + hl('NumberPrefix', '0'), + hl('Number', '0'), + }) + + check_parsing('001', 0, { + -- 012 + ast = { + 'Integer(val=1):0:0:001', + }, + }, { + hl('NumberPrefix', '0'), + hl('Number', '01'), + }) + + check_parsing('01', 0, { + -- 01 + ast = { + 'Integer(val=1):0:0:01', + }, + }, { + hl('NumberPrefix', '0'), + hl('Number', '1'), + }) + + check_parsing('1', 0, { + -- 0 + ast = { + 'Integer(val=1):0:0:1', + }, + }, { + hl('Number', '1'), + }) + end) itp('works (KLEE tests)', function() check_parsing('\0002&A:\000', 0, { ast = nil, @@ -6879,7 +6989,8 @@ describe('Expressions parser', function() 'Integer(val=0):0:0:00', }, }, { - hl('Number', '00'), + hl('NumberPrefix', '0'), + hl('Number', '0'), }) end) end) -- cgit From ed253b5fe6515840fe6dd9df83855a0316de8bad Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 16 Oct 2017 00:39:48 +0300 Subject: klee: Include colors in test --- test/symbolic/klee/viml_expressions_parser.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'test') diff --git a/test/symbolic/klee/viml_expressions_parser.c b/test/symbolic/klee/viml_expressions_parser.c index ed280adb22..8f015ae9a7 100644 --- a/test/symbolic/klee/viml_expressions_parser.c +++ b/test/symbolic/klee/viml_expressions_parser.c @@ -75,6 +75,9 @@ int main(const int argc, const char *const *const argv, #endif ParserLine *cur_pline = &plines[0]; + ParserHighlight colors; + kvi_init(colors); + ParserState pstate = { .reader = { .get_line = simple_get_line, @@ -83,13 +86,18 @@ int main(const int argc, const char *const *const argv, .conv.vc_type = CONV_NONE, }, .pos = { 0, 0 }, - .colors = NULL, + .colors = &colors, .can_continuate = false, }; kvi_init(pstate.reader.lines); const ExprAST ast = viml_pexpr_parse(&pstate, flags); assert(ast.root != NULL || ast.err.msg); + // Can’t possibly have more highlight tokens then there are bytes in string. + assert(kv_size(colors) <= INPUT_SIZE - shift); + kvi_destroy(colors); + // Not destroying pstate.reader.lines because there is no way it could exceed + // its limits in the current circumstances. viml_pexpr_free_ast(ast); assert(allocated_memory == 0); } -- cgit From 15043e93b681ffdf1142d24aa918997e2e63a4b1 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 16 Oct 2017 00:41:41 +0300 Subject: klee: Update key_name_entry table --- test/symbolic/klee/nvim/keymap.c | 292 +++++++++++++++++++-------------------- 1 file changed, 145 insertions(+), 147 deletions(-) (limited to 'test') diff --git a/test/symbolic/klee/nvim/keymap.c b/test/symbolic/klee/nvim/keymap.c index c59d8d4d6e..ff5e46e75b 100644 --- a/test/symbolic/klee/nvim/keymap.c +++ b/test/symbolic/klee/nvim/keymap.c @@ -141,154 +141,152 @@ int handle_x_keys(const int key) } static const struct key_name_entry { - int key; ///< Special key code or ASCII value. - const char *name; ///< Name of the key + int key; // Special key code or ascii value + const char *name; // Name of key } key_names_table[] = { - {' ', "Space"}, - {TAB, "Tab"}, - {K_TAB, "Tab"}, - {NL, "NL"}, - {NL, "NewLine"}, // Alternative name - {NL, "LineFeed"}, // Alternative name - {NL, "LF"}, // Alternative name - {CAR, "CR"}, - {CAR, "Return"}, // Alternative name - {CAR, "Enter"}, // Alternative name - {K_BS, "BS"}, - {K_BS, "BackSpace"}, // Alternative name - {ESC, "Esc"}, - {CSI, "CSI"}, - {K_CSI, "xCSI"}, - {'|', "Bar"}, - {'\\', "Bslash"}, - {K_DEL, "Del"}, - {K_DEL, "Delete"}, // Alternative name - {K_KDEL, "kDel"}, - {K_UP, "Up"}, - {K_DOWN, "Down"}, - {K_LEFT, "Left"}, - {K_RIGHT, "Right"}, - {K_XUP, "xUp"}, - {K_XDOWN, "xDown"}, - {K_XLEFT, "xLeft"}, - {K_XRIGHT, "xRight"}, - - {K_F1, "F1"}, - {K_F2, "F2"}, - {K_F3, "F3"}, - {K_F4, "F4"}, - {K_F5, "F5"}, - {K_F6, "F6"}, - {K_F7, "F7"}, - {K_F8, "F8"}, - {K_F9, "F9"}, - {K_F10, "F10"}, - - {K_F11, "F11"}, - {K_F12, "F12"}, - {K_F13, "F13"}, - {K_F14, "F14"}, - {K_F15, "F15"}, - {K_F16, "F16"}, - {K_F17, "F17"}, - {K_F18, "F18"}, - {K_F19, "F19"}, - {K_F20, "F20"}, - - {K_F21, "F21"}, - {K_F22, "F22"}, - {K_F23, "F23"}, - {K_F24, "F24"}, - {K_F25, "F25"}, - {K_F26, "F26"}, - {K_F27, "F27"}, - {K_F28, "F28"}, - {K_F29, "F29"}, - {K_F30, "F30"}, - - {K_F31, "F31"}, - {K_F32, "F32"}, - {K_F33, "F33"}, - {K_F34, "F34"}, - {K_F35, "F35"}, - {K_F36, "F36"}, - {K_F37, "F37"}, - - {K_XF1, "xF1"}, - {K_XF2, "xF2"}, - {K_XF3, "xF3"}, - {K_XF4, "xF4"}, - - {K_HELP, "Help"}, - {K_UNDO, "Undo"}, - {K_INS, "Insert"}, - {K_INS, "Ins"}, // Alternative name - {K_KINS, "kInsert"}, - {K_HOME, "Home"}, - {K_KHOME, "kHome"}, - {K_XHOME, "xHome"}, - {K_ZHOME, "zHome"}, - {K_END, "End"}, - {K_KEND, "kEnd"}, - {K_XEND, "xEnd"}, - {K_ZEND, "zEnd"}, - {K_PAGEUP, "PageUp"}, - {K_PAGEDOWN, "PageDown"}, - {K_KPAGEUP, "kPageUp"}, - {K_KPAGEDOWN, "kPageDown"}, - - {K_KPLUS, "kPlus"}, - {K_KMINUS, "kMinus"}, - {K_KDIVIDE, "kDivide"}, - {K_KMULTIPLY, "kMultiply"}, - {K_KENTER, "kEnter"}, - {K_KPOINT, "kPoint"}, - - {K_K0, "k0"}, - {K_K1, "k1"}, - {K_K2, "k2"}, - {K_K3, "k3"}, - {K_K4, "k4"}, - {K_K5, "k5"}, - {K_K6, "k6"}, - {K_K7, "k7"}, - {K_K8, "k8"}, - {K_K9, "k9"}, - - {'<', "lt"}, - - {K_MOUSE, "Mouse"}, - {K_LEFTMOUSE, "LeftMouse"}, - {K_LEFTMOUSE_NM, "LeftMouseNM"}, - {K_LEFTDRAG, "LeftDrag"}, - {K_LEFTRELEASE, "LeftRelease"}, - {K_LEFTRELEASE_NM, "LeftReleaseNM"}, - {K_MIDDLEMOUSE, "MiddleMouse"}, - {K_MIDDLEDRAG, "MiddleDrag"}, - {K_MIDDLERELEASE, "MiddleRelease"}, - {K_RIGHTMOUSE, "RightMouse"}, - {K_RIGHTDRAG, "RightDrag"}, - {K_RIGHTRELEASE, "RightRelease"}, - {K_MOUSEDOWN, "ScrollWheelUp"}, - {K_MOUSEUP, "ScrollWheelDown"}, - {K_MOUSELEFT, "ScrollWheelRight"}, - {K_MOUSERIGHT, "ScrollWheelLeft"}, - {K_MOUSEDOWN, "MouseDown"}, // OBSOLETE: Use ScrollWheelXXX instead - {K_MOUSEUP, "MouseUp"}, // Same - {K_X1MOUSE, "X1Mouse"}, - {K_X1DRAG, "X1Drag"}, - {K_X1RELEASE, "X1Release"}, - {K_X2MOUSE, "X2Mouse"}, - {K_X2DRAG, "X2Drag"}, - {K_X2RELEASE, "X2Release"}, - {K_DROP, "Drop"}, - {K_ZERO, "Nul"}, - {K_SNR, "SNR"}, - {K_PLUG, "Plug"}, - {K_PASTE, "Paste"}, - {K_FOCUSGAINED, "FocusGained"}, - {K_FOCUSLOST, "FocusLost"}, - {0, NULL} + { ' ', "Space" }, + { TAB, "Tab" }, + { K_TAB, "Tab" }, + { NL, "NL" }, + { NL, "NewLine" }, // Alternative name + { NL, "LineFeed" }, // Alternative name + { NL, "LF" }, // Alternative name + { CAR, "CR" }, + { CAR, "Return" }, // Alternative name + { CAR, "Enter" }, // Alternative name + { K_BS, "BS" }, + { K_BS, "BackSpace" }, // Alternative name + { ESC, "Esc" }, + { CSI, "CSI" }, + { K_CSI, "xCSI" }, + { '|', "Bar" }, + { '\\', "Bslash" }, + { K_DEL, "Del" }, + { K_DEL, "Delete" }, // Alternative name + { K_KDEL, "kDel" }, + { K_UP, "Up" }, + { K_DOWN, "Down" }, + { K_LEFT, "Left" }, + { K_RIGHT, "Right" }, + { K_XUP, "xUp" }, + { K_XDOWN, "xDown" }, + { K_XLEFT, "xLeft" }, + { K_XRIGHT, "xRight" }, + + { K_F1, "F1" }, + { K_F2, "F2" }, + { K_F3, "F3" }, + { K_F4, "F4" }, + { K_F5, "F5" }, + { K_F6, "F6" }, + { K_F7, "F7" }, + { K_F8, "F8" }, + { K_F9, "F9" }, + { K_F10, "F10" }, + + { K_F11, "F11" }, + { K_F12, "F12" }, + { K_F13, "F13" }, + { K_F14, "F14" }, + { K_F15, "F15" }, + { K_F16, "F16" }, + { K_F17, "F17" }, + { K_F18, "F18" }, + { K_F19, "F19" }, + { K_F20, "F20" }, + + { K_F21, "F21" }, + { K_F22, "F22" }, + { K_F23, "F23" }, + { K_F24, "F24" }, + { K_F25, "F25" }, + { K_F26, "F26" }, + { K_F27, "F27" }, + { K_F28, "F28" }, + { K_F29, "F29" }, + { K_F30, "F30" }, + + { K_F31, "F31" }, + { K_F32, "F32" }, + { K_F33, "F33" }, + { K_F34, "F34" }, + { K_F35, "F35" }, + { K_F36, "F36" }, + { K_F37, "F37" }, + + { K_XF1, "xF1" }, + { K_XF2, "xF2" }, + { K_XF3, "xF3" }, + { K_XF4, "xF4" }, + + { K_HELP, "Help" }, + { K_UNDO, "Undo" }, + { K_INS, "Insert" }, + { K_INS, "Ins" }, // Alternative name + { K_KINS, "kInsert" }, + { K_HOME, "Home" }, + { K_KHOME, "kHome" }, + { K_XHOME, "xHome" }, + { K_ZHOME, "zHome" }, + { K_END, "End" }, + { K_KEND, "kEnd" }, + { K_XEND, "xEnd" }, + { K_ZEND, "zEnd" }, + { K_PAGEUP, "PageUp" }, + { K_PAGEDOWN, "PageDown" }, + { K_KPAGEUP, "kPageUp" }, + { K_KPAGEDOWN, "kPageDown" }, + + { K_KPLUS, "kPlus" }, + { K_KMINUS, "kMinus" }, + { K_KDIVIDE, "kDivide" }, + { K_KMULTIPLY, "kMultiply" }, + { K_KENTER, "kEnter" }, + { K_KPOINT, "kPoint" }, + + { K_K0, "k0" }, + { K_K1, "k1" }, + { K_K2, "k2" }, + { K_K3, "k3" }, + { K_K4, "k4" }, + { K_K5, "k5" }, + { K_K6, "k6" }, + { K_K7, "k7" }, + { K_K8, "k8" }, + { K_K9, "k9" }, + + { '<', "lt" }, + + { K_MOUSE, "Mouse" }, + { K_LEFTMOUSE, "LeftMouse" }, + { K_LEFTMOUSE_NM, "LeftMouseNM" }, + { K_LEFTDRAG, "LeftDrag" }, + { K_LEFTRELEASE, "LeftRelease" }, + { K_LEFTRELEASE_NM, "LeftReleaseNM" }, + { K_MIDDLEMOUSE, "MiddleMouse" }, + { K_MIDDLEDRAG, "MiddleDrag" }, + { K_MIDDLERELEASE, "MiddleRelease" }, + { K_RIGHTMOUSE, "RightMouse" }, + { K_RIGHTDRAG, "RightDrag" }, + { K_RIGHTRELEASE, "RightRelease" }, + { K_MOUSEDOWN, "ScrollWheelUp" }, + { K_MOUSEUP, "ScrollWheelDown" }, + { K_MOUSELEFT, "ScrollWheelRight" }, + { K_MOUSERIGHT, "ScrollWheelLeft" }, + { K_MOUSEDOWN, "MouseDown" }, // OBSOLETE: Use + { K_MOUSEUP, "MouseUp" }, // ScrollWheelXXX instead + { K_X1MOUSE, "X1Mouse" }, + { K_X1DRAG, "X1Drag" }, + { K_X1RELEASE, "X1Release" }, + { K_X2MOUSE, "X2Mouse" }, + { K_X2DRAG, "X2Drag" }, + { K_X2RELEASE, "X2Release" }, + { K_DROP, "Drop" }, + { K_ZERO, "Nul" }, + { K_SNR, "SNR" }, + { K_PLUG, "Plug" }, + { K_PASTE, "Paste" }, + { 0, NULL } }; int get_special_key_code(const char_u *name) -- cgit From 248493f155e42186440c7d081b27ffe760b67b9e Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 16 Oct 2017 03:03:34 +0300 Subject: test/unit/formatc: Fix parsing of most recent viml_parser_highlight --- test/unit/formatc.lua | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'test') diff --git a/test/unit/formatc.lua b/test/unit/formatc.lua index e288081960..2fd37c599a 100644 --- a/test/unit/formatc.lua +++ b/test/unit/formatc.lua @@ -65,11 +65,12 @@ local tokens = P { "tokens"; identifier = Ct(C(R("az","AZ","__") * R("09","az","AZ","__")^0) * Cc"identifier"), -- Single character in a string - string_char = R("az","AZ","09") + S"$%^&*()_-+={[}]:;@~#<,>.!?/ \t" + (P"\\" * S[[ntvbrfa\?'"0x]]), + sstring_char = R("\001&","([","]\255") + (P"\\" * S[[ntvbrfa\?'"0x]]), + dstring_char = R("\001!","#[","]\255") + (P"\\" * S[[ntvbrfa\?'"0x]]), -- String literal - string = Ct(C(P"'" * (V"string_char" + P'"')^0 * P"'" + - P'"' * (V"string_char" + P"'")^0 * P'"') * Cc"string"), + string = Ct(C(P"'" * (V"sstring_char" + P'"')^0 * P"'" + + P'"' * (V"dstring_char" + P"'")^0 * P'"') * Cc"string"), -- Operator operator = Ct(C(P">>=" + P"<<=" + P"..." + -- cgit From c03dc13bb74205d15a83ce3bd6ecb6b76b869878 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 16 Oct 2017 03:05:27 +0300 Subject: klee: Fix possible assertion error No idea how it did not happen to hit me yet. --- test/symbolic/klee/nvim/memory.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'test') diff --git a/test/symbolic/klee/nvim/memory.c b/test/symbolic/klee/nvim/memory.c index 1f9cdce6c0..df422cea3e 100644 --- a/test/symbolic/klee/nvim/memory.c +++ b/test/symbolic/klee/nvim/memory.c @@ -35,6 +35,9 @@ void *xmalloc(const size_t size) void xfree(void *const p) { + if (p == NULL) { + return; + } RINGBUF_FORALL(&arecs, AllocRecord, arec) { if (arec->ptr == p) { allocated_memory -= arec->size; -- cgit From 252a76db80dd846f9ccac4d7001697c12c009826 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 16 Oct 2017 03:06:34 +0300 Subject: unittests: Free everything and check for memory leaks Also improves error reporting. --- test/unit/helpers.lua | 11 +++++- test/unit/viml/expressions/parser_spec.lua | 56 +++++++++++++++++++----------- 2 files changed, 45 insertions(+), 22 deletions(-) (limited to 'test') diff --git a/test/unit/helpers.lua b/test/unit/helpers.lua index d3d14a5ca2..68ce9eed62 100644 --- a/test/unit/helpers.lua +++ b/test/unit/helpers.lua @@ -315,7 +315,7 @@ local function alloc_log_new() eq(exp, self.log) self:clear() end - function log:clear_tmp_allocs() + function log:clear_tmp_allocs(clear_null_frees) local toremove = {} local allocs = {} for i, v in ipairs(self.log) do @@ -327,6 +327,8 @@ local function alloc_log_new() if v.func == 'free' then toremove[#toremove + 1] = i end + elseif clear_null_frees and v.args[1] == self.null then + toremove[#toremove + 1] = i end if v.func == 'realloc' then allocs[tostring(v.ret)] = i @@ -779,6 +781,12 @@ local function kvi_init(kvi) return kvi end +local function kvi_destroy(kvi) + if kvi.items ~= kvi.init_array then + lib.xfree(kvi.items) + end +end + local function kvi_new(ct) return kvi_init(ffi.new(ct)) end @@ -830,6 +838,7 @@ local module = { sc = sc, conv_enum = conv_enum, array_size = array_sive, + kvi_destroy = kvi_destroy, kvi_size = kvi_size, kvi_init = kvi_init, kvi_new = kvi_new, diff --git a/test/unit/viml/expressions/parser_spec.lua b/test/unit/viml/expressions/parser_spec.lua index 54fd54aea8..5f5924c630 100644 --- a/test/unit/viml/expressions/parser_spec.lua +++ b/test/unit/viml/expressions/parser_spec.lua @@ -5,6 +5,8 @@ local viml_helpers = require('test.unit.viml.helpers') local make_enum_conv_tab = helpers.make_enum_conv_tab local child_call_once = helpers.child_call_once +local alloc_log_new = helpers.alloc_log_new +local kvi_destroy = helpers.kvi_destroy local conv_enum = helpers.conv_enum local ptr2key = helpers.ptr2key local cimport = helpers.cimport @@ -23,6 +25,8 @@ local format_luav = global_helpers.format_luav local lib = cimport('./src/nvim/viml/parser/expressions.h') +local alloc_log = alloc_log_new() + local function format_check(expr, flags, ast, hls) -- That forces specific order. print( format_string('\ncheck_parsing(%r, %u, {', expr, flags)) @@ -230,30 +234,40 @@ end) describe('Expressions parser', function() local function check_parsing(str, flags, exp_ast, exp_highlighting_fs) - flags = flags or 0 - - if os.getenv('NVIM_TEST_PARSER_SPEC_PRINT_TEST_CASE') == '1' then - print(str, flags) - end + local err, msg = pcall(function() + flags = flags or 0 - local pstate = new_pstate({str}) - local east = lib.viml_pexpr_parse(pstate, flags) - local ast = east2lua(pstate, east) - local hls = phl2lua(pstate) - if exp_ast == nil then - format_check(str, flags, ast, hls) - return - end - eq(exp_ast, ast) - if exp_highlighting_fs then - local exp_highlighting = {} - local next_col = 0 - for i, h in ipairs(exp_highlighting_fs) do - exp_highlighting[i], next_col = h(next_col) + if os.getenv('NVIM_TEST_PARSER_SPEC_PRINT_TEST_CASE') == '1' then + print(str, flags) + end + alloc_log:check({}) + + local pstate = new_pstate({str}) + local east = lib.viml_pexpr_parse(pstate, flags) + local ast = east2lua(pstate, east) + local hls = phl2lua(pstate) + if exp_ast == nil then + format_check(str, flags, ast, hls) + return + end + eq(exp_ast, ast) + if exp_highlighting_fs then + local exp_highlighting = {} + local next_col = 0 + for i, h in ipairs(exp_highlighting_fs) do + exp_highlighting[i], next_col = h(next_col) + end + eq(exp_highlighting, hls) end - eq(exp_highlighting, hls) + lib.viml_pexpr_free_ast(east) + kvi_destroy(pstate.colors) + alloc_log:clear_tmp_allocs(true) + alloc_log:check({}) + end) + if not err then + msg = format_string('Error while processing test (%r, %u):\n%s', str, flags, msg) + error(msg) end - lib.viml_pexpr_free_ast(east) end local function hl(group, str, shift) return function(next_col) -- cgit From 8e856ebcd0357c8782d6825a233a5c800475f396 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 16 Oct 2017 08:51:26 +0300 Subject: klee: Add run.sh --help and run.sh -s --- test/symbolic/klee/run.sh | 51 ++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 42 insertions(+), 9 deletions(-) (limited to 'test') diff --git a/test/symbolic/klee/run.sh b/test/symbolic/klee/run.sh index c143cd0624..0234a935b5 100755 --- a/test/symbolic/klee/run.sh +++ b/test/symbolic/klee/run.sh @@ -10,13 +10,43 @@ KLEE_TEST_DIR="$PROJECT_SOURCE_DIR/test/symbolic/klee" KLEE_BIN_DIR="$PROJECT_BINARY_DIR/klee" KLEE_OUT_DIR="$KLEE_BIN_DIR/out" +help() { + echo "Usage:" + echo + echo " $0 -c fname" + echo " $0 fname" + echo " $0 -s" + echo + echo "First form compiles executable out of test/symbolic/klee/{fname}.c." + echo "Compiled executable is placed into build/klee/{fname}. Must first" + echo "successfully compile Neovim in order to generate declarations." + echo + echo "Second form runs KLEE in a docker container using file " + echo "test/symbolic/klee/{fname.c}. Bitcode is placed into build/klee/a.bc," + echo "results are placed into build/klee/out/. The latter is first deleted if" + echo "it exists." + echo + echo "Third form runs ktest-tool which prints errors found by KLEE via " + echo "the same container used to run KLEE." +} + main() { local compile= - if test "$1" = "-c" ; then + local print_errs= + if test "$1" = "--help" ; then + help + return + fi + if test "$1" = "-s" ; then + print_errs=1 + shift + elif test "$1" = "-c" ; then compile=1 shift fi - local test="$1" ; shift + if test -z "$print_errs" ; then + local test="$1" ; shift + fi local includes= includes="$includes -I$KLEE_TEST_DIR" @@ -36,14 +66,17 @@ main() { test -d "$KLEE_BIN_DIR" || mkdir -p "$KLEE_BIN_DIR" if test -z "$compile" ; then - test -d "$KLEE_OUT_DIR" && rm -r "$KLEE_OUT_DIR" local line1='cd /image' - line1="$line1 && $(echo clang \ - $includes $defines \ - -o "$KLEE_BIN_DIR/a.bc" -emit-llvm -g -c \ - "$KLEE_TEST_DIR/$test.c")" - line1="$line1 && klee --libc=uclibc --posix-runtime " - line1="$line1 '--output-dir=$KLEE_OUT_DIR' '$KLEE_BIN_DIR/a.bc'" + if test -z "$print_errs" ; then + test -d "$KLEE_OUT_DIR" && rm -r "$KLEE_OUT_DIR" + + line1="$line1 && $(echo clang \ + $includes $defines \ + -o "$KLEE_BIN_DIR/a.bc" -emit-llvm -g -c \ + "$KLEE_TEST_DIR/$test.c")" + line1="$line1 && klee --libc=uclibc --posix-runtime " + line1="$line1 '--output-dir=$KLEE_OUT_DIR' '$KLEE_BIN_DIR/a.bc'" + fi local line2="for t in '$KLEE_OUT_DIR'/*.err" line2="$line2 ; do ktest-tool --write-ints" line2="$line2 \"\$(printf '%s' \"\$t\" | sed -e 's@\\.[^/]*\$@.ktest@')\"" -- cgit From c9f511d24a64da135bef4b9874c7bec04d9330e4 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 16 Oct 2017 09:06:05 +0300 Subject: viml/parser/expressions: Remove unused flag --- test/symbolic/klee/viml_expressions_parser.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'test') diff --git a/test/symbolic/klee/viml_expressions_parser.c b/test/symbolic/klee/viml_expressions_parser.c index 8f015ae9a7..9448937430 100644 --- a/test/symbolic/klee/viml_expressions_parser.c +++ b/test/symbolic/klee/viml_expressions_parser.c @@ -35,7 +35,7 @@ int main(const int argc, const char *const *const argv, { char input[INPUT_SIZE]; uint8_t shift; - int flags; + unsigned flags; const bool peek = false; avoid_optimizing_out = argc; @@ -48,8 +48,7 @@ int main(const int argc, const char *const *const argv, klee_make_symbolic(&shift, sizeof(shift), "shift"); klee_make_symbolic(&flags, sizeof(flags), "flags"); klee_assume(shift < INPUT_SIZE); - klee_assume( - flags <= (kExprFlagsMulti|kExprFlagsDisallowEOC|kExprFlagsPrintError)); + klee_assume(flags <= (kExprFlagsMulti|kExprFlagsDisallowEOC)); #endif ParserLine plines[] = { @@ -91,7 +90,7 @@ int main(const int argc, const char *const *const argv, }; kvi_init(pstate.reader.lines); - const ExprAST ast = viml_pexpr_parse(&pstate, flags); + const ExprAST ast = viml_pexpr_parse(&pstate, (int)flags); assert(ast.root != NULL || ast.err.msg); // Can’t possibly have more highlight tokens then there are bytes in string. assert(kv_size(colors) <= INPUT_SIZE - shift); -- cgit From 47938e1e22816381f26e8882eacd6e7e8baf37fd Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 19 Oct 2017 10:48:05 +0300 Subject: viml/parser/expressions: Fix some errors spotted by KLEE Not all of them are fixed yet though. --- test/unit/viml/expressions/parser_spec.lua | 139 +++++++++++++++++++++++++++++ 1 file changed, 139 insertions(+) (limited to 'test') diff --git a/test/unit/viml/expressions/parser_spec.lua b/test/unit/viml/expressions/parser_spec.lua index 5f5924c630..b81a3c0f82 100644 --- a/test/unit/viml/expressions/parser_spec.lua +++ b/test/unit/viml/expressions/parser_spec.lua @@ -7006,5 +7006,144 @@ describe('Expressions parser', function() hl('NumberPrefix', '0'), hl('Number', '0'), }) + check_parsing('"\\U\\', 0, { + -- 0123 + ast = { + [[DoubleQuotedString(val="U\\"):0:0:"\U\]], + }, + err = { + arg = '"\\U\\', + msg = 'E114: Missing double quote: %.*s', + }, + }, { + hl('InvalidDoubleQuotedString', '"'), + hl('InvalidDoubleQuotedUnknownEscape', '\\U'), + hl('InvalidDoubleQuotedBody', '\\'), + }) + check_parsing('"\\U', 0, { + -- 012 + ast = { + 'DoubleQuotedString(val="U"):0:0:"\\U', + }, + err = { + arg = '"\\U', + msg = 'E114: Missing double quote: %.*s', + }, + }, { + hl('InvalidDoubleQuotedString', '"'), + hl('InvalidDoubleQuotedUnknownEscape', '\\U'), + }) + check_parsing('|"\\U\\', 2, { + -- 01234 + ast = { + { + 'Or:0:0:|', + children = { + 'Missing:0:0:', + 'DoubleQuotedString(val="U\\\\"):0:1:"\\U\\', + }, + }, + }, + err = { + arg = '|"\\U\\', + msg = 'E15: Unexpected EOC character: %.*s', + }, + }, { + hl('InvalidOr', '|'), + hl('InvalidDoubleQuotedString', '"'), + hl('InvalidDoubleQuotedUnknownEscape', '\\U'), + hl('InvalidDoubleQuotedBody', '\\'), + }) + check_parsing('|"\\e"', 2, { + -- 01234 + ast = { + { + 'Or:0:0:|', + children = { + 'Missing:0:0:', + 'DoubleQuotedString(val="\\27"):0:1:"\\e"', + }, + }, + }, + err = { + arg = '|"\\e"', + msg = 'E15: Unexpected EOC character: %.*s', + }, + }, { + hl('InvalidOr', '|'), + hl('DoubleQuotedString', '"'), + hl('DoubleQuotedEscape', '\\e'), + hl('DoubleQuotedString', '"'), + }) + check_parsing('|\029', 2, { + -- 01 + ast = { + { + 'Or:0:0:|', + children = { + 'Missing:0:0:', + 'PlainIdentifier(scope=0,ident=\029):0:1:\029', + }, + }, + }, + err = { + arg = '|\029', + msg = 'E15: Unexpected EOC character: %.*s', + }, + }, { + hl('InvalidOr', '|'), + hl('InvalidIdentifier', '\029'), + }) + check_parsing('"\\<', 0, { + -- 012 + ast = { + 'DoubleQuotedString(val="<"):0:0:"\\<', + }, + err = { + arg = '"\\<', + msg = 'E114: Missing double quote: %.*s', + }, + }, { + hl('InvalidDoubleQuotedString', '"'), + hl('InvalidDoubleQuotedUnknownEscape', '\\<'), + }) + check_parsing('"\\1', 0, { + -- 012 + ast = { + 'DoubleQuotedString(val="\\1"):0:0:"\\1', + }, + err = { + arg = '"\\1', + msg = 'E114: Missing double quote: %.*s', + }, + }, { + hl('InvalidDoubleQuotedString', '"'), + hl('InvalidDoubleQuotedEscape', '\\1'), + }) + check_parsing('}l') + check_parsing(':?\000\000\000\000\000\000\000', 0, { + ast = { + { + 'Colon:0:0::', + children = { + 'Missing:0:0:', + { + 'Ternary:0:1:?', + children = { + 'Missing:0:1:', + 'TernaryValue:0:1:?', + }, + }, + }, + }, + }, + err = { + arg = ':?', + msg = 'E15: Colon outside of dictionary or ternary operator: %.*s', + }, + }, { + hl('InvalidColon', ':'), + hl('InvalidTernary', '?'), + }) end) end) -- cgit From 568cf73c90af2966ee091f2180905a8cf9582064 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 29 Oct 2017 01:29:48 +0300 Subject: viml/parser/expressions: Fix last error found by KLEE --- test/unit/viml/expressions/parser_spec.lua | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) (limited to 'test') diff --git a/test/unit/viml/expressions/parser_spec.lua b/test/unit/viml/expressions/parser_spec.lua index b81a3c0f82..f3d0790e68 100644 --- a/test/unit/viml/expressions/parser_spec.lua +++ b/test/unit/viml/expressions/parser_spec.lua @@ -7120,7 +7120,25 @@ describe('Expressions parser', function() hl('InvalidDoubleQuotedString', '"'), hl('InvalidDoubleQuotedEscape', '\\1'), }) - check_parsing('}l') + check_parsing('}l', 0, { + -- 01 + ast = { + { + 'OpMissing:0:1:', + children = { + 'UnknownFigure(---):0:0:', + 'PlainIdentifier(scope=0,ident=l):0:1:l', + }, + }, + }, + err = { + arg = '}l', + msg = 'E15: Unexpected closing figure brace: %.*s', + }, + }, { + hl('InvalidFigureBrace', '}'), + hl('InvalidIdentifier', 'l'), + }) check_parsing(':?\000\000\000\000\000\000\000', 0, { ast = { { -- cgit From c202f17c8d8cb2140aa1b21b2e2d2ab3925a7812 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 29 Oct 2017 01:31:31 +0300 Subject: unittests: Avoid alloc log checking errors when printing tests --- test/unit/viml/expressions/parser_spec.lua | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'test') diff --git a/test/unit/viml/expressions/parser_spec.lua b/test/unit/viml/expressions/parser_spec.lua index f3d0790e68..125a658f7b 100644 --- a/test/unit/viml/expressions/parser_spec.lua +++ b/test/unit/viml/expressions/parser_spec.lua @@ -248,16 +248,16 @@ describe('Expressions parser', function() local hls = phl2lua(pstate) if exp_ast == nil then format_check(str, flags, ast, hls) - return - end - eq(exp_ast, ast) - if exp_highlighting_fs then - local exp_highlighting = {} - local next_col = 0 - for i, h in ipairs(exp_highlighting_fs) do - exp_highlighting[i], next_col = h(next_col) + else + eq(exp_ast, ast) + if exp_highlighting_fs then + local exp_highlighting = {} + local next_col = 0 + for i, h in ipairs(exp_highlighting_fs) do + exp_highlighting[i], next_col = h(next_col) + end + eq(exp_highlighting, hls) end - eq(exp_highlighting, hls) end lib.viml_pexpr_free_ast(east) kvi_destroy(pstate.colors) -- cgit From 06bdc9ed839eedbead34d58214927d3c0cebff58 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 29 Oct 2017 01:40:55 +0300 Subject: klee: Update vim_str2nr in mock as well --- test/symbolic/klee/nvim/charset.c | 78 +++++++++++++++------------------------ 1 file changed, 30 insertions(+), 48 deletions(-) (limited to 'test') diff --git a/test/symbolic/klee/nvim/charset.c b/test/symbolic/klee/nvim/charset.c index c584bd72ef..f3a218949f 100644 --- a/test/symbolic/klee/nvim/charset.c +++ b/test/symbolic/klee/nvim/charset.c @@ -60,18 +60,19 @@ void vim_str2nr(const char_u *const start, int *const prep, int *const len, // decimal or octal, default is decimal pre = 0; - if (what & STR2NR_OCT) { - // Don't interpret "0", "08" or "0129" as octal. - for (int i = 1; !STRING_ENDED(ptr + i) && ascii_isdigit(ptr[i]); i++) { + if (what & STR2NR_OCT + && !STRING_ENDED(ptr + 1) + && ('0' <= ptr[1] && ptr[1] <= '7')) { + // Assume octal now: what we already know is that string starts with + // zero and some octal digit. + pre = '0'; + // Don’t interpret "0", "008" or "0129" as octal. + for (int i = 2; !STRING_ENDED(ptr + i) && ascii_isdigit(ptr[i]); i++) { if (ptr[i] > '7') { - // can't be octal + // Can’t be octal. pre = 0; break; } - if (ptr[i] >= '0') { - // assume octal - pre = '0'; - } } } } @@ -79,51 +80,32 @@ void vim_str2nr(const char_u *const start, int *const prep, int *const len, // Do the string-to-numeric conversion "manually" to avoid sscanf quirks. uvarnumber_T un = 0; +#define PARSE_NUMBER(base, cond, conv) \ + do { \ + while (!STRING_ENDED(ptr) && (cond)) { \ + /* avoid ubsan error for overflow */ \ + if (un < UVARNUMBER_MAX / base) { \ + un = base * un + (uvarnumber_T)(conv); \ + } else { \ + un = UVARNUMBER_MAX; \ + } \ + ptr++; \ + } \ + } while (0) if (pre == 'B' || pre == 'b' || what == (STR2NR_BIN|STR2NR_FORCE)) { - // bin - while (!STRING_ENDED(ptr) && '0' <= *ptr && *ptr <= '1') { - // avoid ubsan error for overflow - if (un < UVARNUMBER_MAX / 2) { - un = 2 * un + (uvarnumber_T)(*ptr - '0'); - } else { - un = UVARNUMBER_MAX; - } - ptr++; - } + // Binary number. + PARSE_NUMBER(2, (*ptr == '0' || *ptr == '1'), (*ptr - '0')); } else if (pre == '0' || what == (STR2NR_OCT|STR2NR_FORCE)) { - // octal - while (!STRING_ENDED(ptr) && '0' <= *ptr && *ptr <= '7') { - // avoid ubsan error for overflow - if (un < UVARNUMBER_MAX / 8) { - un = 8 * un + (uvarnumber_T)(*ptr - '0'); - } else { - un = UVARNUMBER_MAX; - } - ptr++; - } + // Octal number. + PARSE_NUMBER(8, ('0' <= *ptr && *ptr <= '7'), (*ptr - '0')); } else if (pre == 'X' || pre == 'x' || what == (STR2NR_HEX|STR2NR_FORCE)) { - // hex - while (!STRING_ENDED(ptr) && ascii_isxdigit(*ptr)) { - // avoid ubsan error for overflow - if (un < UVARNUMBER_MAX / 16) { - un = 16 * un + (uvarnumber_T)hex2nr(*ptr); - } else { - un = UVARNUMBER_MAX; - } - ptr++; - } + // Hexadecimal number. + PARSE_NUMBER(16, (ascii_isxdigit(*ptr)), (hex2nr(*ptr))); } else { - // decimal - while (!STRING_ENDED(ptr) && ascii_isdigit(*ptr)) { - // avoid ubsan error for overflow - if (un < UVARNUMBER_MAX / 10) { - un = 10 * un + (uvarnumber_T)(*ptr - '0'); - } else { - un = UVARNUMBER_MAX; - } - ptr++; - } + // Decimal number. + PARSE_NUMBER(10, (ascii_isdigit(*ptr)), (*ptr - '0')); } +#undef PARSE_NUMBER if (prep != NULL) { *prep = pre; -- cgit From b935a12dab17c3887db9c5fd7c90b34b2c51170f Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 29 Oct 2017 16:32:13 +0300 Subject: ex_getln: Make use of new parser to color expressions Retires g:Nvim_color_expr callback. --- test/functional/ui/cmdline_highlight_spec.lua | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) (limited to 'test') diff --git a/test/functional/ui/cmdline_highlight_spec.lua b/test/functional/ui/cmdline_highlight_spec.lua index d87ce72599..60a4a815e7 100644 --- a/test/functional/ui/cmdline_highlight_spec.lua +++ b/test/functional/ui/cmdline_highlight_spec.lua @@ -144,7 +144,9 @@ before_each(function() EOB={bold = true, foreground = Screen.colors.Blue1}, ERR={foreground = Screen.colors.Grey100, background = Screen.colors.Red}, SK={foreground = Screen.colors.Blue}, - PE={bold = true, foreground = Screen.colors.SeaGreen4} + PE={bold = true, foreground = Screen.colors.SeaGreen4}, + NUM={foreground = Screen.colors.Blue2}, + NPAR={foreground = Screen.colors.Yellow}, }) end) @@ -863,7 +865,10 @@ describe('Ex commands coloring support', function() end) describe('Expressions coloring support', function() it('works', function() - meths.set_var('Nvim_color_expr', 'RainBowParens') + meths.command('hi clear NVimNumber') + meths.command('hi clear NVimNestingParenthesis') + meths.command('hi NVimNumber guifg=Blue2') + meths.command('hi NVimNestingParenthesis guifg=Yellow') feed(':echo =(((1)))') screen:expect([[ | @@ -873,21 +878,24 @@ describe('Expressions coloring support', function() {EOB:~ }| {EOB:~ }| {EOB:~ }| - ={RBP1:(}{RBP2:(}{RBP3:(}1{RBP3:)}{RBP2:)}{RBP1:)}^ | + ={NPAR:(((}{NUM:1}{NPAR:)))}^ | ]]) end) - it('errors out when failing to get callback', function() + it('does not use Nvim_color_expr', function() meths.set_var('Nvim_color_expr', 42) + -- Used to error out due to failing to get callback. + meths.command('hi clear NVimNumber') + meths.command('hi NVimNumber guifg=Blue2') feed(':=1') screen:expect([[ + | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| {EOB:~ }| {EOB:~ }| {EOB:~ }| - = | - {ERR:E5409: Unable to get g:Nvim_color_expr c}| - {ERR:allback: Vim:E6000: Argument is not a fu}| - {ERR:nction or function name} | - =1^ | + ={NUM:1}^ | ]]) end) end) -- cgit From 748f3ad5bbf9706dddddeea5df693221d6ae5e94 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 29 Oct 2017 21:30:06 +0300 Subject: syntax,viml/expressions/parser: Create defaults for expr highlighting --- test/unit/viml/expressions/parser_spec.lua | 530 +++++++++++++++-------------- 1 file changed, 281 insertions(+), 249 deletions(-) (limited to 'test') diff --git a/test/unit/viml/expressions/parser_spec.lua b/test/unit/viml/expressions/parser_spec.lua index 125a658f7b..454fbad236 100644 --- a/test/unit/viml/expressions/parser_spec.lua +++ b/test/unit/viml/expressions/parser_spec.lua @@ -1183,7 +1183,7 @@ describe('Expressions parser', function() 'PlainIdentifier(scope=0,ident=var):0:0:var', }, }, { - hl('Identifier', 'var'), + hl('IdentifierName', 'var'), }) check_parsing('g:var', 0, { ast = { @@ -1192,7 +1192,7 @@ describe('Expressions parser', function() }, { hl('IdentifierScope', 'g'), hl('IdentifierScopeDelimiter', ':'), - hl('Identifier', 'var'), + hl('IdentifierName', 'var'), }) check_parsing('g:', 0, { ast = { @@ -1214,7 +1214,7 @@ describe('Expressions parser', function() }, }, { hl('Curly', '{'), - hl('Identifier', 'a'), + hl('IdentifierName', 'a'), hl('Curly', '}'), }) check_parsing('{a:b}', 0, { @@ -1231,7 +1231,7 @@ describe('Expressions parser', function() hl('Curly', '{'), hl('IdentifierScope', 'a'), hl('IdentifierScopeDelimiter', ':'), - hl('Identifier', 'b'), + hl('IdentifierName', 'b'), hl('Curly', '}'), }) check_parsing('{a:@b}', 0, { @@ -1347,7 +1347,7 @@ describe('Expressions parser', function() hl('Curly', '{'), hl('Register', '@a'), hl('Curly', '}'), - hl('Identifier', '_test'), + hl('IdentifierName', '_test'), }) check_parsing('g:{@a}_test', 0, { -- 01234567890 @@ -1377,7 +1377,7 @@ describe('Expressions parser', function() hl('Curly', '{'), hl('Register', '@a'), hl('Curly', '}'), - hl('Identifier', '_test'), + hl('IdentifierName', '_test'), }) check_parsing('g:{@a}_test()', 0, { -- 0123456789012 @@ -1412,7 +1412,7 @@ describe('Expressions parser', function() hl('Curly', '{'), hl('Register', '@a'), hl('Curly', '}'), - hl('Identifier', '_test'), + hl('IdentifierName', '_test'), hl('CallingParenthesis', '('), hl('CallingParenthesis', ')'), }) @@ -1563,7 +1563,7 @@ describe('Expressions parser', function() }, }, { hl('Lambda', '{'), - hl('Identifier', 'a'), + hl('IdentifierName', 'a'), hl('Arrow', '->'), hl('Register', '@a'), hl('Lambda', '}'), @@ -1592,9 +1592,9 @@ describe('Expressions parser', function() }, }, { hl('Lambda', '{'), - hl('Identifier', 'a'), + hl('IdentifierName', 'a'), hl('Comma', ','), - hl('Identifier', 'b'), + hl('IdentifierName', 'b'), hl('Arrow', '->'), hl('Register', '@a'), hl('Lambda', '}'), @@ -1629,11 +1629,11 @@ describe('Expressions parser', function() }, }, { hl('Lambda', '{'), - hl('Identifier', 'a'), + hl('IdentifierName', 'a'), hl('Comma', ','), - hl('Identifier', 'b'), + hl('IdentifierName', 'b'), hl('Comma', ','), - hl('Identifier', 'c'), + hl('IdentifierName', 'c'), hl('Arrow', '->'), hl('Register', '@a'), hl('Lambda', '}'), @@ -1674,13 +1674,13 @@ describe('Expressions parser', function() }, }, { hl('Lambda', '{'), - hl('Identifier', 'a'), + hl('IdentifierName', 'a'), hl('Comma', ','), - hl('Identifier', 'b'), + hl('IdentifierName', 'b'), hl('Comma', ','), - hl('Identifier', 'c'), + hl('IdentifierName', 'c'), hl('Comma', ','), - hl('Identifier', 'd'), + hl('IdentifierName', 'd'), hl('Arrow', '->'), hl('Register', '@a'), hl('Lambda', '}'), @@ -1726,13 +1726,13 @@ describe('Expressions parser', function() }, }, { hl('Lambda', '{'), - hl('Identifier', 'a'), + hl('IdentifierName', 'a'), hl('Comma', ','), - hl('Identifier', 'b'), + hl('IdentifierName', 'b'), hl('Comma', ','), - hl('Identifier', 'c'), + hl('IdentifierName', 'c'), hl('Comma', ','), - hl('Identifier', 'd'), + hl('IdentifierName', 'd'), hl('Comma', ','), hl('Arrow', '->'), hl('Register', '@a'), @@ -1797,19 +1797,19 @@ describe('Expressions parser', function() }, }, { hl('Lambda', '{'), - hl('Identifier', 'a'), + hl('IdentifierName', 'a'), hl('Comma', ','), - hl('Identifier', 'b'), + hl('IdentifierName', 'b'), hl('Arrow', '->'), hl('Lambda', '{'), - hl('Identifier', 'c'), + hl('IdentifierName', 'c'), hl('Comma', ','), - hl('Identifier', 'd'), + hl('IdentifierName', 'd'), hl('Arrow', '->'), hl('Lambda', '{'), - hl('Identifier', 'e'), + hl('IdentifierName', 'e'), hl('Comma', ','), - hl('Identifier', 'f'), + hl('IdentifierName', 'f'), hl('Arrow', '->'), hl('Register', '@a'), hl('Lambda', '}'), @@ -1850,13 +1850,13 @@ describe('Expressions parser', function() }, }, { hl('Lambda', '{'), - hl('Identifier', 'a'), + hl('IdentifierName', 'a'), hl('Comma', ','), - hl('Identifier', 'b'), + hl('IdentifierName', 'b'), hl('Arrow', '->'), - hl('Identifier', 'c'), + hl('IdentifierName', 'c'), hl('InvalidComma', ','), - hl('Identifier', 'd'), + hl('IdentifierName', 'd'), hl('Lambda', '}'), }) check_parsing('a,b,c,d', 0, { @@ -1887,13 +1887,13 @@ describe('Expressions parser', function() msg = 'E15: Comma outside of call, lambda or literal: %.*s', }, }, { - hl('Identifier', 'a'), + hl('IdentifierName', 'a'), hl('InvalidComma', ','), - hl('Identifier', 'b'), + hl('IdentifierName', 'b'), hl('InvalidComma', ','), - hl('Identifier', 'c'), + hl('IdentifierName', 'c'), hl('InvalidComma', ','), - hl('Identifier', 'd'), + hl('IdentifierName', 'd'), }) check_parsing('a,b,c,d,', 0, { -- 0123456789 @@ -1928,13 +1928,13 @@ describe('Expressions parser', function() msg = 'E15: Comma outside of call, lambda or literal: %.*s', }, }, { - hl('Identifier', 'a'), + hl('IdentifierName', 'a'), hl('InvalidComma', ','), - hl('Identifier', 'b'), + hl('IdentifierName', 'b'), hl('InvalidComma', ','), - hl('Identifier', 'c'), + hl('IdentifierName', 'c'), hl('InvalidComma', ','), - hl('Identifier', 'd'), + hl('IdentifierName', 'd'), hl('InvalidComma', ','), }) check_parsing(',', 0, { @@ -1983,7 +1983,7 @@ describe('Expressions parser', function() }, { hl('Curly', '{'), hl('InvalidComma', ','), - hl('Identifier', 'a'), + hl('IdentifierName', 'a'), hl('InvalidArrow', '->'), hl('Register', '@a'), hl('Curly', '}'), @@ -2041,9 +2041,9 @@ describe('Expressions parser', function() }, }, { hl('Lambda', '{'), - hl('Identifier', 'a'), + hl('IdentifierName', 'a'), hl('Comma', ','), - hl('Identifier', 'b'), + hl('IdentifierName', 'b'), hl('InvalidLambda', '}'), }) check_parsing('{a,}', 0, { @@ -2067,7 +2067,7 @@ describe('Expressions parser', function() }, }, { hl('Lambda', '{'), - hl('Identifier', 'a'), + hl('IdentifierName', 'a'), hl('Comma', ','), hl('InvalidLambda', '}'), }) @@ -2343,9 +2343,9 @@ describe('Expressions parser', function() hl('Curly', '{'), hl('NestingParenthesis', '('), hl('Lambda', '{'), - hl('Identifier', 'f'), + hl('IdentifierName', 'f'), hl('Arrow', '->', 1), - hl('Identifier', 'g', 1), + hl('IdentifierName', 'g', 1), hl('Lambda', '}'), hl('NestingParenthesis', ')'), hl('CallingParenthesis', '('), @@ -2387,11 +2387,11 @@ describe('Expressions parser', function() hl('IdentifierScope', 'a'), hl('IdentifierScopeDelimiter', ':'), hl('Curly', '{'), - hl('Identifier', 'b'), + hl('IdentifierName', 'b'), hl('CallingParenthesis', '('), hl('CallingParenthesis', ')'), hl('Curly', '}'), - hl('Identifier', 'c'), + hl('IdentifierName', 'c'), }) check_parsing('a:{{b, c -> @d + @e + ({f -> g})(@h)}(@i)}j', 0, { -- 01234567890123456789012345678901234567890123456 @@ -2478,9 +2478,9 @@ describe('Expressions parser', function() hl('IdentifierScopeDelimiter', ':'), hl('Curly', '{'), hl('Lambda', '{'), - hl('Identifier', 'b'), + hl('IdentifierName', 'b'), hl('Comma', ','), - hl('Identifier', 'c', 1), + hl('IdentifierName', 'c', 1), hl('Arrow', '->', 1), hl('Register', '@d', 1), hl('BinaryPlus', '+', 1), @@ -2488,9 +2488,9 @@ describe('Expressions parser', function() hl('BinaryPlus', '+', 1), hl('NestingParenthesis', '(', 1), hl('Lambda', '{'), - hl('Identifier', 'f'), + hl('IdentifierName', 'f'), hl('Arrow', '->', 1), - hl('Identifier', 'g', 1), + hl('IdentifierName', 'g', 1), hl('Lambda', '}'), hl('NestingParenthesis', ')'), hl('CallingParenthesis', '('), @@ -2501,7 +2501,7 @@ describe('Expressions parser', function() hl('Register', '@i'), hl('CallingParenthesis', ')'), hl('Curly', '}'), - hl('Identifier', 'j'), + hl('IdentifierName', 'j'), }) check_parsing('{@a + @b : @c + @d, @e + @f : @g + @i}', 0, { -- 01234567890123456789012345678901234567 @@ -2635,13 +2635,13 @@ describe('Expressions parser', function() msg = 'E15: Arrow outside of lambda: %.*s', }, }, { - hl('Identifier', 'a'), + hl('IdentifierName', 'a'), hl('InvalidArrow', '->', 1), - hl('Identifier', 'b', 1), + hl('IdentifierName', 'b', 1), hl('InvalidArrow', '->', 1), - hl('Identifier', 'c', 1), + hl('IdentifierName', 'c', 1), hl('InvalidArrow', '->', 1), - hl('Identifier', 'd', 1), + hl('IdentifierName', 'd', 1), }) check_parsing('{a -> b -> c}', 0, { -- 0123456789012 @@ -2672,11 +2672,11 @@ describe('Expressions parser', function() }, }, { hl('Lambda', '{'), - hl('Identifier', 'a'), + hl('IdentifierName', 'a'), hl('Arrow', '->', 1), - hl('Identifier', 'b', 1), + hl('IdentifierName', 'b', 1), hl('InvalidArrow', '->', 1), - hl('Identifier', 'c', 1), + hl('IdentifierName', 'c', 1), hl('Lambda', '}'), }) check_parsing('{a: -> b}', 0, { @@ -2704,7 +2704,7 @@ describe('Expressions parser', function() hl('IdentifierScope', 'a'), hl('IdentifierScopeDelimiter', ':'), hl('InvalidArrow', '->', 1), - hl('Identifier', 'b', 1), + hl('IdentifierName', 'b', 1), hl('Curly', '}'), }) @@ -2732,9 +2732,9 @@ describe('Expressions parser', function() hl('Curly', '{'), hl('IdentifierScope', 'a'), hl('IdentifierScopeDelimiter', ':'), - hl('Identifier', 'b'), + hl('IdentifierName', 'b'), hl('InvalidArrow', '->', 1), - hl('Identifier', 'b', 1), + hl('IdentifierName', 'b', 1), hl('Curly', '}'), }) @@ -2760,9 +2760,9 @@ describe('Expressions parser', function() }, }, { hl('Curly', '{'), - hl('Identifier', 'a#b'), + hl('IdentifierName', 'a#b'), hl('InvalidArrow', '->', 1), - hl('Identifier', 'b', 1), + hl('IdentifierName', 'b', 1), hl('Curly', '}'), }) check_parsing('{a : b : c}', 0, { @@ -2794,11 +2794,11 @@ describe('Expressions parser', function() }, }, { hl('Dict', '{'), - hl('Identifier', 'a'), + hl('IdentifierName', 'a'), hl('Colon', ':', 1), - hl('Identifier', 'b', 1), + hl('IdentifierName', 'b', 1), hl('InvalidColon', ':', 1), - hl('Identifier', 'c', 1), + hl('IdentifierName', 'c', 1), hl('Dict', '}'), }) check_parsing('{', 0, { @@ -2829,7 +2829,7 @@ describe('Expressions parser', function() }, }, { hl('FigureBrace', '{'), - hl('Identifier', 'a'), + hl('IdentifierName', 'a'), }) check_parsing('{a,b', 0, { -- 0123 @@ -2853,9 +2853,9 @@ describe('Expressions parser', function() }, }, { hl('Lambda', '{'), - hl('Identifier', 'a'), + hl('IdentifierName', 'a'), hl('Comma', ','), - hl('Identifier', 'b'), + hl('IdentifierName', 'b'), }) check_parsing('{a,b->', 0, { -- 012345 @@ -2880,9 +2880,9 @@ describe('Expressions parser', function() }, }, { hl('Lambda', '{'), - hl('Identifier', 'a'), + hl('IdentifierName', 'a'), hl('Comma', ','), - hl('Identifier', 'b'), + hl('IdentifierName', 'b'), hl('Arrow', '->'), }) check_parsing('{a,b->c', 0, { @@ -2913,11 +2913,11 @@ describe('Expressions parser', function() }, }, { hl('Lambda', '{'), - hl('Identifier', 'a'), + hl('IdentifierName', 'a'), hl('Comma', ','), - hl('Identifier', 'b'), + hl('IdentifierName', 'b'), hl('Arrow', '->'), - hl('Identifier', 'c'), + hl('IdentifierName', 'c'), }) check_parsing('{a : b', 0, { -- 012345 @@ -2941,9 +2941,9 @@ describe('Expressions parser', function() }, }, { hl('Dict', '{'), - hl('Identifier', 'a'), + hl('IdentifierName', 'a'), hl('Colon', ':', 1), - hl('Identifier', 'b', 1), + hl('IdentifierName', 'b', 1), }) check_parsing('{a : b,', 0, { -- 0123456 @@ -2972,9 +2972,9 @@ describe('Expressions parser', function() }, }, { hl('Dict', '{'), - hl('Identifier', 'a'), + hl('IdentifierName', 'a'), hl('Colon', ':', 1), - hl('Identifier', 'b', 1), + hl('IdentifierName', 'b', 1), hl('Comma', ','), }) end) @@ -2997,11 +2997,11 @@ describe('Expressions parser', function() }, }, }, { - hl('Identifier', 'a'), + hl('IdentifierName', 'a'), hl('Ternary', '?', 1), - hl('Identifier', 'b', 1), + hl('IdentifierName', 'b', 1), hl('TernaryColon', ':', 1), - hl('Identifier', 'c', 1), + hl('IdentifierName', 'c', 1), }) check_parsing('@a?@b?@c:@d:@e', 0, { -- 01234567890123 @@ -3271,9 +3271,9 @@ describe('Expressions parser', function() msg = 'E109: Missing \':\' after \'?\': %.*s', }, }, { - hl('Identifier', 'a'), + hl('IdentifierName', 'a'), hl('Ternary', '?'), - hl('Identifier', 'b'), + hl('IdentifierName', 'b'), }) check_parsing('a?b:', 0, { -- 0123 @@ -3296,7 +3296,7 @@ describe('Expressions parser', function() msg = 'E109: Missing \':\' after \'?\': %.*s', }, }, { - hl('Identifier', 'a'), + hl('IdentifierName', 'a'), hl('Ternary', '?'), hl('IdentifierScope', 'b'), hl('IdentifierScopeDelimiter', ':'), @@ -3320,12 +3320,12 @@ describe('Expressions parser', function() }, }, }, { - hl('Identifier', 'a'), + hl('IdentifierName', 'a'), hl('Ternary', '?'), hl('IdentifierScope', 'b'), hl('IdentifierScopeDelimiter', ':'), hl('TernaryColon', ':'), - hl('Identifier', 'c'), + hl('IdentifierName', 'c'), }) check_parsing('a?b :', 0, { @@ -3349,9 +3349,9 @@ describe('Expressions parser', function() msg = 'E15: Expected value, got EOC: %.*s', }, }, { - hl('Identifier', 'a'), + hl('IdentifierName', 'a'), hl('Ternary', '?'), - hl('Identifier', 'b'), + hl('IdentifierName', 'b'), hl('TernaryColon', ':', 1), }) @@ -3615,15 +3615,15 @@ describe('Expressions parser', function() }, }, }, { - hl('Identifier', 'a'), + hl('IdentifierName', 'a'), hl('Ternary', '?'), - hl('Identifier', 'b'), + hl('IdentifierName', 'b'), hl('Curly', '{'), - hl('Identifier', 'cdef'), + hl('IdentifierName', 'cdef'), hl('Curly', '}'), - hl('Identifier', 'g'), + hl('IdentifierName', 'g'), hl('TernaryColon', ':'), - hl('Identifier', 'h'), + hl('IdentifierName', 'h'), }) check_parsing('a ? b : c : d', 0, { -- 0123456789012 @@ -3654,13 +3654,13 @@ describe('Expressions parser', function() msg = 'E15: Colon outside of dictionary or ternary operator: %.*s', }, }, { - hl('Identifier', 'a'), + hl('IdentifierName', 'a'), hl('Ternary', '?', 1), - hl('Identifier', 'b', 1), + hl('IdentifierName', 'b', 1), hl('TernaryColon', ':', 1), - hl('Identifier', 'c', 1), + hl('IdentifierName', 'c', 1), hl('InvalidColon', ':', 1), - hl('Identifier', 'd', 1), + hl('IdentifierName', 'd', 1), }) end) itp('works with comparison operators', function() @@ -3676,9 +3676,9 @@ describe('Expressions parser', function() }, }, }, { - hl('Identifier', 'a'), - hl('ComparisonOperator', '==', 1), - hl('Identifier', 'b', 1), + hl('IdentifierName', 'a'), + hl('Comparison', '==', 1), + hl('IdentifierName', 'b', 1), }) check_parsing('a ==? b', 0, { @@ -3693,10 +3693,10 @@ describe('Expressions parser', function() }, }, }, { - hl('Identifier', 'a'), - hl('ComparisonOperator', '==', 1), - hl('ComparisonOperatorModifier', '?'), - hl('Identifier', 'b', 1), + hl('IdentifierName', 'a'), + hl('Comparison', '==', 1), + hl('ComparisonModifier', '?'), + hl('IdentifierName', 'b', 1), }) check_parsing('a ==# b', 0, { @@ -3711,10 +3711,10 @@ describe('Expressions parser', function() }, }, }, { - hl('Identifier', 'a'), - hl('ComparisonOperator', '==', 1), - hl('ComparisonOperatorModifier', '#'), - hl('Identifier', 'b', 1), + hl('IdentifierName', 'a'), + hl('Comparison', '==', 1), + hl('ComparisonModifier', '#'), + hl('IdentifierName', 'b', 1), }) check_parsing('a !=# b', 0, { @@ -3729,10 +3729,10 @@ describe('Expressions parser', function() }, }, }, { - hl('Identifier', 'a'), - hl('ComparisonOperator', '!=', 1), - hl('ComparisonOperatorModifier', '#'), - hl('Identifier', 'b', 1), + hl('IdentifierName', 'a'), + hl('Comparison', '!=', 1), + hl('ComparisonModifier', '#'), + hl('IdentifierName', 'b', 1), }) check_parsing('a <=# b', 0, { @@ -3747,10 +3747,10 @@ describe('Expressions parser', function() }, }, }, { - hl('Identifier', 'a'), - hl('ComparisonOperator', '<=', 1), - hl('ComparisonOperatorModifier', '#'), - hl('Identifier', 'b', 1), + hl('IdentifierName', 'a'), + hl('Comparison', '<=', 1), + hl('ComparisonModifier', '#'), + hl('IdentifierName', 'b', 1), }) check_parsing('a >=# b', 0, { @@ -3765,10 +3765,10 @@ describe('Expressions parser', function() }, }, }, { - hl('Identifier', 'a'), - hl('ComparisonOperator', '>=', 1), - hl('ComparisonOperatorModifier', '#'), - hl('Identifier', 'b', 1), + hl('IdentifierName', 'a'), + hl('Comparison', '>=', 1), + hl('ComparisonModifier', '#'), + hl('IdentifierName', 'b', 1), }) check_parsing('a ># b', 0, { @@ -3783,10 +3783,10 @@ describe('Expressions parser', function() }, }, }, { - hl('Identifier', 'a'), - hl('ComparisonOperator', '>', 1), - hl('ComparisonOperatorModifier', '#'), - hl('Identifier', 'b', 1), + hl('IdentifierName', 'a'), + hl('Comparison', '>', 1), + hl('ComparisonModifier', '#'), + hl('IdentifierName', 'b', 1), }) check_parsing('a <# b', 0, { @@ -3801,10 +3801,10 @@ describe('Expressions parser', function() }, }, }, { - hl('Identifier', 'a'), - hl('ComparisonOperator', '<', 1), - hl('ComparisonOperatorModifier', '#'), - hl('Identifier', 'b', 1), + hl('IdentifierName', 'a'), + hl('Comparison', '<', 1), + hl('ComparisonModifier', '#'), + hl('IdentifierName', 'b', 1), }) check_parsing('a is#b', 0, { @@ -3819,10 +3819,10 @@ describe('Expressions parser', function() }, }, }, { - hl('Identifier', 'a'), - hl('ComparisonOperator', 'is', 1), - hl('ComparisonOperatorModifier', '#'), - hl('Identifier', 'b'), + hl('IdentifierName', 'a'), + hl('Comparison', 'is', 1), + hl('ComparisonModifier', '#'), + hl('IdentifierName', 'b'), }) check_parsing('a is?b', 0, { @@ -3837,10 +3837,10 @@ describe('Expressions parser', function() }, }, }, { - hl('Identifier', 'a'), - hl('ComparisonOperator', 'is', 1), - hl('ComparisonOperatorModifier', '?'), - hl('Identifier', 'b'), + hl('IdentifierName', 'a'), + hl('Comparison', 'is', 1), + hl('ComparisonModifier', '?'), + hl('IdentifierName', 'b'), }) check_parsing('a isnot b', 0, { @@ -3855,9 +3855,9 @@ describe('Expressions parser', function() }, }, }, { - hl('Identifier', 'a'), - hl('ComparisonOperator', 'isnot', 1), - hl('Identifier', 'b', 1), + hl('IdentifierName', 'a'), + hl('Comparison', 'isnot', 1), + hl('IdentifierName', 'b', 1), }) check_parsing('a < b < c', 0, { @@ -3882,12 +3882,43 @@ describe('Expressions parser', function() msg = 'E15: Operator is not associative: %.*s', }, }, { - hl('Identifier', 'a'), - hl('ComparisonOperator', '<', 1), - hl('Identifier', 'b', 1), - hl('InvalidComparisonOperator', '<', 1), - hl('Identifier', 'c', 1), + hl('IdentifierName', 'a'), + hl('Comparison', '<', 1), + hl('IdentifierName', 'b', 1), + hl('InvalidComparison', '<', 1), + hl('IdentifierName', 'c', 1), }) + + check_parsing('a < b <# c', 0, { + -- 012345678 + ast = { + { + 'Comparison(type=GreaterOrEqual,inv=1,ccs=UseOption):0:1: <', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + { + 'Comparison(type=GreaterOrEqual,inv=1,ccs=MatchCase):0:5: <#', + children = { + 'PlainIdentifier(scope=0,ident=b):0:3: b', + 'PlainIdentifier(scope=0,ident=c):0:8: c', + }, + }, + }, + }, + }, + err = { + arg = ' <# c', + msg = 'E15: Operator is not associative: %.*s', + }, + }, { + hl('IdentifierName', 'a'), + hl('Comparison', '<', 1), + hl('IdentifierName', 'b', 1), + hl('InvalidComparison', '<', 1), + hl('InvalidComparisonModifier', '#'), + hl('IdentifierName', 'c', 1), + }) + check_parsing('a += b', 0, { -- 012345 ast = { @@ -3910,10 +3941,10 @@ describe('Expressions parser', function() msg = 'E15: Expected == or =~: %.*s', }, }, { - hl('Identifier', 'a'), + hl('IdentifierName', 'a'), hl('BinaryPlus', '+', 1), - hl('InvalidComparisonOperator', '='), - hl('Identifier', 'b', 1), + hl('InvalidComparison', '='), + hl('IdentifierName', 'b', 1), }) check_parsing('a + b == c + d', 0, { -- 01234567890123 @@ -3940,13 +3971,13 @@ describe('Expressions parser', function() }, }, }, { - hl('Identifier', 'a'), + hl('IdentifierName', 'a'), hl('BinaryPlus', '+', 1), - hl('Identifier', 'b', 1), - hl('ComparisonOperator', '==', 1), - hl('Identifier', 'c', 1), + hl('IdentifierName', 'b', 1), + hl('Comparison', '==', 1), + hl('IdentifierName', 'c', 1), hl('BinaryPlus', '+', 1), - hl('Identifier', 'd', 1), + hl('IdentifierName', 'd', 1), }) check_parsing('+ a == + b', 0, { -- 0123456789 @@ -3971,10 +4002,10 @@ describe('Expressions parser', function() }, }, { hl('UnaryPlus', '+'), - hl('Identifier', 'a', 1), - hl('ComparisonOperator', '==', 1), + hl('IdentifierName', 'a', 1), + hl('Comparison', '==', 1), hl('UnaryPlus', '+', 1), - hl('Identifier', 'b', 1), + hl('IdentifierName', 'b', 1), }) end) itp('works with concat/subscript', function() @@ -4011,7 +4042,7 @@ describe('Expressions parser', function() msg = 'E15: Expected value, got EOC: %.*s', }, }, { - hl('Identifier', 'a'), + hl('IdentifierName', 'a'), hl('ConcatOrSubscript', '.'), }) @@ -4027,7 +4058,7 @@ describe('Expressions parser', function() }, }, }, { - hl('Identifier', 'a'), + hl('IdentifierName', 'a'), hl('ConcatOrSubscript', '.'), hl('IdentifierKey', 'b'), }) @@ -4084,7 +4115,7 @@ describe('Expressions parser', function() }, }, }, { - hl('Identifier', 'a'), + hl('IdentifierName', 'a'), hl('Concat', '.', 1), hl('Number', '1', 1), hl('ConcatOrSubscript', '.'), @@ -4116,7 +4147,7 @@ describe('Expressions parser', function() hl('BinaryPlus', '+', 1), hl('Float', '1.2', 1), hl('Concat', '.', 1), - hl('Identifier', 'a', 1), + hl('IdentifierName', 'a', 1), }) check_parsing('1.3e-5 + a . 1.2', 0, { @@ -4146,7 +4177,7 @@ describe('Expressions parser', function() }, { hl('Float', '1.3e-5'), hl('BinaryPlus', '+', 1), - hl('Identifier', 'a', 1), + hl('IdentifierName', 'a', 1), hl('Concat', '.', 1), hl('Number', '1', 1), hl('ConcatOrSubscript', '.'), @@ -4196,7 +4227,7 @@ describe('Expressions parser', function() }, }, }, { - hl('Identifier', 'a'), + hl('IdentifierName', 'a'), hl('ConcatOrSubscript', '.'), hl('IdentifierKey', '1'), hl('ConcatOrSubscript', '.'), @@ -4221,7 +4252,7 @@ describe('Expressions parser', function() }, }, }, { - hl('Identifier', 'a'), + hl('IdentifierName', 'a'), hl('Concat', '.', 1), hl('Number', '1', 1), hl('ConcatOrSubscript', '.'), @@ -4251,10 +4282,10 @@ describe('Expressions parser', function() }, }, { hl('UnaryPlus', '+'), - hl('Identifier', 'a'), + hl('IdentifierName', 'a'), hl('Concat', '.', 1), hl('UnaryPlus', '+', 1), - hl('Identifier', 'b'), + hl('IdentifierName', 'b'), }) check_parsing('a. b', 0, { @@ -4269,9 +4300,9 @@ describe('Expressions parser', function() }, }, }, { - hl('Identifier', 'a'), + hl('IdentifierName', 'a'), hl('ConcatOrSubscript', '.'), - hl('Identifier', 'b', 1), + hl('IdentifierName', 'b', 1), }) check_parsing('a. 1', 0, { @@ -4286,7 +4317,7 @@ describe('Expressions parser', function() }, }, }, { - hl('Identifier', 'a'), + hl('IdentifierName', 'a'), hl('ConcatOrSubscript', '.'), hl('Number', '1', 1), }) @@ -4324,9 +4355,9 @@ describe('Expressions parser', function() msg = 'E15: Expected value, got closing bracket: %.*s', }, }, { - hl('Identifier', 'a'), - hl('Subscript', '['), - hl('InvalidSubscript', ']'), + hl('IdentifierName', 'a'), + hl('SubscriptBracket', '['), + hl('InvalidSubscriptBracket', ']'), }) check_parsing('a[b:]', 0, { -- 01234 @@ -4340,11 +4371,11 @@ describe('Expressions parser', function() }, }, }, { - hl('Identifier', 'a'), - hl('Subscript', '['), + hl('IdentifierName', 'a'), + hl('SubscriptBracket', '['), hl('IdentifierScope', 'b'), hl('IdentifierScopeDelimiter', ':'), - hl('Subscript', ']'), + hl('SubscriptBracket', ']'), }) check_parsing('a[b:c]', 0, { @@ -4359,12 +4390,12 @@ describe('Expressions parser', function() }, }, }, { - hl('Identifier', 'a'), - hl('Subscript', '['), + hl('IdentifierName', 'a'), + hl('SubscriptBracket', '['), hl('IdentifierScope', 'b'), hl('IdentifierScopeDelimiter', ':'), - hl('Identifier', 'c'), - hl('Subscript', ']'), + hl('IdentifierName', 'c'), + hl('SubscriptBracket', ']'), }) check_parsing('a[b : c]', 0, { -- 01234567 @@ -4384,12 +4415,12 @@ describe('Expressions parser', function() }, }, }, { - hl('Identifier', 'a'), - hl('Subscript', '['), - hl('Identifier', 'b'), + hl('IdentifierName', 'a'), + hl('SubscriptBracket', '['), + hl('IdentifierName', 'b'), hl('SubscriptColon', ':', 1), - hl('Identifier', 'c', 1), - hl('Subscript', ']'), + hl('IdentifierName', 'c', 1), + hl('SubscriptBracket', ']'), }) check_parsing('a[: b]', 0, { @@ -4410,11 +4441,11 @@ describe('Expressions parser', function() }, }, }, { - hl('Identifier', 'a'), - hl('Subscript', '['), + hl('IdentifierName', 'a'), + hl('SubscriptBracket', '['), hl('SubscriptColon', ':'), - hl('Identifier', 'b', 1), - hl('Subscript', ']'), + hl('IdentifierName', 'b', 1), + hl('SubscriptBracket', ']'), }) check_parsing('a[b :]', 0, { @@ -4434,11 +4465,11 @@ describe('Expressions parser', function() }, }, }, { - hl('Identifier', 'a'), - hl('Subscript', '['), - hl('Identifier', 'b'), + hl('IdentifierName', 'a'), + hl('SubscriptBracket', '['), + hl('IdentifierName', 'b'), hl('SubscriptColon', ':', 1), - hl('Subscript', ']'), + hl('SubscriptBracket', ']'), }) check_parsing('a[b][c][d](e)(f)(g)', 0, { -- 0123456789012345678 @@ -4483,24 +4514,24 @@ describe('Expressions parser', function() }, }, }, { - hl('Identifier', 'a'), - hl('Subscript', '['), - hl('Identifier', 'b'), - hl('Subscript', ']'), - hl('Subscript', '['), - hl('Identifier', 'c'), - hl('Subscript', ']'), - hl('Subscript', '['), - hl('Identifier', 'd'), - hl('Subscript', ']'), + hl('IdentifierName', 'a'), + hl('SubscriptBracket', '['), + hl('IdentifierName', 'b'), + hl('SubscriptBracket', ']'), + hl('SubscriptBracket', '['), + hl('IdentifierName', 'c'), + hl('SubscriptBracket', ']'), + hl('SubscriptBracket', '['), + hl('IdentifierName', 'd'), + hl('SubscriptBracket', ']'), hl('CallingParenthesis', '('), - hl('Identifier', 'e'), + hl('IdentifierName', 'e'), hl('CallingParenthesis', ')'), hl('CallingParenthesis', '('), - hl('Identifier', 'f'), + hl('IdentifierName', 'f'), hl('CallingParenthesis', ')'), hl('CallingParenthesis', '('), - hl('Identifier', 'g'), + hl('IdentifierName', 'g'), hl('CallingParenthesis', ')'), }) check_parsing('{a}{b}{c}[d][e][f]', 0, { @@ -4556,23 +4587,23 @@ describe('Expressions parser', function() }, }, { hl('Curly', '{'), - hl('Identifier', 'a'), + hl('IdentifierName', 'a'), hl('Curly', '}'), hl('Curly', '{'), - hl('Identifier', 'b'), + hl('IdentifierName', 'b'), hl('Curly', '}'), hl('Curly', '{'), - hl('Identifier', 'c'), + hl('IdentifierName', 'c'), hl('Curly', '}'), - hl('Subscript', '['), - hl('Identifier', 'd'), - hl('Subscript', ']'), - hl('Subscript', '['), - hl('Identifier', 'e'), - hl('Subscript', ']'), - hl('Subscript', '['), - hl('Identifier', 'f'), - hl('Subscript', ']'), + hl('SubscriptBracket', '['), + hl('IdentifierName', 'd'), + hl('SubscriptBracket', ']'), + hl('SubscriptBracket', '['), + hl('IdentifierName', 'e'), + hl('SubscriptBracket', ']'), + hl('SubscriptBracket', '['), + hl('IdentifierName', 'f'), + hl('SubscriptBracket', ']'), }) end) itp('supports list literals', function() @@ -4598,7 +4629,7 @@ describe('Expressions parser', function() }, }, { hl('List', '['), - hl('Identifier', 'a'), + hl('IdentifierName', 'a'), hl('List', ']'), }) @@ -4620,9 +4651,9 @@ describe('Expressions parser', function() }, }, { hl('List', '['), - hl('Identifier', 'a'), + hl('IdentifierName', 'a'), hl('Comma', ','), - hl('Identifier', 'b', 1), + hl('IdentifierName', 'b', 1), hl('List', ']'), }) @@ -4650,11 +4681,11 @@ describe('Expressions parser', function() }, }, { hl('List', '['), - hl('Identifier', 'a'), + hl('IdentifierName', 'a'), hl('Comma', ','), - hl('Identifier', 'b', 1), + hl('IdentifierName', 'b', 1), hl('Comma', ','), - hl('Identifier', 'c', 1), + hl('IdentifierName', 'c', 1), hl('List', ']'), }) @@ -4688,11 +4719,11 @@ describe('Expressions parser', function() }, }, { hl('List', '['), - hl('Identifier', 'a'), + hl('IdentifierName', 'a'), hl('Comma', ','), - hl('Identifier', 'b', 1), + hl('IdentifierName', 'b', 1), hl('Comma', ','), - hl('Identifier', 'c', 1), + hl('IdentifierName', 'c', 1), hl('Comma', ','), hl('List', ']', 1), }) @@ -4732,13 +4763,13 @@ describe('Expressions parser', function() }, }, { hl('List', '['), - hl('Identifier', 'a'), + hl('IdentifierName', 'a'), hl('InvalidColon', ':', 1), - hl('Identifier', 'b', 1), + hl('IdentifierName', 'b', 1), hl('Comma', ','), - hl('Identifier', 'c', 1), + hl('IdentifierName', 'c', 1), hl('InvalidColon', ':', 1), - hl('Identifier', 'd', 1), + hl('IdentifierName', 'd', 1), hl('List', ']'), }) @@ -4770,7 +4801,7 @@ describe('Expressions parser', function() msg = 'E15: Unexpected closing figure brace: %.*s', }, }, { - hl('Identifier', 'a'), + hl('IdentifierName', 'a'), hl('InvalidList', ']'), }) @@ -4814,8 +4845,8 @@ describe('Expressions parser', function() }, { hl('List', '['), hl('List', ']'), - hl('Subscript', '['), - hl('InvalidSubscript', ']'), + hl('SubscriptBracket', '['), + hl('InvalidSubscriptBracket', ']'), }) check_parsing('[', 0, { @@ -6119,13 +6150,13 @@ describe('Expressions parser', function() }, }, }, { - hl('Identifier', 'a'), + hl('IdentifierName', 'a'), hl('And', '&&', 1), - hl('Identifier', 'b', 1), + hl('IdentifierName', 'b', 1), hl('Or', '||', 1), - hl('Identifier', 'c', 1), + hl('IdentifierName', 'c', 1), hl('And', '&&', 1), - hl('Identifier', 'd', 1), + hl('IdentifierName', 'd', 1), }) check_parsing('&& a', 0, { @@ -6145,7 +6176,7 @@ describe('Expressions parser', function() }, }, { hl('InvalidAnd', '&&'), - hl('Identifier', 'a', 1), + hl('IdentifierName', 'a', 1), }) check_parsing('|| a', 0, { @@ -6165,7 +6196,7 @@ describe('Expressions parser', function() }, }, { hl('InvalidOr', '||'), - hl('Identifier', 'a', 1), + hl('IdentifierName', 'a', 1), }) check_parsing('a||', 0, { @@ -6183,7 +6214,7 @@ describe('Expressions parser', function() msg = 'E15: Expected value, got EOC: %.*s', }, }, { - hl('Identifier', 'a'), + hl('IdentifierName', 'a'), hl('Or', '||'), }) @@ -6202,7 +6233,7 @@ describe('Expressions parser', function() msg = 'E15: Expected value, got EOC: %.*s', }, }, { - hl('Identifier', 'a'), + hl('IdentifierName', 'a'), hl('And', '&&'), }) @@ -6280,7 +6311,7 @@ describe('Expressions parser', function() }, }, { hl('NestingParenthesis', '('), - hl('Identifier', 'a'), + hl('IdentifierName', 'a'), hl('Or', '||'), hl('InvalidNestingParenthesis', ')'), }) @@ -6307,7 +6338,7 @@ describe('Expressions parser', function() }, }, { hl('NestingParenthesis', '('), - hl('Identifier', 'a'), + hl('IdentifierName', 'a'), hl('And', '&&'), hl('InvalidNestingParenthesis', ')'), }) @@ -6335,7 +6366,7 @@ describe('Expressions parser', function() }, { hl('NestingParenthesis', '('), hl('InvalidAnd', '&&'), - hl('Identifier', 'a'), + hl('IdentifierName', 'a'), hl('NestingParenthesis', ')'), }) @@ -6362,7 +6393,7 @@ describe('Expressions parser', function() }, { hl('NestingParenthesis', '('), hl('InvalidOr', '||'), - hl('Identifier', 'a'), + hl('IdentifierName', 'a'), hl('NestingParenthesis', ')'), }) end) @@ -6433,7 +6464,7 @@ describe('Expressions parser', function() hl('OptionSigil', '&'), hl('Option', 's'), hl('InvalidColon', ':'), - hl('Identifier', 'opt'), + hl('IdentifierName', 'opt'), }) check_parsing('& ', 0, { @@ -6496,7 +6527,7 @@ describe('Expressions parser', function() }, { hl('OptionSigil', '&'), hl('Option', 'xxx'), - hl('InvalidIdentifier', '_yyy'), + hl('InvalidIdentifierName', '_yyy'), }) check_parsing('(1+&)', 0, { @@ -6588,7 +6619,7 @@ describe('Expressions parser', function() hl('EnvironmentSigil', '$'), hl('Environment', 'g'), hl('InvalidColon', ':'), - hl('Identifier', 'A'), + hl('IdentifierName', 'A'), }) check_parsing('$A', 0, { @@ -7092,7 +7123,7 @@ describe('Expressions parser', function() }, }, { hl('InvalidOr', '|'), - hl('InvalidIdentifier', '\029'), + hl('InvalidIdentifierName', '\029'), }) check_parsing('"\\<', 0, { -- 012 @@ -7137,7 +7168,7 @@ describe('Expressions parser', function() }, }, { hl('InvalidFigureBrace', '}'), - hl('InvalidIdentifier', 'l'), + hl('InvalidIdentifierName', 'l'), }) check_parsing(':?\000\000\000\000\000\000\000', 0, { ast = { @@ -7164,4 +7195,5 @@ describe('Expressions parser', function() hl('InvalidTernary', '?'), }) end) + -- FIXME: check flag effects end) -- cgit From 538af1c90a4ac9928f60e97338869e516def4956 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 29 Oct 2017 22:02:19 +0300 Subject: syntax,viml/parser/expressions: Add missing highlight groups Also adjusts some names. --- test/unit/viml/expressions/parser_spec.lua | 290 ++++++++++++++--------------- 1 file changed, 145 insertions(+), 145 deletions(-) (limited to 'test') diff --git a/test/unit/viml/expressions/parser_spec.lua b/test/unit/viml/expressions/parser_spec.lua index 454fbad236..019fe69046 100644 --- a/test/unit/viml/expressions/parser_spec.lua +++ b/test/unit/viml/expressions/parser_spec.lua @@ -4888,9 +4888,9 @@ describe('Expressions parser', function() 'SingleQuotedString(val="abc"):0:0:\'abc\'', }, }, { - hl('SingleQuotedString', '\''), + hl('SingleQuote', '\''), hl('SingleQuotedBody', 'abc'), - hl('SingleQuotedString', '\''), + hl('SingleQuote', '\''), }) check_parsing('"abc"', 0, { -- 01234 @@ -4898,9 +4898,9 @@ describe('Expressions parser', function() 'DoubleQuotedString(val="abc"):0:0:"abc"', }, }, { - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), hl('DoubleQuotedBody', 'abc'), - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), }) check_parsing('\'\'', 0, { -- 01 @@ -4908,8 +4908,8 @@ describe('Expressions parser', function() 'SingleQuotedString(val=NULL):0:0:\'\'', }, }, { - hl('SingleQuotedString', '\''), - hl('SingleQuotedString', '\''), + hl('SingleQuote', '\''), + hl('SingleQuote', '\''), }) check_parsing('""', 0, { -- 01 @@ -4917,8 +4917,8 @@ describe('Expressions parser', function() 'DoubleQuotedString(val=NULL):0:0:""', }, }, { - hl('DoubleQuotedString', '"'), - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), + hl('DoubleQuote', '"'), }) check_parsing('"', 0, { -- 0 @@ -4930,7 +4930,7 @@ describe('Expressions parser', function() msg = 'E114: Missing double quote: %.*s', }, }, { - hl('InvalidDoubleQuotedString', '"'), + hl('InvalidDoubleQuote', '"'), }) check_parsing('\'', 0, { -- 0 @@ -4942,7 +4942,7 @@ describe('Expressions parser', function() msg = 'E115: Missing single quote: %.*s', }, }, { - hl('InvalidSingleQuotedString', '\''), + hl('InvalidSingleQuote', '\''), }) check_parsing('"a', 0, { -- 01 @@ -4954,7 +4954,7 @@ describe('Expressions parser', function() msg = 'E114: Missing double quote: %.*s', }, }, { - hl('InvalidDoubleQuotedString', '"'), + hl('InvalidDoubleQuote', '"'), hl('InvalidDoubleQuotedBody', 'a'), }) check_parsing('\'a', 0, { @@ -4967,7 +4967,7 @@ describe('Expressions parser', function() msg = 'E115: Missing single quote: %.*s', }, }, { - hl('InvalidSingleQuotedString', '\''), + hl('InvalidSingleQuote', '\''), hl('InvalidSingleQuotedBody', 'a'), }) check_parsing('\'abc\'\'def\'', 0, { @@ -4976,11 +4976,11 @@ describe('Expressions parser', function() 'SingleQuotedString(val="abc\'def"):0:0:\'abc\'\'def\'', }, }, { - hl('SingleQuotedString', '\''), + hl('SingleQuote', '\''), hl('SingleQuotedBody', 'abc'), hl('SingleQuotedQuote', '\'\''), hl('SingleQuotedBody', 'def'), - hl('SingleQuotedString', '\''), + hl('SingleQuote', '\''), }) check_parsing('\'abc\'\'', 0, { -- 012345 @@ -4992,7 +4992,7 @@ describe('Expressions parser', function() msg = 'E115: Missing single quote: %.*s', }, }, { - hl('InvalidSingleQuotedString', '\''), + hl('InvalidSingleQuote', '\''), hl('InvalidSingleQuotedBody', 'abc'), hl('InvalidSingleQuotedQuote', '\'\''), }) @@ -5002,11 +5002,11 @@ describe('Expressions parser', function() 'SingleQuotedString(val="\'\'\'"):0:0:\'\'\'\'\'\'\'\'', }, }, { - hl('SingleQuotedString', '\''), + hl('SingleQuote', '\''), hl('SingleQuotedQuote', '\'\''), hl('SingleQuotedQuote', '\'\''), hl('SingleQuotedQuote', '\'\''), - hl('SingleQuotedString', '\''), + hl('SingleQuote', '\''), }) check_parsing('\'\'\'a\'\'\'\'bc\'', 0, { -- 01234567890 @@ -5015,13 +5015,13 @@ describe('Expressions parser', function() 'SingleQuotedString(val="\'a\'\'bc"):0:0:\'\'\'a\'\'\'\'bc\'', }, }, { - hl('SingleQuotedString', '\''), + hl('SingleQuote', '\''), hl('SingleQuotedQuote', '\'\''), hl('SingleQuotedBody', 'a'), hl('SingleQuotedQuote', '\'\''), hl('SingleQuotedQuote', '\'\''), hl('SingleQuotedBody', 'bc'), - hl('SingleQuotedString', '\''), + hl('SingleQuote', '\''), }) check_parsing('"\\"\\"\\"\\""', 0, { -- 0123456789 @@ -5029,12 +5029,12 @@ describe('Expressions parser', function() 'DoubleQuotedString(val="\\"\\"\\"\\""):0:0:"\\"\\"\\"\\""', }, }, { - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), hl('DoubleQuotedEscape', '\\"'), hl('DoubleQuotedEscape', '\\"'), hl('DoubleQuotedEscape', '\\"'), hl('DoubleQuotedEscape', '\\"'), - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), }) check_parsing('"abc\\"def\\"ghi\\"jkl\\"mno"', 0, { -- 0123456789012345678901234 @@ -5043,7 +5043,7 @@ describe('Expressions parser', function() 'DoubleQuotedString(val="abc\\"def\\"ghi\\"jkl\\"mno"):0:0:"abc\\"def\\"ghi\\"jkl\\"mno"', }, }, { - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), hl('DoubleQuotedBody', 'abc'), hl('DoubleQuotedEscape', '\\"'), hl('DoubleQuotedBody', 'def'), @@ -5053,7 +5053,7 @@ describe('Expressions parser', function() hl('DoubleQuotedBody', 'jkl'), hl('DoubleQuotedEscape', '\\"'), hl('DoubleQuotedBody', 'mno'), - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), }) check_parsing('"\\b\\e\\f\\r\\t\\\\"', 0, { -- 0123456789012345 @@ -5062,14 +5062,14 @@ describe('Expressions parser', function() [[DoubleQuotedString(val="\8\27\12\13\9\\"):0:0:"\b\e\f\r\t\\"]], }, }, { - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), hl('DoubleQuotedEscape', '\\b'), hl('DoubleQuotedEscape', '\\e'), hl('DoubleQuotedEscape', '\\f'), hl('DoubleQuotedEscape', '\\r'), hl('DoubleQuotedEscape', '\\t'), hl('DoubleQuotedEscape', '\\\\'), - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), }) check_parsing('"\\n\n"', 0, { -- 01234 @@ -5077,10 +5077,10 @@ describe('Expressions parser', function() 'DoubleQuotedString(val="\\\n\\\n"):0:0:"\\n\n"', }, }, { - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), hl('DoubleQuotedEscape', '\\n'), hl('DoubleQuotedBody', '\n'), - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), }) check_parsing('"\\x00"', 0, { -- 012345 @@ -5088,9 +5088,9 @@ describe('Expressions parser', function() 'DoubleQuotedString(val="\\0"):0:0:"\\x00"', }, }, { - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), hl('DoubleQuotedEscape', '\\x00'), - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), }) check_parsing('"\\xFF"', 0, { -- 012345 @@ -5098,9 +5098,9 @@ describe('Expressions parser', function() 'DoubleQuotedString(val="\255"):0:0:"\\xFF"', }, }, { - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), hl('DoubleQuotedEscape', '\\xFF'), - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), }) check_parsing('"\\xF"', 0, { -- 012345 @@ -5108,9 +5108,9 @@ describe('Expressions parser', function() 'DoubleQuotedString(val="\\15"):0:0:"\\xF"', }, }, { - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), hl('DoubleQuotedEscape', '\\xF'), - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), }) check_parsing('"\\u00AB"', 0, { -- 01234567 @@ -5118,9 +5118,9 @@ describe('Expressions parser', function() 'DoubleQuotedString(val="«"):0:0:"\\u00AB"', }, }, { - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), hl('DoubleQuotedEscape', '\\u00AB'), - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), }) check_parsing('"\\U000000AB"', 0, { -- 01234567 @@ -5128,9 +5128,9 @@ describe('Expressions parser', function() 'DoubleQuotedString(val="«"):0:0:"\\U000000AB"', }, }, { - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), hl('DoubleQuotedEscape', '\\U000000AB'), - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), }) check_parsing('"\\x"', 0, { -- 0123 @@ -5138,9 +5138,9 @@ describe('Expressions parser', function() 'DoubleQuotedString(val="x"):0:0:"\\x"', }, }, { - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), hl('DoubleQuotedUnknownEscape', '\\x'), - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), }) check_parsing('"\\x', 0, { @@ -5153,7 +5153,7 @@ describe('Expressions parser', function() msg = 'E114: Missing double quote: %.*s', }, }, { - hl('InvalidDoubleQuotedString', '"'), + hl('InvalidDoubleQuote', '"'), hl('InvalidDoubleQuotedUnknownEscape', '\\x'), }) @@ -5167,7 +5167,7 @@ describe('Expressions parser', function() msg = 'E114: Missing double quote: %.*s', }, }, { - hl('InvalidDoubleQuotedString', '"'), + hl('InvalidDoubleQuote', '"'), hl('InvalidDoubleQuotedEscape', '\\xF'), }) @@ -5177,9 +5177,9 @@ describe('Expressions parser', function() 'DoubleQuotedString(val="u"):0:0:"\\u"', }, }, { - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), hl('DoubleQuotedUnknownEscape', '\\u'), - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), }) check_parsing('"\\u', 0, { @@ -5192,7 +5192,7 @@ describe('Expressions parser', function() msg = 'E114: Missing double quote: %.*s', }, }, { - hl('InvalidDoubleQuotedString', '"'), + hl('InvalidDoubleQuote', '"'), hl('InvalidDoubleQuotedUnknownEscape', '\\u'), }) @@ -5206,7 +5206,7 @@ describe('Expressions parser', function() msg = 'E114: Missing double quote: %.*s', }, }, { - hl('InvalidDoubleQuotedString', '"'), + hl('InvalidDoubleQuote', '"'), hl('InvalidDoubleQuotedUnknownEscape', '\\U'), }) @@ -5216,9 +5216,9 @@ describe('Expressions parser', function() 'DoubleQuotedString(val="U"):0:0:"\\U"', }, }, { - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), hl('DoubleQuotedUnknownEscape', '\\U'), - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), }) check_parsing('"\\xFX"', 0, { @@ -5227,10 +5227,10 @@ describe('Expressions parser', function() 'DoubleQuotedString(val="\\15X"):0:0:"\\xFX"', }, }, { - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), hl('DoubleQuotedEscape', '\\xF'), hl('DoubleQuotedBody', 'X'), - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), }) check_parsing('"\\XFX"', 0, { @@ -5239,10 +5239,10 @@ describe('Expressions parser', function() 'DoubleQuotedString(val="\\15X"):0:0:"\\XFX"', }, }, { - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), hl('DoubleQuotedEscape', '\\XF'), hl('DoubleQuotedBody', 'X'), - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), }) check_parsing('"\\xX"', 0, { @@ -5251,10 +5251,10 @@ describe('Expressions parser', function() 'DoubleQuotedString(val="xX"):0:0:"\\xX"', }, }, { - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), hl('DoubleQuotedUnknownEscape', '\\x'), hl('DoubleQuotedBody', 'X'), - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), }) check_parsing('"\\XX"', 0, { @@ -5263,10 +5263,10 @@ describe('Expressions parser', function() 'DoubleQuotedString(val="XX"):0:0:"\\XX"', }, }, { - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), hl('DoubleQuotedUnknownEscape', '\\X'), hl('DoubleQuotedBody', 'X'), - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), }) check_parsing('"\\uX"', 0, { @@ -5275,10 +5275,10 @@ describe('Expressions parser', function() 'DoubleQuotedString(val="uX"):0:0:"\\uX"', }, }, { - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), hl('DoubleQuotedUnknownEscape', '\\u'), hl('DoubleQuotedBody', 'X'), - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), }) check_parsing('"\\UX"', 0, { @@ -5287,10 +5287,10 @@ describe('Expressions parser', function() 'DoubleQuotedString(val="UX"):0:0:"\\UX"', }, }, { - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), hl('DoubleQuotedUnknownEscape', '\\U'), hl('DoubleQuotedBody', 'X'), - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), }) check_parsing('"\\x0X"', 0, { @@ -5299,10 +5299,10 @@ describe('Expressions parser', function() 'DoubleQuotedString(val="\\0X"):0:0:"\\x0X"', }, }, { - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), hl('DoubleQuotedEscape', '\\x0'), hl('DoubleQuotedBody', 'X'), - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), }) check_parsing('"\\X0X"', 0, { @@ -5311,10 +5311,10 @@ describe('Expressions parser', function() 'DoubleQuotedString(val="\\0X"):0:0:"\\X0X"', }, }, { - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), hl('DoubleQuotedEscape', '\\X0'), hl('DoubleQuotedBody', 'X'), - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), }) check_parsing('"\\u0X"', 0, { @@ -5323,10 +5323,10 @@ describe('Expressions parser', function() 'DoubleQuotedString(val="\\0X"):0:0:"\\u0X"', }, }, { - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), hl('DoubleQuotedEscape', '\\u0'), hl('DoubleQuotedBody', 'X'), - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), }) check_parsing('"\\U0X"', 0, { @@ -5335,10 +5335,10 @@ describe('Expressions parser', function() 'DoubleQuotedString(val="\\0X"):0:0:"\\U0X"', }, }, { - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), hl('DoubleQuotedEscape', '\\U0'), hl('DoubleQuotedBody', 'X'), - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), }) check_parsing('"\\x00X"', 0, { @@ -5347,10 +5347,10 @@ describe('Expressions parser', function() 'DoubleQuotedString(val="\\0X"):0:0:"\\x00X"', }, }, { - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), hl('DoubleQuotedEscape', '\\x00'), hl('DoubleQuotedBody', 'X'), - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), }) check_parsing('"\\X00X"', 0, { @@ -5359,10 +5359,10 @@ describe('Expressions parser', function() 'DoubleQuotedString(val="\\0X"):0:0:"\\X00X"', }, }, { - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), hl('DoubleQuotedEscape', '\\X00'), hl('DoubleQuotedBody', 'X'), - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), }) check_parsing('"\\u00X"', 0, { @@ -5371,10 +5371,10 @@ describe('Expressions parser', function() 'DoubleQuotedString(val="\\0X"):0:0:"\\u00X"', }, }, { - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), hl('DoubleQuotedEscape', '\\u00'), hl('DoubleQuotedBody', 'X'), - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), }) check_parsing('"\\U00X"', 0, { @@ -5383,10 +5383,10 @@ describe('Expressions parser', function() 'DoubleQuotedString(val="\\0X"):0:0:"\\U00X"', }, }, { - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), hl('DoubleQuotedEscape', '\\U00'), hl('DoubleQuotedBody', 'X'), - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), }) check_parsing('"\\u000X"', 0, { @@ -5395,10 +5395,10 @@ describe('Expressions parser', function() 'DoubleQuotedString(val="\\0X"):0:0:"\\u000X"', }, }, { - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), hl('DoubleQuotedEscape', '\\u000'), hl('DoubleQuotedBody', 'X'), - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), }) check_parsing('"\\U000X"', 0, { @@ -5407,10 +5407,10 @@ describe('Expressions parser', function() 'DoubleQuotedString(val="\\0X"):0:0:"\\U000X"', }, }, { - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), hl('DoubleQuotedEscape', '\\U000'), hl('DoubleQuotedBody', 'X'), - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), }) check_parsing('"\\u0000X"', 0, { @@ -5419,10 +5419,10 @@ describe('Expressions parser', function() 'DoubleQuotedString(val="\\0X"):0:0:"\\u0000X"', }, }, { - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), hl('DoubleQuotedEscape', '\\u0000'), hl('DoubleQuotedBody', 'X'), - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), }) check_parsing('"\\U0000X"', 0, { @@ -5431,10 +5431,10 @@ describe('Expressions parser', function() 'DoubleQuotedString(val="\\0X"):0:0:"\\U0000X"', }, }, { - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), hl('DoubleQuotedEscape', '\\U0000'), hl('DoubleQuotedBody', 'X'), - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), }) check_parsing('"\\U00000X"', 0, { @@ -5443,10 +5443,10 @@ describe('Expressions parser', function() 'DoubleQuotedString(val="\\0X"):0:0:"\\U00000X"', }, }, { - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), hl('DoubleQuotedEscape', '\\U00000'), hl('DoubleQuotedBody', 'X'), - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), }) check_parsing('"\\U000000X"', 0, { @@ -5456,10 +5456,10 @@ describe('Expressions parser', function() 'DoubleQuotedString(val="\\0X"):0:0:"\\U000000X"', }, }, { - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), hl('DoubleQuotedEscape', '\\U000000'), hl('DoubleQuotedBody', 'X'), - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), }) check_parsing('"\\U0000000X"', 0, { @@ -5469,10 +5469,10 @@ describe('Expressions parser', function() 'DoubleQuotedString(val="\\0X"):0:0:"\\U0000000X"', }, }, { - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), hl('DoubleQuotedEscape', '\\U0000000'), hl('DoubleQuotedBody', 'X'), - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), }) check_parsing('"\\U00000000X"', 0, { @@ -5482,10 +5482,10 @@ describe('Expressions parser', function() 'DoubleQuotedString(val="\\0X"):0:0:"\\U00000000X"', }, }, { - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), hl('DoubleQuotedEscape', '\\U00000000'), hl('DoubleQuotedBody', 'X'), - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), }) check_parsing('"\\x000X"', 0, { @@ -5494,10 +5494,10 @@ describe('Expressions parser', function() 'DoubleQuotedString(val="\\0000X"):0:0:"\\x000X"', }, }, { - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), hl('DoubleQuotedEscape', '\\x00'), hl('DoubleQuotedBody', '0X'), - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), }) check_parsing('"\\X000X"', 0, { @@ -5506,10 +5506,10 @@ describe('Expressions parser', function() 'DoubleQuotedString(val="\\0000X"):0:0:"\\X000X"', }, }, { - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), hl('DoubleQuotedEscape', '\\X00'), hl('DoubleQuotedBody', '0X'), - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), }) check_parsing('"\\u00000X"', 0, { @@ -5518,10 +5518,10 @@ describe('Expressions parser', function() 'DoubleQuotedString(val="\\0000X"):0:0:"\\u00000X"', }, }, { - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), hl('DoubleQuotedEscape', '\\u0000'), hl('DoubleQuotedBody', '0X'), - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), }) check_parsing('"\\U000000000X"', 0, { @@ -5531,10 +5531,10 @@ describe('Expressions parser', function() 'DoubleQuotedString(val="\\0000X"):0:0:"\\U000000000X"', }, }, { - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), hl('DoubleQuotedEscape', '\\U00000000'), hl('DoubleQuotedBody', '0X'), - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), }) check_parsing('"\\0"', 0, { @@ -5543,9 +5543,9 @@ describe('Expressions parser', function() 'DoubleQuotedString(val="\\0"):0:0:"\\0"', }, }, { - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), hl('DoubleQuotedEscape', '\\0'), - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), }) check_parsing('"\\00"', 0, { @@ -5554,9 +5554,9 @@ describe('Expressions parser', function() 'DoubleQuotedString(val="\\0"):0:0:"\\00"', }, }, { - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), hl('DoubleQuotedEscape', '\\00'), - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), }) check_parsing('"\\000"', 0, { @@ -5565,9 +5565,9 @@ describe('Expressions parser', function() 'DoubleQuotedString(val="\\0"):0:0:"\\000"', }, }, { - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), hl('DoubleQuotedEscape', '\\000'), - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), }) check_parsing('"\\0000"', 0, { @@ -5576,10 +5576,10 @@ describe('Expressions parser', function() 'DoubleQuotedString(val="\\0000"):0:0:"\\0000"', }, }, { - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), hl('DoubleQuotedEscape', '\\000'), hl('DoubleQuotedBody', '0'), - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), }) check_parsing('"\\8"', 0, { @@ -5588,9 +5588,9 @@ describe('Expressions parser', function() 'DoubleQuotedString(val="8"):0:0:"\\8"', }, }, { - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), hl('DoubleQuotedUnknownEscape', '\\8'), - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), }) check_parsing('"\\08"', 0, { @@ -5599,10 +5599,10 @@ describe('Expressions parser', function() 'DoubleQuotedString(val="\\0008"):0:0:"\\08"', }, }, { - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), hl('DoubleQuotedEscape', '\\0'), hl('DoubleQuotedBody', '8'), - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), }) check_parsing('"\\008"', 0, { @@ -5611,10 +5611,10 @@ describe('Expressions parser', function() 'DoubleQuotedString(val="\\0008"):0:0:"\\008"', }, }, { - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), hl('DoubleQuotedEscape', '\\00'), hl('DoubleQuotedBody', '8'), - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), }) check_parsing('"\\0008"', 0, { @@ -5623,10 +5623,10 @@ describe('Expressions parser', function() 'DoubleQuotedString(val="\\0008"):0:0:"\\0008"', }, }, { - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), hl('DoubleQuotedEscape', '\\000'), hl('DoubleQuotedBody', '8'), - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), }) check_parsing('"\\777"', 0, { @@ -5635,9 +5635,9 @@ describe('Expressions parser', function() 'DoubleQuotedString(val="\255"):0:0:"\\777"', }, }, { - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), hl('DoubleQuotedEscape', '\\777'), - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), }) check_parsing('"\\050"', 0, { @@ -5646,9 +5646,9 @@ describe('Expressions parser', function() 'DoubleQuotedString(val="\40"):0:0:"\\050"', }, }, { - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), hl('DoubleQuotedEscape', '\\050'), - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), }) check_parsing('"\\"', 0, { @@ -5657,9 +5657,9 @@ describe('Expressions parser', function() 'DoubleQuotedString(val="\\21"):0:0:"\\"', }, }, { - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), hl('DoubleQuotedEscape', '\\'), - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), }) check_parsing('"\\<', 0, { @@ -5672,7 +5672,7 @@ describe('Expressions parser', function() msg = 'E114: Missing double quote: %.*s', }, }, { - hl('InvalidDoubleQuotedString', '"'), + hl('InvalidDoubleQuote', '"'), hl('InvalidDoubleQuotedUnknownEscape', '\\<'), }) @@ -5682,9 +5682,9 @@ describe('Expressions parser', function() 'DoubleQuotedString(val="<"):0:0:"\\<"', }, }, { - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), hl('DoubleQuotedUnknownEscape', '\\<'), - hl('DoubleQuotedString', '"'), + hl('DoubleQuote', '"'), }) check_parsing('"\\ Date: Mon, 30 Oct 2017 01:48:32 +0300 Subject: tests: Fix testlint errors --- test/functional/ui/cmdline_highlight_spec.lua | 4 ++++ test/helpers.lua | 4 ++-- test/unit/helpers.lua | 6 +++--- test/unit/viml/expressions/lexer_spec.lua | 3 +-- test/unit/viml/expressions/parser_spec.lua | 9 ++------- test/unit/viml/helpers.lua | 6 ------ 6 files changed, 12 insertions(+), 20 deletions(-) (limited to 'test') diff --git a/test/functional/ui/cmdline_highlight_spec.lua b/test/functional/ui/cmdline_highlight_spec.lua index 60a4a815e7..b16b4a5602 100644 --- a/test/functional/ui/cmdline_highlight_spec.lua +++ b/test/functional/ui/cmdline_highlight_spec.lua @@ -898,4 +898,8 @@ describe('Expressions coloring support', function() ={NUM:1}^ | ]]) end) + -- FIXME: Test expr coloring when using -u NORC and -u NONE. + -- FIXME: Test all highlight groups, using long expression. + -- FIXME: Test different ways of triggering expression highlighting (:=, + -- i=, :e, "=). end) diff --git a/test/helpers.lua b/test/helpers.lua index 83e78ba059..10f2f80191 100644 --- a/test/helpers.lua +++ b/test/helpers.lua @@ -376,8 +376,8 @@ local function format_string(fmt, ...) return args[i] end local ret = fmt:gsub('%%[0-9*]*%.?[0-9*]*[cdEefgGiouXxqsr%%]', function(match) - local subfmt = match:gsub('%*', function(match) - return getarg() + local subfmt = match:gsub('%*', function() + return tostring(getarg()) end) local arg = nil if subfmt:sub(-1) ~= '%' then diff --git a/test/unit/helpers.lua b/test/unit/helpers.lua index 68ce9eed62..96aa505739 100644 --- a/test/unit/helpers.lua +++ b/test/unit/helpers.lua @@ -791,7 +791,7 @@ local function kvi_new(ct) return kvi_init(ffi.new(ct)) end -local function make_enum_conv_tab(lib, values, skip_pref, set_cb) +local function make_enum_conv_tab(m, values, skip_pref, set_cb) child_call_once(function() local ret = {} for _, v in ipairs(values) do @@ -799,7 +799,7 @@ local function make_enum_conv_tab(lib, values, skip_pref, set_cb) if v:sub(1, #skip_pref) == skip_pref then str_v = v:sub(#skip_pref + 1) end - ret[tonumber(lib[v])] = str_v + ret[tonumber(m[v])] = str_v end set_cb(ret) end) @@ -837,7 +837,7 @@ local module = { child_cleanup_once = child_cleanup_once, sc = sc, conv_enum = conv_enum, - array_size = array_sive, + array_size = array_size, kvi_destroy = kvi_destroy, kvi_size = kvi_size, kvi_init = kvi_init, diff --git a/test/unit/viml/expressions/lexer_spec.lua b/test/unit/viml/expressions/lexer_spec.lua index 5910468017..d4ec870a4e 100644 --- a/test/unit/viml/expressions/lexer_spec.lua +++ b/test/unit/viml/expressions/lexer_spec.lua @@ -10,7 +10,6 @@ local ffi = helpers.ffi local eq = helpers.eq local conv_ccs = viml_helpers.conv_ccs -local pline2lua = viml_helpers.pline2lua local new_pstate = viml_helpers.new_pstate local intchar2lua = viml_helpers.intchar2lua local conv_cmp_type = viml_helpers.conv_cmp_type @@ -183,7 +182,7 @@ describe('Expressions lexer', function() end local function simple_test(pstate_arg, exp_type, exp_len, exp) local pstate = new_pstate(pstate_arg) - local exp = shallowcopy(exp) + exp = shallowcopy(exp) exp.type = exp_type exp.len = exp_len or #(pstate_arg[0]) exp.start = { col = 0, line = 0 } diff --git a/test/unit/viml/expressions/parser_spec.lua b/test/unit/viml/expressions/parser_spec.lua index 019fe69046..df69c60dd0 100644 --- a/test/unit/viml/expressions/parser_spec.lua +++ b/test/unit/viml/expressions/parser_spec.lua @@ -14,7 +14,6 @@ local ffi = helpers.ffi local eq = helpers.eq local conv_ccs = viml_helpers.conv_ccs -local pline2lua = viml_helpers.pline2lua local new_pstate = viml_helpers.new_pstate local intchar2lua = viml_helpers.intchar2lua local conv_cmp_type = viml_helpers.conv_cmp_type @@ -174,10 +173,7 @@ local function eastnode2lua(pstate, eastnode, checked_nodes) ffi.string(eastnode.data.env.ident, eastnode.data.env.ident_len)) end ret_str = typ .. ':' .. ret_str - local can_simplify = true - for k, v in pairs(ret) do - can_simplify = false - end + local can_simplify = not ret.children if can_simplify then ret = ret_str else @@ -218,12 +214,11 @@ local function phl2lua(pstate) pstate, chunk.start, chunk.end_col - chunk.start.col, { group = ffi.string(chunk.group), }) - chunk_str = ('%s:%u:%u:%s'):format( + ret[i + 1] = ('%s:%u:%u:%s'):format( chunk_tbl.group, chunk_tbl.start.line, chunk_tbl.start.col, chunk_tbl.str) - ret[i + 1] = chunk_str end return ret end diff --git a/test/unit/viml/helpers.lua b/test/unit/viml/helpers.lua index c965cacb29..70949e8278 100644 --- a/test/unit/viml/helpers.lua +++ b/test/unit/viml/helpers.lua @@ -5,7 +5,6 @@ local cimport = helpers.cimport local kvi_new = helpers.kvi_new local kvi_init = helpers.kvi_init local conv_enum = helpers.conv_enum -local child_call_once = helpers.child_call_once local make_enum_conv_tab = helpers.make_enum_conv_tab local lib = cimport('./src/nvim/viml/parser/expressions.h') @@ -33,11 +32,6 @@ local function new_pstate(strings) ret_pline.size = size ret_pline.allocated = false end - local pline_init = { - data = nil, - size = 0, - allocated = false, - } local state = { reader = { get_line = get_line, -- cgit From d98199de9c2969d430ba489187ffa02d8c489dea Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 2 Nov 2017 10:44:20 +0300 Subject: charset: Refactor vim_str2nr --- test/symbolic/klee/nvim/charset.c | 129 ++++++++++++++++++++++------------ test/unit/charset/vim_str2nr_spec.lua | 1 + 2 files changed, 85 insertions(+), 45 deletions(-) create mode 100644 test/unit/charset/vim_str2nr_spec.lua (limited to 'test') diff --git a/test/symbolic/klee/nvim/charset.c b/test/symbolic/klee/nvim/charset.c index f3a218949f..77f690f08d 100644 --- a/test/symbolic/klee/nvim/charset.c +++ b/test/symbolic/klee/nvim/charset.c @@ -31,55 +31,83 @@ void vim_str2nr(const char_u *const start, int *const prep, int *const len, #define STRING_ENDED(ptr) \ (!(maxlen == 0 || (int)((ptr) - (const char *)start) < maxlen)) int pre = 0; // default is decimal - bool negative = false; + const bool negative = (ptr[0] == '-'); + uvarnumber_T un = 0; - if (ptr[0] == '-') { - negative = true; + if (negative) { ptr++; } - // Recognize hex, octal and bin. - if ((what & (STR2NR_HEX|STR2NR_OCT|STR2NR_BIN)) - && !STRING_ENDED(ptr + 1) - && ptr[0] == '0' && ptr[1] != '8' && ptr[1] != '9') { + if (what & STR2NR_FORCE) { + // When forcing main consideration is skipping the prefix. Octal and decimal + // numbers have no prefixes to skip. pre is not set. + switch ((unsigned)what & (~(unsigned)STR2NR_FORCE)) { + case STR2NR_HEX: { + if (!STRING_ENDED(ptr + 2) + && ptr[0] == '0' + && (ptr[1] == 'x' || ptr[1] == 'X') + && ascii_isxdigit(ptr[2])) { + ptr += 2; + } + goto vim_str2nr_hex; + } + case STR2NR_BIN: { + if (!STRING_ENDED(ptr + 2) + && ptr[0] == '0' + && (ptr[1] == 'b' || ptr[1] == 'B') + && ascii_isbdigit(ptr[2])) { + ptr += 2; + } + goto vim_str2nr_bin; + } + case STR2NR_OCT: { + goto vim_str2nr_oct; + } + case 0: { + goto vim_str2nr_dec; + } + default: { + assert(false); + } + } + } else if ((what & (STR2NR_HEX|STR2NR_OCT|STR2NR_BIN)) + && !STRING_ENDED(ptr + 1) + && ptr[0] == '0' && ptr[1] != '8' && ptr[1] != '9') { pre = ptr[1]; - + // Detect hexadecimal: 0x or 0X follwed by hex digit if ((what & STR2NR_HEX) && !STRING_ENDED(ptr + 2) && (pre == 'X' || pre == 'x') && ascii_isxdigit(ptr[2])) { - // hexadecimal ptr += 2; - } else if ((what & STR2NR_BIN) - && !STRING_ENDED(ptr + 2) - && (pre == 'B' || pre == 'b') - && ascii_isbdigit(ptr[2])) { - // binary + goto vim_str2nr_hex; + } + // Detect binary: 0b or 0B follwed by 0 or 1 + if ((what & STR2NR_BIN) + && !STRING_ENDED(ptr + 2) + && (pre == 'B' || pre == 'b') + && ascii_isbdigit(ptr[2])) { ptr += 2; - } else { - // decimal or octal, default is decimal - pre = 0; - - if (what & STR2NR_OCT - && !STRING_ENDED(ptr + 1) - && ('0' <= ptr[1] && ptr[1] <= '7')) { - // Assume octal now: what we already know is that string starts with - // zero and some octal digit. - pre = '0'; - // Don’t interpret "0", "008" or "0129" as octal. - for (int i = 2; !STRING_ENDED(ptr + i) && ascii_isdigit(ptr[i]); i++) { - if (ptr[i] > '7') { - // Can’t be octal. - pre = 0; - break; - } - } + goto vim_str2nr_bin; + } + // Detect octal number: zero followed by octal digits without '8' or '9' + pre = 0; + if (!(what & STR2NR_OCT)) { + goto vim_str2nr_dec; + } + for (int i = 2; !STRING_ENDED(ptr + i) && ascii_isdigit(ptr[i]); i++) { + if (ptr[i] > '7') { + goto vim_str2nr_dec; } } + pre = '0'; + goto vim_str2nr_oct; + } else { + goto vim_str2nr_dec; } // Do the string-to-numeric conversion "manually" to avoid sscanf quirks. - uvarnumber_T un = 0; + assert(false); // Should’ve used goto earlier. #define PARSE_NUMBER(base, cond, conv) \ do { \ while (!STRING_ENDED(ptr) && (cond)) { \ @@ -92,18 +120,29 @@ void vim_str2nr(const char_u *const start, int *const prep, int *const len, ptr++; \ } \ } while (0) - if (pre == 'B' || pre == 'b' || what == (STR2NR_BIN|STR2NR_FORCE)) { - // Binary number. - PARSE_NUMBER(2, (*ptr == '0' || *ptr == '1'), (*ptr - '0')); - } else if (pre == '0' || what == (STR2NR_OCT|STR2NR_FORCE)) { - // Octal number. - PARSE_NUMBER(8, ('0' <= *ptr && *ptr <= '7'), (*ptr - '0')); - } else if (pre == 'X' || pre == 'x' || what == (STR2NR_HEX|STR2NR_FORCE)) { - // Hexadecimal number. - PARSE_NUMBER(16, (ascii_isxdigit(*ptr)), (hex2nr(*ptr))); - } else { - // Decimal number. - PARSE_NUMBER(10, (ascii_isdigit(*ptr)), (*ptr - '0')); + switch (pre) { + case 'b': + case 'B': { +vim_str2nr_bin: + PARSE_NUMBER(2, (*ptr == '0' || *ptr == '1'), (*ptr - '0')); + break; + } + case '0': { +vim_str2nr_oct: + PARSE_NUMBER(8, ('0' <= *ptr && *ptr <= '7'), (*ptr - '0')); + break; + } + case 0: { +vim_str2nr_dec: + PARSE_NUMBER(10, (ascii_isdigit(*ptr)), (*ptr - '0')); + break; + } + case 'x': + case 'X': { +vim_str2nr_hex: + PARSE_NUMBER(16, (ascii_isxdigit(*ptr)), (hex2nr(*ptr))); + break; + } } #undef PARSE_NUMBER diff --git a/test/unit/charset/vim_str2nr_spec.lua b/test/unit/charset/vim_str2nr_spec.lua new file mode 100644 index 0000000000..cfbc77bc2a --- /dev/null +++ b/test/unit/charset/vim_str2nr_spec.lua @@ -0,0 +1 @@ +-- FIXME -- cgit From b9d5aea073521f3278bea257be306b7ac2b8d83c Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 3 Nov 2017 11:38:59 +0300 Subject: api/vim: Create part of nvim_parse_expression function --- test/functional/api/vim_spec.lua | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'test') diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua index b849304d45..6b44698638 100644 --- a/test/functional/api/vim_spec.lua +++ b/test/functional/api/vim_spec.lua @@ -710,4 +710,9 @@ describe('api', function() ok(err:match(': Wrong type for argument 1, expecting String') ~= nil) end) + describe('nvim_parse_expression', function() + -- FIXME + -- FIXME Test error + end) + end) -- cgit From 7bc6de75263f58c6c4f999bc86a6454ae9f28b80 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 5 Nov 2017 02:41:44 +0300 Subject: api/vim,functests: Add tests for nvim_parse_expression, fix found bugs --- test/functional/api/vim_spec.lua | 6868 ++++++++++++++++++++++++++++ test/helpers.lua | 6 + test/unit/viml/expressions/lexer_spec.lua | 2 +- test/unit/viml/expressions/parser_spec.lua | 5 +- test/unit/viml/helpers.lua | 5 - 5 files changed, 6878 insertions(+), 8 deletions(-) (limited to 'test') diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua index 6b44698638..bb7785657d 100644 --- a/test/functional/api/vim_spec.lua +++ b/test/functional/api/vim_spec.lua @@ -1,5 +1,7 @@ local helpers = require('test.functional.helpers')(after_each) local Screen = require('test.functional.ui.screen') +local global_helpers = require('test.helpers') + local NIL = helpers.NIL local clear, nvim, eq, neq = helpers.clear, helpers.nvim, helpers.eq, helpers.neq local ok, nvim_async, feed = helpers.ok, helpers.nvim_async, helpers.feed @@ -10,6 +12,9 @@ local request = helpers.request local meth_pcall = helpers.meth_pcall local command = helpers.command +local intchar2lua = global_helpers.intchar2lua +local format_string = global_helpers.format_string + describe('api', function() before_each(clear) @@ -711,8 +716,6871 @@ describe('api', function() end) describe('nvim_parse_expression', function() + local function simplify_east_api_node(line, east_api_node) + if east_api_node.children then + for k, v in pairs(east_api_node.children) do + east_api_node.children[k] = simplify_east_api_node(line, v) + end + end + local typ = east_api_node.type + if typ == 'Register' then + typ = typ .. ('(name=%s)'):format( + tostring(intchar2lua(east_api_node.name))) + east_api_node.name = nil + elseif typ == 'PlainIdentifier' then + typ = typ .. ('(scope=%s,ident=%s)'):format( + tostring(intchar2lua(east_api_node.scope)), east_api_node.ident) + east_api_node.scope = nil + east_api_node.ident = nil + elseif typ == 'PlainKey' then + typ = typ .. ('(key=%s)'):format(east_api_node.ident) + east_api_node.ident = nil + elseif typ == 'Comparison' then + typ = typ .. ('(type=%s,inv=%u,ccs=%s)'):format( + east_api_node.cmp_type, east_api_node.invert and 1 or 0, + east_api_node.ccs_strategy) + east_api_node.ccs_strategy = nil + east_api_node.cmp_type = nil + east_api_node.invert = nil + elseif typ == 'Integer' then + typ = typ .. ('(val=%u)'):format(east_api_node.ivalue) + east_api_node.ivalue = nil + elseif typ == 'Float' then + typ = typ .. ('(val=%e)'):format(east_api_node.fvalue) + east_api_node.fvalue = nil + elseif typ == 'SingleQuotedString' or typ == 'DoubleQuotedString' then + typ = format_string('%s(val=%q)', typ, east_api_node.svalue) + east_api_node.svalue = nil + elseif typ == 'Option' then + typ = ('%s(scope=%s,ident=%s)'):format( + typ, + tostring(intchar2lua(east_api_node.scope)), + east_api_node.ident) + east_api_node.ident = nil + east_api_node.scope = nil + elseif typ == 'Environment' then + typ = ('%s(ident=%s)'):format(typ, east_api_node.ident) + east_api_node.ident = nil + end + typ = ('%s:%u:%u:%s'):format( + typ, east_api_node.start[1], east_api_node.start[2], + line:sub(east_api_node.start[2] + 1, + east_api_node.start[2] + 1 + east_api_node.len - 1)) + assert(east_api_node.start[2] + east_api_node.len - 1 <= #line) + for k, _ in pairs(east_api_node.start) do + assert(({true, true})[k]) + end + east_api_node.start = nil + east_api_node.type = nil + east_api_node.len = nil + local can_simplify = true + for _, _ in pairs(east_api_node) do + if can_simplify then can_simplify = false end + end + if can_simplify then + return typ + else + east_api_node[1] = typ + return east_api_node + end + end + local function simplify_east_api(line, east_api) + if east_api.error then + east_api.err = east_api.error + east_api.error = nil + east_api.err.msg = east_api.err.message + east_api.err.message = nil + end + if east_api.ast then + east_api.ast = {simplify_east_api_node(line, east_api.ast)} + end + return east_api + end + local function simplify_east_hl(line, east_hl) + for i, v in ipairs(east_hl) do + east_hl[i] = ('%s:%u:%u:%s'):format( + v[4], + v[1], + v[2], + line:sub(v[2] + 1, v[3])) + end + return east_hl + end + local function check_parsing(str, flags, exp_ast, exp_highlighting_fs) + if flags == 0 then + flags = "" + end + + local err, msg = pcall(function() + local east_api = meths.parse_expression(str, flags, true) + local east_hl = east_api.highlight + east_api.highlight = nil + local ast = simplify_east_api(str, east_api) + local hls = simplify_east_hl(str, east_hl) + eq(exp_ast, ast) + if exp_highlighting_fs then + local exp_highlighting = {} + local next_col = 0 + for i, h in ipairs(exp_highlighting_fs) do + exp_highlighting[i], next_col = h(next_col) + end + eq(exp_highlighting, hls) + end + end) + if not err then + msg = format_string('Error while processing test (%r, %s):\n%s', + str, flags, msg) + error(msg) + end + end + local function hl(group, str, shift) + return function(next_col) + local col = next_col + (shift or 0) + return (('%s:%u:%u:%s'):format( + 'NVim' .. group, + 0, + col, + str)), (col + #str) + end + end + it('works with + and @a', function() + check_parsing('@a', 0, { + ast = { + 'Register(name=a):0:0:@a', + }, + }, { + hl('Register', '@a'), + }) + check_parsing('+@a', 0, { + ast = { + { + 'UnaryPlus:0:0:+', + children = { + 'Register(name=a):0:1:@a', + }, + }, + }, + }, { + hl('UnaryPlus', '+'), + hl('Register', '@a'), + }) + check_parsing('@a+@b', 0, { + ast = { + { + 'BinaryPlus:0:2:+', + children = { + 'Register(name=a):0:0:@a', + 'Register(name=b):0:3:@b', + }, + }, + }, + }, { + hl('Register', '@a'), + hl('BinaryPlus', '+'), + hl('Register', '@b'), + }) + check_parsing('@a+@b+@c', 0, { + ast = { + { + 'BinaryPlus:0:5:+', + children = { + { + 'BinaryPlus:0:2:+', + children = { + 'Register(name=a):0:0:@a', + 'Register(name=b):0:3:@b', + }, + }, + 'Register(name=c):0:6:@c', + }, + }, + }, + }, { + hl('Register', '@a'), + hl('BinaryPlus', '+'), + hl('Register', '@b'), + hl('BinaryPlus', '+'), + hl('Register', '@c'), + }) + check_parsing('+@a+@b', 0, { + ast = { + { + 'BinaryPlus:0:3:+', + children = { + { + 'UnaryPlus:0:0:+', + children = { + 'Register(name=a):0:1:@a', + }, + }, + 'Register(name=b):0:4:@b', + }, + }, + }, + }, { + hl('UnaryPlus', '+'), + hl('Register', '@a'), + hl('BinaryPlus', '+'), + hl('Register', '@b'), + }) + check_parsing('+@a++@b', 0, { + ast = { + { + 'BinaryPlus:0:3:+', + children = { + { + 'UnaryPlus:0:0:+', + children = { + 'Register(name=a):0:1:@a', + }, + }, + { + 'UnaryPlus:0:4:+', + children = { + 'Register(name=b):0:5:@b', + }, + }, + }, + }, + }, + }, { + hl('UnaryPlus', '+'), + hl('Register', '@a'), + hl('BinaryPlus', '+'), + hl('UnaryPlus', '+'), + hl('Register', '@b'), + }) + check_parsing('@a@b', 0, { + ast = { + { + 'OpMissing:0:2:', + children = { + 'Register(name=a):0:0:@a', + 'Register(name=b):0:2:@b', + }, + }, + }, + err = { + arg = '@b', + msg = 'E15: Missing operator: %.*s', + }, + }, { + hl('Register', '@a'), + hl('InvalidRegister', '@b'), + }) + check_parsing(' @a \t @b', 0, { + ast = { + { + 'OpMissing:0:3:', + children = { + 'Register(name=a):0:0: @a', + 'Register(name=b):0:3: \t @b', + }, + }, + }, + err = { + arg = '@b', + msg = 'E15: Missing operator: %.*s', + }, + }, { + hl('Register', '@a', 1), + hl('InvalidSpacing', ' \t '), + hl('Register', '@b'), + }) + check_parsing('+', 0, { + ast = { + 'UnaryPlus:0:0:+', + }, + err = { + arg = '', + msg = 'E15: Expected value, got EOC: %.*s', + }, + }, { + hl('UnaryPlus', '+'), + }) + check_parsing(' +', 0, { + ast = { + 'UnaryPlus:0:0: +', + }, + err = { + arg = '', + msg = 'E15: Expected value, got EOC: %.*s', + }, + }, { + hl('UnaryPlus', '+', 1), + }) + check_parsing('@a+ ', 0, { + ast = { + { + 'BinaryPlus:0:2:+', + children = { + 'Register(name=a):0:0:@a', + }, + }, + }, + err = { + arg = '', + msg = 'E15: Expected value, got EOC: %.*s', + }, + }, { + hl('Register', '@a'), + hl('BinaryPlus', '+'), + }) + end) + it('works with @a, + and parenthesis', function() + check_parsing('(@a)', 0, { + ast = { + { + 'Nested:0:0:(', + children = { + 'Register(name=a):0:1:@a', + }, + }, + }, + }, { + hl('NestingParenthesis', '('), + hl('Register', '@a'), + hl('NestingParenthesis', ')'), + }) + check_parsing('()', 0, { + ast = { + { + 'Nested:0:0:(', + children = { + 'Missing:0:1:', + }, + }, + }, + err = { + arg = ')', + msg = 'E15: Expected value, got parenthesis: %.*s', + }, + }, { + hl('NestingParenthesis', '('), + hl('InvalidNestingParenthesis', ')'), + }) + check_parsing(')', 0, { + ast = { + { + 'Nested:0:0:', + children = { + 'Missing:0:0:', + }, + }, + }, + err = { + arg = ')', + msg = 'E15: Expected value, got parenthesis: %.*s', + }, + }, { + hl('InvalidNestingParenthesis', ')'), + }) + check_parsing('+)', 0, { + ast = { + { + 'Nested:0:1:', + children = { + { + 'UnaryPlus:0:0:+', + children = { + 'Missing:0:1:', + }, + }, + }, + }, + }, + err = { + arg = ')', + msg = 'E15: Expected value, got parenthesis: %.*s', + }, + }, { + hl('UnaryPlus', '+'), + hl('InvalidNestingParenthesis', ')'), + }) + check_parsing('+@a(@b)', 0, { + ast = { + { + 'UnaryPlus:0:0:+', + children = { + { + 'Call:0:3:(', + children = { + 'Register(name=a):0:1:@a', + 'Register(name=b):0:4:@b', + }, + }, + }, + }, + }, + }, { + hl('UnaryPlus', '+'), + hl('Register', '@a'), + hl('CallingParenthesis', '('), + hl('Register', '@b'), + hl('CallingParenthesis', ')'), + }) + check_parsing('@a+@b(@c)', 0, { + ast = { + { + 'BinaryPlus:0:2:+', + children = { + 'Register(name=a):0:0:@a', + { + 'Call:0:5:(', + children = { + 'Register(name=b):0:3:@b', + 'Register(name=c):0:6:@c', + }, + }, + }, + }, + }, + }, { + hl('Register', '@a'), + hl('BinaryPlus', '+'), + hl('Register', '@b'), + hl('CallingParenthesis', '('), + hl('Register', '@c'), + hl('CallingParenthesis', ')'), + }) + check_parsing('@a()', 0, { + ast = { + { + 'Call:0:2:(', + children = { + 'Register(name=a):0:0:@a', + }, + }, + }, + }, { + hl('Register', '@a'), + hl('CallingParenthesis', '('), + hl('CallingParenthesis', ')'), + }) + check_parsing('@a ()', 0, { + ast = { + { + 'OpMissing:0:2:', + children = { + 'Register(name=a):0:0:@a', + { + 'Nested:0:2: (', + children = { + 'Missing:0:4:', + }, + }, + }, + }, + }, + err = { + arg = '()', + msg = 'E15: Missing operator: %.*s', + }, + }, { + hl('Register', '@a'), + hl('InvalidSpacing', ' '), + hl('NestingParenthesis', '('), + hl('InvalidNestingParenthesis', ')'), + }) + check_parsing( + '@a + (@b)', 0, { + ast = { + { + 'BinaryPlus:0:2: +', + children = { + 'Register(name=a):0:0:@a', + { + 'Nested:0:4: (', + children = { + 'Register(name=b):0:6:@b', + }, + }, + }, + }, + }, + }, { + hl('Register', '@a'), + hl('BinaryPlus', '+', 1), + hl('NestingParenthesis', '(', 1), + hl('Register', '@b'), + hl('NestingParenthesis', ')'), + }) + check_parsing( + '@a + (+@b)', 0, { + ast = { + { + 'BinaryPlus:0:2: +', + children = { + 'Register(name=a):0:0:@a', + { + 'Nested:0:4: (', + children = { + { + 'UnaryPlus:0:6:+', + children = { + 'Register(name=b):0:7:@b', + }, + }, + }, + }, + }, + }, + }, + }, { + hl('Register', '@a'), + hl('BinaryPlus', '+', 1), + hl('NestingParenthesis', '(', 1), + hl('UnaryPlus', '+'), + hl('Register', '@b'), + hl('NestingParenthesis', ')'), + }) + check_parsing( + '@a + (@b + @c)', 0, { + ast = { + { + 'BinaryPlus:0:2: +', + children = { + 'Register(name=a):0:0:@a', + { + 'Nested:0:4: (', + children = { + { + 'BinaryPlus:0:8: +', + children = { + 'Register(name=b):0:6:@b', + 'Register(name=c):0:10: @c', + }, + }, + }, + }, + }, + }, + }, + }, { + hl('Register', '@a'), + hl('BinaryPlus', '+', 1), + hl('NestingParenthesis', '(', 1), + hl('Register', '@b'), + hl('BinaryPlus', '+', 1), + hl('Register', '@c', 1), + hl('NestingParenthesis', ')'), + }) + check_parsing('(@a)+@b', 0, { + ast = { + { + 'BinaryPlus:0:4:+', + children = { + { + 'Nested:0:0:(', + children = { + 'Register(name=a):0:1:@a', + }, + }, + 'Register(name=b):0:5:@b', + }, + }, + }, + }, { + hl('NestingParenthesis', '('), + hl('Register', '@a'), + hl('NestingParenthesis', ')'), + hl('BinaryPlus', '+'), + hl('Register', '@b'), + }) + check_parsing('@a+(@b)(@c)', 0, { + -- 01234567890 + ast = { + { + 'BinaryPlus:0:2:+', + children = { + 'Register(name=a):0:0:@a', + { + 'Call:0:7:(', + children = { + { + 'Nested:0:3:(', + children = { 'Register(name=b):0:4:@b' }, + }, + 'Register(name=c):0:8:@c', + }, + }, + }, + }, + }, + }, { + hl('Register', '@a'), + hl('BinaryPlus', '+'), + hl('NestingParenthesis', '('), + hl('Register', '@b'), + hl('NestingParenthesis', ')'), + hl('CallingParenthesis', '('), + hl('Register', '@c'), + hl('CallingParenthesis', ')'), + }) + check_parsing('@a+((@b))(@c)', 0, { + -- 01234567890123456890123456789 + -- 0 1 2 + ast = { + { + 'BinaryPlus:0:2:+', + children = { + 'Register(name=a):0:0:@a', + { + 'Call:0:9:(', + children = { + { + 'Nested:0:3:(', + children = { + { + 'Nested:0:4:(', + children = { 'Register(name=b):0:5:@b' } + }, + }, + }, + 'Register(name=c):0:10:@c', + }, + }, + }, + }, + }, + }, { + hl('Register', '@a'), + hl('BinaryPlus', '+'), + hl('NestingParenthesis', '('), + hl('NestingParenthesis', '('), + hl('Register', '@b'), + hl('NestingParenthesis', ')'), + hl('NestingParenthesis', ')'), + hl('CallingParenthesis', '('), + hl('Register', '@c'), + hl('CallingParenthesis', ')'), + }) + check_parsing('@a+((@b))+@c', 0, { + -- 01234567890123456890123456789 + -- 0 1 2 + ast = { + { + 'BinaryPlus:0:9:+', + children = { + { + 'BinaryPlus:0:2:+', + children = { + 'Register(name=a):0:0:@a', + { + 'Nested:0:3:(', + children = { + { + 'Nested:0:4:(', + children = { 'Register(name=b):0:5:@b' } + }, + }, + }, + }, + }, + 'Register(name=c):0:10:@c', + }, + }, + }, + }, { + hl('Register', '@a'), + hl('BinaryPlus', '+'), + hl('NestingParenthesis', '('), + hl('NestingParenthesis', '('), + hl('Register', '@b'), + hl('NestingParenthesis', ')'), + hl('NestingParenthesis', ')'), + hl('BinaryPlus', '+'), + hl('Register', '@c'), + }) + check_parsing( + '@a + (@b + @c) + @d(@e) + (+@f) + ((+@g(@h))(@j)(@k))(@l)', 0, {--[[ + | | | | | | | | || | | || | | ||| || || || || + 000000000011111111112222222222333333333344444444445555555 + 012345678901234567890123456789012345678901234567890123456 + ]] + ast = {{ + 'BinaryPlus:0:31: +', + children = { + { + 'BinaryPlus:0:23: +', + children = { + { + 'BinaryPlus:0:14: +', + children = { + { + 'BinaryPlus:0:2: +', + children = { + 'Register(name=a):0:0:@a', + { + 'Nested:0:4: (', + children = { + { + 'BinaryPlus:0:8: +', + children = { + 'Register(name=b):0:6:@b', + 'Register(name=c):0:10: @c', + }, + }, + }, + }, + }, + }, + { + 'Call:0:19:(', + children = { + 'Register(name=d):0:16: @d', + 'Register(name=e):0:20:@e', + }, + }, + }, + }, + { + 'Nested:0:25: (', + children = { + { + 'UnaryPlus:0:27:+', + children = { + 'Register(name=f):0:28:@f', + }, + }, + }, + }, + }, + }, + { + 'Call:0:53:(', + children = { + { + 'Nested:0:33: (', + children = { + { + 'Call:0:48:(', + children = { + { + 'Call:0:44:(', + children = { + { + 'Nested:0:35:(', + children = { + { + 'UnaryPlus:0:36:+', + children = { + { + 'Call:0:39:(', + children = { + 'Register(name=g):0:37:@g', + 'Register(name=h):0:40:@h', + }, + }, + }, + }, + }, + }, + 'Register(name=j):0:45:@j', + }, + }, + 'Register(name=k):0:49:@k', + }, + }, + }, + }, + 'Register(name=l):0:54:@l', + }, + }, + }, + }}, + }, { + hl('Register', '@a'), + hl('BinaryPlus', '+', 1), + hl('NestingParenthesis', '(', 1), + hl('Register', '@b'), + hl('BinaryPlus', '+', 1), + hl('Register', '@c', 1), + hl('NestingParenthesis', ')'), + hl('BinaryPlus', '+', 1), + hl('Register', '@d', 1), + hl('CallingParenthesis', '('), + hl('Register', '@e'), + hl('CallingParenthesis', ')'), + hl('BinaryPlus', '+', 1), + hl('NestingParenthesis', '(', 1), + hl('UnaryPlus', '+'), + hl('Register', '@f'), + hl('NestingParenthesis', ')'), + hl('BinaryPlus', '+', 1), + hl('NestingParenthesis', '(', 1), + hl('NestingParenthesis', '('), + hl('UnaryPlus', '+'), + hl('Register', '@g'), + hl('CallingParenthesis', '('), + hl('Register', '@h'), + hl('CallingParenthesis', ')'), + hl('NestingParenthesis', ')'), + hl('CallingParenthesis', '('), + hl('Register', '@j'), + hl('CallingParenthesis', ')'), + hl('CallingParenthesis', '('), + hl('Register', '@k'), + hl('CallingParenthesis', ')'), + hl('NestingParenthesis', ')'), + hl('CallingParenthesis', '('), + hl('Register', '@l'), + hl('CallingParenthesis', ')'), + }) + check_parsing('@a)', 0, { + -- 012 + ast = { + { + 'Nested:0:2:', + children = { + 'Register(name=a):0:0:@a', + }, + }, + }, + err = { + arg = ')', + msg = 'E15: Unexpected closing parenthesis: %.*s', + }, + }, { + hl('Register', '@a'), + hl('InvalidNestingParenthesis', ')'), + }) + check_parsing('(@a', 0, { + -- 012 + ast = { + { + 'Nested:0:0:(', + children = { + 'Register(name=a):0:1:@a', + }, + }, + }, + err = { + arg = '(@a', + msg = 'E110: Missing closing parenthesis for nested expression: %.*s', + }, + }, { + hl('NestingParenthesis', '('), + hl('Register', '@a'), + }) + check_parsing('@a(@b', 0, { + -- 01234 + ast = { + { + 'Call:0:2:(', + children = { + 'Register(name=a):0:0:@a', + 'Register(name=b):0:3:@b', + }, + }, + }, + err = { + arg = '(@b', + msg = 'E116: Missing closing parenthesis for function call: %.*s', + }, + }, { + hl('Register', '@a'), + hl('CallingParenthesis', '('), + hl('Register', '@b'), + }) + check_parsing('@a(@b, @c, @d, @e)', 0, { + -- 012345678901234567 + -- 0 1 + ast = { + { + 'Call:0:2:(', + children = { + 'Register(name=a):0:0:@a', + { + 'Comma:0:5:,', + children = { + 'Register(name=b):0:3:@b', + { + 'Comma:0:9:,', + children = { + 'Register(name=c):0:6: @c', + { + 'Comma:0:13:,', + children = { + 'Register(name=d):0:10: @d', + 'Register(name=e):0:14: @e', + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, { + hl('Register', '@a'), + hl('CallingParenthesis', '('), + hl('Register', '@b'), + hl('Comma', ','), + hl('Register', '@c', 1), + hl('Comma', ','), + hl('Register', '@d', 1), + hl('Comma', ','), + hl('Register', '@e', 1), + hl('CallingParenthesis', ')'), + }) + check_parsing('@a(@b(@c))', 0, { + -- 01234567890123456789012345678901234567 + -- 0 1 2 3 + ast = { + { + 'Call:0:2:(', + children = { + 'Register(name=a):0:0:@a', + { + 'Call:0:5:(', + children = { + 'Register(name=b):0:3:@b', + 'Register(name=c):0:6:@c', + }, + }, + }, + }, + }, + }, { + hl('Register', '@a'), + hl('CallingParenthesis', '('), + hl('Register', '@b'), + hl('CallingParenthesis', '('), + hl('Register', '@c'), + hl('CallingParenthesis', ')'), + hl('CallingParenthesis', ')'), + }) + check_parsing('@a(@b(@c(@d(@e), @f(@g(@h), @i(@j)))))', 0, { + -- 01234567890123456789012345678901234567 + -- 0 1 2 3 + ast = { + { + 'Call:0:2:(', + children = { + 'Register(name=a):0:0:@a', + { + 'Call:0:5:(', + children = { + 'Register(name=b):0:3:@b', + { + 'Call:0:8:(', + children = { + 'Register(name=c):0:6:@c', + { + 'Comma:0:15:,', + children = { + { + 'Call:0:11:(', + children = { + 'Register(name=d):0:9:@d', + 'Register(name=e):0:12:@e', + }, + }, + { + 'Call:0:19:(', + children = { + 'Register(name=f):0:16: @f', + { + 'Comma:0:26:,', + children = { + { + 'Call:0:22:(', + children = { + 'Register(name=g):0:20:@g', + 'Register(name=h):0:23:@h', + }, + }, + { + 'Call:0:30:(', + children = { + 'Register(name=i):0:27: @i', + 'Register(name=j):0:31:@j', + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, { + hl('Register', '@a'), + hl('CallingParenthesis', '('), + hl('Register', '@b'), + hl('CallingParenthesis', '('), + hl('Register', '@c'), + hl('CallingParenthesis', '('), + hl('Register', '@d'), + hl('CallingParenthesis', '('), + hl('Register', '@e'), + hl('CallingParenthesis', ')'), + hl('Comma', ','), + hl('Register', '@f', 1), + hl('CallingParenthesis', '('), + hl('Register', '@g'), + hl('CallingParenthesis', '('), + hl('Register', '@h'), + hl('CallingParenthesis', ')'), + hl('Comma', ','), + hl('Register', '@i', 1), + hl('CallingParenthesis', '('), + hl('Register', '@j'), + hl('CallingParenthesis', ')'), + hl('CallingParenthesis', ')'), + hl('CallingParenthesis', ')'), + hl('CallingParenthesis', ')'), + hl('CallingParenthesis', ')'), + }) + end) + it('works with variable names, including curly braces ones', function() + check_parsing('var', 0, { + ast = { + 'PlainIdentifier(scope=0,ident=var):0:0:var', + }, + }, { + hl('IdentifierName', 'var'), + }) + check_parsing('g:var', 0, { + ast = { + 'PlainIdentifier(scope=g,ident=var):0:0:g:var', + }, + }, { + hl('IdentifierScope', 'g'), + hl('IdentifierScopeDelimiter', ':'), + hl('IdentifierName', 'var'), + }) + check_parsing('g:', 0, { + ast = { + 'PlainIdentifier(scope=g,ident=):0:0:g:', + }, + }, { + hl('IdentifierScope', 'g'), + hl('IdentifierScopeDelimiter', ':'), + }) + check_parsing('{a}', 0, { + -- 012 + ast = { + { + 'CurlyBracesIdentifier:0:0:{', + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + }, + }, + }, + }, { + hl('Curly', '{'), + hl('IdentifierName', 'a'), + hl('Curly', '}'), + }) + check_parsing('{a:b}', 0, { + -- 012 + ast = { + { + 'CurlyBracesIdentifier:0:0:{', + children = { + 'PlainIdentifier(scope=a,ident=b):0:1:a:b', + }, + }, + }, + }, { + hl('Curly', '{'), + hl('IdentifierScope', 'a'), + hl('IdentifierScopeDelimiter', ':'), + hl('IdentifierName', 'b'), + hl('Curly', '}'), + }) + check_parsing('{a:@b}', 0, { + -- 012345 + ast = { + { + 'CurlyBracesIdentifier:0:0:{', + children = { + { + 'OpMissing:0:3:', + children={ + 'PlainIdentifier(scope=a,ident=):0:1:a:', + 'Register(name=b):0:3:@b', + }, + }, + }, + }, + }, + err = { + arg = '@b}', + msg = 'E15: Missing operator: %.*s', + }, + }, { + hl('Curly', '{'), + hl('IdentifierScope', 'a'), + hl('IdentifierScopeDelimiter', ':'), + hl('InvalidRegister', '@b'), + hl('Curly', '}'), + }) + check_parsing('{@a}', 0, { + ast = { + { + 'CurlyBracesIdentifier:0:0:{', + children = { + 'Register(name=a):0:1:@a', + }, + }, + }, + }, { + hl('Curly', '{'), + hl('Register', '@a'), + hl('Curly', '}'), + }) + check_parsing('{@a}{@b}', 0, { + -- 01234567 + ast = { + { + 'ComplexIdentifier:0:4:', + children = { + { + 'CurlyBracesIdentifier:0:0:{', + children = { + 'Register(name=a):0:1:@a', + }, + }, + { + 'CurlyBracesIdentifier:0:4:{', + children = { + 'Register(name=b):0:5:@b', + }, + }, + }, + }, + }, + }, { + hl('Curly', '{'), + hl('Register', '@a'), + hl('Curly', '}'), + hl('Curly', '{'), + hl('Register', '@b'), + hl('Curly', '}'), + }) + check_parsing('g:{@a}', 0, { + -- 01234567 + ast = { + { + 'ComplexIdentifier:0:2:', + children = { + 'PlainIdentifier(scope=g,ident=):0:0:g:', + { + 'CurlyBracesIdentifier:0:2:{', + children = { + 'Register(name=a):0:3:@a', + }, + }, + }, + }, + }, + }, { + hl('IdentifierScope', 'g'), + hl('IdentifierScopeDelimiter', ':'), + hl('Curly', '{'), + hl('Register', '@a'), + hl('Curly', '}'), + }) + check_parsing('{@a}_test', 0, { + -- 012345678 + ast = { + { + 'ComplexIdentifier:0:4:', + children = { + { + 'CurlyBracesIdentifier:0:0:{', + children = { + 'Register(name=a):0:1:@a', + }, + }, + 'PlainIdentifier(scope=0,ident=_test):0:4:_test', + }, + }, + }, + }, { + hl('Curly', '{'), + hl('Register', '@a'), + hl('Curly', '}'), + hl('IdentifierName', '_test'), + }) + check_parsing('g:{@a}_test', 0, { + -- 01234567890 + ast = { + { + 'ComplexIdentifier:0:2:', + children = { + 'PlainIdentifier(scope=g,ident=):0:0:g:', + { + 'ComplexIdentifier:0:6:', + children = { + { + 'CurlyBracesIdentifier:0:2:{', + children = { + 'Register(name=a):0:3:@a', + }, + }, + 'PlainIdentifier(scope=0,ident=_test):0:6:_test', + }, + }, + }, + }, + }, + }, { + hl('IdentifierScope', 'g'), + hl('IdentifierScopeDelimiter', ':'), + hl('Curly', '{'), + hl('Register', '@a'), + hl('Curly', '}'), + hl('IdentifierName', '_test'), + }) + check_parsing('g:{@a}_test()', 0, { + -- 0123456789012 + ast = { + { + 'Call:0:11:(', + children = { + { + 'ComplexIdentifier:0:2:', + children = { + 'PlainIdentifier(scope=g,ident=):0:0:g:', + { + 'ComplexIdentifier:0:6:', + children = { + { + 'CurlyBracesIdentifier:0:2:{', + children = { + 'Register(name=a):0:3:@a', + }, + }, + 'PlainIdentifier(scope=0,ident=_test):0:6:_test', + }, + }, + }, + }, + }, + }, + }, + }, { + hl('IdentifierScope', 'g'), + hl('IdentifierScopeDelimiter', ':'), + hl('Curly', '{'), + hl('Register', '@a'), + hl('Curly', '}'), + hl('IdentifierName', '_test'), + hl('CallingParenthesis', '('), + hl('CallingParenthesis', ')'), + }) + check_parsing('{@a} ()', 0, { + -- 0123456789012 + ast = { + { + 'Call:0:4: (', + children = { + { + 'CurlyBracesIdentifier:0:0:{', + children = { + 'Register(name=a):0:1:@a', + }, + }, + }, + }, + }, + }, { + hl('Curly', '{'), + hl('Register', '@a'), + hl('Curly', '}'), + hl('CallingParenthesis', '(', 1), + hl('CallingParenthesis', ')'), + }) + check_parsing('g:{@a} ()', 0, { + -- 0123456789012 + ast = { + { + 'Call:0:6: (', + children = { + { + 'ComplexIdentifier:0:2:', + children = { + 'PlainIdentifier(scope=g,ident=):0:0:g:', + { + 'CurlyBracesIdentifier:0:2:{', + children = { + 'Register(name=a):0:3:@a', + }, + }, + }, + }, + }, + }, + }, + }, { + hl('IdentifierScope', 'g'), + hl('IdentifierScopeDelimiter', ':'), + hl('Curly', '{'), + hl('Register', '@a'), + hl('Curly', '}'), + hl('CallingParenthesis', '(', 1), + hl('CallingParenthesis', ')'), + }) + check_parsing('{@a', 0, { + -- 012 + ast = { + { + 'UnknownFigure:0:0:{', + children = { + 'Register(name=a):0:1:@a', + }, + }, + }, + err = { + arg = '{@a', + msg = 'E15: Missing closing figure brace: %.*s', + }, + }, { + hl('FigureBrace', '{'), + hl('Register', '@a'), + }) + end) + it('works with lambdas and dictionaries', function() + check_parsing('{}', 0, { + ast = { + 'DictLiteral:0:0:{', + }, + }, { + hl('Dict', '{'), + hl('Dict', '}'), + }) + check_parsing('{->@a}', 0, { + ast = { + { + 'Lambda:0:0:{', + children = { + { + 'Arrow:0:1:->', + children = { + 'Register(name=a):0:3:@a', + }, + }, + }, + }, + }, + }, { + hl('Lambda', '{'), + hl('Arrow', '->'), + hl('Register', '@a'), + hl('Lambda', '}'), + }) + check_parsing('{->@a+@b}', 0, { + -- 012345678 + ast = { + { + 'Lambda:0:0:{', + children = { + { + 'Arrow:0:1:->', + children = { + { + 'BinaryPlus:0:5:+', + children = { + 'Register(name=a):0:3:@a', + 'Register(name=b):0:6:@b', + }, + }, + }, + }, + }, + }, + }, + }, { + hl('Lambda', '{'), + hl('Arrow', '->'), + hl('Register', '@a'), + hl('BinaryPlus', '+'), + hl('Register', '@b'), + hl('Lambda', '}'), + }) + check_parsing('{a->@a}', 0, { + -- 012345678 + ast = { + { + 'Lambda:0:0:{', + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + { + 'Arrow:0:2:->', + children = { + 'Register(name=a):0:4:@a', + }, + }, + }, + }, + }, + }, { + hl('Lambda', '{'), + hl('IdentifierName', 'a'), + hl('Arrow', '->'), + hl('Register', '@a'), + hl('Lambda', '}'), + }) + check_parsing('{a,b->@a}', 0, { + -- 012345678 + ast = { + { + 'Lambda:0:0:{', + children = { + { + 'Comma:0:2:,', + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + 'PlainIdentifier(scope=0,ident=b):0:3:b', + }, + }, + { + 'Arrow:0:4:->', + children = { + 'Register(name=a):0:6:@a', + }, + }, + }, + }, + }, + }, { + hl('Lambda', '{'), + hl('IdentifierName', 'a'), + hl('Comma', ','), + hl('IdentifierName', 'b'), + hl('Arrow', '->'), + hl('Register', '@a'), + hl('Lambda', '}'), + }) + check_parsing('{a,b,c->@a}', 0, { + -- 01234567890 + ast = { + { + 'Lambda:0:0:{', + children = { + { + 'Comma:0:2:,', + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + { + 'Comma:0:4:,', + children = { + 'PlainIdentifier(scope=0,ident=b):0:3:b', + 'PlainIdentifier(scope=0,ident=c):0:5:c', + }, + }, + }, + }, + { + 'Arrow:0:6:->', + children = { + 'Register(name=a):0:8:@a', + }, + }, + }, + }, + }, + }, { + hl('Lambda', '{'), + hl('IdentifierName', 'a'), + hl('Comma', ','), + hl('IdentifierName', 'b'), + hl('Comma', ','), + hl('IdentifierName', 'c'), + hl('Arrow', '->'), + hl('Register', '@a'), + hl('Lambda', '}'), + }) + check_parsing('{a,b,c,d->@a}', 0, { + -- 0123456789012 + ast = { + { + 'Lambda:0:0:{', + children = { + { + 'Comma:0:2:,', + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + { + 'Comma:0:4:,', + children = { + 'PlainIdentifier(scope=0,ident=b):0:3:b', + { + 'Comma:0:6:,', + children = { + 'PlainIdentifier(scope=0,ident=c):0:5:c', + 'PlainIdentifier(scope=0,ident=d):0:7:d', + }, + }, + }, + }, + }, + }, + { + 'Arrow:0:8:->', + children = { + 'Register(name=a):0:10:@a', + }, + }, + }, + }, + }, + }, { + hl('Lambda', '{'), + hl('IdentifierName', 'a'), + hl('Comma', ','), + hl('IdentifierName', 'b'), + hl('Comma', ','), + hl('IdentifierName', 'c'), + hl('Comma', ','), + hl('IdentifierName', 'd'), + hl('Arrow', '->'), + hl('Register', '@a'), + hl('Lambda', '}'), + }) + check_parsing('{a,b,c,d,->@a}', 0, { + -- 01234567890123 + ast = { + { + 'Lambda:0:0:{', + children = { + { + 'Comma:0:2:,', + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + { + 'Comma:0:4:,', + children = { + 'PlainIdentifier(scope=0,ident=b):0:3:b', + { + 'Comma:0:6:,', + children = { + 'PlainIdentifier(scope=0,ident=c):0:5:c', + { + 'Comma:0:8:,', + children = { + 'PlainIdentifier(scope=0,ident=d):0:7:d', + }, + }, + }, + }, + }, + }, + }, + }, + { + 'Arrow:0:9:->', + children = { + 'Register(name=a):0:11:@a', + }, + }, + }, + }, + }, + }, { + hl('Lambda', '{'), + hl('IdentifierName', 'a'), + hl('Comma', ','), + hl('IdentifierName', 'b'), + hl('Comma', ','), + hl('IdentifierName', 'c'), + hl('Comma', ','), + hl('IdentifierName', 'd'), + hl('Comma', ','), + hl('Arrow', '->'), + hl('Register', '@a'), + hl('Lambda', '}'), + }) + check_parsing('{a,b->{c,d->{e,f->@a}}}', 0, { + -- 01234567890123456789012 + -- 0 1 2 + ast = { + { + 'Lambda:0:0:{', + children = { + { + 'Comma:0:2:,', + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + 'PlainIdentifier(scope=0,ident=b):0:3:b', + }, + }, + { + 'Arrow:0:4:->', + children = { + { + 'Lambda:0:6:{', + children = { + { + 'Comma:0:8:,', + children = { + 'PlainIdentifier(scope=0,ident=c):0:7:c', + 'PlainIdentifier(scope=0,ident=d):0:9:d', + }, + }, + { + 'Arrow:0:10:->', + children = { + { + 'Lambda:0:12:{', + children = { + { + 'Comma:0:14:,', + children = { + 'PlainIdentifier(scope=0,ident=e):0:13:e', + 'PlainIdentifier(scope=0,ident=f):0:15:f', + }, + }, + { + 'Arrow:0:16:->', + children = { + 'Register(name=a):0:18:@a', + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, { + hl('Lambda', '{'), + hl('IdentifierName', 'a'), + hl('Comma', ','), + hl('IdentifierName', 'b'), + hl('Arrow', '->'), + hl('Lambda', '{'), + hl('IdentifierName', 'c'), + hl('Comma', ','), + hl('IdentifierName', 'd'), + hl('Arrow', '->'), + hl('Lambda', '{'), + hl('IdentifierName', 'e'), + hl('Comma', ','), + hl('IdentifierName', 'f'), + hl('Arrow', '->'), + hl('Register', '@a'), + hl('Lambda', '}'), + hl('Lambda', '}'), + hl('Lambda', '}'), + }) + check_parsing('{a,b->c,d}', 0, { + -- 0123456789 + ast = { + { + 'Lambda:0:0:{', + children = { + { + 'Comma:0:2:,', + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + 'PlainIdentifier(scope=0,ident=b):0:3:b', + }, + }, + { + 'Arrow:0:4:->', + children = { + { + 'Comma:0:7:,', + children = { + 'PlainIdentifier(scope=0,ident=c):0:6:c', + 'PlainIdentifier(scope=0,ident=d):0:8:d', + }, + }, + }, + }, + }, + }, + }, + err = { + arg = ',d}', + msg = 'E15: Comma outside of call, lambda or literal: %.*s', + }, + }, { + hl('Lambda', '{'), + hl('IdentifierName', 'a'), + hl('Comma', ','), + hl('IdentifierName', 'b'), + hl('Arrow', '->'), + hl('IdentifierName', 'c'), + hl('InvalidComma', ','), + hl('IdentifierName', 'd'), + hl('Lambda', '}'), + }) + check_parsing('a,b,c,d', 0, { + -- 0123456789 + ast = { + { + 'Comma:0:1:,', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + { + 'Comma:0:3:,', + children = { + 'PlainIdentifier(scope=0,ident=b):0:2:b', + { + 'Comma:0:5:,', + children = { + 'PlainIdentifier(scope=0,ident=c):0:4:c', + 'PlainIdentifier(scope=0,ident=d):0:6:d', + }, + }, + }, + }, + }, + }, + }, + err = { + arg = ',b,c,d', + msg = 'E15: Comma outside of call, lambda or literal: %.*s', + }, + }, { + hl('IdentifierName', 'a'), + hl('InvalidComma', ','), + hl('IdentifierName', 'b'), + hl('InvalidComma', ','), + hl('IdentifierName', 'c'), + hl('InvalidComma', ','), + hl('IdentifierName', 'd'), + }) + check_parsing('a,b,c,d,', 0, { + -- 0123456789 + ast = { + { + 'Comma:0:1:,', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + { + 'Comma:0:3:,', + children = { + 'PlainIdentifier(scope=0,ident=b):0:2:b', + { + 'Comma:0:5:,', + children = { + 'PlainIdentifier(scope=0,ident=c):0:4:c', + { + 'Comma:0:7:,', + children = { + 'PlainIdentifier(scope=0,ident=d):0:6:d', + }, + }, + }, + }, + }, + }, + }, + }, + }, + err = { + arg = ',b,c,d,', + msg = 'E15: Comma outside of call, lambda or literal: %.*s', + }, + }, { + hl('IdentifierName', 'a'), + hl('InvalidComma', ','), + hl('IdentifierName', 'b'), + hl('InvalidComma', ','), + hl('IdentifierName', 'c'), + hl('InvalidComma', ','), + hl('IdentifierName', 'd'), + hl('InvalidComma', ','), + }) + check_parsing(',', 0, { + -- 0123456789 + ast = { + { + 'Comma:0:0:,', + children = { + 'Missing:0:0:', + }, + }, + }, + err = { + arg = ',', + msg = 'E15: Expected value, got comma: %.*s', + }, + }, { + hl('InvalidComma', ','), + }) + check_parsing('{,a->@a}', 0, { + -- 0123456789 + ast = { + { + 'CurlyBracesIdentifier:0:0:{', + children = { + { + 'Arrow:0:3:->', + children = { + { + 'Comma:0:1:,', + children = { + 'Missing:0:1:', + 'PlainIdentifier(scope=0,ident=a):0:2:a', + }, + }, + 'Register(name=a):0:5:@a', + }, + }, + }, + }, + }, + err = { + arg = ',a->@a}', + msg = 'E15: Expected value, got comma: %.*s', + }, + }, { + hl('Curly', '{'), + hl('InvalidComma', ','), + hl('IdentifierName', 'a'), + hl('InvalidArrow', '->'), + hl('Register', '@a'), + hl('Curly', '}'), + }) + check_parsing('}', 0, { + -- 0123456789 + ast = { + 'UnknownFigure:0:0:', + }, + err = { + arg = '}', + msg = 'E15: Unexpected closing figure brace: %.*s', + }, + }, { + hl('InvalidFigureBrace', '}'), + }) + check_parsing('{->}', 0, { + -- 0123456789 + ast = { + { + 'Lambda:0:0:{', + children = { + 'Arrow:0:1:->', + }, + }, + }, + err = { + arg = '}', + msg = 'E15: Expected value, got closing figure brace: %.*s', + }, + }, { + hl('Lambda', '{'), + hl('Arrow', '->'), + hl('InvalidLambda', '}'), + }) + check_parsing('{a,b}', 0, { + -- 0123456789 + ast = { + { + 'Lambda:0:0:{', + children = { + { + 'Comma:0:2:,', + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + 'PlainIdentifier(scope=0,ident=b):0:3:b', + }, + }, + }, + }, + }, + err = { + arg = '}', + msg = 'E15: Expected lambda arguments list or arrow: %.*s', + }, + }, { + hl('Lambda', '{'), + hl('IdentifierName', 'a'), + hl('Comma', ','), + hl('IdentifierName', 'b'), + hl('InvalidLambda', '}'), + }) + check_parsing('{a,}', 0, { + -- 0123456789 + ast = { + { + 'Lambda:0:0:{', + children = { + { + 'Comma:0:2:,', + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + }, + }, + }, + }, + }, + err = { + arg = '}', + msg = 'E15: Expected lambda arguments list or arrow: %.*s', + }, + }, { + hl('Lambda', '{'), + hl('IdentifierName', 'a'), + hl('Comma', ','), + hl('InvalidLambda', '}'), + }) + check_parsing('{@a:@b}', 0, { + -- 0123456789 + ast = { + { + 'DictLiteral:0:0:{', + children = { + { + 'Colon:0:3::', + children = { + 'Register(name=a):0:1:@a', + 'Register(name=b):0:4:@b', + }, + }, + }, + }, + }, + }, { + hl('Dict', '{'), + hl('Register', '@a'), + hl('Colon', ':'), + hl('Register', '@b'), + hl('Dict', '}'), + }) + check_parsing('{@a:@b,@c:@d}', 0, { + -- 0123456789012 + -- 0 1 + ast = { + { + 'DictLiteral:0:0:{', + children = { + { + 'Comma:0:6:,', + children = { + { + 'Colon:0:3::', + children = { + 'Register(name=a):0:1:@a', + 'Register(name=b):0:4:@b', + }, + }, + { + 'Colon:0:9::', + children = { + 'Register(name=c):0:7:@c', + 'Register(name=d):0:10:@d', + }, + }, + }, + }, + }, + }, + }, + }, { + hl('Dict', '{'), + hl('Register', '@a'), + hl('Colon', ':'), + hl('Register', '@b'), + hl('Comma', ','), + hl('Register', '@c'), + hl('Colon', ':'), + hl('Register', '@d'), + hl('Dict', '}'), + }) + check_parsing('{@a:@b,@c:@d,@e:@f,}', 0, { + -- 01234567890123456789 + -- 0 1 + ast = { + { + 'DictLiteral:0:0:{', + children = { + { + 'Comma:0:6:,', + children = { + { + 'Colon:0:3::', + children = { + 'Register(name=a):0:1:@a', + 'Register(name=b):0:4:@b', + }, + }, + { + 'Comma:0:12:,', + children = { + { + 'Colon:0:9::', + children = { + 'Register(name=c):0:7:@c', + 'Register(name=d):0:10:@d', + }, + }, + { + 'Comma:0:18:,', + children = { + { + 'Colon:0:15::', + children = { + 'Register(name=e):0:13:@e', + 'Register(name=f):0:16:@f', + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, { + hl('Dict', '{'), + hl('Register', '@a'), + hl('Colon', ':'), + hl('Register', '@b'), + hl('Comma', ','), + hl('Register', '@c'), + hl('Colon', ':'), + hl('Register', '@d'), + hl('Comma', ','), + hl('Register', '@e'), + hl('Colon', ':'), + hl('Register', '@f'), + hl('Comma', ','), + hl('Dict', '}'), + }) + check_parsing('{@a:@b,@c:@d,@e:@f,@g:}', 0, { + -- 01234567890123456789012 + -- 0 1 2 + ast = { + { + 'DictLiteral:0:0:{', + children = { + { + 'Comma:0:6:,', + children = { + { + 'Colon:0:3::', + children = { + 'Register(name=a):0:1:@a', + 'Register(name=b):0:4:@b', + }, + }, + { + 'Comma:0:12:,', + children = { + { + 'Colon:0:9::', + children = { + 'Register(name=c):0:7:@c', + 'Register(name=d):0:10:@d', + }, + }, + { + 'Comma:0:18:,', + children = { + { + 'Colon:0:15::', + children = { + 'Register(name=e):0:13:@e', + 'Register(name=f):0:16:@f', + }, + }, + { + 'Colon:0:21::', + children = { + 'Register(name=g):0:19:@g', + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + err = { + arg = '}', + msg = 'E15: Expected value, got closing figure brace: %.*s', + }, + }, { + hl('Dict', '{'), + hl('Register', '@a'), + hl('Colon', ':'), + hl('Register', '@b'), + hl('Comma', ','), + hl('Register', '@c'), + hl('Colon', ':'), + hl('Register', '@d'), + hl('Comma', ','), + hl('Register', '@e'), + hl('Colon', ':'), + hl('Register', '@f'), + hl('Comma', ','), + hl('Register', '@g'), + hl('Colon', ':'), + hl('InvalidDict', '}'), + }) + check_parsing('{@a:@b,}', 0, { + -- 01234567890123 + -- 0 1 + ast = { + { + 'DictLiteral:0:0:{', + children = { + { + 'Comma:0:6:,', + children = { + { + 'Colon:0:3::', + children = { + 'Register(name=a):0:1:@a', + 'Register(name=b):0:4:@b', + }, + }, + }, + }, + }, + }, + }, + }, { + hl('Dict', '{'), + hl('Register', '@a'), + hl('Colon', ':'), + hl('Register', '@b'), + hl('Comma', ','), + hl('Dict', '}'), + }) + check_parsing('{({f -> g})(@h)(@i)}', 0, { + -- 01234567890123456789 + -- 0 1 + ast = { + { + 'CurlyBracesIdentifier:0:0:{', + children = { + { + 'Call:0:15:(', + children = { + { + 'Call:0:11:(', + children = { + { + 'Nested:0:1:(', + children = { + { + 'Lambda:0:2:{', + children = { + 'PlainIdentifier(scope=0,ident=f):0:3:f', + { + 'Arrow:0:4: ->', + children = { + 'PlainIdentifier(scope=0,ident=g):0:7: g', + }, + }, + }, + }, + }, + }, + 'Register(name=h):0:12:@h', + }, + }, + 'Register(name=i):0:16:@i', + }, + }, + }, + }, + }, + }, { + hl('Curly', '{'), + hl('NestingParenthesis', '('), + hl('Lambda', '{'), + hl('IdentifierName', 'f'), + hl('Arrow', '->', 1), + hl('IdentifierName', 'g', 1), + hl('Lambda', '}'), + hl('NestingParenthesis', ')'), + hl('CallingParenthesis', '('), + hl('Register', '@h'), + hl('CallingParenthesis', ')'), + hl('CallingParenthesis', '('), + hl('Register', '@i'), + hl('CallingParenthesis', ')'), + hl('Curly', '}'), + }) + check_parsing('a:{b()}c', 0, { + -- 01234567 + ast = { + { + 'ComplexIdentifier:0:2:', + children = { + 'PlainIdentifier(scope=a,ident=):0:0:a:', + { + 'ComplexIdentifier:0:7:', + children = { + { + 'CurlyBracesIdentifier:0:2:{', + children = { + { + 'Call:0:4:(', + children = { + 'PlainIdentifier(scope=0,ident=b):0:3:b', + }, + }, + }, + }, + 'PlainIdentifier(scope=0,ident=c):0:7:c', + }, + }, + }, + }, + }, + }, { + hl('IdentifierScope', 'a'), + hl('IdentifierScopeDelimiter', ':'), + hl('Curly', '{'), + hl('IdentifierName', 'b'), + hl('CallingParenthesis', '('), + hl('CallingParenthesis', ')'), + hl('Curly', '}'), + hl('IdentifierName', 'c'), + }) + check_parsing('a:{{b, c -> @d + @e + ({f -> g})(@h)}(@i)}j', 0, { + -- 01234567890123456789012345678901234567890123456 + -- 0 1 2 3 4 + ast = { + { + 'ComplexIdentifier:0:2:', + children = { + 'PlainIdentifier(scope=a,ident=):0:0:a:', + { + 'ComplexIdentifier:0:42:', + children = { + { + 'CurlyBracesIdentifier:0:2:{', + children = { + { + 'Call:0:37:(', + children = { + { + 'Lambda:0:3:{', + children = { + { + 'Comma:0:5:,', + children = { + 'PlainIdentifier(scope=0,ident=b):0:4:b', + 'PlainIdentifier(scope=0,ident=c):0:6: c', + }, + }, + { + 'Arrow:0:8: ->', + children = { + { + 'BinaryPlus:0:19: +', + children = { + { + 'BinaryPlus:0:14: +', + children = { + 'Register(name=d):0:11: @d', + 'Register(name=e):0:16: @e', + }, + }, + { + 'Call:0:32:(', + children = { + { + 'Nested:0:21: (', + children = { + { + 'Lambda:0:23:{', + children = { + 'PlainIdentifier(scope=0,ident=f):0:24:f', + { + 'Arrow:0:25: ->', + children = { + 'PlainIdentifier(scope=0,ident=g):0:28: g', + }, + }, + }, + }, + }, + }, + 'Register(name=h):0:33:@h', + }, + }, + }, + }, + }, + }, + }, + }, + 'Register(name=i):0:38:@i', + }, + }, + }, + }, + 'PlainIdentifier(scope=0,ident=j):0:42:j', + }, + }, + }, + }, + }, + }, { + hl('IdentifierScope', 'a'), + hl('IdentifierScopeDelimiter', ':'), + hl('Curly', '{'), + hl('Lambda', '{'), + hl('IdentifierName', 'b'), + hl('Comma', ','), + hl('IdentifierName', 'c', 1), + hl('Arrow', '->', 1), + hl('Register', '@d', 1), + hl('BinaryPlus', '+', 1), + hl('Register', '@e', 1), + hl('BinaryPlus', '+', 1), + hl('NestingParenthesis', '(', 1), + hl('Lambda', '{'), + hl('IdentifierName', 'f'), + hl('Arrow', '->', 1), + hl('IdentifierName', 'g', 1), + hl('Lambda', '}'), + hl('NestingParenthesis', ')'), + hl('CallingParenthesis', '('), + hl('Register', '@h'), + hl('CallingParenthesis', ')'), + hl('Lambda', '}'), + hl('CallingParenthesis', '('), + hl('Register', '@i'), + hl('CallingParenthesis', ')'), + hl('Curly', '}'), + hl('IdentifierName', 'j'), + }) + check_parsing('{@a + @b : @c + @d, @e + @f : @g + @i}', 0, { + -- 01234567890123456789012345678901234567 + -- 0 1 2 3 + ast = { + { + 'DictLiteral:0:0:{', + children = { + { + 'Comma:0:18:,', + children = { + { + 'Colon:0:8: :', + children = { + { + 'BinaryPlus:0:3: +', + children = { + 'Register(name=a):0:1:@a', + 'Register(name=b):0:5: @b', + }, + }, + { + 'BinaryPlus:0:13: +', + children = { + 'Register(name=c):0:10: @c', + 'Register(name=d):0:15: @d', + }, + }, + }, + }, + { + 'Colon:0:27: :', + children = { + { + 'BinaryPlus:0:22: +', + children = { + 'Register(name=e):0:19: @e', + 'Register(name=f):0:24: @f', + }, + }, + { + 'BinaryPlus:0:32: +', + children = { + 'Register(name=g):0:29: @g', + 'Register(name=i):0:34: @i', + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, { + hl('Dict', '{'), + hl('Register', '@a'), + hl('BinaryPlus', '+', 1), + hl('Register', '@b', 1), + hl('Colon', ':', 1), + hl('Register', '@c', 1), + hl('BinaryPlus', '+', 1), + hl('Register', '@d', 1), + hl('Comma', ','), + hl('Register', '@e', 1), + hl('BinaryPlus', '+', 1), + hl('Register', '@f', 1), + hl('Colon', ':', 1), + hl('Register', '@g', 1), + hl('BinaryPlus', '+', 1), + hl('Register', '@i', 1), + hl('Dict', '}'), + }) + check_parsing('-> -> ->', 0, { + -- 01234567 + ast = { + { + 'Arrow:0:0:->', + children = { + 'Missing:0:0:', + { + 'Arrow:0:2: ->', + children = { + 'Missing:0:2:', + { + 'Arrow:0:5: ->', + children = { + 'Missing:0:5:', + }, + }, + }, + }, + }, + }, + }, + err = { + arg = '-> -> ->', + msg = 'E15: Unexpected arrow: %.*s', + }, + }, { + hl('InvalidArrow', '->'), + hl('InvalidArrow', '->', 1), + hl('InvalidArrow', '->', 1), + }) + check_parsing('a -> b -> c -> d', 0, { + -- 0123456789012345 + -- 0 1 + ast = { + { + 'Arrow:0:1: ->', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + { + 'Arrow:0:6: ->', + children = { + 'PlainIdentifier(scope=0,ident=b):0:4: b', + { + 'Arrow:0:11: ->', + children = { + 'PlainIdentifier(scope=0,ident=c):0:9: c', + 'PlainIdentifier(scope=0,ident=d):0:14: d', + }, + }, + }, + }, + }, + }, + }, + err = { + arg = '-> b -> c -> d', + msg = 'E15: Arrow outside of lambda: %.*s', + }, + }, { + hl('IdentifierName', 'a'), + hl('InvalidArrow', '->', 1), + hl('IdentifierName', 'b', 1), + hl('InvalidArrow', '->', 1), + hl('IdentifierName', 'c', 1), + hl('InvalidArrow', '->', 1), + hl('IdentifierName', 'd', 1), + }) + check_parsing('{a -> b -> c}', 0, { + -- 0123456789012 + -- 0 1 + ast = { + { + 'Lambda:0:0:{', + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + { + 'Arrow:0:2: ->', + children = { + { + 'Arrow:0:7: ->', + children = { + 'PlainIdentifier(scope=0,ident=b):0:5: b', + 'PlainIdentifier(scope=0,ident=c):0:10: c', + }, + }, + }, + }, + }, + }, + }, + err = { + arg = '-> c}', + msg = 'E15: Arrow outside of lambda: %.*s', + }, + }, { + hl('Lambda', '{'), + hl('IdentifierName', 'a'), + hl('Arrow', '->', 1), + hl('IdentifierName', 'b', 1), + hl('InvalidArrow', '->', 1), + hl('IdentifierName', 'c', 1), + hl('Lambda', '}'), + }) + check_parsing('{a: -> b}', 0, { + -- 012345678 + ast = { + { + 'CurlyBracesIdentifier:0:0:{', + children = { + { + 'Arrow:0:3: ->', + children = { + 'PlainIdentifier(scope=a,ident=):0:1:a:', + 'PlainIdentifier(scope=0,ident=b):0:6: b', + }, + }, + }, + }, + }, + err = { + arg = '-> b}', + msg = 'E15: Arrow outside of lambda: %.*s', + }, + }, { + hl('Curly', '{'), + hl('IdentifierScope', 'a'), + hl('IdentifierScopeDelimiter', ':'), + hl('InvalidArrow', '->', 1), + hl('IdentifierName', 'b', 1), + hl('Curly', '}'), + }) + + check_parsing('{a:b -> b}', 0, { + -- 0123456789 + ast = { + { + 'CurlyBracesIdentifier:0:0:{', + children = { + { + 'Arrow:0:4: ->', + children = { + 'PlainIdentifier(scope=a,ident=b):0:1:a:b', + 'PlainIdentifier(scope=0,ident=b):0:7: b', + }, + }, + }, + }, + }, + err = { + arg = '-> b}', + msg = 'E15: Arrow outside of lambda: %.*s', + }, + }, { + hl('Curly', '{'), + hl('IdentifierScope', 'a'), + hl('IdentifierScopeDelimiter', ':'), + hl('IdentifierName', 'b'), + hl('InvalidArrow', '->', 1), + hl('IdentifierName', 'b', 1), + hl('Curly', '}'), + }) + + check_parsing('{a#b -> b}', 0, { + -- 0123456789 + ast = { + { + 'CurlyBracesIdentifier:0:0:{', + children = { + { + 'Arrow:0:4: ->', + children = { + 'PlainIdentifier(scope=0,ident=a#b):0:1:a#b', + 'PlainIdentifier(scope=0,ident=b):0:7: b', + }, + }, + }, + }, + }, + err = { + arg = '-> b}', + msg = 'E15: Arrow outside of lambda: %.*s', + }, + }, { + hl('Curly', '{'), + hl('IdentifierName', 'a#b'), + hl('InvalidArrow', '->', 1), + hl('IdentifierName', 'b', 1), + hl('Curly', '}'), + }) + check_parsing('{a : b : c}', 0, { + -- 01234567890 + -- 0 1 + ast = { + { + 'DictLiteral:0:0:{', + children = { + { + 'Colon:0:2: :', + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + { + 'Colon:0:6: :', + children = { + 'PlainIdentifier(scope=0,ident=b):0:4: b', + 'PlainIdentifier(scope=0,ident=c):0:8: c', + }, + }, + }, + }, + }, + }, + }, + err = { + arg = ': c}', + msg = 'E15: Colon outside of dictionary or ternary operator: %.*s', + }, + }, { + hl('Dict', '{'), + hl('IdentifierName', 'a'), + hl('Colon', ':', 1), + hl('IdentifierName', 'b', 1), + hl('InvalidColon', ':', 1), + hl('IdentifierName', 'c', 1), + hl('Dict', '}'), + }) + check_parsing('{', 0, { + -- 0 + ast = { + 'UnknownFigure:0:0:{', + }, + err = { + arg = '{', + msg = 'E15: Missing closing figure brace: %.*s', + }, + }, { + hl('FigureBrace', '{'), + }) + check_parsing('{a', 0, { + -- 01 + ast = { + { + 'UnknownFigure:0:0:{', + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + }, + }, + }, + err = { + arg = '{a', + msg = 'E15: Missing closing figure brace: %.*s', + }, + }, { + hl('FigureBrace', '{'), + hl('IdentifierName', 'a'), + }) + check_parsing('{a,b', 0, { + -- 0123 + ast = { + { + 'Lambda:0:0:{', + children = { + { + 'Comma:0:2:,', + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + 'PlainIdentifier(scope=0,ident=b):0:3:b', + }, + }, + }, + }, + }, + err = { + arg = '{a,b', + msg = 'E15: Missing closing figure brace for lambda: %.*s', + }, + }, { + hl('Lambda', '{'), + hl('IdentifierName', 'a'), + hl('Comma', ','), + hl('IdentifierName', 'b'), + }) + check_parsing('{a,b->', 0, { + -- 012345 + ast = { + { + 'Lambda:0:0:{', + children = { + { + 'Comma:0:2:,', + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + 'PlainIdentifier(scope=0,ident=b):0:3:b', + }, + }, + 'Arrow:0:4:->', + }, + }, + }, + err = { + arg = '', + msg = 'E15: Expected value, got EOC: %.*s', + }, + }, { + hl('Lambda', '{'), + hl('IdentifierName', 'a'), + hl('Comma', ','), + hl('IdentifierName', 'b'), + hl('Arrow', '->'), + }) + check_parsing('{a,b->c', 0, { + -- 0123456 + ast = { + { + 'Lambda:0:0:{', + children = { + { + 'Comma:0:2:,', + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + 'PlainIdentifier(scope=0,ident=b):0:3:b', + }, + }, + { + 'Arrow:0:4:->', + children = { + 'PlainIdentifier(scope=0,ident=c):0:6:c', + }, + }, + }, + }, + }, + err = { + arg = '{a,b->c', + msg = 'E15: Missing closing figure brace for lambda: %.*s', + }, + }, { + hl('Lambda', '{'), + hl('IdentifierName', 'a'), + hl('Comma', ','), + hl('IdentifierName', 'b'), + hl('Arrow', '->'), + hl('IdentifierName', 'c'), + }) + check_parsing('{a : b', 0, { + -- 012345 + ast = { + { + 'DictLiteral:0:0:{', + children = { + { + 'Colon:0:2: :', + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + 'PlainIdentifier(scope=0,ident=b):0:4: b', + }, + }, + }, + }, + }, + err = { + arg = '{a : b', + msg = 'E723: Missing end of Dictionary \'}\': %.*s', + }, + }, { + hl('Dict', '{'), + hl('IdentifierName', 'a'), + hl('Colon', ':', 1), + hl('IdentifierName', 'b', 1), + }) + check_parsing('{a : b,', 0, { + -- 0123456 + ast = { + { + 'DictLiteral:0:0:{', + children = { + { + 'Comma:0:6:,', + children = { + { + 'Colon:0:2: :', + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + 'PlainIdentifier(scope=0,ident=b):0:4: b', + }, + }, + }, + }, + }, + }, + }, + err = { + arg = '', + msg = 'E15: Expected value, got EOC: %.*s', + }, + }, { + hl('Dict', '{'), + hl('IdentifierName', 'a'), + hl('Colon', ':', 1), + hl('IdentifierName', 'b', 1), + hl('Comma', ','), + }) + end) + it('works with ternary operator', function() + check_parsing('a ? b : c', 0, { + -- 012345678 + ast = { + { + 'Ternary:0:1: ?', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + { + 'TernaryValue:0:5: :', + children = { + 'PlainIdentifier(scope=0,ident=b):0:3: b', + 'PlainIdentifier(scope=0,ident=c):0:7: c', + }, + }, + }, + }, + }, + }, { + hl('IdentifierName', 'a'), + hl('Ternary', '?', 1), + hl('IdentifierName', 'b', 1), + hl('TernaryColon', ':', 1), + hl('IdentifierName', 'c', 1), + }) + check_parsing('@a?@b?@c:@d:@e', 0, { + -- 01234567890123 + -- 0 1 + ast = { + { + 'Ternary:0:2:?', + children = { + 'Register(name=a):0:0:@a', + { + 'TernaryValue:0:11::', + children = { + { + 'Ternary:0:5:?', + children = { + 'Register(name=b):0:3:@b', + { + 'TernaryValue:0:8::', + children = { + 'Register(name=c):0:6:@c', + 'Register(name=d):0:9:@d', + }, + }, + }, + }, + 'Register(name=e):0:12:@e', + }, + }, + }, + }, + }, + }, { + hl('Register', '@a'), + hl('Ternary', '?'), + hl('Register', '@b'), + hl('Ternary', '?'), + hl('Register', '@c'), + hl('TernaryColon', ':'), + hl('Register', '@d'), + hl('TernaryColon', ':'), + hl('Register', '@e'), + }) + check_parsing('@a?@b:@c?@d:@e', 0, { + -- 01234567890123 + -- 0 1 + ast = { + { + 'Ternary:0:2:?', + children = { + 'Register(name=a):0:0:@a', + { + 'TernaryValue:0:5::', + children = { + 'Register(name=b):0:3:@b', + { + 'Ternary:0:8:?', + children = { + 'Register(name=c):0:6:@c', + { + 'TernaryValue:0:11::', + children = { + 'Register(name=d):0:9:@d', + 'Register(name=e):0:12:@e', + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, { + hl('Register', '@a'), + hl('Ternary', '?'), + hl('Register', '@b'), + hl('TernaryColon', ':'), + hl('Register', '@c'), + hl('Ternary', '?'), + hl('Register', '@d'), + hl('TernaryColon', ':'), + hl('Register', '@e'), + }) + check_parsing('@a?@b?@c?@d:@e?@f:@g:@h?@i:@j:@k', 0, { + -- 01234567890123456789012345678901 + -- 0 1 2 3 + ast = { + { + 'Ternary:0:2:?', + children = { + 'Register(name=a):0:0:@a', + { + 'TernaryValue:0:29::', + children = { + { + 'Ternary:0:5:?', + children = { + 'Register(name=b):0:3:@b', + { + 'TernaryValue:0:20::', + children = { + { + 'Ternary:0:8:?', + children = { + 'Register(name=c):0:6:@c', + { + 'TernaryValue:0:11::', + children = { + 'Register(name=d):0:9:@d', + { + 'Ternary:0:14:?', + children = { + 'Register(name=e):0:12:@e', + { + 'TernaryValue:0:17::', + children = { + 'Register(name=f):0:15:@f', + 'Register(name=g):0:18:@g', + }, + }, + }, + }, + }, + }, + }, + }, + { + 'Ternary:0:23:?', + children = { + 'Register(name=h):0:21:@h', + { + 'TernaryValue:0:26::', + children = { + 'Register(name=i):0:24:@i', + 'Register(name=j):0:27:@j', + }, + }, + }, + }, + }, + }, + }, + }, + 'Register(name=k):0:30:@k', + }, + }, + }, + }, + }, + }, { + hl('Register', '@a'), + hl('Ternary', '?'), + hl('Register', '@b'), + hl('Ternary', '?'), + hl('Register', '@c'), + hl('Ternary', '?'), + hl('Register', '@d'), + hl('TernaryColon', ':'), + hl('Register', '@e'), + hl('Ternary', '?'), + hl('Register', '@f'), + hl('TernaryColon', ':'), + hl('Register', '@g'), + hl('TernaryColon', ':'), + hl('Register', '@h'), + hl('Ternary', '?'), + hl('Register', '@i'), + hl('TernaryColon', ':'), + hl('Register', '@j'), + hl('TernaryColon', ':'), + hl('Register', '@k'), + }) + check_parsing('?', 0, { + -- 0 + ast = { + { + 'Ternary:0:0:?', + children = { + 'Missing:0:0:', + 'TernaryValue:0:0:?', + }, + }, + }, + err = { + arg = '?', + msg = 'E15: Expected value, got question mark: %.*s', + }, + }, { + hl('InvalidTernary', '?'), + }) + + check_parsing('?:', 0, { + -- 01 + ast = { + { + 'Ternary:0:0:?', + children = { + 'Missing:0:0:', + { + 'TernaryValue:0:1::', + children = { + 'Missing:0:1:', + }, + }, + }, + }, + }, + err = { + arg = '?:', + msg = 'E15: Expected value, got question mark: %.*s', + }, + }, { + hl('InvalidTernary', '?'), + hl('InvalidTernaryColon', ':'), + }) + + check_parsing('?::', 0, { + -- 012 + ast = { + { + 'Colon:0:2::', + children = { + { + 'Ternary:0:0:?', + children = { + 'Missing:0:0:', + { + 'TernaryValue:0:1::', + children = { + 'Missing:0:1:', + 'Missing:0:2:', + }, + }, + }, + }, + }, + }, + }, + err = { + arg = '?::', + msg = 'E15: Expected value, got question mark: %.*s', + }, + }, { + hl('InvalidTernary', '?'), + hl('InvalidTernaryColon', ':'), + hl('InvalidColon', ':'), + }) + + check_parsing('a?b', 0, { + -- 012 + ast = { + { + 'Ternary:0:1:?', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + { + 'TernaryValue:0:1:?', + children = { + 'PlainIdentifier(scope=0,ident=b):0:2:b', + }, + }, + }, + }, + }, + err = { + arg = '?b', + msg = 'E109: Missing \':\' after \'?\': %.*s', + }, + }, { + hl('IdentifierName', 'a'), + hl('Ternary', '?'), + hl('IdentifierName', 'b'), + }) + check_parsing('a?b:', 0, { + -- 0123 + ast = { + { + 'Ternary:0:1:?', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + { + 'TernaryValue:0:1:?', + children = { + 'PlainIdentifier(scope=b,ident=):0:2:b:', + }, + }, + }, + }, + }, + err = { + arg = '?b:', + msg = 'E109: Missing \':\' after \'?\': %.*s', + }, + }, { + hl('IdentifierName', 'a'), + hl('Ternary', '?'), + hl('IdentifierScope', 'b'), + hl('IdentifierScopeDelimiter', ':'), + }) + + check_parsing('a?b::c', 0, { + -- 012345 + ast = { + { + 'Ternary:0:1:?', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + { + 'TernaryValue:0:4::', + children = { + 'PlainIdentifier(scope=b,ident=):0:2:b:', + 'PlainIdentifier(scope=0,ident=c):0:5:c', + }, + }, + }, + }, + }, + }, { + hl('IdentifierName', 'a'), + hl('Ternary', '?'), + hl('IdentifierScope', 'b'), + hl('IdentifierScopeDelimiter', ':'), + hl('TernaryColon', ':'), + hl('IdentifierName', 'c'), + }) + + check_parsing('a?b :', 0, { + -- 01234 + ast = { + { + 'Ternary:0:1:?', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + { + 'TernaryValue:0:3: :', + children = { + 'PlainIdentifier(scope=0,ident=b):0:2:b', + }, + }, + }, + }, + }, + err = { + arg = '', + msg = 'E15: Expected value, got EOC: %.*s', + }, + }, { + hl('IdentifierName', 'a'), + hl('Ternary', '?'), + hl('IdentifierName', 'b'), + hl('TernaryColon', ':', 1), + }) + + check_parsing('(@a?@b:@c)?@d:@e', 0, { + -- 0123456789012345 + -- 0 1 + ast = { + { + 'Ternary:0:10:?', + children = { + { + 'Nested:0:0:(', + children = { + { + 'Ternary:0:3:?', + children = { + 'Register(name=a):0:1:@a', + { + 'TernaryValue:0:6::', + children = { + 'Register(name=b):0:4:@b', + 'Register(name=c):0:7:@c', + }, + }, + }, + }, + }, + }, + { + 'TernaryValue:0:13::', + children = { + 'Register(name=d):0:11:@d', + 'Register(name=e):0:14:@e', + }, + }, + }, + }, + }, + }, { + hl('NestingParenthesis', '('), + hl('Register', '@a'), + hl('Ternary', '?'), + hl('Register', '@b'), + hl('TernaryColon', ':'), + hl('Register', '@c'), + hl('NestingParenthesis', ')'), + hl('Ternary', '?'), + hl('Register', '@d'), + hl('TernaryColon', ':'), + hl('Register', '@e'), + }) + + check_parsing('(@a?@b:@c)?(@d?@e:@f):(@g?@h:@i)', 0, { + -- 01234567890123456789012345678901 + -- 0 1 2 3 + ast = { + { + 'Ternary:0:10:?', + children = { + { + 'Nested:0:0:(', + children = { + { + 'Ternary:0:3:?', + children = { + 'Register(name=a):0:1:@a', + { + 'TernaryValue:0:6::', + children = { + 'Register(name=b):0:4:@b', + 'Register(name=c):0:7:@c', + }, + }, + }, + }, + }, + }, + { + 'TernaryValue:0:21::', + children = { + { + 'Nested:0:11:(', + children = { + { + 'Ternary:0:14:?', + children = { + 'Register(name=d):0:12:@d', + { + 'TernaryValue:0:17::', + children = { + 'Register(name=e):0:15:@e', + 'Register(name=f):0:18:@f', + }, + }, + }, + }, + }, + }, + { + 'Nested:0:22:(', + children = { + { + 'Ternary:0:25:?', + children = { + 'Register(name=g):0:23:@g', + { + 'TernaryValue:0:28::', + children = { + 'Register(name=h):0:26:@h', + 'Register(name=i):0:29:@i', + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, { + hl('NestingParenthesis', '('), + hl('Register', '@a'), + hl('Ternary', '?'), + hl('Register', '@b'), + hl('TernaryColon', ':'), + hl('Register', '@c'), + hl('NestingParenthesis', ')'), + hl('Ternary', '?'), + hl('NestingParenthesis', '('), + hl('Register', '@d'), + hl('Ternary', '?'), + hl('Register', '@e'), + hl('TernaryColon', ':'), + hl('Register', '@f'), + hl('NestingParenthesis', ')'), + hl('TernaryColon', ':'), + hl('NestingParenthesis', '('), + hl('Register', '@g'), + hl('Ternary', '?'), + hl('Register', '@h'), + hl('TernaryColon', ':'), + hl('Register', '@i'), + hl('NestingParenthesis', ')'), + }) + + check_parsing('(@a?@b:@c)?@d?@e:@f:@g?@h:@i', 0, { + -- 0123456789012345678901234567 + -- 0 1 2 + ast = { + { + 'Ternary:0:10:?', + children = { + { + 'Nested:0:0:(', + children = { + { + 'Ternary:0:3:?', + children = { + 'Register(name=a):0:1:@a', + { + 'TernaryValue:0:6::', + children = { + 'Register(name=b):0:4:@b', + 'Register(name=c):0:7:@c', + }, + }, + }, + }, + }, + }, + { + 'TernaryValue:0:19::', + children = { + { + 'Ternary:0:13:?', + children = { + 'Register(name=d):0:11:@d', + { + 'TernaryValue:0:16::', + children = { + 'Register(name=e):0:14:@e', + 'Register(name=f):0:17:@f', + }, + }, + }, + }, + { + 'Ternary:0:22:?', + children = { + 'Register(name=g):0:20:@g', + { + 'TernaryValue:0:25::', + children = { + 'Register(name=h):0:23:@h', + 'Register(name=i):0:26:@i', + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, { + hl('NestingParenthesis', '('), + hl('Register', '@a'), + hl('Ternary', '?'), + hl('Register', '@b'), + hl('TernaryColon', ':'), + hl('Register', '@c'), + hl('NestingParenthesis', ')'), + hl('Ternary', '?'), + hl('Register', '@d'), + hl('Ternary', '?'), + hl('Register', '@e'), + hl('TernaryColon', ':'), + hl('Register', '@f'), + hl('TernaryColon', ':'), + hl('Register', '@g'), + hl('Ternary', '?'), + hl('Register', '@h'), + hl('TernaryColon', ':'), + hl('Register', '@i'), + }) + check_parsing('a?b{cdef}g:h', 0, { + -- 012345678901 + -- 0 1 + ast = { + { + 'Ternary:0:1:?', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + { + 'TernaryValue:0:10::', + children = { + { + 'ComplexIdentifier:0:3:', + children = { + 'PlainIdentifier(scope=0,ident=b):0:2:b', + { + 'ComplexIdentifier:0:9:', + children = { + { + 'CurlyBracesIdentifier:0:3:{', + children = { + 'PlainIdentifier(scope=0,ident=cdef):0:4:cdef', + }, + }, + 'PlainIdentifier(scope=0,ident=g):0:9:g', + }, + }, + }, + }, + 'PlainIdentifier(scope=0,ident=h):0:11:h', + }, + }, + }, + }, + }, + }, { + hl('IdentifierName', 'a'), + hl('Ternary', '?'), + hl('IdentifierName', 'b'), + hl('Curly', '{'), + hl('IdentifierName', 'cdef'), + hl('Curly', '}'), + hl('IdentifierName', 'g'), + hl('TernaryColon', ':'), + hl('IdentifierName', 'h'), + }) + check_parsing('a ? b : c : d', 0, { + -- 0123456789012 + -- 0 1 + ast = { + { + 'Colon:0:9: :', + children = { + { + 'Ternary:0:1: ?', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + { + 'TernaryValue:0:5: :', + children = { + 'PlainIdentifier(scope=0,ident=b):0:3: b', + 'PlainIdentifier(scope=0,ident=c):0:7: c', + }, + }, + }, + }, + 'PlainIdentifier(scope=0,ident=d):0:11: d', + }, + }, + }, + err = { + arg = ': d', + msg = 'E15: Colon outside of dictionary or ternary operator: %.*s', + }, + }, { + hl('IdentifierName', 'a'), + hl('Ternary', '?', 1), + hl('IdentifierName', 'b', 1), + hl('TernaryColon', ':', 1), + hl('IdentifierName', 'c', 1), + hl('InvalidColon', ':', 1), + hl('IdentifierName', 'd', 1), + }) + end) + it('works with comparison operators', function() + check_parsing('a == b', 0, { + -- 012345 + ast = { + { + 'Comparison(type=Equal,inv=0,ccs=UseOption):0:1: ==', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + 'PlainIdentifier(scope=0,ident=b):0:4: b', + }, + }, + }, + }, { + hl('IdentifierName', 'a'), + hl('Comparison', '==', 1), + hl('IdentifierName', 'b', 1), + }) + + check_parsing('a ==? b', 0, { + -- 0123456 + ast = { + { + 'Comparison(type=Equal,inv=0,ccs=IgnoreCase):0:1: ==?', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + 'PlainIdentifier(scope=0,ident=b):0:5: b', + }, + }, + }, + }, { + hl('IdentifierName', 'a'), + hl('Comparison', '==', 1), + hl('ComparisonModifier', '?'), + hl('IdentifierName', 'b', 1), + }) + + check_parsing('a ==# b', 0, { + -- 0123456 + ast = { + { + 'Comparison(type=Equal,inv=0,ccs=MatchCase):0:1: ==#', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + 'PlainIdentifier(scope=0,ident=b):0:5: b', + }, + }, + }, + }, { + hl('IdentifierName', 'a'), + hl('Comparison', '==', 1), + hl('ComparisonModifier', '#'), + hl('IdentifierName', 'b', 1), + }) + + check_parsing('a !=# b', 0, { + -- 0123456 + ast = { + { + 'Comparison(type=Equal,inv=1,ccs=MatchCase):0:1: !=#', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + 'PlainIdentifier(scope=0,ident=b):0:5: b', + }, + }, + }, + }, { + hl('IdentifierName', 'a'), + hl('Comparison', '!=', 1), + hl('ComparisonModifier', '#'), + hl('IdentifierName', 'b', 1), + }) + + check_parsing('a <=# b', 0, { + -- 0123456 + ast = { + { + 'Comparison(type=Greater,inv=1,ccs=MatchCase):0:1: <=#', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + 'PlainIdentifier(scope=0,ident=b):0:5: b', + }, + }, + }, + }, { + hl('IdentifierName', 'a'), + hl('Comparison', '<=', 1), + hl('ComparisonModifier', '#'), + hl('IdentifierName', 'b', 1), + }) + + check_parsing('a >=# b', 0, { + -- 0123456 + ast = { + { + 'Comparison(type=GreaterOrEqual,inv=0,ccs=MatchCase):0:1: >=#', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + 'PlainIdentifier(scope=0,ident=b):0:5: b', + }, + }, + }, + }, { + hl('IdentifierName', 'a'), + hl('Comparison', '>=', 1), + hl('ComparisonModifier', '#'), + hl('IdentifierName', 'b', 1), + }) + + check_parsing('a ># b', 0, { + -- 012345 + ast = { + { + 'Comparison(type=Greater,inv=0,ccs=MatchCase):0:1: >#', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + 'PlainIdentifier(scope=0,ident=b):0:4: b', + }, + }, + }, + }, { + hl('IdentifierName', 'a'), + hl('Comparison', '>', 1), + hl('ComparisonModifier', '#'), + hl('IdentifierName', 'b', 1), + }) + + check_parsing('a <# b', 0, { + -- 012345 + ast = { + { + 'Comparison(type=GreaterOrEqual,inv=1,ccs=MatchCase):0:1: <#', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + 'PlainIdentifier(scope=0,ident=b):0:4: b', + }, + }, + }, + }, { + hl('IdentifierName', 'a'), + hl('Comparison', '<', 1), + hl('ComparisonModifier', '#'), + hl('IdentifierName', 'b', 1), + }) + + check_parsing('a is#b', 0, { + -- 012345 + ast = { + { + 'Comparison(type=Identical,inv=0,ccs=MatchCase):0:1: is#', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + 'PlainIdentifier(scope=0,ident=b):0:5:b', + }, + }, + }, + }, { + hl('IdentifierName', 'a'), + hl('Comparison', 'is', 1), + hl('ComparisonModifier', '#'), + hl('IdentifierName', 'b'), + }) + + check_parsing('a is?b', 0, { + -- 012345 + ast = { + { + 'Comparison(type=Identical,inv=0,ccs=IgnoreCase):0:1: is?', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + 'PlainIdentifier(scope=0,ident=b):0:5:b', + }, + }, + }, + }, { + hl('IdentifierName', 'a'), + hl('Comparison', 'is', 1), + hl('ComparisonModifier', '?'), + hl('IdentifierName', 'b'), + }) + + check_parsing('a isnot b', 0, { + -- 012345678 + ast = { + { + 'Comparison(type=Identical,inv=1,ccs=UseOption):0:1: isnot', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + 'PlainIdentifier(scope=0,ident=b):0:7: b', + }, + }, + }, + }, { + hl('IdentifierName', 'a'), + hl('Comparison', 'isnot', 1), + hl('IdentifierName', 'b', 1), + }) + + check_parsing('a < b < c', 0, { + -- 012345678 + ast = { + { + 'Comparison(type=GreaterOrEqual,inv=1,ccs=UseOption):0:1: <', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + { + 'Comparison(type=GreaterOrEqual,inv=1,ccs=UseOption):0:5: <', + children = { + 'PlainIdentifier(scope=0,ident=b):0:3: b', + 'PlainIdentifier(scope=0,ident=c):0:7: c', + }, + }, + }, + }, + }, + err = { + arg = ' < c', + msg = 'E15: Operator is not associative: %.*s', + }, + }, { + hl('IdentifierName', 'a'), + hl('Comparison', '<', 1), + hl('IdentifierName', 'b', 1), + hl('InvalidComparison', '<', 1), + hl('IdentifierName', 'c', 1), + }) + + check_parsing('a < b <# c', 0, { + -- 012345678 + ast = { + { + 'Comparison(type=GreaterOrEqual,inv=1,ccs=UseOption):0:1: <', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + { + 'Comparison(type=GreaterOrEqual,inv=1,ccs=MatchCase):0:5: <#', + children = { + 'PlainIdentifier(scope=0,ident=b):0:3: b', + 'PlainIdentifier(scope=0,ident=c):0:8: c', + }, + }, + }, + }, + }, + err = { + arg = ' <# c', + msg = 'E15: Operator is not associative: %.*s', + }, + }, { + hl('IdentifierName', 'a'), + hl('Comparison', '<', 1), + hl('IdentifierName', 'b', 1), + hl('InvalidComparison', '<', 1), + hl('InvalidComparisonModifier', '#'), + hl('IdentifierName', 'c', 1), + }) + + check_parsing('a += b', 0, { + -- 012345 + ast = { + { + 'Comparison(type=Equal,inv=0,ccs=UseOption):0:3:=', + children = { + { + 'BinaryPlus:0:1: +', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + 'Missing:0:3:', + }, + }, + 'PlainIdentifier(scope=0,ident=b):0:4: b', + }, + }, + }, + err = { + arg = '= b', + msg = 'E15: Expected == or =~: %.*s', + }, + }, { + hl('IdentifierName', 'a'), + hl('BinaryPlus', '+', 1), + hl('InvalidComparison', '='), + hl('IdentifierName', 'b', 1), + }) + check_parsing('a + b == c + d', 0, { + -- 01234567890123 + -- 0 1 + ast = { + { + 'Comparison(type=Equal,inv=0,ccs=UseOption):0:5: ==', + children = { + { + 'BinaryPlus:0:1: +', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + 'PlainIdentifier(scope=0,ident=b):0:3: b', + }, + }, + { + 'BinaryPlus:0:10: +', + children = { + 'PlainIdentifier(scope=0,ident=c):0:8: c', + 'PlainIdentifier(scope=0,ident=d):0:12: d', + }, + }, + }, + }, + }, + }, { + hl('IdentifierName', 'a'), + hl('BinaryPlus', '+', 1), + hl('IdentifierName', 'b', 1), + hl('Comparison', '==', 1), + hl('IdentifierName', 'c', 1), + hl('BinaryPlus', '+', 1), + hl('IdentifierName', 'd', 1), + }) + check_parsing('+ a == + b', 0, { + -- 0123456789 + ast = { + { + 'Comparison(type=Equal,inv=0,ccs=UseOption):0:3: ==', + children = { + { + 'UnaryPlus:0:0:+', + children = { + 'PlainIdentifier(scope=0,ident=a):0:1: a', + }, + }, + { + 'UnaryPlus:0:6: +', + children = { + 'PlainIdentifier(scope=0,ident=b):0:8: b', + }, + }, + }, + }, + }, + }, { + hl('UnaryPlus', '+'), + hl('IdentifierName', 'a', 1), + hl('Comparison', '==', 1), + hl('UnaryPlus', '+', 1), + hl('IdentifierName', 'b', 1), + }) + end) + it('works with concat/subscript', function() + check_parsing('.', 0, { + -- 0 + ast = { + { + 'ConcatOrSubscript:0:0:.', + children = { + 'Missing:0:0:', + }, + }, + }, + err = { + arg = '.', + msg = 'E15: Unexpected dot: %.*s', + }, + }, { + hl('InvalidConcatOrSubscript', '.'), + }) + + check_parsing('a.', 0, { + -- 01 + ast = { + { + 'ConcatOrSubscript:0:1:.', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + }, + }, + }, + err = { + arg = '', + msg = 'E15: Expected value, got EOC: %.*s', + }, + }, { + hl('IdentifierName', 'a'), + hl('ConcatOrSubscript', '.'), + }) + + check_parsing('a.b', 0, { + -- 012 + ast = { + { + 'ConcatOrSubscript:0:1:.', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + 'PlainKey(key=b):0:2:b', + }, + }, + }, + }, { + hl('IdentifierName', 'a'), + hl('ConcatOrSubscript', '.'), + hl('IdentifierKey', 'b'), + }) + + check_parsing('1.2', 0, { + -- 012 + ast = { + 'Float(val=1.200000e+00):0:0:1.2', + }, + }, { + hl('Float', '1.2'), + }) + + check_parsing('1.2 + 1.3e-5', 0, { + -- 012345678901 + -- 0 1 + ast = { + { + 'BinaryPlus:0:3: +', + children = { + 'Float(val=1.200000e+00):0:0:1.2', + 'Float(val=1.300000e-05):0:5: 1.3e-5', + }, + }, + }, + }, { + hl('Float', '1.2'), + hl('BinaryPlus', '+', 1), + hl('Float', '1.3e-5', 1), + }) + + check_parsing('a . 1.2 + 1.3e-5', 0, { + -- 0123456789012345 + -- 0 1 + ast = { + { + 'BinaryPlus:0:7: +', + children = { + { + 'Concat:0:1: .', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + { + 'ConcatOrSubscript:0:5:.', + children = { + 'Integer(val=1):0:3: 1', + 'PlainKey(key=2):0:6:2', + }, + }, + }, + }, + 'Float(val=1.300000e-05):0:9: 1.3e-5', + }, + }, + }, + }, { + hl('IdentifierName', 'a'), + hl('Concat', '.', 1), + hl('Number', '1', 1), + hl('ConcatOrSubscript', '.'), + hl('IdentifierKey', '2'), + hl('BinaryPlus', '+', 1), + hl('Float', '1.3e-5', 1), + }) + + check_parsing('1.3e-5 + 1.2 . a', 0, { + -- 0123456789012345 + -- 0 1 + ast = { + { + 'Concat:0:12: .', + children = { + { + 'BinaryPlus:0:6: +', + children = { + 'Float(val=1.300000e-05):0:0:1.3e-5', + 'Float(val=1.200000e+00):0:8: 1.2', + }, + }, + 'PlainIdentifier(scope=0,ident=a):0:14: a', + }, + }, + }, + }, { + hl('Float', '1.3e-5'), + hl('BinaryPlus', '+', 1), + hl('Float', '1.2', 1), + hl('Concat', '.', 1), + hl('IdentifierName', 'a', 1), + }) + + check_parsing('1.3e-5 + a . 1.2', 0, { + -- 0123456789012345 + -- 0 1 + ast = { + { + 'Concat:0:10: .', + children = { + { + 'BinaryPlus:0:6: +', + children = { + 'Float(val=1.300000e-05):0:0:1.3e-5', + 'PlainIdentifier(scope=0,ident=a):0:8: a', + }, + }, + { + 'ConcatOrSubscript:0:14:.', + children = { + 'Integer(val=1):0:12: 1', + 'PlainKey(key=2):0:15:2', + }, + }, + }, + }, + }, + }, { + hl('Float', '1.3e-5'), + hl('BinaryPlus', '+', 1), + hl('IdentifierName', 'a', 1), + hl('Concat', '.', 1), + hl('Number', '1', 1), + hl('ConcatOrSubscript', '.'), + hl('IdentifierKey', '2'), + }) + + check_parsing('1.2.3', 0, { + -- 01234 + ast = { + { + 'ConcatOrSubscript:0:3:.', + children = { + { + 'ConcatOrSubscript:0:1:.', + children = { + 'Integer(val=1):0:0:1', + 'PlainKey(key=2):0:2:2', + }, + }, + 'PlainKey(key=3):0:4:3', + }, + }, + }, + }, { + hl('Number', '1'), + hl('ConcatOrSubscript', '.'), + hl('IdentifierKey', '2'), + hl('ConcatOrSubscript', '.'), + hl('IdentifierKey', '3'), + }) + + check_parsing('a.1.2', 0, { + -- 01234 + ast = { + { + 'ConcatOrSubscript:0:3:.', + children = { + { + 'ConcatOrSubscript:0:1:.', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + 'PlainKey(key=1):0:2:1', + }, + }, + 'PlainKey(key=2):0:4:2', + }, + }, + }, + }, { + hl('IdentifierName', 'a'), + hl('ConcatOrSubscript', '.'), + hl('IdentifierKey', '1'), + hl('ConcatOrSubscript', '.'), + hl('IdentifierKey', '2'), + }) + + check_parsing('a . 1.2', 0, { + -- 0123456 + ast = { + { + 'Concat:0:1: .', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + { + 'ConcatOrSubscript:0:5:.', + children = { + 'Integer(val=1):0:3: 1', + 'PlainKey(key=2):0:6:2', + }, + }, + }, + }, + }, + }, { + hl('IdentifierName', 'a'), + hl('Concat', '.', 1), + hl('Number', '1', 1), + hl('ConcatOrSubscript', '.'), + hl('IdentifierKey', '2'), + }) + + check_parsing('+a . +b', 0, { + -- 0123456 + ast = { + { + 'Concat:0:2: .', + children = { + { + 'UnaryPlus:0:0:+', + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + }, + }, + { + 'UnaryPlus:0:4: +', + children = { + 'PlainIdentifier(scope=0,ident=b):0:6:b', + }, + }, + }, + }, + }, + }, { + hl('UnaryPlus', '+'), + hl('IdentifierName', 'a'), + hl('Concat', '.', 1), + hl('UnaryPlus', '+', 1), + hl('IdentifierName', 'b'), + }) + + check_parsing('a. b', 0, { + -- 0123 + ast = { + { + 'Concat:0:1:.', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + 'PlainIdentifier(scope=0,ident=b):0:2: b', + }, + }, + }, + }, { + hl('IdentifierName', 'a'), + hl('ConcatOrSubscript', '.'), + hl('IdentifierName', 'b', 1), + }) + + check_parsing('a. 1', 0, { + -- 0123 + ast = { + { + 'Concat:0:1:.', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + 'Integer(val=1):0:2: 1', + }, + }, + }, + }, { + hl('IdentifierName', 'a'), + hl('ConcatOrSubscript', '.'), + hl('Number', '1', 1), + }) + end) + it('works with bracket subscripts', function() + check_parsing(':', 0, { + -- 0 + ast = { + { + 'Colon:0:0::', + children = { + 'Missing:0:0:', + }, + }, + }, + err = { + arg = ':', + msg = 'E15: Colon outside of dictionary or ternary operator: %.*s', + }, + }, { + hl('InvalidColon', ':'), + }) + check_parsing('a[]', 0, { + -- 012 + ast = { + { + 'Subscript:0:1:[', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + }, + }, + }, + err = { + arg = ']', + msg = 'E15: Expected value, got closing bracket: %.*s', + }, + }, { + hl('IdentifierName', 'a'), + hl('SubscriptBracket', '['), + hl('InvalidSubscriptBracket', ']'), + }) + check_parsing('a[b:]', 0, { + -- 01234 + ast = { + { + 'Subscript:0:1:[', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + 'PlainIdentifier(scope=b,ident=):0:2:b:', + }, + }, + }, + }, { + hl('IdentifierName', 'a'), + hl('SubscriptBracket', '['), + hl('IdentifierScope', 'b'), + hl('IdentifierScopeDelimiter', ':'), + hl('SubscriptBracket', ']'), + }) + + check_parsing('a[b:c]', 0, { + -- 012345 + ast = { + { + 'Subscript:0:1:[', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + 'PlainIdentifier(scope=b,ident=c):0:2:b:c', + }, + }, + }, + }, { + hl('IdentifierName', 'a'), + hl('SubscriptBracket', '['), + hl('IdentifierScope', 'b'), + hl('IdentifierScopeDelimiter', ':'), + hl('IdentifierName', 'c'), + hl('SubscriptBracket', ']'), + }) + check_parsing('a[b : c]', 0, { + -- 01234567 + ast = { + { + 'Subscript:0:1:[', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + { + 'Colon:0:3: :', + children = { + 'PlainIdentifier(scope=0,ident=b):0:2:b', + 'PlainIdentifier(scope=0,ident=c):0:5: c', + }, + }, + }, + }, + }, + }, { + hl('IdentifierName', 'a'), + hl('SubscriptBracket', '['), + hl('IdentifierName', 'b'), + hl('SubscriptColon', ':', 1), + hl('IdentifierName', 'c', 1), + hl('SubscriptBracket', ']'), + }) + + check_parsing('a[: b]', 0, { + -- 012345 + ast = { + { + 'Subscript:0:1:[', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + { + 'Colon:0:2::', + children = { + 'Missing:0:2:', + 'PlainIdentifier(scope=0,ident=b):0:3: b', + }, + }, + }, + }, + }, + }, { + hl('IdentifierName', 'a'), + hl('SubscriptBracket', '['), + hl('SubscriptColon', ':'), + hl('IdentifierName', 'b', 1), + hl('SubscriptBracket', ']'), + }) + + check_parsing('a[b :]', 0, { + -- 012345 + ast = { + { + 'Subscript:0:1:[', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + { + 'Colon:0:3: :', + children = { + 'PlainIdentifier(scope=0,ident=b):0:2:b', + }, + }, + }, + }, + }, + }, { + hl('IdentifierName', 'a'), + hl('SubscriptBracket', '['), + hl('IdentifierName', 'b'), + hl('SubscriptColon', ':', 1), + hl('SubscriptBracket', ']'), + }) + check_parsing('a[b][c][d](e)(f)(g)', 0, { + -- 0123456789012345678 + -- 0 1 + ast = { + { + 'Call:0:16:(', + children = { + { + 'Call:0:13:(', + children = { + { + 'Call:0:10:(', + children = { + { + 'Subscript:0:7:[', + children = { + { + 'Subscript:0:4:[', + children = { + { + 'Subscript:0:1:[', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + 'PlainIdentifier(scope=0,ident=b):0:2:b', + }, + }, + 'PlainIdentifier(scope=0,ident=c):0:5:c', + }, + }, + 'PlainIdentifier(scope=0,ident=d):0:8:d', + }, + }, + 'PlainIdentifier(scope=0,ident=e):0:11:e', + }, + }, + 'PlainIdentifier(scope=0,ident=f):0:14:f', + }, + }, + 'PlainIdentifier(scope=0,ident=g):0:17:g', + }, + }, + }, + }, { + hl('IdentifierName', 'a'), + hl('SubscriptBracket', '['), + hl('IdentifierName', 'b'), + hl('SubscriptBracket', ']'), + hl('SubscriptBracket', '['), + hl('IdentifierName', 'c'), + hl('SubscriptBracket', ']'), + hl('SubscriptBracket', '['), + hl('IdentifierName', 'd'), + hl('SubscriptBracket', ']'), + hl('CallingParenthesis', '('), + hl('IdentifierName', 'e'), + hl('CallingParenthesis', ')'), + hl('CallingParenthesis', '('), + hl('IdentifierName', 'f'), + hl('CallingParenthesis', ')'), + hl('CallingParenthesis', '('), + hl('IdentifierName', 'g'), + hl('CallingParenthesis', ')'), + }) + check_parsing('{a}{b}{c}[d][e][f]', 0, { + -- 012345678901234567 + -- 0 1 + ast = { + { + 'Subscript:0:15:[', + children = { + { + 'Subscript:0:12:[', + children = { + { + 'Subscript:0:9:[', + children = { + { + 'ComplexIdentifier:0:3:', + children = { + { + 'CurlyBracesIdentifier:0:0:{', + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + }, + }, + { + 'ComplexIdentifier:0:6:', + children = { + { + 'CurlyBracesIdentifier:0:3:{', + children = { + 'PlainIdentifier(scope=0,ident=b):0:4:b', + }, + }, + { + 'CurlyBracesIdentifier:0:6:{', + children = { + 'PlainIdentifier(scope=0,ident=c):0:7:c', + }, + }, + }, + }, + }, + }, + 'PlainIdentifier(scope=0,ident=d):0:10:d', + }, + }, + 'PlainIdentifier(scope=0,ident=e):0:13:e', + }, + }, + 'PlainIdentifier(scope=0,ident=f):0:16:f', + }, + }, + }, + }, { + hl('Curly', '{'), + hl('IdentifierName', 'a'), + hl('Curly', '}'), + hl('Curly', '{'), + hl('IdentifierName', 'b'), + hl('Curly', '}'), + hl('Curly', '{'), + hl('IdentifierName', 'c'), + hl('Curly', '}'), + hl('SubscriptBracket', '['), + hl('IdentifierName', 'd'), + hl('SubscriptBracket', ']'), + hl('SubscriptBracket', '['), + hl('IdentifierName', 'e'), + hl('SubscriptBracket', ']'), + hl('SubscriptBracket', '['), + hl('IdentifierName', 'f'), + hl('SubscriptBracket', ']'), + }) + end) + it('supports list literals', function() + check_parsing('[]', 0, { + -- 01 + ast = { + 'ListLiteral:0:0:[', + }, + }, { + hl('List', '['), + hl('List', ']'), + }) + + check_parsing('[a]', 0, { + -- 012 + ast = { + { + 'ListLiteral:0:0:[', + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + }, + }, + }, + }, { + hl('List', '['), + hl('IdentifierName', 'a'), + hl('List', ']'), + }) + + check_parsing('[a, b]', 0, { + -- 012345 + ast = { + { + 'ListLiteral:0:0:[', + children = { + { + 'Comma:0:2:,', + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + 'PlainIdentifier(scope=0,ident=b):0:3: b', + }, + }, + }, + }, + }, + }, { + hl('List', '['), + hl('IdentifierName', 'a'), + hl('Comma', ','), + hl('IdentifierName', 'b', 1), + hl('List', ']'), + }) + + check_parsing('[a, b, c]', 0, { + -- 012345678 + ast = { + { + 'ListLiteral:0:0:[', + children = { + { + 'Comma:0:2:,', + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + { + 'Comma:0:5:,', + children = { + 'PlainIdentifier(scope=0,ident=b):0:3: b', + 'PlainIdentifier(scope=0,ident=c):0:6: c', + }, + }, + }, + }, + }, + }, + }, + }, { + hl('List', '['), + hl('IdentifierName', 'a'), + hl('Comma', ','), + hl('IdentifierName', 'b', 1), + hl('Comma', ','), + hl('IdentifierName', 'c', 1), + hl('List', ']'), + }) + + check_parsing('[a, b, c, ]', 0, { + -- 01234567890 + -- 0 1 + ast = { + { + 'ListLiteral:0:0:[', + children = { + { + 'Comma:0:2:,', + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + { + 'Comma:0:5:,', + children = { + 'PlainIdentifier(scope=0,ident=b):0:3: b', + { + 'Comma:0:8:,', + children = { + 'PlainIdentifier(scope=0,ident=c):0:6: c', + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, { + hl('List', '['), + hl('IdentifierName', 'a'), + hl('Comma', ','), + hl('IdentifierName', 'b', 1), + hl('Comma', ','), + hl('IdentifierName', 'c', 1), + hl('Comma', ','), + hl('List', ']', 1), + }) + + check_parsing('[a : b, c : d]', 0, { + -- 01234567890123 + -- 0 1 + ast = { + { + 'ListLiteral:0:0:[', + children = { + { + 'Comma:0:6:,', + children = { + { + 'Colon:0:2: :', + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + 'PlainIdentifier(scope=0,ident=b):0:4: b', + }, + }, + { + 'Colon:0:9: :', + children = { + 'PlainIdentifier(scope=0,ident=c):0:7: c', + 'PlainIdentifier(scope=0,ident=d):0:11: d', + }, + }, + }, + }, + }, + }, + }, + err = { + arg = ': b, c : d]', + msg = 'E15: Colon outside of dictionary or ternary operator: %.*s', + }, + }, { + hl('List', '['), + hl('IdentifierName', 'a'), + hl('InvalidColon', ':', 1), + hl('IdentifierName', 'b', 1), + hl('Comma', ','), + hl('IdentifierName', 'c', 1), + hl('InvalidColon', ':', 1), + hl('IdentifierName', 'd', 1), + hl('List', ']'), + }) + + check_parsing(']', 0, { + -- 0 + ast = { + 'ListLiteral:0:0:', + }, + err = { + arg = ']', + msg = 'E15: Unexpected closing figure brace: %.*s', + }, + }, { + hl('InvalidList', ']'), + }) + + check_parsing('a]', 0, { + -- 01 + ast = { + { + 'ListLiteral:0:1:', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + }, + }, + }, + err = { + arg = ']', + msg = 'E15: Unexpected closing figure brace: %.*s', + }, + }, { + hl('IdentifierName', 'a'), + hl('InvalidList', ']'), + }) + + check_parsing('[] []', 0, { + -- 01234 + ast = { + { + 'OpMissing:0:2:', + children = { + 'ListLiteral:0:0:[', + 'ListLiteral:0:2: [', + }, + }, + }, + err = { + arg = '[]', + msg = 'E15: Missing operator: %.*s', + }, + }, { + hl('List', '['), + hl('List', ']'), + hl('InvalidSpacing', ' '), + hl('List', '['), + hl('List', ']'), + }) + + check_parsing('[][]', 0, { + -- 0123 + ast = { + { + 'Subscript:0:2:[', + children = { + 'ListLiteral:0:0:[', + }, + }, + }, + err = { + arg = ']', + msg = 'E15: Expected value, got closing bracket: %.*s', + }, + }, { + hl('List', '['), + hl('List', ']'), + hl('SubscriptBracket', '['), + hl('InvalidSubscriptBracket', ']'), + }) + + check_parsing('[', 0, { + -- 0 + ast = { + 'ListLiteral:0:0:[', + }, + err = { + arg = '', + msg = 'E15: Expected value, got EOC: %.*s', + }, + }, { + hl('List', '['), + }) + + check_parsing('[1', 0, { + -- 01 + ast = { + { + 'ListLiteral:0:0:[', + children = { + 'Integer(val=1):0:1:1', + }, + }, + }, + err = { + arg = '[1', + msg = 'E697: Missing end of List \']\': %.*s', + }, + }, { + hl('List', '['), + hl('Number', '1'), + }) + end) + it('works with strings', function() + check_parsing('\'abc\'', 0, { + -- 01234 + ast = { + 'SingleQuotedString(val="abc"):0:0:\'abc\'', + }, + }, { + hl('SingleQuote', '\''), + hl('SingleQuotedBody', 'abc'), + hl('SingleQuote', '\''), + }) + check_parsing('"abc"', 0, { + -- 01234 + ast = { + 'DoubleQuotedString(val="abc"):0:0:"abc"', + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedBody', 'abc'), + hl('DoubleQuote', '"'), + }) + check_parsing('\'\'', 0, { + -- 01 + ast = { + 'SingleQuotedString(val=""):0:0:\'\'', + }, + }, { + hl('SingleQuote', '\''), + hl('SingleQuote', '\''), + }) + check_parsing('""', 0, { + -- 01 + ast = { + 'DoubleQuotedString(val=""):0:0:""', + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuote', '"'), + }) + check_parsing('"', 0, { + -- 0 + ast = { + 'DoubleQuotedString(val=""):0:0:"', + }, + err = { + arg = '"', + msg = 'E114: Missing double quote: %.*s', + }, + }, { + hl('InvalidDoubleQuote', '"'), + }) + check_parsing('\'', 0, { + -- 0 + ast = { + 'SingleQuotedString(val=""):0:0:\'', + }, + err = { + arg = '\'', + msg = 'E115: Missing single quote: %.*s', + }, + }, { + hl('InvalidSingleQuote', '\''), + }) + check_parsing('"a', 0, { + -- 01 + ast = { + 'DoubleQuotedString(val="a"):0:0:"a', + }, + err = { + arg = '"a', + msg = 'E114: Missing double quote: %.*s', + }, + }, { + hl('InvalidDoubleQuote', '"'), + hl('InvalidDoubleQuotedBody', 'a'), + }) + check_parsing('\'a', 0, { + -- 01 + ast = { + 'SingleQuotedString(val="a"):0:0:\'a', + }, + err = { + arg = '\'a', + msg = 'E115: Missing single quote: %.*s', + }, + }, { + hl('InvalidSingleQuote', '\''), + hl('InvalidSingleQuotedBody', 'a'), + }) + check_parsing('\'abc\'\'def\'', 0, { + -- 0123456789 + ast = { + 'SingleQuotedString(val="abc\'def"):0:0:\'abc\'\'def\'', + }, + }, { + hl('SingleQuote', '\''), + hl('SingleQuotedBody', 'abc'), + hl('SingleQuotedQuote', '\'\''), + hl('SingleQuotedBody', 'def'), + hl('SingleQuote', '\''), + }) + check_parsing('\'abc\'\'', 0, { + -- 012345 + ast = { + 'SingleQuotedString(val="abc\'"):0:0:\'abc\'\'', + }, + err = { + arg = '\'abc\'\'', + msg = 'E115: Missing single quote: %.*s', + }, + }, { + hl('InvalidSingleQuote', '\''), + hl('InvalidSingleQuotedBody', 'abc'), + hl('InvalidSingleQuotedQuote', '\'\''), + }) + check_parsing('\'\'\'\'\'\'\'\'', 0, { + -- 01234567 + ast = { + 'SingleQuotedString(val="\'\'\'"):0:0:\'\'\'\'\'\'\'\'', + }, + }, { + hl('SingleQuote', '\''), + hl('SingleQuotedQuote', '\'\''), + hl('SingleQuotedQuote', '\'\''), + hl('SingleQuotedQuote', '\'\''), + hl('SingleQuote', '\''), + }) + check_parsing('\'\'\'a\'\'\'\'bc\'', 0, { + -- 01234567890 + -- 0 1 + ast = { + 'SingleQuotedString(val="\'a\'\'bc"):0:0:\'\'\'a\'\'\'\'bc\'', + }, + }, { + hl('SingleQuote', '\''), + hl('SingleQuotedQuote', '\'\''), + hl('SingleQuotedBody', 'a'), + hl('SingleQuotedQuote', '\'\''), + hl('SingleQuotedQuote', '\'\''), + hl('SingleQuotedBody', 'bc'), + hl('SingleQuote', '\''), + }) + check_parsing('"\\"\\"\\"\\""', 0, { + -- 0123456789 + ast = { + 'DoubleQuotedString(val="\\"\\"\\"\\""):0:0:"\\"\\"\\"\\""', + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedEscape', '\\"'), + hl('DoubleQuotedEscape', '\\"'), + hl('DoubleQuotedEscape', '\\"'), + hl('DoubleQuotedEscape', '\\"'), + hl('DoubleQuote', '"'), + }) + check_parsing('"abc\\"def\\"ghi\\"jkl\\"mno"', 0, { + -- 0123456789012345678901234 + -- 0 1 2 + ast = { + 'DoubleQuotedString(val="abc\\"def\\"ghi\\"jkl\\"mno"):0:0:"abc\\"def\\"ghi\\"jkl\\"mno"', + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedBody', 'abc'), + hl('DoubleQuotedEscape', '\\"'), + hl('DoubleQuotedBody', 'def'), + hl('DoubleQuotedEscape', '\\"'), + hl('DoubleQuotedBody', 'ghi'), + hl('DoubleQuotedEscape', '\\"'), + hl('DoubleQuotedBody', 'jkl'), + hl('DoubleQuotedEscape', '\\"'), + hl('DoubleQuotedBody', 'mno'), + hl('DoubleQuote', '"'), + }) + check_parsing('"\\b\\e\\f\\r\\t\\\\"', 0, { + -- 0123456789012345 + -- 0 1 + ast = { + [[DoubleQuotedString(val="\8\27\12\13\9\\"):0:0:"\b\e\f\r\t\\"]], + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedEscape', '\\b'), + hl('DoubleQuotedEscape', '\\e'), + hl('DoubleQuotedEscape', '\\f'), + hl('DoubleQuotedEscape', '\\r'), + hl('DoubleQuotedEscape', '\\t'), + hl('DoubleQuotedEscape', '\\\\'), + hl('DoubleQuote', '"'), + }) + check_parsing('"\\n\n"', 0, { + -- 01234 + ast = { + 'DoubleQuotedString(val="\\\n\\\n"):0:0:"\\n\n"', + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedEscape', '\\n'), + hl('DoubleQuotedBody', '\n'), + hl('DoubleQuote', '"'), + }) + check_parsing('"\\x00"', 0, { + -- 012345 + ast = { + 'DoubleQuotedString(val="\\0"):0:0:"\\x00"', + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedEscape', '\\x00'), + hl('DoubleQuote', '"'), + }) + check_parsing('"\\xFF"', 0, { + -- 012345 + ast = { + 'DoubleQuotedString(val="\255"):0:0:"\\xFF"', + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedEscape', '\\xFF'), + hl('DoubleQuote', '"'), + }) + check_parsing('"\\xF"', 0, { + -- 012345 + ast = { + 'DoubleQuotedString(val="\\15"):0:0:"\\xF"', + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedEscape', '\\xF'), + hl('DoubleQuote', '"'), + }) + check_parsing('"\\u00AB"', 0, { + -- 01234567 + ast = { + 'DoubleQuotedString(val="«"):0:0:"\\u00AB"', + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedEscape', '\\u00AB'), + hl('DoubleQuote', '"'), + }) + check_parsing('"\\U000000AB"', 0, { + -- 01234567 + ast = { + 'DoubleQuotedString(val="«"):0:0:"\\U000000AB"', + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedEscape', '\\U000000AB'), + hl('DoubleQuote', '"'), + }) + check_parsing('"\\x"', 0, { + -- 0123 + ast = { + 'DoubleQuotedString(val="x"):0:0:"\\x"', + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedUnknownEscape', '\\x'), + hl('DoubleQuote', '"'), + }) + + check_parsing('"\\x', 0, { + -- 012 + ast = { + 'DoubleQuotedString(val="x"):0:0:"\\x', + }, + err = { + arg = '"\\x', + msg = 'E114: Missing double quote: %.*s', + }, + }, { + hl('InvalidDoubleQuote', '"'), + hl('InvalidDoubleQuotedUnknownEscape', '\\x'), + }) + + check_parsing('"\\xF', 0, { + -- 0123 + ast = { + 'DoubleQuotedString(val="\\15"):0:0:"\\xF', + }, + err = { + arg = '"\\xF', + msg = 'E114: Missing double quote: %.*s', + }, + }, { + hl('InvalidDoubleQuote', '"'), + hl('InvalidDoubleQuotedEscape', '\\xF'), + }) + + check_parsing('"\\u"', 0, { + -- 0123 + ast = { + 'DoubleQuotedString(val="u"):0:0:"\\u"', + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedUnknownEscape', '\\u'), + hl('DoubleQuote', '"'), + }) + + check_parsing('"\\u', 0, { + -- 012 + ast = { + 'DoubleQuotedString(val="u"):0:0:"\\u', + }, + err = { + arg = '"\\u', + msg = 'E114: Missing double quote: %.*s', + }, + }, { + hl('InvalidDoubleQuote', '"'), + hl('InvalidDoubleQuotedUnknownEscape', '\\u'), + }) + + check_parsing('"\\U', 0, { + -- 012 + ast = { + 'DoubleQuotedString(val="U"):0:0:"\\U', + }, + err = { + arg = '"\\U', + msg = 'E114: Missing double quote: %.*s', + }, + }, { + hl('InvalidDoubleQuote', '"'), + hl('InvalidDoubleQuotedUnknownEscape', '\\U'), + }) + + check_parsing('"\\U"', 0, { + -- 0123 + ast = { + 'DoubleQuotedString(val="U"):0:0:"\\U"', + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedUnknownEscape', '\\U'), + hl('DoubleQuote', '"'), + }) + + check_parsing('"\\xFX"', 0, { + -- 012345 + ast = { + 'DoubleQuotedString(val="\\15X"):0:0:"\\xFX"', + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedEscape', '\\xF'), + hl('DoubleQuotedBody', 'X'), + hl('DoubleQuote', '"'), + }) + + check_parsing('"\\XFX"', 0, { + -- 012345 + ast = { + 'DoubleQuotedString(val="\\15X"):0:0:"\\XFX"', + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedEscape', '\\XF'), + hl('DoubleQuotedBody', 'X'), + hl('DoubleQuote', '"'), + }) + + check_parsing('"\\xX"', 0, { + -- 01234 + ast = { + 'DoubleQuotedString(val="xX"):0:0:"\\xX"', + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedUnknownEscape', '\\x'), + hl('DoubleQuotedBody', 'X'), + hl('DoubleQuote', '"'), + }) + + check_parsing('"\\XX"', 0, { + -- 01234 + ast = { + 'DoubleQuotedString(val="XX"):0:0:"\\XX"', + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedUnknownEscape', '\\X'), + hl('DoubleQuotedBody', 'X'), + hl('DoubleQuote', '"'), + }) + + check_parsing('"\\uX"', 0, { + -- 01234 + ast = { + 'DoubleQuotedString(val="uX"):0:0:"\\uX"', + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedUnknownEscape', '\\u'), + hl('DoubleQuotedBody', 'X'), + hl('DoubleQuote', '"'), + }) + + check_parsing('"\\UX"', 0, { + -- 01234 + ast = { + 'DoubleQuotedString(val="UX"):0:0:"\\UX"', + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedUnknownEscape', '\\U'), + hl('DoubleQuotedBody', 'X'), + hl('DoubleQuote', '"'), + }) + + check_parsing('"\\x0X"', 0, { + -- 012345 + ast = { + 'DoubleQuotedString(val="\\0X"):0:0:"\\x0X"', + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedEscape', '\\x0'), + hl('DoubleQuotedBody', 'X'), + hl('DoubleQuote', '"'), + }) + + check_parsing('"\\X0X"', 0, { + -- 012345 + ast = { + 'DoubleQuotedString(val="\\0X"):0:0:"\\X0X"', + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedEscape', '\\X0'), + hl('DoubleQuotedBody', 'X'), + hl('DoubleQuote', '"'), + }) + + check_parsing('"\\u0X"', 0, { + -- 012345 + ast = { + 'DoubleQuotedString(val="\\0X"):0:0:"\\u0X"', + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedEscape', '\\u0'), + hl('DoubleQuotedBody', 'X'), + hl('DoubleQuote', '"'), + }) + + check_parsing('"\\U0X"', 0, { + -- 012345 + ast = { + 'DoubleQuotedString(val="\\0X"):0:0:"\\U0X"', + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedEscape', '\\U0'), + hl('DoubleQuotedBody', 'X'), + hl('DoubleQuote', '"'), + }) + + check_parsing('"\\x00X"', 0, { + -- 0123456 + ast = { + 'DoubleQuotedString(val="\\0X"):0:0:"\\x00X"', + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedEscape', '\\x00'), + hl('DoubleQuotedBody', 'X'), + hl('DoubleQuote', '"'), + }) + + check_parsing('"\\X00X"', 0, { + -- 0123456 + ast = { + 'DoubleQuotedString(val="\\0X"):0:0:"\\X00X"', + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedEscape', '\\X00'), + hl('DoubleQuotedBody', 'X'), + hl('DoubleQuote', '"'), + }) + + check_parsing('"\\u00X"', 0, { + -- 0123456 + ast = { + 'DoubleQuotedString(val="\\0X"):0:0:"\\u00X"', + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedEscape', '\\u00'), + hl('DoubleQuotedBody', 'X'), + hl('DoubleQuote', '"'), + }) + + check_parsing('"\\U00X"', 0, { + -- 0123456 + ast = { + 'DoubleQuotedString(val="\\0X"):0:0:"\\U00X"', + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedEscape', '\\U00'), + hl('DoubleQuotedBody', 'X'), + hl('DoubleQuote', '"'), + }) + + check_parsing('"\\u000X"', 0, { + -- 01234567 + ast = { + 'DoubleQuotedString(val="\\0X"):0:0:"\\u000X"', + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedEscape', '\\u000'), + hl('DoubleQuotedBody', 'X'), + hl('DoubleQuote', '"'), + }) + + check_parsing('"\\U000X"', 0, { + -- 01234567 + ast = { + 'DoubleQuotedString(val="\\0X"):0:0:"\\U000X"', + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedEscape', '\\U000'), + hl('DoubleQuotedBody', 'X'), + hl('DoubleQuote', '"'), + }) + + check_parsing('"\\u0000X"', 0, { + -- 012345678 + ast = { + 'DoubleQuotedString(val="\\0X"):0:0:"\\u0000X"', + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedEscape', '\\u0000'), + hl('DoubleQuotedBody', 'X'), + hl('DoubleQuote', '"'), + }) + + check_parsing('"\\U0000X"', 0, { + -- 012345678 + ast = { + 'DoubleQuotedString(val="\\0X"):0:0:"\\U0000X"', + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedEscape', '\\U0000'), + hl('DoubleQuotedBody', 'X'), + hl('DoubleQuote', '"'), + }) + + check_parsing('"\\U00000X"', 0, { + -- 0123456789 + ast = { + 'DoubleQuotedString(val="\\0X"):0:0:"\\U00000X"', + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedEscape', '\\U00000'), + hl('DoubleQuotedBody', 'X'), + hl('DoubleQuote', '"'), + }) + + check_parsing('"\\U000000X"', 0, { + -- 01234567890 + -- 0 1 + ast = { + 'DoubleQuotedString(val="\\0X"):0:0:"\\U000000X"', + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedEscape', '\\U000000'), + hl('DoubleQuotedBody', 'X'), + hl('DoubleQuote', '"'), + }) + + check_parsing('"\\U0000000X"', 0, { + -- 012345678901 + -- 0 1 + ast = { + 'DoubleQuotedString(val="\\0X"):0:0:"\\U0000000X"', + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedEscape', '\\U0000000'), + hl('DoubleQuotedBody', 'X'), + hl('DoubleQuote', '"'), + }) + + check_parsing('"\\U00000000X"', 0, { + -- 0123456789012 + -- 0 1 + ast = { + 'DoubleQuotedString(val="\\0X"):0:0:"\\U00000000X"', + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedEscape', '\\U00000000'), + hl('DoubleQuotedBody', 'X'), + hl('DoubleQuote', '"'), + }) + + check_parsing('"\\x000X"', 0, { + -- 01234567 + ast = { + 'DoubleQuotedString(val="\\0000X"):0:0:"\\x000X"', + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedEscape', '\\x00'), + hl('DoubleQuotedBody', '0X'), + hl('DoubleQuote', '"'), + }) + + check_parsing('"\\X000X"', 0, { + -- 01234567 + ast = { + 'DoubleQuotedString(val="\\0000X"):0:0:"\\X000X"', + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedEscape', '\\X00'), + hl('DoubleQuotedBody', '0X'), + hl('DoubleQuote', '"'), + }) + + check_parsing('"\\u00000X"', 0, { + -- 0123456789 + ast = { + 'DoubleQuotedString(val="\\0000X"):0:0:"\\u00000X"', + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedEscape', '\\u0000'), + hl('DoubleQuotedBody', '0X'), + hl('DoubleQuote', '"'), + }) + + check_parsing('"\\U000000000X"', 0, { + -- 01234567890123 + -- 0 1 + ast = { + 'DoubleQuotedString(val="\\0000X"):0:0:"\\U000000000X"', + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedEscape', '\\U00000000'), + hl('DoubleQuotedBody', '0X'), + hl('DoubleQuote', '"'), + }) + + check_parsing('"\\0"', 0, { + -- 0123 + ast = { + 'DoubleQuotedString(val="\\0"):0:0:"\\0"', + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedEscape', '\\0'), + hl('DoubleQuote', '"'), + }) + + check_parsing('"\\00"', 0, { + -- 01234 + ast = { + 'DoubleQuotedString(val="\\0"):0:0:"\\00"', + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedEscape', '\\00'), + hl('DoubleQuote', '"'), + }) + + check_parsing('"\\000"', 0, { + -- 012345 + ast = { + 'DoubleQuotedString(val="\\0"):0:0:"\\000"', + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedEscape', '\\000'), + hl('DoubleQuote', '"'), + }) + + check_parsing('"\\0000"', 0, { + -- 0123456 + ast = { + 'DoubleQuotedString(val="\\0000"):0:0:"\\0000"', + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedEscape', '\\000'), + hl('DoubleQuotedBody', '0'), + hl('DoubleQuote', '"'), + }) + + check_parsing('"\\8"', 0, { + -- 0123 + ast = { + 'DoubleQuotedString(val="8"):0:0:"\\8"', + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedUnknownEscape', '\\8'), + hl('DoubleQuote', '"'), + }) + + check_parsing('"\\08"', 0, { + -- 01234 + ast = { + 'DoubleQuotedString(val="\\0008"):0:0:"\\08"', + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedEscape', '\\0'), + hl('DoubleQuotedBody', '8'), + hl('DoubleQuote', '"'), + }) + + check_parsing('"\\008"', 0, { + -- 012345 + ast = { + 'DoubleQuotedString(val="\\0008"):0:0:"\\008"', + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedEscape', '\\00'), + hl('DoubleQuotedBody', '8'), + hl('DoubleQuote', '"'), + }) + + check_parsing('"\\0008"', 0, { + -- 0123456 + ast = { + 'DoubleQuotedString(val="\\0008"):0:0:"\\0008"', + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedEscape', '\\000'), + hl('DoubleQuotedBody', '8'), + hl('DoubleQuote', '"'), + }) + + check_parsing('"\\777"', 0, { + -- 012345 + ast = { + 'DoubleQuotedString(val="\255"):0:0:"\\777"', + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedEscape', '\\777'), + hl('DoubleQuote', '"'), + }) + + check_parsing('"\\050"', 0, { + -- 012345 + ast = { + 'DoubleQuotedString(val="\40"):0:0:"\\050"', + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedEscape', '\\050'), + hl('DoubleQuote', '"'), + }) + + check_parsing('"\\"', 0, { + -- 012345 + ast = { + 'DoubleQuotedString(val="\\21"):0:0:"\\"', + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedEscape', '\\'), + hl('DoubleQuote', '"'), + }) + + check_parsing('"\\<', 0, { + -- 012 + ast = { + 'DoubleQuotedString(val="<"):0:0:"\\<', + }, + err = { + arg = '"\\<', + msg = 'E114: Missing double quote: %.*s', + }, + }, { + hl('InvalidDoubleQuote', '"'), + hl('InvalidDoubleQuotedUnknownEscape', '\\<'), + }) + + check_parsing('"\\<"', 0, { + -- 0123 + ast = { + 'DoubleQuotedString(val="<"):0:0:"\\<"', + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedUnknownEscape', '\\<'), + hl('DoubleQuote', '"'), + }) + + check_parsing('"\\ Date: Sun, 5 Nov 2017 21:06:12 +0300 Subject: tests: Add missing test cases --- test/functional/api/vim_spec.lua | 1197 ++++++++++++++++++---------- test/helpers.lua | 42 + test/unit/viml/expressions/parser_spec.lua | 1105 ++++++++++++++----------- 3 files changed, 1448 insertions(+), 896 deletions(-) (limited to 'test') diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua index bb7785657d..b904bd2a8f 100644 --- a/test/functional/api/vim_spec.lua +++ b/test/functional/api/vim_spec.lua @@ -12,8 +12,10 @@ local request = helpers.request local meth_pcall = helpers.meth_pcall local command = helpers.command +local REMOVE_THIS = global_helpers.REMOVE_THIS local intchar2lua = global_helpers.intchar2lua local format_string = global_helpers.format_string +local mergedicts_copy = global_helpers.mergedicts_copy describe('api', function() before_each(clear) @@ -717,6 +719,9 @@ describe('api', function() describe('nvim_parse_expression', function() local function simplify_east_api_node(line, east_api_node) + if east_api_node == NIL then + return nil + end if east_api_node.children then for k, v in pairs(east_api_node.children) do east_api_node.children[k] = simplify_east_api_node(line, v) @@ -806,31 +811,53 @@ describe('api', function() end return east_hl end - local function check_parsing(str, flags, exp_ast, exp_highlighting_fs) - if flags == 0 then - flags = "" - end - - local err, msg = pcall(function() - local east_api = meths.parse_expression(str, flags, true) - local east_hl = east_api.highlight - east_api.highlight = nil - local ast = simplify_east_api(str, east_api) - local hls = simplify_east_hl(str, east_hl) - eq(exp_ast, ast) - if exp_highlighting_fs then - local exp_highlighting = {} - local next_col = 0 - for i, h in ipairs(exp_highlighting_fs) do - exp_highlighting[i], next_col = h(next_col) + local FLAGS_TO_STR = { + [0] = "", + [1] = "m", + [2] = "E", + [3] = "mE", + } + local function check_parsing(str, exp_ast, exp_highlighting_fs, + nz_flags_exps) + nz_flags_exps = nz_flags_exps or {} + for _, flags in ipairs({0, 1, 2, 3}) do + local err, msg = pcall(function() + local east_api = meths.parse_expression(str, FLAGS_TO_STR[flags], true) + local east_hl = east_api.highlight + east_api.highlight = nil + local ast = simplify_east_api(str, east_api) + local hls = simplify_east_hl(str, east_hl) + local exps = { + ast = exp_ast, + hl_fs = exp_highlighting_fs, + } + local add_exps = nz_flags_exps[flags] + if not add_exps and flags == 3 then + add_exps = nz_flags_exps[1] or nz_flags_exps[2] end - eq(exp_highlighting, hls) + if add_exps then + if add_exps.ast then + exps.ast = mergedicts_copy(exps.ast, add_exps.ast) + end + if add_exps.hl_fs then + exps.hl_fs = mergedicts_copy(exps.hl_fs, add_exps.hl_fs) + end + end + eq(exps.ast, ast) + if exp_highlighting_fs then + local exp_highlighting = {} + local next_col = 0 + for i, h in ipairs(exps.hl_fs) do + exp_highlighting[i], next_col = h(next_col) + end + eq(exp_highlighting, hls) + end + end) + if not err then + msg = format_string('Error while processing test (%r, %s):\n%s', + str, FLAGS_TO_STR[flags], msg) + error(msg) end - end) - if not err then - msg = format_string('Error while processing test (%r, %s):\n%s', - str, flags, msg) - error(msg) end end local function hl(group, str, shift) @@ -844,14 +871,14 @@ describe('api', function() end end it('works with + and @a', function() - check_parsing('@a', 0, { + check_parsing('@a', { ast = { 'Register(name=a):0:0:@a', }, }, { hl('Register', '@a'), }) - check_parsing('+@a', 0, { + check_parsing('+@a', { ast = { { 'UnaryPlus:0:0:+', @@ -864,7 +891,7 @@ describe('api', function() hl('UnaryPlus', '+'), hl('Register', '@a'), }) - check_parsing('@a+@b', 0, { + check_parsing('@a+@b', { ast = { { 'BinaryPlus:0:2:+', @@ -879,7 +906,7 @@ describe('api', function() hl('BinaryPlus', '+'), hl('Register', '@b'), }) - check_parsing('@a+@b+@c', 0, { + check_parsing('@a+@b+@c', { ast = { { 'BinaryPlus:0:5:+', @@ -902,7 +929,7 @@ describe('api', function() hl('BinaryPlus', '+'), hl('Register', '@c'), }) - check_parsing('+@a+@b', 0, { + check_parsing('+@a+@b', { ast = { { 'BinaryPlus:0:3:+', @@ -923,7 +950,7 @@ describe('api', function() hl('BinaryPlus', '+'), hl('Register', '@b'), }) - check_parsing('+@a++@b', 0, { + check_parsing('+@a++@b', { ast = { { 'BinaryPlus:0:3:+', @@ -950,7 +977,7 @@ describe('api', function() hl('UnaryPlus', '+'), hl('Register', '@b'), }) - check_parsing('@a@b', 0, { + check_parsing('@a@b', { ast = { { 'OpMissing:0:2:', @@ -967,8 +994,20 @@ describe('api', function() }, { hl('Register', '@a'), hl('InvalidRegister', '@b'), + }, { + [1] = { + ast = { + err = REMOVE_THIS, + ast = { + 'Register(name=a):0:0:@a' + }, + }, + hl_fs = { + [2] = REMOVE_THIS, + }, + }, }) - check_parsing(' @a \t @b', 0, { + check_parsing(' @a \t @b', { ast = { { 'OpMissing:0:3:', @@ -986,8 +1025,21 @@ describe('api', function() hl('Register', '@a', 1), hl('InvalidSpacing', ' \t '), hl('Register', '@b'), + }, { + [1] = { + ast = { + err = REMOVE_THIS, + ast = { + 'Register(name=a):0:0: @a' + }, + }, + hl_fs = { + [2] = REMOVE_THIS, + [3] = REMOVE_THIS, + }, + }, }) - check_parsing('+', 0, { + check_parsing('+', { ast = { 'UnaryPlus:0:0:+', }, @@ -998,7 +1050,7 @@ describe('api', function() }, { hl('UnaryPlus', '+'), }) - check_parsing(' +', 0, { + check_parsing(' +', { ast = { 'UnaryPlus:0:0: +', }, @@ -1009,7 +1061,7 @@ describe('api', function() }, { hl('UnaryPlus', '+', 1), }) - check_parsing('@a+ ', 0, { + check_parsing('@a+ ', { ast = { { 'BinaryPlus:0:2:+', @@ -1028,7 +1080,7 @@ describe('api', function() }) end) it('works with @a, + and parenthesis', function() - check_parsing('(@a)', 0, { + check_parsing('(@a)', { ast = { { 'Nested:0:0:(', @@ -1042,7 +1094,7 @@ describe('api', function() hl('Register', '@a'), hl('NestingParenthesis', ')'), }) - check_parsing('()', 0, { + check_parsing('()', { ast = { { 'Nested:0:0:(', @@ -1059,7 +1111,7 @@ describe('api', function() hl('NestingParenthesis', '('), hl('InvalidNestingParenthesis', ')'), }) - check_parsing(')', 0, { + check_parsing(')', { ast = { { 'Nested:0:0:', @@ -1075,7 +1127,7 @@ describe('api', function() }, { hl('InvalidNestingParenthesis', ')'), }) - check_parsing('+)', 0, { + check_parsing('+)', { ast = { { 'Nested:0:1:', @@ -1097,7 +1149,7 @@ describe('api', function() hl('UnaryPlus', '+'), hl('InvalidNestingParenthesis', ')'), }) - check_parsing('+@a(@b)', 0, { + check_parsing('+@a(@b)', { ast = { { 'UnaryPlus:0:0:+', @@ -1119,7 +1171,7 @@ describe('api', function() hl('Register', '@b'), hl('CallingParenthesis', ')'), }) - check_parsing('@a+@b(@c)', 0, { + check_parsing('@a+@b(@c)', { ast = { { 'BinaryPlus:0:2:+', @@ -1143,7 +1195,7 @@ describe('api', function() hl('Register', '@c'), hl('CallingParenthesis', ')'), }) - check_parsing('@a()', 0, { + check_parsing('@a()', { ast = { { 'Call:0:2:(', @@ -1157,7 +1209,7 @@ describe('api', function() hl('CallingParenthesis', '('), hl('CallingParenthesis', ')'), }) - check_parsing('@a ()', 0, { + check_parsing('@a ()', { ast = { { 'OpMissing:0:2:', @@ -1181,91 +1233,102 @@ describe('api', function() hl('InvalidSpacing', ' '), hl('NestingParenthesis', '('), hl('InvalidNestingParenthesis', ')'), - }) - check_parsing( - '@a + (@b)', 0, { + }, { + [1] = { ast = { - { - 'BinaryPlus:0:2: +', - children = { - 'Register(name=a):0:0:@a', - { - 'Nested:0:4: (', - children = { - 'Register(name=b):0:6:@b', - }, + err = REMOVE_THIS, + ast = { + 'Register(name=a):0:0:@a', + }, + }, + hl_fs = { + [2] = REMOVE_THIS, + [3] = REMOVE_THIS, + [4] = REMOVE_THIS, + }, + }, + }) + check_parsing('@a + (@b)', { + ast = { + { + 'BinaryPlus:0:2: +', + children = { + 'Register(name=a):0:0:@a', + { + 'Nested:0:4: (', + children = { + 'Register(name=b):0:6:@b', }, }, }, }, - }, { - hl('Register', '@a'), - hl('BinaryPlus', '+', 1), - hl('NestingParenthesis', '(', 1), - hl('Register', '@b'), - hl('NestingParenthesis', ')'), - }) - check_parsing( - '@a + (+@b)', 0, { - ast = { - { - 'BinaryPlus:0:2: +', - children = { - 'Register(name=a):0:0:@a', - { - 'Nested:0:4: (', - children = { - { - 'UnaryPlus:0:6:+', - children = { - 'Register(name=b):0:7:@b', - }, + }, + }, { + hl('Register', '@a'), + hl('BinaryPlus', '+', 1), + hl('NestingParenthesis', '(', 1), + hl('Register', '@b'), + hl('NestingParenthesis', ')'), + }) + check_parsing('@a + (+@b)', { + ast = { + { + 'BinaryPlus:0:2: +', + children = { + 'Register(name=a):0:0:@a', + { + 'Nested:0:4: (', + children = { + { + 'UnaryPlus:0:6:+', + children = { + 'Register(name=b):0:7:@b', }, }, }, }, }, }, - }, { - hl('Register', '@a'), - hl('BinaryPlus', '+', 1), - hl('NestingParenthesis', '(', 1), - hl('UnaryPlus', '+'), - hl('Register', '@b'), - hl('NestingParenthesis', ')'), - }) - check_parsing( - '@a + (@b + @c)', 0, { - ast = { - { - 'BinaryPlus:0:2: +', - children = { - 'Register(name=a):0:0:@a', - { - 'Nested:0:4: (', - children = { - { - 'BinaryPlus:0:8: +', - children = { - 'Register(name=b):0:6:@b', - 'Register(name=c):0:10: @c', - }, + }, + }, { + hl('Register', '@a'), + hl('BinaryPlus', '+', 1), + hl('NestingParenthesis', '(', 1), + hl('UnaryPlus', '+'), + hl('Register', '@b'), + hl('NestingParenthesis', ')'), + }) + check_parsing('@a + (@b + @c)', { + ast = { + { + 'BinaryPlus:0:2: +', + children = { + 'Register(name=a):0:0:@a', + { + 'Nested:0:4: (', + children = { + { + 'BinaryPlus:0:8: +', + children = { + 'Register(name=b):0:6:@b', + 'Register(name=c):0:10: @c', }, }, }, }, }, }, - }, { - hl('Register', '@a'), - hl('BinaryPlus', '+', 1), - hl('NestingParenthesis', '(', 1), - hl('Register', '@b'), - hl('BinaryPlus', '+', 1), - hl('Register', '@c', 1), - hl('NestingParenthesis', ')'), - }) - check_parsing('(@a)+@b', 0, { + }, + }, { + hl('Register', '@a'), + hl('BinaryPlus', '+', 1), + hl('NestingParenthesis', '(', 1), + hl('Register', '@b'), + hl('BinaryPlus', '+', 1), + hl('Register', '@c', 1), + hl('NestingParenthesis', ')'), + }) + check_parsing('(@a)+@b', { ast = { { 'BinaryPlus:0:4:+', @@ -1287,7 +1350,7 @@ describe('api', function() hl('BinaryPlus', '+'), hl('Register', '@b'), }) - check_parsing('@a+(@b)(@c)', 0, { + check_parsing('@a+(@b)(@c)', { -- 01234567890 ast = { { @@ -1317,7 +1380,7 @@ describe('api', function() hl('Register', '@c'), hl('CallingParenthesis', ')'), }) - check_parsing('@a+((@b))(@c)', 0, { + check_parsing('@a+((@b))(@c)', { -- 01234567890123456890123456789 -- 0 1 2 ast = { @@ -1355,7 +1418,7 @@ describe('api', function() hl('Register', '@c'), hl('CallingParenthesis', ')'), }) - check_parsing('@a+((@b))+@c', 0, { + check_parsing('@a+((@b))+@c', { -- 01234567890123456890123456789 -- 0 1 2 ast = { @@ -1393,7 +1456,7 @@ describe('api', function() hl('Register', '@c'), }) check_parsing( - '@a + (@b + @c) + @d(@e) + (+@f) + ((+@g(@h))(@j)(@k))(@l)', 0, {--[[ + '@a + (@b + @c) + @d(@e) + (+@f) + ((+@g(@h))(@j)(@k))(@l)', {--[[ | | | | | | | | || | | || | | ||| || || || || 000000000011111111112222222222333333333344444444445555555 012345678901234567890123456789012345678901234567890123456 @@ -1527,7 +1590,7 @@ describe('api', function() hl('Register', '@l'), hl('CallingParenthesis', ')'), }) - check_parsing('@a)', 0, { + check_parsing('@a)', { -- 012 ast = { { @@ -1545,7 +1608,7 @@ describe('api', function() hl('Register', '@a'), hl('InvalidNestingParenthesis', ')'), }) - check_parsing('(@a', 0, { + check_parsing('(@a', { -- 012 ast = { { @@ -1563,7 +1626,7 @@ describe('api', function() hl('NestingParenthesis', '('), hl('Register', '@a'), }) - check_parsing('@a(@b', 0, { + check_parsing('@a(@b', { -- 01234 ast = { { @@ -1583,7 +1646,7 @@ describe('api', function() hl('CallingParenthesis', '('), hl('Register', '@b'), }) - check_parsing('@a(@b, @c, @d, @e)', 0, { + check_parsing('@a(@b, @c, @d, @e)', { -- 012345678901234567 -- 0 1 ast = { @@ -1625,7 +1688,7 @@ describe('api', function() hl('Register', '@e', 1), hl('CallingParenthesis', ')'), }) - check_parsing('@a(@b(@c))', 0, { + check_parsing('@a(@b(@c))', { -- 01234567890123456789012345678901234567 -- 0 1 2 3 ast = { @@ -1652,7 +1715,7 @@ describe('api', function() hl('CallingParenthesis', ')'), hl('CallingParenthesis', ')'), }) - check_parsing('@a(@b(@c(@d(@e), @f(@g(@h), @i(@j)))))', 0, { + check_parsing('@a(@b(@c(@d(@e), @f(@g(@h), @i(@j)))))', { -- 01234567890123456789012345678901234567 -- 0 1 2 3 ast = { @@ -1742,14 +1805,14 @@ describe('api', function() }) end) it('works with variable names, including curly braces ones', function() - check_parsing('var', 0, { + check_parsing('var', { ast = { 'PlainIdentifier(scope=0,ident=var):0:0:var', }, }, { hl('IdentifierName', 'var'), }) - check_parsing('g:var', 0, { + check_parsing('g:var', { ast = { 'PlainIdentifier(scope=g,ident=var):0:0:g:var', }, @@ -1758,7 +1821,7 @@ describe('api', function() hl('IdentifierScopeDelimiter', ':'), hl('IdentifierName', 'var'), }) - check_parsing('g:', 0, { + check_parsing('g:', { ast = { 'PlainIdentifier(scope=g,ident=):0:0:g:', }, @@ -1766,7 +1829,7 @@ describe('api', function() hl('IdentifierScope', 'g'), hl('IdentifierScopeDelimiter', ':'), }) - check_parsing('{a}', 0, { + check_parsing('{a}', { -- 012 ast = { { @@ -1781,7 +1844,7 @@ describe('api', function() hl('IdentifierName', 'a'), hl('Curly', '}'), }) - check_parsing('{a:b}', 0, { + check_parsing('{a:b}', { -- 012 ast = { { @@ -1798,7 +1861,7 @@ describe('api', function() hl('IdentifierName', 'b'), hl('Curly', '}'), }) - check_parsing('{a:@b}', 0, { + check_parsing('{a:@b}', { -- 012345 ast = { { @@ -1825,7 +1888,7 @@ describe('api', function() hl('InvalidRegister', '@b'), hl('Curly', '}'), }) - check_parsing('{@a}', 0, { + check_parsing('{@a}', { ast = { { 'CurlyBracesIdentifier:0:0:{', @@ -1839,7 +1902,7 @@ describe('api', function() hl('Register', '@a'), hl('Curly', '}'), }) - check_parsing('{@a}{@b}', 0, { + check_parsing('{@a}{@b}', { -- 01234567 ast = { { @@ -1868,7 +1931,7 @@ describe('api', function() hl('Register', '@b'), hl('Curly', '}'), }) - check_parsing('g:{@a}', 0, { + check_parsing('g:{@a}', { -- 01234567 ast = { { @@ -1891,7 +1954,7 @@ describe('api', function() hl('Register', '@a'), hl('Curly', '}'), }) - check_parsing('{@a}_test', 0, { + check_parsing('{@a}_test', { -- 012345678 ast = { { @@ -1913,7 +1976,7 @@ describe('api', function() hl('Curly', '}'), hl('IdentifierName', '_test'), }) - check_parsing('g:{@a}_test', 0, { + check_parsing('g:{@a}_test', { -- 01234567890 ast = { { @@ -1943,7 +2006,7 @@ describe('api', function() hl('Curly', '}'), hl('IdentifierName', '_test'), }) - check_parsing('g:{@a}_test()', 0, { + check_parsing('g:{@a}_test()', { -- 0123456789012 ast = { { @@ -1980,7 +2043,7 @@ describe('api', function() hl('CallingParenthesis', '('), hl('CallingParenthesis', ')'), }) - check_parsing('{@a} ()', 0, { + check_parsing('{@a} ()', { -- 0123456789012 ast = { { @@ -2002,7 +2065,7 @@ describe('api', function() hl('CallingParenthesis', '(', 1), hl('CallingParenthesis', ')'), }) - check_parsing('g:{@a} ()', 0, { + check_parsing('g:{@a} ()', { -- 0123456789012 ast = { { @@ -2032,7 +2095,7 @@ describe('api', function() hl('CallingParenthesis', '(', 1), hl('CallingParenthesis', ')'), }) - check_parsing('{@a', 0, { + check_parsing('{@a', { -- 012 ast = { { @@ -2052,7 +2115,7 @@ describe('api', function() }) end) it('works with lambdas and dictionaries', function() - check_parsing('{}', 0, { + check_parsing('{}', { ast = { 'DictLiteral:0:0:{', }, @@ -2060,7 +2123,7 @@ describe('api', function() hl('Dict', '{'), hl('Dict', '}'), }) - check_parsing('{->@a}', 0, { + check_parsing('{->@a}', { ast = { { 'Lambda:0:0:{', @@ -2080,7 +2143,7 @@ describe('api', function() hl('Register', '@a'), hl('Lambda', '}'), }) - check_parsing('{->@a+@b}', 0, { + check_parsing('{->@a+@b}', { -- 012345678 ast = { { @@ -2109,7 +2172,7 @@ describe('api', function() hl('Register', '@b'), hl('Lambda', '}'), }) - check_parsing('{a->@a}', 0, { + check_parsing('{a->@a}', { -- 012345678 ast = { { @@ -2132,7 +2195,7 @@ describe('api', function() hl('Register', '@a'), hl('Lambda', '}'), }) - check_parsing('{a,b->@a}', 0, { + check_parsing('{a,b->@a}', { -- 012345678 ast = { { @@ -2163,7 +2226,7 @@ describe('api', function() hl('Register', '@a'), hl('Lambda', '}'), }) - check_parsing('{a,b,c->@a}', 0, { + check_parsing('{a,b,c->@a}', { -- 01234567890 ast = { { @@ -2202,7 +2265,7 @@ describe('api', function() hl('Register', '@a'), hl('Lambda', '}'), }) - check_parsing('{a,b,c,d->@a}', 0, { + check_parsing('{a,b,c,d->@a}', { -- 0123456789012 ast = { { @@ -2249,7 +2312,7 @@ describe('api', function() hl('Register', '@a'), hl('Lambda', '}'), }) - check_parsing('{a,b,c,d,->@a}', 0, { + check_parsing('{a,b,c,d,->@a}', { -- 01234567890123 ast = { { @@ -2302,7 +2365,7 @@ describe('api', function() hl('Register', '@a'), hl('Lambda', '}'), }) - check_parsing('{a,b->{c,d->{e,f->@a}}}', 0, { + check_parsing('{a,b->{c,d->{e,f->@a}}}', { -- 01234567890123456789012 -- 0 1 2 ast = { @@ -2380,7 +2443,7 @@ describe('api', function() hl('Lambda', '}'), hl('Lambda', '}'), }) - check_parsing('{a,b->c,d}', 0, { + check_parsing('{a,b->c,d}', { -- 0123456789 ast = { { @@ -2423,7 +2486,7 @@ describe('api', function() hl('IdentifierName', 'd'), hl('Lambda', '}'), }) - check_parsing('a,b,c,d', 0, { + check_parsing('a,b,c,d', { -- 0123456789 ast = { { @@ -2459,7 +2522,7 @@ describe('api', function() hl('InvalidComma', ','), hl('IdentifierName', 'd'), }) - check_parsing('a,b,c,d,', 0, { + check_parsing('a,b,c,d,', { -- 0123456789 ast = { { @@ -2501,7 +2564,7 @@ describe('api', function() hl('IdentifierName', 'd'), hl('InvalidComma', ','), }) - check_parsing(',', 0, { + check_parsing(',', { -- 0123456789 ast = { { @@ -2518,7 +2581,7 @@ describe('api', function() }, { hl('InvalidComma', ','), }) - check_parsing('{,a->@a}', 0, { + check_parsing('{,a->@a}', { -- 0123456789 ast = { { @@ -2552,7 +2615,7 @@ describe('api', function() hl('Register', '@a'), hl('Curly', '}'), }) - check_parsing('}', 0, { + check_parsing('}', { -- 0123456789 ast = { 'UnknownFigure:0:0:', @@ -2564,7 +2627,7 @@ describe('api', function() }, { hl('InvalidFigureBrace', '}'), }) - check_parsing('{->}', 0, { + check_parsing('{->}', { -- 0123456789 ast = { { @@ -2583,7 +2646,7 @@ describe('api', function() hl('Arrow', '->'), hl('InvalidLambda', '}'), }) - check_parsing('{a,b}', 0, { + check_parsing('{a,b}', { -- 0123456789 ast = { { @@ -2610,7 +2673,7 @@ describe('api', function() hl('IdentifierName', 'b'), hl('InvalidLambda', '}'), }) - check_parsing('{a,}', 0, { + check_parsing('{a,}', { -- 0123456789 ast = { { @@ -2635,7 +2698,7 @@ describe('api', function() hl('Comma', ','), hl('InvalidLambda', '}'), }) - check_parsing('{@a:@b}', 0, { + check_parsing('{@a:@b}', { -- 0123456789 ast = { { @@ -2658,7 +2721,7 @@ describe('api', function() hl('Register', '@b'), hl('Dict', '}'), }) - check_parsing('{@a:@b,@c:@d}', 0, { + check_parsing('{@a:@b,@c:@d}', { -- 0123456789012 -- 0 1 ast = { @@ -2698,7 +2761,7 @@ describe('api', function() hl('Register', '@d'), hl('Dict', '}'), }) - check_parsing('{@a:@b,@c:@d,@e:@f,}', 0, { + check_parsing('{@a:@b,@c:@d,@e:@f,}', { -- 01234567890123456789 -- 0 1 ast = { @@ -2760,7 +2823,7 @@ describe('api', function() hl('Comma', ','), hl('Dict', '}'), }) - check_parsing('{@a:@b,@c:@d,@e:@f,@g:}', 0, { + check_parsing('{@a:@b,@c:@d,@e:@f,@g:}', { -- 01234567890123456789012 -- 0 1 2 ast = { @@ -2834,7 +2897,7 @@ describe('api', function() hl('Colon', ':'), hl('InvalidDict', '}'), }) - check_parsing('{@a:@b,}', 0, { + check_parsing('{@a:@b,}', { -- 01234567890123 -- 0 1 ast = { @@ -2864,7 +2927,7 @@ describe('api', function() hl('Comma', ','), hl('Dict', '}'), }) - check_parsing('{({f -> g})(@h)(@i)}', 0, { + check_parsing('{({f -> g})(@h)(@i)}', { -- 01234567890123456789 -- 0 1 ast = { @@ -2920,7 +2983,7 @@ describe('api', function() hl('CallingParenthesis', ')'), hl('Curly', '}'), }) - check_parsing('a:{b()}c', 0, { + check_parsing('a:{b()}c', { -- 01234567 ast = { { @@ -2957,7 +3020,7 @@ describe('api', function() hl('Curly', '}'), hl('IdentifierName', 'c'), }) - check_parsing('a:{{b, c -> @d + @e + ({f -> g})(@h)}(@i)}j', 0, { + check_parsing('a:{{b, c -> @d + @e + ({f -> g})(@h)}(@i)}j', { -- 01234567890123456789012345678901234567890123456 -- 0 1 2 3 4 ast = { @@ -3067,7 +3130,7 @@ describe('api', function() hl('Curly', '}'), hl('IdentifierName', 'j'), }) - check_parsing('{@a + @b : @c + @d, @e + @f : @g + @i}', 0, { + check_parsing('{@a + @b : @c + @d, @e + @f : @g + @i}', { -- 01234567890123456789012345678901234567 -- 0 1 2 3 ast = { @@ -3139,7 +3202,7 @@ describe('api', function() hl('Register', '@i', 1), hl('Dict', '}'), }) - check_parsing('-> -> ->', 0, { + check_parsing('-> -> ->', { -- 01234567 ast = { { @@ -3170,7 +3233,7 @@ describe('api', function() hl('InvalidArrow', '->', 1), hl('InvalidArrow', '->', 1), }) - check_parsing('a -> b -> c -> d', 0, { + check_parsing('a -> b -> c -> d', { -- 0123456789012345 -- 0 1 ast = { @@ -3207,7 +3270,7 @@ describe('api', function() hl('InvalidArrow', '->', 1), hl('IdentifierName', 'd', 1), }) - check_parsing('{a -> b -> c}', 0, { + check_parsing('{a -> b -> c}', { -- 0123456789012 -- 0 1 ast = { @@ -3243,7 +3306,7 @@ describe('api', function() hl('IdentifierName', 'c', 1), hl('Lambda', '}'), }) - check_parsing('{a: -> b}', 0, { + check_parsing('{a: -> b}', { -- 012345678 ast = { { @@ -3272,7 +3335,7 @@ describe('api', function() hl('Curly', '}'), }) - check_parsing('{a:b -> b}', 0, { + check_parsing('{a:b -> b}', { -- 0123456789 ast = { { @@ -3302,7 +3365,7 @@ describe('api', function() hl('Curly', '}'), }) - check_parsing('{a#b -> b}', 0, { + check_parsing('{a#b -> b}', { -- 0123456789 ast = { { @@ -3329,7 +3392,7 @@ describe('api', function() hl('IdentifierName', 'b', 1), hl('Curly', '}'), }) - check_parsing('{a : b : c}', 0, { + check_parsing('{a : b : c}', { -- 01234567890 -- 0 1 ast = { @@ -3365,7 +3428,7 @@ describe('api', function() hl('IdentifierName', 'c', 1), hl('Dict', '}'), }) - check_parsing('{', 0, { + check_parsing('{', { -- 0 ast = { 'UnknownFigure:0:0:{', @@ -3377,7 +3440,7 @@ describe('api', function() }, { hl('FigureBrace', '{'), }) - check_parsing('{a', 0, { + check_parsing('{a', { -- 01 ast = { { @@ -3395,7 +3458,7 @@ describe('api', function() hl('FigureBrace', '{'), hl('IdentifierName', 'a'), }) - check_parsing('{a,b', 0, { + check_parsing('{a,b', { -- 0123 ast = { { @@ -3421,7 +3484,7 @@ describe('api', function() hl('Comma', ','), hl('IdentifierName', 'b'), }) - check_parsing('{a,b->', 0, { + check_parsing('{a,b->', { -- 012345 ast = { { @@ -3449,7 +3512,7 @@ describe('api', function() hl('IdentifierName', 'b'), hl('Arrow', '->'), }) - check_parsing('{a,b->c', 0, { + check_parsing('{a,b->c', { -- 0123456 ast = { { @@ -3483,7 +3546,7 @@ describe('api', function() hl('Arrow', '->'), hl('IdentifierName', 'c'), }) - check_parsing('{a : b', 0, { + check_parsing('{a : b', { -- 012345 ast = { { @@ -3509,7 +3572,7 @@ describe('api', function() hl('Colon', ':', 1), hl('IdentifierName', 'b', 1), }) - check_parsing('{a : b,', 0, { + check_parsing('{a : b,', { -- 0123456 ast = { { @@ -3543,7 +3606,7 @@ describe('api', function() }) end) it('works with ternary operator', function() - check_parsing('a ? b : c', 0, { + check_parsing('a ? b : c', { -- 012345678 ast = { { @@ -3567,7 +3630,7 @@ describe('api', function() hl('TernaryColon', ':', 1), hl('IdentifierName', 'c', 1), }) - check_parsing('@a?@b?@c:@d:@e', 0, { + check_parsing('@a?@b?@c:@d:@e', { -- 01234567890123 -- 0 1 ast = { @@ -3608,7 +3671,7 @@ describe('api', function() hl('TernaryColon', ':'), hl('Register', '@e'), }) - check_parsing('@a?@b:@c?@d:@e', 0, { + check_parsing('@a?@b:@c?@d:@e', { -- 01234567890123 -- 0 1 ast = { @@ -3649,7 +3712,7 @@ describe('api', function() hl('TernaryColon', ':'), hl('Register', '@e'), }) - check_parsing('@a?@b?@c?@d:@e?@f:@g:@h?@i:@j:@k', 0, { + check_parsing('@a?@b?@c?@d:@e?@f:@g:@h?@i:@j:@k', { -- 01234567890123456789012345678901 -- 0 1 2 3 ast = { @@ -3738,7 +3801,7 @@ describe('api', function() hl('TernaryColon', ':'), hl('Register', '@k'), }) - check_parsing('?', 0, { + check_parsing('?', { -- 0 ast = { { @@ -3757,7 +3820,7 @@ describe('api', function() hl('InvalidTernary', '?'), }) - check_parsing('?:', 0, { + check_parsing('?:', { -- 01 ast = { { @@ -3782,7 +3845,7 @@ describe('api', function() hl('InvalidTernaryColon', ':'), }) - check_parsing('?::', 0, { + check_parsing('?::', { -- 012 ast = { { @@ -3814,7 +3877,7 @@ describe('api', function() hl('InvalidColon', ':'), }) - check_parsing('a?b', 0, { + check_parsing('a?b', { -- 012 ast = { { @@ -3839,7 +3902,7 @@ describe('api', function() hl('Ternary', '?'), hl('IdentifierName', 'b'), }) - check_parsing('a?b:', 0, { + check_parsing('a?b:', { -- 0123 ast = { { @@ -3866,7 +3929,7 @@ describe('api', function() hl('IdentifierScopeDelimiter', ':'), }) - check_parsing('a?b::c', 0, { + check_parsing('a?b::c', { -- 012345 ast = { { @@ -3892,7 +3955,7 @@ describe('api', function() hl('IdentifierName', 'c'), }) - check_parsing('a?b :', 0, { + check_parsing('a?b :', { -- 01234 ast = { { @@ -3919,7 +3982,7 @@ describe('api', function() hl('TernaryColon', ':', 1), }) - check_parsing('(@a?@b:@c)?@d:@e', 0, { + check_parsing('(@a?@b:@c)?@d:@e', { -- 0123456789012345 -- 0 1 ast = { @@ -3968,7 +4031,7 @@ describe('api', function() hl('Register', '@e'), }) - check_parsing('(@a?@b:@c)?(@d?@e:@f):(@g?@h:@i)', 0, { + check_parsing('(@a?@b:@c)?(@d?@e:@f):(@g?@h:@i)', { -- 01234567890123456789012345678901 -- 0 1 2 3 ast = { @@ -4063,7 +4126,7 @@ describe('api', function() hl('NestingParenthesis', ')'), }) - check_parsing('(@a?@b:@c)?@d?@e:@f:@g?@h:@i', 0, { + check_parsing('(@a?@b:@c)?@d?@e:@f:@g?@h:@i', { -- 0123456789012345678901234567 -- 0 1 2 ast = { @@ -4143,7 +4206,7 @@ describe('api', function() hl('TernaryColon', ':'), hl('Register', '@i'), }) - check_parsing('a?b{cdef}g:h', 0, { + check_parsing('a?b{cdef}g:h', { -- 012345678901 -- 0 1 ast = { @@ -4189,7 +4252,7 @@ describe('api', function() hl('TernaryColon', ':'), hl('IdentifierName', 'h'), }) - check_parsing('a ? b : c : d', 0, { + check_parsing('a ? b : c : d', { -- 0123456789012 -- 0 1 ast = { @@ -4228,7 +4291,7 @@ describe('api', function() }) end) it('works with comparison operators', function() - check_parsing('a == b', 0, { + check_parsing('a == b', { -- 012345 ast = { { @@ -4245,7 +4308,7 @@ describe('api', function() hl('IdentifierName', 'b', 1), }) - check_parsing('a ==? b', 0, { + check_parsing('a ==? b', { -- 0123456 ast = { { @@ -4263,7 +4326,7 @@ describe('api', function() hl('IdentifierName', 'b', 1), }) - check_parsing('a ==# b', 0, { + check_parsing('a ==# b', { -- 0123456 ast = { { @@ -4281,7 +4344,7 @@ describe('api', function() hl('IdentifierName', 'b', 1), }) - check_parsing('a !=# b', 0, { + check_parsing('a !=# b', { -- 0123456 ast = { { @@ -4299,7 +4362,7 @@ describe('api', function() hl('IdentifierName', 'b', 1), }) - check_parsing('a <=# b', 0, { + check_parsing('a <=# b', { -- 0123456 ast = { { @@ -4317,7 +4380,7 @@ describe('api', function() hl('IdentifierName', 'b', 1), }) - check_parsing('a >=# b', 0, { + check_parsing('a >=# b', { -- 0123456 ast = { { @@ -4335,7 +4398,7 @@ describe('api', function() hl('IdentifierName', 'b', 1), }) - check_parsing('a ># b', 0, { + check_parsing('a ># b', { -- 012345 ast = { { @@ -4353,7 +4416,7 @@ describe('api', function() hl('IdentifierName', 'b', 1), }) - check_parsing('a <# b', 0, { + check_parsing('a <# b', { -- 012345 ast = { { @@ -4371,7 +4434,7 @@ describe('api', function() hl('IdentifierName', 'b', 1), }) - check_parsing('a is#b', 0, { + check_parsing('a is#b', { -- 012345 ast = { { @@ -4389,7 +4452,7 @@ describe('api', function() hl('IdentifierName', 'b'), }) - check_parsing('a is?b', 0, { + check_parsing('a is?b', { -- 012345 ast = { { @@ -4407,7 +4470,7 @@ describe('api', function() hl('IdentifierName', 'b'), }) - check_parsing('a isnot b', 0, { + check_parsing('a isnot b', { -- 012345678 ast = { { @@ -4424,7 +4487,7 @@ describe('api', function() hl('IdentifierName', 'b', 1), }) - check_parsing('a < b < c', 0, { + check_parsing('a < b < c', { -- 012345678 ast = { { @@ -4453,7 +4516,7 @@ describe('api', function() hl('IdentifierName', 'c', 1), }) - check_parsing('a < b <# c', 0, { + check_parsing('a < b <# c', { -- 012345678 ast = { { @@ -4483,7 +4546,7 @@ describe('api', function() hl('IdentifierName', 'c', 1), }) - check_parsing('a += b', 0, { + check_parsing('a += b', { -- 012345 ast = { { @@ -4510,7 +4573,7 @@ describe('api', function() hl('InvalidComparison', '='), hl('IdentifierName', 'b', 1), }) - check_parsing('a + b == c + d', 0, { + check_parsing('a + b == c + d', { -- 01234567890123 -- 0 1 ast = { @@ -4543,7 +4606,7 @@ describe('api', function() hl('BinaryPlus', '+', 1), hl('IdentifierName', 'd', 1), }) - check_parsing('+ a == + b', 0, { + check_parsing('+ a == + b', { -- 0123456789 ast = { { @@ -4573,7 +4636,7 @@ describe('api', function() }) end) it('works with concat/subscript', function() - check_parsing('.', 0, { + check_parsing('.', { -- 0 ast = { { @@ -4591,7 +4654,7 @@ describe('api', function() hl('InvalidConcatOrSubscript', '.'), }) - check_parsing('a.', 0, { + check_parsing('a.', { -- 01 ast = { { @@ -4610,7 +4673,7 @@ describe('api', function() hl('ConcatOrSubscript', '.'), }) - check_parsing('a.b', 0, { + check_parsing('a.b', { -- 012 ast = { { @@ -4627,7 +4690,7 @@ describe('api', function() hl('IdentifierKey', 'b'), }) - check_parsing('1.2', 0, { + check_parsing('1.2', { -- 012 ast = { 'Float(val=1.200000e+00):0:0:1.2', @@ -4636,7 +4699,7 @@ describe('api', function() hl('Float', '1.2'), }) - check_parsing('1.2 + 1.3e-5', 0, { + check_parsing('1.2 + 1.3e-5', { -- 012345678901 -- 0 1 ast = { @@ -4654,7 +4717,7 @@ describe('api', function() hl('Float', '1.3e-5', 1), }) - check_parsing('a . 1.2 + 1.3e-5', 0, { + check_parsing('a . 1.2 + 1.3e-5', { -- 0123456789012345 -- 0 1 ast = { @@ -4688,7 +4751,7 @@ describe('api', function() hl('Float', '1.3e-5', 1), }) - check_parsing('1.3e-5 + 1.2 . a', 0, { + check_parsing('1.3e-5 + 1.2 . a', { -- 0123456789012345 -- 0 1 ast = { @@ -4714,7 +4777,7 @@ describe('api', function() hl('IdentifierName', 'a', 1), }) - check_parsing('1.3e-5 + a . 1.2', 0, { + check_parsing('1.3e-5 + a . 1.2', { -- 0123456789012345 -- 0 1 ast = { @@ -4748,7 +4811,7 @@ describe('api', function() hl('IdentifierKey', '2'), }) - check_parsing('1.2.3', 0, { + check_parsing('1.2.3', { -- 01234 ast = { { @@ -4773,7 +4836,7 @@ describe('api', function() hl('IdentifierKey', '3'), }) - check_parsing('a.1.2', 0, { + check_parsing('a.1.2', { -- 01234 ast = { { @@ -4798,7 +4861,7 @@ describe('api', function() hl('IdentifierKey', '2'), }) - check_parsing('a . 1.2', 0, { + check_parsing('a . 1.2', { -- 0123456 ast = { { @@ -4823,7 +4886,7 @@ describe('api', function() hl('IdentifierKey', '2'), }) - check_parsing('+a . +b', 0, { + check_parsing('+a . +b', { -- 0123456 ast = { { @@ -4852,7 +4915,7 @@ describe('api', function() hl('IdentifierName', 'b'), }) - check_parsing('a. b', 0, { + check_parsing('a. b', { -- 0123 ast = { { @@ -4869,7 +4932,7 @@ describe('api', function() hl('IdentifierName', 'b', 1), }) - check_parsing('a. 1', 0, { + check_parsing('a. 1', { -- 0123 ast = { { @@ -4887,7 +4950,7 @@ describe('api', function() }) end) it('works with bracket subscripts', function() - check_parsing(':', 0, { + check_parsing(':', { -- 0 ast = { { @@ -4904,7 +4967,7 @@ describe('api', function() }, { hl('InvalidColon', ':'), }) - check_parsing('a[]', 0, { + check_parsing('a[]', { -- 012 ast = { { @@ -4923,7 +4986,7 @@ describe('api', function() hl('SubscriptBracket', '['), hl('InvalidSubscriptBracket', ']'), }) - check_parsing('a[b:]', 0, { + check_parsing('a[b:]', { -- 01234 ast = { { @@ -4942,7 +5005,7 @@ describe('api', function() hl('SubscriptBracket', ']'), }) - check_parsing('a[b:c]', 0, { + check_parsing('a[b:c]', { -- 012345 ast = { { @@ -4961,7 +5024,7 @@ describe('api', function() hl('IdentifierName', 'c'), hl('SubscriptBracket', ']'), }) - check_parsing('a[b : c]', 0, { + check_parsing('a[b : c]', { -- 01234567 ast = { { @@ -4987,7 +5050,7 @@ describe('api', function() hl('SubscriptBracket', ']'), }) - check_parsing('a[: b]', 0, { + check_parsing('a[: b]', { -- 012345 ast = { { @@ -5012,7 +5075,7 @@ describe('api', function() hl('SubscriptBracket', ']'), }) - check_parsing('a[b :]', 0, { + check_parsing('a[b :]', { -- 012345 ast = { { @@ -5035,7 +5098,7 @@ describe('api', function() hl('SubscriptColon', ':', 1), hl('SubscriptBracket', ']'), }) - check_parsing('a[b][c][d](e)(f)(g)', 0, { + check_parsing('a[b][c][d](e)(f)(g)', { -- 0123456789012345678 -- 0 1 ast = { @@ -5098,7 +5161,7 @@ describe('api', function() hl('IdentifierName', 'g'), hl('CallingParenthesis', ')'), }) - check_parsing('{a}{b}{c}[d][e][f]', 0, { + check_parsing('{a}{b}{c}[d][e][f]', { -- 012345678901234567 -- 0 1 ast = { @@ -5171,7 +5234,7 @@ describe('api', function() }) end) it('supports list literals', function() - check_parsing('[]', 0, { + check_parsing('[]', { -- 01 ast = { 'ListLiteral:0:0:[', @@ -5181,7 +5244,7 @@ describe('api', function() hl('List', ']'), }) - check_parsing('[a]', 0, { + check_parsing('[a]', { -- 012 ast = { { @@ -5197,7 +5260,7 @@ describe('api', function() hl('List', ']'), }) - check_parsing('[a, b]', 0, { + check_parsing('[a, b]', { -- 012345 ast = { { @@ -5221,7 +5284,7 @@ describe('api', function() hl('List', ']'), }) - check_parsing('[a, b, c]', 0, { + check_parsing('[a, b, c]', { -- 012345678 ast = { { @@ -5253,7 +5316,7 @@ describe('api', function() hl('List', ']'), }) - check_parsing('[a, b, c, ]', 0, { + check_parsing('[a, b, c, ]', { -- 01234567890 -- 0 1 ast = { @@ -5292,7 +5355,7 @@ describe('api', function() hl('List', ']', 1), }) - check_parsing('[a : b, c : d]', 0, { + check_parsing('[a : b, c : d]', { -- 01234567890123 -- 0 1 ast = { @@ -5337,7 +5400,7 @@ describe('api', function() hl('List', ']'), }) - check_parsing(']', 0, { + check_parsing(']', { -- 0 ast = { 'ListLiteral:0:0:', @@ -5350,7 +5413,7 @@ describe('api', function() hl('InvalidList', ']'), }) - check_parsing('a]', 0, { + check_parsing('a]', { -- 01 ast = { { @@ -5369,7 +5432,7 @@ describe('api', function() hl('InvalidList', ']'), }) - check_parsing('[] []', 0, { + check_parsing('[] []', { -- 01234 ast = { { @@ -5390,9 +5453,23 @@ describe('api', function() hl('InvalidSpacing', ' '), hl('List', '['), hl('List', ']'), + }, { + [1] = { + ast = { + err = REMOVE_THIS, + ast = { + 'ListLiteral:0:0:[', + }, + }, + hl_fs = { + [3] = REMOVE_THIS, + [4] = REMOVE_THIS, + [5] = REMOVE_THIS, + }, + }, }) - check_parsing('[][]', 0, { + check_parsing('[][]', { -- 0123 ast = { { @@ -5413,7 +5490,7 @@ describe('api', function() hl('InvalidSubscriptBracket', ']'), }) - check_parsing('[', 0, { + check_parsing('[', { -- 0 ast = { 'ListLiteral:0:0:[', @@ -5426,7 +5503,7 @@ describe('api', function() hl('List', '['), }) - check_parsing('[1', 0, { + check_parsing('[1', { -- 01 ast = { { @@ -5446,7 +5523,7 @@ describe('api', function() }) end) it('works with strings', function() - check_parsing('\'abc\'', 0, { + check_parsing('\'abc\'', { -- 01234 ast = { 'SingleQuotedString(val="abc"):0:0:\'abc\'', @@ -5456,7 +5533,7 @@ describe('api', function() hl('SingleQuotedBody', 'abc'), hl('SingleQuote', '\''), }) - check_parsing('"abc"', 0, { + check_parsing('"abc"', { -- 01234 ast = { 'DoubleQuotedString(val="abc"):0:0:"abc"', @@ -5466,7 +5543,7 @@ describe('api', function() hl('DoubleQuotedBody', 'abc'), hl('DoubleQuote', '"'), }) - check_parsing('\'\'', 0, { + check_parsing('\'\'', { -- 01 ast = { 'SingleQuotedString(val=""):0:0:\'\'', @@ -5475,7 +5552,7 @@ describe('api', function() hl('SingleQuote', '\''), hl('SingleQuote', '\''), }) - check_parsing('""', 0, { + check_parsing('""', { -- 01 ast = { 'DoubleQuotedString(val=""):0:0:""', @@ -5484,7 +5561,7 @@ describe('api', function() hl('DoubleQuote', '"'), hl('DoubleQuote', '"'), }) - check_parsing('"', 0, { + check_parsing('"', { -- 0 ast = { 'DoubleQuotedString(val=""):0:0:"', @@ -5496,7 +5573,7 @@ describe('api', function() }, { hl('InvalidDoubleQuote', '"'), }) - check_parsing('\'', 0, { + check_parsing('\'', { -- 0 ast = { 'SingleQuotedString(val=""):0:0:\'', @@ -5508,7 +5585,7 @@ describe('api', function() }, { hl('InvalidSingleQuote', '\''), }) - check_parsing('"a', 0, { + check_parsing('"a', { -- 01 ast = { 'DoubleQuotedString(val="a"):0:0:"a', @@ -5521,7 +5598,7 @@ describe('api', function() hl('InvalidDoubleQuote', '"'), hl('InvalidDoubleQuotedBody', 'a'), }) - check_parsing('\'a', 0, { + check_parsing('\'a', { -- 01 ast = { 'SingleQuotedString(val="a"):0:0:\'a', @@ -5534,7 +5611,7 @@ describe('api', function() hl('InvalidSingleQuote', '\''), hl('InvalidSingleQuotedBody', 'a'), }) - check_parsing('\'abc\'\'def\'', 0, { + check_parsing('\'abc\'\'def\'', { -- 0123456789 ast = { 'SingleQuotedString(val="abc\'def"):0:0:\'abc\'\'def\'', @@ -5546,7 +5623,7 @@ describe('api', function() hl('SingleQuotedBody', 'def'), hl('SingleQuote', '\''), }) - check_parsing('\'abc\'\'', 0, { + check_parsing('\'abc\'\'', { -- 012345 ast = { 'SingleQuotedString(val="abc\'"):0:0:\'abc\'\'', @@ -5560,7 +5637,7 @@ describe('api', function() hl('InvalidSingleQuotedBody', 'abc'), hl('InvalidSingleQuotedQuote', '\'\''), }) - check_parsing('\'\'\'\'\'\'\'\'', 0, { + check_parsing('\'\'\'\'\'\'\'\'', { -- 01234567 ast = { 'SingleQuotedString(val="\'\'\'"):0:0:\'\'\'\'\'\'\'\'', @@ -5572,7 +5649,7 @@ describe('api', function() hl('SingleQuotedQuote', '\'\''), hl('SingleQuote', '\''), }) - check_parsing('\'\'\'a\'\'\'\'bc\'', 0, { + check_parsing('\'\'\'a\'\'\'\'bc\'', { -- 01234567890 -- 0 1 ast = { @@ -5587,7 +5664,7 @@ describe('api', function() hl('SingleQuotedBody', 'bc'), hl('SingleQuote', '\''), }) - check_parsing('"\\"\\"\\"\\""', 0, { + check_parsing('"\\"\\"\\"\\""', { -- 0123456789 ast = { 'DoubleQuotedString(val="\\"\\"\\"\\""):0:0:"\\"\\"\\"\\""', @@ -5600,7 +5677,7 @@ describe('api', function() hl('DoubleQuotedEscape', '\\"'), hl('DoubleQuote', '"'), }) - check_parsing('"abc\\"def\\"ghi\\"jkl\\"mno"', 0, { + check_parsing('"abc\\"def\\"ghi\\"jkl\\"mno"', { -- 0123456789012345678901234 -- 0 1 2 ast = { @@ -5619,7 +5696,7 @@ describe('api', function() hl('DoubleQuotedBody', 'mno'), hl('DoubleQuote', '"'), }) - check_parsing('"\\b\\e\\f\\r\\t\\\\"', 0, { + check_parsing('"\\b\\e\\f\\r\\t\\\\"', { -- 0123456789012345 -- 0 1 ast = { @@ -5635,7 +5712,7 @@ describe('api', function() hl('DoubleQuotedEscape', '\\\\'), hl('DoubleQuote', '"'), }) - check_parsing('"\\n\n"', 0, { + check_parsing('"\\n\n"', { -- 01234 ast = { 'DoubleQuotedString(val="\\\n\\\n"):0:0:"\\n\n"', @@ -5646,7 +5723,7 @@ describe('api', function() hl('DoubleQuotedBody', '\n'), hl('DoubleQuote', '"'), }) - check_parsing('"\\x00"', 0, { + check_parsing('"\\x00"', { -- 012345 ast = { 'DoubleQuotedString(val="\\0"):0:0:"\\x00"', @@ -5656,7 +5733,7 @@ describe('api', function() hl('DoubleQuotedEscape', '\\x00'), hl('DoubleQuote', '"'), }) - check_parsing('"\\xFF"', 0, { + check_parsing('"\\xFF"', { -- 012345 ast = { 'DoubleQuotedString(val="\255"):0:0:"\\xFF"', @@ -5666,7 +5743,7 @@ describe('api', function() hl('DoubleQuotedEscape', '\\xFF'), hl('DoubleQuote', '"'), }) - check_parsing('"\\xF"', 0, { + check_parsing('"\\xF"', { -- 012345 ast = { 'DoubleQuotedString(val="\\15"):0:0:"\\xF"', @@ -5676,7 +5753,7 @@ describe('api', function() hl('DoubleQuotedEscape', '\\xF'), hl('DoubleQuote', '"'), }) - check_parsing('"\\u00AB"', 0, { + check_parsing('"\\u00AB"', { -- 01234567 ast = { 'DoubleQuotedString(val="«"):0:0:"\\u00AB"', @@ -5686,7 +5763,7 @@ describe('api', function() hl('DoubleQuotedEscape', '\\u00AB'), hl('DoubleQuote', '"'), }) - check_parsing('"\\U000000AB"', 0, { + check_parsing('"\\U000000AB"', { -- 01234567 ast = { 'DoubleQuotedString(val="«"):0:0:"\\U000000AB"', @@ -5696,7 +5773,7 @@ describe('api', function() hl('DoubleQuotedEscape', '\\U000000AB'), hl('DoubleQuote', '"'), }) - check_parsing('"\\x"', 0, { + check_parsing('"\\x"', { -- 0123 ast = { 'DoubleQuotedString(val="x"):0:0:"\\x"', @@ -5707,7 +5784,7 @@ describe('api', function() hl('DoubleQuote', '"'), }) - check_parsing('"\\x', 0, { + check_parsing('"\\x', { -- 012 ast = { 'DoubleQuotedString(val="x"):0:0:"\\x', @@ -5721,7 +5798,7 @@ describe('api', function() hl('InvalidDoubleQuotedUnknownEscape', '\\x'), }) - check_parsing('"\\xF', 0, { + check_parsing('"\\xF', { -- 0123 ast = { 'DoubleQuotedString(val="\\15"):0:0:"\\xF', @@ -5735,7 +5812,7 @@ describe('api', function() hl('InvalidDoubleQuotedEscape', '\\xF'), }) - check_parsing('"\\u"', 0, { + check_parsing('"\\u"', { -- 0123 ast = { 'DoubleQuotedString(val="u"):0:0:"\\u"', @@ -5746,7 +5823,7 @@ describe('api', function() hl('DoubleQuote', '"'), }) - check_parsing('"\\u', 0, { + check_parsing('"\\u', { -- 012 ast = { 'DoubleQuotedString(val="u"):0:0:"\\u', @@ -5760,7 +5837,7 @@ describe('api', function() hl('InvalidDoubleQuotedUnknownEscape', '\\u'), }) - check_parsing('"\\U', 0, { + check_parsing('"\\U', { -- 012 ast = { 'DoubleQuotedString(val="U"):0:0:"\\U', @@ -5774,7 +5851,7 @@ describe('api', function() hl('InvalidDoubleQuotedUnknownEscape', '\\U'), }) - check_parsing('"\\U"', 0, { + check_parsing('"\\U"', { -- 0123 ast = { 'DoubleQuotedString(val="U"):0:0:"\\U"', @@ -5785,7 +5862,7 @@ describe('api', function() hl('DoubleQuote', '"'), }) - check_parsing('"\\xFX"', 0, { + check_parsing('"\\xFX"', { -- 012345 ast = { 'DoubleQuotedString(val="\\15X"):0:0:"\\xFX"', @@ -5797,7 +5874,7 @@ describe('api', function() hl('DoubleQuote', '"'), }) - check_parsing('"\\XFX"', 0, { + check_parsing('"\\XFX"', { -- 012345 ast = { 'DoubleQuotedString(val="\\15X"):0:0:"\\XFX"', @@ -5809,7 +5886,7 @@ describe('api', function() hl('DoubleQuote', '"'), }) - check_parsing('"\\xX"', 0, { + check_parsing('"\\xX"', { -- 01234 ast = { 'DoubleQuotedString(val="xX"):0:0:"\\xX"', @@ -5821,7 +5898,7 @@ describe('api', function() hl('DoubleQuote', '"'), }) - check_parsing('"\\XX"', 0, { + check_parsing('"\\XX"', { -- 01234 ast = { 'DoubleQuotedString(val="XX"):0:0:"\\XX"', @@ -5833,7 +5910,7 @@ describe('api', function() hl('DoubleQuote', '"'), }) - check_parsing('"\\uX"', 0, { + check_parsing('"\\uX"', { -- 01234 ast = { 'DoubleQuotedString(val="uX"):0:0:"\\uX"', @@ -5845,7 +5922,7 @@ describe('api', function() hl('DoubleQuote', '"'), }) - check_parsing('"\\UX"', 0, { + check_parsing('"\\UX"', { -- 01234 ast = { 'DoubleQuotedString(val="UX"):0:0:"\\UX"', @@ -5857,7 +5934,7 @@ describe('api', function() hl('DoubleQuote', '"'), }) - check_parsing('"\\x0X"', 0, { + check_parsing('"\\x0X"', { -- 012345 ast = { 'DoubleQuotedString(val="\\0X"):0:0:"\\x0X"', @@ -5869,7 +5946,7 @@ describe('api', function() hl('DoubleQuote', '"'), }) - check_parsing('"\\X0X"', 0, { + check_parsing('"\\X0X"', { -- 012345 ast = { 'DoubleQuotedString(val="\\0X"):0:0:"\\X0X"', @@ -5881,7 +5958,7 @@ describe('api', function() hl('DoubleQuote', '"'), }) - check_parsing('"\\u0X"', 0, { + check_parsing('"\\u0X"', { -- 012345 ast = { 'DoubleQuotedString(val="\\0X"):0:0:"\\u0X"', @@ -5893,7 +5970,7 @@ describe('api', function() hl('DoubleQuote', '"'), }) - check_parsing('"\\U0X"', 0, { + check_parsing('"\\U0X"', { -- 012345 ast = { 'DoubleQuotedString(val="\\0X"):0:0:"\\U0X"', @@ -5905,7 +5982,7 @@ describe('api', function() hl('DoubleQuote', '"'), }) - check_parsing('"\\x00X"', 0, { + check_parsing('"\\x00X"', { -- 0123456 ast = { 'DoubleQuotedString(val="\\0X"):0:0:"\\x00X"', @@ -5917,7 +5994,7 @@ describe('api', function() hl('DoubleQuote', '"'), }) - check_parsing('"\\X00X"', 0, { + check_parsing('"\\X00X"', { -- 0123456 ast = { 'DoubleQuotedString(val="\\0X"):0:0:"\\X00X"', @@ -5929,7 +6006,7 @@ describe('api', function() hl('DoubleQuote', '"'), }) - check_parsing('"\\u00X"', 0, { + check_parsing('"\\u00X"', { -- 0123456 ast = { 'DoubleQuotedString(val="\\0X"):0:0:"\\u00X"', @@ -5941,7 +6018,7 @@ describe('api', function() hl('DoubleQuote', '"'), }) - check_parsing('"\\U00X"', 0, { + check_parsing('"\\U00X"', { -- 0123456 ast = { 'DoubleQuotedString(val="\\0X"):0:0:"\\U00X"', @@ -5953,7 +6030,7 @@ describe('api', function() hl('DoubleQuote', '"'), }) - check_parsing('"\\u000X"', 0, { + check_parsing('"\\u000X"', { -- 01234567 ast = { 'DoubleQuotedString(val="\\0X"):0:0:"\\u000X"', @@ -5965,7 +6042,7 @@ describe('api', function() hl('DoubleQuote', '"'), }) - check_parsing('"\\U000X"', 0, { + check_parsing('"\\U000X"', { -- 01234567 ast = { 'DoubleQuotedString(val="\\0X"):0:0:"\\U000X"', @@ -5977,7 +6054,7 @@ describe('api', function() hl('DoubleQuote', '"'), }) - check_parsing('"\\u0000X"', 0, { + check_parsing('"\\u0000X"', { -- 012345678 ast = { 'DoubleQuotedString(val="\\0X"):0:0:"\\u0000X"', @@ -5989,7 +6066,7 @@ describe('api', function() hl('DoubleQuote', '"'), }) - check_parsing('"\\U0000X"', 0, { + check_parsing('"\\U0000X"', { -- 012345678 ast = { 'DoubleQuotedString(val="\\0X"):0:0:"\\U0000X"', @@ -6001,7 +6078,7 @@ describe('api', function() hl('DoubleQuote', '"'), }) - check_parsing('"\\U00000X"', 0, { + check_parsing('"\\U00000X"', { -- 0123456789 ast = { 'DoubleQuotedString(val="\\0X"):0:0:"\\U00000X"', @@ -6013,7 +6090,7 @@ describe('api', function() hl('DoubleQuote', '"'), }) - check_parsing('"\\U000000X"', 0, { + check_parsing('"\\U000000X"', { -- 01234567890 -- 0 1 ast = { @@ -6026,7 +6103,7 @@ describe('api', function() hl('DoubleQuote', '"'), }) - check_parsing('"\\U0000000X"', 0, { + check_parsing('"\\U0000000X"', { -- 012345678901 -- 0 1 ast = { @@ -6039,7 +6116,7 @@ describe('api', function() hl('DoubleQuote', '"'), }) - check_parsing('"\\U00000000X"', 0, { + check_parsing('"\\U00000000X"', { -- 0123456789012 -- 0 1 ast = { @@ -6052,7 +6129,7 @@ describe('api', function() hl('DoubleQuote', '"'), }) - check_parsing('"\\x000X"', 0, { + check_parsing('"\\x000X"', { -- 01234567 ast = { 'DoubleQuotedString(val="\\0000X"):0:0:"\\x000X"', @@ -6064,7 +6141,7 @@ describe('api', function() hl('DoubleQuote', '"'), }) - check_parsing('"\\X000X"', 0, { + check_parsing('"\\X000X"', { -- 01234567 ast = { 'DoubleQuotedString(val="\\0000X"):0:0:"\\X000X"', @@ -6076,7 +6153,7 @@ describe('api', function() hl('DoubleQuote', '"'), }) - check_parsing('"\\u00000X"', 0, { + check_parsing('"\\u00000X"', { -- 0123456789 ast = { 'DoubleQuotedString(val="\\0000X"):0:0:"\\u00000X"', @@ -6088,7 +6165,7 @@ describe('api', function() hl('DoubleQuote', '"'), }) - check_parsing('"\\U000000000X"', 0, { + check_parsing('"\\U000000000X"', { -- 01234567890123 -- 0 1 ast = { @@ -6101,7 +6178,7 @@ describe('api', function() hl('DoubleQuote', '"'), }) - check_parsing('"\\0"', 0, { + check_parsing('"\\0"', { -- 0123 ast = { 'DoubleQuotedString(val="\\0"):0:0:"\\0"', @@ -6112,7 +6189,7 @@ describe('api', function() hl('DoubleQuote', '"'), }) - check_parsing('"\\00"', 0, { + check_parsing('"\\00"', { -- 01234 ast = { 'DoubleQuotedString(val="\\0"):0:0:"\\00"', @@ -6123,7 +6200,7 @@ describe('api', function() hl('DoubleQuote', '"'), }) - check_parsing('"\\000"', 0, { + check_parsing('"\\000"', { -- 012345 ast = { 'DoubleQuotedString(val="\\0"):0:0:"\\000"', @@ -6134,7 +6211,7 @@ describe('api', function() hl('DoubleQuote', '"'), }) - check_parsing('"\\0000"', 0, { + check_parsing('"\\0000"', { -- 0123456 ast = { 'DoubleQuotedString(val="\\0000"):0:0:"\\0000"', @@ -6146,7 +6223,7 @@ describe('api', function() hl('DoubleQuote', '"'), }) - check_parsing('"\\8"', 0, { + check_parsing('"\\8"', { -- 0123 ast = { 'DoubleQuotedString(val="8"):0:0:"\\8"', @@ -6157,7 +6234,7 @@ describe('api', function() hl('DoubleQuote', '"'), }) - check_parsing('"\\08"', 0, { + check_parsing('"\\08"', { -- 01234 ast = { 'DoubleQuotedString(val="\\0008"):0:0:"\\08"', @@ -6169,7 +6246,7 @@ describe('api', function() hl('DoubleQuote', '"'), }) - check_parsing('"\\008"', 0, { + check_parsing('"\\008"', { -- 012345 ast = { 'DoubleQuotedString(val="\\0008"):0:0:"\\008"', @@ -6181,7 +6258,7 @@ describe('api', function() hl('DoubleQuote', '"'), }) - check_parsing('"\\0008"', 0, { + check_parsing('"\\0008"', { -- 0123456 ast = { 'DoubleQuotedString(val="\\0008"):0:0:"\\0008"', @@ -6193,7 +6270,7 @@ describe('api', function() hl('DoubleQuote', '"'), }) - check_parsing('"\\777"', 0, { + check_parsing('"\\777"', { -- 012345 ast = { 'DoubleQuotedString(val="\255"):0:0:"\\777"', @@ -6204,7 +6281,7 @@ describe('api', function() hl('DoubleQuote', '"'), }) - check_parsing('"\\050"', 0, { + check_parsing('"\\050"', { -- 012345 ast = { 'DoubleQuotedString(val="\40"):0:0:"\\050"', @@ -6215,7 +6292,7 @@ describe('api', function() hl('DoubleQuote', '"'), }) - check_parsing('"\\"', 0, { + check_parsing('"\\"', { -- 012345 ast = { 'DoubleQuotedString(val="\\21"):0:0:"\\"', @@ -6226,7 +6303,7 @@ describe('api', function() hl('DoubleQuote', '"'), }) - check_parsing('"\\<', 0, { + check_parsing('"\\<', { -- 012 ast = { 'DoubleQuotedString(val="<"):0:0:"\\<', @@ -6240,7 +6317,7 @@ describe('api', function() hl('InvalidDoubleQuotedUnknownEscape', '\\<'), }) - check_parsing('"\\<"', 0, { + check_parsing('"\\<"', { -- 0123 ast = { 'DoubleQuotedString(val="<"):0:0:"\\<"', @@ -6251,7 +6328,7 @@ describe('api', function() hl('DoubleQuote', '"'), }) - check_parsing('"\\@a}', 0, { + check_parsing('{->@a}', { ast = { { 'Lambda(\\di):0:0:{', @@ -1512,7 +1567,7 @@ describe('Expressions parser', function() hl('Register', '@a'), hl('Lambda', '}'), }) - check_parsing('{->@a+@b}', 0, { + check_parsing('{->@a+@b}', { -- 012345678 ast = { { @@ -1541,7 +1596,7 @@ describe('Expressions parser', function() hl('Register', '@b'), hl('Lambda', '}'), }) - check_parsing('{a->@a}', 0, { + check_parsing('{a->@a}', { -- 012345678 ast = { { @@ -1564,7 +1619,7 @@ describe('Expressions parser', function() hl('Register', '@a'), hl('Lambda', '}'), }) - check_parsing('{a,b->@a}', 0, { + check_parsing('{a,b->@a}', { -- 012345678 ast = { { @@ -1595,7 +1650,7 @@ describe('Expressions parser', function() hl('Register', '@a'), hl('Lambda', '}'), }) - check_parsing('{a,b,c->@a}', 0, { + check_parsing('{a,b,c->@a}', { -- 01234567890 ast = { { @@ -1634,7 +1689,7 @@ describe('Expressions parser', function() hl('Register', '@a'), hl('Lambda', '}'), }) - check_parsing('{a,b,c,d->@a}', 0, { + check_parsing('{a,b,c,d->@a}', { -- 0123456789012 ast = { { @@ -1681,7 +1736,7 @@ describe('Expressions parser', function() hl('Register', '@a'), hl('Lambda', '}'), }) - check_parsing('{a,b,c,d,->@a}', 0, { + check_parsing('{a,b,c,d,->@a}', { -- 01234567890123 ast = { { @@ -1734,7 +1789,7 @@ describe('Expressions parser', function() hl('Register', '@a'), hl('Lambda', '}'), }) - check_parsing('{a,b->{c,d->{e,f->@a}}}', 0, { + check_parsing('{a,b->{c,d->{e,f->@a}}}', { -- 01234567890123456789012 -- 0 1 2 ast = { @@ -1812,7 +1867,7 @@ describe('Expressions parser', function() hl('Lambda', '}'), hl('Lambda', '}'), }) - check_parsing('{a,b->c,d}', 0, { + check_parsing('{a,b->c,d}', { -- 0123456789 ast = { { @@ -1855,7 +1910,7 @@ describe('Expressions parser', function() hl('IdentifierName', 'd'), hl('Lambda', '}'), }) - check_parsing('a,b,c,d', 0, { + check_parsing('a,b,c,d', { -- 0123456789 ast = { { @@ -1891,7 +1946,7 @@ describe('Expressions parser', function() hl('InvalidComma', ','), hl('IdentifierName', 'd'), }) - check_parsing('a,b,c,d,', 0, { + check_parsing('a,b,c,d,', { -- 0123456789 ast = { { @@ -1933,7 +1988,7 @@ describe('Expressions parser', function() hl('IdentifierName', 'd'), hl('InvalidComma', ','), }) - check_parsing(',', 0, { + check_parsing(',', { -- 0123456789 ast = { { @@ -1950,7 +2005,7 @@ describe('Expressions parser', function() }, { hl('InvalidComma', ','), }) - check_parsing('{,a->@a}', 0, { + check_parsing('{,a->@a}', { -- 0123456789 ast = { { @@ -1984,7 +2039,7 @@ describe('Expressions parser', function() hl('Register', '@a'), hl('Curly', '}'), }) - check_parsing('}', 0, { + check_parsing('}', { -- 0123456789 ast = { 'UnknownFigure(---):0:0:', @@ -1996,7 +2051,7 @@ describe('Expressions parser', function() }, { hl('InvalidFigureBrace', '}'), }) - check_parsing('{->}', 0, { + check_parsing('{->}', { -- 0123456789 ast = { { @@ -2015,7 +2070,7 @@ describe('Expressions parser', function() hl('Arrow', '->'), hl('InvalidLambda', '}'), }) - check_parsing('{a,b}', 0, { + check_parsing('{a,b}', { -- 0123456789 ast = { { @@ -2042,7 +2097,7 @@ describe('Expressions parser', function() hl('IdentifierName', 'b'), hl('InvalidLambda', '}'), }) - check_parsing('{a,}', 0, { + check_parsing('{a,}', { -- 0123456789 ast = { { @@ -2067,7 +2122,7 @@ describe('Expressions parser', function() hl('Comma', ','), hl('InvalidLambda', '}'), }) - check_parsing('{@a:@b}', 0, { + check_parsing('{@a:@b}', { -- 0123456789 ast = { { @@ -2090,7 +2145,7 @@ describe('Expressions parser', function() hl('Register', '@b'), hl('Dict', '}'), }) - check_parsing('{@a:@b,@c:@d}', 0, { + check_parsing('{@a:@b,@c:@d}', { -- 0123456789012 -- 0 1 ast = { @@ -2130,7 +2185,7 @@ describe('Expressions parser', function() hl('Register', '@d'), hl('Dict', '}'), }) - check_parsing('{@a:@b,@c:@d,@e:@f,}', 0, { + check_parsing('{@a:@b,@c:@d,@e:@f,}', { -- 01234567890123456789 -- 0 1 ast = { @@ -2192,7 +2247,7 @@ describe('Expressions parser', function() hl('Comma', ','), hl('Dict', '}'), }) - check_parsing('{@a:@b,@c:@d,@e:@f,@g:}', 0, { + check_parsing('{@a:@b,@c:@d,@e:@f,@g:}', { -- 01234567890123456789012 -- 0 1 2 ast = { @@ -2266,7 +2321,7 @@ describe('Expressions parser', function() hl('Colon', ':'), hl('InvalidDict', '}'), }) - check_parsing('{@a:@b,}', 0, { + check_parsing('{@a:@b,}', { -- 01234567890123 -- 0 1 ast = { @@ -2296,7 +2351,7 @@ describe('Expressions parser', function() hl('Comma', ','), hl('Dict', '}'), }) - check_parsing('{({f -> g})(@h)(@i)}', 0, { + check_parsing('{({f -> g})(@h)(@i)}', { -- 01234567890123456789 -- 0 1 ast = { @@ -2352,7 +2407,7 @@ describe('Expressions parser', function() hl('CallingParenthesis', ')'), hl('Curly', '}'), }) - check_parsing('a:{b()}c', 0, { + check_parsing('a:{b()}c', { -- 01234567 ast = { { @@ -2389,7 +2444,7 @@ describe('Expressions parser', function() hl('Curly', '}'), hl('IdentifierName', 'c'), }) - check_parsing('a:{{b, c -> @d + @e + ({f -> g})(@h)}(@i)}j', 0, { + check_parsing('a:{{b, c -> @d + @e + ({f -> g})(@h)}(@i)}j', { -- 01234567890123456789012345678901234567890123456 -- 0 1 2 3 4 ast = { @@ -2499,7 +2554,7 @@ describe('Expressions parser', function() hl('Curly', '}'), hl('IdentifierName', 'j'), }) - check_parsing('{@a + @b : @c + @d, @e + @f : @g + @i}', 0, { + check_parsing('{@a + @b : @c + @d, @e + @f : @g + @i}', { -- 01234567890123456789012345678901234567 -- 0 1 2 3 ast = { @@ -2571,7 +2626,7 @@ describe('Expressions parser', function() hl('Register', '@i', 1), hl('Dict', '}'), }) - check_parsing('-> -> ->', 0, { + check_parsing('-> -> ->', { -- 01234567 ast = { { @@ -2602,7 +2657,7 @@ describe('Expressions parser', function() hl('InvalidArrow', '->', 1), hl('InvalidArrow', '->', 1), }) - check_parsing('a -> b -> c -> d', 0, { + check_parsing('a -> b -> c -> d', { -- 0123456789012345 -- 0 1 ast = { @@ -2639,7 +2694,7 @@ describe('Expressions parser', function() hl('InvalidArrow', '->', 1), hl('IdentifierName', 'd', 1), }) - check_parsing('{a -> b -> c}', 0, { + check_parsing('{a -> b -> c}', { -- 0123456789012 -- 0 1 ast = { @@ -2675,7 +2730,7 @@ describe('Expressions parser', function() hl('IdentifierName', 'c', 1), hl('Lambda', '}'), }) - check_parsing('{a: -> b}', 0, { + check_parsing('{a: -> b}', { -- 012345678 ast = { { @@ -2704,7 +2759,7 @@ describe('Expressions parser', function() hl('Curly', '}'), }) - check_parsing('{a:b -> b}', 0, { + check_parsing('{a:b -> b}', { -- 0123456789 ast = { { @@ -2734,7 +2789,7 @@ describe('Expressions parser', function() hl('Curly', '}'), }) - check_parsing('{a#b -> b}', 0, { + check_parsing('{a#b -> b}', { -- 0123456789 ast = { { @@ -2761,7 +2816,7 @@ describe('Expressions parser', function() hl('IdentifierName', 'b', 1), hl('Curly', '}'), }) - check_parsing('{a : b : c}', 0, { + check_parsing('{a : b : c}', { -- 01234567890 -- 0 1 ast = { @@ -2797,7 +2852,7 @@ describe('Expressions parser', function() hl('IdentifierName', 'c', 1), hl('Dict', '}'), }) - check_parsing('{', 0, { + check_parsing('{', { -- 0 ast = { 'UnknownFigure(\\di):0:0:{', @@ -2809,7 +2864,7 @@ describe('Expressions parser', function() }, { hl('FigureBrace', '{'), }) - check_parsing('{a', 0, { + check_parsing('{a', { -- 01 ast = { { @@ -2827,7 +2882,7 @@ describe('Expressions parser', function() hl('FigureBrace', '{'), hl('IdentifierName', 'a'), }) - check_parsing('{a,b', 0, { + check_parsing('{a,b', { -- 0123 ast = { { @@ -2853,7 +2908,7 @@ describe('Expressions parser', function() hl('Comma', ','), hl('IdentifierName', 'b'), }) - check_parsing('{a,b->', 0, { + check_parsing('{a,b->', { -- 012345 ast = { { @@ -2881,7 +2936,7 @@ describe('Expressions parser', function() hl('IdentifierName', 'b'), hl('Arrow', '->'), }) - check_parsing('{a,b->c', 0, { + check_parsing('{a,b->c', { -- 0123456 ast = { { @@ -2915,7 +2970,7 @@ describe('Expressions parser', function() hl('Arrow', '->'), hl('IdentifierName', 'c'), }) - check_parsing('{a : b', 0, { + check_parsing('{a : b', { -- 012345 ast = { { @@ -2941,7 +2996,7 @@ describe('Expressions parser', function() hl('Colon', ':', 1), hl('IdentifierName', 'b', 1), }) - check_parsing('{a : b,', 0, { + check_parsing('{a : b,', { -- 0123456 ast = { { @@ -2975,7 +3030,7 @@ describe('Expressions parser', function() }) end) itp('works with ternary operator', function() - check_parsing('a ? b : c', 0, { + check_parsing('a ? b : c', { -- 012345678 ast = { { @@ -2999,7 +3054,7 @@ describe('Expressions parser', function() hl('TernaryColon', ':', 1), hl('IdentifierName', 'c', 1), }) - check_parsing('@a?@b?@c:@d:@e', 0, { + check_parsing('@a?@b?@c:@d:@e', { -- 01234567890123 -- 0 1 ast = { @@ -3040,7 +3095,7 @@ describe('Expressions parser', function() hl('TernaryColon', ':'), hl('Register', '@e'), }) - check_parsing('@a?@b:@c?@d:@e', 0, { + check_parsing('@a?@b:@c?@d:@e', { -- 01234567890123 -- 0 1 ast = { @@ -3081,7 +3136,7 @@ describe('Expressions parser', function() hl('TernaryColon', ':'), hl('Register', '@e'), }) - check_parsing('@a?@b?@c?@d:@e?@f:@g:@h?@i:@j:@k', 0, { + check_parsing('@a?@b?@c?@d:@e?@f:@g:@h?@i:@j:@k', { -- 01234567890123456789012345678901 -- 0 1 2 3 ast = { @@ -3170,7 +3225,7 @@ describe('Expressions parser', function() hl('TernaryColon', ':'), hl('Register', '@k'), }) - check_parsing('?', 0, { + check_parsing('?', { -- 0 ast = { { @@ -3189,7 +3244,7 @@ describe('Expressions parser', function() hl('InvalidTernary', '?'), }) - check_parsing('?:', 0, { + check_parsing('?:', { -- 01 ast = { { @@ -3214,7 +3269,7 @@ describe('Expressions parser', function() hl('InvalidTernaryColon', ':'), }) - check_parsing('?::', 0, { + check_parsing('?::', { -- 012 ast = { { @@ -3246,7 +3301,7 @@ describe('Expressions parser', function() hl('InvalidColon', ':'), }) - check_parsing('a?b', 0, { + check_parsing('a?b', { -- 012 ast = { { @@ -3271,7 +3326,7 @@ describe('Expressions parser', function() hl('Ternary', '?'), hl('IdentifierName', 'b'), }) - check_parsing('a?b:', 0, { + check_parsing('a?b:', { -- 0123 ast = { { @@ -3298,7 +3353,7 @@ describe('Expressions parser', function() hl('IdentifierScopeDelimiter', ':'), }) - check_parsing('a?b::c', 0, { + check_parsing('a?b::c', { -- 012345 ast = { { @@ -3324,7 +3379,7 @@ describe('Expressions parser', function() hl('IdentifierName', 'c'), }) - check_parsing('a?b :', 0, { + check_parsing('a?b :', { -- 01234 ast = { { @@ -3351,7 +3406,7 @@ describe('Expressions parser', function() hl('TernaryColon', ':', 1), }) - check_parsing('(@a?@b:@c)?@d:@e', 0, { + check_parsing('(@a?@b:@c)?@d:@e', { -- 0123456789012345 -- 0 1 ast = { @@ -3400,7 +3455,7 @@ describe('Expressions parser', function() hl('Register', '@e'), }) - check_parsing('(@a?@b:@c)?(@d?@e:@f):(@g?@h:@i)', 0, { + check_parsing('(@a?@b:@c)?(@d?@e:@f):(@g?@h:@i)', { -- 01234567890123456789012345678901 -- 0 1 2 3 ast = { @@ -3495,7 +3550,7 @@ describe('Expressions parser', function() hl('NestingParenthesis', ')'), }) - check_parsing('(@a?@b:@c)?@d?@e:@f:@g?@h:@i', 0, { + check_parsing('(@a?@b:@c)?@d?@e:@f:@g?@h:@i', { -- 0123456789012345678901234567 -- 0 1 2 ast = { @@ -3575,7 +3630,7 @@ describe('Expressions parser', function() hl('TernaryColon', ':'), hl('Register', '@i'), }) - check_parsing('a?b{cdef}g:h', 0, { + check_parsing('a?b{cdef}g:h', { -- 012345678901 -- 0 1 ast = { @@ -3621,7 +3676,7 @@ describe('Expressions parser', function() hl('TernaryColon', ':'), hl('IdentifierName', 'h'), }) - check_parsing('a ? b : c : d', 0, { + check_parsing('a ? b : c : d', { -- 0123456789012 -- 0 1 ast = { @@ -3660,7 +3715,7 @@ describe('Expressions parser', function() }) end) itp('works with comparison operators', function() - check_parsing('a == b', 0, { + check_parsing('a == b', { -- 012345 ast = { { @@ -3677,7 +3732,7 @@ describe('Expressions parser', function() hl('IdentifierName', 'b', 1), }) - check_parsing('a ==? b', 0, { + check_parsing('a ==? b', { -- 0123456 ast = { { @@ -3695,7 +3750,7 @@ describe('Expressions parser', function() hl('IdentifierName', 'b', 1), }) - check_parsing('a ==# b', 0, { + check_parsing('a ==# b', { -- 0123456 ast = { { @@ -3713,7 +3768,7 @@ describe('Expressions parser', function() hl('IdentifierName', 'b', 1), }) - check_parsing('a !=# b', 0, { + check_parsing('a !=# b', { -- 0123456 ast = { { @@ -3731,7 +3786,7 @@ describe('Expressions parser', function() hl('IdentifierName', 'b', 1), }) - check_parsing('a <=# b', 0, { + check_parsing('a <=# b', { -- 0123456 ast = { { @@ -3749,7 +3804,7 @@ describe('Expressions parser', function() hl('IdentifierName', 'b', 1), }) - check_parsing('a >=# b', 0, { + check_parsing('a >=# b', { -- 0123456 ast = { { @@ -3767,7 +3822,7 @@ describe('Expressions parser', function() hl('IdentifierName', 'b', 1), }) - check_parsing('a ># b', 0, { + check_parsing('a ># b', { -- 012345 ast = { { @@ -3785,7 +3840,7 @@ describe('Expressions parser', function() hl('IdentifierName', 'b', 1), }) - check_parsing('a <# b', 0, { + check_parsing('a <# b', { -- 012345 ast = { { @@ -3803,7 +3858,7 @@ describe('Expressions parser', function() hl('IdentifierName', 'b', 1), }) - check_parsing('a is#b', 0, { + check_parsing('a is#b', { -- 012345 ast = { { @@ -3821,7 +3876,7 @@ describe('Expressions parser', function() hl('IdentifierName', 'b'), }) - check_parsing('a is?b', 0, { + check_parsing('a is?b', { -- 012345 ast = { { @@ -3839,7 +3894,7 @@ describe('Expressions parser', function() hl('IdentifierName', 'b'), }) - check_parsing('a isnot b', 0, { + check_parsing('a isnot b', { -- 012345678 ast = { { @@ -3856,7 +3911,7 @@ describe('Expressions parser', function() hl('IdentifierName', 'b', 1), }) - check_parsing('a < b < c', 0, { + check_parsing('a < b < c', { -- 012345678 ast = { { @@ -3885,7 +3940,7 @@ describe('Expressions parser', function() hl('IdentifierName', 'c', 1), }) - check_parsing('a < b <# c', 0, { + check_parsing('a < b <# c', { -- 012345678 ast = { { @@ -3915,7 +3970,7 @@ describe('Expressions parser', function() hl('IdentifierName', 'c', 1), }) - check_parsing('a += b', 0, { + check_parsing('a += b', { -- 012345 ast = { { @@ -3942,7 +3997,7 @@ describe('Expressions parser', function() hl('InvalidComparison', '='), hl('IdentifierName', 'b', 1), }) - check_parsing('a + b == c + d', 0, { + check_parsing('a + b == c + d', { -- 01234567890123 -- 0 1 ast = { @@ -3975,7 +4030,7 @@ describe('Expressions parser', function() hl('BinaryPlus', '+', 1), hl('IdentifierName', 'd', 1), }) - check_parsing('+ a == + b', 0, { + check_parsing('+ a == + b', { -- 0123456789 ast = { { @@ -4005,7 +4060,7 @@ describe('Expressions parser', function() }) end) itp('works with concat/subscript', function() - check_parsing('.', 0, { + check_parsing('.', { -- 0 ast = { { @@ -4023,7 +4078,7 @@ describe('Expressions parser', function() hl('InvalidConcatOrSubscript', '.'), }) - check_parsing('a.', 0, { + check_parsing('a.', { -- 01 ast = { { @@ -4042,7 +4097,7 @@ describe('Expressions parser', function() hl('ConcatOrSubscript', '.'), }) - check_parsing('a.b', 0, { + check_parsing('a.b', { -- 012 ast = { { @@ -4059,7 +4114,7 @@ describe('Expressions parser', function() hl('IdentifierKey', 'b'), }) - check_parsing('1.2', 0, { + check_parsing('1.2', { -- 012 ast = { 'Float(val=1.200000e+00):0:0:1.2', @@ -4068,7 +4123,7 @@ describe('Expressions parser', function() hl('Float', '1.2'), }) - check_parsing('1.2 + 1.3e-5', 0, { + check_parsing('1.2 + 1.3e-5', { -- 012345678901 -- 0 1 ast = { @@ -4086,7 +4141,7 @@ describe('Expressions parser', function() hl('Float', '1.3e-5', 1), }) - check_parsing('a . 1.2 + 1.3e-5', 0, { + check_parsing('a . 1.2 + 1.3e-5', { -- 0123456789012345 -- 0 1 ast = { @@ -4120,7 +4175,7 @@ describe('Expressions parser', function() hl('Float', '1.3e-5', 1), }) - check_parsing('1.3e-5 + 1.2 . a', 0, { + check_parsing('1.3e-5 + 1.2 . a', { -- 0123456789012345 -- 0 1 ast = { @@ -4146,7 +4201,7 @@ describe('Expressions parser', function() hl('IdentifierName', 'a', 1), }) - check_parsing('1.3e-5 + a . 1.2', 0, { + check_parsing('1.3e-5 + a . 1.2', { -- 0123456789012345 -- 0 1 ast = { @@ -4180,7 +4235,7 @@ describe('Expressions parser', function() hl('IdentifierKey', '2'), }) - check_parsing('1.2.3', 0, { + check_parsing('1.2.3', { -- 01234 ast = { { @@ -4205,7 +4260,7 @@ describe('Expressions parser', function() hl('IdentifierKey', '3'), }) - check_parsing('a.1.2', 0, { + check_parsing('a.1.2', { -- 01234 ast = { { @@ -4230,7 +4285,7 @@ describe('Expressions parser', function() hl('IdentifierKey', '2'), }) - check_parsing('a . 1.2', 0, { + check_parsing('a . 1.2', { -- 0123456 ast = { { @@ -4255,7 +4310,7 @@ describe('Expressions parser', function() hl('IdentifierKey', '2'), }) - check_parsing('+a . +b', 0, { + check_parsing('+a . +b', { -- 0123456 ast = { { @@ -4284,7 +4339,7 @@ describe('Expressions parser', function() hl('IdentifierName', 'b'), }) - check_parsing('a. b', 0, { + check_parsing('a. b', { -- 0123 ast = { { @@ -4301,7 +4356,7 @@ describe('Expressions parser', function() hl('IdentifierName', 'b', 1), }) - check_parsing('a. 1', 0, { + check_parsing('a. 1', { -- 0123 ast = { { @@ -4319,7 +4374,7 @@ describe('Expressions parser', function() }) end) itp('works with bracket subscripts', function() - check_parsing(':', 0, { + check_parsing(':', { -- 0 ast = { { @@ -4336,7 +4391,7 @@ describe('Expressions parser', function() }, { hl('InvalidColon', ':'), }) - check_parsing('a[]', 0, { + check_parsing('a[]', { -- 012 ast = { { @@ -4355,7 +4410,7 @@ describe('Expressions parser', function() hl('SubscriptBracket', '['), hl('InvalidSubscriptBracket', ']'), }) - check_parsing('a[b:]', 0, { + check_parsing('a[b:]', { -- 01234 ast = { { @@ -4374,7 +4429,7 @@ describe('Expressions parser', function() hl('SubscriptBracket', ']'), }) - check_parsing('a[b:c]', 0, { + check_parsing('a[b:c]', { -- 012345 ast = { { @@ -4393,7 +4448,7 @@ describe('Expressions parser', function() hl('IdentifierName', 'c'), hl('SubscriptBracket', ']'), }) - check_parsing('a[b : c]', 0, { + check_parsing('a[b : c]', { -- 01234567 ast = { { @@ -4419,7 +4474,7 @@ describe('Expressions parser', function() hl('SubscriptBracket', ']'), }) - check_parsing('a[: b]', 0, { + check_parsing('a[: b]', { -- 012345 ast = { { @@ -4444,7 +4499,7 @@ describe('Expressions parser', function() hl('SubscriptBracket', ']'), }) - check_parsing('a[b :]', 0, { + check_parsing('a[b :]', { -- 012345 ast = { { @@ -4467,7 +4522,7 @@ describe('Expressions parser', function() hl('SubscriptColon', ':', 1), hl('SubscriptBracket', ']'), }) - check_parsing('a[b][c][d](e)(f)(g)', 0, { + check_parsing('a[b][c][d](e)(f)(g)', { -- 0123456789012345678 -- 0 1 ast = { @@ -4530,7 +4585,7 @@ describe('Expressions parser', function() hl('IdentifierName', 'g'), hl('CallingParenthesis', ')'), }) - check_parsing('{a}{b}{c}[d][e][f]', 0, { + check_parsing('{a}{b}{c}[d][e][f]', { -- 012345678901234567 -- 0 1 ast = { @@ -4603,7 +4658,7 @@ describe('Expressions parser', function() }) end) itp('supports list literals', function() - check_parsing('[]', 0, { + check_parsing('[]', { -- 01 ast = { 'ListLiteral:0:0:[', @@ -4613,7 +4668,7 @@ describe('Expressions parser', function() hl('List', ']'), }) - check_parsing('[a]', 0, { + check_parsing('[a]', { -- 012 ast = { { @@ -4629,7 +4684,7 @@ describe('Expressions parser', function() hl('List', ']'), }) - check_parsing('[a, b]', 0, { + check_parsing('[a, b]', { -- 012345 ast = { { @@ -4653,7 +4708,7 @@ describe('Expressions parser', function() hl('List', ']'), }) - check_parsing('[a, b, c]', 0, { + check_parsing('[a, b, c]', { -- 012345678 ast = { { @@ -4685,7 +4740,7 @@ describe('Expressions parser', function() hl('List', ']'), }) - check_parsing('[a, b, c, ]', 0, { + check_parsing('[a, b, c, ]', { -- 01234567890 -- 0 1 ast = { @@ -4724,7 +4779,7 @@ describe('Expressions parser', function() hl('List', ']', 1), }) - check_parsing('[a : b, c : d]', 0, { + check_parsing('[a : b, c : d]', { -- 01234567890123 -- 0 1 ast = { @@ -4769,7 +4824,7 @@ describe('Expressions parser', function() hl('List', ']'), }) - check_parsing(']', 0, { + check_parsing(']', { -- 0 ast = { 'ListLiteral:0:0:', @@ -4782,7 +4837,7 @@ describe('Expressions parser', function() hl('InvalidList', ']'), }) - check_parsing('a]', 0, { + check_parsing('a]', { -- 01 ast = { { @@ -4801,7 +4856,7 @@ describe('Expressions parser', function() hl('InvalidList', ']'), }) - check_parsing('[] []', 0, { + check_parsing('[] []', { -- 01234 ast = { { @@ -4822,9 +4877,23 @@ describe('Expressions parser', function() hl('InvalidSpacing', ' '), hl('List', '['), hl('List', ']'), + }, { + [1] = { + ast = { + err = REMOVE_THIS, + ast = { + 'ListLiteral:0:0:[', + }, + }, + hl_fs = { + [3] = REMOVE_THIS, + [4] = REMOVE_THIS, + [5] = REMOVE_THIS, + }, + }, }) - check_parsing('[][]', 0, { + check_parsing('[][]', { -- 0123 ast = { { @@ -4845,7 +4914,7 @@ describe('Expressions parser', function() hl('InvalidSubscriptBracket', ']'), }) - check_parsing('[', 0, { + check_parsing('[', { -- 0 ast = { 'ListLiteral:0:0:[', @@ -4858,7 +4927,7 @@ describe('Expressions parser', function() hl('List', '['), }) - check_parsing('[1', 0, { + check_parsing('[1', { -- 01 ast = { { @@ -4878,7 +4947,7 @@ describe('Expressions parser', function() }) end) itp('works with strings', function() - check_parsing('\'abc\'', 0, { + check_parsing('\'abc\'', { -- 01234 ast = { 'SingleQuotedString(val="abc"):0:0:\'abc\'', @@ -4888,7 +4957,7 @@ describe('Expressions parser', function() hl('SingleQuotedBody', 'abc'), hl('SingleQuote', '\''), }) - check_parsing('"abc"', 0, { + check_parsing('"abc"', { -- 01234 ast = { 'DoubleQuotedString(val="abc"):0:0:"abc"', @@ -4898,7 +4967,7 @@ describe('Expressions parser', function() hl('DoubleQuotedBody', 'abc'), hl('DoubleQuote', '"'), }) - check_parsing('\'\'', 0, { + check_parsing('\'\'', { -- 01 ast = { 'SingleQuotedString(val=NULL):0:0:\'\'', @@ -4907,7 +4976,7 @@ describe('Expressions parser', function() hl('SingleQuote', '\''), hl('SingleQuote', '\''), }) - check_parsing('""', 0, { + check_parsing('""', { -- 01 ast = { 'DoubleQuotedString(val=NULL):0:0:""', @@ -4916,7 +4985,7 @@ describe('Expressions parser', function() hl('DoubleQuote', '"'), hl('DoubleQuote', '"'), }) - check_parsing('"', 0, { + check_parsing('"', { -- 0 ast = { 'DoubleQuotedString(val=NULL):0:0:"', @@ -4928,7 +4997,7 @@ describe('Expressions parser', function() }, { hl('InvalidDoubleQuote', '"'), }) - check_parsing('\'', 0, { + check_parsing('\'', { -- 0 ast = { 'SingleQuotedString(val=NULL):0:0:\'', @@ -4940,7 +5009,7 @@ describe('Expressions parser', function() }, { hl('InvalidSingleQuote', '\''), }) - check_parsing('"a', 0, { + check_parsing('"a', { -- 01 ast = { 'DoubleQuotedString(val="a"):0:0:"a', @@ -4953,7 +5022,7 @@ describe('Expressions parser', function() hl('InvalidDoubleQuote', '"'), hl('InvalidDoubleQuotedBody', 'a'), }) - check_parsing('\'a', 0, { + check_parsing('\'a', { -- 01 ast = { 'SingleQuotedString(val="a"):0:0:\'a', @@ -4966,7 +5035,7 @@ describe('Expressions parser', function() hl('InvalidSingleQuote', '\''), hl('InvalidSingleQuotedBody', 'a'), }) - check_parsing('\'abc\'\'def\'', 0, { + check_parsing('\'abc\'\'def\'', { -- 0123456789 ast = { 'SingleQuotedString(val="abc\'def"):0:0:\'abc\'\'def\'', @@ -4978,7 +5047,7 @@ describe('Expressions parser', function() hl('SingleQuotedBody', 'def'), hl('SingleQuote', '\''), }) - check_parsing('\'abc\'\'', 0, { + check_parsing('\'abc\'\'', { -- 012345 ast = { 'SingleQuotedString(val="abc\'"):0:0:\'abc\'\'', @@ -4992,7 +5061,7 @@ describe('Expressions parser', function() hl('InvalidSingleQuotedBody', 'abc'), hl('InvalidSingleQuotedQuote', '\'\''), }) - check_parsing('\'\'\'\'\'\'\'\'', 0, { + check_parsing('\'\'\'\'\'\'\'\'', { -- 01234567 ast = { 'SingleQuotedString(val="\'\'\'"):0:0:\'\'\'\'\'\'\'\'', @@ -5004,7 +5073,7 @@ describe('Expressions parser', function() hl('SingleQuotedQuote', '\'\''), hl('SingleQuote', '\''), }) - check_parsing('\'\'\'a\'\'\'\'bc\'', 0, { + check_parsing('\'\'\'a\'\'\'\'bc\'', { -- 01234567890 -- 0 1 ast = { @@ -5019,7 +5088,7 @@ describe('Expressions parser', function() hl('SingleQuotedBody', 'bc'), hl('SingleQuote', '\''), }) - check_parsing('"\\"\\"\\"\\""', 0, { + check_parsing('"\\"\\"\\"\\""', { -- 0123456789 ast = { 'DoubleQuotedString(val="\\"\\"\\"\\""):0:0:"\\"\\"\\"\\""', @@ -5032,7 +5101,7 @@ describe('Expressions parser', function() hl('DoubleQuotedEscape', '\\"'), hl('DoubleQuote', '"'), }) - check_parsing('"abc\\"def\\"ghi\\"jkl\\"mno"', 0, { + check_parsing('"abc\\"def\\"ghi\\"jkl\\"mno"', { -- 0123456789012345678901234 -- 0 1 2 ast = { @@ -5051,7 +5120,7 @@ describe('Expressions parser', function() hl('DoubleQuotedBody', 'mno'), hl('DoubleQuote', '"'), }) - check_parsing('"\\b\\e\\f\\r\\t\\\\"', 0, { + check_parsing('"\\b\\e\\f\\r\\t\\\\"', { -- 0123456789012345 -- 0 1 ast = { @@ -5067,7 +5136,7 @@ describe('Expressions parser', function() hl('DoubleQuotedEscape', '\\\\'), hl('DoubleQuote', '"'), }) - check_parsing('"\\n\n"', 0, { + check_parsing('"\\n\n"', { -- 01234 ast = { 'DoubleQuotedString(val="\\\n\\\n"):0:0:"\\n\n"', @@ -5078,7 +5147,7 @@ describe('Expressions parser', function() hl('DoubleQuotedBody', '\n'), hl('DoubleQuote', '"'), }) - check_parsing('"\\x00"', 0, { + check_parsing('"\\x00"', { -- 012345 ast = { 'DoubleQuotedString(val="\\0"):0:0:"\\x00"', @@ -5088,7 +5157,7 @@ describe('Expressions parser', function() hl('DoubleQuotedEscape', '\\x00'), hl('DoubleQuote', '"'), }) - check_parsing('"\\xFF"', 0, { + check_parsing('"\\xFF"', { -- 012345 ast = { 'DoubleQuotedString(val="\255"):0:0:"\\xFF"', @@ -5098,7 +5167,7 @@ describe('Expressions parser', function() hl('DoubleQuotedEscape', '\\xFF'), hl('DoubleQuote', '"'), }) - check_parsing('"\\xF"', 0, { + check_parsing('"\\xF"', { -- 012345 ast = { 'DoubleQuotedString(val="\\15"):0:0:"\\xF"', @@ -5108,7 +5177,7 @@ describe('Expressions parser', function() hl('DoubleQuotedEscape', '\\xF'), hl('DoubleQuote', '"'), }) - check_parsing('"\\u00AB"', 0, { + check_parsing('"\\u00AB"', { -- 01234567 ast = { 'DoubleQuotedString(val="«"):0:0:"\\u00AB"', @@ -5118,7 +5187,7 @@ describe('Expressions parser', function() hl('DoubleQuotedEscape', '\\u00AB'), hl('DoubleQuote', '"'), }) - check_parsing('"\\U000000AB"', 0, { + check_parsing('"\\U000000AB"', { -- 01234567 ast = { 'DoubleQuotedString(val="«"):0:0:"\\U000000AB"', @@ -5128,7 +5197,7 @@ describe('Expressions parser', function() hl('DoubleQuotedEscape', '\\U000000AB'), hl('DoubleQuote', '"'), }) - check_parsing('"\\x"', 0, { + check_parsing('"\\x"', { -- 0123 ast = { 'DoubleQuotedString(val="x"):0:0:"\\x"', @@ -5139,7 +5208,7 @@ describe('Expressions parser', function() hl('DoubleQuote', '"'), }) - check_parsing('"\\x', 0, { + check_parsing('"\\x', { -- 012 ast = { 'DoubleQuotedString(val="x"):0:0:"\\x', @@ -5153,7 +5222,7 @@ describe('Expressions parser', function() hl('InvalidDoubleQuotedUnknownEscape', '\\x'), }) - check_parsing('"\\xF', 0, { + check_parsing('"\\xF', { -- 0123 ast = { 'DoubleQuotedString(val="\\15"):0:0:"\\xF', @@ -5167,7 +5236,7 @@ describe('Expressions parser', function() hl('InvalidDoubleQuotedEscape', '\\xF'), }) - check_parsing('"\\u"', 0, { + check_parsing('"\\u"', { -- 0123 ast = { 'DoubleQuotedString(val="u"):0:0:"\\u"', @@ -5178,7 +5247,7 @@ describe('Expressions parser', function() hl('DoubleQuote', '"'), }) - check_parsing('"\\u', 0, { + check_parsing('"\\u', { -- 012 ast = { 'DoubleQuotedString(val="u"):0:0:"\\u', @@ -5192,7 +5261,7 @@ describe('Expressions parser', function() hl('InvalidDoubleQuotedUnknownEscape', '\\u'), }) - check_parsing('"\\U', 0, { + check_parsing('"\\U', { -- 012 ast = { 'DoubleQuotedString(val="U"):0:0:"\\U', @@ -5206,7 +5275,7 @@ describe('Expressions parser', function() hl('InvalidDoubleQuotedUnknownEscape', '\\U'), }) - check_parsing('"\\U"', 0, { + check_parsing('"\\U"', { -- 0123 ast = { 'DoubleQuotedString(val="U"):0:0:"\\U"', @@ -5217,7 +5286,7 @@ describe('Expressions parser', function() hl('DoubleQuote', '"'), }) - check_parsing('"\\xFX"', 0, { + check_parsing('"\\xFX"', { -- 012345 ast = { 'DoubleQuotedString(val="\\15X"):0:0:"\\xFX"', @@ -5229,7 +5298,7 @@ describe('Expressions parser', function() hl('DoubleQuote', '"'), }) - check_parsing('"\\XFX"', 0, { + check_parsing('"\\XFX"', { -- 012345 ast = { 'DoubleQuotedString(val="\\15X"):0:0:"\\XFX"', @@ -5241,7 +5310,7 @@ describe('Expressions parser', function() hl('DoubleQuote', '"'), }) - check_parsing('"\\xX"', 0, { + check_parsing('"\\xX"', { -- 01234 ast = { 'DoubleQuotedString(val="xX"):0:0:"\\xX"', @@ -5253,7 +5322,7 @@ describe('Expressions parser', function() hl('DoubleQuote', '"'), }) - check_parsing('"\\XX"', 0, { + check_parsing('"\\XX"', { -- 01234 ast = { 'DoubleQuotedString(val="XX"):0:0:"\\XX"', @@ -5265,7 +5334,7 @@ describe('Expressions parser', function() hl('DoubleQuote', '"'), }) - check_parsing('"\\uX"', 0, { + check_parsing('"\\uX"', { -- 01234 ast = { 'DoubleQuotedString(val="uX"):0:0:"\\uX"', @@ -5277,7 +5346,7 @@ describe('Expressions parser', function() hl('DoubleQuote', '"'), }) - check_parsing('"\\UX"', 0, { + check_parsing('"\\UX"', { -- 01234 ast = { 'DoubleQuotedString(val="UX"):0:0:"\\UX"', @@ -5289,7 +5358,7 @@ describe('Expressions parser', function() hl('DoubleQuote', '"'), }) - check_parsing('"\\x0X"', 0, { + check_parsing('"\\x0X"', { -- 012345 ast = { 'DoubleQuotedString(val="\\0X"):0:0:"\\x0X"', @@ -5301,7 +5370,7 @@ describe('Expressions parser', function() hl('DoubleQuote', '"'), }) - check_parsing('"\\X0X"', 0, { + check_parsing('"\\X0X"', { -- 012345 ast = { 'DoubleQuotedString(val="\\0X"):0:0:"\\X0X"', @@ -5313,7 +5382,7 @@ describe('Expressions parser', function() hl('DoubleQuote', '"'), }) - check_parsing('"\\u0X"', 0, { + check_parsing('"\\u0X"', { -- 012345 ast = { 'DoubleQuotedString(val="\\0X"):0:0:"\\u0X"', @@ -5325,7 +5394,7 @@ describe('Expressions parser', function() hl('DoubleQuote', '"'), }) - check_parsing('"\\U0X"', 0, { + check_parsing('"\\U0X"', { -- 012345 ast = { 'DoubleQuotedString(val="\\0X"):0:0:"\\U0X"', @@ -5337,7 +5406,7 @@ describe('Expressions parser', function() hl('DoubleQuote', '"'), }) - check_parsing('"\\x00X"', 0, { + check_parsing('"\\x00X"', { -- 0123456 ast = { 'DoubleQuotedString(val="\\0X"):0:0:"\\x00X"', @@ -5349,7 +5418,7 @@ describe('Expressions parser', function() hl('DoubleQuote', '"'), }) - check_parsing('"\\X00X"', 0, { + check_parsing('"\\X00X"', { -- 0123456 ast = { 'DoubleQuotedString(val="\\0X"):0:0:"\\X00X"', @@ -5361,7 +5430,7 @@ describe('Expressions parser', function() hl('DoubleQuote', '"'), }) - check_parsing('"\\u00X"', 0, { + check_parsing('"\\u00X"', { -- 0123456 ast = { 'DoubleQuotedString(val="\\0X"):0:0:"\\u00X"', @@ -5373,7 +5442,7 @@ describe('Expressions parser', function() hl('DoubleQuote', '"'), }) - check_parsing('"\\U00X"', 0, { + check_parsing('"\\U00X"', { -- 0123456 ast = { 'DoubleQuotedString(val="\\0X"):0:0:"\\U00X"', @@ -5385,7 +5454,7 @@ describe('Expressions parser', function() hl('DoubleQuote', '"'), }) - check_parsing('"\\u000X"', 0, { + check_parsing('"\\u000X"', { -- 01234567 ast = { 'DoubleQuotedString(val="\\0X"):0:0:"\\u000X"', @@ -5397,7 +5466,7 @@ describe('Expressions parser', function() hl('DoubleQuote', '"'), }) - check_parsing('"\\U000X"', 0, { + check_parsing('"\\U000X"', { -- 01234567 ast = { 'DoubleQuotedString(val="\\0X"):0:0:"\\U000X"', @@ -5409,7 +5478,7 @@ describe('Expressions parser', function() hl('DoubleQuote', '"'), }) - check_parsing('"\\u0000X"', 0, { + check_parsing('"\\u0000X"', { -- 012345678 ast = { 'DoubleQuotedString(val="\\0X"):0:0:"\\u0000X"', @@ -5421,7 +5490,7 @@ describe('Expressions parser', function() hl('DoubleQuote', '"'), }) - check_parsing('"\\U0000X"', 0, { + check_parsing('"\\U0000X"', { -- 012345678 ast = { 'DoubleQuotedString(val="\\0X"):0:0:"\\U0000X"', @@ -5433,7 +5502,7 @@ describe('Expressions parser', function() hl('DoubleQuote', '"'), }) - check_parsing('"\\U00000X"', 0, { + check_parsing('"\\U00000X"', { -- 0123456789 ast = { 'DoubleQuotedString(val="\\0X"):0:0:"\\U00000X"', @@ -5445,7 +5514,7 @@ describe('Expressions parser', function() hl('DoubleQuote', '"'), }) - check_parsing('"\\U000000X"', 0, { + check_parsing('"\\U000000X"', { -- 01234567890 -- 0 1 ast = { @@ -5458,7 +5527,7 @@ describe('Expressions parser', function() hl('DoubleQuote', '"'), }) - check_parsing('"\\U0000000X"', 0, { + check_parsing('"\\U0000000X"', { -- 012345678901 -- 0 1 ast = { @@ -5471,7 +5540,7 @@ describe('Expressions parser', function() hl('DoubleQuote', '"'), }) - check_parsing('"\\U00000000X"', 0, { + check_parsing('"\\U00000000X"', { -- 0123456789012 -- 0 1 ast = { @@ -5484,7 +5553,7 @@ describe('Expressions parser', function() hl('DoubleQuote', '"'), }) - check_parsing('"\\x000X"', 0, { + check_parsing('"\\x000X"', { -- 01234567 ast = { 'DoubleQuotedString(val="\\0000X"):0:0:"\\x000X"', @@ -5496,7 +5565,7 @@ describe('Expressions parser', function() hl('DoubleQuote', '"'), }) - check_parsing('"\\X000X"', 0, { + check_parsing('"\\X000X"', { -- 01234567 ast = { 'DoubleQuotedString(val="\\0000X"):0:0:"\\X000X"', @@ -5508,7 +5577,7 @@ describe('Expressions parser', function() hl('DoubleQuote', '"'), }) - check_parsing('"\\u00000X"', 0, { + check_parsing('"\\u00000X"', { -- 0123456789 ast = { 'DoubleQuotedString(val="\\0000X"):0:0:"\\u00000X"', @@ -5520,7 +5589,7 @@ describe('Expressions parser', function() hl('DoubleQuote', '"'), }) - check_parsing('"\\U000000000X"', 0, { + check_parsing('"\\U000000000X"', { -- 01234567890123 -- 0 1 ast = { @@ -5533,7 +5602,7 @@ describe('Expressions parser', function() hl('DoubleQuote', '"'), }) - check_parsing('"\\0"', 0, { + check_parsing('"\\0"', { -- 0123 ast = { 'DoubleQuotedString(val="\\0"):0:0:"\\0"', @@ -5544,7 +5613,7 @@ describe('Expressions parser', function() hl('DoubleQuote', '"'), }) - check_parsing('"\\00"', 0, { + check_parsing('"\\00"', { -- 01234 ast = { 'DoubleQuotedString(val="\\0"):0:0:"\\00"', @@ -5555,7 +5624,7 @@ describe('Expressions parser', function() hl('DoubleQuote', '"'), }) - check_parsing('"\\000"', 0, { + check_parsing('"\\000"', { -- 012345 ast = { 'DoubleQuotedString(val="\\0"):0:0:"\\000"', @@ -5566,7 +5635,7 @@ describe('Expressions parser', function() hl('DoubleQuote', '"'), }) - check_parsing('"\\0000"', 0, { + check_parsing('"\\0000"', { -- 0123456 ast = { 'DoubleQuotedString(val="\\0000"):0:0:"\\0000"', @@ -5578,7 +5647,7 @@ describe('Expressions parser', function() hl('DoubleQuote', '"'), }) - check_parsing('"\\8"', 0, { + check_parsing('"\\8"', { -- 0123 ast = { 'DoubleQuotedString(val="8"):0:0:"\\8"', @@ -5589,7 +5658,7 @@ describe('Expressions parser', function() hl('DoubleQuote', '"'), }) - check_parsing('"\\08"', 0, { + check_parsing('"\\08"', { -- 01234 ast = { 'DoubleQuotedString(val="\\0008"):0:0:"\\08"', @@ -5601,7 +5670,7 @@ describe('Expressions parser', function() hl('DoubleQuote', '"'), }) - check_parsing('"\\008"', 0, { + check_parsing('"\\008"', { -- 012345 ast = { 'DoubleQuotedString(val="\\0008"):0:0:"\\008"', @@ -5613,7 +5682,7 @@ describe('Expressions parser', function() hl('DoubleQuote', '"'), }) - check_parsing('"\\0008"', 0, { + check_parsing('"\\0008"', { -- 0123456 ast = { 'DoubleQuotedString(val="\\0008"):0:0:"\\0008"', @@ -5625,7 +5694,7 @@ describe('Expressions parser', function() hl('DoubleQuote', '"'), }) - check_parsing('"\\777"', 0, { + check_parsing('"\\777"', { -- 012345 ast = { 'DoubleQuotedString(val="\255"):0:0:"\\777"', @@ -5636,7 +5705,7 @@ describe('Expressions parser', function() hl('DoubleQuote', '"'), }) - check_parsing('"\\050"', 0, { + check_parsing('"\\050"', { -- 012345 ast = { 'DoubleQuotedString(val="\40"):0:0:"\\050"', @@ -5647,7 +5716,7 @@ describe('Expressions parser', function() hl('DoubleQuote', '"'), }) - check_parsing('"\\"', 0, { + check_parsing('"\\"', { -- 012345 ast = { 'DoubleQuotedString(val="\\21"):0:0:"\\"', @@ -5658,7 +5727,7 @@ describe('Expressions parser', function() hl('DoubleQuote', '"'), }) - check_parsing('"\\<', 0, { + check_parsing('"\\<', { -- 012 ast = { 'DoubleQuotedString(val="<"):0:0:"\\<', @@ -5672,7 +5741,7 @@ describe('Expressions parser', function() hl('InvalidDoubleQuotedUnknownEscape', '\\<'), }) - check_parsing('"\\<"', 0, { + check_parsing('"\\<"', { -- 0123 ast = { 'DoubleQuotedString(val="<"):0:0:"\\<"', @@ -5683,7 +5752,7 @@ describe('Expressions parser', function() hl('DoubleQuote', '"'), }) - check_parsing('"\\ Date: Mon, 6 Nov 2017 01:15:18 +0300 Subject: api/vim: Add “len” dictionary key MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This allows determining where parsing ended which may be needed for e.g. parsing `:echo` with that API function. --- test/functional/api/vim_spec.lua | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'test') diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua index b904bd2a8f..714b1988fb 100644 --- a/test/functional/api/vim_spec.lua +++ b/test/functional/api/vim_spec.lua @@ -799,6 +799,9 @@ describe('api', function() if east_api.ast then east_api.ast = {simplify_east_api_node(line, east_api.ast)} end + if east_api.len == #line then + east_api.len = nil + end return east_api end local function simplify_east_hl(line, east_hl) @@ -997,6 +1000,7 @@ describe('api', function() }, { [1] = { ast = { + len = 2, err = REMOVE_THIS, ast = { 'Register(name=a):0:0:@a' @@ -1028,6 +1032,7 @@ describe('api', function() }, { [1] = { ast = { + len = 6, err = REMOVE_THIS, ast = { 'Register(name=a):0:0: @a' @@ -1236,6 +1241,7 @@ describe('api', function() }, { [1] = { ast = { + len = 3, err = REMOVE_THIS, ast = { 'Register(name=a):0:0:@a', @@ -5456,6 +5462,7 @@ describe('api', function() }, { [1] = { ast = { + len = 3, err = REMOVE_THIS, ast = { 'ListLiteral:0:0:[', @@ -7135,6 +7142,7 @@ describe('api', function() }, { [1] = { ast = { + len = 4, err = REMOVE_THIS, ast = { 'Option(scope=0,ident=xxx):0:0:&xxx', @@ -7520,6 +7528,7 @@ describe('api', function() }, { [1] = { ast = { + len = 1, err = REMOVE_THIS, ast = { 'Integer(val=1):0:0:1', @@ -7652,6 +7661,7 @@ describe('api', function() end) it('respects highlight argument', function() eq({ + len = 1, ast = { ivalue = 1, len = 1, @@ -7660,6 +7670,7 @@ describe('api', function() }, }, meths.parse_expression('1', '', false)) eq({ + len = 1, ast = { ivalue = 1, len = 1, @@ -7674,6 +7685,7 @@ describe('api', function() it('works (KLEE tests)', function() check_parsing('\0002&A:\000', { ast = {}, + len = 0, err = { arg = '\0002&A:\0', msg = 'E15: Expected value, got EOC: %.*s', @@ -7682,6 +7694,7 @@ describe('api', function() }, { [2] = { ast = { + len = REMOVE_THIS, ast = { { 'Colon:0:4::', @@ -7711,6 +7724,7 @@ describe('api', function() }, [3] = { ast = { + len = 2, ast = { 'Integer(val=2):0:1:2', }, @@ -7754,6 +7768,7 @@ describe('api', function() check_parsing('|"\\U\\', { -- 01234 ast = {}, + len = 0, err = { arg = '|"\\U\\', msg = 'E15: Expected value, got EOC: %.*s', @@ -7762,6 +7777,7 @@ describe('api', function() }, { [2] = { ast = { + len = REMOVE_THIS, ast = { { 'Or:0:0:|', @@ -7786,6 +7802,7 @@ describe('api', function() check_parsing('|"\\e"', { -- 01234 ast = {}, + len = 0, err = { arg = '|"\\e"', msg = 'E15: Expected value, got EOC: %.*s', @@ -7794,6 +7811,7 @@ describe('api', function() }, { [2] = { ast = { + len = REMOVE_THIS, ast = { { 'Or:0:0:|', @@ -7818,6 +7836,7 @@ describe('api', function() check_parsing('|\029', { -- 01 ast = {}, + len = 0, err = { arg = '|\029', msg = 'E15: Expected value, got EOC: %.*s', @@ -7826,6 +7845,7 @@ describe('api', function() }, { [2] = { ast = { + len = REMOVE_THIS, ast = { { 'Or:0:0:|', @@ -7892,6 +7912,7 @@ describe('api', function() }, { [1] = { ast = { + len = 1, ast = { 'UnknownFigure:0:0:', }, @@ -7917,6 +7938,7 @@ describe('api', function() }, }, }, + len = 2, err = { arg = ':?\000\000\000\000\000\000\000', msg = 'E15: Colon outside of dictionary or ternary operator: %.*s', @@ -7926,6 +7948,9 @@ describe('api', function() hl('InvalidTernary', '?'), }, { [2] = { + ast = { + len = REMOVE_THIS, + }, hl_fs = { [3] = hl('InvalidSpacing', '\0'), [4] = hl('InvalidSpacing', '\0'), -- cgit From 42959d0e8f9e779ba4983016b24967bf02abb59f Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 6 Nov 2017 20:15:05 +0300 Subject: unittests: Add tests for vim_str2nr --- test/helpers.lua | 8 + test/unit/charset/vim_str2nr_spec.lua | 294 +++++++++++++++++++++++++++++++++- 2 files changed, 301 insertions(+), 1 deletion(-) (limited to 'test') diff --git a/test/helpers.lua b/test/helpers.lua index 20fe23821f..16b9818f12 100644 --- a/test/helpers.lua +++ b/test/helpers.lua @@ -310,6 +310,13 @@ local function mergedicts_copy(d1, d2) return ret end +local function updated(d, d2) + for k, v in pairs(d2) do + d[k] = v + end + return d +end + local function concat_tables(...) local ret = {} for i = 1, select('#', ...) do @@ -460,4 +467,5 @@ return { format_luav = format_luav, format_string = format_string, intchar2lua = intchar2lua, + updated = updated, } diff --git a/test/unit/charset/vim_str2nr_spec.lua b/test/unit/charset/vim_str2nr_spec.lua index cfbc77bc2a..9309dc380c 100644 --- a/test/unit/charset/vim_str2nr_spec.lua +++ b/test/unit/charset/vim_str2nr_spec.lua @@ -1 +1,293 @@ --- FIXME +local helpers = require("test.unit.helpers")(after_each) +local global_helpers = require('test.helpers') + +local itp = helpers.gen_itp(it) + +local cimport = helpers.cimport +local ffi = helpers.ffi +local eq = helpers.eq + +local shallowcopy = global_helpers.shallowcopy +local updated = global_helpers.updated + +local lib = cimport('./src/nvim/charset.h') + +local ARGTYPES = { + num = ffi.typeof('varnumber_T[1]'), + unum = ffi.typeof('uvarnumber_T[1]'), + pre = ffi.typeof('int[1]'), + len = ffi.typeof('int[1]'), +} + +local icnt = -42 +local ucnt = 4242 + +local function arginit(arg) + if arg == 'unum' then + ucnt = ucnt + 1 + return ARGTYPES[arg]({ucnt}) + else + icnt = icnt - 1 + return ARGTYPES[arg]({icnt}) + end +end + +local function test_vim_str2nr(s, what, exp, maxlen) + local comb = {[''] = {}} + for k, _ in pairs(exp) do + for ck, cv in pairs(comb) do + comb[ck .. ',' .. k] = updated(shallowcopy(cv), { [k] = arginit(k) }) + end + end + maxlen = maxlen or #s + for _, cv in pairs(comb) do + lib.vim_str2nr(s, cv.pre, cv.len, what, cv.num, cv.unum, maxlen) + for cck, ccv in pairs(cv) do + if exp[cck] ~= tonumber(ccv[0]) then + error(('Failed check (%s = %d) in test (s=%s, w=%u, m=%d): %d'):format( + cck, exp[cck], s, tonumber(what), maxlen, tonumber(ccv[0]) + )) + end + end + end +end + +describe('vim_str2nr()', function() + itp('works fine when it has nothing to do', function() + test_vim_str2nr('', 0, {len = 0, num = 0, unum = 0, pre = 0}, 0) + test_vim_str2nr('', lib.STR2NR_ALL, {len = 0, num = 0, unum = 0, pre = 0}, 0) + test_vim_str2nr('', lib.STR2NR_BIN, {len = 0, num = 0, unum = 0, pre = 0}, 0) + test_vim_str2nr('', lib.STR2NR_OCT, {len = 0, num = 0, unum = 0, pre = 0}, 0) + test_vim_str2nr('', lib.STR2NR_HEX, {len = 0, num = 0, unum = 0, pre = 0}, 0) + test_vim_str2nr('', lib.STR2NR_FORCE + lib.STR2NR_DEC, {len = 0, num = 0, unum = 0, pre = 0}, 0) + test_vim_str2nr('', lib.STR2NR_FORCE + lib.STR2NR_BIN, {len = 0, num = 0, unum = 0, pre = 0}, 0) + test_vim_str2nr('', lib.STR2NR_FORCE + lib.STR2NR_OCT, {len = 0, num = 0, unum = 0, pre = 0}, 0) + test_vim_str2nr('', lib.STR2NR_FORCE + lib.STR2NR_HEX, {len = 0, num = 0, unum = 0, pre = 0}, 0) + end) + itp('works with decimal numbers', function() + for _, flags in ipairs({ + 0, + lib.STR2NR_BIN, + lib.STR2NR_OCT, + lib.STR2NR_HEX, + lib.STR2NR_BIN + lib.STR2NR_OCT, + lib.STR2NR_BIN + lib.STR2NR_HEX, + lib.STR2NR_OCT + lib.STR2NR_HEX, + lib.STR2NR_ALL, + lib.STR2NR_FORCE + lib.STR2NR_DEC, + }) do + -- Check that all digits are recognized + test_vim_str2nr( '12345', flags, {len = 5, num = 12345, unum = 12345, pre = 0}, 0) + test_vim_str2nr( '67890', flags, {len = 5, num = 67890, unum = 67890, pre = 0}, 0) + test_vim_str2nr( '12345A', flags, {len = 5, num = 12345, unum = 12345, pre = 0}, 0) + test_vim_str2nr( '67890A', flags, {len = 5, num = 67890, unum = 67890, pre = 0}, 0) + + test_vim_str2nr( '42', flags, {len = 2, num = 42, unum = 42, pre = 0}, 0) + test_vim_str2nr( '42', flags, {len = 1, num = 4, unum = 4, pre = 0}, 1) + test_vim_str2nr( '42', flags, {len = 2, num = 42, unum = 42, pre = 0}, 2) + test_vim_str2nr( '42', flags, {len = 2, num = 42, unum = 42, pre = 0}, 3) -- includes NUL byte in maxlen + + test_vim_str2nr( '42x', flags, {len = 2, num = 42, unum = 42, pre = 0}, 0) + test_vim_str2nr( '42x', flags, {len = 2, num = 42, unum = 42, pre = 0}, 3) + + test_vim_str2nr('-42', flags, {len = 3, num = -42, unum = 42, pre = 0}, 3) + test_vim_str2nr('-42', flags, {len = 1, num = 0, unum = 0, pre = 0}, 1) + + test_vim_str2nr('-42x', flags, {len = 3, num = -42, unum = 42, pre = 0}, 0) + test_vim_str2nr('-42x', flags, {len = 3, num = -42, unum = 42, pre = 0}, 4) + end + end) + itp('works with binary numbers', function() + for _, flags in ipairs({ + lib.STR2NR_BIN, + lib.STR2NR_BIN + lib.STR2NR_OCT, + lib.STR2NR_BIN + lib.STR2NR_HEX, + lib.STR2NR_ALL, + lib.STR2NR_FORCE + lib.STR2NR_BIN, + }) do + local bin + local BIN + if flags > lib.STR2NR_FORCE then + bin = 0 + BIN = 0 + else + bin = ('b'):byte() + BIN = ('B'):byte() + end + + test_vim_str2nr( '0b101', flags, {len = 5, num = 5, unum = 5, pre = bin}, 0) + test_vim_str2nr( '0b101', flags, {len = 1, num = 0, unum = 0, pre = 0 }, 1) + test_vim_str2nr( '0b101', flags, {len = 1, num = 0, unum = 0, pre = 0 }, 2) + test_vim_str2nr( '0b101', flags, {len = 3, num = 1, unum = 1, pre = bin}, 3) + test_vim_str2nr( '0b101', flags, {len = 4, num = 2, unum = 2, pre = bin}, 4) + test_vim_str2nr( '0b101', flags, {len = 5, num = 5, unum = 5, pre = bin}, 5) + test_vim_str2nr( '0b101', flags, {len = 5, num = 5, unum = 5, pre = bin}, 6) + + test_vim_str2nr( '0b1012', flags, {len = 5, num = 5, unum = 5, pre = bin}, 0) + test_vim_str2nr( '0b1012', flags, {len = 5, num = 5, unum = 5, pre = bin}, 6) + + test_vim_str2nr('-0b101', flags, {len = 6, num = -5, unum = 5, pre = bin}, 0) + test_vim_str2nr('-0b101', flags, {len = 1, num = 0, unum = 0, pre = 0 }, 1) + test_vim_str2nr('-0b101', flags, {len = 2, num = 0, unum = 0, pre = 0 }, 2) + test_vim_str2nr('-0b101', flags, {len = 2, num = 0, unum = 0, pre = 0 }, 3) + test_vim_str2nr('-0b101', flags, {len = 4, num = -1, unum = 1, pre = bin}, 4) + test_vim_str2nr('-0b101', flags, {len = 5, num = -2, unum = 2, pre = bin}, 5) + test_vim_str2nr('-0b101', flags, {len = 6, num = -5, unum = 5, pre = bin}, 6) + test_vim_str2nr('-0b101', flags, {len = 6, num = -5, unum = 5, pre = bin}, 7) + + test_vim_str2nr('-0b1012', flags, {len = 6, num = -5, unum = 5, pre = bin}, 0) + test_vim_str2nr('-0b1012', flags, {len = 6, num = -5, unum = 5, pre = bin}, 7) + + test_vim_str2nr( '0B101', flags, {len = 5, num = 5, unum = 5, pre = BIN}, 0) + test_vim_str2nr( '0B101', flags, {len = 1, num = 0, unum = 0, pre = 0 }, 1) + test_vim_str2nr( '0B101', flags, {len = 1, num = 0, unum = 0, pre = 0 }, 2) + test_vim_str2nr( '0B101', flags, {len = 3, num = 1, unum = 1, pre = BIN}, 3) + test_vim_str2nr( '0B101', flags, {len = 4, num = 2, unum = 2, pre = BIN}, 4) + test_vim_str2nr( '0B101', flags, {len = 5, num = 5, unum = 5, pre = BIN}, 5) + test_vim_str2nr( '0B101', flags, {len = 5, num = 5, unum = 5, pre = BIN}, 6) + + test_vim_str2nr( '0B1012', flags, {len = 5, num = 5, unum = 5, pre = BIN}, 0) + test_vim_str2nr( '0B1012', flags, {len = 5, num = 5, unum = 5, pre = BIN}, 6) + + test_vim_str2nr('-0B101', flags, {len = 6, num = -5, unum = 5, pre = BIN}, 0) + test_vim_str2nr('-0B101', flags, {len = 1, num = 0, unum = 0, pre = 0 }, 1) + test_vim_str2nr('-0B101', flags, {len = 2, num = 0, unum = 0, pre = 0 }, 2) + test_vim_str2nr('-0B101', flags, {len = 2, num = 0, unum = 0, pre = 0 }, 3) + test_vim_str2nr('-0B101', flags, {len = 4, num = -1, unum = 1, pre = BIN}, 4) + test_vim_str2nr('-0B101', flags, {len = 5, num = -2, unum = 2, pre = BIN}, 5) + test_vim_str2nr('-0B101', flags, {len = 6, num = -5, unum = 5, pre = BIN}, 6) + test_vim_str2nr('-0B101', flags, {len = 6, num = -5, unum = 5, pre = BIN}, 7) + + test_vim_str2nr('-0B1012', flags, {len = 6, num = -5, unum = 5, pre = BIN}, 0) + test_vim_str2nr('-0B1012', flags, {len = 6, num = -5, unum = 5, pre = BIN}, 7) + + if flags > lib.STR2NR_FORCE then + test_vim_str2nr('-101', flags, {len = 4, num = -5, unum = 5, pre = 0}, 0) + end + end + end) + itp('works with octal numbers', function() + for _, flags in ipairs({ + lib.STR2NR_OCT, + lib.STR2NR_OCT + lib.STR2NR_BIN, + lib.STR2NR_OCT + lib.STR2NR_HEX, + lib.STR2NR_ALL, + lib.STR2NR_FORCE + lib.STR2NR_OCT, + }) do + local oct + if flags > lib.STR2NR_FORCE then + oct = 0 + else + oct = ('0'):byte() + end + + -- Check that all digits are recognized + test_vim_str2nr( '012345670', flags, {len = 9, num = 2739128, unum = 2739128, pre = oct}, 0) + + test_vim_str2nr( '054', flags, {len = 3, num = 44, unum = 44, pre = oct}, 0) + test_vim_str2nr( '054', flags, {len = 1, num = 0, unum = 0, pre = 0 }, 1) + test_vim_str2nr( '054', flags, {len = 2, num = 5, unum = 5, pre = oct}, 2) + test_vim_str2nr( '054', flags, {len = 3, num = 44, unum = 44, pre = oct}, 3) + test_vim_str2nr( '0548', flags, {len = 3, num = 44, unum = 44, pre = oct}, 3) + test_vim_str2nr( '054', flags, {len = 3, num = 44, unum = 44, pre = oct}, 4) + + test_vim_str2nr( '054x', flags, {len = 3, num = 44, unum = 44, pre = oct}, 4) + test_vim_str2nr( '054x', flags, {len = 3, num = 44, unum = 44, pre = oct}, 0) + + test_vim_str2nr('-054', flags, {len = 4, num = -44, unum = 44, pre = oct}, 0) + test_vim_str2nr('-054', flags, {len = 1, num = 0, unum = 0, pre = 0 }, 1) + test_vim_str2nr('-054', flags, {len = 2, num = 0, unum = 0, pre = 0 }, 2) + test_vim_str2nr('-054', flags, {len = 3, num = -5, unum = 5, pre = oct}, 3) + test_vim_str2nr('-054', flags, {len = 4, num = -44, unum = 44, pre = oct}, 4) + test_vim_str2nr('-0548', flags, {len = 4, num = -44, unum = 44, pre = oct}, 4) + test_vim_str2nr('-054', flags, {len = 4, num = -44, unum = 44, pre = oct}, 5) + + test_vim_str2nr('-054x', flags, {len = 4, num = -44, unum = 44, pre = oct}, 5) + test_vim_str2nr('-054x', flags, {len = 4, num = -44, unum = 44, pre = oct}, 0) + + if flags > lib.STR2NR_FORCE then + test_vim_str2nr('-54', flags, {len = 3, num = -44, unum = 44, pre = 0}, 0) + test_vim_str2nr('-0548', flags, {len = 4, num = -44, unum = 44, pre = 0}, 5) + test_vim_str2nr('-0548', flags, {len = 4, num = -44, unum = 44, pre = 0}, 0) + else + test_vim_str2nr('-0548', flags, {len = 5, num = -548, unum = 548, pre = 0}, 5) + test_vim_str2nr('-0548', flags, {len = 5, num = -548, unum = 548, pre = 0}, 0) + end + end + end) + itp('works with hexadecimal numbers', function() + for _, flags in ipairs({ + lib.STR2NR_HEX, + lib.STR2NR_HEX + lib.STR2NR_BIN, + lib.STR2NR_HEX + lib.STR2NR_OCT, + lib.STR2NR_ALL, + lib.STR2NR_FORCE + lib.STR2NR_HEX, + }) do + local hex + local HEX + if flags > lib.STR2NR_FORCE then + hex = 0 + HEX = 0 + else + hex = ('x'):byte() + HEX = ('X'):byte() + end + + -- Check that all digits are recognized + test_vim_str2nr('0x12345', flags, {len = 7, num = 74565, unum = 74565, pre = hex}, 0) + test_vim_str2nr('0x67890', flags, {len = 7, num = 424080, unum = 424080, pre = hex}, 0) + test_vim_str2nr('0xABCDEF', flags, {len = 8, num = 11259375, unum = 11259375, pre = hex}, 0) + test_vim_str2nr('0xabcdef', flags, {len = 8, num = 11259375, unum = 11259375, pre = hex}, 0) + + test_vim_str2nr( '0x101', flags, {len = 5, num = 257, unum =257, pre = hex}, 0) + test_vim_str2nr( '0x101', flags, {len = 1, num = 0, unum = 0, pre = 0 }, 1) + test_vim_str2nr( '0x101', flags, {len = 1, num = 0, unum = 0, pre = 0 }, 2) + test_vim_str2nr( '0x101', flags, {len = 3, num = 1, unum = 1, pre = hex}, 3) + test_vim_str2nr( '0x101', flags, {len = 4, num = 16, unum = 16, pre = hex}, 4) + test_vim_str2nr( '0x101', flags, {len = 5, num = 257, unum =257, pre = hex}, 5) + test_vim_str2nr( '0x101', flags, {len = 5, num = 257, unum =257, pre = hex}, 6) + + test_vim_str2nr( '0x101G', flags, {len = 5, num = 257, unum =257, pre = hex}, 0) + test_vim_str2nr( '0x101G', flags, {len = 5, num = 257, unum =257, pre = hex}, 6) + + test_vim_str2nr('-0x101', flags, {len = 6, num =-257, unum =257, pre = hex}, 0) + test_vim_str2nr('-0x101', flags, {len = 1, num = 0, unum = 0, pre = 0 }, 1) + test_vim_str2nr('-0x101', flags, {len = 2, num = 0, unum = 0, pre = 0 }, 2) + test_vim_str2nr('-0x101', flags, {len = 2, num = 0, unum = 0, pre = 0 }, 3) + test_vim_str2nr('-0x101', flags, {len = 4, num = -1, unum = 1, pre = hex}, 4) + test_vim_str2nr('-0x101', flags, {len = 5, num = -16, unum = 16, pre = hex}, 5) + test_vim_str2nr('-0x101', flags, {len = 6, num =-257, unum =257, pre = hex}, 6) + test_vim_str2nr('-0x101', flags, {len = 6, num =-257, unum =257, pre = hex}, 7) + + test_vim_str2nr('-0x101G', flags, {len = 6, num =-257, unum =257, pre = hex}, 0) + test_vim_str2nr('-0x101G', flags, {len = 6, num =-257, unum =257, pre = hex}, 7) + + test_vim_str2nr( '0X101', flags, {len = 5, num = 257, unum =257, pre = HEX}, 0) + test_vim_str2nr( '0X101', flags, {len = 1, num = 0, unum = 0, pre = 0 }, 1) + test_vim_str2nr( '0X101', flags, {len = 1, num = 0, unum = 0, pre = 0 }, 2) + test_vim_str2nr( '0X101', flags, {len = 3, num = 1, unum = 1, pre = HEX}, 3) + test_vim_str2nr( '0X101', flags, {len = 4, num = 16, unum = 16, pre = HEX}, 4) + test_vim_str2nr( '0X101', flags, {len = 5, num = 257, unum =257, pre = HEX}, 5) + test_vim_str2nr( '0X101', flags, {len = 5, num = 257, unum =257, pre = HEX}, 6) + + test_vim_str2nr( '0X101G', flags, {len = 5, num = 257, unum =257, pre = HEX}, 0) + test_vim_str2nr( '0X101G', flags, {len = 5, num = 257, unum =257, pre = HEX}, 6) + + test_vim_str2nr('-0X101', flags, {len = 6, num =-257, unum =257, pre = HEX}, 0) + test_vim_str2nr('-0X101', flags, {len = 1, num = 0, unum = 0, pre = 0 }, 1) + test_vim_str2nr('-0X101', flags, {len = 2, num = 0, unum = 0, pre = 0 }, 2) + test_vim_str2nr('-0X101', flags, {len = 2, num = 0, unum = 0, pre = 0 }, 3) + test_vim_str2nr('-0X101', flags, {len = 4, num = -1, unum = 1, pre = HEX}, 4) + test_vim_str2nr('-0X101', flags, {len = 5, num = -16, unum = 16, pre = HEX}, 5) + test_vim_str2nr('-0X101', flags, {len = 6, num =-257, unum =257, pre = HEX}, 6) + test_vim_str2nr('-0X101', flags, {len = 6, num =-257, unum =257, pre = HEX}, 7) + + test_vim_str2nr('-0X101G', flags, {len = 6, num =-257, unum =257, pre = HEX}, 0) + test_vim_str2nr('-0X101G', flags, {len = 6, num =-257, unum =257, pre = HEX}, 7) + + if flags > lib.STR2NR_FORCE then + test_vim_str2nr('-101', flags, {len = 4, num = -257, unum = 257, pre = 0}, 0) + end + end + end) +end) -- cgit From 4aebd00a9eeeb2f56ff53dd4e383825e997ee7be Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 6 Nov 2017 20:28:37 +0300 Subject: *: Fix linter errors --- test/helpers.lua | 6 +++--- test/unit/charset/vim_str2nr_spec.lua | 1 - test/unit/viml/helpers.lua | 1 - 3 files changed, 3 insertions(+), 5 deletions(-) (limited to 'test') diff --git a/test/helpers.lua b/test/helpers.lua index 16b9818f12..d24fae745b 100644 --- a/test/helpers.lua +++ b/test/helpers.lua @@ -301,10 +301,10 @@ local function mergedicts_copy(d1, d2) for k, v in pairs(d2) do if d2[k] == REMOVE_THIS then ret[k] = nil - elseif type(d1[k]) == 'table' and type(d2[k]) == 'table' then - ret[k] = mergedicts_copy(d1[k], d2[k]) + elseif type(d1[k]) == 'table' and type(v) == 'table' then + ret[k] = mergedicts_copy(d1[k], v) else - ret[k] = d2[k] + ret[k] = v end end return ret diff --git a/test/unit/charset/vim_str2nr_spec.lua b/test/unit/charset/vim_str2nr_spec.lua index 9309dc380c..22504649f6 100644 --- a/test/unit/charset/vim_str2nr_spec.lua +++ b/test/unit/charset/vim_str2nr_spec.lua @@ -5,7 +5,6 @@ local itp = helpers.gen_itp(it) local cimport = helpers.cimport local ffi = helpers.ffi -local eq = helpers.eq local shallowcopy = global_helpers.shallowcopy local updated = global_helpers.updated diff --git a/test/unit/viml/helpers.lua b/test/unit/viml/helpers.lua index aeb886a66f..9d2d7b61c7 100644 --- a/test/unit/viml/helpers.lua +++ b/test/unit/viml/helpers.lua @@ -112,7 +112,6 @@ return { pline2lua = pline2lua, pstate_str = pstate_str, new_pstate = new_pstate, - intchar2lua = intchar2lua, conv_cmp_type = conv_cmp_type, pstate_set_str = pstate_set_str, } -- cgit From bbb21e5dd3891727c272ffd3aa4ce2a4841a1f0b Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 11 Nov 2017 23:50:37 +0300 Subject: unittests: Add a way to show some custom messages only when crashed --- test/unit/helpers.lua | 17 ++++++++++++++++- test/unit/viml/expressions/parser_spec.lua | 2 ++ 2 files changed, 18 insertions(+), 1 deletion(-) (limited to 'test') diff --git a/test/unit/helpers.lua b/test/unit/helpers.lua index 96aa505739..2c148630dd 100644 --- a/test/unit/helpers.lua +++ b/test/unit/helpers.lua @@ -529,9 +529,13 @@ local hook_numlen = 5 local hook_msglen = 1 + 1 + 1 + (1 + hook_fnamelen) + (1 + hook_sfnamelen) + (1 + hook_numlen) + 1 local tracehelp = dedent([[ + Trace: either in the format described below or custom debug output starting + with `>`. Latter lines still have the same width in byte. + ┌ Trace type: _r_eturn from function , function _c_all, _l_ine executed, │ _t_ail return, _C_ount (should not actually appear), - │ _s_aved from previous run for reference. + │ _s_aved from previous run for reference, _>_ for custom debug + │ output. │┏ Function type: _L_ua function, _C_ function, _m_ain part of chunk, │┃ function that did _t_ail call. │┃┌ Function name type: _g_lobal, _l_ocal, _m_ethod, _f_ield, _u_pvalue, @@ -629,7 +633,17 @@ end local trace_end_msg = ('E%s\n'):format((' '):rep(hook_msglen - 2)) +local _debug_log + +local debug_log = only_separate(function(...) + return _debug_log(...) +end) + local function itp_child(wr, func) + _debug_log = function(s) + s = s:sub(1, hook_msglen - 2) + sc.write(wr, '>' .. s .. (' '):rep(hook_msglen - 2 - #s) .. '\n') + end init() collectgarbage('stop') child_sethook(wr) @@ -845,6 +859,7 @@ local module = { make_enum_conv_tab = make_enum_conv_tab, ptr2addr = ptr2addr, ptr2key = ptr2key, + debug_log = debug_log, } return function() return module diff --git a/test/unit/viml/expressions/parser_spec.lua b/test/unit/viml/expressions/parser_spec.lua index e5d0f2b84c..cfc9fe95ac 100644 --- a/test/unit/viml/expressions/parser_spec.lua +++ b/test/unit/viml/expressions/parser_spec.lua @@ -8,6 +8,7 @@ local child_call_once = helpers.child_call_once local alloc_log_new = helpers.alloc_log_new local kvi_destroy = helpers.kvi_destroy local conv_enum = helpers.conv_enum +local debug_log = helpers.debug_log local ptr2key = helpers.ptr2key local cimport = helpers.cimport local ffi = helpers.ffi @@ -233,6 +234,7 @@ describe('Expressions parser', function() local function check_parsing(str, exp_ast, exp_highlighting_fs, nz_flags_exps) nz_flags_exps = nz_flags_exps or {} for _, flags in ipairs({0, 1, 2, 3}) do + debug_log(('Running test case (%s, %u)'):format(str, flags)) local err, msg = pcall(function() if os.getenv('NVIM_TEST_PARSER_SPEC_PRINT_TEST_CASE') == '1' then print(str, flags) -- cgit From c7495ebcc0918ffd682083408895451318e41d1f Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 12 Nov 2017 02:18:43 +0300 Subject: viml/parser/expressions: Add support for parsing assignments --- test/symbolic/klee/viml_expressions_parser.c | 3 ++- test/unit/viml/expressions/lexer_spec.lua | 10 +++++++++- test/unit/viml/expressions/parser_spec.lua | 23 +++++++++++------------ test/unit/viml/helpers.lua | 13 +++++++++++++ 4 files changed, 35 insertions(+), 14 deletions(-) (limited to 'test') diff --git a/test/symbolic/klee/viml_expressions_parser.c b/test/symbolic/klee/viml_expressions_parser.c index 9448937430..fd75e8d355 100644 --- a/test/symbolic/klee/viml_expressions_parser.c +++ b/test/symbolic/klee/viml_expressions_parser.c @@ -48,7 +48,8 @@ int main(const int argc, const char *const *const argv, klee_make_symbolic(&shift, sizeof(shift), "shift"); klee_make_symbolic(&flags, sizeof(flags), "flags"); klee_assume(shift < INPUT_SIZE); - klee_assume(flags <= (kExprFlagsMulti|kExprFlagsDisallowEOC)); + klee_assume( + flags <= (kExprFlagsMulti|kExprFlagsDisallowEOC|kExprFlagsParseLet)); #endif ParserLine plines[] = { diff --git a/test/unit/viml/expressions/lexer_spec.lua b/test/unit/viml/expressions/lexer_spec.lua index 75a641c48a..1b57a24ad5 100644 --- a/test/unit/viml/expressions/lexer_spec.lua +++ b/test/unit/viml/expressions/lexer_spec.lua @@ -13,6 +13,7 @@ local conv_ccs = viml_helpers.conv_ccs local new_pstate = viml_helpers.new_pstate local conv_cmp_type = viml_helpers.conv_cmp_type local pstate_set_str = viml_helpers.pstate_set_str +local conv_expr_asgn_type = viml_helpers.conv_expr_asgn_type local shallowcopy = global_helpers.shallowcopy local intchar2lua = global_helpers.intchar2lua @@ -52,6 +53,8 @@ child_call_once(function() [tonumber(lib.kExprLexParenthesis)] = 'Parenthesis', [tonumber(lib.kExprLexComma)] = 'Comma', [tonumber(lib.kExprLexArrow)] = 'Arrow', + + [tonumber(lib.kExprLexAssignment)] = 'Assignment', } eltkn_mul_type_tab = { @@ -118,6 +121,8 @@ local function eltkn2lua(pstate, tkn) ret.data.val = tonumber(tkn.data.num.is_float and tkn.data.num.val.floating or tkn.data.num.val.integer) + elseif ret.type == 'Assignment' then + ret.data = { type = conv_expr_asgn_type(tkn.data.ass.type) } elseif ret.type == 'Invalid' then ret.data = { error = ffi.string(tkn.data.err.msg) } end @@ -198,7 +203,9 @@ describe('Expressions lexer', function() singl_eltkn_test('Question', '?') singl_eltkn_test('Colon', ':') singl_eltkn_test('Dot', '.') + singl_eltkn_test('Assignment', '.=', {type='Concat'}) singl_eltkn_test('Plus', '+') + singl_eltkn_test('Assignment', '+=', {type='Add'}) singl_eltkn_test('Comma', ',') singl_eltkn_test('Multiplication', '*', {type='Mul'}) singl_eltkn_test('Multiplication', '/', {type='Div'}) @@ -266,12 +273,13 @@ describe('Expressions lexer', function() singl_eltkn_test('DoubleQuotedString', '"x\\"', {closed=false}) singl_eltkn_test('DoubleQuotedString', '"\\"x', {closed=false}) singl_eltkn_test('Not', '!') - singl_eltkn_test('Invalid', '=', {error='E15: Expected == or =~: %.*s'}) + singl_eltkn_test('Assignment', '=', {type='Plain'}) comparison_test('==', '!=', 'Equal') comparison_test('=~', '!~', 'Matches') comparison_test('>', '<=', 'Greater') comparison_test('>=', '<', 'GreaterOrEqual') singl_eltkn_test('Minus', '-') + singl_eltkn_test('Assignment', '-=', {type='Subtract'}) singl_eltkn_test('Arrow', '->') singl_eltkn_test('Invalid', '~', {error='E15: Unidentified character: %.*s'}) simple_test({{data=nil, size=0}}, 'EOC', 0, {error='start.col >= #pstr'}) diff --git a/test/unit/viml/expressions/parser_spec.lua b/test/unit/viml/expressions/parser_spec.lua index cfc9fe95ac..810d7bfbc6 100644 --- a/test/unit/viml/expressions/parser_spec.lua +++ b/test/unit/viml/expressions/parser_spec.lua @@ -18,6 +18,7 @@ local conv_ccs = viml_helpers.conv_ccs local new_pstate = viml_helpers.new_pstate local conv_cmp_type = viml_helpers.conv_cmp_type local pstate_set_str = viml_helpers.pstate_set_str +local conv_expr_asgn_type = viml_helpers.conv_expr_asgn_type local mergedicts_copy = global_helpers.mergedicts_copy local format_string = global_helpers.format_string @@ -109,6 +110,7 @@ make_enum_conv_tab(lib, { 'kExprNodeMod', 'kExprNodeOption', 'kExprNodeEnvironment', + 'kExprNodeAssignment', }, 'kExprNode', function(ret) east_node_type_tab = ret end) local function conv_east_node_type(typ) @@ -174,6 +176,8 @@ local function eastnode2lua(pstate, eastnode, checked_nodes) typ = ('%s(ident=%s)'):format( typ, ffi.string(eastnode.data.env.ident, eastnode.data.env.ident_len)) + elseif typ == 'Assignment' then + typ = ('%s(%s)'):format(typ, conv_expr_asgn_type(eastnode.data.ass.type)) end ret_str = typ .. ':' .. ret_str local can_simplify = not ret.children @@ -3976,27 +3980,20 @@ describe('Expressions parser', function() -- 012345 ast = { { - 'Comparison(type=Equal,inv=0,ccs=UseOption):0:3:=', + 'Assignment(Add):0:1: +=', children = { - { - 'BinaryPlus:0:1: +', - children = { - 'PlainIdentifier(scope=0,ident=a):0:0:a', - 'Missing:0:3:', - }, - }, + 'PlainIdentifier(scope=0,ident=a):0:0:a', 'PlainIdentifier(scope=0,ident=b):0:4: b', }, }, }, err = { - arg = '= b', - msg = 'E15: Expected == or =~: %.*s', + arg = '+= b', + msg = 'E15: Misplaced assignment: %.*s', }, }, { hl('IdentifierName', 'a'), - hl('BinaryPlus', '+', 1), - hl('InvalidComparison', '='), + hl('InvalidAssignmentWithAddition', '+=', 1), hl('IdentifierName', 'b', 1), }) check_parsing('a + b == c + d', { @@ -7347,4 +7344,6 @@ describe('Expressions parser', function() }, }) end) + -- FIXME: Test assignments thoroughly + -- FIXME: Test that parsing assignments can be used for `:for` pre-`in` part. end) diff --git a/test/unit/viml/helpers.lua b/test/unit/viml/helpers.lua index 9d2d7b61c7..9d8102e023 100644 --- a/test/unit/viml/helpers.lua +++ b/test/unit/viml/helpers.lua @@ -107,6 +107,18 @@ local function conv_ccs(ccs) return conv_enum(ccs_tab, ccs) end +local expr_asgn_type_tab +make_enum_conv_tab(lib, { + 'kExprAsgnPlain', + 'kExprAsgnAdd', + 'kExprAsgnSubtract', + 'kExprAsgnConcat', +}, 'kExprAsgn', function(ret) expr_asgn_type_tab = ret end) + +local function conv_expr_asgn_type(expr_asgn_type) + return conv_enum(expr_asgn_type_tab, expr_asgn_type) +end + return { conv_ccs = conv_ccs, pline2lua = pline2lua, @@ -114,4 +126,5 @@ return { new_pstate = new_pstate, conv_cmp_type = conv_cmp_type, pstate_set_str = pstate_set_str, + conv_expr_asgn_type = conv_expr_asgn_type, } -- cgit From 45445e2e03f1cbfa25dde76ccf3e24d0d297cabe Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 12 Nov 2017 03:52:26 +0300 Subject: unittests: Add some more edge test cases --- test/unit/viml/expressions/parser_spec.lua | 143 +++++++++++++++++++++++++++++ 1 file changed, 143 insertions(+) (limited to 'test') diff --git a/test/unit/viml/expressions/parser_spec.lua b/test/unit/viml/expressions/parser_spec.lua index 810d7bfbc6..df45cae304 100644 --- a/test/unit/viml/expressions/parser_spec.lua +++ b/test/unit/viml/expressions/parser_spec.lua @@ -1233,6 +1233,132 @@ describe('Expressions parser', function() hl('CallingParenthesis', ')'), hl('CallingParenthesis', ')'), }) + check_parsing('()()', { + -- 0123 + ast = { + { + 'Call:0:2:(', + children = { + { + 'Nested:0:0:(', + children = { + 'Missing:0:1:', + }, + }, + }, + }, + }, + err = { + arg = ')()', + msg = 'E15: Expected value, got parenthesis: %.*s', + }, + }, { + hl('NestingParenthesis', '('), + hl('InvalidNestingParenthesis', ')'), + hl('CallingParenthesis', '('), + hl('CallingParenthesis', ')'), + }) + check_parsing('(@a)()', { + -- 012345 + ast = { + { + 'Call:0:4:(', + children = { + { + 'Nested:0:0:(', + children = { + 'Register(name=a):0:1:@a', + }, + }, + }, + }, + }, + }, { + hl('NestingParenthesis', '('), + hl('Register', '@a'), + hl('NestingParenthesis', ')'), + hl('CallingParenthesis', '('), + hl('CallingParenthesis', ')'), + }) + check_parsing('(@a)(@b)', { + -- 01234567 + ast = { + { + 'Call:0:4:(', + children = { + { + 'Nested:0:0:(', + children = { + 'Register(name=a):0:1:@a', + }, + }, + 'Register(name=b):0:5:@b', + }, + }, + }, + }, { + hl('NestingParenthesis', '('), + hl('Register', '@a'), + hl('NestingParenthesis', ')'), + hl('CallingParenthesis', '('), + hl('Register', '@b'), + hl('CallingParenthesis', ')'), + }) + check_parsing('(@a) (@b)', { + -- 012345678 + ast = { + { + 'OpMissing:0:4:', + children = { + { + 'Nested:0:0:(', + children = { + 'Register(name=a):0:1:@a', + }, + }, + { + 'Nested:0:4: (', + children = { + 'Register(name=b):0:6:@b', + }, + }, + }, + }, + }, + err = { + arg = '(@b)', + msg = 'E15: Missing operator: %.*s', + }, + }, { + hl('NestingParenthesis', '('), + hl('Register', '@a'), + hl('NestingParenthesis', ')'), + hl('InvalidSpacing', ' '), + hl('NestingParenthesis', '('), + hl('Register', '@b'), + hl('NestingParenthesis', ')'), + }, { + [1] = { + ast = { + ast = { + { + 'Nested:0:0:(', + children = { + 'Register(name=a):0:1:@a', + REMOVE_THIS, + }, + }, + }, + err = REMOVE_THIS, + }, + hl_fs = { + [4] = REMOVE_THIS, + [5] = REMOVE_THIS, + [6] = REMOVE_THIS, + [7] = REMOVE_THIS, + }, + }, + }) end) itp('works with variable names, including curly braces ones', function() check_parsing('var', { @@ -1543,6 +1669,21 @@ describe('Expressions parser', function() hl('FigureBrace', '{'), hl('Register', '@a'), }) + check_parsing('a ()', { + -- 0123 + ast = { + { + 'Call:0:1: (', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + }, + }, + }, + }, { + hl('IdentifierName', 'a'), + hl('CallingParenthesis', '(', 1), + hl('CallingParenthesis', ')'), + }) end) itp('works with lambdas and dictionaries', function() check_parsing('{}', { @@ -7346,4 +7487,6 @@ describe('Expressions parser', function() end) -- FIXME: Test assignments thoroughly -- FIXME: Test that parsing assignments can be used for `:for` pre-`in` part. + -- FIXME: Somehow make functional tests use the same code. Or, at least, + -- create an automated script which will do the import. end) -- cgit From 556451a7f2fd513db33b9d7ac1b653d356b7b915 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 12 Nov 2017 16:59:36 +0300 Subject: unittests,syntax: Check for sanity of highlight_init_cmdline Also fixes some errors found. --- test/functional/ui/cmdline_highlight_spec.lua | 1 - test/unit/viml/expressions/parser_spec.lua | 159 +++++++++++++++++++++++++- 2 files changed, 157 insertions(+), 3 deletions(-) (limited to 'test') diff --git a/test/functional/ui/cmdline_highlight_spec.lua b/test/functional/ui/cmdline_highlight_spec.lua index b16b4a5602..ab195f9f1e 100644 --- a/test/functional/ui/cmdline_highlight_spec.lua +++ b/test/functional/ui/cmdline_highlight_spec.lua @@ -899,7 +899,6 @@ describe('Expressions coloring support', function() ]]) end) -- FIXME: Test expr coloring when using -u NORC and -u NONE. - -- FIXME: Test all highlight groups, using long expression. -- FIXME: Test different ways of triggering expression highlighting (:=, -- i=, :e, "=). end) diff --git a/test/unit/viml/expressions/parser_spec.lua b/test/unit/viml/expressions/parser_spec.lua index df45cae304..cfcd63f005 100644 --- a/test/unit/viml/expressions/parser_spec.lua +++ b/test/unit/viml/expressions/parser_spec.lua @@ -7,11 +7,13 @@ local make_enum_conv_tab = helpers.make_enum_conv_tab local child_call_once = helpers.child_call_once local alloc_log_new = helpers.alloc_log_new local kvi_destroy = helpers.kvi_destroy +local array_size = helpers.array_size local conv_enum = helpers.conv_enum local debug_log = helpers.debug_log local ptr2key = helpers.ptr2key local cimport = helpers.cimport local ffi = helpers.ffi +local neq = helpers.neq local eq = helpers.eq local conv_ccs = viml_helpers.conv_ccs @@ -26,10 +28,158 @@ local format_luav = global_helpers.format_luav local intchar2lua = global_helpers.intchar2lua local REMOVE_THIS = global_helpers.REMOVE_THIS -local lib = cimport('./src/nvim/viml/parser/expressions.h') +local lib = cimport('./src/nvim/viml/parser/expressions.h', + './src/nvim/syntax.h') local alloc_log = alloc_log_new() +local predefined_hl_defs = { + -- From highlight_init_both + Conceal=true, + Cursor=true, + lCursor=true, + DiffText=true, + ErrorMsg=true, + IncSearch=true, + ModeMsg=true, + NonText=true, + PmenuSbar=true, + StatusLine=true, + StatusLineNC=true, + TabLineFill=true, + TabLineSel=true, + TermCursor=true, + VertSplit=true, + WildMenu=true, + EndOfBuffer=true, + QuickFixLine=true, + Substitute=true, + Whitespace=true, + + -- From highlight_init_(dark|light) + ColorColumn=true, + CursorColumn=true, + CursorLine=true, + CursorLineNr=true, + DiffAdd=true, + DiffChange=true, + DiffDelete=true, + Directory=true, + FoldColumn=true, + Folded=true, + LineNr=true, + MatchParen=true, + MoreMsg=true, + Pmenu=true, + PmenuSel=true, + PmenuThumb=true, + Question=true, + Search=true, + SignColumn=true, + SpecialKey=true, + SpellBad=true, + SpellCap=true, + SpellLocal=true, + SpellRare=true, + TabLine=true, + Title=true, + Visual=true, + WarningMsg=true, + Normal=true, + + -- From syncolor.vim, if &background + Comment=true, + Constant=true, + Special=true, + Identifier=true, + Statement=true, + PreProc=true, + Type=true, + Underlined=true, + Ignore=true, + + -- From syncolor.vim, below if &background + Error=true, + Todo=true, + + -- From syncolor.vim, links at the bottom + String=true, + Character=true, + Number=true, + Boolean=true, + Float=true, + Function=true, + Conditional=true, + Repeat=true, + Label=true, + Operator=true, + Keyword=true, + Exception=true, + Include=true, + Define=true, + Macro=true, + PreCondit=true, + StorageClass=true, + Structure=true, + Typedef=true, + Tag=true, + SpecialChar=true, + Delimiter=true, + SpecialComment=true, + Debug=true, +} + +local nvim_hl_defs = {} + +child_call_once(function() + local i = 0 + while lib.highlight_init_cmdline[i] ~= nil do + local hl_args = lib.highlight_init_cmdline[i] + local s = ffi.string(hl_args) + local err, msg = pcall(function() + if s:sub(1, 13) == 'default link ' then + local new_grp, grp_link = s:match('^default link (%w+) (%w+)$') + neq(nil, new_grp) + -- Note: group to link to must be already defined at the time of + -- linking, otherwise it will be created as cleared. So existence + -- of the group is checked here and not in the next pass over + -- nvim_hl_defs. + eq(true, not not (nvim_hl_defs[grp_link] + or predefined_hl_defs[grp_link])) + nvim_hl_defs[new_grp] = {'link', grp_link} + else + local new_grp, grp_args = s:match('^(%w+) (.*)') + neq(nil, new_grp) + eq(false, not not (nvim_hl_defs[grp_link] + or predefined_hl_defs[grp_link])) + nvim_hl_defs[new_grp] = {'definition', grp_args} + end + end) + if not err then + msg = format_string( + 'Error while processing string %s at position %u:\n%s', s, i, msg) + error(msg) + end + i = i + 1 + end + for k, _ in ipairs(nvim_hl_defs) do + eq('NVim', k:sub(1, 4)) + -- NVimInvalid + -- 12345678901 + local err, msg = pcall(function() + if k:sub(5, 11) == 'Invalid' then + neq(nil, nvim_hl_defs['NVim' .. k:sub(12)]) + else + neq(nil, nvim_hl_defs['NVimInvalid' .. k:sub(5)]) + end + end) + if not err then + msg = format_string('Error while processing group %s:\n%s', k, msg) + error(msg) + end + end +end) + local function format_check(expr, flags, ast, hls) -- That forces specific order. print( format_string('\ncheck_parsing(%r, %u, {', expr, flags)) @@ -237,6 +387,8 @@ end) describe('Expressions parser', function() local function check_parsing(str, exp_ast, exp_highlighting_fs, nz_flags_exps) nz_flags_exps = nz_flags_exps or {} + local format_check_data = function() + end for _, flags in ipairs({0, 1, 2, 3}) do debug_log(('Running test case (%s, %u)'):format(str, flags)) local err, msg = pcall(function() @@ -266,7 +418,7 @@ describe('Expressions parser', function() end end if exp_ast == nil then - format_check(str, flags, ast, hls) + format_check(str, ast, hls) else eq(exps.ast, ast) if exp_highlighting_fs then @@ -292,6 +444,9 @@ describe('Expressions parser', function() end local function hl(group, str, shift) return function(next_col) + if nvim_hl_defs['NVim' .. group] == nil then + error(('Unknown group: NVim%s'):format(group)) + end local col = next_col + (shift or 0) return (('%s:%u:%u:%s'):format( 'NVim' .. group, -- cgit From 39c75d31be98e4cbb63a01c398d89e231f71f380 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 12 Nov 2017 18:52:49 +0300 Subject: unittests: Fix automatic test case generation --- test/helpers.lua | 93 +++++++++++++++++++++------- test/unit/viml/expressions/parser_spec.lua | 98 +++++++++++++++++++++++------- 2 files changed, 148 insertions(+), 43 deletions(-) (limited to 'test') diff --git a/test/helpers.lua b/test/helpers.lua index d24fae745b..6c6611d061 100644 --- a/test/helpers.lua +++ b/test/helpers.lua @@ -310,6 +310,43 @@ local function mergedicts_copy(d1, d2) return ret end +-- dictdiff: find a diff so that mergedicts_copy(d1, diff) is equal to d2 +-- +-- Note: does not copy values from d2 +local function dictdiff(d1, d2) + local ret = {} + local hasdiff = false + for k, v in pairs(d1) do + if d2[k] == nil then + hasdiff = true + ret[k] = REMOVE_THIS + elseif type(v) == type(d2[k]) then + if type(v) == 'table' and type(d2[k]) == 'table' then + local subdiff = dictdiff(v, d2[k]) + if subdiff ~= nil then + hasdiff = true + ret[k] = subdiff + end + elseif v ~= d2[k] then + ret[k] = d2[k] + end + else + ret[k] = d2[k] + end + end + for k, v in pairs(d2) do + if d1[k] == nil then + ret[k] = v + hasdiff = true + end + end + if hasdiff then + return ret + else + return nil + end +end + local function updated(d, d2) for k, v in pairs(d2) do d[k] = v @@ -365,39 +402,52 @@ local SUBTBL = { local format_luav -format_luav = function(v, indent) +format_luav = function(v, indent, opts) + opts = opts or {} local linesep = '\n' - local next_indent = nil + local next_indent_arg = nil if indent == nil then indent = '' linesep = '' else - next_indent = indent .. ' ' + next_indent_arg = indent .. ' ' end + local next_indent = indent .. ' ' local ret = '' if type(v) == 'string' then - ret = tostring(v):gsub('[\'\\]', '\\%0'):gsub('[%z\1-\31]', function(match) - return SUBTBL[match:byte() + 1] - end) - ret = '\'' .. ret .. '\'' - elseif type(v) == 'table' then - local processed_keys = {} - ret = '{' .. linesep - for i, subv in ipairs(v) do - ret = ret .. (next_indent or '') .. format_luav(subv, next_indent) .. ',\n' - processed_keys[i] = true + if opts.literal_strings then + ret = v + else + ret = tostring(v):gsub('[\'\\]', '\\%0'):gsub( + '[%z\1-\31]', function(match) + return SUBTBL[match:byte() + 1] + end) + ret = '\'' .. ret .. '\'' end - for k, subv in pairs(v) do - if not processed_keys[k] then - if type(k) == 'string' and k:match('^[a-zA-Z_][a-zA-Z0-9_]*$') then - ret = ret .. next_indent .. k .. ' = ' - else - ret = ret .. next_indent .. '[' .. format_luav(k) .. '] = ' + elseif type(v) == 'table' then + if v == REMOVE_THIS then + ret = 'REMOVE_THIS' + else + local processed_keys = {} + ret = '{' .. linesep + for i, subv in ipairs(v) do + ret = ('%s%s%s,\n'):format(ret, next_indent, + format_luav(subv, next_indent_arg, opts)) + processed_keys[i] = true + end + for k, subv in pairs(v) do + if not processed_keys[k] then + if type(k) == 'string' and k:match('^[a-zA-Z_][a-zA-Z0-9_]*$') then + ret = ret .. next_indent .. k .. ' = ' + else + ret = ('%s%s[%s] = '):format(ret, next_indent, + format_luav(k, nil, opts)) + end + ret = ret .. format_luav(subv, next_indent_arg, opts) .. ',\n' end - ret = ret .. format_luav(subv, next_indent) .. ',\n' end + ret = ret .. indent .. '}' end - ret = ret .. indent .. '}' elseif type(v) == 'number' then if v % 1 == 0 then ret = ('%d'):format(v) @@ -461,6 +511,7 @@ return { shallowcopy = shallowcopy, deepcopy = deepcopy, mergedicts_copy = mergedicts_copy, + dictdiff = dictdiff, REMOVE_THIS = REMOVE_THIS, concat_tables = concat_tables, dedent = dedent, diff --git a/test/unit/viml/expressions/parser_spec.lua b/test/unit/viml/expressions/parser_spec.lua index cfcd63f005..25a41518eb 100644 --- a/test/unit/viml/expressions/parser_spec.lua +++ b/test/unit/viml/expressions/parser_spec.lua @@ -27,6 +27,7 @@ local format_string = global_helpers.format_string local format_luav = global_helpers.format_luav local intchar2lua = global_helpers.intchar2lua local REMOVE_THIS = global_helpers.REMOVE_THIS +local dictdiff = global_helpers.dictdiff local lib = cimport('./src/nvim/viml/parser/expressions.h', './src/nvim/syntax.h') @@ -180,9 +181,31 @@ child_call_once(function() end end) -local function format_check(expr, flags, ast, hls) +local function hls_to_hl_fs(hls) + local ret = {} + local next_col = 0 + for i, v in ipairs(hls) do + local group, line, col, str = v:match('^NVim([a-zA-Z]+):(%d+):(%d+):(.*)$') + col = tonumber(col) + line = tonumber(line) + assert(line == 0) + local col_shift = col - next_col + assert(col_shift >= 0) + next_col = col + #str + ret[i] = format_string('hl(%r, %r%s)', + group, + str, + (col_shift == 0 + and '' + or (', %u'):format(col_shift))) + end + return ret +end + +local function format_check(expr, format_check_data) -- That forces specific order. - print( format_string('\ncheck_parsing(%r, %u, {', expr, flags)) + local zdata = format_check_data[0] + print(format_string('\ncheck_parsing(%r, {', expr, flags)) local digits = ' -- ' local digits2 = ' -- ' for i = 0, #expr - 1 do @@ -195,27 +218,56 @@ local function format_check(expr, flags, ast, hls) if #expr > 10 then print(digits2) end - print(' ast = ' .. format_luav(ast.ast, ' ') .. ',') - if ast.err then + print(' ast = ' .. format_luav(zdata.ast.ast, ' ') .. ',') + if zdata.ast.err then print(' err = {') - print(' arg = ' .. format_luav(ast.err.arg) .. ',') - print(' msg = ' .. format_luav(ast.err.msg) .. ',') + print(' arg = ' .. format_luav(zdata.ast.err.arg) .. ',') + print(' msg = ' .. format_luav(zdata.ast.err.msg) .. ',') print(' },') end print('}, {') - local next_col = 0 - for _, v in ipairs(hls) do - local group, line, col, str = v:match('NVim([a-zA-Z]+):(%d+):(%d+):(.*)') - col = tonumber(col) - line = tonumber(line) - assert(line == 0) - local col_shift = col - next_col - assert(col_shift >= 0) - next_col = col + #str - print(format_string(' hl(%r, %r%s),', - group, - str, - (col_shift == 0 and '' or (', %u'):format(col_shift)))) + for _, v in ipairs(zdata.hl_fs) do + print(' ' .. v .. ',') + end + local diffs = {} + local diffs_num = 0 + for flags, v in pairs(format_check_data) do + if flags ~= 0 then + diffs[flags] = dictdiff(zdata, v) + if diffs[flags] then + if flags == 3 then + if (dictdiff(format_check_data[1], format_check_data[3]) == nil + or dictdiff(format_check_data[2], format_check_data[3]) == nil) then + diffs[flags] = nil + else + diffs_num = diffs_num + 1 + end + else + diffs_num = diffs_num + 1 + end + end + end + end + if diffs_num ~= 0 then + print('}, {') + local flags = 1 + while diffs_num ~= 0 do + if diffs[flags] then + diffs_num = diffs_num - 1 + local diff = diffs[flags] + print((' [%u] = {'):format(flags)) + if diff.ast then + print(' ast = ' .. format_luav(diff.ast, ' ')) + end + if diff.hl_fs then + print(' hl_fs = ' .. format_luav(diff.hl_fs, ' ', { + literal_strings=true + })) + end + print(' },') + end + flags = flags + 1 + end end print('})') end @@ -387,8 +439,7 @@ end) describe('Expressions parser', function() local function check_parsing(str, exp_ast, exp_highlighting_fs, nz_flags_exps) nz_flags_exps = nz_flags_exps or {} - local format_check_data = function() - end + local format_check_data = {} for _, flags in ipairs({0, 1, 2, 3}) do debug_log(('Running test case (%s, %u)'):format(str, flags)) local err, msg = pcall(function() @@ -418,7 +469,7 @@ describe('Expressions parser', function() end end if exp_ast == nil then - format_check(str, ast, hls) + format_check_data[flags] = {ast=ast, hl_fs=hls_to_hl_fs(hls)} else eq(exps.ast, ast) if exp_highlighting_fs then @@ -441,6 +492,9 @@ describe('Expressions parser', function() error(msg) end end + if exp_ast == nil then + format_check(str, format_check_data) + end end local function hl(group, str, shift) return function(next_col) -- cgit From 342239a9c53cf4857d18c0583d4cab1fdca534fa Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 13 Nov 2017 01:10:39 +0300 Subject: unittests,viml/parser/expressions: Start adding asgn parsing tests --- test/helpers.lua | 53 ++++- test/symbolic/klee/viml_expressions_parser.c | 14 ++ test/unit/viml/expressions/parser_spec.lua | 280 ++++++++++++++++++++++++--- 3 files changed, 311 insertions(+), 36 deletions(-) (limited to 'test') diff --git a/test/helpers.lua b/test/helpers.lua index 6c6611d061..ada690a4d2 100644 --- a/test/helpers.lua +++ b/test/helpers.lua @@ -264,6 +264,9 @@ local function which(exe) end local function shallowcopy(orig) + if type(orig) ~= 'table' then + return orig + end local copy = {} for orig_key, orig_value in pairs(orig) do copy[orig_key] = orig_value @@ -312,7 +315,7 @@ end -- dictdiff: find a diff so that mergedicts_copy(d1, diff) is equal to d2 -- --- Note: does not copy values from d2 +-- Note: does not do copies of d2 values used. local function dictdiff(d1, d2) local ret = {} local hasdiff = false @@ -321,7 +324,7 @@ local function dictdiff(d1, d2) hasdiff = true ret[k] = REMOVE_THIS elseif type(v) == type(d2[k]) then - if type(v) == 'table' and type(d2[k]) == 'table' then + if type(v) == 'table' then local subdiff = dictdiff(v, d2[k]) if subdiff ~= nil then hasdiff = true @@ -329,14 +332,16 @@ local function dictdiff(d1, d2) end elseif v ~= d2[k] then ret[k] = d2[k] + hasdiff = true end else ret[k] = d2[k] + hasdiff = true end end for k, v in pairs(d2) do if d1[k] == nil then - ret[k] = v + ret[k] = shallowcopy(v) hasdiff = true end end @@ -406,13 +411,18 @@ format_luav = function(v, indent, opts) opts = opts or {} local linesep = '\n' local next_indent_arg = nil + local indent_shift = opts.indent_shift or ' ' + local next_indent + local nl = '\n' if indent == nil then indent = '' linesep = '' + next_indent = '' + nl = ' ' else - next_indent_arg = indent .. ' ' + next_indent_arg = indent .. indent_shift + next_indent = indent .. indent_shift end - local next_indent = indent .. ' ' local ret = '' if type(v) == 'string' then if opts.literal_strings then @@ -430,10 +440,12 @@ format_luav = function(v, indent, opts) else local processed_keys = {} ret = '{' .. linesep + local non_empty = false for i, subv in ipairs(v) do - ret = ('%s%s%s,\n'):format(ret, next_indent, - format_luav(subv, next_indent_arg, opts)) + ret = ('%s%s%s,%s'):format(ret, next_indent, + format_luav(subv, next_indent_arg, opts), nl) processed_keys[i] = true + non_empty = true end for k, subv in pairs(v) do if not processed_keys[k] then @@ -443,9 +455,13 @@ format_luav = function(v, indent, opts) ret = ('%s%s[%s] = '):format(ret, next_indent, format_luav(k, nil, opts)) end - ret = ret .. format_luav(subv, next_indent_arg, opts) .. ',\n' + ret = ret .. format_luav(subv, next_indent_arg, opts) .. ',' .. nl + non_empty = true end end + if nl == ' ' and non_empty then + ret = ret:sub(1, -3) + end ret = ret .. indent .. '}' end elseif type(v) == 'number' then @@ -495,6 +511,25 @@ local function intchar2lua(ch) return (20 <= ch and ch < 127) and ('%c'):format(ch) or ch end +local fixtbl_metatable = { + __newindex = function() + assert(false) + end, +} + +local function fixtbl(tbl) + return setmetatable(tbl, fixtbl_metatable) +end + +local function fixtbl_rec(tbl) + for k, v in pairs(tbl) do + if type(v) == 'table' then + fixtbl_rec(v) + end + end + return fixtbl(tbl) +end + return { eq = eq, neq = neq, @@ -519,4 +554,6 @@ return { format_string = format_string, intchar2lua = intchar2lua, updated = updated, + fixtbl = fixtbl, + fixtbl_rec = fixtbl_rec, } diff --git a/test/symbolic/klee/viml_expressions_parser.c b/test/symbolic/klee/viml_expressions_parser.c index fd75e8d355..9a876ed3fa 100644 --- a/test/symbolic/klee/viml_expressions_parser.c +++ b/test/symbolic/klee/viml_expressions_parser.c @@ -93,6 +93,20 @@ int main(const int argc, const char *const *const argv, const ExprAST ast = viml_pexpr_parse(&pstate, (int)flags); assert(ast.root != NULL || ast.err.msg); + if (flags & kExprFlagsParseLet) { + assert(ast.err.msg != NULL + || ast.root->type == kExprNodeAssignment + || (ast.root->type == kExprNodeListLiteral + && ast.root->children != NULL) + || ast.root->type == kExprNodeComplexIdentifier + || ast.root->type == kExprNodeCurlyBracesIdentifier + || ast.root->type == kExprNodePlainIdentifier + || ast.root->type == kExprNodeRegister + || ast.root->type == kExprNodeEnvironment + || ast.root->type == kExprNodeOption + || ast.root->type == kExprNodeSubscript + || ast.root->type == kExprNodeConcatOrSubscript); + } // Can’t possibly have more highlight tokens then there are bytes in string. assert(kv_size(colors) <= INPUT_SIZE - shift); kvi_destroy(colors); diff --git a/test/unit/viml/expressions/parser_spec.lua b/test/unit/viml/expressions/parser_spec.lua index 25a41518eb..d6d8dd0807 100644 --- a/test/unit/viml/expressions/parser_spec.lua +++ b/test/unit/viml/expressions/parser_spec.lua @@ -202,12 +202,20 @@ local function hls_to_hl_fs(hls) return ret end -local function format_check(expr, format_check_data) +local function format_check(expr, format_check_data, opts) -- That forces specific order. - local zdata = format_check_data[0] - print(format_string('\ncheck_parsing(%r, {', expr, flags)) - local digits = ' -- ' - local digits2 = ' -- ' + local zflags = opts.flags[1] + local zdata = format_check_data[zflags] + local dig_len = 0 + if opts.funcname then + print(format_string('\n%s(%r, {', opts.funcname, expr)) + dig_len = #opts.funcname + 2 + else + print(format_string('\n_check_parsing(%r, %r, {', opts, expr)) + dig_len = #('_check_parsing(, \'') + #(format_string('%r', opts)) + end + local digits = ' --' .. (' '):rep(dig_len - #(' --')) + local digits2 = digits:sub(1, -10) for i = 0, #expr - 1 do if i % 10 == 0 then digits2 = ('%s%10u'):format(digits2, i / 10) @@ -232,12 +240,15 @@ local function format_check(expr, format_check_data) local diffs = {} local diffs_num = 0 for flags, v in pairs(format_check_data) do - if flags ~= 0 then + if flags ~= zflags then diffs[flags] = dictdiff(zdata, v) if diffs[flags] then - if flags == 3 then - if (dictdiff(format_check_data[1], format_check_data[3]) == nil - or dictdiff(format_check_data[2], format_check_data[3]) == nil) then + if flags == 3 + zflags then + if (dictdiff(format_check_data[1 + zflags], + format_check_data[3 + zflags]) == nil + or dictdiff(format_check_data[2 + zflags], + format_check_data[3 + zflags]) == nil) + then diffs[flags] = nil else diffs_num = diffs_num + 1 @@ -437,10 +448,12 @@ child_call_once(function() end) describe('Expressions parser', function() - local function check_parsing(str, exp_ast, exp_highlighting_fs, nz_flags_exps) + local function _check_parsing(opts, str, exp_ast, exp_highlighting_fs, + nz_flags_exps) + local zflags = opts.flags[1] nz_flags_exps = nz_flags_exps or {} local format_check_data = {} - for _, flags in ipairs({0, 1, 2, 3}) do + for _, flags in ipairs(opts.flags) do debug_log(('Running test case (%s, %u)'):format(str, flags)) local err, msg = pcall(function() if os.getenv('NVIM_TEST_PARSER_SPEC_PRINT_TEST_CASE') == '1' then @@ -452,25 +465,25 @@ describe('Expressions parser', function() local east = lib.viml_pexpr_parse(pstate, flags) local ast = east2lua(pstate, east) local hls = phl2lua(pstate) - local exps = { - ast = exp_ast, - hl_fs = exp_highlighting_fs, - } - local add_exps = nz_flags_exps[flags] - if not add_exps and flags == 3 then - add_exps = nz_flags_exps[1] or nz_flags_exps[2] - end - if add_exps then - if add_exps.ast then - exps.ast = mergedicts_copy(exps.ast, add_exps.ast) - end - if add_exps.hl_fs then - exps.hl_fs = mergedicts_copy(exps.hl_fs, add_exps.hl_fs) - end - end if exp_ast == nil then format_check_data[flags] = {ast=ast, hl_fs=hls_to_hl_fs(hls)} else + local exps = { + ast = exp_ast, + hl_fs = exp_highlighting_fs, + } + local add_exps = nz_flags_exps[flags] + if not add_exps and flags == 3 + zflags then + add_exps = nz_flags_exps[1 + zflags] or nz_flags_exps[2 + zflags] + end + if add_exps then + if add_exps.ast then + exps.ast = mergedicts_copy(exps.ast, add_exps.ast) + end + if add_exps.hl_fs then + exps.hl_fs = mergedicts_copy(exps.hl_fs, add_exps.hl_fs) + end + end eq(exps.ast, ast) if exp_highlighting_fs then local exp_highlighting = {} @@ -493,9 +506,18 @@ describe('Expressions parser', function() end end if exp_ast == nil then - format_check(str, format_check_data) + format_check(str, format_check_data, opts) end end + local function check_parsing(...) + return _check_parsing({flags={0, 1, 2, 3}, funcname='check_parsing'}, ...) + end + local function check_asgn_parsing(...) + return _check_parsing({ + flags={4, 5, 6, 7}, + funcname='check_asgn_parsing', + }, ...) + end local function hl(group, str, shift) return function(next_col) if nvim_hl_defs['NVim' .. group] == nil then @@ -7694,6 +7716,208 @@ describe('Expressions parser', function() }, }) end) + itp('works with assignments', function() + check_asgn_parsing('a=b', { + -- 012 + ast = { + { + 'Assignment(Plain):0:1:=', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + 'PlainIdentifier(scope=0,ident=b):0:2:b', + }, + }, + }, + }, { + hl('IdentifierName', 'a'), + hl('PlainAssignment', '='), + hl('IdentifierName', 'b'), + }) + + check_asgn_parsing('a+=b', { + -- 0123 + ast = { + { + 'Assignment(Add):0:1:+=', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + 'PlainIdentifier(scope=0,ident=b):0:3:b', + }, + }, + }, + }, { + hl('IdentifierName', 'a'), + hl('AssignmentWithAddition', '+='), + hl('IdentifierName', 'b'), + }) + + check_asgn_parsing('a-=b', { + -- 0123 + ast = { + { + 'Assignment(Subtract):0:1:-=', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + 'PlainIdentifier(scope=0,ident=b):0:3:b', + }, + }, + }, + }, { + hl('IdentifierName', 'a'), + hl('AssignmentWithSubtraction', '-='), + hl('IdentifierName', 'b'), + }) + + check_asgn_parsing('a.=b', { + -- 0123 + ast = { + { + 'Assignment(Concat):0:1:.=', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + 'PlainIdentifier(scope=0,ident=b):0:3:b', + }, + }, + }, + }, { + hl('IdentifierName', 'a'), + hl('AssignmentWithConcatenation', '.='), + hl('IdentifierName', 'b'), + }) + + check_asgn_parsing('a', { + -- 0 + ast = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + }, + }, { + hl('IdentifierName', 'a'), + }) + + check_asgn_parsing('a b', { + -- 012 + ast = { + { + 'OpMissing:0:1:', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + 'PlainIdentifier(scope=0,ident=b):0:1: b', + }, + }, + }, + err = { + arg = 'b', + msg = 'E15: Expected assignment operator or subscript: %.*s', + }, + }, { + hl('IdentifierName', 'a'), + hl('InvalidSpacing', ' '), + hl('IdentifierName', 'b'), + }, { + [5] = { + ast = { + ast = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + }, + err = REMOVE_THIS, + }, + hl_fs = { + [2] = REMOVE_THIS, + [3] = REMOVE_THIS, + } + }, + }) + + check_asgn_parsing('[a, b, c]', { + -- 012345678 + ast = { + { + 'ListLiteral:0:0:[', + children = { + { + 'Comma:0:2:,', + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + { + 'Comma:0:5:,', + children = { + 'PlainIdentifier(scope=0,ident=b):0:3: b', + 'PlainIdentifier(scope=0,ident=c):0:6: c', + }, + }, + }, + }, + }, + }, + }, + }, { + hl('List', '['), + hl('IdentifierName', 'a'), + hl('Comma', ','), + hl('IdentifierName', 'b', 1), + hl('Comma', ','), + hl('IdentifierName', 'c', 1), + hl('List', ']'), + }) + + check_asgn_parsing('[a, b]', { + -- 012345 + ast = { + { + 'ListLiteral:0:0:[', + children = { + { + 'Comma:0:2:,', + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + 'PlainIdentifier(scope=0,ident=b):0:3: b', + }, + }, + }, + }, + }, + }, { + hl('List', '['), + hl('IdentifierName', 'a'), + hl('Comma', ','), + hl('IdentifierName', 'b', 1), + hl('List', ']'), + }) + + check_asgn_parsing('[a]', { + -- 012 + ast = { + { + 'ListLiteral:0:0:[', + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + }, + }, + }, + }, { + hl('List', '['), + hl('IdentifierName', 'a'), + hl('List', ']'), + }) + + check_asgn_parsing('[]', { + -- 01 + ast = { + 'ListLiteral:0:0:[', + }, + err = { + arg = ']', + msg = 'E475: Unable to assign to empty list: %.*s', + }, + }, { + hl('List', '['), + hl('InvalidList', ']'), + }) + + -- check_asgn_parsing('a[1 + 2] += 3') + -- check_asgn_parsing('a[{-> {b{3}: 4}[5]}()] += 6') + -- check_asgn_parsing('a{1}.2[{-> {b{3}: 4}[5]}()]') + end) -- FIXME: Test assignments thoroughly -- FIXME: Test that parsing assignments can be used for `:for` pre-`in` part. -- FIXME: Somehow make functional tests use the same code. Or, at least, -- cgit From c287893225bad586af486b37546f5982e5b1cd03 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 19 Nov 2017 19:22:54 +0300 Subject: viml/parser/expressions,unittests: Do better testing, fix found issues --- test/functional/ui/cmdline_highlight_spec.lua | 1 + test/unit/viml/expressions/parser_spec.lua | 771 +++++++++++++++++++++++++- 2 files changed, 765 insertions(+), 7 deletions(-) (limited to 'test') diff --git a/test/functional/ui/cmdline_highlight_spec.lua b/test/functional/ui/cmdline_highlight_spec.lua index ab195f9f1e..023673738d 100644 --- a/test/functional/ui/cmdline_highlight_spec.lua +++ b/test/functional/ui/cmdline_highlight_spec.lua @@ -901,4 +901,5 @@ describe('Expressions coloring support', function() -- FIXME: Test expr coloring when using -u NORC and -u NONE. -- FIXME: Test different ways of triggering expression highlighting (:=, -- i=, :e, "=). + -- FIXME: Test with various invalid unicode and multibyte characters. end) diff --git a/test/unit/viml/expressions/parser_spec.lua b/test/unit/viml/expressions/parser_spec.lua index d6d8dd0807..eca76aa9cd 100644 --- a/test/unit/viml/expressions/parser_spec.lua +++ b/test/unit/viml/expressions/parser_spec.lua @@ -268,12 +268,12 @@ local function format_check(expr, format_check_data, opts) local diff = diffs[flags] print((' [%u] = {'):format(flags)) if diff.ast then - print(' ast = ' .. format_luav(diff.ast, ' ')) + print(' ast = ' .. format_luav(diff.ast, ' ') .. ',') end if diff.hl_fs then print(' hl_fs = ' .. format_luav(diff.hl_fs, ' ', { literal_strings=true - })) + }) .. ',') end print(' },') end @@ -7914,12 +7914,769 @@ describe('Expressions parser', function() hl('InvalidList', ']'), }) - -- check_asgn_parsing('a[1 + 2] += 3') - -- check_asgn_parsing('a[{-> {b{3}: 4}[5]}()] += 6') - -- check_asgn_parsing('a{1}.2[{-> {b{3}: 4}[5]}()]') + check_asgn_parsing('a[1] += 3', { + -- 012345678 + ast = { + { + 'Assignment(Add):0:4: +=', + children = { + { + 'Subscript:0:1:[', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + 'Integer(val=1):0:2:1', + }, + }, + 'Integer(val=3):0:7: 3', + }, + }, + }, + }, { + hl('IdentifierName', 'a'), + hl('SubscriptBracket', '['), + hl('Number', '1'), + hl('SubscriptBracket', ']'), + hl('AssignmentWithAddition', '+=', 1), + hl('Number', '3', 1), + }) + + check_asgn_parsing('a[1 + 2] += 3', { + -- 0123456789012 + -- 0 1 + ast = { + { + 'Assignment(Add):0:8: +=', + children = { + { + 'Subscript:0:1:[', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + { + 'BinaryPlus:0:3: +', + children = { + 'Integer(val=1):0:2:1', + 'Integer(val=2):0:5: 2', + }, + }, + }, + }, + 'Integer(val=3):0:11: 3', + }, + }, + }, + }, { + hl('IdentifierName', 'a'), + hl('SubscriptBracket', '['), + hl('Number', '1'), + hl('BinaryPlus', '+', 1), + hl('Number', '2', 1), + hl('SubscriptBracket', ']'), + hl('AssignmentWithAddition', '+=', 1), + hl('Number', '3', 1), + }) + + check_asgn_parsing('a[{-> {b{3}: 4}[5]}()] += 6', { + -- 012345678901234567890123456 + -- 0 1 2 + ast = { + { + 'Assignment(Add):0:22: +=', + children = { + { + 'Subscript:0:1:[', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + { + 'Call:0:19:(', + children = { + { + 'Lambda(\\di):0:2:{', + children = { + { + 'Arrow:0:3:->', + children = { + { + 'Subscript:0:15:[', + children = { + { + 'DictLiteral(-di):0:5: {', + children = { + { + 'Colon:0:11::', + children = { + { + 'ComplexIdentifier:0:8:', + children = { + 'PlainIdentifier(scope=0,ident=b):0:7:b', + { + 'CurlyBracesIdentifier(--i):0:8:{', + children = { + 'Integer(val=3):0:9:3', + }, + }, + }, + }, + 'Integer(val=4):0:12: 4', + }, + }, + }, + }, + 'Integer(val=5):0:16:5', + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + 'Integer(val=6):0:25: 6', + }, + }, + }, + }, { + hl('IdentifierName', 'a'), + hl('SubscriptBracket', '['), + hl('Lambda', '{'), + hl('Arrow', '->'), + hl('Dict', '{', 1), + hl('IdentifierName', 'b'), + hl('Curly', '{'), + hl('Number', '3'), + hl('Curly', '}'), + hl('Colon', ':'), + hl('Number', '4', 1), + hl('Dict', '}'), + hl('SubscriptBracket', '['), + hl('Number', '5'), + hl('SubscriptBracket', ']'), + hl('Lambda', '}'), + hl('CallingParenthesis', '('), + hl('CallingParenthesis', ')'), + hl('SubscriptBracket', ']'), + hl('AssignmentWithAddition', '+=', 1), + hl('Number', '6', 1), + }) + + check_asgn_parsing('a{1}.2[{-> {b{3}: 4}[5]}()]', { + -- 012345678901234567890123456 + -- 0 1 2 + ast = { + { + 'Subscript:0:6:[', + children = { + { + 'ConcatOrSubscript:0:4:.', + children = { + { + 'ComplexIdentifier:0:1:', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + { + 'CurlyBracesIdentifier(--i):0:1:{', + children = { + 'Integer(val=1):0:2:1', + }, + }, + }, + }, + 'PlainKey(key=2):0:5:2', + }, + }, + { + 'Call:0:24:(', + children = { + { + 'Lambda(\\di):0:7:{', + children = { + { + 'Arrow:0:8:->', + children = { + { + 'Subscript:0:20:[', + children = { + { + 'DictLiteral(-di):0:10: {', + children = { + { + 'Colon:0:16::', + children = { + { + 'ComplexIdentifier:0:13:', + children = { + 'PlainIdentifier(scope=0,ident=b):0:12:b', + { + 'CurlyBracesIdentifier(--i):0:13:{', + children = { + 'Integer(val=3):0:14:3', + }, + }, + }, + }, + 'Integer(val=4):0:17: 4', + }, + }, + }, + }, + 'Integer(val=5):0:21:5', + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, { + hl('IdentifierName', 'a'), + hl('Curly', '{'), + hl('Number', '1'), + hl('Curly', '}'), + hl('ConcatOrSubscript', '.'), + hl('IdentifierKey', '2'), + hl('SubscriptBracket', '['), + hl('Lambda', '{'), + hl('Arrow', '->'), + hl('Dict', '{', 1), + hl('IdentifierName', 'b'), + hl('Curly', '{'), + hl('Number', '3'), + hl('Curly', '}'), + hl('Colon', ':'), + hl('Number', '4', 1), + hl('Dict', '}'), + hl('SubscriptBracket', '['), + hl('Number', '5'), + hl('SubscriptBracket', ']'), + hl('Lambda', '}'), + hl('CallingParenthesis', '('), + hl('CallingParenthesis', ')'), + hl('SubscriptBracket', ']'), + }) + + check_asgn_parsing('a', { + -- 0 + ast = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + }, + }, { + hl('IdentifierName', 'a'), + }) + + check_asgn_parsing('{a}', { + -- 012 + ast = { + { + 'CurlyBracesIdentifier(--i):0:0:{', + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + }, + }, + }, + }, { + hl('FigureBrace', '{'), + hl('IdentifierName', 'a'), + hl('Curly', '}'), + }) + + check_asgn_parsing('{a}b', { + -- 0123 + ast = { + { + 'ComplexIdentifier:0:3:', + children = { + { + 'CurlyBracesIdentifier(--i):0:0:{', + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + }, + }, + 'PlainIdentifier(scope=0,ident=b):0:3:b', + }, + }, + }, + }, { + hl('FigureBrace', '{'), + hl('IdentifierName', 'a'), + hl('Curly', '}'), + hl('IdentifierName', 'b'), + }) + + check_asgn_parsing('a{b}c', { + -- 01234 + ast = { + { + 'ComplexIdentifier:0:1:', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + { + 'ComplexIdentifier:0:4:', + children = { + { + 'CurlyBracesIdentifier(--i):0:1:{', + children = { + 'PlainIdentifier(scope=0,ident=b):0:2:b', + }, + }, + 'PlainIdentifier(scope=0,ident=c):0:4:c', + }, + }, + }, + }, + }, + }, { + hl('IdentifierName', 'a'), + hl('Curly', '{'), + hl('IdentifierName', 'b'), + hl('Curly', '}'), + hl('IdentifierName', 'c'), + }) + + check_asgn_parsing('a{b}c[0]', { + -- 01234567 + ast = { + { + 'Subscript:0:5:[', + children = { + { + 'ComplexIdentifier:0:1:', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + { + 'ComplexIdentifier:0:4:', + children = { + { + 'CurlyBracesIdentifier(--i):0:1:{', + children = { + 'PlainIdentifier(scope=0,ident=b):0:2:b', + }, + }, + 'PlainIdentifier(scope=0,ident=c):0:4:c', + }, + }, + }, + }, + 'Integer(val=0):0:6:0', + }, + }, + }, + }, { + hl('IdentifierName', 'a'), + hl('Curly', '{'), + hl('IdentifierName', 'b'), + hl('Curly', '}'), + hl('IdentifierName', 'c'), + hl('SubscriptBracket', '['), + hl('Number', '0'), + hl('SubscriptBracket', ']'), + }) + + check_asgn_parsing('a{b}c.0', { + -- 0123456 + ast = { + { + 'ConcatOrSubscript:0:5:.', + children = { + { + 'ComplexIdentifier:0:1:', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + { + 'ComplexIdentifier:0:4:', + children = { + { + 'CurlyBracesIdentifier(--i):0:1:{', + children = { + 'PlainIdentifier(scope=0,ident=b):0:2:b', + }, + }, + 'PlainIdentifier(scope=0,ident=c):0:4:c', + }, + }, + }, + }, + 'PlainKey(key=0):0:6:0', + }, + }, + }, + }, { + hl('IdentifierName', 'a'), + hl('Curly', '{'), + hl('IdentifierName', 'b'), + hl('Curly', '}'), + hl('IdentifierName', 'c'), + hl('ConcatOrSubscript', '.'), + hl('IdentifierKey', '0'), + }) + + check_asgn_parsing('[a{b}c[0].0]', { + -- 012345678901 + -- 0 1 + ast = { + { + 'ListLiteral:0:0:[', + children = { + { + 'ConcatOrSubscript:0:9:.', + children = { + { + 'Subscript:0:6:[', + children = { + { + 'ComplexIdentifier:0:2:', + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + { + 'ComplexIdentifier:0:5:', + children = { + { + 'CurlyBracesIdentifier(--i):0:2:{', + children = { + 'PlainIdentifier(scope=0,ident=b):0:3:b', + }, + }, + 'PlainIdentifier(scope=0,ident=c):0:5:c', + }, + }, + }, + }, + 'Integer(val=0):0:7:0', + }, + }, + 'PlainKey(key=0):0:10:0', + }, + }, + }, + }, + }, + }, { + hl('List', '['), + hl('IdentifierName', 'a'), + hl('Curly', '{'), + hl('IdentifierName', 'b'), + hl('Curly', '}'), + hl('IdentifierName', 'c'), + hl('SubscriptBracket', '['), + hl('Number', '0'), + hl('SubscriptBracket', ']'), + hl('ConcatOrSubscript', '.'), + hl('IdentifierKey', '0'), + hl('List', ']'), + }) + + check_asgn_parsing('{a}{b}', { + -- 012345 + ast = { + { + 'ComplexIdentifier:0:3:', + children = { + { + 'CurlyBracesIdentifier(--i):0:0:{', + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + }, + }, + { + 'CurlyBracesIdentifier(--i):0:3:{', + children = { + 'PlainIdentifier(scope=0,ident=b):0:4:b', + }, + }, + }, + }, + }, + }, { + hl('FigureBrace', '{'), + hl('IdentifierName', 'a'), + hl('Curly', '}'), + hl('Curly', '{'), + hl('IdentifierName', 'b'), + hl('Curly', '}'), + }) + + check_asgn_parsing('a.b{c}{d}', { + -- 012345678 + ast = { + { + 'OpMissing:0:3:', + children = { + { + 'ConcatOrSubscript:0:1:.', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + 'PlainKey(key=b):0:2:b', + }, + }, + { + 'ComplexIdentifier:0:6:', + children = { + { + 'CurlyBracesIdentifier(--i):0:3:{', + children = { + 'PlainIdentifier(scope=0,ident=c):0:4:c', + }, + }, + { + 'CurlyBracesIdentifier(--i):0:6:{', + children = { + 'PlainIdentifier(scope=0,ident=d):0:7:d', + }, + }, + }, + }, + }, + }, + }, + err = { + arg = '{c}{d}', + msg = 'E15: Missing operator: %.*s', + }, + }, { + hl('IdentifierName', 'a'), + hl('ConcatOrSubscript', '.'), + hl('IdentifierKey', 'b'), + hl('InvalidFigureBrace', '{'), + hl('IdentifierName', 'c'), + hl('Curly', '}'), + hl('Curly', '{'), + hl('IdentifierName', 'd'), + hl('Curly', '}'), + }) + + check_asgn_parsing('[a] = 1', { + -- 0123456 + ast = { + { + 'Assignment(Plain):0:3: =', + children = { + { + 'ListLiteral:0:0:[', + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + }, + }, + 'Integer(val=1):0:5: 1', + }, + }, + }, + }, { + hl('List', '['), + hl('IdentifierName', 'a'), + hl('List', ']'), + hl('PlainAssignment', '=', 1), + hl('Number', '1', 1), + }) + + check_asgn_parsing('[a[b], [c, [d, [e]]]] = 1', { + -- 0123456789012345678901234 + -- 0 1 2 + ast = { + { + 'Assignment(Plain):0:21: =', + children = { + { + 'ListLiteral:0:0:[', + children = { + { + 'Comma:0:5:,', + children = { + { + 'Subscript:0:2:[', + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + 'PlainIdentifier(scope=0,ident=b):0:3:b', + }, + }, + { + 'ListLiteral:0:6: [', + children = { + { + 'Comma:0:9:,', + children = { + 'PlainIdentifier(scope=0,ident=c):0:8:c', + { + 'ListLiteral:0:10: [', + children = { + { + 'Comma:0:13:,', + children = { + 'PlainIdentifier(scope=0,ident=d):0:12:d', + { + 'ListLiteral:0:14: [', + children = { + 'PlainIdentifier(scope=0,ident=e):0:16:e', + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + 'Integer(val=1):0:23: 1', + }, + }, + }, + err = { + arg = '[c, [d, [e]]]] = 1', + msg = 'E475: Nested lists not allowed when assigning: %.*s', + }, + }, { + hl('List', '['), + hl('IdentifierName', 'a'), + hl('SubscriptBracket', '['), + hl('IdentifierName', 'b'), + hl('SubscriptBracket', ']'), + hl('Comma', ','), + hl('InvalidList', '[', 1), + hl('IdentifierName', 'c'), + hl('Comma', ','), + hl('InvalidList', '[', 1), + hl('IdentifierName', 'd'), + hl('Comma', ','), + hl('InvalidList', '[', 1), + hl('IdentifierName', 'e'), + hl('List', ']'), + hl('List', ']'), + hl('List', ']'), + hl('List', ']'), + hl('PlainAssignment', '=', 1), + hl('Number', '1', 1), + }) + + check_asgn_parsing('$X += 1', { + -- 0123456 + ast = { + { + 'Assignment(Add):0:2: +=', + children = { + 'Environment(ident=X):0:0:$X', + 'Integer(val=1):0:5: 1', + }, + }, + }, + }, { + hl('EnvironmentSigil', '$'), + hl('EnvironmentName', 'X'), + hl('AssignmentWithAddition', '+=', 1), + hl('Number', '1', 1), + }) + + check_asgn_parsing('@a .= 1', { + -- 0123456 + ast = { + { + 'Assignment(Concat):0:2: .=', + children = { + 'Register(name=a):0:0:@a', + 'Integer(val=1):0:5: 1', + }, + }, + }, + }, { + hl('Register', '@a'), + hl('AssignmentWithConcatenation', '.=', 1), + hl('Number', '1', 1), + }) + + check_asgn_parsing('&option -= 1', { + -- 012345678901 + -- 0 1 + ast = { + { + 'Assignment(Subtract):0:7: -=', + children = { + 'Option(scope=0,ident=option):0:0:&option', + 'Integer(val=1):0:10: 1', + }, + }, + }, + }, { + hl('OptionSigil', '&'), + hl('OptionName', 'option'), + hl('AssignmentWithSubtraction', '-=', 1), + hl('Number', '1', 1), + }) + + check_asgn_parsing('[$X, @a, &l:option] = [1, 2, 3]', { + -- 0123456789012345678901234567890 + -- 0 1 2 3 + ast = { + { + 'Assignment(Plain):0:19: =', + children = { + { + 'ListLiteral:0:0:[', + children = { + { + 'Comma:0:3:,', + children = { + 'Environment(ident=X):0:1:$X', + { + 'Comma:0:7:,', + children = { + 'Register(name=a):0:4: @a', + 'Option(scope=l,ident=option):0:8: &l:option', + }, + }, + }, + }, + }, + }, + { + 'ListLiteral:0:21: [', + children = { + { + 'Comma:0:24:,', + children = { + 'Integer(val=1):0:23:1', + { + 'Comma:0:27:,', + children = { + 'Integer(val=2):0:25: 2', + 'Integer(val=3):0:28: 3', + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, { + hl('List', '['), + hl('EnvironmentSigil', '$'), + hl('EnvironmentName', 'X'), + hl('Comma', ','), + hl('Register', '@a', 1), + hl('Comma', ','), + hl('OptionSigil', '&', 1), + hl('OptionScope', 'l'), + hl('OptionScopeDelimiter', ':'), + hl('OptionName', 'option'), + hl('List', ']'), + hl('PlainAssignment', '=', 1), + hl('List', '[', 1), + hl('Number', '1'), + hl('Comma', ','), + hl('Number', '2', 1), + hl('Comma', ','), + hl('Number', '3', 1), + hl('List', ']'), + }) end) - -- FIXME: Test assignments thoroughly - -- FIXME: Test that parsing assignments can be used for `:for` pre-`in` part. -- FIXME: Somehow make functional tests use the same code. Or, at least, -- create an automated script which will do the import. end) -- cgit From 53fa435a1f081e1d7e1890e3ecd978b85c39e0eb Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 19 Nov 2017 19:34:15 +0300 Subject: functests: Fix ui/cmdline test --- test/functional/ui/cmdline_spec.lua | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) (limited to 'test') diff --git a/test/functional/ui/cmdline_spec.lua b/test/functional/ui/cmdline_spec.lua index 0f8302b036..136ab09d4f 100644 --- a/test/functional/ui/cmdline_spec.lua +++ b/test/functional/ui/cmdline_spec.lua @@ -226,7 +226,11 @@ describe('external cmdline', function() prompt = "", special = {'"', true}, },{ - content = { { {}, "1+2" } }, + content = { + { {}, "1" }, + { {}, "+" }, + { {}, "2" }, + }, firstc = "=", indent = 0, pos = 3, @@ -303,7 +307,7 @@ describe('external cmdline', function() pos = 0, prompt = "", }}, cmdline) - eq({{{{}, 'function Foo()'}}}, block) + eq({ { { {}, 'function Foo()'} } }, block) end) feed('line1') @@ -314,8 +318,8 @@ describe('external cmdline', function() ~ | | ]], nil, nil, function() - eq({{{{}, 'function Foo()'}}, - {{{}, ' line1'}}}, block) + eq({ { { {}, 'function Foo()'} }, + { { {}, ' line1'} } }, block) end) block = {} @@ -327,8 +331,8 @@ describe('external cmdline', function() ~ | ^ | ]], nil, nil, function() - eq({{{{}, 'function Foo()'}}, - {{{}, ' line1'}}}, block) + eq({ { { {}, 'function Foo()'} }, + { { {}, ' line1'} } }, block) end) -- cgit From a94255a7ac22649311f858e39b217543d0d7e5e8 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 19 Nov 2017 20:20:06 +0300 Subject: tests: Use single test file for unit and functional parser tests --- test/functional/api/vim_spec.lua | 7132 +---------------------- test/unit/viml/expressions/parser_spec.lua | 8180 +------------------------- test/unit/viml/expressions/parser_tests.lua | 8185 +++++++++++++++++++++++++++ 3 files changed, 8241 insertions(+), 15256 deletions(-) create mode 100644 test/unit/viml/expressions/parser_tests.lua (limited to 'test') diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua index 714b1988fb..3939bc9b52 100644 --- a/test/functional/api/vim_spec.lua +++ b/test/functional/api/vim_spec.lua @@ -766,6 +766,11 @@ describe('api', function() elseif typ == 'Environment' then typ = ('%s(ident=%s)'):format(typ, east_api_node.ident) east_api_node.ident = nil + elseif typ == 'Assignment' then + local aug = east_api_node.augmentation + if aug == '' then aug = 'Plain' end + typ = ('%s(%s)'):format(typ, aug) + east_api_node.augmentation = nil end typ = ('%s:%u:%u:%s'):format( typ, east_api_node.start[1], east_api_node.start[2], @@ -798,6 +803,9 @@ describe('api', function() end if east_api.ast then east_api.ast = {simplify_east_api_node(line, east_api.ast)} + if #east_api.ast == 0 then + east_api.ast = nil + end end if east_api.len == #line then east_api.len = nil @@ -819,11 +827,19 @@ describe('api', function() [1] = "m", [2] = "E", [3] = "mE", + [4] = "l", + [5] = "lm", + [6] = "lE", + [7] = "lmE", } - local function check_parsing(str, exp_ast, exp_highlighting_fs, - nz_flags_exps) + local function _check_parsing(opts, str, exp_ast, exp_highlighting_fs, + nz_flags_exps) + if type(str) ~= 'string' then + return + end + local zflags = opts.flags[1] nz_flags_exps = nz_flags_exps or {} - for _, flags in ipairs({0, 1, 2, 3}) do + for _, flags in ipairs(opts.flags) do local err, msg = pcall(function() local east_api = meths.parse_expression(str, FLAGS_TO_STR[flags], true) local east_hl = east_api.highlight @@ -835,8 +851,8 @@ describe('api', function() hl_fs = exp_highlighting_fs, } local add_exps = nz_flags_exps[flags] - if not add_exps and flags == 3 then - add_exps = nz_flags_exps[1] or nz_flags_exps[2] + if not add_exps and flags == 3 + zflags then + add_exps = nz_flags_exps[1 + zflags] or nz_flags_exps[2 + zflags] end if add_exps then if add_exps.ast then @@ -873,7096 +889,22 @@ describe('api', function() str)), (col + #str) end end - it('works with + and @a', function() - check_parsing('@a', { - ast = { - 'Register(name=a):0:0:@a', - }, - }, { - hl('Register', '@a'), - }) - check_parsing('+@a', { - ast = { - { - 'UnaryPlus:0:0:+', - children = { - 'Register(name=a):0:1:@a', - }, - }, - }, - }, { - hl('UnaryPlus', '+'), - hl('Register', '@a'), - }) - check_parsing('@a+@b', { - ast = { - { - 'BinaryPlus:0:2:+', - children = { - 'Register(name=a):0:0:@a', - 'Register(name=b):0:3:@b', - }, - }, - }, - }, { - hl('Register', '@a'), - hl('BinaryPlus', '+'), - hl('Register', '@b'), - }) - check_parsing('@a+@b+@c', { - ast = { - { - 'BinaryPlus:0:5:+', - children = { - { - 'BinaryPlus:0:2:+', - children = { - 'Register(name=a):0:0:@a', - 'Register(name=b):0:3:@b', - }, - }, - 'Register(name=c):0:6:@c', - }, - }, - }, - }, { - hl('Register', '@a'), - hl('BinaryPlus', '+'), - hl('Register', '@b'), - hl('BinaryPlus', '+'), - hl('Register', '@c'), - }) - check_parsing('+@a+@b', { - ast = { - { - 'BinaryPlus:0:3:+', - children = { - { - 'UnaryPlus:0:0:+', - children = { - 'Register(name=a):0:1:@a', - }, - }, - 'Register(name=b):0:4:@b', - }, - }, - }, - }, { - hl('UnaryPlus', '+'), - hl('Register', '@a'), - hl('BinaryPlus', '+'), - hl('Register', '@b'), - }) - check_parsing('+@a++@b', { - ast = { - { - 'BinaryPlus:0:3:+', - children = { - { - 'UnaryPlus:0:0:+', - children = { - 'Register(name=a):0:1:@a', - }, - }, - { - 'UnaryPlus:0:4:+', - children = { - 'Register(name=b):0:5:@b', - }, - }, - }, - }, - }, - }, { - hl('UnaryPlus', '+'), - hl('Register', '@a'), - hl('BinaryPlus', '+'), - hl('UnaryPlus', '+'), - hl('Register', '@b'), - }) - check_parsing('@a@b', { - ast = { - { - 'OpMissing:0:2:', - children = { - 'Register(name=a):0:0:@a', - 'Register(name=b):0:2:@b', - }, - }, - }, - err = { - arg = '@b', - msg = 'E15: Missing operator: %.*s', - }, - }, { - hl('Register', '@a'), - hl('InvalidRegister', '@b'), - }, { - [1] = { - ast = { - len = 2, - err = REMOVE_THIS, - ast = { - 'Register(name=a):0:0:@a' - }, - }, - hl_fs = { - [2] = REMOVE_THIS, - }, - }, - }) - check_parsing(' @a \t @b', { - ast = { - { - 'OpMissing:0:3:', - children = { - 'Register(name=a):0:0: @a', - 'Register(name=b):0:3: \t @b', - }, - }, - }, - err = { - arg = '@b', - msg = 'E15: Missing operator: %.*s', - }, - }, { - hl('Register', '@a', 1), - hl('InvalidSpacing', ' \t '), - hl('Register', '@b'), - }, { - [1] = { - ast = { - len = 6, - err = REMOVE_THIS, - ast = { - 'Register(name=a):0:0: @a' - }, - }, - hl_fs = { - [2] = REMOVE_THIS, - [3] = REMOVE_THIS, - }, - }, - }) - check_parsing('+', { - ast = { - 'UnaryPlus:0:0:+', - }, - err = { - arg = '', - msg = 'E15: Expected value, got EOC: %.*s', - }, - }, { - hl('UnaryPlus', '+'), - }) - check_parsing(' +', { - ast = { - 'UnaryPlus:0:0: +', - }, - err = { - arg = '', - msg = 'E15: Expected value, got EOC: %.*s', - }, - }, { - hl('UnaryPlus', '+', 1), - }) - check_parsing('@a+ ', { - ast = { - { - 'BinaryPlus:0:2:+', - children = { - 'Register(name=a):0:0:@a', - }, - }, - }, - err = { - arg = '', - msg = 'E15: Expected value, got EOC: %.*s', - }, - }, { - hl('Register', '@a'), - hl('BinaryPlus', '+'), - }) - end) - it('works with @a, + and parenthesis', function() - check_parsing('(@a)', { - ast = { - { - 'Nested:0:0:(', - children = { - 'Register(name=a):0:1:@a', - }, - }, - }, - }, { - hl('NestingParenthesis', '('), - hl('Register', '@a'), - hl('NestingParenthesis', ')'), - }) - check_parsing('()', { - ast = { - { - 'Nested:0:0:(', - children = { - 'Missing:0:1:', - }, - }, - }, - err = { - arg = ')', - msg = 'E15: Expected value, got parenthesis: %.*s', - }, - }, { - hl('NestingParenthesis', '('), - hl('InvalidNestingParenthesis', ')'), - }) - check_parsing(')', { - ast = { - { - 'Nested:0:0:', - children = { - 'Missing:0:0:', - }, - }, - }, - err = { - arg = ')', - msg = 'E15: Expected value, got parenthesis: %.*s', - }, - }, { - hl('InvalidNestingParenthesis', ')'), - }) - check_parsing('+)', { - ast = { - { - 'Nested:0:1:', - children = { - { - 'UnaryPlus:0:0:+', - children = { - 'Missing:0:1:', - }, - }, - }, - }, - }, - err = { - arg = ')', - msg = 'E15: Expected value, got parenthesis: %.*s', - }, - }, { - hl('UnaryPlus', '+'), - hl('InvalidNestingParenthesis', ')'), - }) - check_parsing('+@a(@b)', { - ast = { - { - 'UnaryPlus:0:0:+', - children = { - { - 'Call:0:3:(', - children = { - 'Register(name=a):0:1:@a', - 'Register(name=b):0:4:@b', - }, - }, - }, - }, - }, - }, { - hl('UnaryPlus', '+'), - hl('Register', '@a'), - hl('CallingParenthesis', '('), - hl('Register', '@b'), - hl('CallingParenthesis', ')'), - }) - check_parsing('@a+@b(@c)', { - ast = { - { - 'BinaryPlus:0:2:+', - children = { - 'Register(name=a):0:0:@a', - { - 'Call:0:5:(', - children = { - 'Register(name=b):0:3:@b', - 'Register(name=c):0:6:@c', - }, - }, - }, - }, - }, - }, { - hl('Register', '@a'), - hl('BinaryPlus', '+'), - hl('Register', '@b'), - hl('CallingParenthesis', '('), - hl('Register', '@c'), - hl('CallingParenthesis', ')'), - }) - check_parsing('@a()', { - ast = { - { - 'Call:0:2:(', - children = { - 'Register(name=a):0:0:@a', - }, - }, - }, - }, { - hl('Register', '@a'), - hl('CallingParenthesis', '('), - hl('CallingParenthesis', ')'), - }) - check_parsing('@a ()', { - ast = { - { - 'OpMissing:0:2:', - children = { - 'Register(name=a):0:0:@a', - { - 'Nested:0:2: (', - children = { - 'Missing:0:4:', - }, - }, - }, - }, - }, - err = { - arg = '()', - msg = 'E15: Missing operator: %.*s', - }, - }, { - hl('Register', '@a'), - hl('InvalidSpacing', ' '), - hl('NestingParenthesis', '('), - hl('InvalidNestingParenthesis', ')'), - }, { - [1] = { - ast = { - len = 3, - err = REMOVE_THIS, - ast = { - 'Register(name=a):0:0:@a', - }, - }, - hl_fs = { - [2] = REMOVE_THIS, - [3] = REMOVE_THIS, - [4] = REMOVE_THIS, - }, - }, - }) - check_parsing('@a + (@b)', { - ast = { - { - 'BinaryPlus:0:2: +', - children = { - 'Register(name=a):0:0:@a', - { - 'Nested:0:4: (', - children = { - 'Register(name=b):0:6:@b', - }, - }, - }, - }, - }, - }, { - hl('Register', '@a'), - hl('BinaryPlus', '+', 1), - hl('NestingParenthesis', '(', 1), - hl('Register', '@b'), - hl('NestingParenthesis', ')'), - }) - check_parsing('@a + (+@b)', { - ast = { - { - 'BinaryPlus:0:2: +', - children = { - 'Register(name=a):0:0:@a', - { - 'Nested:0:4: (', - children = { - { - 'UnaryPlus:0:6:+', - children = { - 'Register(name=b):0:7:@b', - }, - }, - }, - }, - }, - }, - }, - }, { - hl('Register', '@a'), - hl('BinaryPlus', '+', 1), - hl('NestingParenthesis', '(', 1), - hl('UnaryPlus', '+'), - hl('Register', '@b'), - hl('NestingParenthesis', ')'), - }) - check_parsing('@a + (@b + @c)', { - ast = { - { - 'BinaryPlus:0:2: +', - children = { - 'Register(name=a):0:0:@a', - { - 'Nested:0:4: (', - children = { - { - 'BinaryPlus:0:8: +', - children = { - 'Register(name=b):0:6:@b', - 'Register(name=c):0:10: @c', - }, - }, - }, - }, - }, - }, - }, - }, { - hl('Register', '@a'), - hl('BinaryPlus', '+', 1), - hl('NestingParenthesis', '(', 1), - hl('Register', '@b'), - hl('BinaryPlus', '+', 1), - hl('Register', '@c', 1), - hl('NestingParenthesis', ')'), - }) - check_parsing('(@a)+@b', { - ast = { - { - 'BinaryPlus:0:4:+', - children = { - { - 'Nested:0:0:(', - children = { - 'Register(name=a):0:1:@a', - }, - }, - 'Register(name=b):0:5:@b', - }, - }, - }, - }, { - hl('NestingParenthesis', '('), - hl('Register', '@a'), - hl('NestingParenthesis', ')'), - hl('BinaryPlus', '+'), - hl('Register', '@b'), - }) - check_parsing('@a+(@b)(@c)', { - -- 01234567890 - ast = { - { - 'BinaryPlus:0:2:+', - children = { - 'Register(name=a):0:0:@a', - { - 'Call:0:7:(', - children = { - { - 'Nested:0:3:(', - children = { 'Register(name=b):0:4:@b' }, - }, - 'Register(name=c):0:8:@c', - }, - }, - }, - }, - }, - }, { - hl('Register', '@a'), - hl('BinaryPlus', '+'), - hl('NestingParenthesis', '('), - hl('Register', '@b'), - hl('NestingParenthesis', ')'), - hl('CallingParenthesis', '('), - hl('Register', '@c'), - hl('CallingParenthesis', ')'), - }) - check_parsing('@a+((@b))(@c)', { - -- 01234567890123456890123456789 - -- 0 1 2 - ast = { - { - 'BinaryPlus:0:2:+', - children = { - 'Register(name=a):0:0:@a', - { - 'Call:0:9:(', - children = { - { - 'Nested:0:3:(', - children = { - { - 'Nested:0:4:(', - children = { 'Register(name=b):0:5:@b' } - }, - }, - }, - 'Register(name=c):0:10:@c', - }, - }, - }, - }, - }, - }, { - hl('Register', '@a'), - hl('BinaryPlus', '+'), - hl('NestingParenthesis', '('), - hl('NestingParenthesis', '('), - hl('Register', '@b'), - hl('NestingParenthesis', ')'), - hl('NestingParenthesis', ')'), - hl('CallingParenthesis', '('), - hl('Register', '@c'), - hl('CallingParenthesis', ')'), - }) - check_parsing('@a+((@b))+@c', { - -- 01234567890123456890123456789 - -- 0 1 2 - ast = { - { - 'BinaryPlus:0:9:+', - children = { - { - 'BinaryPlus:0:2:+', - children = { - 'Register(name=a):0:0:@a', - { - 'Nested:0:3:(', - children = { - { - 'Nested:0:4:(', - children = { 'Register(name=b):0:5:@b' } - }, - }, - }, - }, - }, - 'Register(name=c):0:10:@c', - }, - }, - }, - }, { - hl('Register', '@a'), - hl('BinaryPlus', '+'), - hl('NestingParenthesis', '('), - hl('NestingParenthesis', '('), - hl('Register', '@b'), - hl('NestingParenthesis', ')'), - hl('NestingParenthesis', ')'), - hl('BinaryPlus', '+'), - hl('Register', '@c'), - }) - check_parsing( - '@a + (@b + @c) + @d(@e) + (+@f) + ((+@g(@h))(@j)(@k))(@l)', {--[[ - | | | | | | | | || | | || | | ||| || || || || - 000000000011111111112222222222333333333344444444445555555 - 012345678901234567890123456789012345678901234567890123456 - ]] - ast = {{ - 'BinaryPlus:0:31: +', - children = { - { - 'BinaryPlus:0:23: +', - children = { - { - 'BinaryPlus:0:14: +', - children = { - { - 'BinaryPlus:0:2: +', - children = { - 'Register(name=a):0:0:@a', - { - 'Nested:0:4: (', - children = { - { - 'BinaryPlus:0:8: +', - children = { - 'Register(name=b):0:6:@b', - 'Register(name=c):0:10: @c', - }, - }, - }, - }, - }, - }, - { - 'Call:0:19:(', - children = { - 'Register(name=d):0:16: @d', - 'Register(name=e):0:20:@e', - }, - }, - }, - }, - { - 'Nested:0:25: (', - children = { - { - 'UnaryPlus:0:27:+', - children = { - 'Register(name=f):0:28:@f', - }, - }, - }, - }, - }, - }, - { - 'Call:0:53:(', - children = { - { - 'Nested:0:33: (', - children = { - { - 'Call:0:48:(', - children = { - { - 'Call:0:44:(', - children = { - { - 'Nested:0:35:(', - children = { - { - 'UnaryPlus:0:36:+', - children = { - { - 'Call:0:39:(', - children = { - 'Register(name=g):0:37:@g', - 'Register(name=h):0:40:@h', - }, - }, - }, - }, - }, - }, - 'Register(name=j):0:45:@j', - }, - }, - 'Register(name=k):0:49:@k', - }, - }, - }, - }, - 'Register(name=l):0:54:@l', - }, - }, - }, - }}, - }, { - hl('Register', '@a'), - hl('BinaryPlus', '+', 1), - hl('NestingParenthesis', '(', 1), - hl('Register', '@b'), - hl('BinaryPlus', '+', 1), - hl('Register', '@c', 1), - hl('NestingParenthesis', ')'), - hl('BinaryPlus', '+', 1), - hl('Register', '@d', 1), - hl('CallingParenthesis', '('), - hl('Register', '@e'), - hl('CallingParenthesis', ')'), - hl('BinaryPlus', '+', 1), - hl('NestingParenthesis', '(', 1), - hl('UnaryPlus', '+'), - hl('Register', '@f'), - hl('NestingParenthesis', ')'), - hl('BinaryPlus', '+', 1), - hl('NestingParenthesis', '(', 1), - hl('NestingParenthesis', '('), - hl('UnaryPlus', '+'), - hl('Register', '@g'), - hl('CallingParenthesis', '('), - hl('Register', '@h'), - hl('CallingParenthesis', ')'), - hl('NestingParenthesis', ')'), - hl('CallingParenthesis', '('), - hl('Register', '@j'), - hl('CallingParenthesis', ')'), - hl('CallingParenthesis', '('), - hl('Register', '@k'), - hl('CallingParenthesis', ')'), - hl('NestingParenthesis', ')'), - hl('CallingParenthesis', '('), - hl('Register', '@l'), - hl('CallingParenthesis', ')'), - }) - check_parsing('@a)', { - -- 012 - ast = { - { - 'Nested:0:2:', - children = { - 'Register(name=a):0:0:@a', - }, - }, - }, - err = { - arg = ')', - msg = 'E15: Unexpected closing parenthesis: %.*s', - }, - }, { - hl('Register', '@a'), - hl('InvalidNestingParenthesis', ')'), - }) - check_parsing('(@a', { - -- 012 - ast = { - { - 'Nested:0:0:(', - children = { - 'Register(name=a):0:1:@a', - }, - }, - }, - err = { - arg = '(@a', - msg = 'E110: Missing closing parenthesis for nested expression: %.*s', - }, - }, { - hl('NestingParenthesis', '('), - hl('Register', '@a'), - }) - check_parsing('@a(@b', { - -- 01234 - ast = { - { - 'Call:0:2:(', - children = { - 'Register(name=a):0:0:@a', - 'Register(name=b):0:3:@b', - }, - }, - }, - err = { - arg = '(@b', - msg = 'E116: Missing closing parenthesis for function call: %.*s', - }, - }, { - hl('Register', '@a'), - hl('CallingParenthesis', '('), - hl('Register', '@b'), - }) - check_parsing('@a(@b, @c, @d, @e)', { - -- 012345678901234567 - -- 0 1 - ast = { - { - 'Call:0:2:(', - children = { - 'Register(name=a):0:0:@a', - { - 'Comma:0:5:,', - children = { - 'Register(name=b):0:3:@b', - { - 'Comma:0:9:,', - children = { - 'Register(name=c):0:6: @c', - { - 'Comma:0:13:,', - children = { - 'Register(name=d):0:10: @d', - 'Register(name=e):0:14: @e', - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, { - hl('Register', '@a'), - hl('CallingParenthesis', '('), - hl('Register', '@b'), - hl('Comma', ','), - hl('Register', '@c', 1), - hl('Comma', ','), - hl('Register', '@d', 1), - hl('Comma', ','), - hl('Register', '@e', 1), - hl('CallingParenthesis', ')'), - }) - check_parsing('@a(@b(@c))', { - -- 01234567890123456789012345678901234567 - -- 0 1 2 3 - ast = { - { - 'Call:0:2:(', - children = { - 'Register(name=a):0:0:@a', - { - 'Call:0:5:(', - children = { - 'Register(name=b):0:3:@b', - 'Register(name=c):0:6:@c', - }, - }, - }, - }, - }, - }, { - hl('Register', '@a'), - hl('CallingParenthesis', '('), - hl('Register', '@b'), - hl('CallingParenthesis', '('), - hl('Register', '@c'), - hl('CallingParenthesis', ')'), - hl('CallingParenthesis', ')'), - }) - check_parsing('@a(@b(@c(@d(@e), @f(@g(@h), @i(@j)))))', { - -- 01234567890123456789012345678901234567 - -- 0 1 2 3 - ast = { - { - 'Call:0:2:(', - children = { - 'Register(name=a):0:0:@a', - { - 'Call:0:5:(', - children = { - 'Register(name=b):0:3:@b', - { - 'Call:0:8:(', - children = { - 'Register(name=c):0:6:@c', - { - 'Comma:0:15:,', - children = { - { - 'Call:0:11:(', - children = { - 'Register(name=d):0:9:@d', - 'Register(name=e):0:12:@e', - }, - }, - { - 'Call:0:19:(', - children = { - 'Register(name=f):0:16: @f', - { - 'Comma:0:26:,', - children = { - { - 'Call:0:22:(', - children = { - 'Register(name=g):0:20:@g', - 'Register(name=h):0:23:@h', - }, - }, - { - 'Call:0:30:(', - children = { - 'Register(name=i):0:27: @i', - 'Register(name=j):0:31:@j', - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, { - hl('Register', '@a'), - hl('CallingParenthesis', '('), - hl('Register', '@b'), - hl('CallingParenthesis', '('), - hl('Register', '@c'), - hl('CallingParenthesis', '('), - hl('Register', '@d'), - hl('CallingParenthesis', '('), - hl('Register', '@e'), - hl('CallingParenthesis', ')'), - hl('Comma', ','), - hl('Register', '@f', 1), - hl('CallingParenthesis', '('), - hl('Register', '@g'), - hl('CallingParenthesis', '('), - hl('Register', '@h'), - hl('CallingParenthesis', ')'), - hl('Comma', ','), - hl('Register', '@i', 1), - hl('CallingParenthesis', '('), - hl('Register', '@j'), - hl('CallingParenthesis', ')'), - hl('CallingParenthesis', ')'), - hl('CallingParenthesis', ')'), - hl('CallingParenthesis', ')'), - hl('CallingParenthesis', ')'), - }) - end) - it('works with variable names, including curly braces ones', function() - check_parsing('var', { - ast = { - 'PlainIdentifier(scope=0,ident=var):0:0:var', - }, - }, { - hl('IdentifierName', 'var'), - }) - check_parsing('g:var', { - ast = { - 'PlainIdentifier(scope=g,ident=var):0:0:g:var', - }, - }, { - hl('IdentifierScope', 'g'), - hl('IdentifierScopeDelimiter', ':'), - hl('IdentifierName', 'var'), - }) - check_parsing('g:', { - ast = { - 'PlainIdentifier(scope=g,ident=):0:0:g:', - }, - }, { - hl('IdentifierScope', 'g'), - hl('IdentifierScopeDelimiter', ':'), - }) - check_parsing('{a}', { - -- 012 - ast = { - { - 'CurlyBracesIdentifier:0:0:{', - children = { - 'PlainIdentifier(scope=0,ident=a):0:1:a', - }, - }, - }, - }, { - hl('Curly', '{'), - hl('IdentifierName', 'a'), - hl('Curly', '}'), - }) - check_parsing('{a:b}', { - -- 012 - ast = { - { - 'CurlyBracesIdentifier:0:0:{', - children = { - 'PlainIdentifier(scope=a,ident=b):0:1:a:b', - }, - }, - }, - }, { - hl('Curly', '{'), - hl('IdentifierScope', 'a'), - hl('IdentifierScopeDelimiter', ':'), - hl('IdentifierName', 'b'), - hl('Curly', '}'), - }) - check_parsing('{a:@b}', { - -- 012345 - ast = { - { - 'CurlyBracesIdentifier:0:0:{', - children = { - { - 'OpMissing:0:3:', - children={ - 'PlainIdentifier(scope=a,ident=):0:1:a:', - 'Register(name=b):0:3:@b', - }, - }, - }, - }, - }, - err = { - arg = '@b}', - msg = 'E15: Missing operator: %.*s', - }, - }, { - hl('Curly', '{'), - hl('IdentifierScope', 'a'), - hl('IdentifierScopeDelimiter', ':'), - hl('InvalidRegister', '@b'), - hl('Curly', '}'), - }) - check_parsing('{@a}', { - ast = { - { - 'CurlyBracesIdentifier:0:0:{', - children = { - 'Register(name=a):0:1:@a', - }, - }, - }, - }, { - hl('Curly', '{'), - hl('Register', '@a'), - hl('Curly', '}'), - }) - check_parsing('{@a}{@b}', { - -- 01234567 - ast = { - { - 'ComplexIdentifier:0:4:', - children = { - { - 'CurlyBracesIdentifier:0:0:{', - children = { - 'Register(name=a):0:1:@a', - }, - }, - { - 'CurlyBracesIdentifier:0:4:{', - children = { - 'Register(name=b):0:5:@b', - }, - }, - }, - }, - }, - }, { - hl('Curly', '{'), - hl('Register', '@a'), - hl('Curly', '}'), - hl('Curly', '{'), - hl('Register', '@b'), - hl('Curly', '}'), - }) - check_parsing('g:{@a}', { - -- 01234567 - ast = { - { - 'ComplexIdentifier:0:2:', - children = { - 'PlainIdentifier(scope=g,ident=):0:0:g:', - { - 'CurlyBracesIdentifier:0:2:{', - children = { - 'Register(name=a):0:3:@a', - }, - }, - }, - }, - }, - }, { - hl('IdentifierScope', 'g'), - hl('IdentifierScopeDelimiter', ':'), - hl('Curly', '{'), - hl('Register', '@a'), - hl('Curly', '}'), - }) - check_parsing('{@a}_test', { - -- 012345678 - ast = { - { - 'ComplexIdentifier:0:4:', - children = { - { - 'CurlyBracesIdentifier:0:0:{', - children = { - 'Register(name=a):0:1:@a', - }, - }, - 'PlainIdentifier(scope=0,ident=_test):0:4:_test', - }, - }, - }, - }, { - hl('Curly', '{'), - hl('Register', '@a'), - hl('Curly', '}'), - hl('IdentifierName', '_test'), - }) - check_parsing('g:{@a}_test', { - -- 01234567890 - ast = { - { - 'ComplexIdentifier:0:2:', - children = { - 'PlainIdentifier(scope=g,ident=):0:0:g:', - { - 'ComplexIdentifier:0:6:', - children = { - { - 'CurlyBracesIdentifier:0:2:{', - children = { - 'Register(name=a):0:3:@a', - }, - }, - 'PlainIdentifier(scope=0,ident=_test):0:6:_test', - }, - }, - }, - }, - }, - }, { - hl('IdentifierScope', 'g'), - hl('IdentifierScopeDelimiter', ':'), - hl('Curly', '{'), - hl('Register', '@a'), - hl('Curly', '}'), - hl('IdentifierName', '_test'), - }) - check_parsing('g:{@a}_test()', { - -- 0123456789012 - ast = { - { - 'Call:0:11:(', - children = { - { - 'ComplexIdentifier:0:2:', - children = { - 'PlainIdentifier(scope=g,ident=):0:0:g:', - { - 'ComplexIdentifier:0:6:', - children = { - { - 'CurlyBracesIdentifier:0:2:{', - children = { - 'Register(name=a):0:3:@a', - }, - }, - 'PlainIdentifier(scope=0,ident=_test):0:6:_test', - }, - }, - }, - }, - }, - }, - }, - }, { - hl('IdentifierScope', 'g'), - hl('IdentifierScopeDelimiter', ':'), - hl('Curly', '{'), - hl('Register', '@a'), - hl('Curly', '}'), - hl('IdentifierName', '_test'), - hl('CallingParenthesis', '('), - hl('CallingParenthesis', ')'), - }) - check_parsing('{@a} ()', { - -- 0123456789012 - ast = { - { - 'Call:0:4: (', - children = { - { - 'CurlyBracesIdentifier:0:0:{', - children = { - 'Register(name=a):0:1:@a', - }, - }, - }, - }, - }, - }, { - hl('Curly', '{'), - hl('Register', '@a'), - hl('Curly', '}'), - hl('CallingParenthesis', '(', 1), - hl('CallingParenthesis', ')'), - }) - check_parsing('g:{@a} ()', { - -- 0123456789012 - ast = { - { - 'Call:0:6: (', - children = { - { - 'ComplexIdentifier:0:2:', - children = { - 'PlainIdentifier(scope=g,ident=):0:0:g:', - { - 'CurlyBracesIdentifier:0:2:{', - children = { - 'Register(name=a):0:3:@a', - }, - }, - }, - }, - }, - }, - }, - }, { - hl('IdentifierScope', 'g'), - hl('IdentifierScopeDelimiter', ':'), - hl('Curly', '{'), - hl('Register', '@a'), - hl('Curly', '}'), - hl('CallingParenthesis', '(', 1), - hl('CallingParenthesis', ')'), - }) - check_parsing('{@a', { - -- 012 - ast = { - { - 'UnknownFigure:0:0:{', - children = { - 'Register(name=a):0:1:@a', - }, - }, - }, - err = { - arg = '{@a', - msg = 'E15: Missing closing figure brace: %.*s', - }, - }, { - hl('FigureBrace', '{'), - hl('Register', '@a'), - }) - end) - it('works with lambdas and dictionaries', function() - check_parsing('{}', { - ast = { - 'DictLiteral:0:0:{', - }, - }, { - hl('Dict', '{'), - hl('Dict', '}'), - }) - check_parsing('{->@a}', { - ast = { - { - 'Lambda:0:0:{', - children = { - { - 'Arrow:0:1:->', - children = { - 'Register(name=a):0:3:@a', - }, - }, - }, - }, - }, - }, { - hl('Lambda', '{'), - hl('Arrow', '->'), - hl('Register', '@a'), - hl('Lambda', '}'), - }) - check_parsing('{->@a+@b}', { - -- 012345678 - ast = { - { - 'Lambda:0:0:{', - children = { - { - 'Arrow:0:1:->', - children = { - { - 'BinaryPlus:0:5:+', - children = { - 'Register(name=a):0:3:@a', - 'Register(name=b):0:6:@b', - }, - }, - }, - }, - }, - }, - }, - }, { - hl('Lambda', '{'), - hl('Arrow', '->'), - hl('Register', '@a'), - hl('BinaryPlus', '+'), - hl('Register', '@b'), - hl('Lambda', '}'), - }) - check_parsing('{a->@a}', { - -- 012345678 - ast = { - { - 'Lambda:0:0:{', - children = { - 'PlainIdentifier(scope=0,ident=a):0:1:a', - { - 'Arrow:0:2:->', - children = { - 'Register(name=a):0:4:@a', - }, - }, - }, - }, - }, - }, { - hl('Lambda', '{'), - hl('IdentifierName', 'a'), - hl('Arrow', '->'), - hl('Register', '@a'), - hl('Lambda', '}'), - }) - check_parsing('{a,b->@a}', { - -- 012345678 - ast = { - { - 'Lambda:0:0:{', - children = { - { - 'Comma:0:2:,', - children = { - 'PlainIdentifier(scope=0,ident=a):0:1:a', - 'PlainIdentifier(scope=0,ident=b):0:3:b', - }, - }, - { - 'Arrow:0:4:->', - children = { - 'Register(name=a):0:6:@a', - }, - }, - }, - }, - }, - }, { - hl('Lambda', '{'), - hl('IdentifierName', 'a'), - hl('Comma', ','), - hl('IdentifierName', 'b'), - hl('Arrow', '->'), - hl('Register', '@a'), - hl('Lambda', '}'), - }) - check_parsing('{a,b,c->@a}', { - -- 01234567890 - ast = { - { - 'Lambda:0:0:{', - children = { - { - 'Comma:0:2:,', - children = { - 'PlainIdentifier(scope=0,ident=a):0:1:a', - { - 'Comma:0:4:,', - children = { - 'PlainIdentifier(scope=0,ident=b):0:3:b', - 'PlainIdentifier(scope=0,ident=c):0:5:c', - }, - }, - }, - }, - { - 'Arrow:0:6:->', - children = { - 'Register(name=a):0:8:@a', - }, - }, - }, - }, - }, - }, { - hl('Lambda', '{'), - hl('IdentifierName', 'a'), - hl('Comma', ','), - hl('IdentifierName', 'b'), - hl('Comma', ','), - hl('IdentifierName', 'c'), - hl('Arrow', '->'), - hl('Register', '@a'), - hl('Lambda', '}'), - }) - check_parsing('{a,b,c,d->@a}', { - -- 0123456789012 - ast = { - { - 'Lambda:0:0:{', - children = { - { - 'Comma:0:2:,', - children = { - 'PlainIdentifier(scope=0,ident=a):0:1:a', - { - 'Comma:0:4:,', - children = { - 'PlainIdentifier(scope=0,ident=b):0:3:b', - { - 'Comma:0:6:,', - children = { - 'PlainIdentifier(scope=0,ident=c):0:5:c', - 'PlainIdentifier(scope=0,ident=d):0:7:d', - }, - }, - }, - }, - }, - }, - { - 'Arrow:0:8:->', - children = { - 'Register(name=a):0:10:@a', - }, - }, - }, - }, - }, - }, { - hl('Lambda', '{'), - hl('IdentifierName', 'a'), - hl('Comma', ','), - hl('IdentifierName', 'b'), - hl('Comma', ','), - hl('IdentifierName', 'c'), - hl('Comma', ','), - hl('IdentifierName', 'd'), - hl('Arrow', '->'), - hl('Register', '@a'), - hl('Lambda', '}'), - }) - check_parsing('{a,b,c,d,->@a}', { - -- 01234567890123 - ast = { - { - 'Lambda:0:0:{', - children = { - { - 'Comma:0:2:,', - children = { - 'PlainIdentifier(scope=0,ident=a):0:1:a', - { - 'Comma:0:4:,', - children = { - 'PlainIdentifier(scope=0,ident=b):0:3:b', - { - 'Comma:0:6:,', - children = { - 'PlainIdentifier(scope=0,ident=c):0:5:c', - { - 'Comma:0:8:,', - children = { - 'PlainIdentifier(scope=0,ident=d):0:7:d', - }, - }, - }, - }, - }, - }, - }, - }, - { - 'Arrow:0:9:->', - children = { - 'Register(name=a):0:11:@a', - }, - }, - }, - }, - }, - }, { - hl('Lambda', '{'), - hl('IdentifierName', 'a'), - hl('Comma', ','), - hl('IdentifierName', 'b'), - hl('Comma', ','), - hl('IdentifierName', 'c'), - hl('Comma', ','), - hl('IdentifierName', 'd'), - hl('Comma', ','), - hl('Arrow', '->'), - hl('Register', '@a'), - hl('Lambda', '}'), - }) - check_parsing('{a,b->{c,d->{e,f->@a}}}', { - -- 01234567890123456789012 - -- 0 1 2 - ast = { - { - 'Lambda:0:0:{', - children = { - { - 'Comma:0:2:,', - children = { - 'PlainIdentifier(scope=0,ident=a):0:1:a', - 'PlainIdentifier(scope=0,ident=b):0:3:b', - }, - }, - { - 'Arrow:0:4:->', - children = { - { - 'Lambda:0:6:{', - children = { - { - 'Comma:0:8:,', - children = { - 'PlainIdentifier(scope=0,ident=c):0:7:c', - 'PlainIdentifier(scope=0,ident=d):0:9:d', - }, - }, - { - 'Arrow:0:10:->', - children = { - { - 'Lambda:0:12:{', - children = { - { - 'Comma:0:14:,', - children = { - 'PlainIdentifier(scope=0,ident=e):0:13:e', - 'PlainIdentifier(scope=0,ident=f):0:15:f', - }, - }, - { - 'Arrow:0:16:->', - children = { - 'Register(name=a):0:18:@a', - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, { - hl('Lambda', '{'), - hl('IdentifierName', 'a'), - hl('Comma', ','), - hl('IdentifierName', 'b'), - hl('Arrow', '->'), - hl('Lambda', '{'), - hl('IdentifierName', 'c'), - hl('Comma', ','), - hl('IdentifierName', 'd'), - hl('Arrow', '->'), - hl('Lambda', '{'), - hl('IdentifierName', 'e'), - hl('Comma', ','), - hl('IdentifierName', 'f'), - hl('Arrow', '->'), - hl('Register', '@a'), - hl('Lambda', '}'), - hl('Lambda', '}'), - hl('Lambda', '}'), - }) - check_parsing('{a,b->c,d}', { - -- 0123456789 - ast = { - { - 'Lambda:0:0:{', - children = { - { - 'Comma:0:2:,', - children = { - 'PlainIdentifier(scope=0,ident=a):0:1:a', - 'PlainIdentifier(scope=0,ident=b):0:3:b', - }, - }, - { - 'Arrow:0:4:->', - children = { - { - 'Comma:0:7:,', - children = { - 'PlainIdentifier(scope=0,ident=c):0:6:c', - 'PlainIdentifier(scope=0,ident=d):0:8:d', - }, - }, - }, - }, - }, - }, - }, - err = { - arg = ',d}', - msg = 'E15: Comma outside of call, lambda or literal: %.*s', - }, - }, { - hl('Lambda', '{'), - hl('IdentifierName', 'a'), - hl('Comma', ','), - hl('IdentifierName', 'b'), - hl('Arrow', '->'), - hl('IdentifierName', 'c'), - hl('InvalidComma', ','), - hl('IdentifierName', 'd'), - hl('Lambda', '}'), - }) - check_parsing('a,b,c,d', { - -- 0123456789 - ast = { - { - 'Comma:0:1:,', - children = { - 'PlainIdentifier(scope=0,ident=a):0:0:a', - { - 'Comma:0:3:,', - children = { - 'PlainIdentifier(scope=0,ident=b):0:2:b', - { - 'Comma:0:5:,', - children = { - 'PlainIdentifier(scope=0,ident=c):0:4:c', - 'PlainIdentifier(scope=0,ident=d):0:6:d', - }, - }, - }, - }, - }, - }, - }, - err = { - arg = ',b,c,d', - msg = 'E15: Comma outside of call, lambda or literal: %.*s', - }, - }, { - hl('IdentifierName', 'a'), - hl('InvalidComma', ','), - hl('IdentifierName', 'b'), - hl('InvalidComma', ','), - hl('IdentifierName', 'c'), - hl('InvalidComma', ','), - hl('IdentifierName', 'd'), - }) - check_parsing('a,b,c,d,', { - -- 0123456789 - ast = { - { - 'Comma:0:1:,', - children = { - 'PlainIdentifier(scope=0,ident=a):0:0:a', - { - 'Comma:0:3:,', - children = { - 'PlainIdentifier(scope=0,ident=b):0:2:b', - { - 'Comma:0:5:,', - children = { - 'PlainIdentifier(scope=0,ident=c):0:4:c', - { - 'Comma:0:7:,', - children = { - 'PlainIdentifier(scope=0,ident=d):0:6:d', - }, - }, - }, - }, - }, - }, - }, - }, - }, - err = { - arg = ',b,c,d,', - msg = 'E15: Comma outside of call, lambda or literal: %.*s', - }, - }, { - hl('IdentifierName', 'a'), - hl('InvalidComma', ','), - hl('IdentifierName', 'b'), - hl('InvalidComma', ','), - hl('IdentifierName', 'c'), - hl('InvalidComma', ','), - hl('IdentifierName', 'd'), - hl('InvalidComma', ','), - }) - check_parsing(',', { - -- 0123456789 - ast = { - { - 'Comma:0:0:,', - children = { - 'Missing:0:0:', - }, - }, - }, - err = { - arg = ',', - msg = 'E15: Expected value, got comma: %.*s', - }, - }, { - hl('InvalidComma', ','), - }) - check_parsing('{,a->@a}', { - -- 0123456789 - ast = { - { - 'CurlyBracesIdentifier:0:0:{', - children = { - { - 'Arrow:0:3:->', - children = { - { - 'Comma:0:1:,', - children = { - 'Missing:0:1:', - 'PlainIdentifier(scope=0,ident=a):0:2:a', - }, - }, - 'Register(name=a):0:5:@a', - }, - }, - }, - }, - }, - err = { - arg = ',a->@a}', - msg = 'E15: Expected value, got comma: %.*s', - }, - }, { - hl('Curly', '{'), - hl('InvalidComma', ','), - hl('IdentifierName', 'a'), - hl('InvalidArrow', '->'), - hl('Register', '@a'), - hl('Curly', '}'), - }) - check_parsing('}', { - -- 0123456789 - ast = { - 'UnknownFigure:0:0:', - }, - err = { - arg = '}', - msg = 'E15: Unexpected closing figure brace: %.*s', - }, - }, { - hl('InvalidFigureBrace', '}'), - }) - check_parsing('{->}', { - -- 0123456789 - ast = { - { - 'Lambda:0:0:{', - children = { - 'Arrow:0:1:->', - }, - }, - }, - err = { - arg = '}', - msg = 'E15: Expected value, got closing figure brace: %.*s', - }, - }, { - hl('Lambda', '{'), - hl('Arrow', '->'), - hl('InvalidLambda', '}'), - }) - check_parsing('{a,b}', { - -- 0123456789 - ast = { - { - 'Lambda:0:0:{', - children = { - { - 'Comma:0:2:,', - children = { - 'PlainIdentifier(scope=0,ident=a):0:1:a', - 'PlainIdentifier(scope=0,ident=b):0:3:b', - }, - }, - }, - }, - }, - err = { - arg = '}', - msg = 'E15: Expected lambda arguments list or arrow: %.*s', - }, - }, { - hl('Lambda', '{'), - hl('IdentifierName', 'a'), - hl('Comma', ','), - hl('IdentifierName', 'b'), - hl('InvalidLambda', '}'), - }) - check_parsing('{a,}', { - -- 0123456789 - ast = { - { - 'Lambda:0:0:{', - children = { - { - 'Comma:0:2:,', - children = { - 'PlainIdentifier(scope=0,ident=a):0:1:a', - }, - }, - }, - }, - }, - err = { - arg = '}', - msg = 'E15: Expected lambda arguments list or arrow: %.*s', - }, - }, { - hl('Lambda', '{'), - hl('IdentifierName', 'a'), - hl('Comma', ','), - hl('InvalidLambda', '}'), - }) - check_parsing('{@a:@b}', { - -- 0123456789 - ast = { - { - 'DictLiteral:0:0:{', - children = { - { - 'Colon:0:3::', - children = { - 'Register(name=a):0:1:@a', - 'Register(name=b):0:4:@b', - }, - }, - }, - }, - }, - }, { - hl('Dict', '{'), - hl('Register', '@a'), - hl('Colon', ':'), - hl('Register', '@b'), - hl('Dict', '}'), - }) - check_parsing('{@a:@b,@c:@d}', { - -- 0123456789012 - -- 0 1 - ast = { - { - 'DictLiteral:0:0:{', - children = { - { - 'Comma:0:6:,', - children = { - { - 'Colon:0:3::', - children = { - 'Register(name=a):0:1:@a', - 'Register(name=b):0:4:@b', - }, - }, - { - 'Colon:0:9::', - children = { - 'Register(name=c):0:7:@c', - 'Register(name=d):0:10:@d', - }, - }, - }, - }, - }, - }, - }, - }, { - hl('Dict', '{'), - hl('Register', '@a'), - hl('Colon', ':'), - hl('Register', '@b'), - hl('Comma', ','), - hl('Register', '@c'), - hl('Colon', ':'), - hl('Register', '@d'), - hl('Dict', '}'), - }) - check_parsing('{@a:@b,@c:@d,@e:@f,}', { - -- 01234567890123456789 - -- 0 1 - ast = { - { - 'DictLiteral:0:0:{', - children = { - { - 'Comma:0:6:,', - children = { - { - 'Colon:0:3::', - children = { - 'Register(name=a):0:1:@a', - 'Register(name=b):0:4:@b', - }, - }, - { - 'Comma:0:12:,', - children = { - { - 'Colon:0:9::', - children = { - 'Register(name=c):0:7:@c', - 'Register(name=d):0:10:@d', - }, - }, - { - 'Comma:0:18:,', - children = { - { - 'Colon:0:15::', - children = { - 'Register(name=e):0:13:@e', - 'Register(name=f):0:16:@f', - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, { - hl('Dict', '{'), - hl('Register', '@a'), - hl('Colon', ':'), - hl('Register', '@b'), - hl('Comma', ','), - hl('Register', '@c'), - hl('Colon', ':'), - hl('Register', '@d'), - hl('Comma', ','), - hl('Register', '@e'), - hl('Colon', ':'), - hl('Register', '@f'), - hl('Comma', ','), - hl('Dict', '}'), - }) - check_parsing('{@a:@b,@c:@d,@e:@f,@g:}', { - -- 01234567890123456789012 - -- 0 1 2 - ast = { - { - 'DictLiteral:0:0:{', - children = { - { - 'Comma:0:6:,', - children = { - { - 'Colon:0:3::', - children = { - 'Register(name=a):0:1:@a', - 'Register(name=b):0:4:@b', - }, - }, - { - 'Comma:0:12:,', - children = { - { - 'Colon:0:9::', - children = { - 'Register(name=c):0:7:@c', - 'Register(name=d):0:10:@d', - }, - }, - { - 'Comma:0:18:,', - children = { - { - 'Colon:0:15::', - children = { - 'Register(name=e):0:13:@e', - 'Register(name=f):0:16:@f', - }, - }, - { - 'Colon:0:21::', - children = { - 'Register(name=g):0:19:@g', - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - err = { - arg = '}', - msg = 'E15: Expected value, got closing figure brace: %.*s', - }, - }, { - hl('Dict', '{'), - hl('Register', '@a'), - hl('Colon', ':'), - hl('Register', '@b'), - hl('Comma', ','), - hl('Register', '@c'), - hl('Colon', ':'), - hl('Register', '@d'), - hl('Comma', ','), - hl('Register', '@e'), - hl('Colon', ':'), - hl('Register', '@f'), - hl('Comma', ','), - hl('Register', '@g'), - hl('Colon', ':'), - hl('InvalidDict', '}'), - }) - check_parsing('{@a:@b,}', { - -- 01234567890123 - -- 0 1 - ast = { - { - 'DictLiteral:0:0:{', - children = { - { - 'Comma:0:6:,', - children = { - { - 'Colon:0:3::', - children = { - 'Register(name=a):0:1:@a', - 'Register(name=b):0:4:@b', - }, - }, - }, - }, - }, - }, - }, - }, { - hl('Dict', '{'), - hl('Register', '@a'), - hl('Colon', ':'), - hl('Register', '@b'), - hl('Comma', ','), - hl('Dict', '}'), - }) - check_parsing('{({f -> g})(@h)(@i)}', { - -- 01234567890123456789 - -- 0 1 - ast = { - { - 'CurlyBracesIdentifier:0:0:{', - children = { - { - 'Call:0:15:(', - children = { - { - 'Call:0:11:(', - children = { - { - 'Nested:0:1:(', - children = { - { - 'Lambda:0:2:{', - children = { - 'PlainIdentifier(scope=0,ident=f):0:3:f', - { - 'Arrow:0:4: ->', - children = { - 'PlainIdentifier(scope=0,ident=g):0:7: g', - }, - }, - }, - }, - }, - }, - 'Register(name=h):0:12:@h', - }, - }, - 'Register(name=i):0:16:@i', - }, - }, - }, - }, - }, - }, { - hl('Curly', '{'), - hl('NestingParenthesis', '('), - hl('Lambda', '{'), - hl('IdentifierName', 'f'), - hl('Arrow', '->', 1), - hl('IdentifierName', 'g', 1), - hl('Lambda', '}'), - hl('NestingParenthesis', ')'), - hl('CallingParenthesis', '('), - hl('Register', '@h'), - hl('CallingParenthesis', ')'), - hl('CallingParenthesis', '('), - hl('Register', '@i'), - hl('CallingParenthesis', ')'), - hl('Curly', '}'), - }) - check_parsing('a:{b()}c', { - -- 01234567 - ast = { - { - 'ComplexIdentifier:0:2:', - children = { - 'PlainIdentifier(scope=a,ident=):0:0:a:', - { - 'ComplexIdentifier:0:7:', - children = { - { - 'CurlyBracesIdentifier:0:2:{', - children = { - { - 'Call:0:4:(', - children = { - 'PlainIdentifier(scope=0,ident=b):0:3:b', - }, - }, - }, - }, - 'PlainIdentifier(scope=0,ident=c):0:7:c', - }, - }, - }, - }, - }, - }, { - hl('IdentifierScope', 'a'), - hl('IdentifierScopeDelimiter', ':'), - hl('Curly', '{'), - hl('IdentifierName', 'b'), - hl('CallingParenthesis', '('), - hl('CallingParenthesis', ')'), - hl('Curly', '}'), - hl('IdentifierName', 'c'), - }) - check_parsing('a:{{b, c -> @d + @e + ({f -> g})(@h)}(@i)}j', { - -- 01234567890123456789012345678901234567890123456 - -- 0 1 2 3 4 - ast = { - { - 'ComplexIdentifier:0:2:', - children = { - 'PlainIdentifier(scope=a,ident=):0:0:a:', - { - 'ComplexIdentifier:0:42:', - children = { - { - 'CurlyBracesIdentifier:0:2:{', - children = { - { - 'Call:0:37:(', - children = { - { - 'Lambda:0:3:{', - children = { - { - 'Comma:0:5:,', - children = { - 'PlainIdentifier(scope=0,ident=b):0:4:b', - 'PlainIdentifier(scope=0,ident=c):0:6: c', - }, - }, - { - 'Arrow:0:8: ->', - children = { - { - 'BinaryPlus:0:19: +', - children = { - { - 'BinaryPlus:0:14: +', - children = { - 'Register(name=d):0:11: @d', - 'Register(name=e):0:16: @e', - }, - }, - { - 'Call:0:32:(', - children = { - { - 'Nested:0:21: (', - children = { - { - 'Lambda:0:23:{', - children = { - 'PlainIdentifier(scope=0,ident=f):0:24:f', - { - 'Arrow:0:25: ->', - children = { - 'PlainIdentifier(scope=0,ident=g):0:28: g', - }, - }, - }, - }, - }, - }, - 'Register(name=h):0:33:@h', - }, - }, - }, - }, - }, - }, - }, - }, - 'Register(name=i):0:38:@i', - }, - }, - }, - }, - 'PlainIdentifier(scope=0,ident=j):0:42:j', - }, - }, - }, - }, - }, - }, { - hl('IdentifierScope', 'a'), - hl('IdentifierScopeDelimiter', ':'), - hl('Curly', '{'), - hl('Lambda', '{'), - hl('IdentifierName', 'b'), - hl('Comma', ','), - hl('IdentifierName', 'c', 1), - hl('Arrow', '->', 1), - hl('Register', '@d', 1), - hl('BinaryPlus', '+', 1), - hl('Register', '@e', 1), - hl('BinaryPlus', '+', 1), - hl('NestingParenthesis', '(', 1), - hl('Lambda', '{'), - hl('IdentifierName', 'f'), - hl('Arrow', '->', 1), - hl('IdentifierName', 'g', 1), - hl('Lambda', '}'), - hl('NestingParenthesis', ')'), - hl('CallingParenthesis', '('), - hl('Register', '@h'), - hl('CallingParenthesis', ')'), - hl('Lambda', '}'), - hl('CallingParenthesis', '('), - hl('Register', '@i'), - hl('CallingParenthesis', ')'), - hl('Curly', '}'), - hl('IdentifierName', 'j'), - }) - check_parsing('{@a + @b : @c + @d, @e + @f : @g + @i}', { - -- 01234567890123456789012345678901234567 - -- 0 1 2 3 - ast = { - { - 'DictLiteral:0:0:{', - children = { - { - 'Comma:0:18:,', - children = { - { - 'Colon:0:8: :', - children = { - { - 'BinaryPlus:0:3: +', - children = { - 'Register(name=a):0:1:@a', - 'Register(name=b):0:5: @b', - }, - }, - { - 'BinaryPlus:0:13: +', - children = { - 'Register(name=c):0:10: @c', - 'Register(name=d):0:15: @d', - }, - }, - }, - }, - { - 'Colon:0:27: :', - children = { - { - 'BinaryPlus:0:22: +', - children = { - 'Register(name=e):0:19: @e', - 'Register(name=f):0:24: @f', - }, - }, - { - 'BinaryPlus:0:32: +', - children = { - 'Register(name=g):0:29: @g', - 'Register(name=i):0:34: @i', - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, { - hl('Dict', '{'), - hl('Register', '@a'), - hl('BinaryPlus', '+', 1), - hl('Register', '@b', 1), - hl('Colon', ':', 1), - hl('Register', '@c', 1), - hl('BinaryPlus', '+', 1), - hl('Register', '@d', 1), - hl('Comma', ','), - hl('Register', '@e', 1), - hl('BinaryPlus', '+', 1), - hl('Register', '@f', 1), - hl('Colon', ':', 1), - hl('Register', '@g', 1), - hl('BinaryPlus', '+', 1), - hl('Register', '@i', 1), - hl('Dict', '}'), - }) - check_parsing('-> -> ->', { - -- 01234567 - ast = { - { - 'Arrow:0:0:->', - children = { - 'Missing:0:0:', - { - 'Arrow:0:2: ->', - children = { - 'Missing:0:2:', - { - 'Arrow:0:5: ->', - children = { - 'Missing:0:5:', - }, - }, - }, - }, - }, - }, - }, - err = { - arg = '-> -> ->', - msg = 'E15: Unexpected arrow: %.*s', - }, - }, { - hl('InvalidArrow', '->'), - hl('InvalidArrow', '->', 1), - hl('InvalidArrow', '->', 1), - }) - check_parsing('a -> b -> c -> d', { - -- 0123456789012345 - -- 0 1 - ast = { - { - 'Arrow:0:1: ->', - children = { - 'PlainIdentifier(scope=0,ident=a):0:0:a', - { - 'Arrow:0:6: ->', - children = { - 'PlainIdentifier(scope=0,ident=b):0:4: b', - { - 'Arrow:0:11: ->', - children = { - 'PlainIdentifier(scope=0,ident=c):0:9: c', - 'PlainIdentifier(scope=0,ident=d):0:14: d', - }, - }, - }, - }, - }, - }, - }, - err = { - arg = '-> b -> c -> d', - msg = 'E15: Arrow outside of lambda: %.*s', - }, - }, { - hl('IdentifierName', 'a'), - hl('InvalidArrow', '->', 1), - hl('IdentifierName', 'b', 1), - hl('InvalidArrow', '->', 1), - hl('IdentifierName', 'c', 1), - hl('InvalidArrow', '->', 1), - hl('IdentifierName', 'd', 1), - }) - check_parsing('{a -> b -> c}', { - -- 0123456789012 - -- 0 1 - ast = { - { - 'Lambda:0:0:{', - children = { - 'PlainIdentifier(scope=0,ident=a):0:1:a', - { - 'Arrow:0:2: ->', - children = { - { - 'Arrow:0:7: ->', - children = { - 'PlainIdentifier(scope=0,ident=b):0:5: b', - 'PlainIdentifier(scope=0,ident=c):0:10: c', - }, - }, - }, - }, - }, - }, - }, - err = { - arg = '-> c}', - msg = 'E15: Arrow outside of lambda: %.*s', - }, - }, { - hl('Lambda', '{'), - hl('IdentifierName', 'a'), - hl('Arrow', '->', 1), - hl('IdentifierName', 'b', 1), - hl('InvalidArrow', '->', 1), - hl('IdentifierName', 'c', 1), - hl('Lambda', '}'), - }) - check_parsing('{a: -> b}', { - -- 012345678 - ast = { - { - 'CurlyBracesIdentifier:0:0:{', - children = { - { - 'Arrow:0:3: ->', - children = { - 'PlainIdentifier(scope=a,ident=):0:1:a:', - 'PlainIdentifier(scope=0,ident=b):0:6: b', - }, - }, - }, - }, - }, - err = { - arg = '-> b}', - msg = 'E15: Arrow outside of lambda: %.*s', - }, - }, { - hl('Curly', '{'), - hl('IdentifierScope', 'a'), - hl('IdentifierScopeDelimiter', ':'), - hl('InvalidArrow', '->', 1), - hl('IdentifierName', 'b', 1), - hl('Curly', '}'), - }) - - check_parsing('{a:b -> b}', { - -- 0123456789 - ast = { - { - 'CurlyBracesIdentifier:0:0:{', - children = { - { - 'Arrow:0:4: ->', - children = { - 'PlainIdentifier(scope=a,ident=b):0:1:a:b', - 'PlainIdentifier(scope=0,ident=b):0:7: b', - }, - }, - }, - }, - }, - err = { - arg = '-> b}', - msg = 'E15: Arrow outside of lambda: %.*s', - }, - }, { - hl('Curly', '{'), - hl('IdentifierScope', 'a'), - hl('IdentifierScopeDelimiter', ':'), - hl('IdentifierName', 'b'), - hl('InvalidArrow', '->', 1), - hl('IdentifierName', 'b', 1), - hl('Curly', '}'), - }) - - check_parsing('{a#b -> b}', { - -- 0123456789 - ast = { - { - 'CurlyBracesIdentifier:0:0:{', - children = { - { - 'Arrow:0:4: ->', - children = { - 'PlainIdentifier(scope=0,ident=a#b):0:1:a#b', - 'PlainIdentifier(scope=0,ident=b):0:7: b', - }, - }, - }, - }, - }, - err = { - arg = '-> b}', - msg = 'E15: Arrow outside of lambda: %.*s', - }, - }, { - hl('Curly', '{'), - hl('IdentifierName', 'a#b'), - hl('InvalidArrow', '->', 1), - hl('IdentifierName', 'b', 1), - hl('Curly', '}'), - }) - check_parsing('{a : b : c}', { - -- 01234567890 - -- 0 1 - ast = { - { - 'DictLiteral:0:0:{', - children = { - { - 'Colon:0:2: :', - children = { - 'PlainIdentifier(scope=0,ident=a):0:1:a', - { - 'Colon:0:6: :', - children = { - 'PlainIdentifier(scope=0,ident=b):0:4: b', - 'PlainIdentifier(scope=0,ident=c):0:8: c', - }, - }, - }, - }, - }, - }, - }, - err = { - arg = ': c}', - msg = 'E15: Colon outside of dictionary or ternary operator: %.*s', - }, - }, { - hl('Dict', '{'), - hl('IdentifierName', 'a'), - hl('Colon', ':', 1), - hl('IdentifierName', 'b', 1), - hl('InvalidColon', ':', 1), - hl('IdentifierName', 'c', 1), - hl('Dict', '}'), - }) - check_parsing('{', { - -- 0 - ast = { - 'UnknownFigure:0:0:{', - }, - err = { - arg = '{', - msg = 'E15: Missing closing figure brace: %.*s', - }, - }, { - hl('FigureBrace', '{'), - }) - check_parsing('{a', { - -- 01 - ast = { - { - 'UnknownFigure:0:0:{', - children = { - 'PlainIdentifier(scope=0,ident=a):0:1:a', - }, - }, - }, - err = { - arg = '{a', - msg = 'E15: Missing closing figure brace: %.*s', - }, - }, { - hl('FigureBrace', '{'), - hl('IdentifierName', 'a'), - }) - check_parsing('{a,b', { - -- 0123 - ast = { - { - 'Lambda:0:0:{', - children = { - { - 'Comma:0:2:,', - children = { - 'PlainIdentifier(scope=0,ident=a):0:1:a', - 'PlainIdentifier(scope=0,ident=b):0:3:b', - }, - }, - }, - }, - }, - err = { - arg = '{a,b', - msg = 'E15: Missing closing figure brace for lambda: %.*s', - }, - }, { - hl('Lambda', '{'), - hl('IdentifierName', 'a'), - hl('Comma', ','), - hl('IdentifierName', 'b'), - }) - check_parsing('{a,b->', { - -- 012345 - ast = { - { - 'Lambda:0:0:{', - children = { - { - 'Comma:0:2:,', - children = { - 'PlainIdentifier(scope=0,ident=a):0:1:a', - 'PlainIdentifier(scope=0,ident=b):0:3:b', - }, - }, - 'Arrow:0:4:->', - }, - }, - }, - err = { - arg = '', - msg = 'E15: Expected value, got EOC: %.*s', - }, - }, { - hl('Lambda', '{'), - hl('IdentifierName', 'a'), - hl('Comma', ','), - hl('IdentifierName', 'b'), - hl('Arrow', '->'), - }) - check_parsing('{a,b->c', { - -- 0123456 - ast = { - { - 'Lambda:0:0:{', - children = { - { - 'Comma:0:2:,', - children = { - 'PlainIdentifier(scope=0,ident=a):0:1:a', - 'PlainIdentifier(scope=0,ident=b):0:3:b', - }, - }, - { - 'Arrow:0:4:->', - children = { - 'PlainIdentifier(scope=0,ident=c):0:6:c', - }, - }, - }, - }, - }, - err = { - arg = '{a,b->c', - msg = 'E15: Missing closing figure brace for lambda: %.*s', - }, - }, { - hl('Lambda', '{'), - hl('IdentifierName', 'a'), - hl('Comma', ','), - hl('IdentifierName', 'b'), - hl('Arrow', '->'), - hl('IdentifierName', 'c'), - }) - check_parsing('{a : b', { - -- 012345 - ast = { - { - 'DictLiteral:0:0:{', - children = { - { - 'Colon:0:2: :', - children = { - 'PlainIdentifier(scope=0,ident=a):0:1:a', - 'PlainIdentifier(scope=0,ident=b):0:4: b', - }, - }, - }, - }, - }, - err = { - arg = '{a : b', - msg = 'E723: Missing end of Dictionary \'}\': %.*s', - }, - }, { - hl('Dict', '{'), - hl('IdentifierName', 'a'), - hl('Colon', ':', 1), - hl('IdentifierName', 'b', 1), - }) - check_parsing('{a : b,', { - -- 0123456 - ast = { - { - 'DictLiteral:0:0:{', - children = { - { - 'Comma:0:6:,', - children = { - { - 'Colon:0:2: :', - children = { - 'PlainIdentifier(scope=0,ident=a):0:1:a', - 'PlainIdentifier(scope=0,ident=b):0:4: b', - }, - }, - }, - }, - }, - }, - }, - err = { - arg = '', - msg = 'E15: Expected value, got EOC: %.*s', - }, - }, { - hl('Dict', '{'), - hl('IdentifierName', 'a'), - hl('Colon', ':', 1), - hl('IdentifierName', 'b', 1), - hl('Comma', ','), - }) - end) - it('works with ternary operator', function() - check_parsing('a ? b : c', { - -- 012345678 - ast = { - { - 'Ternary:0:1: ?', - children = { - 'PlainIdentifier(scope=0,ident=a):0:0:a', - { - 'TernaryValue:0:5: :', - children = { - 'PlainIdentifier(scope=0,ident=b):0:3: b', - 'PlainIdentifier(scope=0,ident=c):0:7: c', - }, - }, - }, - }, - }, - }, { - hl('IdentifierName', 'a'), - hl('Ternary', '?', 1), - hl('IdentifierName', 'b', 1), - hl('TernaryColon', ':', 1), - hl('IdentifierName', 'c', 1), - }) - check_parsing('@a?@b?@c:@d:@e', { - -- 01234567890123 - -- 0 1 - ast = { - { - 'Ternary:0:2:?', - children = { - 'Register(name=a):0:0:@a', - { - 'TernaryValue:0:11::', - children = { - { - 'Ternary:0:5:?', - children = { - 'Register(name=b):0:3:@b', - { - 'TernaryValue:0:8::', - children = { - 'Register(name=c):0:6:@c', - 'Register(name=d):0:9:@d', - }, - }, - }, - }, - 'Register(name=e):0:12:@e', - }, - }, - }, - }, - }, - }, { - hl('Register', '@a'), - hl('Ternary', '?'), - hl('Register', '@b'), - hl('Ternary', '?'), - hl('Register', '@c'), - hl('TernaryColon', ':'), - hl('Register', '@d'), - hl('TernaryColon', ':'), - hl('Register', '@e'), - }) - check_parsing('@a?@b:@c?@d:@e', { - -- 01234567890123 - -- 0 1 - ast = { - { - 'Ternary:0:2:?', - children = { - 'Register(name=a):0:0:@a', - { - 'TernaryValue:0:5::', - children = { - 'Register(name=b):0:3:@b', - { - 'Ternary:0:8:?', - children = { - 'Register(name=c):0:6:@c', - { - 'TernaryValue:0:11::', - children = { - 'Register(name=d):0:9:@d', - 'Register(name=e):0:12:@e', - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, { - hl('Register', '@a'), - hl('Ternary', '?'), - hl('Register', '@b'), - hl('TernaryColon', ':'), - hl('Register', '@c'), - hl('Ternary', '?'), - hl('Register', '@d'), - hl('TernaryColon', ':'), - hl('Register', '@e'), - }) - check_parsing('@a?@b?@c?@d:@e?@f:@g:@h?@i:@j:@k', { - -- 01234567890123456789012345678901 - -- 0 1 2 3 - ast = { - { - 'Ternary:0:2:?', - children = { - 'Register(name=a):0:0:@a', - { - 'TernaryValue:0:29::', - children = { - { - 'Ternary:0:5:?', - children = { - 'Register(name=b):0:3:@b', - { - 'TernaryValue:0:20::', - children = { - { - 'Ternary:0:8:?', - children = { - 'Register(name=c):0:6:@c', - { - 'TernaryValue:0:11::', - children = { - 'Register(name=d):0:9:@d', - { - 'Ternary:0:14:?', - children = { - 'Register(name=e):0:12:@e', - { - 'TernaryValue:0:17::', - children = { - 'Register(name=f):0:15:@f', - 'Register(name=g):0:18:@g', - }, - }, - }, - }, - }, - }, - }, - }, - { - 'Ternary:0:23:?', - children = { - 'Register(name=h):0:21:@h', - { - 'TernaryValue:0:26::', - children = { - 'Register(name=i):0:24:@i', - 'Register(name=j):0:27:@j', - }, - }, - }, - }, - }, - }, - }, - }, - 'Register(name=k):0:30:@k', - }, - }, - }, - }, - }, - }, { - hl('Register', '@a'), - hl('Ternary', '?'), - hl('Register', '@b'), - hl('Ternary', '?'), - hl('Register', '@c'), - hl('Ternary', '?'), - hl('Register', '@d'), - hl('TernaryColon', ':'), - hl('Register', '@e'), - hl('Ternary', '?'), - hl('Register', '@f'), - hl('TernaryColon', ':'), - hl('Register', '@g'), - hl('TernaryColon', ':'), - hl('Register', '@h'), - hl('Ternary', '?'), - hl('Register', '@i'), - hl('TernaryColon', ':'), - hl('Register', '@j'), - hl('TernaryColon', ':'), - hl('Register', '@k'), - }) - check_parsing('?', { - -- 0 - ast = { - { - 'Ternary:0:0:?', - children = { - 'Missing:0:0:', - 'TernaryValue:0:0:?', - }, - }, - }, - err = { - arg = '?', - msg = 'E15: Expected value, got question mark: %.*s', - }, - }, { - hl('InvalidTernary', '?'), - }) - - check_parsing('?:', { - -- 01 - ast = { - { - 'Ternary:0:0:?', - children = { - 'Missing:0:0:', - { - 'TernaryValue:0:1::', - children = { - 'Missing:0:1:', - }, - }, - }, - }, - }, - err = { - arg = '?:', - msg = 'E15: Expected value, got question mark: %.*s', - }, - }, { - hl('InvalidTernary', '?'), - hl('InvalidTernaryColon', ':'), - }) - - check_parsing('?::', { - -- 012 - ast = { - { - 'Colon:0:2::', - children = { - { - 'Ternary:0:0:?', - children = { - 'Missing:0:0:', - { - 'TernaryValue:0:1::', - children = { - 'Missing:0:1:', - 'Missing:0:2:', - }, - }, - }, - }, - }, - }, - }, - err = { - arg = '?::', - msg = 'E15: Expected value, got question mark: %.*s', - }, - }, { - hl('InvalidTernary', '?'), - hl('InvalidTernaryColon', ':'), - hl('InvalidColon', ':'), - }) - - check_parsing('a?b', { - -- 012 - ast = { - { - 'Ternary:0:1:?', - children = { - 'PlainIdentifier(scope=0,ident=a):0:0:a', - { - 'TernaryValue:0:1:?', - children = { - 'PlainIdentifier(scope=0,ident=b):0:2:b', - }, - }, - }, - }, - }, - err = { - arg = '?b', - msg = 'E109: Missing \':\' after \'?\': %.*s', - }, - }, { - hl('IdentifierName', 'a'), - hl('Ternary', '?'), - hl('IdentifierName', 'b'), - }) - check_parsing('a?b:', { - -- 0123 - ast = { - { - 'Ternary:0:1:?', - children = { - 'PlainIdentifier(scope=0,ident=a):0:0:a', - { - 'TernaryValue:0:1:?', - children = { - 'PlainIdentifier(scope=b,ident=):0:2:b:', - }, - }, - }, - }, - }, - err = { - arg = '?b:', - msg = 'E109: Missing \':\' after \'?\': %.*s', - }, - }, { - hl('IdentifierName', 'a'), - hl('Ternary', '?'), - hl('IdentifierScope', 'b'), - hl('IdentifierScopeDelimiter', ':'), - }) - - check_parsing('a?b::c', { - -- 012345 - ast = { - { - 'Ternary:0:1:?', - children = { - 'PlainIdentifier(scope=0,ident=a):0:0:a', - { - 'TernaryValue:0:4::', - children = { - 'PlainIdentifier(scope=b,ident=):0:2:b:', - 'PlainIdentifier(scope=0,ident=c):0:5:c', - }, - }, - }, - }, - }, - }, { - hl('IdentifierName', 'a'), - hl('Ternary', '?'), - hl('IdentifierScope', 'b'), - hl('IdentifierScopeDelimiter', ':'), - hl('TernaryColon', ':'), - hl('IdentifierName', 'c'), - }) - - check_parsing('a?b :', { - -- 01234 - ast = { - { - 'Ternary:0:1:?', - children = { - 'PlainIdentifier(scope=0,ident=a):0:0:a', - { - 'TernaryValue:0:3: :', - children = { - 'PlainIdentifier(scope=0,ident=b):0:2:b', - }, - }, - }, - }, - }, - err = { - arg = '', - msg = 'E15: Expected value, got EOC: %.*s', - }, - }, { - hl('IdentifierName', 'a'), - hl('Ternary', '?'), - hl('IdentifierName', 'b'), - hl('TernaryColon', ':', 1), - }) - - check_parsing('(@a?@b:@c)?@d:@e', { - -- 0123456789012345 - -- 0 1 - ast = { - { - 'Ternary:0:10:?', - children = { - { - 'Nested:0:0:(', - children = { - { - 'Ternary:0:3:?', - children = { - 'Register(name=a):0:1:@a', - { - 'TernaryValue:0:6::', - children = { - 'Register(name=b):0:4:@b', - 'Register(name=c):0:7:@c', - }, - }, - }, - }, - }, - }, - { - 'TernaryValue:0:13::', - children = { - 'Register(name=d):0:11:@d', - 'Register(name=e):0:14:@e', - }, - }, - }, - }, - }, - }, { - hl('NestingParenthesis', '('), - hl('Register', '@a'), - hl('Ternary', '?'), - hl('Register', '@b'), - hl('TernaryColon', ':'), - hl('Register', '@c'), - hl('NestingParenthesis', ')'), - hl('Ternary', '?'), - hl('Register', '@d'), - hl('TernaryColon', ':'), - hl('Register', '@e'), - }) - - check_parsing('(@a?@b:@c)?(@d?@e:@f):(@g?@h:@i)', { - -- 01234567890123456789012345678901 - -- 0 1 2 3 - ast = { - { - 'Ternary:0:10:?', - children = { - { - 'Nested:0:0:(', - children = { - { - 'Ternary:0:3:?', - children = { - 'Register(name=a):0:1:@a', - { - 'TernaryValue:0:6::', - children = { - 'Register(name=b):0:4:@b', - 'Register(name=c):0:7:@c', - }, - }, - }, - }, - }, - }, - { - 'TernaryValue:0:21::', - children = { - { - 'Nested:0:11:(', - children = { - { - 'Ternary:0:14:?', - children = { - 'Register(name=d):0:12:@d', - { - 'TernaryValue:0:17::', - children = { - 'Register(name=e):0:15:@e', - 'Register(name=f):0:18:@f', - }, - }, - }, - }, - }, - }, - { - 'Nested:0:22:(', - children = { - { - 'Ternary:0:25:?', - children = { - 'Register(name=g):0:23:@g', - { - 'TernaryValue:0:28::', - children = { - 'Register(name=h):0:26:@h', - 'Register(name=i):0:29:@i', - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, { - hl('NestingParenthesis', '('), - hl('Register', '@a'), - hl('Ternary', '?'), - hl('Register', '@b'), - hl('TernaryColon', ':'), - hl('Register', '@c'), - hl('NestingParenthesis', ')'), - hl('Ternary', '?'), - hl('NestingParenthesis', '('), - hl('Register', '@d'), - hl('Ternary', '?'), - hl('Register', '@e'), - hl('TernaryColon', ':'), - hl('Register', '@f'), - hl('NestingParenthesis', ')'), - hl('TernaryColon', ':'), - hl('NestingParenthesis', '('), - hl('Register', '@g'), - hl('Ternary', '?'), - hl('Register', '@h'), - hl('TernaryColon', ':'), - hl('Register', '@i'), - hl('NestingParenthesis', ')'), - }) - - check_parsing('(@a?@b:@c)?@d?@e:@f:@g?@h:@i', { - -- 0123456789012345678901234567 - -- 0 1 2 - ast = { - { - 'Ternary:0:10:?', - children = { - { - 'Nested:0:0:(', - children = { - { - 'Ternary:0:3:?', - children = { - 'Register(name=a):0:1:@a', - { - 'TernaryValue:0:6::', - children = { - 'Register(name=b):0:4:@b', - 'Register(name=c):0:7:@c', - }, - }, - }, - }, - }, - }, - { - 'TernaryValue:0:19::', - children = { - { - 'Ternary:0:13:?', - children = { - 'Register(name=d):0:11:@d', - { - 'TernaryValue:0:16::', - children = { - 'Register(name=e):0:14:@e', - 'Register(name=f):0:17:@f', - }, - }, - }, - }, - { - 'Ternary:0:22:?', - children = { - 'Register(name=g):0:20:@g', - { - 'TernaryValue:0:25::', - children = { - 'Register(name=h):0:23:@h', - 'Register(name=i):0:26:@i', - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, { - hl('NestingParenthesis', '('), - hl('Register', '@a'), - hl('Ternary', '?'), - hl('Register', '@b'), - hl('TernaryColon', ':'), - hl('Register', '@c'), - hl('NestingParenthesis', ')'), - hl('Ternary', '?'), - hl('Register', '@d'), - hl('Ternary', '?'), - hl('Register', '@e'), - hl('TernaryColon', ':'), - hl('Register', '@f'), - hl('TernaryColon', ':'), - hl('Register', '@g'), - hl('Ternary', '?'), - hl('Register', '@h'), - hl('TernaryColon', ':'), - hl('Register', '@i'), - }) - check_parsing('a?b{cdef}g:h', { - -- 012345678901 - -- 0 1 - ast = { - { - 'Ternary:0:1:?', - children = { - 'PlainIdentifier(scope=0,ident=a):0:0:a', - { - 'TernaryValue:0:10::', - children = { - { - 'ComplexIdentifier:0:3:', - children = { - 'PlainIdentifier(scope=0,ident=b):0:2:b', - { - 'ComplexIdentifier:0:9:', - children = { - { - 'CurlyBracesIdentifier:0:3:{', - children = { - 'PlainIdentifier(scope=0,ident=cdef):0:4:cdef', - }, - }, - 'PlainIdentifier(scope=0,ident=g):0:9:g', - }, - }, - }, - }, - 'PlainIdentifier(scope=0,ident=h):0:11:h', - }, - }, - }, - }, - }, - }, { - hl('IdentifierName', 'a'), - hl('Ternary', '?'), - hl('IdentifierName', 'b'), - hl('Curly', '{'), - hl('IdentifierName', 'cdef'), - hl('Curly', '}'), - hl('IdentifierName', 'g'), - hl('TernaryColon', ':'), - hl('IdentifierName', 'h'), - }) - check_parsing('a ? b : c : d', { - -- 0123456789012 - -- 0 1 - ast = { - { - 'Colon:0:9: :', - children = { - { - 'Ternary:0:1: ?', - children = { - 'PlainIdentifier(scope=0,ident=a):0:0:a', - { - 'TernaryValue:0:5: :', - children = { - 'PlainIdentifier(scope=0,ident=b):0:3: b', - 'PlainIdentifier(scope=0,ident=c):0:7: c', - }, - }, - }, - }, - 'PlainIdentifier(scope=0,ident=d):0:11: d', - }, - }, - }, - err = { - arg = ': d', - msg = 'E15: Colon outside of dictionary or ternary operator: %.*s', - }, - }, { - hl('IdentifierName', 'a'), - hl('Ternary', '?', 1), - hl('IdentifierName', 'b', 1), - hl('TernaryColon', ':', 1), - hl('IdentifierName', 'c', 1), - hl('InvalidColon', ':', 1), - hl('IdentifierName', 'd', 1), - }) - end) - it('works with comparison operators', function() - check_parsing('a == b', { - -- 012345 - ast = { - { - 'Comparison(type=Equal,inv=0,ccs=UseOption):0:1: ==', - children = { - 'PlainIdentifier(scope=0,ident=a):0:0:a', - 'PlainIdentifier(scope=0,ident=b):0:4: b', - }, - }, - }, - }, { - hl('IdentifierName', 'a'), - hl('Comparison', '==', 1), - hl('IdentifierName', 'b', 1), - }) - - check_parsing('a ==? b', { - -- 0123456 - ast = { - { - 'Comparison(type=Equal,inv=0,ccs=IgnoreCase):0:1: ==?', - children = { - 'PlainIdentifier(scope=0,ident=a):0:0:a', - 'PlainIdentifier(scope=0,ident=b):0:5: b', - }, - }, - }, - }, { - hl('IdentifierName', 'a'), - hl('Comparison', '==', 1), - hl('ComparisonModifier', '?'), - hl('IdentifierName', 'b', 1), - }) - - check_parsing('a ==# b', { - -- 0123456 - ast = { - { - 'Comparison(type=Equal,inv=0,ccs=MatchCase):0:1: ==#', - children = { - 'PlainIdentifier(scope=0,ident=a):0:0:a', - 'PlainIdentifier(scope=0,ident=b):0:5: b', - }, - }, - }, - }, { - hl('IdentifierName', 'a'), - hl('Comparison', '==', 1), - hl('ComparisonModifier', '#'), - hl('IdentifierName', 'b', 1), - }) - - check_parsing('a !=# b', { - -- 0123456 - ast = { - { - 'Comparison(type=Equal,inv=1,ccs=MatchCase):0:1: !=#', - children = { - 'PlainIdentifier(scope=0,ident=a):0:0:a', - 'PlainIdentifier(scope=0,ident=b):0:5: b', - }, - }, - }, - }, { - hl('IdentifierName', 'a'), - hl('Comparison', '!=', 1), - hl('ComparisonModifier', '#'), - hl('IdentifierName', 'b', 1), - }) - - check_parsing('a <=# b', { - -- 0123456 - ast = { - { - 'Comparison(type=Greater,inv=1,ccs=MatchCase):0:1: <=#', - children = { - 'PlainIdentifier(scope=0,ident=a):0:0:a', - 'PlainIdentifier(scope=0,ident=b):0:5: b', - }, - }, - }, - }, { - hl('IdentifierName', 'a'), - hl('Comparison', '<=', 1), - hl('ComparisonModifier', '#'), - hl('IdentifierName', 'b', 1), - }) - - check_parsing('a >=# b', { - -- 0123456 - ast = { - { - 'Comparison(type=GreaterOrEqual,inv=0,ccs=MatchCase):0:1: >=#', - children = { - 'PlainIdentifier(scope=0,ident=a):0:0:a', - 'PlainIdentifier(scope=0,ident=b):0:5: b', - }, - }, - }, - }, { - hl('IdentifierName', 'a'), - hl('Comparison', '>=', 1), - hl('ComparisonModifier', '#'), - hl('IdentifierName', 'b', 1), - }) - - check_parsing('a ># b', { - -- 012345 - ast = { - { - 'Comparison(type=Greater,inv=0,ccs=MatchCase):0:1: >#', - children = { - 'PlainIdentifier(scope=0,ident=a):0:0:a', - 'PlainIdentifier(scope=0,ident=b):0:4: b', - }, - }, - }, - }, { - hl('IdentifierName', 'a'), - hl('Comparison', '>', 1), - hl('ComparisonModifier', '#'), - hl('IdentifierName', 'b', 1), - }) - - check_parsing('a <# b', { - -- 012345 - ast = { - { - 'Comparison(type=GreaterOrEqual,inv=1,ccs=MatchCase):0:1: <#', - children = { - 'PlainIdentifier(scope=0,ident=a):0:0:a', - 'PlainIdentifier(scope=0,ident=b):0:4: b', - }, - }, - }, - }, { - hl('IdentifierName', 'a'), - hl('Comparison', '<', 1), - hl('ComparisonModifier', '#'), - hl('IdentifierName', 'b', 1), - }) - - check_parsing('a is#b', { - -- 012345 - ast = { - { - 'Comparison(type=Identical,inv=0,ccs=MatchCase):0:1: is#', - children = { - 'PlainIdentifier(scope=0,ident=a):0:0:a', - 'PlainIdentifier(scope=0,ident=b):0:5:b', - }, - }, - }, - }, { - hl('IdentifierName', 'a'), - hl('Comparison', 'is', 1), - hl('ComparisonModifier', '#'), - hl('IdentifierName', 'b'), - }) - - check_parsing('a is?b', { - -- 012345 - ast = { - { - 'Comparison(type=Identical,inv=0,ccs=IgnoreCase):0:1: is?', - children = { - 'PlainIdentifier(scope=0,ident=a):0:0:a', - 'PlainIdentifier(scope=0,ident=b):0:5:b', - }, - }, - }, - }, { - hl('IdentifierName', 'a'), - hl('Comparison', 'is', 1), - hl('ComparisonModifier', '?'), - hl('IdentifierName', 'b'), - }) - - check_parsing('a isnot b', { - -- 012345678 - ast = { - { - 'Comparison(type=Identical,inv=1,ccs=UseOption):0:1: isnot', - children = { - 'PlainIdentifier(scope=0,ident=a):0:0:a', - 'PlainIdentifier(scope=0,ident=b):0:7: b', - }, - }, - }, - }, { - hl('IdentifierName', 'a'), - hl('Comparison', 'isnot', 1), - hl('IdentifierName', 'b', 1), - }) - - check_parsing('a < b < c', { - -- 012345678 - ast = { - { - 'Comparison(type=GreaterOrEqual,inv=1,ccs=UseOption):0:1: <', - children = { - 'PlainIdentifier(scope=0,ident=a):0:0:a', - { - 'Comparison(type=GreaterOrEqual,inv=1,ccs=UseOption):0:5: <', - children = { - 'PlainIdentifier(scope=0,ident=b):0:3: b', - 'PlainIdentifier(scope=0,ident=c):0:7: c', - }, - }, - }, - }, - }, - err = { - arg = ' < c', - msg = 'E15: Operator is not associative: %.*s', - }, - }, { - hl('IdentifierName', 'a'), - hl('Comparison', '<', 1), - hl('IdentifierName', 'b', 1), - hl('InvalidComparison', '<', 1), - hl('IdentifierName', 'c', 1), - }) - - check_parsing('a < b <# c', { - -- 012345678 - ast = { - { - 'Comparison(type=GreaterOrEqual,inv=1,ccs=UseOption):0:1: <', - children = { - 'PlainIdentifier(scope=0,ident=a):0:0:a', - { - 'Comparison(type=GreaterOrEqual,inv=1,ccs=MatchCase):0:5: <#', - children = { - 'PlainIdentifier(scope=0,ident=b):0:3: b', - 'PlainIdentifier(scope=0,ident=c):0:8: c', - }, - }, - }, - }, - }, - err = { - arg = ' <# c', - msg = 'E15: Operator is not associative: %.*s', - }, - }, { - hl('IdentifierName', 'a'), - hl('Comparison', '<', 1), - hl('IdentifierName', 'b', 1), - hl('InvalidComparison', '<', 1), - hl('InvalidComparisonModifier', '#'), - hl('IdentifierName', 'c', 1), - }) - - check_parsing('a += b', { - -- 012345 - ast = { - { - 'Comparison(type=Equal,inv=0,ccs=UseOption):0:3:=', - children = { - { - 'BinaryPlus:0:1: +', - children = { - 'PlainIdentifier(scope=0,ident=a):0:0:a', - 'Missing:0:3:', - }, - }, - 'PlainIdentifier(scope=0,ident=b):0:4: b', - }, - }, - }, - err = { - arg = '= b', - msg = 'E15: Expected == or =~: %.*s', - }, - }, { - hl('IdentifierName', 'a'), - hl('BinaryPlus', '+', 1), - hl('InvalidComparison', '='), - hl('IdentifierName', 'b', 1), - }) - check_parsing('a + b == c + d', { - -- 01234567890123 - -- 0 1 - ast = { - { - 'Comparison(type=Equal,inv=0,ccs=UseOption):0:5: ==', - children = { - { - 'BinaryPlus:0:1: +', - children = { - 'PlainIdentifier(scope=0,ident=a):0:0:a', - 'PlainIdentifier(scope=0,ident=b):0:3: b', - }, - }, - { - 'BinaryPlus:0:10: +', - children = { - 'PlainIdentifier(scope=0,ident=c):0:8: c', - 'PlainIdentifier(scope=0,ident=d):0:12: d', - }, - }, - }, - }, - }, - }, { - hl('IdentifierName', 'a'), - hl('BinaryPlus', '+', 1), - hl('IdentifierName', 'b', 1), - hl('Comparison', '==', 1), - hl('IdentifierName', 'c', 1), - hl('BinaryPlus', '+', 1), - hl('IdentifierName', 'd', 1), - }) - check_parsing('+ a == + b', { - -- 0123456789 - ast = { - { - 'Comparison(type=Equal,inv=0,ccs=UseOption):0:3: ==', - children = { - { - 'UnaryPlus:0:0:+', - children = { - 'PlainIdentifier(scope=0,ident=a):0:1: a', - }, - }, - { - 'UnaryPlus:0:6: +', - children = { - 'PlainIdentifier(scope=0,ident=b):0:8: b', - }, - }, - }, - }, - }, - }, { - hl('UnaryPlus', '+'), - hl('IdentifierName', 'a', 1), - hl('Comparison', '==', 1), - hl('UnaryPlus', '+', 1), - hl('IdentifierName', 'b', 1), - }) - end) - it('works with concat/subscript', function() - check_parsing('.', { - -- 0 - ast = { - { - 'ConcatOrSubscript:0:0:.', - children = { - 'Missing:0:0:', - }, - }, - }, - err = { - arg = '.', - msg = 'E15: Unexpected dot: %.*s', - }, - }, { - hl('InvalidConcatOrSubscript', '.'), - }) - - check_parsing('a.', { - -- 01 - ast = { - { - 'ConcatOrSubscript:0:1:.', - children = { - 'PlainIdentifier(scope=0,ident=a):0:0:a', - }, - }, - }, - err = { - arg = '', - msg = 'E15: Expected value, got EOC: %.*s', - }, - }, { - hl('IdentifierName', 'a'), - hl('ConcatOrSubscript', '.'), - }) - - check_parsing('a.b', { - -- 012 - ast = { - { - 'ConcatOrSubscript:0:1:.', - children = { - 'PlainIdentifier(scope=0,ident=a):0:0:a', - 'PlainKey(key=b):0:2:b', - }, - }, - }, - }, { - hl('IdentifierName', 'a'), - hl('ConcatOrSubscript', '.'), - hl('IdentifierKey', 'b'), - }) - - check_parsing('1.2', { - -- 012 - ast = { - 'Float(val=1.200000e+00):0:0:1.2', - }, - }, { - hl('Float', '1.2'), - }) - - check_parsing('1.2 + 1.3e-5', { - -- 012345678901 - -- 0 1 - ast = { - { - 'BinaryPlus:0:3: +', - children = { - 'Float(val=1.200000e+00):0:0:1.2', - 'Float(val=1.300000e-05):0:5: 1.3e-5', - }, - }, - }, - }, { - hl('Float', '1.2'), - hl('BinaryPlus', '+', 1), - hl('Float', '1.3e-5', 1), - }) - - check_parsing('a . 1.2 + 1.3e-5', { - -- 0123456789012345 - -- 0 1 - ast = { - { - 'BinaryPlus:0:7: +', - children = { - { - 'Concat:0:1: .', - children = { - 'PlainIdentifier(scope=0,ident=a):0:0:a', - { - 'ConcatOrSubscript:0:5:.', - children = { - 'Integer(val=1):0:3: 1', - 'PlainKey(key=2):0:6:2', - }, - }, - }, - }, - 'Float(val=1.300000e-05):0:9: 1.3e-5', - }, - }, - }, - }, { - hl('IdentifierName', 'a'), - hl('Concat', '.', 1), - hl('Number', '1', 1), - hl('ConcatOrSubscript', '.'), - hl('IdentifierKey', '2'), - hl('BinaryPlus', '+', 1), - hl('Float', '1.3e-5', 1), - }) - - check_parsing('1.3e-5 + 1.2 . a', { - -- 0123456789012345 - -- 0 1 - ast = { - { - 'Concat:0:12: .', - children = { - { - 'BinaryPlus:0:6: +', - children = { - 'Float(val=1.300000e-05):0:0:1.3e-5', - 'Float(val=1.200000e+00):0:8: 1.2', - }, - }, - 'PlainIdentifier(scope=0,ident=a):0:14: a', - }, - }, - }, - }, { - hl('Float', '1.3e-5'), - hl('BinaryPlus', '+', 1), - hl('Float', '1.2', 1), - hl('Concat', '.', 1), - hl('IdentifierName', 'a', 1), - }) - - check_parsing('1.3e-5 + a . 1.2', { - -- 0123456789012345 - -- 0 1 - ast = { - { - 'Concat:0:10: .', - children = { - { - 'BinaryPlus:0:6: +', - children = { - 'Float(val=1.300000e-05):0:0:1.3e-5', - 'PlainIdentifier(scope=0,ident=a):0:8: a', - }, - }, - { - 'ConcatOrSubscript:0:14:.', - children = { - 'Integer(val=1):0:12: 1', - 'PlainKey(key=2):0:15:2', - }, - }, - }, - }, - }, - }, { - hl('Float', '1.3e-5'), - hl('BinaryPlus', '+', 1), - hl('IdentifierName', 'a', 1), - hl('Concat', '.', 1), - hl('Number', '1', 1), - hl('ConcatOrSubscript', '.'), - hl('IdentifierKey', '2'), - }) - - check_parsing('1.2.3', { - -- 01234 - ast = { - { - 'ConcatOrSubscript:0:3:.', - children = { - { - 'ConcatOrSubscript:0:1:.', - children = { - 'Integer(val=1):0:0:1', - 'PlainKey(key=2):0:2:2', - }, - }, - 'PlainKey(key=3):0:4:3', - }, - }, - }, - }, { - hl('Number', '1'), - hl('ConcatOrSubscript', '.'), - hl('IdentifierKey', '2'), - hl('ConcatOrSubscript', '.'), - hl('IdentifierKey', '3'), - }) - - check_parsing('a.1.2', { - -- 01234 - ast = { - { - 'ConcatOrSubscript:0:3:.', - children = { - { - 'ConcatOrSubscript:0:1:.', - children = { - 'PlainIdentifier(scope=0,ident=a):0:0:a', - 'PlainKey(key=1):0:2:1', - }, - }, - 'PlainKey(key=2):0:4:2', - }, - }, - }, - }, { - hl('IdentifierName', 'a'), - hl('ConcatOrSubscript', '.'), - hl('IdentifierKey', '1'), - hl('ConcatOrSubscript', '.'), - hl('IdentifierKey', '2'), - }) - - check_parsing('a . 1.2', { - -- 0123456 - ast = { - { - 'Concat:0:1: .', - children = { - 'PlainIdentifier(scope=0,ident=a):0:0:a', - { - 'ConcatOrSubscript:0:5:.', - children = { - 'Integer(val=1):0:3: 1', - 'PlainKey(key=2):0:6:2', - }, - }, - }, - }, - }, - }, { - hl('IdentifierName', 'a'), - hl('Concat', '.', 1), - hl('Number', '1', 1), - hl('ConcatOrSubscript', '.'), - hl('IdentifierKey', '2'), - }) - - check_parsing('+a . +b', { - -- 0123456 - ast = { - { - 'Concat:0:2: .', - children = { - { - 'UnaryPlus:0:0:+', - children = { - 'PlainIdentifier(scope=0,ident=a):0:1:a', - }, - }, - { - 'UnaryPlus:0:4: +', - children = { - 'PlainIdentifier(scope=0,ident=b):0:6:b', - }, - }, - }, - }, - }, - }, { - hl('UnaryPlus', '+'), - hl('IdentifierName', 'a'), - hl('Concat', '.', 1), - hl('UnaryPlus', '+', 1), - hl('IdentifierName', 'b'), - }) - - check_parsing('a. b', { - -- 0123 - ast = { - { - 'Concat:0:1:.', - children = { - 'PlainIdentifier(scope=0,ident=a):0:0:a', - 'PlainIdentifier(scope=0,ident=b):0:2: b', - }, - }, - }, - }, { - hl('IdentifierName', 'a'), - hl('ConcatOrSubscript', '.'), - hl('IdentifierName', 'b', 1), - }) - - check_parsing('a. 1', { - -- 0123 - ast = { - { - 'Concat:0:1:.', - children = { - 'PlainIdentifier(scope=0,ident=a):0:0:a', - 'Integer(val=1):0:2: 1', - }, - }, - }, - }, { - hl('IdentifierName', 'a'), - hl('ConcatOrSubscript', '.'), - hl('Number', '1', 1), - }) - end) - it('works with bracket subscripts', function() - check_parsing(':', { - -- 0 - ast = { - { - 'Colon:0:0::', - children = { - 'Missing:0:0:', - }, - }, - }, - err = { - arg = ':', - msg = 'E15: Colon outside of dictionary or ternary operator: %.*s', - }, - }, { - hl('InvalidColon', ':'), - }) - check_parsing('a[]', { - -- 012 - ast = { - { - 'Subscript:0:1:[', - children = { - 'PlainIdentifier(scope=0,ident=a):0:0:a', - }, - }, - }, - err = { - arg = ']', - msg = 'E15: Expected value, got closing bracket: %.*s', - }, - }, { - hl('IdentifierName', 'a'), - hl('SubscriptBracket', '['), - hl('InvalidSubscriptBracket', ']'), - }) - check_parsing('a[b:]', { - -- 01234 - ast = { - { - 'Subscript:0:1:[', - children = { - 'PlainIdentifier(scope=0,ident=a):0:0:a', - 'PlainIdentifier(scope=b,ident=):0:2:b:', - }, - }, - }, - }, { - hl('IdentifierName', 'a'), - hl('SubscriptBracket', '['), - hl('IdentifierScope', 'b'), - hl('IdentifierScopeDelimiter', ':'), - hl('SubscriptBracket', ']'), - }) - - check_parsing('a[b:c]', { - -- 012345 - ast = { - { - 'Subscript:0:1:[', - children = { - 'PlainIdentifier(scope=0,ident=a):0:0:a', - 'PlainIdentifier(scope=b,ident=c):0:2:b:c', - }, - }, - }, - }, { - hl('IdentifierName', 'a'), - hl('SubscriptBracket', '['), - hl('IdentifierScope', 'b'), - hl('IdentifierScopeDelimiter', ':'), - hl('IdentifierName', 'c'), - hl('SubscriptBracket', ']'), - }) - check_parsing('a[b : c]', { - -- 01234567 - ast = { - { - 'Subscript:0:1:[', - children = { - 'PlainIdentifier(scope=0,ident=a):0:0:a', - { - 'Colon:0:3: :', - children = { - 'PlainIdentifier(scope=0,ident=b):0:2:b', - 'PlainIdentifier(scope=0,ident=c):0:5: c', - }, - }, - }, - }, - }, - }, { - hl('IdentifierName', 'a'), - hl('SubscriptBracket', '['), - hl('IdentifierName', 'b'), - hl('SubscriptColon', ':', 1), - hl('IdentifierName', 'c', 1), - hl('SubscriptBracket', ']'), - }) - - check_parsing('a[: b]', { - -- 012345 - ast = { - { - 'Subscript:0:1:[', - children = { - 'PlainIdentifier(scope=0,ident=a):0:0:a', - { - 'Colon:0:2::', - children = { - 'Missing:0:2:', - 'PlainIdentifier(scope=0,ident=b):0:3: b', - }, - }, - }, - }, - }, - }, { - hl('IdentifierName', 'a'), - hl('SubscriptBracket', '['), - hl('SubscriptColon', ':'), - hl('IdentifierName', 'b', 1), - hl('SubscriptBracket', ']'), - }) - - check_parsing('a[b :]', { - -- 012345 - ast = { - { - 'Subscript:0:1:[', - children = { - 'PlainIdentifier(scope=0,ident=a):0:0:a', - { - 'Colon:0:3: :', - children = { - 'PlainIdentifier(scope=0,ident=b):0:2:b', - }, - }, - }, - }, - }, - }, { - hl('IdentifierName', 'a'), - hl('SubscriptBracket', '['), - hl('IdentifierName', 'b'), - hl('SubscriptColon', ':', 1), - hl('SubscriptBracket', ']'), - }) - check_parsing('a[b][c][d](e)(f)(g)', { - -- 0123456789012345678 - -- 0 1 - ast = { - { - 'Call:0:16:(', - children = { - { - 'Call:0:13:(', - children = { - { - 'Call:0:10:(', - children = { - { - 'Subscript:0:7:[', - children = { - { - 'Subscript:0:4:[', - children = { - { - 'Subscript:0:1:[', - children = { - 'PlainIdentifier(scope=0,ident=a):0:0:a', - 'PlainIdentifier(scope=0,ident=b):0:2:b', - }, - }, - 'PlainIdentifier(scope=0,ident=c):0:5:c', - }, - }, - 'PlainIdentifier(scope=0,ident=d):0:8:d', - }, - }, - 'PlainIdentifier(scope=0,ident=e):0:11:e', - }, - }, - 'PlainIdentifier(scope=0,ident=f):0:14:f', - }, - }, - 'PlainIdentifier(scope=0,ident=g):0:17:g', - }, - }, - }, - }, { - hl('IdentifierName', 'a'), - hl('SubscriptBracket', '['), - hl('IdentifierName', 'b'), - hl('SubscriptBracket', ']'), - hl('SubscriptBracket', '['), - hl('IdentifierName', 'c'), - hl('SubscriptBracket', ']'), - hl('SubscriptBracket', '['), - hl('IdentifierName', 'd'), - hl('SubscriptBracket', ']'), - hl('CallingParenthesis', '('), - hl('IdentifierName', 'e'), - hl('CallingParenthesis', ')'), - hl('CallingParenthesis', '('), - hl('IdentifierName', 'f'), - hl('CallingParenthesis', ')'), - hl('CallingParenthesis', '('), - hl('IdentifierName', 'g'), - hl('CallingParenthesis', ')'), - }) - check_parsing('{a}{b}{c}[d][e][f]', { - -- 012345678901234567 - -- 0 1 - ast = { - { - 'Subscript:0:15:[', - children = { - { - 'Subscript:0:12:[', - children = { - { - 'Subscript:0:9:[', - children = { - { - 'ComplexIdentifier:0:3:', - children = { - { - 'CurlyBracesIdentifier:0:0:{', - children = { - 'PlainIdentifier(scope=0,ident=a):0:1:a', - }, - }, - { - 'ComplexIdentifier:0:6:', - children = { - { - 'CurlyBracesIdentifier:0:3:{', - children = { - 'PlainIdentifier(scope=0,ident=b):0:4:b', - }, - }, - { - 'CurlyBracesIdentifier:0:6:{', - children = { - 'PlainIdentifier(scope=0,ident=c):0:7:c', - }, - }, - }, - }, - }, - }, - 'PlainIdentifier(scope=0,ident=d):0:10:d', - }, - }, - 'PlainIdentifier(scope=0,ident=e):0:13:e', - }, - }, - 'PlainIdentifier(scope=0,ident=f):0:16:f', - }, - }, - }, - }, { - hl('Curly', '{'), - hl('IdentifierName', 'a'), - hl('Curly', '}'), - hl('Curly', '{'), - hl('IdentifierName', 'b'), - hl('Curly', '}'), - hl('Curly', '{'), - hl('IdentifierName', 'c'), - hl('Curly', '}'), - hl('SubscriptBracket', '['), - hl('IdentifierName', 'd'), - hl('SubscriptBracket', ']'), - hl('SubscriptBracket', '['), - hl('IdentifierName', 'e'), - hl('SubscriptBracket', ']'), - hl('SubscriptBracket', '['), - hl('IdentifierName', 'f'), - hl('SubscriptBracket', ']'), - }) - end) - it('supports list literals', function() - check_parsing('[]', { - -- 01 - ast = { - 'ListLiteral:0:0:[', - }, - }, { - hl('List', '['), - hl('List', ']'), - }) - - check_parsing('[a]', { - -- 012 - ast = { - { - 'ListLiteral:0:0:[', - children = { - 'PlainIdentifier(scope=0,ident=a):0:1:a', - }, - }, - }, - }, { - hl('List', '['), - hl('IdentifierName', 'a'), - hl('List', ']'), - }) - - check_parsing('[a, b]', { - -- 012345 - ast = { - { - 'ListLiteral:0:0:[', - children = { - { - 'Comma:0:2:,', - children = { - 'PlainIdentifier(scope=0,ident=a):0:1:a', - 'PlainIdentifier(scope=0,ident=b):0:3: b', - }, - }, - }, - }, - }, - }, { - hl('List', '['), - hl('IdentifierName', 'a'), - hl('Comma', ','), - hl('IdentifierName', 'b', 1), - hl('List', ']'), - }) - - check_parsing('[a, b, c]', { - -- 012345678 - ast = { - { - 'ListLiteral:0:0:[', - children = { - { - 'Comma:0:2:,', - children = { - 'PlainIdentifier(scope=0,ident=a):0:1:a', - { - 'Comma:0:5:,', - children = { - 'PlainIdentifier(scope=0,ident=b):0:3: b', - 'PlainIdentifier(scope=0,ident=c):0:6: c', - }, - }, - }, - }, - }, - }, - }, - }, { - hl('List', '['), - hl('IdentifierName', 'a'), - hl('Comma', ','), - hl('IdentifierName', 'b', 1), - hl('Comma', ','), - hl('IdentifierName', 'c', 1), - hl('List', ']'), - }) - - check_parsing('[a, b, c, ]', { - -- 01234567890 - -- 0 1 - ast = { - { - 'ListLiteral:0:0:[', - children = { - { - 'Comma:0:2:,', - children = { - 'PlainIdentifier(scope=0,ident=a):0:1:a', - { - 'Comma:0:5:,', - children = { - 'PlainIdentifier(scope=0,ident=b):0:3: b', - { - 'Comma:0:8:,', - children = { - 'PlainIdentifier(scope=0,ident=c):0:6: c', - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, { - hl('List', '['), - hl('IdentifierName', 'a'), - hl('Comma', ','), - hl('IdentifierName', 'b', 1), - hl('Comma', ','), - hl('IdentifierName', 'c', 1), - hl('Comma', ','), - hl('List', ']', 1), - }) - - check_parsing('[a : b, c : d]', { - -- 01234567890123 - -- 0 1 - ast = { - { - 'ListLiteral:0:0:[', - children = { - { - 'Comma:0:6:,', - children = { - { - 'Colon:0:2: :', - children = { - 'PlainIdentifier(scope=0,ident=a):0:1:a', - 'PlainIdentifier(scope=0,ident=b):0:4: b', - }, - }, - { - 'Colon:0:9: :', - children = { - 'PlainIdentifier(scope=0,ident=c):0:7: c', - 'PlainIdentifier(scope=0,ident=d):0:11: d', - }, - }, - }, - }, - }, - }, - }, - err = { - arg = ': b, c : d]', - msg = 'E15: Colon outside of dictionary or ternary operator: %.*s', - }, - }, { - hl('List', '['), - hl('IdentifierName', 'a'), - hl('InvalidColon', ':', 1), - hl('IdentifierName', 'b', 1), - hl('Comma', ','), - hl('IdentifierName', 'c', 1), - hl('InvalidColon', ':', 1), - hl('IdentifierName', 'd', 1), - hl('List', ']'), - }) - - check_parsing(']', { - -- 0 - ast = { - 'ListLiteral:0:0:', - }, - err = { - arg = ']', - msg = 'E15: Unexpected closing figure brace: %.*s', - }, - }, { - hl('InvalidList', ']'), - }) - - check_parsing('a]', { - -- 01 - ast = { - { - 'ListLiteral:0:1:', - children = { - 'PlainIdentifier(scope=0,ident=a):0:0:a', - }, - }, - }, - err = { - arg = ']', - msg = 'E15: Unexpected closing figure brace: %.*s', - }, - }, { - hl('IdentifierName', 'a'), - hl('InvalidList', ']'), - }) - - check_parsing('[] []', { - -- 01234 - ast = { - { - 'OpMissing:0:2:', - children = { - 'ListLiteral:0:0:[', - 'ListLiteral:0:2: [', - }, - }, - }, - err = { - arg = '[]', - msg = 'E15: Missing operator: %.*s', - }, - }, { - hl('List', '['), - hl('List', ']'), - hl('InvalidSpacing', ' '), - hl('List', '['), - hl('List', ']'), - }, { - [1] = { - ast = { - len = 3, - err = REMOVE_THIS, - ast = { - 'ListLiteral:0:0:[', - }, - }, - hl_fs = { - [3] = REMOVE_THIS, - [4] = REMOVE_THIS, - [5] = REMOVE_THIS, - }, - }, - }) - - check_parsing('[][]', { - -- 0123 - ast = { - { - 'Subscript:0:2:[', - children = { - 'ListLiteral:0:0:[', - }, - }, - }, - err = { - arg = ']', - msg = 'E15: Expected value, got closing bracket: %.*s', - }, - }, { - hl('List', '['), - hl('List', ']'), - hl('SubscriptBracket', '['), - hl('InvalidSubscriptBracket', ']'), - }) - - check_parsing('[', { - -- 0 - ast = { - 'ListLiteral:0:0:[', - }, - err = { - arg = '', - msg = 'E15: Expected value, got EOC: %.*s', - }, - }, { - hl('List', '['), - }) - - check_parsing('[1', { - -- 01 - ast = { - { - 'ListLiteral:0:0:[', - children = { - 'Integer(val=1):0:1:1', - }, - }, - }, - err = { - arg = '[1', - msg = 'E697: Missing end of List \']\': %.*s', - }, - }, { - hl('List', '['), - hl('Number', '1'), - }) - end) - it('works with strings', function() - check_parsing('\'abc\'', { - -- 01234 - ast = { - 'SingleQuotedString(val="abc"):0:0:\'abc\'', - }, - }, { - hl('SingleQuote', '\''), - hl('SingleQuotedBody', 'abc'), - hl('SingleQuote', '\''), - }) - check_parsing('"abc"', { - -- 01234 - ast = { - 'DoubleQuotedString(val="abc"):0:0:"abc"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedBody', 'abc'), - hl('DoubleQuote', '"'), - }) - check_parsing('\'\'', { - -- 01 - ast = { - 'SingleQuotedString(val=""):0:0:\'\'', - }, - }, { - hl('SingleQuote', '\''), - hl('SingleQuote', '\''), - }) - check_parsing('""', { - -- 01 - ast = { - 'DoubleQuotedString(val=""):0:0:""', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuote', '"'), - }) - check_parsing('"', { - -- 0 - ast = { - 'DoubleQuotedString(val=""):0:0:"', - }, - err = { - arg = '"', - msg = 'E114: Missing double quote: %.*s', - }, - }, { - hl('InvalidDoubleQuote', '"'), - }) - check_parsing('\'', { - -- 0 - ast = { - 'SingleQuotedString(val=""):0:0:\'', - }, - err = { - arg = '\'', - msg = 'E115: Missing single quote: %.*s', - }, - }, { - hl('InvalidSingleQuote', '\''), - }) - check_parsing('"a', { - -- 01 - ast = { - 'DoubleQuotedString(val="a"):0:0:"a', - }, - err = { - arg = '"a', - msg = 'E114: Missing double quote: %.*s', - }, - }, { - hl('InvalidDoubleQuote', '"'), - hl('InvalidDoubleQuotedBody', 'a'), - }) - check_parsing('\'a', { - -- 01 - ast = { - 'SingleQuotedString(val="a"):0:0:\'a', - }, - err = { - arg = '\'a', - msg = 'E115: Missing single quote: %.*s', - }, - }, { - hl('InvalidSingleQuote', '\''), - hl('InvalidSingleQuotedBody', 'a'), - }) - check_parsing('\'abc\'\'def\'', { - -- 0123456789 - ast = { - 'SingleQuotedString(val="abc\'def"):0:0:\'abc\'\'def\'', - }, - }, { - hl('SingleQuote', '\''), - hl('SingleQuotedBody', 'abc'), - hl('SingleQuotedQuote', '\'\''), - hl('SingleQuotedBody', 'def'), - hl('SingleQuote', '\''), - }) - check_parsing('\'abc\'\'', { - -- 012345 - ast = { - 'SingleQuotedString(val="abc\'"):0:0:\'abc\'\'', - }, - err = { - arg = '\'abc\'\'', - msg = 'E115: Missing single quote: %.*s', - }, - }, { - hl('InvalidSingleQuote', '\''), - hl('InvalidSingleQuotedBody', 'abc'), - hl('InvalidSingleQuotedQuote', '\'\''), - }) - check_parsing('\'\'\'\'\'\'\'\'', { - -- 01234567 - ast = { - 'SingleQuotedString(val="\'\'\'"):0:0:\'\'\'\'\'\'\'\'', - }, - }, { - hl('SingleQuote', '\''), - hl('SingleQuotedQuote', '\'\''), - hl('SingleQuotedQuote', '\'\''), - hl('SingleQuotedQuote', '\'\''), - hl('SingleQuote', '\''), - }) - check_parsing('\'\'\'a\'\'\'\'bc\'', { - -- 01234567890 - -- 0 1 - ast = { - 'SingleQuotedString(val="\'a\'\'bc"):0:0:\'\'\'a\'\'\'\'bc\'', - }, - }, { - hl('SingleQuote', '\''), - hl('SingleQuotedQuote', '\'\''), - hl('SingleQuotedBody', 'a'), - hl('SingleQuotedQuote', '\'\''), - hl('SingleQuotedQuote', '\'\''), - hl('SingleQuotedBody', 'bc'), - hl('SingleQuote', '\''), - }) - check_parsing('"\\"\\"\\"\\""', { - -- 0123456789 - ast = { - 'DoubleQuotedString(val="\\"\\"\\"\\""):0:0:"\\"\\"\\"\\""', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedEscape', '\\"'), - hl('DoubleQuotedEscape', '\\"'), - hl('DoubleQuotedEscape', '\\"'), - hl('DoubleQuotedEscape', '\\"'), - hl('DoubleQuote', '"'), - }) - check_parsing('"abc\\"def\\"ghi\\"jkl\\"mno"', { - -- 0123456789012345678901234 - -- 0 1 2 - ast = { - 'DoubleQuotedString(val="abc\\"def\\"ghi\\"jkl\\"mno"):0:0:"abc\\"def\\"ghi\\"jkl\\"mno"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedBody', 'abc'), - hl('DoubleQuotedEscape', '\\"'), - hl('DoubleQuotedBody', 'def'), - hl('DoubleQuotedEscape', '\\"'), - hl('DoubleQuotedBody', 'ghi'), - hl('DoubleQuotedEscape', '\\"'), - hl('DoubleQuotedBody', 'jkl'), - hl('DoubleQuotedEscape', '\\"'), - hl('DoubleQuotedBody', 'mno'), - hl('DoubleQuote', '"'), - }) - check_parsing('"\\b\\e\\f\\r\\t\\\\"', { - -- 0123456789012345 - -- 0 1 - ast = { - [[DoubleQuotedString(val="\8\27\12\13\9\\"):0:0:"\b\e\f\r\t\\"]], - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedEscape', '\\b'), - hl('DoubleQuotedEscape', '\\e'), - hl('DoubleQuotedEscape', '\\f'), - hl('DoubleQuotedEscape', '\\r'), - hl('DoubleQuotedEscape', '\\t'), - hl('DoubleQuotedEscape', '\\\\'), - hl('DoubleQuote', '"'), - }) - check_parsing('"\\n\n"', { - -- 01234 - ast = { - 'DoubleQuotedString(val="\\\n\\\n"):0:0:"\\n\n"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedEscape', '\\n'), - hl('DoubleQuotedBody', '\n'), - hl('DoubleQuote', '"'), - }) - check_parsing('"\\x00"', { - -- 012345 - ast = { - 'DoubleQuotedString(val="\\0"):0:0:"\\x00"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedEscape', '\\x00'), - hl('DoubleQuote', '"'), - }) - check_parsing('"\\xFF"', { - -- 012345 - ast = { - 'DoubleQuotedString(val="\255"):0:0:"\\xFF"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedEscape', '\\xFF'), - hl('DoubleQuote', '"'), - }) - check_parsing('"\\xF"', { - -- 012345 - ast = { - 'DoubleQuotedString(val="\\15"):0:0:"\\xF"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedEscape', '\\xF'), - hl('DoubleQuote', '"'), - }) - check_parsing('"\\u00AB"', { - -- 01234567 - ast = { - 'DoubleQuotedString(val="«"):0:0:"\\u00AB"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedEscape', '\\u00AB'), - hl('DoubleQuote', '"'), - }) - check_parsing('"\\U000000AB"', { - -- 01234567 - ast = { - 'DoubleQuotedString(val="«"):0:0:"\\U000000AB"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedEscape', '\\U000000AB'), - hl('DoubleQuote', '"'), - }) - check_parsing('"\\x"', { - -- 0123 - ast = { - 'DoubleQuotedString(val="x"):0:0:"\\x"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedUnknownEscape', '\\x'), - hl('DoubleQuote', '"'), - }) - - check_parsing('"\\x', { - -- 012 - ast = { - 'DoubleQuotedString(val="x"):0:0:"\\x', - }, - err = { - arg = '"\\x', - msg = 'E114: Missing double quote: %.*s', - }, - }, { - hl('InvalidDoubleQuote', '"'), - hl('InvalidDoubleQuotedUnknownEscape', '\\x'), - }) - - check_parsing('"\\xF', { - -- 0123 - ast = { - 'DoubleQuotedString(val="\\15"):0:0:"\\xF', - }, - err = { - arg = '"\\xF', - msg = 'E114: Missing double quote: %.*s', - }, - }, { - hl('InvalidDoubleQuote', '"'), - hl('InvalidDoubleQuotedEscape', '\\xF'), - }) - - check_parsing('"\\u"', { - -- 0123 - ast = { - 'DoubleQuotedString(val="u"):0:0:"\\u"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedUnknownEscape', '\\u'), - hl('DoubleQuote', '"'), - }) - - check_parsing('"\\u', { - -- 012 - ast = { - 'DoubleQuotedString(val="u"):0:0:"\\u', - }, - err = { - arg = '"\\u', - msg = 'E114: Missing double quote: %.*s', - }, - }, { - hl('InvalidDoubleQuote', '"'), - hl('InvalidDoubleQuotedUnknownEscape', '\\u'), - }) - - check_parsing('"\\U', { - -- 012 - ast = { - 'DoubleQuotedString(val="U"):0:0:"\\U', - }, - err = { - arg = '"\\U', - msg = 'E114: Missing double quote: %.*s', - }, - }, { - hl('InvalidDoubleQuote', '"'), - hl('InvalidDoubleQuotedUnknownEscape', '\\U'), - }) - - check_parsing('"\\U"', { - -- 0123 - ast = { - 'DoubleQuotedString(val="U"):0:0:"\\U"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedUnknownEscape', '\\U'), - hl('DoubleQuote', '"'), - }) - - check_parsing('"\\xFX"', { - -- 012345 - ast = { - 'DoubleQuotedString(val="\\15X"):0:0:"\\xFX"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedEscape', '\\xF'), - hl('DoubleQuotedBody', 'X'), - hl('DoubleQuote', '"'), - }) - - check_parsing('"\\XFX"', { - -- 012345 - ast = { - 'DoubleQuotedString(val="\\15X"):0:0:"\\XFX"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedEscape', '\\XF'), - hl('DoubleQuotedBody', 'X'), - hl('DoubleQuote', '"'), - }) - - check_parsing('"\\xX"', { - -- 01234 - ast = { - 'DoubleQuotedString(val="xX"):0:0:"\\xX"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedUnknownEscape', '\\x'), - hl('DoubleQuotedBody', 'X'), - hl('DoubleQuote', '"'), - }) - - check_parsing('"\\XX"', { - -- 01234 - ast = { - 'DoubleQuotedString(val="XX"):0:0:"\\XX"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedUnknownEscape', '\\X'), - hl('DoubleQuotedBody', 'X'), - hl('DoubleQuote', '"'), - }) - - check_parsing('"\\uX"', { - -- 01234 - ast = { - 'DoubleQuotedString(val="uX"):0:0:"\\uX"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedUnknownEscape', '\\u'), - hl('DoubleQuotedBody', 'X'), - hl('DoubleQuote', '"'), - }) - - check_parsing('"\\UX"', { - -- 01234 - ast = { - 'DoubleQuotedString(val="UX"):0:0:"\\UX"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedUnknownEscape', '\\U'), - hl('DoubleQuotedBody', 'X'), - hl('DoubleQuote', '"'), - }) - - check_parsing('"\\x0X"', { - -- 012345 - ast = { - 'DoubleQuotedString(val="\\0X"):0:0:"\\x0X"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedEscape', '\\x0'), - hl('DoubleQuotedBody', 'X'), - hl('DoubleQuote', '"'), - }) - - check_parsing('"\\X0X"', { - -- 012345 - ast = { - 'DoubleQuotedString(val="\\0X"):0:0:"\\X0X"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedEscape', '\\X0'), - hl('DoubleQuotedBody', 'X'), - hl('DoubleQuote', '"'), - }) - - check_parsing('"\\u0X"', { - -- 012345 - ast = { - 'DoubleQuotedString(val="\\0X"):0:0:"\\u0X"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedEscape', '\\u0'), - hl('DoubleQuotedBody', 'X'), - hl('DoubleQuote', '"'), - }) - - check_parsing('"\\U0X"', { - -- 012345 - ast = { - 'DoubleQuotedString(val="\\0X"):0:0:"\\U0X"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedEscape', '\\U0'), - hl('DoubleQuotedBody', 'X'), - hl('DoubleQuote', '"'), - }) - - check_parsing('"\\x00X"', { - -- 0123456 - ast = { - 'DoubleQuotedString(val="\\0X"):0:0:"\\x00X"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedEscape', '\\x00'), - hl('DoubleQuotedBody', 'X'), - hl('DoubleQuote', '"'), - }) - - check_parsing('"\\X00X"', { - -- 0123456 - ast = { - 'DoubleQuotedString(val="\\0X"):0:0:"\\X00X"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedEscape', '\\X00'), - hl('DoubleQuotedBody', 'X'), - hl('DoubleQuote', '"'), - }) - - check_parsing('"\\u00X"', { - -- 0123456 - ast = { - 'DoubleQuotedString(val="\\0X"):0:0:"\\u00X"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedEscape', '\\u00'), - hl('DoubleQuotedBody', 'X'), - hl('DoubleQuote', '"'), - }) - - check_parsing('"\\U00X"', { - -- 0123456 - ast = { - 'DoubleQuotedString(val="\\0X"):0:0:"\\U00X"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedEscape', '\\U00'), - hl('DoubleQuotedBody', 'X'), - hl('DoubleQuote', '"'), - }) - - check_parsing('"\\u000X"', { - -- 01234567 - ast = { - 'DoubleQuotedString(val="\\0X"):0:0:"\\u000X"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedEscape', '\\u000'), - hl('DoubleQuotedBody', 'X'), - hl('DoubleQuote', '"'), - }) - - check_parsing('"\\U000X"', { - -- 01234567 - ast = { - 'DoubleQuotedString(val="\\0X"):0:0:"\\U000X"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedEscape', '\\U000'), - hl('DoubleQuotedBody', 'X'), - hl('DoubleQuote', '"'), - }) - - check_parsing('"\\u0000X"', { - -- 012345678 - ast = { - 'DoubleQuotedString(val="\\0X"):0:0:"\\u0000X"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedEscape', '\\u0000'), - hl('DoubleQuotedBody', 'X'), - hl('DoubleQuote', '"'), - }) - - check_parsing('"\\U0000X"', { - -- 012345678 - ast = { - 'DoubleQuotedString(val="\\0X"):0:0:"\\U0000X"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedEscape', '\\U0000'), - hl('DoubleQuotedBody', 'X'), - hl('DoubleQuote', '"'), - }) - - check_parsing('"\\U00000X"', { - -- 0123456789 - ast = { - 'DoubleQuotedString(val="\\0X"):0:0:"\\U00000X"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedEscape', '\\U00000'), - hl('DoubleQuotedBody', 'X'), - hl('DoubleQuote', '"'), - }) - - check_parsing('"\\U000000X"', { - -- 01234567890 - -- 0 1 - ast = { - 'DoubleQuotedString(val="\\0X"):0:0:"\\U000000X"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedEscape', '\\U000000'), - hl('DoubleQuotedBody', 'X'), - hl('DoubleQuote', '"'), - }) - - check_parsing('"\\U0000000X"', { - -- 012345678901 - -- 0 1 - ast = { - 'DoubleQuotedString(val="\\0X"):0:0:"\\U0000000X"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedEscape', '\\U0000000'), - hl('DoubleQuotedBody', 'X'), - hl('DoubleQuote', '"'), - }) - - check_parsing('"\\U00000000X"', { - -- 0123456789012 - -- 0 1 - ast = { - 'DoubleQuotedString(val="\\0X"):0:0:"\\U00000000X"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedEscape', '\\U00000000'), - hl('DoubleQuotedBody', 'X'), - hl('DoubleQuote', '"'), - }) - - check_parsing('"\\x000X"', { - -- 01234567 - ast = { - 'DoubleQuotedString(val="\\0000X"):0:0:"\\x000X"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedEscape', '\\x00'), - hl('DoubleQuotedBody', '0X'), - hl('DoubleQuote', '"'), - }) - - check_parsing('"\\X000X"', { - -- 01234567 - ast = { - 'DoubleQuotedString(val="\\0000X"):0:0:"\\X000X"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedEscape', '\\X00'), - hl('DoubleQuotedBody', '0X'), - hl('DoubleQuote', '"'), - }) - - check_parsing('"\\u00000X"', { - -- 0123456789 - ast = { - 'DoubleQuotedString(val="\\0000X"):0:0:"\\u00000X"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedEscape', '\\u0000'), - hl('DoubleQuotedBody', '0X'), - hl('DoubleQuote', '"'), - }) - - check_parsing('"\\U000000000X"', { - -- 01234567890123 - -- 0 1 - ast = { - 'DoubleQuotedString(val="\\0000X"):0:0:"\\U000000000X"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedEscape', '\\U00000000'), - hl('DoubleQuotedBody', '0X'), - hl('DoubleQuote', '"'), - }) - - check_parsing('"\\0"', { - -- 0123 - ast = { - 'DoubleQuotedString(val="\\0"):0:0:"\\0"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedEscape', '\\0'), - hl('DoubleQuote', '"'), - }) - - check_parsing('"\\00"', { - -- 01234 - ast = { - 'DoubleQuotedString(val="\\0"):0:0:"\\00"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedEscape', '\\00'), - hl('DoubleQuote', '"'), - }) - - check_parsing('"\\000"', { - -- 012345 - ast = { - 'DoubleQuotedString(val="\\0"):0:0:"\\000"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedEscape', '\\000'), - hl('DoubleQuote', '"'), - }) - - check_parsing('"\\0000"', { - -- 0123456 - ast = { - 'DoubleQuotedString(val="\\0000"):0:0:"\\0000"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedEscape', '\\000'), - hl('DoubleQuotedBody', '0'), - hl('DoubleQuote', '"'), - }) - - check_parsing('"\\8"', { - -- 0123 - ast = { - 'DoubleQuotedString(val="8"):0:0:"\\8"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedUnknownEscape', '\\8'), - hl('DoubleQuote', '"'), - }) - - check_parsing('"\\08"', { - -- 01234 - ast = { - 'DoubleQuotedString(val="\\0008"):0:0:"\\08"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedEscape', '\\0'), - hl('DoubleQuotedBody', '8'), - hl('DoubleQuote', '"'), - }) - - check_parsing('"\\008"', { - -- 012345 - ast = { - 'DoubleQuotedString(val="\\0008"):0:0:"\\008"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedEscape', '\\00'), - hl('DoubleQuotedBody', '8'), - hl('DoubleQuote', '"'), - }) - - check_parsing('"\\0008"', { - -- 0123456 - ast = { - 'DoubleQuotedString(val="\\0008"):0:0:"\\0008"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedEscape', '\\000'), - hl('DoubleQuotedBody', '8'), - hl('DoubleQuote', '"'), - }) - - check_parsing('"\\777"', { - -- 012345 - ast = { - 'DoubleQuotedString(val="\255"):0:0:"\\777"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedEscape', '\\777'), - hl('DoubleQuote', '"'), - }) - - check_parsing('"\\050"', { - -- 012345 - ast = { - 'DoubleQuotedString(val="\40"):0:0:"\\050"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedEscape', '\\050'), - hl('DoubleQuote', '"'), - }) - - check_parsing('"\\"', { - -- 012345 - ast = { - 'DoubleQuotedString(val="\\21"):0:0:"\\"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedEscape', '\\'), - hl('DoubleQuote', '"'), - }) - - check_parsing('"\\<', { - -- 012 - ast = { - 'DoubleQuotedString(val="<"):0:0:"\\<', - }, - err = { - arg = '"\\<', - msg = 'E114: Missing double quote: %.*s', - }, - }, { - hl('InvalidDoubleQuote', '"'), - hl('InvalidDoubleQuotedUnknownEscape', '\\<'), - }) - - check_parsing('"\\<"', { - -- 0123 - ast = { - 'DoubleQuotedString(val="<"):0:0:"\\<"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedUnknownEscape', '\\<'), - hl('DoubleQuote', '"'), - }) - - check_parsing('"\\ 10 then print(digits2) end + if zdata.ast.len then + print((' len = %u,'):format(zdata.ast.len)) + end print(' ast = ' .. format_luav(zdata.ast.ast, ' ') .. ',') if zdata.ast.err then print(' err = {') @@ -414,14 +417,21 @@ eastnodelist2lua = function(pstate, eastnode, checked_nodes) return ret end -local function east2lua(pstate, east) +local function east2lua(str, pstate, east) local checked_nodes = {} + local len = tonumber(pstate.pos.col) + if pstate.pos.line == 1 then + len = tonumber(pstate.reader.lines.items[0].size) + end + if type(str) == 'string' and len == #str then + len = nil + end return { err = east.err.msg ~= nil and { msg = ffi.string(east.err.msg), - arg = ('%s'):format( - ffi.string(east.err.arg, east.err.arg_len)), + arg = ffi.string(east.err.arg, east.err.arg_len), } or nil, + len = len, ast = eastnodelist2lua(pstate, east.root, checked_nodes), } end @@ -463,7 +473,7 @@ describe('Expressions parser', function() local pstate = new_pstate({str}) local east = lib.viml_pexpr_parse(pstate, flags) - local ast = east2lua(pstate, east) + local ast = east2lua(str, pstate, east) local hls = phl2lua(pstate) if exp_ast == nil then format_check_data[flags] = {ast=ast, hl_fs=hls_to_hl_fs(hls)} @@ -509,15 +519,6 @@ describe('Expressions parser', function() format_check(str, format_check_data, opts) end end - local function check_parsing(...) - return _check_parsing({flags={0, 1, 2, 3}, funcname='check_parsing'}, ...) - end - local function check_asgn_parsing(...) - return _check_parsing({ - flags={4, 5, 6, 7}, - funcname='check_asgn_parsing', - }, ...) - end local function hl(group, str, shift) return function(next_col) if nvim_hl_defs['NVim' .. group] == nil then @@ -531,8152 +532,9 @@ describe('Expressions parser', function() str)), (col + #str) end end - itp('works with + and @a', function() - check_parsing('@a', { - ast = { - 'Register(name=a):0:0:@a', - }, - }, { - hl('Register', '@a'), - }) - check_parsing('+@a', { - ast = { - { - 'UnaryPlus:0:0:+', - children = { - 'Register(name=a):0:1:@a', - }, - }, - }, - }, { - hl('UnaryPlus', '+'), - hl('Register', '@a'), - }) - check_parsing('@a+@b', { - ast = { - { - 'BinaryPlus:0:2:+', - children = { - 'Register(name=a):0:0:@a', - 'Register(name=b):0:3:@b', - }, - }, - }, - }, { - hl('Register', '@a'), - hl('BinaryPlus', '+'), - hl('Register', '@b'), - }) - check_parsing('@a+@b+@c', { - ast = { - { - 'BinaryPlus:0:5:+', - children = { - { - 'BinaryPlus:0:2:+', - children = { - 'Register(name=a):0:0:@a', - 'Register(name=b):0:3:@b', - }, - }, - 'Register(name=c):0:6:@c', - }, - }, - }, - }, { - hl('Register', '@a'), - hl('BinaryPlus', '+'), - hl('Register', '@b'), - hl('BinaryPlus', '+'), - hl('Register', '@c'), - }) - check_parsing('+@a+@b', { - ast = { - { - 'BinaryPlus:0:3:+', - children = { - { - 'UnaryPlus:0:0:+', - children = { - 'Register(name=a):0:1:@a', - }, - }, - 'Register(name=b):0:4:@b', - }, - }, - }, - }, { - hl('UnaryPlus', '+'), - hl('Register', '@a'), - hl('BinaryPlus', '+'), - hl('Register', '@b'), - }) - check_parsing('+@a++@b', { - ast = { - { - 'BinaryPlus:0:3:+', - children = { - { - 'UnaryPlus:0:0:+', - children = { - 'Register(name=a):0:1:@a', - }, - }, - { - 'UnaryPlus:0:4:+', - children = { - 'Register(name=b):0:5:@b', - }, - }, - }, - }, - }, - }, { - hl('UnaryPlus', '+'), - hl('Register', '@a'), - hl('BinaryPlus', '+'), - hl('UnaryPlus', '+'), - hl('Register', '@b'), - }) - check_parsing('@a@b', { - ast = { - { - 'OpMissing:0:2:', - children = { - 'Register(name=a):0:0:@a', - 'Register(name=b):0:2:@b', - }, - }, - }, - err = { - arg = '@b', - msg = 'E15: Missing operator: %.*s', - }, - }, { - hl('Register', '@a'), - hl('InvalidRegister', '@b'), - }, { - [1] = { - ast = { - err = REMOVE_THIS, - ast = { - 'Register(name=a):0:0:@a' - }, - }, - hl_fs = { - [2] = REMOVE_THIS, - }, - }, - }) - check_parsing(' @a \t @b', { - ast = { - { - 'OpMissing:0:3:', - children = { - 'Register(name=a):0:0: @a', - 'Register(name=b):0:3: \t @b', - }, - }, - }, - err = { - arg = '@b', - msg = 'E15: Missing operator: %.*s', - }, - }, { - hl('Register', '@a', 1), - hl('InvalidSpacing', ' \t '), - hl('Register', '@b'), - }, { - [1] = { - ast = { - err = REMOVE_THIS, - ast = { - 'Register(name=a):0:0: @a' - }, - }, - hl_fs = { - [2] = REMOVE_THIS, - [3] = REMOVE_THIS, - }, - }, - }) - check_parsing('+', { - ast = { - 'UnaryPlus:0:0:+', - }, - err = { - arg = '', - msg = 'E15: Expected value, got EOC: %.*s', - }, - }, { - hl('UnaryPlus', '+'), - }) - check_parsing(' +', { - ast = { - 'UnaryPlus:0:0: +', - }, - err = { - arg = '', - msg = 'E15: Expected value, got EOC: %.*s', - }, - }, { - hl('UnaryPlus', '+', 1), - }) - check_parsing('@a+ ', { - ast = { - { - 'BinaryPlus:0:2:+', - children = { - 'Register(name=a):0:0:@a', - }, - }, - }, - err = { - arg = '', - msg = 'E15: Expected value, got EOC: %.*s', - }, - }, { - hl('Register', '@a'), - hl('BinaryPlus', '+'), - }) - end) - itp('works with @a, + and parenthesis', function() - check_parsing('(@a)', { - ast = { - { - 'Nested:0:0:(', - children = { - 'Register(name=a):0:1:@a', - }, - }, - }, - }, { - hl('NestingParenthesis', '('), - hl('Register', '@a'), - hl('NestingParenthesis', ')'), - }) - check_parsing('()', { - ast = { - { - 'Nested:0:0:(', - children = { - 'Missing:0:1:', - }, - }, - }, - err = { - arg = ')', - msg = 'E15: Expected value, got parenthesis: %.*s', - }, - }, { - hl('NestingParenthesis', '('), - hl('InvalidNestingParenthesis', ')'), - }) - check_parsing(')', { - ast = { - { - 'Nested:0:0:', - children = { - 'Missing:0:0:', - }, - }, - }, - err = { - arg = ')', - msg = 'E15: Expected value, got parenthesis: %.*s', - }, - }, { - hl('InvalidNestingParenthesis', ')'), - }) - check_parsing('+)', { - ast = { - { - 'Nested:0:1:', - children = { - { - 'UnaryPlus:0:0:+', - children = { - 'Missing:0:1:', - }, - }, - }, - }, - }, - err = { - arg = ')', - msg = 'E15: Expected value, got parenthesis: %.*s', - }, - }, { - hl('UnaryPlus', '+'), - hl('InvalidNestingParenthesis', ')'), - }) - check_parsing('+@a(@b)', { - ast = { - { - 'UnaryPlus:0:0:+', - children = { - { - 'Call:0:3:(', - children = { - 'Register(name=a):0:1:@a', - 'Register(name=b):0:4:@b', - }, - }, - }, - }, - }, - }, { - hl('UnaryPlus', '+'), - hl('Register', '@a'), - hl('CallingParenthesis', '('), - hl('Register', '@b'), - hl('CallingParenthesis', ')'), - }) - check_parsing('@a+@b(@c)', { - ast = { - { - 'BinaryPlus:0:2:+', - children = { - 'Register(name=a):0:0:@a', - { - 'Call:0:5:(', - children = { - 'Register(name=b):0:3:@b', - 'Register(name=c):0:6:@c', - }, - }, - }, - }, - }, - }, { - hl('Register', '@a'), - hl('BinaryPlus', '+'), - hl('Register', '@b'), - hl('CallingParenthesis', '('), - hl('Register', '@c'), - hl('CallingParenthesis', ')'), - }) - check_parsing('@a()', { - ast = { - { - 'Call:0:2:(', - children = { - 'Register(name=a):0:0:@a', - }, - }, - }, - }, { - hl('Register', '@a'), - hl('CallingParenthesis', '('), - hl('CallingParenthesis', ')'), - }) - check_parsing('@a ()', { - ast = { - { - 'OpMissing:0:2:', - children = { - 'Register(name=a):0:0:@a', - { - 'Nested:0:2: (', - children = { - 'Missing:0:4:', - }, - }, - }, - }, - }, - err = { - arg = '()', - msg = 'E15: Missing operator: %.*s', - }, - }, { - hl('Register', '@a'), - hl('InvalidSpacing', ' '), - hl('NestingParenthesis', '('), - hl('InvalidNestingParenthesis', ')'), - }, { - [1] = { - ast = { - err = REMOVE_THIS, - ast = { - 'Register(name=a):0:0:@a', - }, - }, - hl_fs = { - [2] = REMOVE_THIS, - [3] = REMOVE_THIS, - [4] = REMOVE_THIS, - }, - }, - }) - check_parsing('@a + (@b)', { - ast = { - { - 'BinaryPlus:0:2: +', - children = { - 'Register(name=a):0:0:@a', - { - 'Nested:0:4: (', - children = { - 'Register(name=b):0:6:@b', - }, - }, - }, - }, - }, - }, { - hl('Register', '@a'), - hl('BinaryPlus', '+', 1), - hl('NestingParenthesis', '(', 1), - hl('Register', '@b'), - hl('NestingParenthesis', ')'), - }) - check_parsing('@a + (+@b)', { - ast = { - { - 'BinaryPlus:0:2: +', - children = { - 'Register(name=a):0:0:@a', - { - 'Nested:0:4: (', - children = { - { - 'UnaryPlus:0:6:+', - children = { - 'Register(name=b):0:7:@b', - }, - }, - }, - }, - }, - }, - }, - }, { - hl('Register', '@a'), - hl('BinaryPlus', '+', 1), - hl('NestingParenthesis', '(', 1), - hl('UnaryPlus', '+'), - hl('Register', '@b'), - hl('NestingParenthesis', ')'), - }) - check_parsing('@a + (@b + @c)', { - ast = { - { - 'BinaryPlus:0:2: +', - children = { - 'Register(name=a):0:0:@a', - { - 'Nested:0:4: (', - children = { - { - 'BinaryPlus:0:8: +', - children = { - 'Register(name=b):0:6:@b', - 'Register(name=c):0:10: @c', - }, - }, - }, - }, - }, - }, - }, - }, { - hl('Register', '@a'), - hl('BinaryPlus', '+', 1), - hl('NestingParenthesis', '(', 1), - hl('Register', '@b'), - hl('BinaryPlus', '+', 1), - hl('Register', '@c', 1), - hl('NestingParenthesis', ')'), - }) - check_parsing('(@a)+@b', { - ast = { - { - 'BinaryPlus:0:4:+', - children = { - { - 'Nested:0:0:(', - children = { - 'Register(name=a):0:1:@a', - }, - }, - 'Register(name=b):0:5:@b', - }, - }, - }, - }, { - hl('NestingParenthesis', '('), - hl('Register', '@a'), - hl('NestingParenthesis', ')'), - hl('BinaryPlus', '+'), - hl('Register', '@b'), - }) - check_parsing('@a+(@b)(@c)', { - -- 01234567890 - ast = { - { - 'BinaryPlus:0:2:+', - children = { - 'Register(name=a):0:0:@a', - { - 'Call:0:7:(', - children = { - { - 'Nested:0:3:(', - children = { 'Register(name=b):0:4:@b' }, - }, - 'Register(name=c):0:8:@c', - }, - }, - }, - }, - }, - }, { - hl('Register', '@a'), - hl('BinaryPlus', '+'), - hl('NestingParenthesis', '('), - hl('Register', '@b'), - hl('NestingParenthesis', ')'), - hl('CallingParenthesis', '('), - hl('Register', '@c'), - hl('CallingParenthesis', ')'), - }) - check_parsing('@a+((@b))(@c)', { - -- 01234567890123456890123456789 - -- 0 1 2 - ast = { - { - 'BinaryPlus:0:2:+', - children = { - 'Register(name=a):0:0:@a', - { - 'Call:0:9:(', - children = { - { - 'Nested:0:3:(', - children = { - { - 'Nested:0:4:(', - children = { 'Register(name=b):0:5:@b' } - }, - }, - }, - 'Register(name=c):0:10:@c', - }, - }, - }, - }, - }, - }, { - hl('Register', '@a'), - hl('BinaryPlus', '+'), - hl('NestingParenthesis', '('), - hl('NestingParenthesis', '('), - hl('Register', '@b'), - hl('NestingParenthesis', ')'), - hl('NestingParenthesis', ')'), - hl('CallingParenthesis', '('), - hl('Register', '@c'), - hl('CallingParenthesis', ')'), - }) - check_parsing('@a+((@b))+@c', { - -- 01234567890123456890123456789 - -- 0 1 2 - ast = { - { - 'BinaryPlus:0:9:+', - children = { - { - 'BinaryPlus:0:2:+', - children = { - 'Register(name=a):0:0:@a', - { - 'Nested:0:3:(', - children = { - { - 'Nested:0:4:(', - children = { 'Register(name=b):0:5:@b' } - }, - }, - }, - }, - }, - 'Register(name=c):0:10:@c', - }, - }, - }, - }, { - hl('Register', '@a'), - hl('BinaryPlus', '+'), - hl('NestingParenthesis', '('), - hl('NestingParenthesis', '('), - hl('Register', '@b'), - hl('NestingParenthesis', ')'), - hl('NestingParenthesis', ')'), - hl('BinaryPlus', '+'), - hl('Register', '@c'), - }) - check_parsing( - '@a + (@b + @c) + @d(@e) + (+@f) + ((+@g(@h))(@j)(@k))(@l)', {--[[ - | | | | | | | | || | | || | | ||| || || || || - 000000000011111111112222222222333333333344444444445555555 - 012345678901234567890123456789012345678901234567890123456 - ]] - ast = {{ - 'BinaryPlus:0:31: +', - children = { - { - 'BinaryPlus:0:23: +', - children = { - { - 'BinaryPlus:0:14: +', - children = { - { - 'BinaryPlus:0:2: +', - children = { - 'Register(name=a):0:0:@a', - { - 'Nested:0:4: (', - children = { - { - 'BinaryPlus:0:8: +', - children = { - 'Register(name=b):0:6:@b', - 'Register(name=c):0:10: @c', - }, - }, - }, - }, - }, - }, - { - 'Call:0:19:(', - children = { - 'Register(name=d):0:16: @d', - 'Register(name=e):0:20:@e', - }, - }, - }, - }, - { - 'Nested:0:25: (', - children = { - { - 'UnaryPlus:0:27:+', - children = { - 'Register(name=f):0:28:@f', - }, - }, - }, - }, - }, - }, - { - 'Call:0:53:(', - children = { - { - 'Nested:0:33: (', - children = { - { - 'Call:0:48:(', - children = { - { - 'Call:0:44:(', - children = { - { - 'Nested:0:35:(', - children = { - { - 'UnaryPlus:0:36:+', - children = { - { - 'Call:0:39:(', - children = { - 'Register(name=g):0:37:@g', - 'Register(name=h):0:40:@h', - }, - }, - }, - }, - }, - }, - 'Register(name=j):0:45:@j', - }, - }, - 'Register(name=k):0:49:@k', - }, - }, - }, - }, - 'Register(name=l):0:54:@l', - }, - }, - }, - }}, - }, { - hl('Register', '@a'), - hl('BinaryPlus', '+', 1), - hl('NestingParenthesis', '(', 1), - hl('Register', '@b'), - hl('BinaryPlus', '+', 1), - hl('Register', '@c', 1), - hl('NestingParenthesis', ')'), - hl('BinaryPlus', '+', 1), - hl('Register', '@d', 1), - hl('CallingParenthesis', '('), - hl('Register', '@e'), - hl('CallingParenthesis', ')'), - hl('BinaryPlus', '+', 1), - hl('NestingParenthesis', '(', 1), - hl('UnaryPlus', '+'), - hl('Register', '@f'), - hl('NestingParenthesis', ')'), - hl('BinaryPlus', '+', 1), - hl('NestingParenthesis', '(', 1), - hl('NestingParenthesis', '('), - hl('UnaryPlus', '+'), - hl('Register', '@g'), - hl('CallingParenthesis', '('), - hl('Register', '@h'), - hl('CallingParenthesis', ')'), - hl('NestingParenthesis', ')'), - hl('CallingParenthesis', '('), - hl('Register', '@j'), - hl('CallingParenthesis', ')'), - hl('CallingParenthesis', '('), - hl('Register', '@k'), - hl('CallingParenthesis', ')'), - hl('NestingParenthesis', ')'), - hl('CallingParenthesis', '('), - hl('Register', '@l'), - hl('CallingParenthesis', ')'), - }) - check_parsing('@a)', { - -- 012 - ast = { - { - 'Nested:0:2:', - children = { - 'Register(name=a):0:0:@a', - }, - }, - }, - err = { - arg = ')', - msg = 'E15: Unexpected closing parenthesis: %.*s', - }, - }, { - hl('Register', '@a'), - hl('InvalidNestingParenthesis', ')'), - }) - check_parsing('(@a', { - -- 012 - ast = { - { - 'Nested:0:0:(', - children = { - 'Register(name=a):0:1:@a', - }, - }, - }, - err = { - arg = '(@a', - msg = 'E110: Missing closing parenthesis for nested expression: %.*s', - }, - }, { - hl('NestingParenthesis', '('), - hl('Register', '@a'), - }) - check_parsing('@a(@b', { - -- 01234 - ast = { - { - 'Call:0:2:(', - children = { - 'Register(name=a):0:0:@a', - 'Register(name=b):0:3:@b', - }, - }, - }, - err = { - arg = '(@b', - msg = 'E116: Missing closing parenthesis for function call: %.*s', - }, - }, { - hl('Register', '@a'), - hl('CallingParenthesis', '('), - hl('Register', '@b'), - }) - check_parsing('@a(@b, @c, @d, @e)', { - -- 012345678901234567 - -- 0 1 - ast = { - { - 'Call:0:2:(', - children = { - 'Register(name=a):0:0:@a', - { - 'Comma:0:5:,', - children = { - 'Register(name=b):0:3:@b', - { - 'Comma:0:9:,', - children = { - 'Register(name=c):0:6: @c', - { - 'Comma:0:13:,', - children = { - 'Register(name=d):0:10: @d', - 'Register(name=e):0:14: @e', - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, { - hl('Register', '@a'), - hl('CallingParenthesis', '('), - hl('Register', '@b'), - hl('Comma', ','), - hl('Register', '@c', 1), - hl('Comma', ','), - hl('Register', '@d', 1), - hl('Comma', ','), - hl('Register', '@e', 1), - hl('CallingParenthesis', ')'), - }) - check_parsing('@a(@b(@c))', { - -- 01234567890123456789012345678901234567 - -- 0 1 2 3 - ast = { - { - 'Call:0:2:(', - children = { - 'Register(name=a):0:0:@a', - { - 'Call:0:5:(', - children = { - 'Register(name=b):0:3:@b', - 'Register(name=c):0:6:@c', - }, - }, - }, - }, - }, - }, { - hl('Register', '@a'), - hl('CallingParenthesis', '('), - hl('Register', '@b'), - hl('CallingParenthesis', '('), - hl('Register', '@c'), - hl('CallingParenthesis', ')'), - hl('CallingParenthesis', ')'), - }) - check_parsing('@a(@b(@c(@d(@e), @f(@g(@h), @i(@j)))))', { - -- 01234567890123456789012345678901234567 - -- 0 1 2 3 - ast = { - { - 'Call:0:2:(', - children = { - 'Register(name=a):0:0:@a', - { - 'Call:0:5:(', - children = { - 'Register(name=b):0:3:@b', - { - 'Call:0:8:(', - children = { - 'Register(name=c):0:6:@c', - { - 'Comma:0:15:,', - children = { - { - 'Call:0:11:(', - children = { - 'Register(name=d):0:9:@d', - 'Register(name=e):0:12:@e', - }, - }, - { - 'Call:0:19:(', - children = { - 'Register(name=f):0:16: @f', - { - 'Comma:0:26:,', - children = { - { - 'Call:0:22:(', - children = { - 'Register(name=g):0:20:@g', - 'Register(name=h):0:23:@h', - }, - }, - { - 'Call:0:30:(', - children = { - 'Register(name=i):0:27: @i', - 'Register(name=j):0:31:@j', - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, { - hl('Register', '@a'), - hl('CallingParenthesis', '('), - hl('Register', '@b'), - hl('CallingParenthesis', '('), - hl('Register', '@c'), - hl('CallingParenthesis', '('), - hl('Register', '@d'), - hl('CallingParenthesis', '('), - hl('Register', '@e'), - hl('CallingParenthesis', ')'), - hl('Comma', ','), - hl('Register', '@f', 1), - hl('CallingParenthesis', '('), - hl('Register', '@g'), - hl('CallingParenthesis', '('), - hl('Register', '@h'), - hl('CallingParenthesis', ')'), - hl('Comma', ','), - hl('Register', '@i', 1), - hl('CallingParenthesis', '('), - hl('Register', '@j'), - hl('CallingParenthesis', ')'), - hl('CallingParenthesis', ')'), - hl('CallingParenthesis', ')'), - hl('CallingParenthesis', ')'), - hl('CallingParenthesis', ')'), - }) - check_parsing('()()', { - -- 0123 - ast = { - { - 'Call:0:2:(', - children = { - { - 'Nested:0:0:(', - children = { - 'Missing:0:1:', - }, - }, - }, - }, - }, - err = { - arg = ')()', - msg = 'E15: Expected value, got parenthesis: %.*s', - }, - }, { - hl('NestingParenthesis', '('), - hl('InvalidNestingParenthesis', ')'), - hl('CallingParenthesis', '('), - hl('CallingParenthesis', ')'), - }) - check_parsing('(@a)()', { - -- 012345 - ast = { - { - 'Call:0:4:(', - children = { - { - 'Nested:0:0:(', - children = { - 'Register(name=a):0:1:@a', - }, - }, - }, - }, - }, - }, { - hl('NestingParenthesis', '('), - hl('Register', '@a'), - hl('NestingParenthesis', ')'), - hl('CallingParenthesis', '('), - hl('CallingParenthesis', ')'), - }) - check_parsing('(@a)(@b)', { - -- 01234567 - ast = { - { - 'Call:0:4:(', - children = { - { - 'Nested:0:0:(', - children = { - 'Register(name=a):0:1:@a', - }, - }, - 'Register(name=b):0:5:@b', - }, - }, - }, - }, { - hl('NestingParenthesis', '('), - hl('Register', '@a'), - hl('NestingParenthesis', ')'), - hl('CallingParenthesis', '('), - hl('Register', '@b'), - hl('CallingParenthesis', ')'), - }) - check_parsing('(@a) (@b)', { - -- 012345678 - ast = { - { - 'OpMissing:0:4:', - children = { - { - 'Nested:0:0:(', - children = { - 'Register(name=a):0:1:@a', - }, - }, - { - 'Nested:0:4: (', - children = { - 'Register(name=b):0:6:@b', - }, - }, - }, - }, - }, - err = { - arg = '(@b)', - msg = 'E15: Missing operator: %.*s', - }, - }, { - hl('NestingParenthesis', '('), - hl('Register', '@a'), - hl('NestingParenthesis', ')'), - hl('InvalidSpacing', ' '), - hl('NestingParenthesis', '('), - hl('Register', '@b'), - hl('NestingParenthesis', ')'), - }, { - [1] = { - ast = { - ast = { - { - 'Nested:0:0:(', - children = { - 'Register(name=a):0:1:@a', - REMOVE_THIS, - }, - }, - }, - err = REMOVE_THIS, - }, - hl_fs = { - [4] = REMOVE_THIS, - [5] = REMOVE_THIS, - [6] = REMOVE_THIS, - [7] = REMOVE_THIS, - }, - }, - }) - end) - itp('works with variable names, including curly braces ones', function() - check_parsing('var', { - ast = { - 'PlainIdentifier(scope=0,ident=var):0:0:var', - }, - }, { - hl('IdentifierName', 'var'), - }) - check_parsing('g:var', { - ast = { - 'PlainIdentifier(scope=g,ident=var):0:0:g:var', - }, - }, { - hl('IdentifierScope', 'g'), - hl('IdentifierScopeDelimiter', ':'), - hl('IdentifierName', 'var'), - }) - check_parsing('g:', { - ast = { - 'PlainIdentifier(scope=g,ident=):0:0:g:', - }, - }, { - hl('IdentifierScope', 'g'), - hl('IdentifierScopeDelimiter', ':'), - }) - check_parsing('{a}', { - -- 012 - ast = { - { - 'CurlyBracesIdentifier(-di):0:0:{', - children = { - 'PlainIdentifier(scope=0,ident=a):0:1:a', - }, - }, - }, - }, { - hl('Curly', '{'), - hl('IdentifierName', 'a'), - hl('Curly', '}'), - }) - check_parsing('{a:b}', { - -- 012 - ast = { - { - 'CurlyBracesIdentifier(-di):0:0:{', - children = { - 'PlainIdentifier(scope=a,ident=b):0:1:a:b', - }, - }, - }, - }, { - hl('Curly', '{'), - hl('IdentifierScope', 'a'), - hl('IdentifierScopeDelimiter', ':'), - hl('IdentifierName', 'b'), - hl('Curly', '}'), - }) - check_parsing('{a:@b}', { - -- 012345 - ast = { - { - 'CurlyBracesIdentifier(-di):0:0:{', - children = { - { - 'OpMissing:0:3:', - children={ - 'PlainIdentifier(scope=a,ident=):0:1:a:', - 'Register(name=b):0:3:@b', - }, - }, - }, - }, - }, - err = { - arg = '@b}', - msg = 'E15: Missing operator: %.*s', - }, - }, { - hl('Curly', '{'), - hl('IdentifierScope', 'a'), - hl('IdentifierScopeDelimiter', ':'), - hl('InvalidRegister', '@b'), - hl('Curly', '}'), - }) - check_parsing('{@a}', { - ast = { - { - 'CurlyBracesIdentifier(-di):0:0:{', - children = { - 'Register(name=a):0:1:@a', - }, - }, - }, - }, { - hl('Curly', '{'), - hl('Register', '@a'), - hl('Curly', '}'), - }) - check_parsing('{@a}{@b}', { - -- 01234567 - ast = { - { - 'ComplexIdentifier:0:4:', - children = { - { - 'CurlyBracesIdentifier(-di):0:0:{', - children = { - 'Register(name=a):0:1:@a', - }, - }, - { - 'CurlyBracesIdentifier(--i):0:4:{', - children = { - 'Register(name=b):0:5:@b', - }, - }, - }, - }, - }, - }, { - hl('Curly', '{'), - hl('Register', '@a'), - hl('Curly', '}'), - hl('Curly', '{'), - hl('Register', '@b'), - hl('Curly', '}'), - }) - check_parsing('g:{@a}', { - -- 01234567 - ast = { - { - 'ComplexIdentifier:0:2:', - children = { - 'PlainIdentifier(scope=g,ident=):0:0:g:', - { - 'CurlyBracesIdentifier(--i):0:2:{', - children = { - 'Register(name=a):0:3:@a', - }, - }, - }, - }, - }, - }, { - hl('IdentifierScope', 'g'), - hl('IdentifierScopeDelimiter', ':'), - hl('Curly', '{'), - hl('Register', '@a'), - hl('Curly', '}'), - }) - check_parsing('{@a}_test', { - -- 012345678 - ast = { - { - 'ComplexIdentifier:0:4:', - children = { - { - 'CurlyBracesIdentifier(-di):0:0:{', - children = { - 'Register(name=a):0:1:@a', - }, - }, - 'PlainIdentifier(scope=0,ident=_test):0:4:_test', - }, - }, - }, - }, { - hl('Curly', '{'), - hl('Register', '@a'), - hl('Curly', '}'), - hl('IdentifierName', '_test'), - }) - check_parsing('g:{@a}_test', { - -- 01234567890 - ast = { - { - 'ComplexIdentifier:0:2:', - children = { - 'PlainIdentifier(scope=g,ident=):0:0:g:', - { - 'ComplexIdentifier:0:6:', - children = { - { - 'CurlyBracesIdentifier(--i):0:2:{', - children = { - 'Register(name=a):0:3:@a', - }, - }, - 'PlainIdentifier(scope=0,ident=_test):0:6:_test', - }, - }, - }, - }, - }, - }, { - hl('IdentifierScope', 'g'), - hl('IdentifierScopeDelimiter', ':'), - hl('Curly', '{'), - hl('Register', '@a'), - hl('Curly', '}'), - hl('IdentifierName', '_test'), - }) - check_parsing('g:{@a}_test()', { - -- 0123456789012 - ast = { - { - 'Call:0:11:(', - children = { - { - 'ComplexIdentifier:0:2:', - children = { - 'PlainIdentifier(scope=g,ident=):0:0:g:', - { - 'ComplexIdentifier:0:6:', - children = { - { - 'CurlyBracesIdentifier(--i):0:2:{', - children = { - 'Register(name=a):0:3:@a', - }, - }, - 'PlainIdentifier(scope=0,ident=_test):0:6:_test', - }, - }, - }, - }, - }, - }, - }, - }, { - hl('IdentifierScope', 'g'), - hl('IdentifierScopeDelimiter', ':'), - hl('Curly', '{'), - hl('Register', '@a'), - hl('Curly', '}'), - hl('IdentifierName', '_test'), - hl('CallingParenthesis', '('), - hl('CallingParenthesis', ')'), - }) - check_parsing('{@a} ()', { - -- 0123456789012 - ast = { - { - 'Call:0:4: (', - children = { - { - 'CurlyBracesIdentifier(-di):0:0:{', - children = { - 'Register(name=a):0:1:@a', - }, - }, - }, - }, - }, - }, { - hl('Curly', '{'), - hl('Register', '@a'), - hl('Curly', '}'), - hl('CallingParenthesis', '(', 1), - hl('CallingParenthesis', ')'), - }) - check_parsing('g:{@a} ()', { - -- 0123456789012 - ast = { - { - 'Call:0:6: (', - children = { - { - 'ComplexIdentifier:0:2:', - children = { - 'PlainIdentifier(scope=g,ident=):0:0:g:', - { - 'CurlyBracesIdentifier(--i):0:2:{', - children = { - 'Register(name=a):0:3:@a', - }, - }, - }, - }, - }, - }, - }, - }, { - hl('IdentifierScope', 'g'), - hl('IdentifierScopeDelimiter', ':'), - hl('Curly', '{'), - hl('Register', '@a'), - hl('Curly', '}'), - hl('CallingParenthesis', '(', 1), - hl('CallingParenthesis', ')'), - }) - check_parsing('{@a', { - -- 012 - ast = { - { - 'UnknownFigure(-di):0:0:{', - children = { - 'Register(name=a):0:1:@a', - }, - }, - }, - err = { - arg = '{@a', - msg = 'E15: Missing closing figure brace: %.*s', - }, - }, { - hl('FigureBrace', '{'), - hl('Register', '@a'), - }) - check_parsing('a ()', { - -- 0123 - ast = { - { - 'Call:0:1: (', - children = { - 'PlainIdentifier(scope=0,ident=a):0:0:a', - }, - }, - }, - }, { - hl('IdentifierName', 'a'), - hl('CallingParenthesis', '(', 1), - hl('CallingParenthesis', ')'), - }) - end) - itp('works with lambdas and dictionaries', function() - check_parsing('{}', { - ast = { - 'DictLiteral(-di):0:0:{', - }, - }, { - hl('Dict', '{'), - hl('Dict', '}'), - }) - check_parsing('{->@a}', { - ast = { - { - 'Lambda(\\di):0:0:{', - children = { - { - 'Arrow:0:1:->', - children = { - 'Register(name=a):0:3:@a', - }, - }, - }, - }, - }, - }, { - hl('Lambda', '{'), - hl('Arrow', '->'), - hl('Register', '@a'), - hl('Lambda', '}'), - }) - check_parsing('{->@a+@b}', { - -- 012345678 - ast = { - { - 'Lambda(\\di):0:0:{', - children = { - { - 'Arrow:0:1:->', - children = { - { - 'BinaryPlus:0:5:+', - children = { - 'Register(name=a):0:3:@a', - 'Register(name=b):0:6:@b', - }, - }, - }, - }, - }, - }, - }, - }, { - hl('Lambda', '{'), - hl('Arrow', '->'), - hl('Register', '@a'), - hl('BinaryPlus', '+'), - hl('Register', '@b'), - hl('Lambda', '}'), - }) - check_parsing('{a->@a}', { - -- 012345678 - ast = { - { - 'Lambda(\\di):0:0:{', - children = { - 'PlainIdentifier(scope=0,ident=a):0:1:a', - { - 'Arrow:0:2:->', - children = { - 'Register(name=a):0:4:@a', - }, - }, - }, - }, - }, - }, { - hl('Lambda', '{'), - hl('IdentifierName', 'a'), - hl('Arrow', '->'), - hl('Register', '@a'), - hl('Lambda', '}'), - }) - check_parsing('{a,b->@a}', { - -- 012345678 - ast = { - { - 'Lambda(\\di):0:0:{', - children = { - { - 'Comma:0:2:,', - children = { - 'PlainIdentifier(scope=0,ident=a):0:1:a', - 'PlainIdentifier(scope=0,ident=b):0:3:b', - }, - }, - { - 'Arrow:0:4:->', - children = { - 'Register(name=a):0:6:@a', - }, - }, - }, - }, - }, - }, { - hl('Lambda', '{'), - hl('IdentifierName', 'a'), - hl('Comma', ','), - hl('IdentifierName', 'b'), - hl('Arrow', '->'), - hl('Register', '@a'), - hl('Lambda', '}'), - }) - check_parsing('{a,b,c->@a}', { - -- 01234567890 - ast = { - { - 'Lambda(\\di):0:0:{', - children = { - { - 'Comma:0:2:,', - children = { - 'PlainIdentifier(scope=0,ident=a):0:1:a', - { - 'Comma:0:4:,', - children = { - 'PlainIdentifier(scope=0,ident=b):0:3:b', - 'PlainIdentifier(scope=0,ident=c):0:5:c', - }, - }, - }, - }, - { - 'Arrow:0:6:->', - children = { - 'Register(name=a):0:8:@a', - }, - }, - }, - }, - }, - }, { - hl('Lambda', '{'), - hl('IdentifierName', 'a'), - hl('Comma', ','), - hl('IdentifierName', 'b'), - hl('Comma', ','), - hl('IdentifierName', 'c'), - hl('Arrow', '->'), - hl('Register', '@a'), - hl('Lambda', '}'), - }) - check_parsing('{a,b,c,d->@a}', { - -- 0123456789012 - ast = { - { - 'Lambda(\\di):0:0:{', - children = { - { - 'Comma:0:2:,', - children = { - 'PlainIdentifier(scope=0,ident=a):0:1:a', - { - 'Comma:0:4:,', - children = { - 'PlainIdentifier(scope=0,ident=b):0:3:b', - { - 'Comma:0:6:,', - children = { - 'PlainIdentifier(scope=0,ident=c):0:5:c', - 'PlainIdentifier(scope=0,ident=d):0:7:d', - }, - }, - }, - }, - }, - }, - { - 'Arrow:0:8:->', - children = { - 'Register(name=a):0:10:@a', - }, - }, - }, - }, - }, - }, { - hl('Lambda', '{'), - hl('IdentifierName', 'a'), - hl('Comma', ','), - hl('IdentifierName', 'b'), - hl('Comma', ','), - hl('IdentifierName', 'c'), - hl('Comma', ','), - hl('IdentifierName', 'd'), - hl('Arrow', '->'), - hl('Register', '@a'), - hl('Lambda', '}'), - }) - check_parsing('{a,b,c,d,->@a}', { - -- 01234567890123 - ast = { - { - 'Lambda(\\di):0:0:{', - children = { - { - 'Comma:0:2:,', - children = { - 'PlainIdentifier(scope=0,ident=a):0:1:a', - { - 'Comma:0:4:,', - children = { - 'PlainIdentifier(scope=0,ident=b):0:3:b', - { - 'Comma:0:6:,', - children = { - 'PlainIdentifier(scope=0,ident=c):0:5:c', - { - 'Comma:0:8:,', - children = { - 'PlainIdentifier(scope=0,ident=d):0:7:d', - }, - }, - }, - }, - }, - }, - }, - }, - { - 'Arrow:0:9:->', - children = { - 'Register(name=a):0:11:@a', - }, - }, - }, - }, - }, - }, { - hl('Lambda', '{'), - hl('IdentifierName', 'a'), - hl('Comma', ','), - hl('IdentifierName', 'b'), - hl('Comma', ','), - hl('IdentifierName', 'c'), - hl('Comma', ','), - hl('IdentifierName', 'd'), - hl('Comma', ','), - hl('Arrow', '->'), - hl('Register', '@a'), - hl('Lambda', '}'), - }) - check_parsing('{a,b->{c,d->{e,f->@a}}}', { - -- 01234567890123456789012 - -- 0 1 2 - ast = { - { - 'Lambda(\\di):0:0:{', - children = { - { - 'Comma:0:2:,', - children = { - 'PlainIdentifier(scope=0,ident=a):0:1:a', - 'PlainIdentifier(scope=0,ident=b):0:3:b', - }, - }, - { - 'Arrow:0:4:->', - children = { - { - 'Lambda(\\di):0:6:{', - children = { - { - 'Comma:0:8:,', - children = { - 'PlainIdentifier(scope=0,ident=c):0:7:c', - 'PlainIdentifier(scope=0,ident=d):0:9:d', - }, - }, - { - 'Arrow:0:10:->', - children = { - { - 'Lambda(\\di):0:12:{', - children = { - { - 'Comma:0:14:,', - children = { - 'PlainIdentifier(scope=0,ident=e):0:13:e', - 'PlainIdentifier(scope=0,ident=f):0:15:f', - }, - }, - { - 'Arrow:0:16:->', - children = { - 'Register(name=a):0:18:@a', - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, { - hl('Lambda', '{'), - hl('IdentifierName', 'a'), - hl('Comma', ','), - hl('IdentifierName', 'b'), - hl('Arrow', '->'), - hl('Lambda', '{'), - hl('IdentifierName', 'c'), - hl('Comma', ','), - hl('IdentifierName', 'd'), - hl('Arrow', '->'), - hl('Lambda', '{'), - hl('IdentifierName', 'e'), - hl('Comma', ','), - hl('IdentifierName', 'f'), - hl('Arrow', '->'), - hl('Register', '@a'), - hl('Lambda', '}'), - hl('Lambda', '}'), - hl('Lambda', '}'), - }) - check_parsing('{a,b->c,d}', { - -- 0123456789 - ast = { - { - 'Lambda(\\di):0:0:{', - children = { - { - 'Comma:0:2:,', - children = { - 'PlainIdentifier(scope=0,ident=a):0:1:a', - 'PlainIdentifier(scope=0,ident=b):0:3:b', - }, - }, - { - 'Arrow:0:4:->', - children = { - { - 'Comma:0:7:,', - children = { - 'PlainIdentifier(scope=0,ident=c):0:6:c', - 'PlainIdentifier(scope=0,ident=d):0:8:d', - }, - }, - }, - }, - }, - }, - }, - err = { - arg = ',d}', - msg = 'E15: Comma outside of call, lambda or literal: %.*s', - }, - }, { - hl('Lambda', '{'), - hl('IdentifierName', 'a'), - hl('Comma', ','), - hl('IdentifierName', 'b'), - hl('Arrow', '->'), - hl('IdentifierName', 'c'), - hl('InvalidComma', ','), - hl('IdentifierName', 'd'), - hl('Lambda', '}'), - }) - check_parsing('a,b,c,d', { - -- 0123456789 - ast = { - { - 'Comma:0:1:,', - children = { - 'PlainIdentifier(scope=0,ident=a):0:0:a', - { - 'Comma:0:3:,', - children = { - 'PlainIdentifier(scope=0,ident=b):0:2:b', - { - 'Comma:0:5:,', - children = { - 'PlainIdentifier(scope=0,ident=c):0:4:c', - 'PlainIdentifier(scope=0,ident=d):0:6:d', - }, - }, - }, - }, - }, - }, - }, - err = { - arg = ',b,c,d', - msg = 'E15: Comma outside of call, lambda or literal: %.*s', - }, - }, { - hl('IdentifierName', 'a'), - hl('InvalidComma', ','), - hl('IdentifierName', 'b'), - hl('InvalidComma', ','), - hl('IdentifierName', 'c'), - hl('InvalidComma', ','), - hl('IdentifierName', 'd'), - }) - check_parsing('a,b,c,d,', { - -- 0123456789 - ast = { - { - 'Comma:0:1:,', - children = { - 'PlainIdentifier(scope=0,ident=a):0:0:a', - { - 'Comma:0:3:,', - children = { - 'PlainIdentifier(scope=0,ident=b):0:2:b', - { - 'Comma:0:5:,', - children = { - 'PlainIdentifier(scope=0,ident=c):0:4:c', - { - 'Comma:0:7:,', - children = { - 'PlainIdentifier(scope=0,ident=d):0:6:d', - }, - }, - }, - }, - }, - }, - }, - }, - }, - err = { - arg = ',b,c,d,', - msg = 'E15: Comma outside of call, lambda or literal: %.*s', - }, - }, { - hl('IdentifierName', 'a'), - hl('InvalidComma', ','), - hl('IdentifierName', 'b'), - hl('InvalidComma', ','), - hl('IdentifierName', 'c'), - hl('InvalidComma', ','), - hl('IdentifierName', 'd'), - hl('InvalidComma', ','), - }) - check_parsing(',', { - -- 0123456789 - ast = { - { - 'Comma:0:0:,', - children = { - 'Missing:0:0:', - }, - }, - }, - err = { - arg = ',', - msg = 'E15: Expected value, got comma: %.*s', - }, - }, { - hl('InvalidComma', ','), - }) - check_parsing('{,a->@a}', { - -- 0123456789 - ast = { - { - 'CurlyBracesIdentifier(-di):0:0:{', - children = { - { - 'Arrow:0:3:->', - children = { - { - 'Comma:0:1:,', - children = { - 'Missing:0:1:', - 'PlainIdentifier(scope=0,ident=a):0:2:a', - }, - }, - 'Register(name=a):0:5:@a', - }, - }, - }, - }, - }, - err = { - arg = ',a->@a}', - msg = 'E15: Expected value, got comma: %.*s', - }, - }, { - hl('Curly', '{'), - hl('InvalidComma', ','), - hl('IdentifierName', 'a'), - hl('InvalidArrow', '->'), - hl('Register', '@a'), - hl('Curly', '}'), - }) - check_parsing('}', { - -- 0123456789 - ast = { - 'UnknownFigure(---):0:0:', - }, - err = { - arg = '}', - msg = 'E15: Unexpected closing figure brace: %.*s', - }, - }, { - hl('InvalidFigureBrace', '}'), - }) - check_parsing('{->}', { - -- 0123456789 - ast = { - { - 'Lambda(\\di):0:0:{', - children = { - 'Arrow:0:1:->', - }, - }, - }, - err = { - arg = '}', - msg = 'E15: Expected value, got closing figure brace: %.*s', - }, - }, { - hl('Lambda', '{'), - hl('Arrow', '->'), - hl('InvalidLambda', '}'), - }) - check_parsing('{a,b}', { - -- 0123456789 - ast = { - { - 'Lambda(-di):0:0:{', - children = { - { - 'Comma:0:2:,', - children = { - 'PlainIdentifier(scope=0,ident=a):0:1:a', - 'PlainIdentifier(scope=0,ident=b):0:3:b', - }, - }, - }, - }, - }, - err = { - arg = '}', - msg = 'E15: Expected lambda arguments list or arrow: %.*s', - }, - }, { - hl('Lambda', '{'), - hl('IdentifierName', 'a'), - hl('Comma', ','), - hl('IdentifierName', 'b'), - hl('InvalidLambda', '}'), - }) - check_parsing('{a,}', { - -- 0123456789 - ast = { - { - 'Lambda(-di):0:0:{', - children = { - { - 'Comma:0:2:,', - children = { - 'PlainIdentifier(scope=0,ident=a):0:1:a', - }, - }, - }, - }, - }, - err = { - arg = '}', - msg = 'E15: Expected lambda arguments list or arrow: %.*s', - }, - }, { - hl('Lambda', '{'), - hl('IdentifierName', 'a'), - hl('Comma', ','), - hl('InvalidLambda', '}'), - }) - check_parsing('{@a:@b}', { - -- 0123456789 - ast = { - { - 'DictLiteral(-di):0:0:{', - children = { - { - 'Colon:0:3::', - children = { - 'Register(name=a):0:1:@a', - 'Register(name=b):0:4:@b', - }, - }, - }, - }, - }, - }, { - hl('Dict', '{'), - hl('Register', '@a'), - hl('Colon', ':'), - hl('Register', '@b'), - hl('Dict', '}'), - }) - check_parsing('{@a:@b,@c:@d}', { - -- 0123456789012 - -- 0 1 - ast = { - { - 'DictLiteral(-di):0:0:{', - children = { - { - 'Comma:0:6:,', - children = { - { - 'Colon:0:3::', - children = { - 'Register(name=a):0:1:@a', - 'Register(name=b):0:4:@b', - }, - }, - { - 'Colon:0:9::', - children = { - 'Register(name=c):0:7:@c', - 'Register(name=d):0:10:@d', - }, - }, - }, - }, - }, - }, - }, - }, { - hl('Dict', '{'), - hl('Register', '@a'), - hl('Colon', ':'), - hl('Register', '@b'), - hl('Comma', ','), - hl('Register', '@c'), - hl('Colon', ':'), - hl('Register', '@d'), - hl('Dict', '}'), - }) - check_parsing('{@a:@b,@c:@d,@e:@f,}', { - -- 01234567890123456789 - -- 0 1 - ast = { - { - 'DictLiteral(-di):0:0:{', - children = { - { - 'Comma:0:6:,', - children = { - { - 'Colon:0:3::', - children = { - 'Register(name=a):0:1:@a', - 'Register(name=b):0:4:@b', - }, - }, - { - 'Comma:0:12:,', - children = { - { - 'Colon:0:9::', - children = { - 'Register(name=c):0:7:@c', - 'Register(name=d):0:10:@d', - }, - }, - { - 'Comma:0:18:,', - children = { - { - 'Colon:0:15::', - children = { - 'Register(name=e):0:13:@e', - 'Register(name=f):0:16:@f', - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, { - hl('Dict', '{'), - hl('Register', '@a'), - hl('Colon', ':'), - hl('Register', '@b'), - hl('Comma', ','), - hl('Register', '@c'), - hl('Colon', ':'), - hl('Register', '@d'), - hl('Comma', ','), - hl('Register', '@e'), - hl('Colon', ':'), - hl('Register', '@f'), - hl('Comma', ','), - hl('Dict', '}'), - }) - check_parsing('{@a:@b,@c:@d,@e:@f,@g:}', { - -- 01234567890123456789012 - -- 0 1 2 - ast = { - { - 'DictLiteral(-di):0:0:{', - children = { - { - 'Comma:0:6:,', - children = { - { - 'Colon:0:3::', - children = { - 'Register(name=a):0:1:@a', - 'Register(name=b):0:4:@b', - }, - }, - { - 'Comma:0:12:,', - children = { - { - 'Colon:0:9::', - children = { - 'Register(name=c):0:7:@c', - 'Register(name=d):0:10:@d', - }, - }, - { - 'Comma:0:18:,', - children = { - { - 'Colon:0:15::', - children = { - 'Register(name=e):0:13:@e', - 'Register(name=f):0:16:@f', - }, - }, - { - 'Colon:0:21::', - children = { - 'Register(name=g):0:19:@g', - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - err = { - arg = '}', - msg = 'E15: Expected value, got closing figure brace: %.*s', - }, - }, { - hl('Dict', '{'), - hl('Register', '@a'), - hl('Colon', ':'), - hl('Register', '@b'), - hl('Comma', ','), - hl('Register', '@c'), - hl('Colon', ':'), - hl('Register', '@d'), - hl('Comma', ','), - hl('Register', '@e'), - hl('Colon', ':'), - hl('Register', '@f'), - hl('Comma', ','), - hl('Register', '@g'), - hl('Colon', ':'), - hl('InvalidDict', '}'), - }) - check_parsing('{@a:@b,}', { - -- 01234567890123 - -- 0 1 - ast = { - { - 'DictLiteral(-di):0:0:{', - children = { - { - 'Comma:0:6:,', - children = { - { - 'Colon:0:3::', - children = { - 'Register(name=a):0:1:@a', - 'Register(name=b):0:4:@b', - }, - }, - }, - }, - }, - }, - }, - }, { - hl('Dict', '{'), - hl('Register', '@a'), - hl('Colon', ':'), - hl('Register', '@b'), - hl('Comma', ','), - hl('Dict', '}'), - }) - check_parsing('{({f -> g})(@h)(@i)}', { - -- 01234567890123456789 - -- 0 1 - ast = { - { - 'CurlyBracesIdentifier(-di):0:0:{', - children = { - { - 'Call:0:15:(', - children = { - { - 'Call:0:11:(', - children = { - { - 'Nested:0:1:(', - children = { - { - 'Lambda(\\di):0:2:{', - children = { - 'PlainIdentifier(scope=0,ident=f):0:3:f', - { - 'Arrow:0:4: ->', - children = { - 'PlainIdentifier(scope=0,ident=g):0:7: g', - }, - }, - }, - }, - }, - }, - 'Register(name=h):0:12:@h', - }, - }, - 'Register(name=i):0:16:@i', - }, - }, - }, - }, - }, - }, { - hl('Curly', '{'), - hl('NestingParenthesis', '('), - hl('Lambda', '{'), - hl('IdentifierName', 'f'), - hl('Arrow', '->', 1), - hl('IdentifierName', 'g', 1), - hl('Lambda', '}'), - hl('NestingParenthesis', ')'), - hl('CallingParenthesis', '('), - hl('Register', '@h'), - hl('CallingParenthesis', ')'), - hl('CallingParenthesis', '('), - hl('Register', '@i'), - hl('CallingParenthesis', ')'), - hl('Curly', '}'), - }) - check_parsing('a:{b()}c', { - -- 01234567 - ast = { - { - 'ComplexIdentifier:0:2:', - children = { - 'PlainIdentifier(scope=a,ident=):0:0:a:', - { - 'ComplexIdentifier:0:7:', - children = { - { - 'CurlyBracesIdentifier(--i):0:2:{', - children = { - { - 'Call:0:4:(', - children = { - 'PlainIdentifier(scope=0,ident=b):0:3:b', - }, - }, - }, - }, - 'PlainIdentifier(scope=0,ident=c):0:7:c', - }, - }, - }, - }, - }, - }, { - hl('IdentifierScope', 'a'), - hl('IdentifierScopeDelimiter', ':'), - hl('Curly', '{'), - hl('IdentifierName', 'b'), - hl('CallingParenthesis', '('), - hl('CallingParenthesis', ')'), - hl('Curly', '}'), - hl('IdentifierName', 'c'), - }) - check_parsing('a:{{b, c -> @d + @e + ({f -> g})(@h)}(@i)}j', { - -- 01234567890123456789012345678901234567890123456 - -- 0 1 2 3 4 - ast = { - { - 'ComplexIdentifier:0:2:', - children = { - 'PlainIdentifier(scope=a,ident=):0:0:a:', - { - 'ComplexIdentifier:0:42:', - children = { - { - 'CurlyBracesIdentifier(--i):0:2:{', - children = { - { - 'Call:0:37:(', - children = { - { - 'Lambda(\\di):0:3:{', - children = { - { - 'Comma:0:5:,', - children = { - 'PlainIdentifier(scope=0,ident=b):0:4:b', - 'PlainIdentifier(scope=0,ident=c):0:6: c', - }, - }, - { - 'Arrow:0:8: ->', - children = { - { - 'BinaryPlus:0:19: +', - children = { - { - 'BinaryPlus:0:14: +', - children = { - 'Register(name=d):0:11: @d', - 'Register(name=e):0:16: @e', - }, - }, - { - 'Call:0:32:(', - children = { - { - 'Nested:0:21: (', - children = { - { - 'Lambda(\\di):0:23:{', - children = { - 'PlainIdentifier(scope=0,ident=f):0:24:f', - { - 'Arrow:0:25: ->', - children = { - 'PlainIdentifier(scope=0,ident=g):0:28: g', - }, - }, - }, - }, - }, - }, - 'Register(name=h):0:33:@h', - }, - }, - }, - }, - }, - }, - }, - }, - 'Register(name=i):0:38:@i', - }, - }, - }, - }, - 'PlainIdentifier(scope=0,ident=j):0:42:j', - }, - }, - }, - }, - }, - }, { - hl('IdentifierScope', 'a'), - hl('IdentifierScopeDelimiter', ':'), - hl('Curly', '{'), - hl('Lambda', '{'), - hl('IdentifierName', 'b'), - hl('Comma', ','), - hl('IdentifierName', 'c', 1), - hl('Arrow', '->', 1), - hl('Register', '@d', 1), - hl('BinaryPlus', '+', 1), - hl('Register', '@e', 1), - hl('BinaryPlus', '+', 1), - hl('NestingParenthesis', '(', 1), - hl('Lambda', '{'), - hl('IdentifierName', 'f'), - hl('Arrow', '->', 1), - hl('IdentifierName', 'g', 1), - hl('Lambda', '}'), - hl('NestingParenthesis', ')'), - hl('CallingParenthesis', '('), - hl('Register', '@h'), - hl('CallingParenthesis', ')'), - hl('Lambda', '}'), - hl('CallingParenthesis', '('), - hl('Register', '@i'), - hl('CallingParenthesis', ')'), - hl('Curly', '}'), - hl('IdentifierName', 'j'), - }) - check_parsing('{@a + @b : @c + @d, @e + @f : @g + @i}', { - -- 01234567890123456789012345678901234567 - -- 0 1 2 3 - ast = { - { - 'DictLiteral(-di):0:0:{', - children = { - { - 'Comma:0:18:,', - children = { - { - 'Colon:0:8: :', - children = { - { - 'BinaryPlus:0:3: +', - children = { - 'Register(name=a):0:1:@a', - 'Register(name=b):0:5: @b', - }, - }, - { - 'BinaryPlus:0:13: +', - children = { - 'Register(name=c):0:10: @c', - 'Register(name=d):0:15: @d', - }, - }, - }, - }, - { - 'Colon:0:27: :', - children = { - { - 'BinaryPlus:0:22: +', - children = { - 'Register(name=e):0:19: @e', - 'Register(name=f):0:24: @f', - }, - }, - { - 'BinaryPlus:0:32: +', - children = { - 'Register(name=g):0:29: @g', - 'Register(name=i):0:34: @i', - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, { - hl('Dict', '{'), - hl('Register', '@a'), - hl('BinaryPlus', '+', 1), - hl('Register', '@b', 1), - hl('Colon', ':', 1), - hl('Register', '@c', 1), - hl('BinaryPlus', '+', 1), - hl('Register', '@d', 1), - hl('Comma', ','), - hl('Register', '@e', 1), - hl('BinaryPlus', '+', 1), - hl('Register', '@f', 1), - hl('Colon', ':', 1), - hl('Register', '@g', 1), - hl('BinaryPlus', '+', 1), - hl('Register', '@i', 1), - hl('Dict', '}'), - }) - check_parsing('-> -> ->', { - -- 01234567 - ast = { - { - 'Arrow:0:0:->', - children = { - 'Missing:0:0:', - { - 'Arrow:0:2: ->', - children = { - 'Missing:0:2:', - { - 'Arrow:0:5: ->', - children = { - 'Missing:0:5:', - }, - }, - }, - }, - }, - }, - }, - err = { - arg = '-> -> ->', - msg = 'E15: Unexpected arrow: %.*s', - }, - }, { - hl('InvalidArrow', '->'), - hl('InvalidArrow', '->', 1), - hl('InvalidArrow', '->', 1), - }) - check_parsing('a -> b -> c -> d', { - -- 0123456789012345 - -- 0 1 - ast = { - { - 'Arrow:0:1: ->', - children = { - 'PlainIdentifier(scope=0,ident=a):0:0:a', - { - 'Arrow:0:6: ->', - children = { - 'PlainIdentifier(scope=0,ident=b):0:4: b', - { - 'Arrow:0:11: ->', - children = { - 'PlainIdentifier(scope=0,ident=c):0:9: c', - 'PlainIdentifier(scope=0,ident=d):0:14: d', - }, - }, - }, - }, - }, - }, - }, - err = { - arg = '-> b -> c -> d', - msg = 'E15: Arrow outside of lambda: %.*s', - }, - }, { - hl('IdentifierName', 'a'), - hl('InvalidArrow', '->', 1), - hl('IdentifierName', 'b', 1), - hl('InvalidArrow', '->', 1), - hl('IdentifierName', 'c', 1), - hl('InvalidArrow', '->', 1), - hl('IdentifierName', 'd', 1), - }) - check_parsing('{a -> b -> c}', { - -- 0123456789012 - -- 0 1 - ast = { - { - 'Lambda(\\di):0:0:{', - children = { - 'PlainIdentifier(scope=0,ident=a):0:1:a', - { - 'Arrow:0:2: ->', - children = { - { - 'Arrow:0:7: ->', - children = { - 'PlainIdentifier(scope=0,ident=b):0:5: b', - 'PlainIdentifier(scope=0,ident=c):0:10: c', - }, - }, - }, - }, - }, - }, - }, - err = { - arg = '-> c}', - msg = 'E15: Arrow outside of lambda: %.*s', - }, - }, { - hl('Lambda', '{'), - hl('IdentifierName', 'a'), - hl('Arrow', '->', 1), - hl('IdentifierName', 'b', 1), - hl('InvalidArrow', '->', 1), - hl('IdentifierName', 'c', 1), - hl('Lambda', '}'), - }) - check_parsing('{a: -> b}', { - -- 012345678 - ast = { - { - 'CurlyBracesIdentifier(-di):0:0:{', - children = { - { - 'Arrow:0:3: ->', - children = { - 'PlainIdentifier(scope=a,ident=):0:1:a:', - 'PlainIdentifier(scope=0,ident=b):0:6: b', - }, - }, - }, - }, - }, - err = { - arg = '-> b}', - msg = 'E15: Arrow outside of lambda: %.*s', - }, - }, { - hl('Curly', '{'), - hl('IdentifierScope', 'a'), - hl('IdentifierScopeDelimiter', ':'), - hl('InvalidArrow', '->', 1), - hl('IdentifierName', 'b', 1), - hl('Curly', '}'), - }) - - check_parsing('{a:b -> b}', { - -- 0123456789 - ast = { - { - 'CurlyBracesIdentifier(-di):0:0:{', - children = { - { - 'Arrow:0:4: ->', - children = { - 'PlainIdentifier(scope=a,ident=b):0:1:a:b', - 'PlainIdentifier(scope=0,ident=b):0:7: b', - }, - }, - }, - }, - }, - err = { - arg = '-> b}', - msg = 'E15: Arrow outside of lambda: %.*s', - }, - }, { - hl('Curly', '{'), - hl('IdentifierScope', 'a'), - hl('IdentifierScopeDelimiter', ':'), - hl('IdentifierName', 'b'), - hl('InvalidArrow', '->', 1), - hl('IdentifierName', 'b', 1), - hl('Curly', '}'), - }) - - check_parsing('{a#b -> b}', { - -- 0123456789 - ast = { - { - 'CurlyBracesIdentifier(-di):0:0:{', - children = { - { - 'Arrow:0:4: ->', - children = { - 'PlainIdentifier(scope=0,ident=a#b):0:1:a#b', - 'PlainIdentifier(scope=0,ident=b):0:7: b', - }, - }, - }, - }, - }, - err = { - arg = '-> b}', - msg = 'E15: Arrow outside of lambda: %.*s', - }, - }, { - hl('Curly', '{'), - hl('IdentifierName', 'a#b'), - hl('InvalidArrow', '->', 1), - hl('IdentifierName', 'b', 1), - hl('Curly', '}'), - }) - check_parsing('{a : b : c}', { - -- 01234567890 - -- 0 1 - ast = { - { - 'DictLiteral(-di):0:0:{', - children = { - { - 'Colon:0:2: :', - children = { - 'PlainIdentifier(scope=0,ident=a):0:1:a', - { - 'Colon:0:6: :', - children = { - 'PlainIdentifier(scope=0,ident=b):0:4: b', - 'PlainIdentifier(scope=0,ident=c):0:8: c', - }, - }, - }, - }, - }, - }, - }, - err = { - arg = ': c}', - msg = 'E15: Colon outside of dictionary or ternary operator: %.*s', - }, - }, { - hl('Dict', '{'), - hl('IdentifierName', 'a'), - hl('Colon', ':', 1), - hl('IdentifierName', 'b', 1), - hl('InvalidColon', ':', 1), - hl('IdentifierName', 'c', 1), - hl('Dict', '}'), - }) - check_parsing('{', { - -- 0 - ast = { - 'UnknownFigure(\\di):0:0:{', - }, - err = { - arg = '{', - msg = 'E15: Missing closing figure brace: %.*s', - }, - }, { - hl('FigureBrace', '{'), - }) - check_parsing('{a', { - -- 01 - ast = { - { - 'UnknownFigure(\\di):0:0:{', - children = { - 'PlainIdentifier(scope=0,ident=a):0:1:a', - }, - }, - }, - err = { - arg = '{a', - msg = 'E15: Missing closing figure brace: %.*s', - }, - }, { - hl('FigureBrace', '{'), - hl('IdentifierName', 'a'), - }) - check_parsing('{a,b', { - -- 0123 - ast = { - { - 'Lambda(\\di):0:0:{', - children = { - { - 'Comma:0:2:,', - children = { - 'PlainIdentifier(scope=0,ident=a):0:1:a', - 'PlainIdentifier(scope=0,ident=b):0:3:b', - }, - }, - }, - }, - }, - err = { - arg = '{a,b', - msg = 'E15: Missing closing figure brace for lambda: %.*s', - }, - }, { - hl('Lambda', '{'), - hl('IdentifierName', 'a'), - hl('Comma', ','), - hl('IdentifierName', 'b'), - }) - check_parsing('{a,b->', { - -- 012345 - ast = { - { - 'Lambda(\\di):0:0:{', - children = { - { - 'Comma:0:2:,', - children = { - 'PlainIdentifier(scope=0,ident=a):0:1:a', - 'PlainIdentifier(scope=0,ident=b):0:3:b', - }, - }, - 'Arrow:0:4:->', - }, - }, - }, - err = { - arg = '', - msg = 'E15: Expected value, got EOC: %.*s', - }, - }, { - hl('Lambda', '{'), - hl('IdentifierName', 'a'), - hl('Comma', ','), - hl('IdentifierName', 'b'), - hl('Arrow', '->'), - }) - check_parsing('{a,b->c', { - -- 0123456 - ast = { - { - 'Lambda(\\di):0:0:{', - children = { - { - 'Comma:0:2:,', - children = { - 'PlainIdentifier(scope=0,ident=a):0:1:a', - 'PlainIdentifier(scope=0,ident=b):0:3:b', - }, - }, - { - 'Arrow:0:4:->', - children = { - 'PlainIdentifier(scope=0,ident=c):0:6:c', - }, - }, - }, - }, - }, - err = { - arg = '{a,b->c', - msg = 'E15: Missing closing figure brace for lambda: %.*s', - }, - }, { - hl('Lambda', '{'), - hl('IdentifierName', 'a'), - hl('Comma', ','), - hl('IdentifierName', 'b'), - hl('Arrow', '->'), - hl('IdentifierName', 'c'), - }) - check_parsing('{a : b', { - -- 012345 - ast = { - { - 'DictLiteral(-di):0:0:{', - children = { - { - 'Colon:0:2: :', - children = { - 'PlainIdentifier(scope=0,ident=a):0:1:a', - 'PlainIdentifier(scope=0,ident=b):0:4: b', - }, - }, - }, - }, - }, - err = { - arg = '{a : b', - msg = 'E723: Missing end of Dictionary \'}\': %.*s', - }, - }, { - hl('Dict', '{'), - hl('IdentifierName', 'a'), - hl('Colon', ':', 1), - hl('IdentifierName', 'b', 1), - }) - check_parsing('{a : b,', { - -- 0123456 - ast = { - { - 'DictLiteral(-di):0:0:{', - children = { - { - 'Comma:0:6:,', - children = { - { - 'Colon:0:2: :', - children = { - 'PlainIdentifier(scope=0,ident=a):0:1:a', - 'PlainIdentifier(scope=0,ident=b):0:4: b', - }, - }, - }, - }, - }, - }, - }, - err = { - arg = '', - msg = 'E15: Expected value, got EOC: %.*s', - }, - }, { - hl('Dict', '{'), - hl('IdentifierName', 'a'), - hl('Colon', ':', 1), - hl('IdentifierName', 'b', 1), - hl('Comma', ','), - }) - end) - itp('works with ternary operator', function() - check_parsing('a ? b : c', { - -- 012345678 - ast = { - { - 'Ternary:0:1: ?', - children = { - 'PlainIdentifier(scope=0,ident=a):0:0:a', - { - 'TernaryValue:0:5: :', - children = { - 'PlainIdentifier(scope=0,ident=b):0:3: b', - 'PlainIdentifier(scope=0,ident=c):0:7: c', - }, - }, - }, - }, - }, - }, { - hl('IdentifierName', 'a'), - hl('Ternary', '?', 1), - hl('IdentifierName', 'b', 1), - hl('TernaryColon', ':', 1), - hl('IdentifierName', 'c', 1), - }) - check_parsing('@a?@b?@c:@d:@e', { - -- 01234567890123 - -- 0 1 - ast = { - { - 'Ternary:0:2:?', - children = { - 'Register(name=a):0:0:@a', - { - 'TernaryValue:0:11::', - children = { - { - 'Ternary:0:5:?', - children = { - 'Register(name=b):0:3:@b', - { - 'TernaryValue:0:8::', - children = { - 'Register(name=c):0:6:@c', - 'Register(name=d):0:9:@d', - }, - }, - }, - }, - 'Register(name=e):0:12:@e', - }, - }, - }, - }, - }, - }, { - hl('Register', '@a'), - hl('Ternary', '?'), - hl('Register', '@b'), - hl('Ternary', '?'), - hl('Register', '@c'), - hl('TernaryColon', ':'), - hl('Register', '@d'), - hl('TernaryColon', ':'), - hl('Register', '@e'), - }) - check_parsing('@a?@b:@c?@d:@e', { - -- 01234567890123 - -- 0 1 - ast = { - { - 'Ternary:0:2:?', - children = { - 'Register(name=a):0:0:@a', - { - 'TernaryValue:0:5::', - children = { - 'Register(name=b):0:3:@b', - { - 'Ternary:0:8:?', - children = { - 'Register(name=c):0:6:@c', - { - 'TernaryValue:0:11::', - children = { - 'Register(name=d):0:9:@d', - 'Register(name=e):0:12:@e', - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, { - hl('Register', '@a'), - hl('Ternary', '?'), - hl('Register', '@b'), - hl('TernaryColon', ':'), - hl('Register', '@c'), - hl('Ternary', '?'), - hl('Register', '@d'), - hl('TernaryColon', ':'), - hl('Register', '@e'), - }) - check_parsing('@a?@b?@c?@d:@e?@f:@g:@h?@i:@j:@k', { - -- 01234567890123456789012345678901 - -- 0 1 2 3 - ast = { - { - 'Ternary:0:2:?', - children = { - 'Register(name=a):0:0:@a', - { - 'TernaryValue:0:29::', - children = { - { - 'Ternary:0:5:?', - children = { - 'Register(name=b):0:3:@b', - { - 'TernaryValue:0:20::', - children = { - { - 'Ternary:0:8:?', - children = { - 'Register(name=c):0:6:@c', - { - 'TernaryValue:0:11::', - children = { - 'Register(name=d):0:9:@d', - { - 'Ternary:0:14:?', - children = { - 'Register(name=e):0:12:@e', - { - 'TernaryValue:0:17::', - children = { - 'Register(name=f):0:15:@f', - 'Register(name=g):0:18:@g', - }, - }, - }, - }, - }, - }, - }, - }, - { - 'Ternary:0:23:?', - children = { - 'Register(name=h):0:21:@h', - { - 'TernaryValue:0:26::', - children = { - 'Register(name=i):0:24:@i', - 'Register(name=j):0:27:@j', - }, - }, - }, - }, - }, - }, - }, - }, - 'Register(name=k):0:30:@k', - }, - }, - }, - }, - }, - }, { - hl('Register', '@a'), - hl('Ternary', '?'), - hl('Register', '@b'), - hl('Ternary', '?'), - hl('Register', '@c'), - hl('Ternary', '?'), - hl('Register', '@d'), - hl('TernaryColon', ':'), - hl('Register', '@e'), - hl('Ternary', '?'), - hl('Register', '@f'), - hl('TernaryColon', ':'), - hl('Register', '@g'), - hl('TernaryColon', ':'), - hl('Register', '@h'), - hl('Ternary', '?'), - hl('Register', '@i'), - hl('TernaryColon', ':'), - hl('Register', '@j'), - hl('TernaryColon', ':'), - hl('Register', '@k'), - }) - check_parsing('?', { - -- 0 - ast = { - { - 'Ternary:0:0:?', - children = { - 'Missing:0:0:', - 'TernaryValue:0:0:?', - }, - }, - }, - err = { - arg = '?', - msg = 'E15: Expected value, got question mark: %.*s', - }, - }, { - hl('InvalidTernary', '?'), - }) - - check_parsing('?:', { - -- 01 - ast = { - { - 'Ternary:0:0:?', - children = { - 'Missing:0:0:', - { - 'TernaryValue:0:1::', - children = { - 'Missing:0:1:', - }, - }, - }, - }, - }, - err = { - arg = '?:', - msg = 'E15: Expected value, got question mark: %.*s', - }, - }, { - hl('InvalidTernary', '?'), - hl('InvalidTernaryColon', ':'), - }) - - check_parsing('?::', { - -- 012 - ast = { - { - 'Colon:0:2::', - children = { - { - 'Ternary:0:0:?', - children = { - 'Missing:0:0:', - { - 'TernaryValue:0:1::', - children = { - 'Missing:0:1:', - 'Missing:0:2:', - }, - }, - }, - }, - }, - }, - }, - err = { - arg = '?::', - msg = 'E15: Expected value, got question mark: %.*s', - }, - }, { - hl('InvalidTernary', '?'), - hl('InvalidTernaryColon', ':'), - hl('InvalidColon', ':'), - }) - - check_parsing('a?b', { - -- 012 - ast = { - { - 'Ternary:0:1:?', - children = { - 'PlainIdentifier(scope=0,ident=a):0:0:a', - { - 'TernaryValue:0:1:?', - children = { - 'PlainIdentifier(scope=0,ident=b):0:2:b', - }, - }, - }, - }, - }, - err = { - arg = '?b', - msg = 'E109: Missing \':\' after \'?\': %.*s', - }, - }, { - hl('IdentifierName', 'a'), - hl('Ternary', '?'), - hl('IdentifierName', 'b'), - }) - check_parsing('a?b:', { - -- 0123 - ast = { - { - 'Ternary:0:1:?', - children = { - 'PlainIdentifier(scope=0,ident=a):0:0:a', - { - 'TernaryValue:0:1:?', - children = { - 'PlainIdentifier(scope=b,ident=):0:2:b:', - }, - }, - }, - }, - }, - err = { - arg = '?b:', - msg = 'E109: Missing \':\' after \'?\': %.*s', - }, - }, { - hl('IdentifierName', 'a'), - hl('Ternary', '?'), - hl('IdentifierScope', 'b'), - hl('IdentifierScopeDelimiter', ':'), - }) - - check_parsing('a?b::c', { - -- 012345 - ast = { - { - 'Ternary:0:1:?', - children = { - 'PlainIdentifier(scope=0,ident=a):0:0:a', - { - 'TernaryValue:0:4::', - children = { - 'PlainIdentifier(scope=b,ident=):0:2:b:', - 'PlainIdentifier(scope=0,ident=c):0:5:c', - }, - }, - }, - }, - }, - }, { - hl('IdentifierName', 'a'), - hl('Ternary', '?'), - hl('IdentifierScope', 'b'), - hl('IdentifierScopeDelimiter', ':'), - hl('TernaryColon', ':'), - hl('IdentifierName', 'c'), - }) - - check_parsing('a?b :', { - -- 01234 - ast = { - { - 'Ternary:0:1:?', - children = { - 'PlainIdentifier(scope=0,ident=a):0:0:a', - { - 'TernaryValue:0:3: :', - children = { - 'PlainIdentifier(scope=0,ident=b):0:2:b', - }, - }, - }, - }, - }, - err = { - arg = '', - msg = 'E15: Expected value, got EOC: %.*s', - }, - }, { - hl('IdentifierName', 'a'), - hl('Ternary', '?'), - hl('IdentifierName', 'b'), - hl('TernaryColon', ':', 1), - }) - - check_parsing('(@a?@b:@c)?@d:@e', { - -- 0123456789012345 - -- 0 1 - ast = { - { - 'Ternary:0:10:?', - children = { - { - 'Nested:0:0:(', - children = { - { - 'Ternary:0:3:?', - children = { - 'Register(name=a):0:1:@a', - { - 'TernaryValue:0:6::', - children = { - 'Register(name=b):0:4:@b', - 'Register(name=c):0:7:@c', - }, - }, - }, - }, - }, - }, - { - 'TernaryValue:0:13::', - children = { - 'Register(name=d):0:11:@d', - 'Register(name=e):0:14:@e', - }, - }, - }, - }, - }, - }, { - hl('NestingParenthesis', '('), - hl('Register', '@a'), - hl('Ternary', '?'), - hl('Register', '@b'), - hl('TernaryColon', ':'), - hl('Register', '@c'), - hl('NestingParenthesis', ')'), - hl('Ternary', '?'), - hl('Register', '@d'), - hl('TernaryColon', ':'), - hl('Register', '@e'), - }) - - check_parsing('(@a?@b:@c)?(@d?@e:@f):(@g?@h:@i)', { - -- 01234567890123456789012345678901 - -- 0 1 2 3 - ast = { - { - 'Ternary:0:10:?', - children = { - { - 'Nested:0:0:(', - children = { - { - 'Ternary:0:3:?', - children = { - 'Register(name=a):0:1:@a', - { - 'TernaryValue:0:6::', - children = { - 'Register(name=b):0:4:@b', - 'Register(name=c):0:7:@c', - }, - }, - }, - }, - }, - }, - { - 'TernaryValue:0:21::', - children = { - { - 'Nested:0:11:(', - children = { - { - 'Ternary:0:14:?', - children = { - 'Register(name=d):0:12:@d', - { - 'TernaryValue:0:17::', - children = { - 'Register(name=e):0:15:@e', - 'Register(name=f):0:18:@f', - }, - }, - }, - }, - }, - }, - { - 'Nested:0:22:(', - children = { - { - 'Ternary:0:25:?', - children = { - 'Register(name=g):0:23:@g', - { - 'TernaryValue:0:28::', - children = { - 'Register(name=h):0:26:@h', - 'Register(name=i):0:29:@i', - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, { - hl('NestingParenthesis', '('), - hl('Register', '@a'), - hl('Ternary', '?'), - hl('Register', '@b'), - hl('TernaryColon', ':'), - hl('Register', '@c'), - hl('NestingParenthesis', ')'), - hl('Ternary', '?'), - hl('NestingParenthesis', '('), - hl('Register', '@d'), - hl('Ternary', '?'), - hl('Register', '@e'), - hl('TernaryColon', ':'), - hl('Register', '@f'), - hl('NestingParenthesis', ')'), - hl('TernaryColon', ':'), - hl('NestingParenthesis', '('), - hl('Register', '@g'), - hl('Ternary', '?'), - hl('Register', '@h'), - hl('TernaryColon', ':'), - hl('Register', '@i'), - hl('NestingParenthesis', ')'), - }) - - check_parsing('(@a?@b:@c)?@d?@e:@f:@g?@h:@i', { - -- 0123456789012345678901234567 - -- 0 1 2 - ast = { - { - 'Ternary:0:10:?', - children = { - { - 'Nested:0:0:(', - children = { - { - 'Ternary:0:3:?', - children = { - 'Register(name=a):0:1:@a', - { - 'TernaryValue:0:6::', - children = { - 'Register(name=b):0:4:@b', - 'Register(name=c):0:7:@c', - }, - }, - }, - }, - }, - }, - { - 'TernaryValue:0:19::', - children = { - { - 'Ternary:0:13:?', - children = { - 'Register(name=d):0:11:@d', - { - 'TernaryValue:0:16::', - children = { - 'Register(name=e):0:14:@e', - 'Register(name=f):0:17:@f', - }, - }, - }, - }, - { - 'Ternary:0:22:?', - children = { - 'Register(name=g):0:20:@g', - { - 'TernaryValue:0:25::', - children = { - 'Register(name=h):0:23:@h', - 'Register(name=i):0:26:@i', - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, { - hl('NestingParenthesis', '('), - hl('Register', '@a'), - hl('Ternary', '?'), - hl('Register', '@b'), - hl('TernaryColon', ':'), - hl('Register', '@c'), - hl('NestingParenthesis', ')'), - hl('Ternary', '?'), - hl('Register', '@d'), - hl('Ternary', '?'), - hl('Register', '@e'), - hl('TernaryColon', ':'), - hl('Register', '@f'), - hl('TernaryColon', ':'), - hl('Register', '@g'), - hl('Ternary', '?'), - hl('Register', '@h'), - hl('TernaryColon', ':'), - hl('Register', '@i'), - }) - check_parsing('a?b{cdef}g:h', { - -- 012345678901 - -- 0 1 - ast = { - { - 'Ternary:0:1:?', - children = { - 'PlainIdentifier(scope=0,ident=a):0:0:a', - { - 'TernaryValue:0:10::', - children = { - { - 'ComplexIdentifier:0:3:', - children = { - 'PlainIdentifier(scope=0,ident=b):0:2:b', - { - 'ComplexIdentifier:0:9:', - children = { - { - 'CurlyBracesIdentifier(--i):0:3:{', - children = { - 'PlainIdentifier(scope=0,ident=cdef):0:4:cdef', - }, - }, - 'PlainIdentifier(scope=0,ident=g):0:9:g', - }, - }, - }, - }, - 'PlainIdentifier(scope=0,ident=h):0:11:h', - }, - }, - }, - }, - }, - }, { - hl('IdentifierName', 'a'), - hl('Ternary', '?'), - hl('IdentifierName', 'b'), - hl('Curly', '{'), - hl('IdentifierName', 'cdef'), - hl('Curly', '}'), - hl('IdentifierName', 'g'), - hl('TernaryColon', ':'), - hl('IdentifierName', 'h'), - }) - check_parsing('a ? b : c : d', { - -- 0123456789012 - -- 0 1 - ast = { - { - 'Colon:0:9: :', - children = { - { - 'Ternary:0:1: ?', - children = { - 'PlainIdentifier(scope=0,ident=a):0:0:a', - { - 'TernaryValue:0:5: :', - children = { - 'PlainIdentifier(scope=0,ident=b):0:3: b', - 'PlainIdentifier(scope=0,ident=c):0:7: c', - }, - }, - }, - }, - 'PlainIdentifier(scope=0,ident=d):0:11: d', - }, - }, - }, - err = { - arg = ': d', - msg = 'E15: Colon outside of dictionary or ternary operator: %.*s', - }, - }, { - hl('IdentifierName', 'a'), - hl('Ternary', '?', 1), - hl('IdentifierName', 'b', 1), - hl('TernaryColon', ':', 1), - hl('IdentifierName', 'c', 1), - hl('InvalidColon', ':', 1), - hl('IdentifierName', 'd', 1), - }) - end) - itp('works with comparison operators', function() - check_parsing('a == b', { - -- 012345 - ast = { - { - 'Comparison(type=Equal,inv=0,ccs=UseOption):0:1: ==', - children = { - 'PlainIdentifier(scope=0,ident=a):0:0:a', - 'PlainIdentifier(scope=0,ident=b):0:4: b', - }, - }, - }, - }, { - hl('IdentifierName', 'a'), - hl('Comparison', '==', 1), - hl('IdentifierName', 'b', 1), - }) - - check_parsing('a ==? b', { - -- 0123456 - ast = { - { - 'Comparison(type=Equal,inv=0,ccs=IgnoreCase):0:1: ==?', - children = { - 'PlainIdentifier(scope=0,ident=a):0:0:a', - 'PlainIdentifier(scope=0,ident=b):0:5: b', - }, - }, - }, - }, { - hl('IdentifierName', 'a'), - hl('Comparison', '==', 1), - hl('ComparisonModifier', '?'), - hl('IdentifierName', 'b', 1), - }) - - check_parsing('a ==# b', { - -- 0123456 - ast = { - { - 'Comparison(type=Equal,inv=0,ccs=MatchCase):0:1: ==#', - children = { - 'PlainIdentifier(scope=0,ident=a):0:0:a', - 'PlainIdentifier(scope=0,ident=b):0:5: b', - }, - }, - }, - }, { - hl('IdentifierName', 'a'), - hl('Comparison', '==', 1), - hl('ComparisonModifier', '#'), - hl('IdentifierName', 'b', 1), - }) - - check_parsing('a !=# b', { - -- 0123456 - ast = { - { - 'Comparison(type=Equal,inv=1,ccs=MatchCase):0:1: !=#', - children = { - 'PlainIdentifier(scope=0,ident=a):0:0:a', - 'PlainIdentifier(scope=0,ident=b):0:5: b', - }, - }, - }, - }, { - hl('IdentifierName', 'a'), - hl('Comparison', '!=', 1), - hl('ComparisonModifier', '#'), - hl('IdentifierName', 'b', 1), - }) - - check_parsing('a <=# b', { - -- 0123456 - ast = { - { - 'Comparison(type=Greater,inv=1,ccs=MatchCase):0:1: <=#', - children = { - 'PlainIdentifier(scope=0,ident=a):0:0:a', - 'PlainIdentifier(scope=0,ident=b):0:5: b', - }, - }, - }, - }, { - hl('IdentifierName', 'a'), - hl('Comparison', '<=', 1), - hl('ComparisonModifier', '#'), - hl('IdentifierName', 'b', 1), - }) - - check_parsing('a >=# b', { - -- 0123456 - ast = { - { - 'Comparison(type=GreaterOrEqual,inv=0,ccs=MatchCase):0:1: >=#', - children = { - 'PlainIdentifier(scope=0,ident=a):0:0:a', - 'PlainIdentifier(scope=0,ident=b):0:5: b', - }, - }, - }, - }, { - hl('IdentifierName', 'a'), - hl('Comparison', '>=', 1), - hl('ComparisonModifier', '#'), - hl('IdentifierName', 'b', 1), - }) - - check_parsing('a ># b', { - -- 012345 - ast = { - { - 'Comparison(type=Greater,inv=0,ccs=MatchCase):0:1: >#', - children = { - 'PlainIdentifier(scope=0,ident=a):0:0:a', - 'PlainIdentifier(scope=0,ident=b):0:4: b', - }, - }, - }, - }, { - hl('IdentifierName', 'a'), - hl('Comparison', '>', 1), - hl('ComparisonModifier', '#'), - hl('IdentifierName', 'b', 1), - }) - - check_parsing('a <# b', { - -- 012345 - ast = { - { - 'Comparison(type=GreaterOrEqual,inv=1,ccs=MatchCase):0:1: <#', - children = { - 'PlainIdentifier(scope=0,ident=a):0:0:a', - 'PlainIdentifier(scope=0,ident=b):0:4: b', - }, - }, - }, - }, { - hl('IdentifierName', 'a'), - hl('Comparison', '<', 1), - hl('ComparisonModifier', '#'), - hl('IdentifierName', 'b', 1), - }) - - check_parsing('a is#b', { - -- 012345 - ast = { - { - 'Comparison(type=Identical,inv=0,ccs=MatchCase):0:1: is#', - children = { - 'PlainIdentifier(scope=0,ident=a):0:0:a', - 'PlainIdentifier(scope=0,ident=b):0:5:b', - }, - }, - }, - }, { - hl('IdentifierName', 'a'), - hl('Comparison', 'is', 1), - hl('ComparisonModifier', '#'), - hl('IdentifierName', 'b'), - }) - - check_parsing('a is?b', { - -- 012345 - ast = { - { - 'Comparison(type=Identical,inv=0,ccs=IgnoreCase):0:1: is?', - children = { - 'PlainIdentifier(scope=0,ident=a):0:0:a', - 'PlainIdentifier(scope=0,ident=b):0:5:b', - }, - }, - }, - }, { - hl('IdentifierName', 'a'), - hl('Comparison', 'is', 1), - hl('ComparisonModifier', '?'), - hl('IdentifierName', 'b'), - }) - - check_parsing('a isnot b', { - -- 012345678 - ast = { - { - 'Comparison(type=Identical,inv=1,ccs=UseOption):0:1: isnot', - children = { - 'PlainIdentifier(scope=0,ident=a):0:0:a', - 'PlainIdentifier(scope=0,ident=b):0:7: b', - }, - }, - }, - }, { - hl('IdentifierName', 'a'), - hl('Comparison', 'isnot', 1), - hl('IdentifierName', 'b', 1), - }) - - check_parsing('a < b < c', { - -- 012345678 - ast = { - { - 'Comparison(type=GreaterOrEqual,inv=1,ccs=UseOption):0:1: <', - children = { - 'PlainIdentifier(scope=0,ident=a):0:0:a', - { - 'Comparison(type=GreaterOrEqual,inv=1,ccs=UseOption):0:5: <', - children = { - 'PlainIdentifier(scope=0,ident=b):0:3: b', - 'PlainIdentifier(scope=0,ident=c):0:7: c', - }, - }, - }, - }, - }, - err = { - arg = ' < c', - msg = 'E15: Operator is not associative: %.*s', - }, - }, { - hl('IdentifierName', 'a'), - hl('Comparison', '<', 1), - hl('IdentifierName', 'b', 1), - hl('InvalidComparison', '<', 1), - hl('IdentifierName', 'c', 1), - }) - - check_parsing('a < b <# c', { - -- 012345678 - ast = { - { - 'Comparison(type=GreaterOrEqual,inv=1,ccs=UseOption):0:1: <', - children = { - 'PlainIdentifier(scope=0,ident=a):0:0:a', - { - 'Comparison(type=GreaterOrEqual,inv=1,ccs=MatchCase):0:5: <#', - children = { - 'PlainIdentifier(scope=0,ident=b):0:3: b', - 'PlainIdentifier(scope=0,ident=c):0:8: c', - }, - }, - }, - }, - }, - err = { - arg = ' <# c', - msg = 'E15: Operator is not associative: %.*s', - }, - }, { - hl('IdentifierName', 'a'), - hl('Comparison', '<', 1), - hl('IdentifierName', 'b', 1), - hl('InvalidComparison', '<', 1), - hl('InvalidComparisonModifier', '#'), - hl('IdentifierName', 'c', 1), - }) - - check_parsing('a += b', { - -- 012345 - ast = { - { - 'Assignment(Add):0:1: +=', - children = { - 'PlainIdentifier(scope=0,ident=a):0:0:a', - 'PlainIdentifier(scope=0,ident=b):0:4: b', - }, - }, - }, - err = { - arg = '+= b', - msg = 'E15: Misplaced assignment: %.*s', - }, - }, { - hl('IdentifierName', 'a'), - hl('InvalidAssignmentWithAddition', '+=', 1), - hl('IdentifierName', 'b', 1), - }) - check_parsing('a + b == c + d', { - -- 01234567890123 - -- 0 1 - ast = { - { - 'Comparison(type=Equal,inv=0,ccs=UseOption):0:5: ==', - children = { - { - 'BinaryPlus:0:1: +', - children = { - 'PlainIdentifier(scope=0,ident=a):0:0:a', - 'PlainIdentifier(scope=0,ident=b):0:3: b', - }, - }, - { - 'BinaryPlus:0:10: +', - children = { - 'PlainIdentifier(scope=0,ident=c):0:8: c', - 'PlainIdentifier(scope=0,ident=d):0:12: d', - }, - }, - }, - }, - }, - }, { - hl('IdentifierName', 'a'), - hl('BinaryPlus', '+', 1), - hl('IdentifierName', 'b', 1), - hl('Comparison', '==', 1), - hl('IdentifierName', 'c', 1), - hl('BinaryPlus', '+', 1), - hl('IdentifierName', 'd', 1), - }) - check_parsing('+ a == + b', { - -- 0123456789 - ast = { - { - 'Comparison(type=Equal,inv=0,ccs=UseOption):0:3: ==', - children = { - { - 'UnaryPlus:0:0:+', - children = { - 'PlainIdentifier(scope=0,ident=a):0:1: a', - }, - }, - { - 'UnaryPlus:0:6: +', - children = { - 'PlainIdentifier(scope=0,ident=b):0:8: b', - }, - }, - }, - }, - }, - }, { - hl('UnaryPlus', '+'), - hl('IdentifierName', 'a', 1), - hl('Comparison', '==', 1), - hl('UnaryPlus', '+', 1), - hl('IdentifierName', 'b', 1), - }) - end) - itp('works with concat/subscript', function() - check_parsing('.', { - -- 0 - ast = { - { - 'ConcatOrSubscript:0:0:.', - children = { - 'Missing:0:0:', - }, - }, - }, - err = { - arg = '.', - msg = 'E15: Unexpected dot: %.*s', - }, - }, { - hl('InvalidConcatOrSubscript', '.'), - }) - - check_parsing('a.', { - -- 01 - ast = { - { - 'ConcatOrSubscript:0:1:.', - children = { - 'PlainIdentifier(scope=0,ident=a):0:0:a', - }, - }, - }, - err = { - arg = '', - msg = 'E15: Expected value, got EOC: %.*s', - }, - }, { - hl('IdentifierName', 'a'), - hl('ConcatOrSubscript', '.'), - }) - - check_parsing('a.b', { - -- 012 - ast = { - { - 'ConcatOrSubscript:0:1:.', - children = { - 'PlainIdentifier(scope=0,ident=a):0:0:a', - 'PlainKey(key=b):0:2:b', - }, - }, - }, - }, { - hl('IdentifierName', 'a'), - hl('ConcatOrSubscript', '.'), - hl('IdentifierKey', 'b'), - }) - - check_parsing('1.2', { - -- 012 - ast = { - 'Float(val=1.200000e+00):0:0:1.2', - }, - }, { - hl('Float', '1.2'), - }) - - check_parsing('1.2 + 1.3e-5', { - -- 012345678901 - -- 0 1 - ast = { - { - 'BinaryPlus:0:3: +', - children = { - 'Float(val=1.200000e+00):0:0:1.2', - 'Float(val=1.300000e-05):0:5: 1.3e-5', - }, - }, - }, - }, { - hl('Float', '1.2'), - hl('BinaryPlus', '+', 1), - hl('Float', '1.3e-5', 1), - }) - - check_parsing('a . 1.2 + 1.3e-5', { - -- 0123456789012345 - -- 0 1 - ast = { - { - 'BinaryPlus:0:7: +', - children = { - { - 'Concat:0:1: .', - children = { - 'PlainIdentifier(scope=0,ident=a):0:0:a', - { - 'ConcatOrSubscript:0:5:.', - children = { - 'Integer(val=1):0:3: 1', - 'PlainKey(key=2):0:6:2', - }, - }, - }, - }, - 'Float(val=1.300000e-05):0:9: 1.3e-5', - }, - }, - }, - }, { - hl('IdentifierName', 'a'), - hl('Concat', '.', 1), - hl('Number', '1', 1), - hl('ConcatOrSubscript', '.'), - hl('IdentifierKey', '2'), - hl('BinaryPlus', '+', 1), - hl('Float', '1.3e-5', 1), - }) - - check_parsing('1.3e-5 + 1.2 . a', { - -- 0123456789012345 - -- 0 1 - ast = { - { - 'Concat:0:12: .', - children = { - { - 'BinaryPlus:0:6: +', - children = { - 'Float(val=1.300000e-05):0:0:1.3e-5', - 'Float(val=1.200000e+00):0:8: 1.2', - }, - }, - 'PlainIdentifier(scope=0,ident=a):0:14: a', - }, - }, - }, - }, { - hl('Float', '1.3e-5'), - hl('BinaryPlus', '+', 1), - hl('Float', '1.2', 1), - hl('Concat', '.', 1), - hl('IdentifierName', 'a', 1), - }) - - check_parsing('1.3e-5 + a . 1.2', { - -- 0123456789012345 - -- 0 1 - ast = { - { - 'Concat:0:10: .', - children = { - { - 'BinaryPlus:0:6: +', - children = { - 'Float(val=1.300000e-05):0:0:1.3e-5', - 'PlainIdentifier(scope=0,ident=a):0:8: a', - }, - }, - { - 'ConcatOrSubscript:0:14:.', - children = { - 'Integer(val=1):0:12: 1', - 'PlainKey(key=2):0:15:2', - }, - }, - }, - }, - }, - }, { - hl('Float', '1.3e-5'), - hl('BinaryPlus', '+', 1), - hl('IdentifierName', 'a', 1), - hl('Concat', '.', 1), - hl('Number', '1', 1), - hl('ConcatOrSubscript', '.'), - hl('IdentifierKey', '2'), - }) - - check_parsing('1.2.3', { - -- 01234 - ast = { - { - 'ConcatOrSubscript:0:3:.', - children = { - { - 'ConcatOrSubscript:0:1:.', - children = { - 'Integer(val=1):0:0:1', - 'PlainKey(key=2):0:2:2', - }, - }, - 'PlainKey(key=3):0:4:3', - }, - }, - }, - }, { - hl('Number', '1'), - hl('ConcatOrSubscript', '.'), - hl('IdentifierKey', '2'), - hl('ConcatOrSubscript', '.'), - hl('IdentifierKey', '3'), - }) - - check_parsing('a.1.2', { - -- 01234 - ast = { - { - 'ConcatOrSubscript:0:3:.', - children = { - { - 'ConcatOrSubscript:0:1:.', - children = { - 'PlainIdentifier(scope=0,ident=a):0:0:a', - 'PlainKey(key=1):0:2:1', - }, - }, - 'PlainKey(key=2):0:4:2', - }, - }, - }, - }, { - hl('IdentifierName', 'a'), - hl('ConcatOrSubscript', '.'), - hl('IdentifierKey', '1'), - hl('ConcatOrSubscript', '.'), - hl('IdentifierKey', '2'), - }) - - check_parsing('a . 1.2', { - -- 0123456 - ast = { - { - 'Concat:0:1: .', - children = { - 'PlainIdentifier(scope=0,ident=a):0:0:a', - { - 'ConcatOrSubscript:0:5:.', - children = { - 'Integer(val=1):0:3: 1', - 'PlainKey(key=2):0:6:2', - }, - }, - }, - }, - }, - }, { - hl('IdentifierName', 'a'), - hl('Concat', '.', 1), - hl('Number', '1', 1), - hl('ConcatOrSubscript', '.'), - hl('IdentifierKey', '2'), - }) - - check_parsing('+a . +b', { - -- 0123456 - ast = { - { - 'Concat:0:2: .', - children = { - { - 'UnaryPlus:0:0:+', - children = { - 'PlainIdentifier(scope=0,ident=a):0:1:a', - }, - }, - { - 'UnaryPlus:0:4: +', - children = { - 'PlainIdentifier(scope=0,ident=b):0:6:b', - }, - }, - }, - }, - }, - }, { - hl('UnaryPlus', '+'), - hl('IdentifierName', 'a'), - hl('Concat', '.', 1), - hl('UnaryPlus', '+', 1), - hl('IdentifierName', 'b'), - }) - - check_parsing('a. b', { - -- 0123 - ast = { - { - 'Concat:0:1:.', - children = { - 'PlainIdentifier(scope=0,ident=a):0:0:a', - 'PlainIdentifier(scope=0,ident=b):0:2: b', - }, - }, - }, - }, { - hl('IdentifierName', 'a'), - hl('ConcatOrSubscript', '.'), - hl('IdentifierName', 'b', 1), - }) - - check_parsing('a. 1', { - -- 0123 - ast = { - { - 'Concat:0:1:.', - children = { - 'PlainIdentifier(scope=0,ident=a):0:0:a', - 'Integer(val=1):0:2: 1', - }, - }, - }, - }, { - hl('IdentifierName', 'a'), - hl('ConcatOrSubscript', '.'), - hl('Number', '1', 1), - }) - end) - itp('works with bracket subscripts', function() - check_parsing(':', { - -- 0 - ast = { - { - 'Colon:0:0::', - children = { - 'Missing:0:0:', - }, - }, - }, - err = { - arg = ':', - msg = 'E15: Colon outside of dictionary or ternary operator: %.*s', - }, - }, { - hl('InvalidColon', ':'), - }) - check_parsing('a[]', { - -- 012 - ast = { - { - 'Subscript:0:1:[', - children = { - 'PlainIdentifier(scope=0,ident=a):0:0:a', - }, - }, - }, - err = { - arg = ']', - msg = 'E15: Expected value, got closing bracket: %.*s', - }, - }, { - hl('IdentifierName', 'a'), - hl('SubscriptBracket', '['), - hl('InvalidSubscriptBracket', ']'), - }) - check_parsing('a[b:]', { - -- 01234 - ast = { - { - 'Subscript:0:1:[', - children = { - 'PlainIdentifier(scope=0,ident=a):0:0:a', - 'PlainIdentifier(scope=b,ident=):0:2:b:', - }, - }, - }, - }, { - hl('IdentifierName', 'a'), - hl('SubscriptBracket', '['), - hl('IdentifierScope', 'b'), - hl('IdentifierScopeDelimiter', ':'), - hl('SubscriptBracket', ']'), - }) - - check_parsing('a[b:c]', { - -- 012345 - ast = { - { - 'Subscript:0:1:[', - children = { - 'PlainIdentifier(scope=0,ident=a):0:0:a', - 'PlainIdentifier(scope=b,ident=c):0:2:b:c', - }, - }, - }, - }, { - hl('IdentifierName', 'a'), - hl('SubscriptBracket', '['), - hl('IdentifierScope', 'b'), - hl('IdentifierScopeDelimiter', ':'), - hl('IdentifierName', 'c'), - hl('SubscriptBracket', ']'), - }) - check_parsing('a[b : c]', { - -- 01234567 - ast = { - { - 'Subscript:0:1:[', - children = { - 'PlainIdentifier(scope=0,ident=a):0:0:a', - { - 'Colon:0:3: :', - children = { - 'PlainIdentifier(scope=0,ident=b):0:2:b', - 'PlainIdentifier(scope=0,ident=c):0:5: c', - }, - }, - }, - }, - }, - }, { - hl('IdentifierName', 'a'), - hl('SubscriptBracket', '['), - hl('IdentifierName', 'b'), - hl('SubscriptColon', ':', 1), - hl('IdentifierName', 'c', 1), - hl('SubscriptBracket', ']'), - }) - - check_parsing('a[: b]', { - -- 012345 - ast = { - { - 'Subscript:0:1:[', - children = { - 'PlainIdentifier(scope=0,ident=a):0:0:a', - { - 'Colon:0:2::', - children = { - 'Missing:0:2:', - 'PlainIdentifier(scope=0,ident=b):0:3: b', - }, - }, - }, - }, - }, - }, { - hl('IdentifierName', 'a'), - hl('SubscriptBracket', '['), - hl('SubscriptColon', ':'), - hl('IdentifierName', 'b', 1), - hl('SubscriptBracket', ']'), - }) - - check_parsing('a[b :]', { - -- 012345 - ast = { - { - 'Subscript:0:1:[', - children = { - 'PlainIdentifier(scope=0,ident=a):0:0:a', - { - 'Colon:0:3: :', - children = { - 'PlainIdentifier(scope=0,ident=b):0:2:b', - }, - }, - }, - }, - }, - }, { - hl('IdentifierName', 'a'), - hl('SubscriptBracket', '['), - hl('IdentifierName', 'b'), - hl('SubscriptColon', ':', 1), - hl('SubscriptBracket', ']'), - }) - check_parsing('a[b][c][d](e)(f)(g)', { - -- 0123456789012345678 - -- 0 1 - ast = { - { - 'Call:0:16:(', - children = { - { - 'Call:0:13:(', - children = { - { - 'Call:0:10:(', - children = { - { - 'Subscript:0:7:[', - children = { - { - 'Subscript:0:4:[', - children = { - { - 'Subscript:0:1:[', - children = { - 'PlainIdentifier(scope=0,ident=a):0:0:a', - 'PlainIdentifier(scope=0,ident=b):0:2:b', - }, - }, - 'PlainIdentifier(scope=0,ident=c):0:5:c', - }, - }, - 'PlainIdentifier(scope=0,ident=d):0:8:d', - }, - }, - 'PlainIdentifier(scope=0,ident=e):0:11:e', - }, - }, - 'PlainIdentifier(scope=0,ident=f):0:14:f', - }, - }, - 'PlainIdentifier(scope=0,ident=g):0:17:g', - }, - }, - }, - }, { - hl('IdentifierName', 'a'), - hl('SubscriptBracket', '['), - hl('IdentifierName', 'b'), - hl('SubscriptBracket', ']'), - hl('SubscriptBracket', '['), - hl('IdentifierName', 'c'), - hl('SubscriptBracket', ']'), - hl('SubscriptBracket', '['), - hl('IdentifierName', 'd'), - hl('SubscriptBracket', ']'), - hl('CallingParenthesis', '('), - hl('IdentifierName', 'e'), - hl('CallingParenthesis', ')'), - hl('CallingParenthesis', '('), - hl('IdentifierName', 'f'), - hl('CallingParenthesis', ')'), - hl('CallingParenthesis', '('), - hl('IdentifierName', 'g'), - hl('CallingParenthesis', ')'), - }) - check_parsing('{a}{b}{c}[d][e][f]', { - -- 012345678901234567 - -- 0 1 - ast = { - { - 'Subscript:0:15:[', - children = { - { - 'Subscript:0:12:[', - children = { - { - 'Subscript:0:9:[', - children = { - { - 'ComplexIdentifier:0:3:', - children = { - { - 'CurlyBracesIdentifier(-di):0:0:{', - children = { - 'PlainIdentifier(scope=0,ident=a):0:1:a', - }, - }, - { - 'ComplexIdentifier:0:6:', - children = { - { - 'CurlyBracesIdentifier(--i):0:3:{', - children = { - 'PlainIdentifier(scope=0,ident=b):0:4:b', - }, - }, - { - 'CurlyBracesIdentifier(--i):0:6:{', - children = { - 'PlainIdentifier(scope=0,ident=c):0:7:c', - }, - }, - }, - }, - }, - }, - 'PlainIdentifier(scope=0,ident=d):0:10:d', - }, - }, - 'PlainIdentifier(scope=0,ident=e):0:13:e', - }, - }, - 'PlainIdentifier(scope=0,ident=f):0:16:f', - }, - }, - }, - }, { - hl('Curly', '{'), - hl('IdentifierName', 'a'), - hl('Curly', '}'), - hl('Curly', '{'), - hl('IdentifierName', 'b'), - hl('Curly', '}'), - hl('Curly', '{'), - hl('IdentifierName', 'c'), - hl('Curly', '}'), - hl('SubscriptBracket', '['), - hl('IdentifierName', 'd'), - hl('SubscriptBracket', ']'), - hl('SubscriptBracket', '['), - hl('IdentifierName', 'e'), - hl('SubscriptBracket', ']'), - hl('SubscriptBracket', '['), - hl('IdentifierName', 'f'), - hl('SubscriptBracket', ']'), - }) - end) - itp('supports list literals', function() - check_parsing('[]', { - -- 01 - ast = { - 'ListLiteral:0:0:[', - }, - }, { - hl('List', '['), - hl('List', ']'), - }) - - check_parsing('[a]', { - -- 012 - ast = { - { - 'ListLiteral:0:0:[', - children = { - 'PlainIdentifier(scope=0,ident=a):0:1:a', - }, - }, - }, - }, { - hl('List', '['), - hl('IdentifierName', 'a'), - hl('List', ']'), - }) - - check_parsing('[a, b]', { - -- 012345 - ast = { - { - 'ListLiteral:0:0:[', - children = { - { - 'Comma:0:2:,', - children = { - 'PlainIdentifier(scope=0,ident=a):0:1:a', - 'PlainIdentifier(scope=0,ident=b):0:3: b', - }, - }, - }, - }, - }, - }, { - hl('List', '['), - hl('IdentifierName', 'a'), - hl('Comma', ','), - hl('IdentifierName', 'b', 1), - hl('List', ']'), - }) - - check_parsing('[a, b, c]', { - -- 012345678 - ast = { - { - 'ListLiteral:0:0:[', - children = { - { - 'Comma:0:2:,', - children = { - 'PlainIdentifier(scope=0,ident=a):0:1:a', - { - 'Comma:0:5:,', - children = { - 'PlainIdentifier(scope=0,ident=b):0:3: b', - 'PlainIdentifier(scope=0,ident=c):0:6: c', - }, - }, - }, - }, - }, - }, - }, - }, { - hl('List', '['), - hl('IdentifierName', 'a'), - hl('Comma', ','), - hl('IdentifierName', 'b', 1), - hl('Comma', ','), - hl('IdentifierName', 'c', 1), - hl('List', ']'), - }) - - check_parsing('[a, b, c, ]', { - -- 01234567890 - -- 0 1 - ast = { - { - 'ListLiteral:0:0:[', - children = { - { - 'Comma:0:2:,', - children = { - 'PlainIdentifier(scope=0,ident=a):0:1:a', - { - 'Comma:0:5:,', - children = { - 'PlainIdentifier(scope=0,ident=b):0:3: b', - { - 'Comma:0:8:,', - children = { - 'PlainIdentifier(scope=0,ident=c):0:6: c', - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, { - hl('List', '['), - hl('IdentifierName', 'a'), - hl('Comma', ','), - hl('IdentifierName', 'b', 1), - hl('Comma', ','), - hl('IdentifierName', 'c', 1), - hl('Comma', ','), - hl('List', ']', 1), - }) - - check_parsing('[a : b, c : d]', { - -- 01234567890123 - -- 0 1 - ast = { - { - 'ListLiteral:0:0:[', - children = { - { - 'Comma:0:6:,', - children = { - { - 'Colon:0:2: :', - children = { - 'PlainIdentifier(scope=0,ident=a):0:1:a', - 'PlainIdentifier(scope=0,ident=b):0:4: b', - }, - }, - { - 'Colon:0:9: :', - children = { - 'PlainIdentifier(scope=0,ident=c):0:7: c', - 'PlainIdentifier(scope=0,ident=d):0:11: d', - }, - }, - }, - }, - }, - }, - }, - err = { - arg = ': b, c : d]', - msg = 'E15: Colon outside of dictionary or ternary operator: %.*s', - }, - }, { - hl('List', '['), - hl('IdentifierName', 'a'), - hl('InvalidColon', ':', 1), - hl('IdentifierName', 'b', 1), - hl('Comma', ','), - hl('IdentifierName', 'c', 1), - hl('InvalidColon', ':', 1), - hl('IdentifierName', 'd', 1), - hl('List', ']'), - }) - - check_parsing(']', { - -- 0 - ast = { - 'ListLiteral:0:0:', - }, - err = { - arg = ']', - msg = 'E15: Unexpected closing figure brace: %.*s', - }, - }, { - hl('InvalidList', ']'), - }) - - check_parsing('a]', { - -- 01 - ast = { - { - 'ListLiteral:0:1:', - children = { - 'PlainIdentifier(scope=0,ident=a):0:0:a', - }, - }, - }, - err = { - arg = ']', - msg = 'E15: Unexpected closing figure brace: %.*s', - }, - }, { - hl('IdentifierName', 'a'), - hl('InvalidList', ']'), - }) - - check_parsing('[] []', { - -- 01234 - ast = { - { - 'OpMissing:0:2:', - children = { - 'ListLiteral:0:0:[', - 'ListLiteral:0:2: [', - }, - }, - }, - err = { - arg = '[]', - msg = 'E15: Missing operator: %.*s', - }, - }, { - hl('List', '['), - hl('List', ']'), - hl('InvalidSpacing', ' '), - hl('List', '['), - hl('List', ']'), - }, { - [1] = { - ast = { - err = REMOVE_THIS, - ast = { - 'ListLiteral:0:0:[', - }, - }, - hl_fs = { - [3] = REMOVE_THIS, - [4] = REMOVE_THIS, - [5] = REMOVE_THIS, - }, - }, - }) - - check_parsing('[][]', { - -- 0123 - ast = { - { - 'Subscript:0:2:[', - children = { - 'ListLiteral:0:0:[', - }, - }, - }, - err = { - arg = ']', - msg = 'E15: Expected value, got closing bracket: %.*s', - }, - }, { - hl('List', '['), - hl('List', ']'), - hl('SubscriptBracket', '['), - hl('InvalidSubscriptBracket', ']'), - }) - - check_parsing('[', { - -- 0 - ast = { - 'ListLiteral:0:0:[', - }, - err = { - arg = '', - msg = 'E15: Expected value, got EOC: %.*s', - }, - }, { - hl('List', '['), - }) - - check_parsing('[1', { - -- 01 - ast = { - { - 'ListLiteral:0:0:[', - children = { - 'Integer(val=1):0:1:1', - }, - }, - }, - err = { - arg = '[1', - msg = 'E697: Missing end of List \']\': %.*s', - }, - }, { - hl('List', '['), - hl('Number', '1'), - }) - end) - itp('works with strings', function() - check_parsing('\'abc\'', { - -- 01234 - ast = { - 'SingleQuotedString(val="abc"):0:0:\'abc\'', - }, - }, { - hl('SingleQuote', '\''), - hl('SingleQuotedBody', 'abc'), - hl('SingleQuote', '\''), - }) - check_parsing('"abc"', { - -- 01234 - ast = { - 'DoubleQuotedString(val="abc"):0:0:"abc"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedBody', 'abc'), - hl('DoubleQuote', '"'), - }) - check_parsing('\'\'', { - -- 01 - ast = { - 'SingleQuotedString(val=NULL):0:0:\'\'', - }, - }, { - hl('SingleQuote', '\''), - hl('SingleQuote', '\''), - }) - check_parsing('""', { - -- 01 - ast = { - 'DoubleQuotedString(val=NULL):0:0:""', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuote', '"'), - }) - check_parsing('"', { - -- 0 - ast = { - 'DoubleQuotedString(val=NULL):0:0:"', - }, - err = { - arg = '"', - msg = 'E114: Missing double quote: %.*s', - }, - }, { - hl('InvalidDoubleQuote', '"'), - }) - check_parsing('\'', { - -- 0 - ast = { - 'SingleQuotedString(val=NULL):0:0:\'', - }, - err = { - arg = '\'', - msg = 'E115: Missing single quote: %.*s', - }, - }, { - hl('InvalidSingleQuote', '\''), - }) - check_parsing('"a', { - -- 01 - ast = { - 'DoubleQuotedString(val="a"):0:0:"a', - }, - err = { - arg = '"a', - msg = 'E114: Missing double quote: %.*s', - }, - }, { - hl('InvalidDoubleQuote', '"'), - hl('InvalidDoubleQuotedBody', 'a'), - }) - check_parsing('\'a', { - -- 01 - ast = { - 'SingleQuotedString(val="a"):0:0:\'a', - }, - err = { - arg = '\'a', - msg = 'E115: Missing single quote: %.*s', - }, - }, { - hl('InvalidSingleQuote', '\''), - hl('InvalidSingleQuotedBody', 'a'), - }) - check_parsing('\'abc\'\'def\'', { - -- 0123456789 - ast = { - 'SingleQuotedString(val="abc\'def"):0:0:\'abc\'\'def\'', - }, - }, { - hl('SingleQuote', '\''), - hl('SingleQuotedBody', 'abc'), - hl('SingleQuotedQuote', '\'\''), - hl('SingleQuotedBody', 'def'), - hl('SingleQuote', '\''), - }) - check_parsing('\'abc\'\'', { - -- 012345 - ast = { - 'SingleQuotedString(val="abc\'"):0:0:\'abc\'\'', - }, - err = { - arg = '\'abc\'\'', - msg = 'E115: Missing single quote: %.*s', - }, - }, { - hl('InvalidSingleQuote', '\''), - hl('InvalidSingleQuotedBody', 'abc'), - hl('InvalidSingleQuotedQuote', '\'\''), - }) - check_parsing('\'\'\'\'\'\'\'\'', { - -- 01234567 - ast = { - 'SingleQuotedString(val="\'\'\'"):0:0:\'\'\'\'\'\'\'\'', - }, - }, { - hl('SingleQuote', '\''), - hl('SingleQuotedQuote', '\'\''), - hl('SingleQuotedQuote', '\'\''), - hl('SingleQuotedQuote', '\'\''), - hl('SingleQuote', '\''), - }) - check_parsing('\'\'\'a\'\'\'\'bc\'', { - -- 01234567890 - -- 0 1 - ast = { - 'SingleQuotedString(val="\'a\'\'bc"):0:0:\'\'\'a\'\'\'\'bc\'', - }, - }, { - hl('SingleQuote', '\''), - hl('SingleQuotedQuote', '\'\''), - hl('SingleQuotedBody', 'a'), - hl('SingleQuotedQuote', '\'\''), - hl('SingleQuotedQuote', '\'\''), - hl('SingleQuotedBody', 'bc'), - hl('SingleQuote', '\''), - }) - check_parsing('"\\"\\"\\"\\""', { - -- 0123456789 - ast = { - 'DoubleQuotedString(val="\\"\\"\\"\\""):0:0:"\\"\\"\\"\\""', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedEscape', '\\"'), - hl('DoubleQuotedEscape', '\\"'), - hl('DoubleQuotedEscape', '\\"'), - hl('DoubleQuotedEscape', '\\"'), - hl('DoubleQuote', '"'), - }) - check_parsing('"abc\\"def\\"ghi\\"jkl\\"mno"', { - -- 0123456789012345678901234 - -- 0 1 2 - ast = { - 'DoubleQuotedString(val="abc\\"def\\"ghi\\"jkl\\"mno"):0:0:"abc\\"def\\"ghi\\"jkl\\"mno"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedBody', 'abc'), - hl('DoubleQuotedEscape', '\\"'), - hl('DoubleQuotedBody', 'def'), - hl('DoubleQuotedEscape', '\\"'), - hl('DoubleQuotedBody', 'ghi'), - hl('DoubleQuotedEscape', '\\"'), - hl('DoubleQuotedBody', 'jkl'), - hl('DoubleQuotedEscape', '\\"'), - hl('DoubleQuotedBody', 'mno'), - hl('DoubleQuote', '"'), - }) - check_parsing('"\\b\\e\\f\\r\\t\\\\"', { - -- 0123456789012345 - -- 0 1 - ast = { - [[DoubleQuotedString(val="\8\27\12\13\9\\"):0:0:"\b\e\f\r\t\\"]], - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedEscape', '\\b'), - hl('DoubleQuotedEscape', '\\e'), - hl('DoubleQuotedEscape', '\\f'), - hl('DoubleQuotedEscape', '\\r'), - hl('DoubleQuotedEscape', '\\t'), - hl('DoubleQuotedEscape', '\\\\'), - hl('DoubleQuote', '"'), - }) - check_parsing('"\\n\n"', { - -- 01234 - ast = { - 'DoubleQuotedString(val="\\\n\\\n"):0:0:"\\n\n"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedEscape', '\\n'), - hl('DoubleQuotedBody', '\n'), - hl('DoubleQuote', '"'), - }) - check_parsing('"\\x00"', { - -- 012345 - ast = { - 'DoubleQuotedString(val="\\0"):0:0:"\\x00"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedEscape', '\\x00'), - hl('DoubleQuote', '"'), - }) - check_parsing('"\\xFF"', { - -- 012345 - ast = { - 'DoubleQuotedString(val="\255"):0:0:"\\xFF"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedEscape', '\\xFF'), - hl('DoubleQuote', '"'), - }) - check_parsing('"\\xF"', { - -- 012345 - ast = { - 'DoubleQuotedString(val="\\15"):0:0:"\\xF"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedEscape', '\\xF'), - hl('DoubleQuote', '"'), - }) - check_parsing('"\\u00AB"', { - -- 01234567 - ast = { - 'DoubleQuotedString(val="«"):0:0:"\\u00AB"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedEscape', '\\u00AB'), - hl('DoubleQuote', '"'), - }) - check_parsing('"\\U000000AB"', { - -- 01234567 - ast = { - 'DoubleQuotedString(val="«"):0:0:"\\U000000AB"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedEscape', '\\U000000AB'), - hl('DoubleQuote', '"'), - }) - check_parsing('"\\x"', { - -- 0123 - ast = { - 'DoubleQuotedString(val="x"):0:0:"\\x"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedUnknownEscape', '\\x'), - hl('DoubleQuote', '"'), - }) - - check_parsing('"\\x', { - -- 012 - ast = { - 'DoubleQuotedString(val="x"):0:0:"\\x', - }, - err = { - arg = '"\\x', - msg = 'E114: Missing double quote: %.*s', - }, - }, { - hl('InvalidDoubleQuote', '"'), - hl('InvalidDoubleQuotedUnknownEscape', '\\x'), - }) - - check_parsing('"\\xF', { - -- 0123 - ast = { - 'DoubleQuotedString(val="\\15"):0:0:"\\xF', - }, - err = { - arg = '"\\xF', - msg = 'E114: Missing double quote: %.*s', - }, - }, { - hl('InvalidDoubleQuote', '"'), - hl('InvalidDoubleQuotedEscape', '\\xF'), - }) - - check_parsing('"\\u"', { - -- 0123 - ast = { - 'DoubleQuotedString(val="u"):0:0:"\\u"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedUnknownEscape', '\\u'), - hl('DoubleQuote', '"'), - }) - - check_parsing('"\\u', { - -- 012 - ast = { - 'DoubleQuotedString(val="u"):0:0:"\\u', - }, - err = { - arg = '"\\u', - msg = 'E114: Missing double quote: %.*s', - }, - }, { - hl('InvalidDoubleQuote', '"'), - hl('InvalidDoubleQuotedUnknownEscape', '\\u'), - }) - - check_parsing('"\\U', { - -- 012 - ast = { - 'DoubleQuotedString(val="U"):0:0:"\\U', - }, - err = { - arg = '"\\U', - msg = 'E114: Missing double quote: %.*s', - }, - }, { - hl('InvalidDoubleQuote', '"'), - hl('InvalidDoubleQuotedUnknownEscape', '\\U'), - }) - - check_parsing('"\\U"', { - -- 0123 - ast = { - 'DoubleQuotedString(val="U"):0:0:"\\U"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedUnknownEscape', '\\U'), - hl('DoubleQuote', '"'), - }) - - check_parsing('"\\xFX"', { - -- 012345 - ast = { - 'DoubleQuotedString(val="\\15X"):0:0:"\\xFX"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedEscape', '\\xF'), - hl('DoubleQuotedBody', 'X'), - hl('DoubleQuote', '"'), - }) - - check_parsing('"\\XFX"', { - -- 012345 - ast = { - 'DoubleQuotedString(val="\\15X"):0:0:"\\XFX"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedEscape', '\\XF'), - hl('DoubleQuotedBody', 'X'), - hl('DoubleQuote', '"'), - }) - - check_parsing('"\\xX"', { - -- 01234 - ast = { - 'DoubleQuotedString(val="xX"):0:0:"\\xX"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedUnknownEscape', '\\x'), - hl('DoubleQuotedBody', 'X'), - hl('DoubleQuote', '"'), - }) - - check_parsing('"\\XX"', { - -- 01234 - ast = { - 'DoubleQuotedString(val="XX"):0:0:"\\XX"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedUnknownEscape', '\\X'), - hl('DoubleQuotedBody', 'X'), - hl('DoubleQuote', '"'), - }) - - check_parsing('"\\uX"', { - -- 01234 - ast = { - 'DoubleQuotedString(val="uX"):0:0:"\\uX"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedUnknownEscape', '\\u'), - hl('DoubleQuotedBody', 'X'), - hl('DoubleQuote', '"'), - }) - - check_parsing('"\\UX"', { - -- 01234 - ast = { - 'DoubleQuotedString(val="UX"):0:0:"\\UX"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedUnknownEscape', '\\U'), - hl('DoubleQuotedBody', 'X'), - hl('DoubleQuote', '"'), - }) - - check_parsing('"\\x0X"', { - -- 012345 - ast = { - 'DoubleQuotedString(val="\\0X"):0:0:"\\x0X"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedEscape', '\\x0'), - hl('DoubleQuotedBody', 'X'), - hl('DoubleQuote', '"'), - }) - - check_parsing('"\\X0X"', { - -- 012345 - ast = { - 'DoubleQuotedString(val="\\0X"):0:0:"\\X0X"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedEscape', '\\X0'), - hl('DoubleQuotedBody', 'X'), - hl('DoubleQuote', '"'), - }) - - check_parsing('"\\u0X"', { - -- 012345 - ast = { - 'DoubleQuotedString(val="\\0X"):0:0:"\\u0X"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedEscape', '\\u0'), - hl('DoubleQuotedBody', 'X'), - hl('DoubleQuote', '"'), - }) - - check_parsing('"\\U0X"', { - -- 012345 - ast = { - 'DoubleQuotedString(val="\\0X"):0:0:"\\U0X"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedEscape', '\\U0'), - hl('DoubleQuotedBody', 'X'), - hl('DoubleQuote', '"'), - }) - - check_parsing('"\\x00X"', { - -- 0123456 - ast = { - 'DoubleQuotedString(val="\\0X"):0:0:"\\x00X"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedEscape', '\\x00'), - hl('DoubleQuotedBody', 'X'), - hl('DoubleQuote', '"'), - }) - - check_parsing('"\\X00X"', { - -- 0123456 - ast = { - 'DoubleQuotedString(val="\\0X"):0:0:"\\X00X"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedEscape', '\\X00'), - hl('DoubleQuotedBody', 'X'), - hl('DoubleQuote', '"'), - }) - - check_parsing('"\\u00X"', { - -- 0123456 - ast = { - 'DoubleQuotedString(val="\\0X"):0:0:"\\u00X"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedEscape', '\\u00'), - hl('DoubleQuotedBody', 'X'), - hl('DoubleQuote', '"'), - }) - - check_parsing('"\\U00X"', { - -- 0123456 - ast = { - 'DoubleQuotedString(val="\\0X"):0:0:"\\U00X"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedEscape', '\\U00'), - hl('DoubleQuotedBody', 'X'), - hl('DoubleQuote', '"'), - }) - - check_parsing('"\\u000X"', { - -- 01234567 - ast = { - 'DoubleQuotedString(val="\\0X"):0:0:"\\u000X"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedEscape', '\\u000'), - hl('DoubleQuotedBody', 'X'), - hl('DoubleQuote', '"'), - }) - - check_parsing('"\\U000X"', { - -- 01234567 - ast = { - 'DoubleQuotedString(val="\\0X"):0:0:"\\U000X"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedEscape', '\\U000'), - hl('DoubleQuotedBody', 'X'), - hl('DoubleQuote', '"'), - }) - - check_parsing('"\\u0000X"', { - -- 012345678 - ast = { - 'DoubleQuotedString(val="\\0X"):0:0:"\\u0000X"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedEscape', '\\u0000'), - hl('DoubleQuotedBody', 'X'), - hl('DoubleQuote', '"'), - }) - - check_parsing('"\\U0000X"', { - -- 012345678 - ast = { - 'DoubleQuotedString(val="\\0X"):0:0:"\\U0000X"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedEscape', '\\U0000'), - hl('DoubleQuotedBody', 'X'), - hl('DoubleQuote', '"'), - }) - - check_parsing('"\\U00000X"', { - -- 0123456789 - ast = { - 'DoubleQuotedString(val="\\0X"):0:0:"\\U00000X"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedEscape', '\\U00000'), - hl('DoubleQuotedBody', 'X'), - hl('DoubleQuote', '"'), - }) - - check_parsing('"\\U000000X"', { - -- 01234567890 - -- 0 1 - ast = { - 'DoubleQuotedString(val="\\0X"):0:0:"\\U000000X"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedEscape', '\\U000000'), - hl('DoubleQuotedBody', 'X'), - hl('DoubleQuote', '"'), - }) - - check_parsing('"\\U0000000X"', { - -- 012345678901 - -- 0 1 - ast = { - 'DoubleQuotedString(val="\\0X"):0:0:"\\U0000000X"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedEscape', '\\U0000000'), - hl('DoubleQuotedBody', 'X'), - hl('DoubleQuote', '"'), - }) - - check_parsing('"\\U00000000X"', { - -- 0123456789012 - -- 0 1 - ast = { - 'DoubleQuotedString(val="\\0X"):0:0:"\\U00000000X"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedEscape', '\\U00000000'), - hl('DoubleQuotedBody', 'X'), - hl('DoubleQuote', '"'), - }) - - check_parsing('"\\x000X"', { - -- 01234567 - ast = { - 'DoubleQuotedString(val="\\0000X"):0:0:"\\x000X"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedEscape', '\\x00'), - hl('DoubleQuotedBody', '0X'), - hl('DoubleQuote', '"'), - }) - - check_parsing('"\\X000X"', { - -- 01234567 - ast = { - 'DoubleQuotedString(val="\\0000X"):0:0:"\\X000X"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedEscape', '\\X00'), - hl('DoubleQuotedBody', '0X'), - hl('DoubleQuote', '"'), - }) - - check_parsing('"\\u00000X"', { - -- 0123456789 - ast = { - 'DoubleQuotedString(val="\\0000X"):0:0:"\\u00000X"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedEscape', '\\u0000'), - hl('DoubleQuotedBody', '0X'), - hl('DoubleQuote', '"'), - }) - - check_parsing('"\\U000000000X"', { - -- 01234567890123 - -- 0 1 - ast = { - 'DoubleQuotedString(val="\\0000X"):0:0:"\\U000000000X"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedEscape', '\\U00000000'), - hl('DoubleQuotedBody', '0X'), - hl('DoubleQuote', '"'), - }) - - check_parsing('"\\0"', { - -- 0123 - ast = { - 'DoubleQuotedString(val="\\0"):0:0:"\\0"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedEscape', '\\0'), - hl('DoubleQuote', '"'), - }) - - check_parsing('"\\00"', { - -- 01234 - ast = { - 'DoubleQuotedString(val="\\0"):0:0:"\\00"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedEscape', '\\00'), - hl('DoubleQuote', '"'), - }) - - check_parsing('"\\000"', { - -- 012345 - ast = { - 'DoubleQuotedString(val="\\0"):0:0:"\\000"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedEscape', '\\000'), - hl('DoubleQuote', '"'), - }) - - check_parsing('"\\0000"', { - -- 0123456 - ast = { - 'DoubleQuotedString(val="\\0000"):0:0:"\\0000"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedEscape', '\\000'), - hl('DoubleQuotedBody', '0'), - hl('DoubleQuote', '"'), - }) - - check_parsing('"\\8"', { - -- 0123 - ast = { - 'DoubleQuotedString(val="8"):0:0:"\\8"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedUnknownEscape', '\\8'), - hl('DoubleQuote', '"'), - }) - - check_parsing('"\\08"', { - -- 01234 - ast = { - 'DoubleQuotedString(val="\\0008"):0:0:"\\08"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedEscape', '\\0'), - hl('DoubleQuotedBody', '8'), - hl('DoubleQuote', '"'), - }) - - check_parsing('"\\008"', { - -- 012345 - ast = { - 'DoubleQuotedString(val="\\0008"):0:0:"\\008"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedEscape', '\\00'), - hl('DoubleQuotedBody', '8'), - hl('DoubleQuote', '"'), - }) - - check_parsing('"\\0008"', { - -- 0123456 - ast = { - 'DoubleQuotedString(val="\\0008"):0:0:"\\0008"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedEscape', '\\000'), - hl('DoubleQuotedBody', '8'), - hl('DoubleQuote', '"'), - }) - - check_parsing('"\\777"', { - -- 012345 - ast = { - 'DoubleQuotedString(val="\255"):0:0:"\\777"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedEscape', '\\777'), - hl('DoubleQuote', '"'), - }) - - check_parsing('"\\050"', { - -- 012345 - ast = { - 'DoubleQuotedString(val="\40"):0:0:"\\050"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedEscape', '\\050'), - hl('DoubleQuote', '"'), - }) - - check_parsing('"\\"', { - -- 012345 - ast = { - 'DoubleQuotedString(val="\\21"):0:0:"\\"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedEscape', '\\'), - hl('DoubleQuote', '"'), - }) - - check_parsing('"\\<', { - -- 012 - ast = { - 'DoubleQuotedString(val="<"):0:0:"\\<', - }, - err = { - arg = '"\\<', - msg = 'E114: Missing double quote: %.*s', - }, - }, { - hl('InvalidDoubleQuote', '"'), - hl('InvalidDoubleQuotedUnknownEscape', '\\<'), - }) - - check_parsing('"\\<"', { - -- 0123 - ast = { - 'DoubleQuotedString(val="<"):0:0:"\\<"', - }, - }, { - hl('DoubleQuote', '"'), - hl('DoubleQuotedUnknownEscape', '\\<'), - hl('DoubleQuote', '"'), - }) - - check_parsing('"\\ {b{3}: 4}[5]}()] += 6', { - -- 012345678901234567890123456 - -- 0 1 2 - ast = { - { - 'Assignment(Add):0:22: +=', - children = { - { - 'Subscript:0:1:[', - children = { - 'PlainIdentifier(scope=0,ident=a):0:0:a', - { - 'Call:0:19:(', - children = { - { - 'Lambda(\\di):0:2:{', - children = { - { - 'Arrow:0:3:->', - children = { - { - 'Subscript:0:15:[', - children = { - { - 'DictLiteral(-di):0:5: {', - children = { - { - 'Colon:0:11::', - children = { - { - 'ComplexIdentifier:0:8:', - children = { - 'PlainIdentifier(scope=0,ident=b):0:7:b', - { - 'CurlyBracesIdentifier(--i):0:8:{', - children = { - 'Integer(val=3):0:9:3', - }, - }, - }, - }, - 'Integer(val=4):0:12: 4', - }, - }, - }, - }, - 'Integer(val=5):0:16:5', - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - 'Integer(val=6):0:25: 6', - }, - }, - }, - }, { - hl('IdentifierName', 'a'), - hl('SubscriptBracket', '['), - hl('Lambda', '{'), - hl('Arrow', '->'), - hl('Dict', '{', 1), - hl('IdentifierName', 'b'), - hl('Curly', '{'), - hl('Number', '3'), - hl('Curly', '}'), - hl('Colon', ':'), - hl('Number', '4', 1), - hl('Dict', '}'), - hl('SubscriptBracket', '['), - hl('Number', '5'), - hl('SubscriptBracket', ']'), - hl('Lambda', '}'), - hl('CallingParenthesis', '('), - hl('CallingParenthesis', ')'), - hl('SubscriptBracket', ']'), - hl('AssignmentWithAddition', '+=', 1), - hl('Number', '6', 1), - }) - - check_asgn_parsing('a{1}.2[{-> {b{3}: 4}[5]}()]', { - -- 012345678901234567890123456 - -- 0 1 2 - ast = { - { - 'Subscript:0:6:[', - children = { - { - 'ConcatOrSubscript:0:4:.', - children = { - { - 'ComplexIdentifier:0:1:', - children = { - 'PlainIdentifier(scope=0,ident=a):0:0:a', - { - 'CurlyBracesIdentifier(--i):0:1:{', - children = { - 'Integer(val=1):0:2:1', - }, - }, - }, - }, - 'PlainKey(key=2):0:5:2', - }, - }, - { - 'Call:0:24:(', - children = { - { - 'Lambda(\\di):0:7:{', - children = { - { - 'Arrow:0:8:->', - children = { - { - 'Subscript:0:20:[', - children = { - { - 'DictLiteral(-di):0:10: {', - children = { - { - 'Colon:0:16::', - children = { - { - 'ComplexIdentifier:0:13:', - children = { - 'PlainIdentifier(scope=0,ident=b):0:12:b', - { - 'CurlyBracesIdentifier(--i):0:13:{', - children = { - 'Integer(val=3):0:14:3', - }, - }, - }, - }, - 'Integer(val=4):0:17: 4', - }, - }, - }, - }, - 'Integer(val=5):0:21:5', - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, { - hl('IdentifierName', 'a'), - hl('Curly', '{'), - hl('Number', '1'), - hl('Curly', '}'), - hl('ConcatOrSubscript', '.'), - hl('IdentifierKey', '2'), - hl('SubscriptBracket', '['), - hl('Lambda', '{'), - hl('Arrow', '->'), - hl('Dict', '{', 1), - hl('IdentifierName', 'b'), - hl('Curly', '{'), - hl('Number', '3'), - hl('Curly', '}'), - hl('Colon', ':'), - hl('Number', '4', 1), - hl('Dict', '}'), - hl('SubscriptBracket', '['), - hl('Number', '5'), - hl('SubscriptBracket', ']'), - hl('Lambda', '}'), - hl('CallingParenthesis', '('), - hl('CallingParenthesis', ')'), - hl('SubscriptBracket', ']'), - }) - - check_asgn_parsing('a', { - -- 0 - ast = { - 'PlainIdentifier(scope=0,ident=a):0:0:a', - }, - }, { - hl('IdentifierName', 'a'), - }) - - check_asgn_parsing('{a}', { - -- 012 - ast = { - { - 'CurlyBracesIdentifier(--i):0:0:{', - children = { - 'PlainIdentifier(scope=0,ident=a):0:1:a', - }, - }, - }, - }, { - hl('FigureBrace', '{'), - hl('IdentifierName', 'a'), - hl('Curly', '}'), - }) - - check_asgn_parsing('{a}b', { - -- 0123 - ast = { - { - 'ComplexIdentifier:0:3:', - children = { - { - 'CurlyBracesIdentifier(--i):0:0:{', - children = { - 'PlainIdentifier(scope=0,ident=a):0:1:a', - }, - }, - 'PlainIdentifier(scope=0,ident=b):0:3:b', - }, - }, - }, - }, { - hl('FigureBrace', '{'), - hl('IdentifierName', 'a'), - hl('Curly', '}'), - hl('IdentifierName', 'b'), - }) - - check_asgn_parsing('a{b}c', { - -- 01234 - ast = { - { - 'ComplexIdentifier:0:1:', - children = { - 'PlainIdentifier(scope=0,ident=a):0:0:a', - { - 'ComplexIdentifier:0:4:', - children = { - { - 'CurlyBracesIdentifier(--i):0:1:{', - children = { - 'PlainIdentifier(scope=0,ident=b):0:2:b', - }, - }, - 'PlainIdentifier(scope=0,ident=c):0:4:c', - }, - }, - }, - }, - }, - }, { - hl('IdentifierName', 'a'), - hl('Curly', '{'), - hl('IdentifierName', 'b'), - hl('Curly', '}'), - hl('IdentifierName', 'c'), - }) - - check_asgn_parsing('a{b}c[0]', { - -- 01234567 - ast = { - { - 'Subscript:0:5:[', - children = { - { - 'ComplexIdentifier:0:1:', - children = { - 'PlainIdentifier(scope=0,ident=a):0:0:a', - { - 'ComplexIdentifier:0:4:', - children = { - { - 'CurlyBracesIdentifier(--i):0:1:{', - children = { - 'PlainIdentifier(scope=0,ident=b):0:2:b', - }, - }, - 'PlainIdentifier(scope=0,ident=c):0:4:c', - }, - }, - }, - }, - 'Integer(val=0):0:6:0', - }, - }, - }, - }, { - hl('IdentifierName', 'a'), - hl('Curly', '{'), - hl('IdentifierName', 'b'), - hl('Curly', '}'), - hl('IdentifierName', 'c'), - hl('SubscriptBracket', '['), - hl('Number', '0'), - hl('SubscriptBracket', ']'), - }) - - check_asgn_parsing('a{b}c.0', { - -- 0123456 - ast = { - { - 'ConcatOrSubscript:0:5:.', - children = { - { - 'ComplexIdentifier:0:1:', - children = { - 'PlainIdentifier(scope=0,ident=a):0:0:a', - { - 'ComplexIdentifier:0:4:', - children = { - { - 'CurlyBracesIdentifier(--i):0:1:{', - children = { - 'PlainIdentifier(scope=0,ident=b):0:2:b', - }, - }, - 'PlainIdentifier(scope=0,ident=c):0:4:c', - }, - }, - }, - }, - 'PlainKey(key=0):0:6:0', - }, - }, - }, - }, { - hl('IdentifierName', 'a'), - hl('Curly', '{'), - hl('IdentifierName', 'b'), - hl('Curly', '}'), - hl('IdentifierName', 'c'), - hl('ConcatOrSubscript', '.'), - hl('IdentifierKey', '0'), - }) - - check_asgn_parsing('[a{b}c[0].0]', { - -- 012345678901 - -- 0 1 - ast = { - { - 'ListLiteral:0:0:[', - children = { - { - 'ConcatOrSubscript:0:9:.', - children = { - { - 'Subscript:0:6:[', - children = { - { - 'ComplexIdentifier:0:2:', - children = { - 'PlainIdentifier(scope=0,ident=a):0:1:a', - { - 'ComplexIdentifier:0:5:', - children = { - { - 'CurlyBracesIdentifier(--i):0:2:{', - children = { - 'PlainIdentifier(scope=0,ident=b):0:3:b', - }, - }, - 'PlainIdentifier(scope=0,ident=c):0:5:c', - }, - }, - }, - }, - 'Integer(val=0):0:7:0', - }, - }, - 'PlainKey(key=0):0:10:0', - }, - }, - }, - }, - }, - }, { - hl('List', '['), - hl('IdentifierName', 'a'), - hl('Curly', '{'), - hl('IdentifierName', 'b'), - hl('Curly', '}'), - hl('IdentifierName', 'c'), - hl('SubscriptBracket', '['), - hl('Number', '0'), - hl('SubscriptBracket', ']'), - hl('ConcatOrSubscript', '.'), - hl('IdentifierKey', '0'), - hl('List', ']'), - }) - - check_asgn_parsing('{a}{b}', { - -- 012345 - ast = { - { - 'ComplexIdentifier:0:3:', - children = { - { - 'CurlyBracesIdentifier(--i):0:0:{', - children = { - 'PlainIdentifier(scope=0,ident=a):0:1:a', - }, - }, - { - 'CurlyBracesIdentifier(--i):0:3:{', - children = { - 'PlainIdentifier(scope=0,ident=b):0:4:b', - }, - }, - }, - }, - }, - }, { - hl('FigureBrace', '{'), - hl('IdentifierName', 'a'), - hl('Curly', '}'), - hl('Curly', '{'), - hl('IdentifierName', 'b'), - hl('Curly', '}'), - }) - - check_asgn_parsing('a.b{c}{d}', { - -- 012345678 - ast = { - { - 'OpMissing:0:3:', - children = { - { - 'ConcatOrSubscript:0:1:.', - children = { - 'PlainIdentifier(scope=0,ident=a):0:0:a', - 'PlainKey(key=b):0:2:b', - }, - }, - { - 'ComplexIdentifier:0:6:', - children = { - { - 'CurlyBracesIdentifier(--i):0:3:{', - children = { - 'PlainIdentifier(scope=0,ident=c):0:4:c', - }, - }, - { - 'CurlyBracesIdentifier(--i):0:6:{', - children = { - 'PlainIdentifier(scope=0,ident=d):0:7:d', - }, - }, - }, - }, - }, - }, - }, - err = { - arg = '{c}{d}', - msg = 'E15: Missing operator: %.*s', - }, - }, { - hl('IdentifierName', 'a'), - hl('ConcatOrSubscript', '.'), - hl('IdentifierKey', 'b'), - hl('InvalidFigureBrace', '{'), - hl('IdentifierName', 'c'), - hl('Curly', '}'), - hl('Curly', '{'), - hl('IdentifierName', 'd'), - hl('Curly', '}'), - }) - - check_asgn_parsing('[a] = 1', { - -- 0123456 - ast = { - { - 'Assignment(Plain):0:3: =', - children = { - { - 'ListLiteral:0:0:[', - children = { - 'PlainIdentifier(scope=0,ident=a):0:1:a', - }, - }, - 'Integer(val=1):0:5: 1', - }, - }, - }, - }, { - hl('List', '['), - hl('IdentifierName', 'a'), - hl('List', ']'), - hl('PlainAssignment', '=', 1), - hl('Number', '1', 1), - }) - - check_asgn_parsing('[a[b], [c, [d, [e]]]] = 1', { - -- 0123456789012345678901234 - -- 0 1 2 - ast = { - { - 'Assignment(Plain):0:21: =', - children = { - { - 'ListLiteral:0:0:[', - children = { - { - 'Comma:0:5:,', - children = { - { - 'Subscript:0:2:[', - children = { - 'PlainIdentifier(scope=0,ident=a):0:1:a', - 'PlainIdentifier(scope=0,ident=b):0:3:b', - }, - }, - { - 'ListLiteral:0:6: [', - children = { - { - 'Comma:0:9:,', - children = { - 'PlainIdentifier(scope=0,ident=c):0:8:c', - { - 'ListLiteral:0:10: [', - children = { - { - 'Comma:0:13:,', - children = { - 'PlainIdentifier(scope=0,ident=d):0:12:d', - { - 'ListLiteral:0:14: [', - children = { - 'PlainIdentifier(scope=0,ident=e):0:16:e', - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - 'Integer(val=1):0:23: 1', - }, - }, - }, - err = { - arg = '[c, [d, [e]]]] = 1', - msg = 'E475: Nested lists not allowed when assigning: %.*s', - }, - }, { - hl('List', '['), - hl('IdentifierName', 'a'), - hl('SubscriptBracket', '['), - hl('IdentifierName', 'b'), - hl('SubscriptBracket', ']'), - hl('Comma', ','), - hl('InvalidList', '[', 1), - hl('IdentifierName', 'c'), - hl('Comma', ','), - hl('InvalidList', '[', 1), - hl('IdentifierName', 'd'), - hl('Comma', ','), - hl('InvalidList', '[', 1), - hl('IdentifierName', 'e'), - hl('List', ']'), - hl('List', ']'), - hl('List', ']'), - hl('List', ']'), - hl('PlainAssignment', '=', 1), - hl('Number', '1', 1), - }) - - check_asgn_parsing('$X += 1', { - -- 0123456 - ast = { - { - 'Assignment(Add):0:2: +=', - children = { - 'Environment(ident=X):0:0:$X', - 'Integer(val=1):0:5: 1', - }, - }, - }, - }, { - hl('EnvironmentSigil', '$'), - hl('EnvironmentName', 'X'), - hl('AssignmentWithAddition', '+=', 1), - hl('Number', '1', 1), - }) - - check_asgn_parsing('@a .= 1', { - -- 0123456 - ast = { - { - 'Assignment(Concat):0:2: .=', - children = { - 'Register(name=a):0:0:@a', - 'Integer(val=1):0:5: 1', - }, - }, - }, - }, { - hl('Register', '@a'), - hl('AssignmentWithConcatenation', '.=', 1), - hl('Number', '1', 1), - }) - - check_asgn_parsing('&option -= 1', { - -- 012345678901 - -- 0 1 - ast = { - { - 'Assignment(Subtract):0:7: -=', - children = { - 'Option(scope=0,ident=option):0:0:&option', - 'Integer(val=1):0:10: 1', - }, - }, - }, - }, { - hl('OptionSigil', '&'), - hl('OptionName', 'option'), - hl('AssignmentWithSubtraction', '-=', 1), - hl('Number', '1', 1), - }) - - check_asgn_parsing('[$X, @a, &l:option] = [1, 2, 3]', { - -- 0123456789012345678901234567890 - -- 0 1 2 3 - ast = { - { - 'Assignment(Plain):0:19: =', - children = { - { - 'ListLiteral:0:0:[', - children = { - { - 'Comma:0:3:,', - children = { - 'Environment(ident=X):0:1:$X', - { - 'Comma:0:7:,', - children = { - 'Register(name=a):0:4: @a', - 'Option(scope=l,ident=option):0:8: &l:option', - }, - }, - }, - }, - }, - }, - { - 'ListLiteral:0:21: [', - children = { - { - 'Comma:0:24:,', - children = { - 'Integer(val=1):0:23:1', - { - 'Comma:0:27:,', - children = { - 'Integer(val=2):0:25: 2', - 'Integer(val=3):0:28: 3', - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, { - hl('List', '['), - hl('EnvironmentSigil', '$'), - hl('EnvironmentName', 'X'), - hl('Comma', ','), - hl('Register', '@a', 1), - hl('Comma', ','), - hl('OptionSigil', '&', 1), - hl('OptionScope', 'l'), - hl('OptionScopeDelimiter', ':'), - hl('OptionName', 'option'), - hl('List', ']'), - hl('PlainAssignment', '=', 1), - hl('List', '[', 1), - hl('Number', '1'), - hl('Comma', ','), - hl('Number', '2', 1), - hl('Comma', ','), - hl('Number', '3', 1), - hl('List', ']'), - }) - end) - -- FIXME: Somehow make functional tests use the same code. Or, at least, - -- create an automated script which will do the import. + local function fmtn(typ, args, rest) + return ('%s(%s)%s'):format(typ, args, rest) + end + require('test.unit.viml.expressions.parser_tests')( + itp, _check_parsing, hl, fmtn) end) diff --git a/test/unit/viml/expressions/parser_tests.lua b/test/unit/viml/expressions/parser_tests.lua new file mode 100644 index 0000000000..f0c2723a38 --- /dev/null +++ b/test/unit/viml/expressions/parser_tests.lua @@ -0,0 +1,8185 @@ +local global_helpers = require('test.helpers') + +local REMOVE_THIS = global_helpers.REMOVE_THIS + +return function(itp, _check_parsing, hl, fmtn) + local function check_parsing(...) + return _check_parsing({flags={0, 1, 2, 3}, funcname='check_parsing'}, ...) + end + local function check_asgn_parsing(...) + return _check_parsing({ + flags={4, 5, 6, 7}, + funcname='check_asgn_parsing', + }, ...) + end + itp('works with + and @a', function() + check_parsing('@a', { + ast = { + 'Register(name=a):0:0:@a', + }, + }, { + hl('Register', '@a'), + }) + check_parsing('+@a', { + ast = { + { + 'UnaryPlus:0:0:+', + children = { + 'Register(name=a):0:1:@a', + }, + }, + }, + }, { + hl('UnaryPlus', '+'), + hl('Register', '@a'), + }) + check_parsing('@a+@b', { + ast = { + { + 'BinaryPlus:0:2:+', + children = { + 'Register(name=a):0:0:@a', + 'Register(name=b):0:3:@b', + }, + }, + }, + }, { + hl('Register', '@a'), + hl('BinaryPlus', '+'), + hl('Register', '@b'), + }) + check_parsing('@a+@b+@c', { + ast = { + { + 'BinaryPlus:0:5:+', + children = { + { + 'BinaryPlus:0:2:+', + children = { + 'Register(name=a):0:0:@a', + 'Register(name=b):0:3:@b', + }, + }, + 'Register(name=c):0:6:@c', + }, + }, + }, + }, { + hl('Register', '@a'), + hl('BinaryPlus', '+'), + hl('Register', '@b'), + hl('BinaryPlus', '+'), + hl('Register', '@c'), + }) + check_parsing('+@a+@b', { + ast = { + { + 'BinaryPlus:0:3:+', + children = { + { + 'UnaryPlus:0:0:+', + children = { + 'Register(name=a):0:1:@a', + }, + }, + 'Register(name=b):0:4:@b', + }, + }, + }, + }, { + hl('UnaryPlus', '+'), + hl('Register', '@a'), + hl('BinaryPlus', '+'), + hl('Register', '@b'), + }) + check_parsing('+@a++@b', { + ast = { + { + 'BinaryPlus:0:3:+', + children = { + { + 'UnaryPlus:0:0:+', + children = { + 'Register(name=a):0:1:@a', + }, + }, + { + 'UnaryPlus:0:4:+', + children = { + 'Register(name=b):0:5:@b', + }, + }, + }, + }, + }, + }, { + hl('UnaryPlus', '+'), + hl('Register', '@a'), + hl('BinaryPlus', '+'), + hl('UnaryPlus', '+'), + hl('Register', '@b'), + }) + check_parsing('@a@b', { + ast = { + { + 'OpMissing:0:2:', + children = { + 'Register(name=a):0:0:@a', + 'Register(name=b):0:2:@b', + }, + }, + }, + err = { + arg = '@b', + msg = 'E15: Missing operator: %.*s', + }, + }, { + hl('Register', '@a'), + hl('InvalidRegister', '@b'), + }, { + [1] = { + ast = { + len = 2, + err = REMOVE_THIS, + ast = { + 'Register(name=a):0:0:@a' + }, + }, + hl_fs = { + [2] = REMOVE_THIS, + }, + }, + }) + check_parsing(' @a \t @b', { + ast = { + { + 'OpMissing:0:3:', + children = { + 'Register(name=a):0:0: @a', + 'Register(name=b):0:3: \t @b', + }, + }, + }, + err = { + arg = '@b', + msg = 'E15: Missing operator: %.*s', + }, + }, { + hl('Register', '@a', 1), + hl('InvalidSpacing', ' \t '), + hl('Register', '@b'), + }, { + [1] = { + ast = { + len = 6, + err = REMOVE_THIS, + ast = { + 'Register(name=a):0:0: @a' + }, + }, + hl_fs = { + [2] = REMOVE_THIS, + [3] = REMOVE_THIS, + }, + }, + }) + check_parsing('+', { + ast = { + 'UnaryPlus:0:0:+', + }, + err = { + arg = '', + msg = 'E15: Expected value, got EOC: %.*s', + }, + }, { + hl('UnaryPlus', '+'), + }) + check_parsing(' +', { + ast = { + 'UnaryPlus:0:0: +', + }, + err = { + arg = '', + msg = 'E15: Expected value, got EOC: %.*s', + }, + }, { + hl('UnaryPlus', '+', 1), + }) + check_parsing('@a+ ', { + ast = { + { + 'BinaryPlus:0:2:+', + children = { + 'Register(name=a):0:0:@a', + }, + }, + }, + err = { + arg = '', + msg = 'E15: Expected value, got EOC: %.*s', + }, + }, { + hl('Register', '@a'), + hl('BinaryPlus', '+'), + }) + end) + itp('works with @a, + and parenthesis', function() + check_parsing('(@a)', { + ast = { + { + 'Nested:0:0:(', + children = { + 'Register(name=a):0:1:@a', + }, + }, + }, + }, { + hl('NestingParenthesis', '('), + hl('Register', '@a'), + hl('NestingParenthesis', ')'), + }) + check_parsing('()', { + ast = { + { + 'Nested:0:0:(', + children = { + 'Missing:0:1:', + }, + }, + }, + err = { + arg = ')', + msg = 'E15: Expected value, got parenthesis: %.*s', + }, + }, { + hl('NestingParenthesis', '('), + hl('InvalidNestingParenthesis', ')'), + }) + check_parsing(')', { + ast = { + { + 'Nested:0:0:', + children = { + 'Missing:0:0:', + }, + }, + }, + err = { + arg = ')', + msg = 'E15: Expected value, got parenthesis: %.*s', + }, + }, { + hl('InvalidNestingParenthesis', ')'), + }) + check_parsing('+)', { + ast = { + { + 'Nested:0:1:', + children = { + { + 'UnaryPlus:0:0:+', + children = { + 'Missing:0:1:', + }, + }, + }, + }, + }, + err = { + arg = ')', + msg = 'E15: Expected value, got parenthesis: %.*s', + }, + }, { + hl('UnaryPlus', '+'), + hl('InvalidNestingParenthesis', ')'), + }) + check_parsing('+@a(@b)', { + ast = { + { + 'UnaryPlus:0:0:+', + children = { + { + 'Call:0:3:(', + children = { + 'Register(name=a):0:1:@a', + 'Register(name=b):0:4:@b', + }, + }, + }, + }, + }, + }, { + hl('UnaryPlus', '+'), + hl('Register', '@a'), + hl('CallingParenthesis', '('), + hl('Register', '@b'), + hl('CallingParenthesis', ')'), + }) + check_parsing('@a+@b(@c)', { + ast = { + { + 'BinaryPlus:0:2:+', + children = { + 'Register(name=a):0:0:@a', + { + 'Call:0:5:(', + children = { + 'Register(name=b):0:3:@b', + 'Register(name=c):0:6:@c', + }, + }, + }, + }, + }, + }, { + hl('Register', '@a'), + hl('BinaryPlus', '+'), + hl('Register', '@b'), + hl('CallingParenthesis', '('), + hl('Register', '@c'), + hl('CallingParenthesis', ')'), + }) + check_parsing('@a()', { + ast = { + { + 'Call:0:2:(', + children = { + 'Register(name=a):0:0:@a', + }, + }, + }, + }, { + hl('Register', '@a'), + hl('CallingParenthesis', '('), + hl('CallingParenthesis', ')'), + }) + check_parsing('@a ()', { + ast = { + { + 'OpMissing:0:2:', + children = { + 'Register(name=a):0:0:@a', + { + 'Nested:0:2: (', + children = { + 'Missing:0:4:', + }, + }, + }, + }, + }, + err = { + arg = '()', + msg = 'E15: Missing operator: %.*s', + }, + }, { + hl('Register', '@a'), + hl('InvalidSpacing', ' '), + hl('NestingParenthesis', '('), + hl('InvalidNestingParenthesis', ')'), + }, { + [1] = { + ast = { + len = 3, + err = REMOVE_THIS, + ast = { + 'Register(name=a):0:0:@a', + }, + }, + hl_fs = { + [2] = REMOVE_THIS, + [3] = REMOVE_THIS, + [4] = REMOVE_THIS, + }, + }, + }) + check_parsing('@a + (@b)', { + ast = { + { + 'BinaryPlus:0:2: +', + children = { + 'Register(name=a):0:0:@a', + { + 'Nested:0:4: (', + children = { + 'Register(name=b):0:6:@b', + }, + }, + }, + }, + }, + }, { + hl('Register', '@a'), + hl('BinaryPlus', '+', 1), + hl('NestingParenthesis', '(', 1), + hl('Register', '@b'), + hl('NestingParenthesis', ')'), + }) + check_parsing('@a + (+@b)', { + ast = { + { + 'BinaryPlus:0:2: +', + children = { + 'Register(name=a):0:0:@a', + { + 'Nested:0:4: (', + children = { + { + 'UnaryPlus:0:6:+', + children = { + 'Register(name=b):0:7:@b', + }, + }, + }, + }, + }, + }, + }, + }, { + hl('Register', '@a'), + hl('BinaryPlus', '+', 1), + hl('NestingParenthesis', '(', 1), + hl('UnaryPlus', '+'), + hl('Register', '@b'), + hl('NestingParenthesis', ')'), + }) + check_parsing('@a + (@b + @c)', { + ast = { + { + 'BinaryPlus:0:2: +', + children = { + 'Register(name=a):0:0:@a', + { + 'Nested:0:4: (', + children = { + { + 'BinaryPlus:0:8: +', + children = { + 'Register(name=b):0:6:@b', + 'Register(name=c):0:10: @c', + }, + }, + }, + }, + }, + }, + }, + }, { + hl('Register', '@a'), + hl('BinaryPlus', '+', 1), + hl('NestingParenthesis', '(', 1), + hl('Register', '@b'), + hl('BinaryPlus', '+', 1), + hl('Register', '@c', 1), + hl('NestingParenthesis', ')'), + }) + check_parsing('(@a)+@b', { + ast = { + { + 'BinaryPlus:0:4:+', + children = { + { + 'Nested:0:0:(', + children = { + 'Register(name=a):0:1:@a', + }, + }, + 'Register(name=b):0:5:@b', + }, + }, + }, + }, { + hl('NestingParenthesis', '('), + hl('Register', '@a'), + hl('NestingParenthesis', ')'), + hl('BinaryPlus', '+'), + hl('Register', '@b'), + }) + check_parsing('@a+(@b)(@c)', { + -- 01234567890 + ast = { + { + 'BinaryPlus:0:2:+', + children = { + 'Register(name=a):0:0:@a', + { + 'Call:0:7:(', + children = { + { + 'Nested:0:3:(', + children = { 'Register(name=b):0:4:@b' }, + }, + 'Register(name=c):0:8:@c', + }, + }, + }, + }, + }, + }, { + hl('Register', '@a'), + hl('BinaryPlus', '+'), + hl('NestingParenthesis', '('), + hl('Register', '@b'), + hl('NestingParenthesis', ')'), + hl('CallingParenthesis', '('), + hl('Register', '@c'), + hl('CallingParenthesis', ')'), + }) + check_parsing('@a+((@b))(@c)', { + -- 01234567890123456890123456789 + -- 0 1 2 + ast = { + { + 'BinaryPlus:0:2:+', + children = { + 'Register(name=a):0:0:@a', + { + 'Call:0:9:(', + children = { + { + 'Nested:0:3:(', + children = { + { + 'Nested:0:4:(', + children = { 'Register(name=b):0:5:@b' } + }, + }, + }, + 'Register(name=c):0:10:@c', + }, + }, + }, + }, + }, + }, { + hl('Register', '@a'), + hl('BinaryPlus', '+'), + hl('NestingParenthesis', '('), + hl('NestingParenthesis', '('), + hl('Register', '@b'), + hl('NestingParenthesis', ')'), + hl('NestingParenthesis', ')'), + hl('CallingParenthesis', '('), + hl('Register', '@c'), + hl('CallingParenthesis', ')'), + }) + check_parsing('@a+((@b))+@c', { + -- 01234567890123456890123456789 + -- 0 1 2 + ast = { + { + 'BinaryPlus:0:9:+', + children = { + { + 'BinaryPlus:0:2:+', + children = { + 'Register(name=a):0:0:@a', + { + 'Nested:0:3:(', + children = { + { + 'Nested:0:4:(', + children = { 'Register(name=b):0:5:@b' } + }, + }, + }, + }, + }, + 'Register(name=c):0:10:@c', + }, + }, + }, + }, { + hl('Register', '@a'), + hl('BinaryPlus', '+'), + hl('NestingParenthesis', '('), + hl('NestingParenthesis', '('), + hl('Register', '@b'), + hl('NestingParenthesis', ')'), + hl('NestingParenthesis', ')'), + hl('BinaryPlus', '+'), + hl('Register', '@c'), + }) + check_parsing( + '@a + (@b + @c) + @d(@e) + (+@f) + ((+@g(@h))(@j)(@k))(@l)', {--[[ + | | | | | | | | || | | || | | ||| || || || || + 000000000011111111112222222222333333333344444444445555555 + 012345678901234567890123456789012345678901234567890123456 + ]] + ast = {{ + 'BinaryPlus:0:31: +', + children = { + { + 'BinaryPlus:0:23: +', + children = { + { + 'BinaryPlus:0:14: +', + children = { + { + 'BinaryPlus:0:2: +', + children = { + 'Register(name=a):0:0:@a', + { + 'Nested:0:4: (', + children = { + { + 'BinaryPlus:0:8: +', + children = { + 'Register(name=b):0:6:@b', + 'Register(name=c):0:10: @c', + }, + }, + }, + }, + }, + }, + { + 'Call:0:19:(', + children = { + 'Register(name=d):0:16: @d', + 'Register(name=e):0:20:@e', + }, + }, + }, + }, + { + 'Nested:0:25: (', + children = { + { + 'UnaryPlus:0:27:+', + children = { + 'Register(name=f):0:28:@f', + }, + }, + }, + }, + }, + }, + { + 'Call:0:53:(', + children = { + { + 'Nested:0:33: (', + children = { + { + 'Call:0:48:(', + children = { + { + 'Call:0:44:(', + children = { + { + 'Nested:0:35:(', + children = { + { + 'UnaryPlus:0:36:+', + children = { + { + 'Call:0:39:(', + children = { + 'Register(name=g):0:37:@g', + 'Register(name=h):0:40:@h', + }, + }, + }, + }, + }, + }, + 'Register(name=j):0:45:@j', + }, + }, + 'Register(name=k):0:49:@k', + }, + }, + }, + }, + 'Register(name=l):0:54:@l', + }, + }, + }, + }}, + }, { + hl('Register', '@a'), + hl('BinaryPlus', '+', 1), + hl('NestingParenthesis', '(', 1), + hl('Register', '@b'), + hl('BinaryPlus', '+', 1), + hl('Register', '@c', 1), + hl('NestingParenthesis', ')'), + hl('BinaryPlus', '+', 1), + hl('Register', '@d', 1), + hl('CallingParenthesis', '('), + hl('Register', '@e'), + hl('CallingParenthesis', ')'), + hl('BinaryPlus', '+', 1), + hl('NestingParenthesis', '(', 1), + hl('UnaryPlus', '+'), + hl('Register', '@f'), + hl('NestingParenthesis', ')'), + hl('BinaryPlus', '+', 1), + hl('NestingParenthesis', '(', 1), + hl('NestingParenthesis', '('), + hl('UnaryPlus', '+'), + hl('Register', '@g'), + hl('CallingParenthesis', '('), + hl('Register', '@h'), + hl('CallingParenthesis', ')'), + hl('NestingParenthesis', ')'), + hl('CallingParenthesis', '('), + hl('Register', '@j'), + hl('CallingParenthesis', ')'), + hl('CallingParenthesis', '('), + hl('Register', '@k'), + hl('CallingParenthesis', ')'), + hl('NestingParenthesis', ')'), + hl('CallingParenthesis', '('), + hl('Register', '@l'), + hl('CallingParenthesis', ')'), + }) + check_parsing('@a)', { + -- 012 + ast = { + { + 'Nested:0:2:', + children = { + 'Register(name=a):0:0:@a', + }, + }, + }, + err = { + arg = ')', + msg = 'E15: Unexpected closing parenthesis: %.*s', + }, + }, { + hl('Register', '@a'), + hl('InvalidNestingParenthesis', ')'), + }) + check_parsing('(@a', { + -- 012 + ast = { + { + 'Nested:0:0:(', + children = { + 'Register(name=a):0:1:@a', + }, + }, + }, + err = { + arg = '(@a', + msg = 'E110: Missing closing parenthesis for nested expression: %.*s', + }, + }, { + hl('NestingParenthesis', '('), + hl('Register', '@a'), + }) + check_parsing('@a(@b', { + -- 01234 + ast = { + { + 'Call:0:2:(', + children = { + 'Register(name=a):0:0:@a', + 'Register(name=b):0:3:@b', + }, + }, + }, + err = { + arg = '(@b', + msg = 'E116: Missing closing parenthesis for function call: %.*s', + }, + }, { + hl('Register', '@a'), + hl('CallingParenthesis', '('), + hl('Register', '@b'), + }) + check_parsing('@a(@b, @c, @d, @e)', { + -- 012345678901234567 + -- 0 1 + ast = { + { + 'Call:0:2:(', + children = { + 'Register(name=a):0:0:@a', + { + 'Comma:0:5:,', + children = { + 'Register(name=b):0:3:@b', + { + 'Comma:0:9:,', + children = { + 'Register(name=c):0:6: @c', + { + 'Comma:0:13:,', + children = { + 'Register(name=d):0:10: @d', + 'Register(name=e):0:14: @e', + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, { + hl('Register', '@a'), + hl('CallingParenthesis', '('), + hl('Register', '@b'), + hl('Comma', ','), + hl('Register', '@c', 1), + hl('Comma', ','), + hl('Register', '@d', 1), + hl('Comma', ','), + hl('Register', '@e', 1), + hl('CallingParenthesis', ')'), + }) + check_parsing('@a(@b(@c))', { + -- 01234567890123456789012345678901234567 + -- 0 1 2 3 + ast = { + { + 'Call:0:2:(', + children = { + 'Register(name=a):0:0:@a', + { + 'Call:0:5:(', + children = { + 'Register(name=b):0:3:@b', + 'Register(name=c):0:6:@c', + }, + }, + }, + }, + }, + }, { + hl('Register', '@a'), + hl('CallingParenthesis', '('), + hl('Register', '@b'), + hl('CallingParenthesis', '('), + hl('Register', '@c'), + hl('CallingParenthesis', ')'), + hl('CallingParenthesis', ')'), + }) + check_parsing('@a(@b(@c(@d(@e), @f(@g(@h), @i(@j)))))', { + -- 01234567890123456789012345678901234567 + -- 0 1 2 3 + ast = { + { + 'Call:0:2:(', + children = { + 'Register(name=a):0:0:@a', + { + 'Call:0:5:(', + children = { + 'Register(name=b):0:3:@b', + { + 'Call:0:8:(', + children = { + 'Register(name=c):0:6:@c', + { + 'Comma:0:15:,', + children = { + { + 'Call:0:11:(', + children = { + 'Register(name=d):0:9:@d', + 'Register(name=e):0:12:@e', + }, + }, + { + 'Call:0:19:(', + children = { + 'Register(name=f):0:16: @f', + { + 'Comma:0:26:,', + children = { + { + 'Call:0:22:(', + children = { + 'Register(name=g):0:20:@g', + 'Register(name=h):0:23:@h', + }, + }, + { + 'Call:0:30:(', + children = { + 'Register(name=i):0:27: @i', + 'Register(name=j):0:31:@j', + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, { + hl('Register', '@a'), + hl('CallingParenthesis', '('), + hl('Register', '@b'), + hl('CallingParenthesis', '('), + hl('Register', '@c'), + hl('CallingParenthesis', '('), + hl('Register', '@d'), + hl('CallingParenthesis', '('), + hl('Register', '@e'), + hl('CallingParenthesis', ')'), + hl('Comma', ','), + hl('Register', '@f', 1), + hl('CallingParenthesis', '('), + hl('Register', '@g'), + hl('CallingParenthesis', '('), + hl('Register', '@h'), + hl('CallingParenthesis', ')'), + hl('Comma', ','), + hl('Register', '@i', 1), + hl('CallingParenthesis', '('), + hl('Register', '@j'), + hl('CallingParenthesis', ')'), + hl('CallingParenthesis', ')'), + hl('CallingParenthesis', ')'), + hl('CallingParenthesis', ')'), + hl('CallingParenthesis', ')'), + }) + check_parsing('()()', { + -- 0123 + ast = { + { + 'Call:0:2:(', + children = { + { + 'Nested:0:0:(', + children = { + 'Missing:0:1:', + }, + }, + }, + }, + }, + err = { + arg = ')()', + msg = 'E15: Expected value, got parenthesis: %.*s', + }, + }, { + hl('NestingParenthesis', '('), + hl('InvalidNestingParenthesis', ')'), + hl('CallingParenthesis', '('), + hl('CallingParenthesis', ')'), + }) + check_parsing('(@a)()', { + -- 012345 + ast = { + { + 'Call:0:4:(', + children = { + { + 'Nested:0:0:(', + children = { + 'Register(name=a):0:1:@a', + }, + }, + }, + }, + }, + }, { + hl('NestingParenthesis', '('), + hl('Register', '@a'), + hl('NestingParenthesis', ')'), + hl('CallingParenthesis', '('), + hl('CallingParenthesis', ')'), + }) + check_parsing('(@a)(@b)', { + -- 01234567 + ast = { + { + 'Call:0:4:(', + children = { + { + 'Nested:0:0:(', + children = { + 'Register(name=a):0:1:@a', + }, + }, + 'Register(name=b):0:5:@b', + }, + }, + }, + }, { + hl('NestingParenthesis', '('), + hl('Register', '@a'), + hl('NestingParenthesis', ')'), + hl('CallingParenthesis', '('), + hl('Register', '@b'), + hl('CallingParenthesis', ')'), + }) + check_parsing('(@a) (@b)', { + -- 012345678 + ast = { + { + 'OpMissing:0:4:', + children = { + { + 'Nested:0:0:(', + children = { + 'Register(name=a):0:1:@a', + }, + }, + { + 'Nested:0:4: (', + children = { + 'Register(name=b):0:6:@b', + }, + }, + }, + }, + }, + err = { + arg = '(@b)', + msg = 'E15: Missing operator: %.*s', + }, + }, { + hl('NestingParenthesis', '('), + hl('Register', '@a'), + hl('NestingParenthesis', ')'), + hl('InvalidSpacing', ' '), + hl('NestingParenthesis', '('), + hl('Register', '@b'), + hl('NestingParenthesis', ')'), + }, { + [1] = { + ast = { + len = 5, + ast = { + { + 'Nested:0:0:(', + children = { + 'Register(name=a):0:1:@a', + REMOVE_THIS, + }, + }, + }, + err = REMOVE_THIS, + }, + hl_fs = { + [4] = REMOVE_THIS, + [5] = REMOVE_THIS, + [6] = REMOVE_THIS, + [7] = REMOVE_THIS, + }, + }, + }) + end) + itp('works with variable names, including curly braces ones', function() + check_parsing('var', { + ast = { + 'PlainIdentifier(scope=0,ident=var):0:0:var', + }, + }, { + hl('IdentifierName', 'var'), + }) + check_parsing('g:var', { + ast = { + 'PlainIdentifier(scope=g,ident=var):0:0:g:var', + }, + }, { + hl('IdentifierScope', 'g'), + hl('IdentifierScopeDelimiter', ':'), + hl('IdentifierName', 'var'), + }) + check_parsing('g:', { + ast = { + 'PlainIdentifier(scope=g,ident=):0:0:g:', + }, + }, { + hl('IdentifierScope', 'g'), + hl('IdentifierScopeDelimiter', ':'), + }) + check_parsing('{a}', { + -- 012 + ast = { + { + fmtn('CurlyBracesIdentifier', '-di', ':0:0:{'), + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + }, + }, + }, + }, { + hl('Curly', '{'), + hl('IdentifierName', 'a'), + hl('Curly', '}'), + }) + check_parsing('{a:b}', { + -- 012 + ast = { + { + fmtn('CurlyBracesIdentifier', '-di', ':0:0:{'), + children = { + 'PlainIdentifier(scope=a,ident=b):0:1:a:b', + }, + }, + }, + }, { + hl('Curly', '{'), + hl('IdentifierScope', 'a'), + hl('IdentifierScopeDelimiter', ':'), + hl('IdentifierName', 'b'), + hl('Curly', '}'), + }) + check_parsing('{a:@b}', { + -- 012345 + ast = { + { + fmtn('CurlyBracesIdentifier', '-di', ':0:0:{'), + children = { + { + 'OpMissing:0:3:', + children={ + 'PlainIdentifier(scope=a,ident=):0:1:a:', + 'Register(name=b):0:3:@b', + }, + }, + }, + }, + }, + err = { + arg = '@b}', + msg = 'E15: Missing operator: %.*s', + }, + }, { + hl('Curly', '{'), + hl('IdentifierScope', 'a'), + hl('IdentifierScopeDelimiter', ':'), + hl('InvalidRegister', '@b'), + hl('Curly', '}'), + }) + check_parsing('{@a}', { + ast = { + { + fmtn('CurlyBracesIdentifier', '-di', ':0:0:{'), + children = { + 'Register(name=a):0:1:@a', + }, + }, + }, + }, { + hl('Curly', '{'), + hl('Register', '@a'), + hl('Curly', '}'), + }) + check_parsing('{@a}{@b}', { + -- 01234567 + ast = { + { + 'ComplexIdentifier:0:4:', + children = { + { + fmtn('CurlyBracesIdentifier', '-di', ':0:0:{'), + children = { + 'Register(name=a):0:1:@a', + }, + }, + { + fmtn('CurlyBracesIdentifier', '--i', ':0:4:{'), + children = { + 'Register(name=b):0:5:@b', + }, + }, + }, + }, + }, + }, { + hl('Curly', '{'), + hl('Register', '@a'), + hl('Curly', '}'), + hl('Curly', '{'), + hl('Register', '@b'), + hl('Curly', '}'), + }) + check_parsing('g:{@a}', { + -- 01234567 + ast = { + { + 'ComplexIdentifier:0:2:', + children = { + 'PlainIdentifier(scope=g,ident=):0:0:g:', + { + fmtn('CurlyBracesIdentifier', '--i', ':0:2:{'), + children = { + 'Register(name=a):0:3:@a', + }, + }, + }, + }, + }, + }, { + hl('IdentifierScope', 'g'), + hl('IdentifierScopeDelimiter', ':'), + hl('Curly', '{'), + hl('Register', '@a'), + hl('Curly', '}'), + }) + check_parsing('{@a}_test', { + -- 012345678 + ast = { + { + 'ComplexIdentifier:0:4:', + children = { + { + fmtn('CurlyBracesIdentifier', '-di', ':0:0:{'), + children = { + 'Register(name=a):0:1:@a', + }, + }, + 'PlainIdentifier(scope=0,ident=_test):0:4:_test', + }, + }, + }, + }, { + hl('Curly', '{'), + hl('Register', '@a'), + hl('Curly', '}'), + hl('IdentifierName', '_test'), + }) + check_parsing('g:{@a}_test', { + -- 01234567890 + ast = { + { + 'ComplexIdentifier:0:2:', + children = { + 'PlainIdentifier(scope=g,ident=):0:0:g:', + { + 'ComplexIdentifier:0:6:', + children = { + { + fmtn('CurlyBracesIdentifier', '--i', ':0:2:{'), + children = { + 'Register(name=a):0:3:@a', + }, + }, + 'PlainIdentifier(scope=0,ident=_test):0:6:_test', + }, + }, + }, + }, + }, + }, { + hl('IdentifierScope', 'g'), + hl('IdentifierScopeDelimiter', ':'), + hl('Curly', '{'), + hl('Register', '@a'), + hl('Curly', '}'), + hl('IdentifierName', '_test'), + }) + check_parsing('g:{@a}_test()', { + -- 0123456789012 + ast = { + { + 'Call:0:11:(', + children = { + { + 'ComplexIdentifier:0:2:', + children = { + 'PlainIdentifier(scope=g,ident=):0:0:g:', + { + 'ComplexIdentifier:0:6:', + children = { + { + fmtn('CurlyBracesIdentifier', '--i', ':0:2:{'), + children = { + 'Register(name=a):0:3:@a', + }, + }, + 'PlainIdentifier(scope=0,ident=_test):0:6:_test', + }, + }, + }, + }, + }, + }, + }, + }, { + hl('IdentifierScope', 'g'), + hl('IdentifierScopeDelimiter', ':'), + hl('Curly', '{'), + hl('Register', '@a'), + hl('Curly', '}'), + hl('IdentifierName', '_test'), + hl('CallingParenthesis', '('), + hl('CallingParenthesis', ')'), + }) + check_parsing('{@a} ()', { + -- 0123456789012 + ast = { + { + 'Call:0:4: (', + children = { + { + fmtn('CurlyBracesIdentifier', '-di', ':0:0:{'), + children = { + 'Register(name=a):0:1:@a', + }, + }, + }, + }, + }, + }, { + hl('Curly', '{'), + hl('Register', '@a'), + hl('Curly', '}'), + hl('CallingParenthesis', '(', 1), + hl('CallingParenthesis', ')'), + }) + check_parsing('g:{@a} ()', { + -- 0123456789012 + ast = { + { + 'Call:0:6: (', + children = { + { + 'ComplexIdentifier:0:2:', + children = { + 'PlainIdentifier(scope=g,ident=):0:0:g:', + { + fmtn('CurlyBracesIdentifier', '--i', ':0:2:{'), + children = { + 'Register(name=a):0:3:@a', + }, + }, + }, + }, + }, + }, + }, + }, { + hl('IdentifierScope', 'g'), + hl('IdentifierScopeDelimiter', ':'), + hl('Curly', '{'), + hl('Register', '@a'), + hl('Curly', '}'), + hl('CallingParenthesis', '(', 1), + hl('CallingParenthesis', ')'), + }) + check_parsing('{@a', { + -- 012 + ast = { + { + fmtn('UnknownFigure', '-di', ':0:0:{'), + children = { + 'Register(name=a):0:1:@a', + }, + }, + }, + err = { + arg = '{@a', + msg = 'E15: Missing closing figure brace: %.*s', + }, + }, { + hl('FigureBrace', '{'), + hl('Register', '@a'), + }) + check_parsing('a ()', { + -- 0123 + ast = { + { + 'Call:0:1: (', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + }, + }, + }, + }, { + hl('IdentifierName', 'a'), + hl('CallingParenthesis', '(', 1), + hl('CallingParenthesis', ')'), + }) + end) + itp('works with lambdas and dictionaries', function() + check_parsing('{}', { + ast = { + fmtn('DictLiteral', '-di', ':0:0:{'), + }, + }, { + hl('Dict', '{'), + hl('Dict', '}'), + }) + check_parsing('{->@a}', { + ast = { + { + fmtn('Lambda', '\\di', ':0:0:{'), + children = { + { + 'Arrow:0:1:->', + children = { + 'Register(name=a):0:3:@a', + }, + }, + }, + }, + }, + }, { + hl('Lambda', '{'), + hl('Arrow', '->'), + hl('Register', '@a'), + hl('Lambda', '}'), + }) + check_parsing('{->@a+@b}', { + -- 012345678 + ast = { + { + fmtn('Lambda', '\\di', ':0:0:{'), + children = { + { + 'Arrow:0:1:->', + children = { + { + 'BinaryPlus:0:5:+', + children = { + 'Register(name=a):0:3:@a', + 'Register(name=b):0:6:@b', + }, + }, + }, + }, + }, + }, + }, + }, { + hl('Lambda', '{'), + hl('Arrow', '->'), + hl('Register', '@a'), + hl('BinaryPlus', '+'), + hl('Register', '@b'), + hl('Lambda', '}'), + }) + check_parsing('{a->@a}', { + -- 012345678 + ast = { + { + fmtn('Lambda', '\\di', ':0:0:{'), + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + { + 'Arrow:0:2:->', + children = { + 'Register(name=a):0:4:@a', + }, + }, + }, + }, + }, + }, { + hl('Lambda', '{'), + hl('IdentifierName', 'a'), + hl('Arrow', '->'), + hl('Register', '@a'), + hl('Lambda', '}'), + }) + check_parsing('{a,b->@a}', { + -- 012345678 + ast = { + { + fmtn('Lambda', '\\di', ':0:0:{'), + children = { + { + 'Comma:0:2:,', + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + 'PlainIdentifier(scope=0,ident=b):0:3:b', + }, + }, + { + 'Arrow:0:4:->', + children = { + 'Register(name=a):0:6:@a', + }, + }, + }, + }, + }, + }, { + hl('Lambda', '{'), + hl('IdentifierName', 'a'), + hl('Comma', ','), + hl('IdentifierName', 'b'), + hl('Arrow', '->'), + hl('Register', '@a'), + hl('Lambda', '}'), + }) + check_parsing('{a,b,c->@a}', { + -- 01234567890 + ast = { + { + fmtn('Lambda', '\\di', ':0:0:{'), + children = { + { + 'Comma:0:2:,', + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + { + 'Comma:0:4:,', + children = { + 'PlainIdentifier(scope=0,ident=b):0:3:b', + 'PlainIdentifier(scope=0,ident=c):0:5:c', + }, + }, + }, + }, + { + 'Arrow:0:6:->', + children = { + 'Register(name=a):0:8:@a', + }, + }, + }, + }, + }, + }, { + hl('Lambda', '{'), + hl('IdentifierName', 'a'), + hl('Comma', ','), + hl('IdentifierName', 'b'), + hl('Comma', ','), + hl('IdentifierName', 'c'), + hl('Arrow', '->'), + hl('Register', '@a'), + hl('Lambda', '}'), + }) + check_parsing('{a,b,c,d->@a}', { + -- 0123456789012 + ast = { + { + fmtn('Lambda', '\\di', ':0:0:{'), + children = { + { + 'Comma:0:2:,', + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + { + 'Comma:0:4:,', + children = { + 'PlainIdentifier(scope=0,ident=b):0:3:b', + { + 'Comma:0:6:,', + children = { + 'PlainIdentifier(scope=0,ident=c):0:5:c', + 'PlainIdentifier(scope=0,ident=d):0:7:d', + }, + }, + }, + }, + }, + }, + { + 'Arrow:0:8:->', + children = { + 'Register(name=a):0:10:@a', + }, + }, + }, + }, + }, + }, { + hl('Lambda', '{'), + hl('IdentifierName', 'a'), + hl('Comma', ','), + hl('IdentifierName', 'b'), + hl('Comma', ','), + hl('IdentifierName', 'c'), + hl('Comma', ','), + hl('IdentifierName', 'd'), + hl('Arrow', '->'), + hl('Register', '@a'), + hl('Lambda', '}'), + }) + check_parsing('{a,b,c,d,->@a}', { + -- 01234567890123 + ast = { + { + fmtn('Lambda', '\\di', ':0:0:{'), + children = { + { + 'Comma:0:2:,', + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + { + 'Comma:0:4:,', + children = { + 'PlainIdentifier(scope=0,ident=b):0:3:b', + { + 'Comma:0:6:,', + children = { + 'PlainIdentifier(scope=0,ident=c):0:5:c', + { + 'Comma:0:8:,', + children = { + 'PlainIdentifier(scope=0,ident=d):0:7:d', + }, + }, + }, + }, + }, + }, + }, + }, + { + 'Arrow:0:9:->', + children = { + 'Register(name=a):0:11:@a', + }, + }, + }, + }, + }, + }, { + hl('Lambda', '{'), + hl('IdentifierName', 'a'), + hl('Comma', ','), + hl('IdentifierName', 'b'), + hl('Comma', ','), + hl('IdentifierName', 'c'), + hl('Comma', ','), + hl('IdentifierName', 'd'), + hl('Comma', ','), + hl('Arrow', '->'), + hl('Register', '@a'), + hl('Lambda', '}'), + }) + check_parsing('{a,b->{c,d->{e,f->@a}}}', { + -- 01234567890123456789012 + -- 0 1 2 + ast = { + { + fmtn('Lambda', '\\di', ':0:0:{'), + children = { + { + 'Comma:0:2:,', + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + 'PlainIdentifier(scope=0,ident=b):0:3:b', + }, + }, + { + 'Arrow:0:4:->', + children = { + { + fmtn('Lambda', '\\di', ':0:6:{'), + children = { + { + 'Comma:0:8:,', + children = { + 'PlainIdentifier(scope=0,ident=c):0:7:c', + 'PlainIdentifier(scope=0,ident=d):0:9:d', + }, + }, + { + 'Arrow:0:10:->', + children = { + { + fmtn('Lambda', '\\di', ':0:12:{'), + children = { + { + 'Comma:0:14:,', + children = { + 'PlainIdentifier(scope=0,ident=e):0:13:e', + 'PlainIdentifier(scope=0,ident=f):0:15:f', + }, + }, + { + 'Arrow:0:16:->', + children = { + 'Register(name=a):0:18:@a', + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, { + hl('Lambda', '{'), + hl('IdentifierName', 'a'), + hl('Comma', ','), + hl('IdentifierName', 'b'), + hl('Arrow', '->'), + hl('Lambda', '{'), + hl('IdentifierName', 'c'), + hl('Comma', ','), + hl('IdentifierName', 'd'), + hl('Arrow', '->'), + hl('Lambda', '{'), + hl('IdentifierName', 'e'), + hl('Comma', ','), + hl('IdentifierName', 'f'), + hl('Arrow', '->'), + hl('Register', '@a'), + hl('Lambda', '}'), + hl('Lambda', '}'), + hl('Lambda', '}'), + }) + check_parsing('{a,b->c,d}', { + -- 0123456789 + ast = { + { + fmtn('Lambda', '\\di', ':0:0:{'), + children = { + { + 'Comma:0:2:,', + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + 'PlainIdentifier(scope=0,ident=b):0:3:b', + }, + }, + { + 'Arrow:0:4:->', + children = { + { + 'Comma:0:7:,', + children = { + 'PlainIdentifier(scope=0,ident=c):0:6:c', + 'PlainIdentifier(scope=0,ident=d):0:8:d', + }, + }, + }, + }, + }, + }, + }, + err = { + arg = ',d}', + msg = 'E15: Comma outside of call, lambda or literal: %.*s', + }, + }, { + hl('Lambda', '{'), + hl('IdentifierName', 'a'), + hl('Comma', ','), + hl('IdentifierName', 'b'), + hl('Arrow', '->'), + hl('IdentifierName', 'c'), + hl('InvalidComma', ','), + hl('IdentifierName', 'd'), + hl('Lambda', '}'), + }) + check_parsing('a,b,c,d', { + -- 0123456789 + ast = { + { + 'Comma:0:1:,', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + { + 'Comma:0:3:,', + children = { + 'PlainIdentifier(scope=0,ident=b):0:2:b', + { + 'Comma:0:5:,', + children = { + 'PlainIdentifier(scope=0,ident=c):0:4:c', + 'PlainIdentifier(scope=0,ident=d):0:6:d', + }, + }, + }, + }, + }, + }, + }, + err = { + arg = ',b,c,d', + msg = 'E15: Comma outside of call, lambda or literal: %.*s', + }, + }, { + hl('IdentifierName', 'a'), + hl('InvalidComma', ','), + hl('IdentifierName', 'b'), + hl('InvalidComma', ','), + hl('IdentifierName', 'c'), + hl('InvalidComma', ','), + hl('IdentifierName', 'd'), + }) + check_parsing('a,b,c,d,', { + -- 0123456789 + ast = { + { + 'Comma:0:1:,', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + { + 'Comma:0:3:,', + children = { + 'PlainIdentifier(scope=0,ident=b):0:2:b', + { + 'Comma:0:5:,', + children = { + 'PlainIdentifier(scope=0,ident=c):0:4:c', + { + 'Comma:0:7:,', + children = { + 'PlainIdentifier(scope=0,ident=d):0:6:d', + }, + }, + }, + }, + }, + }, + }, + }, + }, + err = { + arg = ',b,c,d,', + msg = 'E15: Comma outside of call, lambda or literal: %.*s', + }, + }, { + hl('IdentifierName', 'a'), + hl('InvalidComma', ','), + hl('IdentifierName', 'b'), + hl('InvalidComma', ','), + hl('IdentifierName', 'c'), + hl('InvalidComma', ','), + hl('IdentifierName', 'd'), + hl('InvalidComma', ','), + }) + check_parsing(',', { + -- 0123456789 + ast = { + { + 'Comma:0:0:,', + children = { + 'Missing:0:0:', + }, + }, + }, + err = { + arg = ',', + msg = 'E15: Expected value, got comma: %.*s', + }, + }, { + hl('InvalidComma', ','), + }) + check_parsing('{,a->@a}', { + -- 0123456789 + ast = { + { + fmtn('CurlyBracesIdentifier', '-di', ':0:0:{'), + children = { + { + 'Arrow:0:3:->', + children = { + { + 'Comma:0:1:,', + children = { + 'Missing:0:1:', + 'PlainIdentifier(scope=0,ident=a):0:2:a', + }, + }, + 'Register(name=a):0:5:@a', + }, + }, + }, + }, + }, + err = { + arg = ',a->@a}', + msg = 'E15: Expected value, got comma: %.*s', + }, + }, { + hl('Curly', '{'), + hl('InvalidComma', ','), + hl('IdentifierName', 'a'), + hl('InvalidArrow', '->'), + hl('Register', '@a'), + hl('Curly', '}'), + }) + check_parsing('}', { + -- 0123456789 + ast = { + fmtn('UnknownFigure', '---', ':0:0:'), + }, + err = { + arg = '}', + msg = 'E15: Unexpected closing figure brace: %.*s', + }, + }, { + hl('InvalidFigureBrace', '}'), + }) + check_parsing('{->}', { + -- 0123456789 + ast = { + { + fmtn('Lambda', '\\di', ':0:0:{'), + children = { + 'Arrow:0:1:->', + }, + }, + }, + err = { + arg = '}', + msg = 'E15: Expected value, got closing figure brace: %.*s', + }, + }, { + hl('Lambda', '{'), + hl('Arrow', '->'), + hl('InvalidLambda', '}'), + }) + check_parsing('{a,b}', { + -- 0123456789 + ast = { + { + fmtn('Lambda', '-di', ':0:0:{'), + children = { + { + 'Comma:0:2:,', + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + 'PlainIdentifier(scope=0,ident=b):0:3:b', + }, + }, + }, + }, + }, + err = { + arg = '}', + msg = 'E15: Expected lambda arguments list or arrow: %.*s', + }, + }, { + hl('Lambda', '{'), + hl('IdentifierName', 'a'), + hl('Comma', ','), + hl('IdentifierName', 'b'), + hl('InvalidLambda', '}'), + }) + check_parsing('{a,}', { + -- 0123456789 + ast = { + { + fmtn('Lambda', '-di', ':0:0:{'), + children = { + { + 'Comma:0:2:,', + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + }, + }, + }, + }, + }, + err = { + arg = '}', + msg = 'E15: Expected lambda arguments list or arrow: %.*s', + }, + }, { + hl('Lambda', '{'), + hl('IdentifierName', 'a'), + hl('Comma', ','), + hl('InvalidLambda', '}'), + }) + check_parsing('{@a:@b}', { + -- 0123456789 + ast = { + { + fmtn('DictLiteral', '-di', ':0:0:{'), + children = { + { + 'Colon:0:3::', + children = { + 'Register(name=a):0:1:@a', + 'Register(name=b):0:4:@b', + }, + }, + }, + }, + }, + }, { + hl('Dict', '{'), + hl('Register', '@a'), + hl('Colon', ':'), + hl('Register', '@b'), + hl('Dict', '}'), + }) + check_parsing('{@a:@b,@c:@d}', { + -- 0123456789012 + -- 0 1 + ast = { + { + fmtn('DictLiteral', '-di', ':0:0:{'), + children = { + { + 'Comma:0:6:,', + children = { + { + 'Colon:0:3::', + children = { + 'Register(name=a):0:1:@a', + 'Register(name=b):0:4:@b', + }, + }, + { + 'Colon:0:9::', + children = { + 'Register(name=c):0:7:@c', + 'Register(name=d):0:10:@d', + }, + }, + }, + }, + }, + }, + }, + }, { + hl('Dict', '{'), + hl('Register', '@a'), + hl('Colon', ':'), + hl('Register', '@b'), + hl('Comma', ','), + hl('Register', '@c'), + hl('Colon', ':'), + hl('Register', '@d'), + hl('Dict', '}'), + }) + check_parsing('{@a:@b,@c:@d,@e:@f,}', { + -- 01234567890123456789 + -- 0 1 + ast = { + { + fmtn('DictLiteral', '-di', ':0:0:{'), + children = { + { + 'Comma:0:6:,', + children = { + { + 'Colon:0:3::', + children = { + 'Register(name=a):0:1:@a', + 'Register(name=b):0:4:@b', + }, + }, + { + 'Comma:0:12:,', + children = { + { + 'Colon:0:9::', + children = { + 'Register(name=c):0:7:@c', + 'Register(name=d):0:10:@d', + }, + }, + { + 'Comma:0:18:,', + children = { + { + 'Colon:0:15::', + children = { + 'Register(name=e):0:13:@e', + 'Register(name=f):0:16:@f', + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, { + hl('Dict', '{'), + hl('Register', '@a'), + hl('Colon', ':'), + hl('Register', '@b'), + hl('Comma', ','), + hl('Register', '@c'), + hl('Colon', ':'), + hl('Register', '@d'), + hl('Comma', ','), + hl('Register', '@e'), + hl('Colon', ':'), + hl('Register', '@f'), + hl('Comma', ','), + hl('Dict', '}'), + }) + check_parsing('{@a:@b,@c:@d,@e:@f,@g:}', { + -- 01234567890123456789012 + -- 0 1 2 + ast = { + { + fmtn('DictLiteral', '-di', ':0:0:{'), + children = { + { + 'Comma:0:6:,', + children = { + { + 'Colon:0:3::', + children = { + 'Register(name=a):0:1:@a', + 'Register(name=b):0:4:@b', + }, + }, + { + 'Comma:0:12:,', + children = { + { + 'Colon:0:9::', + children = { + 'Register(name=c):0:7:@c', + 'Register(name=d):0:10:@d', + }, + }, + { + 'Comma:0:18:,', + children = { + { + 'Colon:0:15::', + children = { + 'Register(name=e):0:13:@e', + 'Register(name=f):0:16:@f', + }, + }, + { + 'Colon:0:21::', + children = { + 'Register(name=g):0:19:@g', + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + err = { + arg = '}', + msg = 'E15: Expected value, got closing figure brace: %.*s', + }, + }, { + hl('Dict', '{'), + hl('Register', '@a'), + hl('Colon', ':'), + hl('Register', '@b'), + hl('Comma', ','), + hl('Register', '@c'), + hl('Colon', ':'), + hl('Register', '@d'), + hl('Comma', ','), + hl('Register', '@e'), + hl('Colon', ':'), + hl('Register', '@f'), + hl('Comma', ','), + hl('Register', '@g'), + hl('Colon', ':'), + hl('InvalidDict', '}'), + }) + check_parsing('{@a:@b,}', { + -- 01234567890123 + -- 0 1 + ast = { + { + fmtn('DictLiteral', '-di', ':0:0:{'), + children = { + { + 'Comma:0:6:,', + children = { + { + 'Colon:0:3::', + children = { + 'Register(name=a):0:1:@a', + 'Register(name=b):0:4:@b', + }, + }, + }, + }, + }, + }, + }, + }, { + hl('Dict', '{'), + hl('Register', '@a'), + hl('Colon', ':'), + hl('Register', '@b'), + hl('Comma', ','), + hl('Dict', '}'), + }) + check_parsing('{({f -> g})(@h)(@i)}', { + -- 01234567890123456789 + -- 0 1 + ast = { + { + fmtn('CurlyBracesIdentifier', '-di', ':0:0:{'), + children = { + { + 'Call:0:15:(', + children = { + { + 'Call:0:11:(', + children = { + { + 'Nested:0:1:(', + children = { + { + fmtn('Lambda', '\\di', ':0:2:{'), + children = { + 'PlainIdentifier(scope=0,ident=f):0:3:f', + { + 'Arrow:0:4: ->', + children = { + 'PlainIdentifier(scope=0,ident=g):0:7: g', + }, + }, + }, + }, + }, + }, + 'Register(name=h):0:12:@h', + }, + }, + 'Register(name=i):0:16:@i', + }, + }, + }, + }, + }, + }, { + hl('Curly', '{'), + hl('NestingParenthesis', '('), + hl('Lambda', '{'), + hl('IdentifierName', 'f'), + hl('Arrow', '->', 1), + hl('IdentifierName', 'g', 1), + hl('Lambda', '}'), + hl('NestingParenthesis', ')'), + hl('CallingParenthesis', '('), + hl('Register', '@h'), + hl('CallingParenthesis', ')'), + hl('CallingParenthesis', '('), + hl('Register', '@i'), + hl('CallingParenthesis', ')'), + hl('Curly', '}'), + }) + check_parsing('a:{b()}c', { + -- 01234567 + ast = { + { + 'ComplexIdentifier:0:2:', + children = { + 'PlainIdentifier(scope=a,ident=):0:0:a:', + { + 'ComplexIdentifier:0:7:', + children = { + { + fmtn('CurlyBracesIdentifier', '--i', ':0:2:{'), + children = { + { + 'Call:0:4:(', + children = { + 'PlainIdentifier(scope=0,ident=b):0:3:b', + }, + }, + }, + }, + 'PlainIdentifier(scope=0,ident=c):0:7:c', + }, + }, + }, + }, + }, + }, { + hl('IdentifierScope', 'a'), + hl('IdentifierScopeDelimiter', ':'), + hl('Curly', '{'), + hl('IdentifierName', 'b'), + hl('CallingParenthesis', '('), + hl('CallingParenthesis', ')'), + hl('Curly', '}'), + hl('IdentifierName', 'c'), + }) + check_parsing('a:{{b, c -> @d + @e + ({f -> g})(@h)}(@i)}j', { + -- 01234567890123456789012345678901234567890123456 + -- 0 1 2 3 4 + ast = { + { + 'ComplexIdentifier:0:2:', + children = { + 'PlainIdentifier(scope=a,ident=):0:0:a:', + { + 'ComplexIdentifier:0:42:', + children = { + { + fmtn('CurlyBracesIdentifier', '--i', ':0:2:{'), + children = { + { + 'Call:0:37:(', + children = { + { + fmtn('Lambda', '\\di', ':0:3:{'), + children = { + { + 'Comma:0:5:,', + children = { + 'PlainIdentifier(scope=0,ident=b):0:4:b', + 'PlainIdentifier(scope=0,ident=c):0:6: c', + }, + }, + { + 'Arrow:0:8: ->', + children = { + { + 'BinaryPlus:0:19: +', + children = { + { + 'BinaryPlus:0:14: +', + children = { + 'Register(name=d):0:11: @d', + 'Register(name=e):0:16: @e', + }, + }, + { + 'Call:0:32:(', + children = { + { + 'Nested:0:21: (', + children = { + { + fmtn('Lambda', '\\di', ':0:23:{'), + children = { + 'PlainIdentifier(scope=0,ident=f):0:24:f', + { + 'Arrow:0:25: ->', + children = { + 'PlainIdentifier(scope=0,ident=g):0:28: g', + }, + }, + }, + }, + }, + }, + 'Register(name=h):0:33:@h', + }, + }, + }, + }, + }, + }, + }, + }, + 'Register(name=i):0:38:@i', + }, + }, + }, + }, + 'PlainIdentifier(scope=0,ident=j):0:42:j', + }, + }, + }, + }, + }, + }, { + hl('IdentifierScope', 'a'), + hl('IdentifierScopeDelimiter', ':'), + hl('Curly', '{'), + hl('Lambda', '{'), + hl('IdentifierName', 'b'), + hl('Comma', ','), + hl('IdentifierName', 'c', 1), + hl('Arrow', '->', 1), + hl('Register', '@d', 1), + hl('BinaryPlus', '+', 1), + hl('Register', '@e', 1), + hl('BinaryPlus', '+', 1), + hl('NestingParenthesis', '(', 1), + hl('Lambda', '{'), + hl('IdentifierName', 'f'), + hl('Arrow', '->', 1), + hl('IdentifierName', 'g', 1), + hl('Lambda', '}'), + hl('NestingParenthesis', ')'), + hl('CallingParenthesis', '('), + hl('Register', '@h'), + hl('CallingParenthesis', ')'), + hl('Lambda', '}'), + hl('CallingParenthesis', '('), + hl('Register', '@i'), + hl('CallingParenthesis', ')'), + hl('Curly', '}'), + hl('IdentifierName', 'j'), + }) + check_parsing('{@a + @b : @c + @d, @e + @f : @g + @i}', { + -- 01234567890123456789012345678901234567 + -- 0 1 2 3 + ast = { + { + fmtn('DictLiteral', '-di', ':0:0:{'), + children = { + { + 'Comma:0:18:,', + children = { + { + 'Colon:0:8: :', + children = { + { + 'BinaryPlus:0:3: +', + children = { + 'Register(name=a):0:1:@a', + 'Register(name=b):0:5: @b', + }, + }, + { + 'BinaryPlus:0:13: +', + children = { + 'Register(name=c):0:10: @c', + 'Register(name=d):0:15: @d', + }, + }, + }, + }, + { + 'Colon:0:27: :', + children = { + { + 'BinaryPlus:0:22: +', + children = { + 'Register(name=e):0:19: @e', + 'Register(name=f):0:24: @f', + }, + }, + { + 'BinaryPlus:0:32: +', + children = { + 'Register(name=g):0:29: @g', + 'Register(name=i):0:34: @i', + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, { + hl('Dict', '{'), + hl('Register', '@a'), + hl('BinaryPlus', '+', 1), + hl('Register', '@b', 1), + hl('Colon', ':', 1), + hl('Register', '@c', 1), + hl('BinaryPlus', '+', 1), + hl('Register', '@d', 1), + hl('Comma', ','), + hl('Register', '@e', 1), + hl('BinaryPlus', '+', 1), + hl('Register', '@f', 1), + hl('Colon', ':', 1), + hl('Register', '@g', 1), + hl('BinaryPlus', '+', 1), + hl('Register', '@i', 1), + hl('Dict', '}'), + }) + check_parsing('-> -> ->', { + -- 01234567 + ast = { + { + 'Arrow:0:0:->', + children = { + 'Missing:0:0:', + { + 'Arrow:0:2: ->', + children = { + 'Missing:0:2:', + { + 'Arrow:0:5: ->', + children = { + 'Missing:0:5:', + }, + }, + }, + }, + }, + }, + }, + err = { + arg = '-> -> ->', + msg = 'E15: Unexpected arrow: %.*s', + }, + }, { + hl('InvalidArrow', '->'), + hl('InvalidArrow', '->', 1), + hl('InvalidArrow', '->', 1), + }) + check_parsing('a -> b -> c -> d', { + -- 0123456789012345 + -- 0 1 + ast = { + { + 'Arrow:0:1: ->', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + { + 'Arrow:0:6: ->', + children = { + 'PlainIdentifier(scope=0,ident=b):0:4: b', + { + 'Arrow:0:11: ->', + children = { + 'PlainIdentifier(scope=0,ident=c):0:9: c', + 'PlainIdentifier(scope=0,ident=d):0:14: d', + }, + }, + }, + }, + }, + }, + }, + err = { + arg = '-> b -> c -> d', + msg = 'E15: Arrow outside of lambda: %.*s', + }, + }, { + hl('IdentifierName', 'a'), + hl('InvalidArrow', '->', 1), + hl('IdentifierName', 'b', 1), + hl('InvalidArrow', '->', 1), + hl('IdentifierName', 'c', 1), + hl('InvalidArrow', '->', 1), + hl('IdentifierName', 'd', 1), + }) + check_parsing('{a -> b -> c}', { + -- 0123456789012 + -- 0 1 + ast = { + { + fmtn('Lambda', '\\di', ':0:0:{'), + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + { + 'Arrow:0:2: ->', + children = { + { + 'Arrow:0:7: ->', + children = { + 'PlainIdentifier(scope=0,ident=b):0:5: b', + 'PlainIdentifier(scope=0,ident=c):0:10: c', + }, + }, + }, + }, + }, + }, + }, + err = { + arg = '-> c}', + msg = 'E15: Arrow outside of lambda: %.*s', + }, + }, { + hl('Lambda', '{'), + hl('IdentifierName', 'a'), + hl('Arrow', '->', 1), + hl('IdentifierName', 'b', 1), + hl('InvalidArrow', '->', 1), + hl('IdentifierName', 'c', 1), + hl('Lambda', '}'), + }) + check_parsing('{a: -> b}', { + -- 012345678 + ast = { + { + fmtn('CurlyBracesIdentifier', '-di', ':0:0:{'), + children = { + { + 'Arrow:0:3: ->', + children = { + 'PlainIdentifier(scope=a,ident=):0:1:a:', + 'PlainIdentifier(scope=0,ident=b):0:6: b', + }, + }, + }, + }, + }, + err = { + arg = '-> b}', + msg = 'E15: Arrow outside of lambda: %.*s', + }, + }, { + hl('Curly', '{'), + hl('IdentifierScope', 'a'), + hl('IdentifierScopeDelimiter', ':'), + hl('InvalidArrow', '->', 1), + hl('IdentifierName', 'b', 1), + hl('Curly', '}'), + }) + + check_parsing('{a:b -> b}', { + -- 0123456789 + ast = { + { + fmtn('CurlyBracesIdentifier', '-di', ':0:0:{'), + children = { + { + 'Arrow:0:4: ->', + children = { + 'PlainIdentifier(scope=a,ident=b):0:1:a:b', + 'PlainIdentifier(scope=0,ident=b):0:7: b', + }, + }, + }, + }, + }, + err = { + arg = '-> b}', + msg = 'E15: Arrow outside of lambda: %.*s', + }, + }, { + hl('Curly', '{'), + hl('IdentifierScope', 'a'), + hl('IdentifierScopeDelimiter', ':'), + hl('IdentifierName', 'b'), + hl('InvalidArrow', '->', 1), + hl('IdentifierName', 'b', 1), + hl('Curly', '}'), + }) + + check_parsing('{a#b -> b}', { + -- 0123456789 + ast = { + { + fmtn('CurlyBracesIdentifier', '-di', ':0:0:{'), + children = { + { + 'Arrow:0:4: ->', + children = { + 'PlainIdentifier(scope=0,ident=a#b):0:1:a#b', + 'PlainIdentifier(scope=0,ident=b):0:7: b', + }, + }, + }, + }, + }, + err = { + arg = '-> b}', + msg = 'E15: Arrow outside of lambda: %.*s', + }, + }, { + hl('Curly', '{'), + hl('IdentifierName', 'a#b'), + hl('InvalidArrow', '->', 1), + hl('IdentifierName', 'b', 1), + hl('Curly', '}'), + }) + check_parsing('{a : b : c}', { + -- 01234567890 + -- 0 1 + ast = { + { + fmtn('DictLiteral', '-di', ':0:0:{'), + children = { + { + 'Colon:0:2: :', + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + { + 'Colon:0:6: :', + children = { + 'PlainIdentifier(scope=0,ident=b):0:4: b', + 'PlainIdentifier(scope=0,ident=c):0:8: c', + }, + }, + }, + }, + }, + }, + }, + err = { + arg = ': c}', + msg = 'E15: Colon outside of dictionary or ternary operator: %.*s', + }, + }, { + hl('Dict', '{'), + hl('IdentifierName', 'a'), + hl('Colon', ':', 1), + hl('IdentifierName', 'b', 1), + hl('InvalidColon', ':', 1), + hl('IdentifierName', 'c', 1), + hl('Dict', '}'), + }) + check_parsing('{', { + -- 0 + ast = { + fmtn('UnknownFigure', '\\di', ':0:0:{'), + }, + err = { + arg = '{', + msg = 'E15: Missing closing figure brace: %.*s', + }, + }, { + hl('FigureBrace', '{'), + }) + check_parsing('{a', { + -- 01 + ast = { + { + fmtn('UnknownFigure', '\\di', ':0:0:{'), + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + }, + }, + }, + err = { + arg = '{a', + msg = 'E15: Missing closing figure brace: %.*s', + }, + }, { + hl('FigureBrace', '{'), + hl('IdentifierName', 'a'), + }) + check_parsing('{a,b', { + -- 0123 + ast = { + { + fmtn('Lambda', '\\di', ':0:0:{'), + children = { + { + 'Comma:0:2:,', + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + 'PlainIdentifier(scope=0,ident=b):0:3:b', + }, + }, + }, + }, + }, + err = { + arg = '{a,b', + msg = 'E15: Missing closing figure brace for lambda: %.*s', + }, + }, { + hl('Lambda', '{'), + hl('IdentifierName', 'a'), + hl('Comma', ','), + hl('IdentifierName', 'b'), + }) + check_parsing('{a,b->', { + -- 012345 + ast = { + { + fmtn('Lambda', '\\di', ':0:0:{'), + children = { + { + 'Comma:0:2:,', + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + 'PlainIdentifier(scope=0,ident=b):0:3:b', + }, + }, + 'Arrow:0:4:->', + }, + }, + }, + err = { + arg = '', + msg = 'E15: Expected value, got EOC: %.*s', + }, + }, { + hl('Lambda', '{'), + hl('IdentifierName', 'a'), + hl('Comma', ','), + hl('IdentifierName', 'b'), + hl('Arrow', '->'), + }) + check_parsing('{a,b->c', { + -- 0123456 + ast = { + { + fmtn('Lambda', '\\di', ':0:0:{'), + children = { + { + 'Comma:0:2:,', + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + 'PlainIdentifier(scope=0,ident=b):0:3:b', + }, + }, + { + 'Arrow:0:4:->', + children = { + 'PlainIdentifier(scope=0,ident=c):0:6:c', + }, + }, + }, + }, + }, + err = { + arg = '{a,b->c', + msg = 'E15: Missing closing figure brace for lambda: %.*s', + }, + }, { + hl('Lambda', '{'), + hl('IdentifierName', 'a'), + hl('Comma', ','), + hl('IdentifierName', 'b'), + hl('Arrow', '->'), + hl('IdentifierName', 'c'), + }) + check_parsing('{a : b', { + -- 012345 + ast = { + { + fmtn('DictLiteral', '-di', ':0:0:{'), + children = { + { + 'Colon:0:2: :', + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + 'PlainIdentifier(scope=0,ident=b):0:4: b', + }, + }, + }, + }, + }, + err = { + arg = '{a : b', + msg = 'E723: Missing end of Dictionary \'}\': %.*s', + }, + }, { + hl('Dict', '{'), + hl('IdentifierName', 'a'), + hl('Colon', ':', 1), + hl('IdentifierName', 'b', 1), + }) + check_parsing('{a : b,', { + -- 0123456 + ast = { + { + fmtn('DictLiteral', '-di', ':0:0:{'), + children = { + { + 'Comma:0:6:,', + children = { + { + 'Colon:0:2: :', + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + 'PlainIdentifier(scope=0,ident=b):0:4: b', + }, + }, + }, + }, + }, + }, + }, + err = { + arg = '', + msg = 'E15: Expected value, got EOC: %.*s', + }, + }, { + hl('Dict', '{'), + hl('IdentifierName', 'a'), + hl('Colon', ':', 1), + hl('IdentifierName', 'b', 1), + hl('Comma', ','), + }) + end) + itp('works with ternary operator', function() + check_parsing('a ? b : c', { + -- 012345678 + ast = { + { + 'Ternary:0:1: ?', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + { + 'TernaryValue:0:5: :', + children = { + 'PlainIdentifier(scope=0,ident=b):0:3: b', + 'PlainIdentifier(scope=0,ident=c):0:7: c', + }, + }, + }, + }, + }, + }, { + hl('IdentifierName', 'a'), + hl('Ternary', '?', 1), + hl('IdentifierName', 'b', 1), + hl('TernaryColon', ':', 1), + hl('IdentifierName', 'c', 1), + }) + check_parsing('@a?@b?@c:@d:@e', { + -- 01234567890123 + -- 0 1 + ast = { + { + 'Ternary:0:2:?', + children = { + 'Register(name=a):0:0:@a', + { + 'TernaryValue:0:11::', + children = { + { + 'Ternary:0:5:?', + children = { + 'Register(name=b):0:3:@b', + { + 'TernaryValue:0:8::', + children = { + 'Register(name=c):0:6:@c', + 'Register(name=d):0:9:@d', + }, + }, + }, + }, + 'Register(name=e):0:12:@e', + }, + }, + }, + }, + }, + }, { + hl('Register', '@a'), + hl('Ternary', '?'), + hl('Register', '@b'), + hl('Ternary', '?'), + hl('Register', '@c'), + hl('TernaryColon', ':'), + hl('Register', '@d'), + hl('TernaryColon', ':'), + hl('Register', '@e'), + }) + check_parsing('@a?@b:@c?@d:@e', { + -- 01234567890123 + -- 0 1 + ast = { + { + 'Ternary:0:2:?', + children = { + 'Register(name=a):0:0:@a', + { + 'TernaryValue:0:5::', + children = { + 'Register(name=b):0:3:@b', + { + 'Ternary:0:8:?', + children = { + 'Register(name=c):0:6:@c', + { + 'TernaryValue:0:11::', + children = { + 'Register(name=d):0:9:@d', + 'Register(name=e):0:12:@e', + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, { + hl('Register', '@a'), + hl('Ternary', '?'), + hl('Register', '@b'), + hl('TernaryColon', ':'), + hl('Register', '@c'), + hl('Ternary', '?'), + hl('Register', '@d'), + hl('TernaryColon', ':'), + hl('Register', '@e'), + }) + check_parsing('@a?@b?@c?@d:@e?@f:@g:@h?@i:@j:@k', { + -- 01234567890123456789012345678901 + -- 0 1 2 3 + ast = { + { + 'Ternary:0:2:?', + children = { + 'Register(name=a):0:0:@a', + { + 'TernaryValue:0:29::', + children = { + { + 'Ternary:0:5:?', + children = { + 'Register(name=b):0:3:@b', + { + 'TernaryValue:0:20::', + children = { + { + 'Ternary:0:8:?', + children = { + 'Register(name=c):0:6:@c', + { + 'TernaryValue:0:11::', + children = { + 'Register(name=d):0:9:@d', + { + 'Ternary:0:14:?', + children = { + 'Register(name=e):0:12:@e', + { + 'TernaryValue:0:17::', + children = { + 'Register(name=f):0:15:@f', + 'Register(name=g):0:18:@g', + }, + }, + }, + }, + }, + }, + }, + }, + { + 'Ternary:0:23:?', + children = { + 'Register(name=h):0:21:@h', + { + 'TernaryValue:0:26::', + children = { + 'Register(name=i):0:24:@i', + 'Register(name=j):0:27:@j', + }, + }, + }, + }, + }, + }, + }, + }, + 'Register(name=k):0:30:@k', + }, + }, + }, + }, + }, + }, { + hl('Register', '@a'), + hl('Ternary', '?'), + hl('Register', '@b'), + hl('Ternary', '?'), + hl('Register', '@c'), + hl('Ternary', '?'), + hl('Register', '@d'), + hl('TernaryColon', ':'), + hl('Register', '@e'), + hl('Ternary', '?'), + hl('Register', '@f'), + hl('TernaryColon', ':'), + hl('Register', '@g'), + hl('TernaryColon', ':'), + hl('Register', '@h'), + hl('Ternary', '?'), + hl('Register', '@i'), + hl('TernaryColon', ':'), + hl('Register', '@j'), + hl('TernaryColon', ':'), + hl('Register', '@k'), + }) + check_parsing('?', { + -- 0 + ast = { + { + 'Ternary:0:0:?', + children = { + 'Missing:0:0:', + 'TernaryValue:0:0:?', + }, + }, + }, + err = { + arg = '?', + msg = 'E15: Expected value, got question mark: %.*s', + }, + }, { + hl('InvalidTernary', '?'), + }) + + check_parsing('?:', { + -- 01 + ast = { + { + 'Ternary:0:0:?', + children = { + 'Missing:0:0:', + { + 'TernaryValue:0:1::', + children = { + 'Missing:0:1:', + }, + }, + }, + }, + }, + err = { + arg = '?:', + msg = 'E15: Expected value, got question mark: %.*s', + }, + }, { + hl('InvalidTernary', '?'), + hl('InvalidTernaryColon', ':'), + }) + + check_parsing('?::', { + -- 012 + ast = { + { + 'Colon:0:2::', + children = { + { + 'Ternary:0:0:?', + children = { + 'Missing:0:0:', + { + 'TernaryValue:0:1::', + children = { + 'Missing:0:1:', + 'Missing:0:2:', + }, + }, + }, + }, + }, + }, + }, + err = { + arg = '?::', + msg = 'E15: Expected value, got question mark: %.*s', + }, + }, { + hl('InvalidTernary', '?'), + hl('InvalidTernaryColon', ':'), + hl('InvalidColon', ':'), + }) + + check_parsing('a?b', { + -- 012 + ast = { + { + 'Ternary:0:1:?', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + { + 'TernaryValue:0:1:?', + children = { + 'PlainIdentifier(scope=0,ident=b):0:2:b', + }, + }, + }, + }, + }, + err = { + arg = '?b', + msg = 'E109: Missing \':\' after \'?\': %.*s', + }, + }, { + hl('IdentifierName', 'a'), + hl('Ternary', '?'), + hl('IdentifierName', 'b'), + }) + check_parsing('a?b:', { + -- 0123 + ast = { + { + 'Ternary:0:1:?', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + { + 'TernaryValue:0:1:?', + children = { + 'PlainIdentifier(scope=b,ident=):0:2:b:', + }, + }, + }, + }, + }, + err = { + arg = '?b:', + msg = 'E109: Missing \':\' after \'?\': %.*s', + }, + }, { + hl('IdentifierName', 'a'), + hl('Ternary', '?'), + hl('IdentifierScope', 'b'), + hl('IdentifierScopeDelimiter', ':'), + }) + + check_parsing('a?b::c', { + -- 012345 + ast = { + { + 'Ternary:0:1:?', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + { + 'TernaryValue:0:4::', + children = { + 'PlainIdentifier(scope=b,ident=):0:2:b:', + 'PlainIdentifier(scope=0,ident=c):0:5:c', + }, + }, + }, + }, + }, + }, { + hl('IdentifierName', 'a'), + hl('Ternary', '?'), + hl('IdentifierScope', 'b'), + hl('IdentifierScopeDelimiter', ':'), + hl('TernaryColon', ':'), + hl('IdentifierName', 'c'), + }) + + check_parsing('a?b :', { + -- 01234 + ast = { + { + 'Ternary:0:1:?', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + { + 'TernaryValue:0:3: :', + children = { + 'PlainIdentifier(scope=0,ident=b):0:2:b', + }, + }, + }, + }, + }, + err = { + arg = '', + msg = 'E15: Expected value, got EOC: %.*s', + }, + }, { + hl('IdentifierName', 'a'), + hl('Ternary', '?'), + hl('IdentifierName', 'b'), + hl('TernaryColon', ':', 1), + }) + + check_parsing('(@a?@b:@c)?@d:@e', { + -- 0123456789012345 + -- 0 1 + ast = { + { + 'Ternary:0:10:?', + children = { + { + 'Nested:0:0:(', + children = { + { + 'Ternary:0:3:?', + children = { + 'Register(name=a):0:1:@a', + { + 'TernaryValue:0:6::', + children = { + 'Register(name=b):0:4:@b', + 'Register(name=c):0:7:@c', + }, + }, + }, + }, + }, + }, + { + 'TernaryValue:0:13::', + children = { + 'Register(name=d):0:11:@d', + 'Register(name=e):0:14:@e', + }, + }, + }, + }, + }, + }, { + hl('NestingParenthesis', '('), + hl('Register', '@a'), + hl('Ternary', '?'), + hl('Register', '@b'), + hl('TernaryColon', ':'), + hl('Register', '@c'), + hl('NestingParenthesis', ')'), + hl('Ternary', '?'), + hl('Register', '@d'), + hl('TernaryColon', ':'), + hl('Register', '@e'), + }) + + check_parsing('(@a?@b:@c)?(@d?@e:@f):(@g?@h:@i)', { + -- 01234567890123456789012345678901 + -- 0 1 2 3 + ast = { + { + 'Ternary:0:10:?', + children = { + { + 'Nested:0:0:(', + children = { + { + 'Ternary:0:3:?', + children = { + 'Register(name=a):0:1:@a', + { + 'TernaryValue:0:6::', + children = { + 'Register(name=b):0:4:@b', + 'Register(name=c):0:7:@c', + }, + }, + }, + }, + }, + }, + { + 'TernaryValue:0:21::', + children = { + { + 'Nested:0:11:(', + children = { + { + 'Ternary:0:14:?', + children = { + 'Register(name=d):0:12:@d', + { + 'TernaryValue:0:17::', + children = { + 'Register(name=e):0:15:@e', + 'Register(name=f):0:18:@f', + }, + }, + }, + }, + }, + }, + { + 'Nested:0:22:(', + children = { + { + 'Ternary:0:25:?', + children = { + 'Register(name=g):0:23:@g', + { + 'TernaryValue:0:28::', + children = { + 'Register(name=h):0:26:@h', + 'Register(name=i):0:29:@i', + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, { + hl('NestingParenthesis', '('), + hl('Register', '@a'), + hl('Ternary', '?'), + hl('Register', '@b'), + hl('TernaryColon', ':'), + hl('Register', '@c'), + hl('NestingParenthesis', ')'), + hl('Ternary', '?'), + hl('NestingParenthesis', '('), + hl('Register', '@d'), + hl('Ternary', '?'), + hl('Register', '@e'), + hl('TernaryColon', ':'), + hl('Register', '@f'), + hl('NestingParenthesis', ')'), + hl('TernaryColon', ':'), + hl('NestingParenthesis', '('), + hl('Register', '@g'), + hl('Ternary', '?'), + hl('Register', '@h'), + hl('TernaryColon', ':'), + hl('Register', '@i'), + hl('NestingParenthesis', ')'), + }) + + check_parsing('(@a?@b:@c)?@d?@e:@f:@g?@h:@i', { + -- 0123456789012345678901234567 + -- 0 1 2 + ast = { + { + 'Ternary:0:10:?', + children = { + { + 'Nested:0:0:(', + children = { + { + 'Ternary:0:3:?', + children = { + 'Register(name=a):0:1:@a', + { + 'TernaryValue:0:6::', + children = { + 'Register(name=b):0:4:@b', + 'Register(name=c):0:7:@c', + }, + }, + }, + }, + }, + }, + { + 'TernaryValue:0:19::', + children = { + { + 'Ternary:0:13:?', + children = { + 'Register(name=d):0:11:@d', + { + 'TernaryValue:0:16::', + children = { + 'Register(name=e):0:14:@e', + 'Register(name=f):0:17:@f', + }, + }, + }, + }, + { + 'Ternary:0:22:?', + children = { + 'Register(name=g):0:20:@g', + { + 'TernaryValue:0:25::', + children = { + 'Register(name=h):0:23:@h', + 'Register(name=i):0:26:@i', + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, { + hl('NestingParenthesis', '('), + hl('Register', '@a'), + hl('Ternary', '?'), + hl('Register', '@b'), + hl('TernaryColon', ':'), + hl('Register', '@c'), + hl('NestingParenthesis', ')'), + hl('Ternary', '?'), + hl('Register', '@d'), + hl('Ternary', '?'), + hl('Register', '@e'), + hl('TernaryColon', ':'), + hl('Register', '@f'), + hl('TernaryColon', ':'), + hl('Register', '@g'), + hl('Ternary', '?'), + hl('Register', '@h'), + hl('TernaryColon', ':'), + hl('Register', '@i'), + }) + check_parsing('a?b{cdef}g:h', { + -- 012345678901 + -- 0 1 + ast = { + { + 'Ternary:0:1:?', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + { + 'TernaryValue:0:10::', + children = { + { + 'ComplexIdentifier:0:3:', + children = { + 'PlainIdentifier(scope=0,ident=b):0:2:b', + { + 'ComplexIdentifier:0:9:', + children = { + { + fmtn('CurlyBracesIdentifier', '--i', ':0:3:{'), + children = { + 'PlainIdentifier(scope=0,ident=cdef):0:4:cdef', + }, + }, + 'PlainIdentifier(scope=0,ident=g):0:9:g', + }, + }, + }, + }, + 'PlainIdentifier(scope=0,ident=h):0:11:h', + }, + }, + }, + }, + }, + }, { + hl('IdentifierName', 'a'), + hl('Ternary', '?'), + hl('IdentifierName', 'b'), + hl('Curly', '{'), + hl('IdentifierName', 'cdef'), + hl('Curly', '}'), + hl('IdentifierName', 'g'), + hl('TernaryColon', ':'), + hl('IdentifierName', 'h'), + }) + check_parsing('a ? b : c : d', { + -- 0123456789012 + -- 0 1 + ast = { + { + 'Colon:0:9: :', + children = { + { + 'Ternary:0:1: ?', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + { + 'TernaryValue:0:5: :', + children = { + 'PlainIdentifier(scope=0,ident=b):0:3: b', + 'PlainIdentifier(scope=0,ident=c):0:7: c', + }, + }, + }, + }, + 'PlainIdentifier(scope=0,ident=d):0:11: d', + }, + }, + }, + err = { + arg = ': d', + msg = 'E15: Colon outside of dictionary or ternary operator: %.*s', + }, + }, { + hl('IdentifierName', 'a'), + hl('Ternary', '?', 1), + hl('IdentifierName', 'b', 1), + hl('TernaryColon', ':', 1), + hl('IdentifierName', 'c', 1), + hl('InvalidColon', ':', 1), + hl('IdentifierName', 'd', 1), + }) + end) + itp('works with comparison operators', function() + check_parsing('a == b', { + -- 012345 + ast = { + { + 'Comparison(type=Equal,inv=0,ccs=UseOption):0:1: ==', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + 'PlainIdentifier(scope=0,ident=b):0:4: b', + }, + }, + }, + }, { + hl('IdentifierName', 'a'), + hl('Comparison', '==', 1), + hl('IdentifierName', 'b', 1), + }) + + check_parsing('a ==? b', { + -- 0123456 + ast = { + { + 'Comparison(type=Equal,inv=0,ccs=IgnoreCase):0:1: ==?', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + 'PlainIdentifier(scope=0,ident=b):0:5: b', + }, + }, + }, + }, { + hl('IdentifierName', 'a'), + hl('Comparison', '==', 1), + hl('ComparisonModifier', '?'), + hl('IdentifierName', 'b', 1), + }) + + check_parsing('a ==# b', { + -- 0123456 + ast = { + { + 'Comparison(type=Equal,inv=0,ccs=MatchCase):0:1: ==#', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + 'PlainIdentifier(scope=0,ident=b):0:5: b', + }, + }, + }, + }, { + hl('IdentifierName', 'a'), + hl('Comparison', '==', 1), + hl('ComparisonModifier', '#'), + hl('IdentifierName', 'b', 1), + }) + + check_parsing('a !=# b', { + -- 0123456 + ast = { + { + 'Comparison(type=Equal,inv=1,ccs=MatchCase):0:1: !=#', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + 'PlainIdentifier(scope=0,ident=b):0:5: b', + }, + }, + }, + }, { + hl('IdentifierName', 'a'), + hl('Comparison', '!=', 1), + hl('ComparisonModifier', '#'), + hl('IdentifierName', 'b', 1), + }) + + check_parsing('a <=# b', { + -- 0123456 + ast = { + { + 'Comparison(type=Greater,inv=1,ccs=MatchCase):0:1: <=#', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + 'PlainIdentifier(scope=0,ident=b):0:5: b', + }, + }, + }, + }, { + hl('IdentifierName', 'a'), + hl('Comparison', '<=', 1), + hl('ComparisonModifier', '#'), + hl('IdentifierName', 'b', 1), + }) + + check_parsing('a >=# b', { + -- 0123456 + ast = { + { + 'Comparison(type=GreaterOrEqual,inv=0,ccs=MatchCase):0:1: >=#', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + 'PlainIdentifier(scope=0,ident=b):0:5: b', + }, + }, + }, + }, { + hl('IdentifierName', 'a'), + hl('Comparison', '>=', 1), + hl('ComparisonModifier', '#'), + hl('IdentifierName', 'b', 1), + }) + + check_parsing('a ># b', { + -- 012345 + ast = { + { + 'Comparison(type=Greater,inv=0,ccs=MatchCase):0:1: >#', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + 'PlainIdentifier(scope=0,ident=b):0:4: b', + }, + }, + }, + }, { + hl('IdentifierName', 'a'), + hl('Comparison', '>', 1), + hl('ComparisonModifier', '#'), + hl('IdentifierName', 'b', 1), + }) + + check_parsing('a <# b', { + -- 012345 + ast = { + { + 'Comparison(type=GreaterOrEqual,inv=1,ccs=MatchCase):0:1: <#', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + 'PlainIdentifier(scope=0,ident=b):0:4: b', + }, + }, + }, + }, { + hl('IdentifierName', 'a'), + hl('Comparison', '<', 1), + hl('ComparisonModifier', '#'), + hl('IdentifierName', 'b', 1), + }) + + check_parsing('a is#b', { + -- 012345 + ast = { + { + 'Comparison(type=Identical,inv=0,ccs=MatchCase):0:1: is#', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + 'PlainIdentifier(scope=0,ident=b):0:5:b', + }, + }, + }, + }, { + hl('IdentifierName', 'a'), + hl('Comparison', 'is', 1), + hl('ComparisonModifier', '#'), + hl('IdentifierName', 'b'), + }) + + check_parsing('a is?b', { + -- 012345 + ast = { + { + 'Comparison(type=Identical,inv=0,ccs=IgnoreCase):0:1: is?', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + 'PlainIdentifier(scope=0,ident=b):0:5:b', + }, + }, + }, + }, { + hl('IdentifierName', 'a'), + hl('Comparison', 'is', 1), + hl('ComparisonModifier', '?'), + hl('IdentifierName', 'b'), + }) + + check_parsing('a isnot b', { + -- 012345678 + ast = { + { + 'Comparison(type=Identical,inv=1,ccs=UseOption):0:1: isnot', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + 'PlainIdentifier(scope=0,ident=b):0:7: b', + }, + }, + }, + }, { + hl('IdentifierName', 'a'), + hl('Comparison', 'isnot', 1), + hl('IdentifierName', 'b', 1), + }) + + check_parsing('a < b < c', { + -- 012345678 + ast = { + { + 'Comparison(type=GreaterOrEqual,inv=1,ccs=UseOption):0:1: <', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + { + 'Comparison(type=GreaterOrEqual,inv=1,ccs=UseOption):0:5: <', + children = { + 'PlainIdentifier(scope=0,ident=b):0:3: b', + 'PlainIdentifier(scope=0,ident=c):0:7: c', + }, + }, + }, + }, + }, + err = { + arg = ' < c', + msg = 'E15: Operator is not associative: %.*s', + }, + }, { + hl('IdentifierName', 'a'), + hl('Comparison', '<', 1), + hl('IdentifierName', 'b', 1), + hl('InvalidComparison', '<', 1), + hl('IdentifierName', 'c', 1), + }) + + check_parsing('a < b <# c', { + -- 012345678 + ast = { + { + 'Comparison(type=GreaterOrEqual,inv=1,ccs=UseOption):0:1: <', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + { + 'Comparison(type=GreaterOrEqual,inv=1,ccs=MatchCase):0:5: <#', + children = { + 'PlainIdentifier(scope=0,ident=b):0:3: b', + 'PlainIdentifier(scope=0,ident=c):0:8: c', + }, + }, + }, + }, + }, + err = { + arg = ' <# c', + msg = 'E15: Operator is not associative: %.*s', + }, + }, { + hl('IdentifierName', 'a'), + hl('Comparison', '<', 1), + hl('IdentifierName', 'b', 1), + hl('InvalidComparison', '<', 1), + hl('InvalidComparisonModifier', '#'), + hl('IdentifierName', 'c', 1), + }) + + check_parsing('a += b', { + -- 012345 + ast = { + { + 'Assignment(Add):0:1: +=', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + 'PlainIdentifier(scope=0,ident=b):0:4: b', + }, + }, + }, + err = { + arg = '+= b', + msg = 'E15: Misplaced assignment: %.*s', + }, + }, { + hl('IdentifierName', 'a'), + hl('InvalidAssignmentWithAddition', '+=', 1), + hl('IdentifierName', 'b', 1), + }) + check_parsing('a + b == c + d', { + -- 01234567890123 + -- 0 1 + ast = { + { + 'Comparison(type=Equal,inv=0,ccs=UseOption):0:5: ==', + children = { + { + 'BinaryPlus:0:1: +', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + 'PlainIdentifier(scope=0,ident=b):0:3: b', + }, + }, + { + 'BinaryPlus:0:10: +', + children = { + 'PlainIdentifier(scope=0,ident=c):0:8: c', + 'PlainIdentifier(scope=0,ident=d):0:12: d', + }, + }, + }, + }, + }, + }, { + hl('IdentifierName', 'a'), + hl('BinaryPlus', '+', 1), + hl('IdentifierName', 'b', 1), + hl('Comparison', '==', 1), + hl('IdentifierName', 'c', 1), + hl('BinaryPlus', '+', 1), + hl('IdentifierName', 'd', 1), + }) + check_parsing('+ a == + b', { + -- 0123456789 + ast = { + { + 'Comparison(type=Equal,inv=0,ccs=UseOption):0:3: ==', + children = { + { + 'UnaryPlus:0:0:+', + children = { + 'PlainIdentifier(scope=0,ident=a):0:1: a', + }, + }, + { + 'UnaryPlus:0:6: +', + children = { + 'PlainIdentifier(scope=0,ident=b):0:8: b', + }, + }, + }, + }, + }, + }, { + hl('UnaryPlus', '+'), + hl('IdentifierName', 'a', 1), + hl('Comparison', '==', 1), + hl('UnaryPlus', '+', 1), + hl('IdentifierName', 'b', 1), + }) + end) + itp('works with concat/subscript', function() + check_parsing('.', { + -- 0 + ast = { + { + 'ConcatOrSubscript:0:0:.', + children = { + 'Missing:0:0:', + }, + }, + }, + err = { + arg = '.', + msg = 'E15: Unexpected dot: %.*s', + }, + }, { + hl('InvalidConcatOrSubscript', '.'), + }) + + check_parsing('a.', { + -- 01 + ast = { + { + 'ConcatOrSubscript:0:1:.', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + }, + }, + }, + err = { + arg = '', + msg = 'E15: Expected value, got EOC: %.*s', + }, + }, { + hl('IdentifierName', 'a'), + hl('ConcatOrSubscript', '.'), + }) + + check_parsing('a.b', { + -- 012 + ast = { + { + 'ConcatOrSubscript:0:1:.', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + 'PlainKey(key=b):0:2:b', + }, + }, + }, + }, { + hl('IdentifierName', 'a'), + hl('ConcatOrSubscript', '.'), + hl('IdentifierKey', 'b'), + }) + + check_parsing('1.2', { + -- 012 + ast = { + 'Float(val=1.200000e+00):0:0:1.2', + }, + }, { + hl('Float', '1.2'), + }) + + check_parsing('1.2 + 1.3e-5', { + -- 012345678901 + -- 0 1 + ast = { + { + 'BinaryPlus:0:3: +', + children = { + 'Float(val=1.200000e+00):0:0:1.2', + 'Float(val=1.300000e-05):0:5: 1.3e-5', + }, + }, + }, + }, { + hl('Float', '1.2'), + hl('BinaryPlus', '+', 1), + hl('Float', '1.3e-5', 1), + }) + + check_parsing('a . 1.2 + 1.3e-5', { + -- 0123456789012345 + -- 0 1 + ast = { + { + 'BinaryPlus:0:7: +', + children = { + { + 'Concat:0:1: .', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + { + 'ConcatOrSubscript:0:5:.', + children = { + 'Integer(val=1):0:3: 1', + 'PlainKey(key=2):0:6:2', + }, + }, + }, + }, + 'Float(val=1.300000e-05):0:9: 1.3e-5', + }, + }, + }, + }, { + hl('IdentifierName', 'a'), + hl('Concat', '.', 1), + hl('Number', '1', 1), + hl('ConcatOrSubscript', '.'), + hl('IdentifierKey', '2'), + hl('BinaryPlus', '+', 1), + hl('Float', '1.3e-5', 1), + }) + + check_parsing('1.3e-5 + 1.2 . a', { + -- 0123456789012345 + -- 0 1 + ast = { + { + 'Concat:0:12: .', + children = { + { + 'BinaryPlus:0:6: +', + children = { + 'Float(val=1.300000e-05):0:0:1.3e-5', + 'Float(val=1.200000e+00):0:8: 1.2', + }, + }, + 'PlainIdentifier(scope=0,ident=a):0:14: a', + }, + }, + }, + }, { + hl('Float', '1.3e-5'), + hl('BinaryPlus', '+', 1), + hl('Float', '1.2', 1), + hl('Concat', '.', 1), + hl('IdentifierName', 'a', 1), + }) + + check_parsing('1.3e-5 + a . 1.2', { + -- 0123456789012345 + -- 0 1 + ast = { + { + 'Concat:0:10: .', + children = { + { + 'BinaryPlus:0:6: +', + children = { + 'Float(val=1.300000e-05):0:0:1.3e-5', + 'PlainIdentifier(scope=0,ident=a):0:8: a', + }, + }, + { + 'ConcatOrSubscript:0:14:.', + children = { + 'Integer(val=1):0:12: 1', + 'PlainKey(key=2):0:15:2', + }, + }, + }, + }, + }, + }, { + hl('Float', '1.3e-5'), + hl('BinaryPlus', '+', 1), + hl('IdentifierName', 'a', 1), + hl('Concat', '.', 1), + hl('Number', '1', 1), + hl('ConcatOrSubscript', '.'), + hl('IdentifierKey', '2'), + }) + + check_parsing('1.2.3', { + -- 01234 + ast = { + { + 'ConcatOrSubscript:0:3:.', + children = { + { + 'ConcatOrSubscript:0:1:.', + children = { + 'Integer(val=1):0:0:1', + 'PlainKey(key=2):0:2:2', + }, + }, + 'PlainKey(key=3):0:4:3', + }, + }, + }, + }, { + hl('Number', '1'), + hl('ConcatOrSubscript', '.'), + hl('IdentifierKey', '2'), + hl('ConcatOrSubscript', '.'), + hl('IdentifierKey', '3'), + }) + + check_parsing('a.1.2', { + -- 01234 + ast = { + { + 'ConcatOrSubscript:0:3:.', + children = { + { + 'ConcatOrSubscript:0:1:.', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + 'PlainKey(key=1):0:2:1', + }, + }, + 'PlainKey(key=2):0:4:2', + }, + }, + }, + }, { + hl('IdentifierName', 'a'), + hl('ConcatOrSubscript', '.'), + hl('IdentifierKey', '1'), + hl('ConcatOrSubscript', '.'), + hl('IdentifierKey', '2'), + }) + + check_parsing('a . 1.2', { + -- 0123456 + ast = { + { + 'Concat:0:1: .', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + { + 'ConcatOrSubscript:0:5:.', + children = { + 'Integer(val=1):0:3: 1', + 'PlainKey(key=2):0:6:2', + }, + }, + }, + }, + }, + }, { + hl('IdentifierName', 'a'), + hl('Concat', '.', 1), + hl('Number', '1', 1), + hl('ConcatOrSubscript', '.'), + hl('IdentifierKey', '2'), + }) + + check_parsing('+a . +b', { + -- 0123456 + ast = { + { + 'Concat:0:2: .', + children = { + { + 'UnaryPlus:0:0:+', + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + }, + }, + { + 'UnaryPlus:0:4: +', + children = { + 'PlainIdentifier(scope=0,ident=b):0:6:b', + }, + }, + }, + }, + }, + }, { + hl('UnaryPlus', '+'), + hl('IdentifierName', 'a'), + hl('Concat', '.', 1), + hl('UnaryPlus', '+', 1), + hl('IdentifierName', 'b'), + }) + + check_parsing('a. b', { + -- 0123 + ast = { + { + 'Concat:0:1:.', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + 'PlainIdentifier(scope=0,ident=b):0:2: b', + }, + }, + }, + }, { + hl('IdentifierName', 'a'), + hl('ConcatOrSubscript', '.'), + hl('IdentifierName', 'b', 1), + }) + + check_parsing('a. 1', { + -- 0123 + ast = { + { + 'Concat:0:1:.', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + 'Integer(val=1):0:2: 1', + }, + }, + }, + }, { + hl('IdentifierName', 'a'), + hl('ConcatOrSubscript', '.'), + hl('Number', '1', 1), + }) + end) + itp('works with bracket subscripts', function() + check_parsing(':', { + -- 0 + ast = { + { + 'Colon:0:0::', + children = { + 'Missing:0:0:', + }, + }, + }, + err = { + arg = ':', + msg = 'E15: Colon outside of dictionary or ternary operator: %.*s', + }, + }, { + hl('InvalidColon', ':'), + }) + check_parsing('a[]', { + -- 012 + ast = { + { + 'Subscript:0:1:[', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + }, + }, + }, + err = { + arg = ']', + msg = 'E15: Expected value, got closing bracket: %.*s', + }, + }, { + hl('IdentifierName', 'a'), + hl('SubscriptBracket', '['), + hl('InvalidSubscriptBracket', ']'), + }) + check_parsing('a[b:]', { + -- 01234 + ast = { + { + 'Subscript:0:1:[', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + 'PlainIdentifier(scope=b,ident=):0:2:b:', + }, + }, + }, + }, { + hl('IdentifierName', 'a'), + hl('SubscriptBracket', '['), + hl('IdentifierScope', 'b'), + hl('IdentifierScopeDelimiter', ':'), + hl('SubscriptBracket', ']'), + }) + + check_parsing('a[b:c]', { + -- 012345 + ast = { + { + 'Subscript:0:1:[', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + 'PlainIdentifier(scope=b,ident=c):0:2:b:c', + }, + }, + }, + }, { + hl('IdentifierName', 'a'), + hl('SubscriptBracket', '['), + hl('IdentifierScope', 'b'), + hl('IdentifierScopeDelimiter', ':'), + hl('IdentifierName', 'c'), + hl('SubscriptBracket', ']'), + }) + check_parsing('a[b : c]', { + -- 01234567 + ast = { + { + 'Subscript:0:1:[', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + { + 'Colon:0:3: :', + children = { + 'PlainIdentifier(scope=0,ident=b):0:2:b', + 'PlainIdentifier(scope=0,ident=c):0:5: c', + }, + }, + }, + }, + }, + }, { + hl('IdentifierName', 'a'), + hl('SubscriptBracket', '['), + hl('IdentifierName', 'b'), + hl('SubscriptColon', ':', 1), + hl('IdentifierName', 'c', 1), + hl('SubscriptBracket', ']'), + }) + + check_parsing('a[: b]', { + -- 012345 + ast = { + { + 'Subscript:0:1:[', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + { + 'Colon:0:2::', + children = { + 'Missing:0:2:', + 'PlainIdentifier(scope=0,ident=b):0:3: b', + }, + }, + }, + }, + }, + }, { + hl('IdentifierName', 'a'), + hl('SubscriptBracket', '['), + hl('SubscriptColon', ':'), + hl('IdentifierName', 'b', 1), + hl('SubscriptBracket', ']'), + }) + + check_parsing('a[b :]', { + -- 012345 + ast = { + { + 'Subscript:0:1:[', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + { + 'Colon:0:3: :', + children = { + 'PlainIdentifier(scope=0,ident=b):0:2:b', + }, + }, + }, + }, + }, + }, { + hl('IdentifierName', 'a'), + hl('SubscriptBracket', '['), + hl('IdentifierName', 'b'), + hl('SubscriptColon', ':', 1), + hl('SubscriptBracket', ']'), + }) + check_parsing('a[b][c][d](e)(f)(g)', { + -- 0123456789012345678 + -- 0 1 + ast = { + { + 'Call:0:16:(', + children = { + { + 'Call:0:13:(', + children = { + { + 'Call:0:10:(', + children = { + { + 'Subscript:0:7:[', + children = { + { + 'Subscript:0:4:[', + children = { + { + 'Subscript:0:1:[', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + 'PlainIdentifier(scope=0,ident=b):0:2:b', + }, + }, + 'PlainIdentifier(scope=0,ident=c):0:5:c', + }, + }, + 'PlainIdentifier(scope=0,ident=d):0:8:d', + }, + }, + 'PlainIdentifier(scope=0,ident=e):0:11:e', + }, + }, + 'PlainIdentifier(scope=0,ident=f):0:14:f', + }, + }, + 'PlainIdentifier(scope=0,ident=g):0:17:g', + }, + }, + }, + }, { + hl('IdentifierName', 'a'), + hl('SubscriptBracket', '['), + hl('IdentifierName', 'b'), + hl('SubscriptBracket', ']'), + hl('SubscriptBracket', '['), + hl('IdentifierName', 'c'), + hl('SubscriptBracket', ']'), + hl('SubscriptBracket', '['), + hl('IdentifierName', 'd'), + hl('SubscriptBracket', ']'), + hl('CallingParenthesis', '('), + hl('IdentifierName', 'e'), + hl('CallingParenthesis', ')'), + hl('CallingParenthesis', '('), + hl('IdentifierName', 'f'), + hl('CallingParenthesis', ')'), + hl('CallingParenthesis', '('), + hl('IdentifierName', 'g'), + hl('CallingParenthesis', ')'), + }) + check_parsing('{a}{b}{c}[d][e][f]', { + -- 012345678901234567 + -- 0 1 + ast = { + { + 'Subscript:0:15:[', + children = { + { + 'Subscript:0:12:[', + children = { + { + 'Subscript:0:9:[', + children = { + { + 'ComplexIdentifier:0:3:', + children = { + { + fmtn('CurlyBracesIdentifier', '-di', ':0:0:{'), + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + }, + }, + { + 'ComplexIdentifier:0:6:', + children = { + { + fmtn('CurlyBracesIdentifier', '--i', ':0:3:{'), + children = { + 'PlainIdentifier(scope=0,ident=b):0:4:b', + }, + }, + { + fmtn('CurlyBracesIdentifier', '--i', ':0:6:{'), + children = { + 'PlainIdentifier(scope=0,ident=c):0:7:c', + }, + }, + }, + }, + }, + }, + 'PlainIdentifier(scope=0,ident=d):0:10:d', + }, + }, + 'PlainIdentifier(scope=0,ident=e):0:13:e', + }, + }, + 'PlainIdentifier(scope=0,ident=f):0:16:f', + }, + }, + }, + }, { + hl('Curly', '{'), + hl('IdentifierName', 'a'), + hl('Curly', '}'), + hl('Curly', '{'), + hl('IdentifierName', 'b'), + hl('Curly', '}'), + hl('Curly', '{'), + hl('IdentifierName', 'c'), + hl('Curly', '}'), + hl('SubscriptBracket', '['), + hl('IdentifierName', 'd'), + hl('SubscriptBracket', ']'), + hl('SubscriptBracket', '['), + hl('IdentifierName', 'e'), + hl('SubscriptBracket', ']'), + hl('SubscriptBracket', '['), + hl('IdentifierName', 'f'), + hl('SubscriptBracket', ']'), + }) + end) + itp('supports list literals', function() + check_parsing('[]', { + -- 01 + ast = { + 'ListLiteral:0:0:[', + }, + }, { + hl('List', '['), + hl('List', ']'), + }) + + check_parsing('[a]', { + -- 012 + ast = { + { + 'ListLiteral:0:0:[', + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + }, + }, + }, + }, { + hl('List', '['), + hl('IdentifierName', 'a'), + hl('List', ']'), + }) + + check_parsing('[a, b]', { + -- 012345 + ast = { + { + 'ListLiteral:0:0:[', + children = { + { + 'Comma:0:2:,', + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + 'PlainIdentifier(scope=0,ident=b):0:3: b', + }, + }, + }, + }, + }, + }, { + hl('List', '['), + hl('IdentifierName', 'a'), + hl('Comma', ','), + hl('IdentifierName', 'b', 1), + hl('List', ']'), + }) + + check_parsing('[a, b, c]', { + -- 012345678 + ast = { + { + 'ListLiteral:0:0:[', + children = { + { + 'Comma:0:2:,', + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + { + 'Comma:0:5:,', + children = { + 'PlainIdentifier(scope=0,ident=b):0:3: b', + 'PlainIdentifier(scope=0,ident=c):0:6: c', + }, + }, + }, + }, + }, + }, + }, + }, { + hl('List', '['), + hl('IdentifierName', 'a'), + hl('Comma', ','), + hl('IdentifierName', 'b', 1), + hl('Comma', ','), + hl('IdentifierName', 'c', 1), + hl('List', ']'), + }) + + check_parsing('[a, b, c, ]', { + -- 01234567890 + -- 0 1 + ast = { + { + 'ListLiteral:0:0:[', + children = { + { + 'Comma:0:2:,', + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + { + 'Comma:0:5:,', + children = { + 'PlainIdentifier(scope=0,ident=b):0:3: b', + { + 'Comma:0:8:,', + children = { + 'PlainIdentifier(scope=0,ident=c):0:6: c', + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, { + hl('List', '['), + hl('IdentifierName', 'a'), + hl('Comma', ','), + hl('IdentifierName', 'b', 1), + hl('Comma', ','), + hl('IdentifierName', 'c', 1), + hl('Comma', ','), + hl('List', ']', 1), + }) + + check_parsing('[a : b, c : d]', { + -- 01234567890123 + -- 0 1 + ast = { + { + 'ListLiteral:0:0:[', + children = { + { + 'Comma:0:6:,', + children = { + { + 'Colon:0:2: :', + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + 'PlainIdentifier(scope=0,ident=b):0:4: b', + }, + }, + { + 'Colon:0:9: :', + children = { + 'PlainIdentifier(scope=0,ident=c):0:7: c', + 'PlainIdentifier(scope=0,ident=d):0:11: d', + }, + }, + }, + }, + }, + }, + }, + err = { + arg = ': b, c : d]', + msg = 'E15: Colon outside of dictionary or ternary operator: %.*s', + }, + }, { + hl('List', '['), + hl('IdentifierName', 'a'), + hl('InvalidColon', ':', 1), + hl('IdentifierName', 'b', 1), + hl('Comma', ','), + hl('IdentifierName', 'c', 1), + hl('InvalidColon', ':', 1), + hl('IdentifierName', 'd', 1), + hl('List', ']'), + }) + + check_parsing(']', { + -- 0 + ast = { + 'ListLiteral:0:0:', + }, + err = { + arg = ']', + msg = 'E15: Unexpected closing figure brace: %.*s', + }, + }, { + hl('InvalidList', ']'), + }) + + check_parsing('a]', { + -- 01 + ast = { + { + 'ListLiteral:0:1:', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + }, + }, + }, + err = { + arg = ']', + msg = 'E15: Unexpected closing figure brace: %.*s', + }, + }, { + hl('IdentifierName', 'a'), + hl('InvalidList', ']'), + }) + + check_parsing('[] []', { + -- 01234 + ast = { + { + 'OpMissing:0:2:', + children = { + 'ListLiteral:0:0:[', + 'ListLiteral:0:2: [', + }, + }, + }, + err = { + arg = '[]', + msg = 'E15: Missing operator: %.*s', + }, + }, { + hl('List', '['), + hl('List', ']'), + hl('InvalidSpacing', ' '), + hl('List', '['), + hl('List', ']'), + }, { + [1] = { + ast = { + len = 3, + err = REMOVE_THIS, + ast = { + 'ListLiteral:0:0:[', + }, + }, + hl_fs = { + [3] = REMOVE_THIS, + [4] = REMOVE_THIS, + [5] = REMOVE_THIS, + }, + }, + }) + + check_parsing('[][]', { + -- 0123 + ast = { + { + 'Subscript:0:2:[', + children = { + 'ListLiteral:0:0:[', + }, + }, + }, + err = { + arg = ']', + msg = 'E15: Expected value, got closing bracket: %.*s', + }, + }, { + hl('List', '['), + hl('List', ']'), + hl('SubscriptBracket', '['), + hl('InvalidSubscriptBracket', ']'), + }) + + check_parsing('[', { + -- 0 + ast = { + 'ListLiteral:0:0:[', + }, + err = { + arg = '', + msg = 'E15: Expected value, got EOC: %.*s', + }, + }, { + hl('List', '['), + }) + + check_parsing('[1', { + -- 01 + ast = { + { + 'ListLiteral:0:0:[', + children = { + 'Integer(val=1):0:1:1', + }, + }, + }, + err = { + arg = '[1', + msg = 'E697: Missing end of List \']\': %.*s', + }, + }, { + hl('List', '['), + hl('Number', '1'), + }) + end) + itp('works with strings', function() + check_parsing('\'abc\'', { + -- 01234 + ast = { + fmtn('SingleQuotedString', 'val="abc"', ':0:0:\'abc\''), + }, + }, { + hl('SingleQuote', '\''), + hl('SingleQuotedBody', 'abc'), + hl('SingleQuote', '\''), + }) + check_parsing('"abc"', { + -- 01234 + ast = { + fmtn('DoubleQuotedString', 'val="abc"', ':0:0:"abc"'), + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedBody', 'abc'), + hl('DoubleQuote', '"'), + }) + check_parsing('\'\'', { + -- 01 + ast = { + fmtn('SingleQuotedString', 'val=NULL', ':0:0:\'\''), + }, + }, { + hl('SingleQuote', '\''), + hl('SingleQuote', '\''), + }) + check_parsing('""', { + -- 01 + ast = { + fmtn('DoubleQuotedString', 'val=NULL', ':0:0:""'), + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuote', '"'), + }) + check_parsing('"', { + -- 0 + ast = { + fmtn('DoubleQuotedString', 'val=NULL', ':0:0:"'), + }, + err = { + arg = '"', + msg = 'E114: Missing double quote: %.*s', + }, + }, { + hl('InvalidDoubleQuote', '"'), + }) + check_parsing('\'', { + -- 0 + ast = { + fmtn('SingleQuotedString', 'val=NULL', ':0:0:\''), + }, + err = { + arg = '\'', + msg = 'E115: Missing single quote: %.*s', + }, + }, { + hl('InvalidSingleQuote', '\''), + }) + check_parsing('"a', { + -- 01 + ast = { + fmtn('DoubleQuotedString', 'val="a"', ':0:0:"a'), + }, + err = { + arg = '"a', + msg = 'E114: Missing double quote: %.*s', + }, + }, { + hl('InvalidDoubleQuote', '"'), + hl('InvalidDoubleQuotedBody', 'a'), + }) + check_parsing('\'a', { + -- 01 + ast = { + fmtn('SingleQuotedString', 'val="a"', ':0:0:\'a'), + }, + err = { + arg = '\'a', + msg = 'E115: Missing single quote: %.*s', + }, + }, { + hl('InvalidSingleQuote', '\''), + hl('InvalidSingleQuotedBody', 'a'), + }) + check_parsing('\'abc\'\'def\'', { + -- 0123456789 + ast = { + fmtn('SingleQuotedString', 'val="abc\'def"', ':0:0:\'abc\'\'def\''), + }, + }, { + hl('SingleQuote', '\''), + hl('SingleQuotedBody', 'abc'), + hl('SingleQuotedQuote', '\'\''), + hl('SingleQuotedBody', 'def'), + hl('SingleQuote', '\''), + }) + check_parsing('\'abc\'\'', { + -- 012345 + ast = { + fmtn('SingleQuotedString', 'val="abc\'"', ':0:0:\'abc\'\''), + }, + err = { + arg = '\'abc\'\'', + msg = 'E115: Missing single quote: %.*s', + }, + }, { + hl('InvalidSingleQuote', '\''), + hl('InvalidSingleQuotedBody', 'abc'), + hl('InvalidSingleQuotedQuote', '\'\''), + }) + check_parsing('\'\'\'\'\'\'\'\'', { + -- 01234567 + ast = { + fmtn('SingleQuotedString', 'val="\'\'\'"', ':0:0:\'\'\'\'\'\'\'\''), + }, + }, { + hl('SingleQuote', '\''), + hl('SingleQuotedQuote', '\'\''), + hl('SingleQuotedQuote', '\'\''), + hl('SingleQuotedQuote', '\'\''), + hl('SingleQuote', '\''), + }) + check_parsing('\'\'\'a\'\'\'\'bc\'', { + -- 01234567890 + -- 0 1 + ast = { + fmtn('SingleQuotedString', 'val="\'a\'\'bc"', ':0:0:\'\'\'a\'\'\'\'bc\''), + }, + }, { + hl('SingleQuote', '\''), + hl('SingleQuotedQuote', '\'\''), + hl('SingleQuotedBody', 'a'), + hl('SingleQuotedQuote', '\'\''), + hl('SingleQuotedQuote', '\'\''), + hl('SingleQuotedBody', 'bc'), + hl('SingleQuote', '\''), + }) + check_parsing('"\\"\\"\\"\\""', { + -- 0123456789 + ast = { + fmtn('DoubleQuotedString', 'val="\\"\\"\\"\\""', ':0:0:"\\"\\"\\"\\""'), + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedEscape', '\\"'), + hl('DoubleQuotedEscape', '\\"'), + hl('DoubleQuotedEscape', '\\"'), + hl('DoubleQuotedEscape', '\\"'), + hl('DoubleQuote', '"'), + }) + check_parsing('"abc\\"def\\"ghi\\"jkl\\"mno"', { + -- 0123456789012345678901234 + -- 0 1 2 + ast = { + fmtn('DoubleQuotedString', 'val="abc\\"def\\"ghi\\"jkl\\"mno"', ':0:0:"abc\\"def\\"ghi\\"jkl\\"mno"'), + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedBody', 'abc'), + hl('DoubleQuotedEscape', '\\"'), + hl('DoubleQuotedBody', 'def'), + hl('DoubleQuotedEscape', '\\"'), + hl('DoubleQuotedBody', 'ghi'), + hl('DoubleQuotedEscape', '\\"'), + hl('DoubleQuotedBody', 'jkl'), + hl('DoubleQuotedEscape', '\\"'), + hl('DoubleQuotedBody', 'mno'), + hl('DoubleQuote', '"'), + }) + check_parsing('"\\b\\e\\f\\r\\t\\\\"', { + -- 0123456789012345 + -- 0 1 + ast = { + [[DoubleQuotedString(val="\8\27\12\13\9\\"):0:0:"\b\e\f\r\t\\"]], + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedEscape', '\\b'), + hl('DoubleQuotedEscape', '\\e'), + hl('DoubleQuotedEscape', '\\f'), + hl('DoubleQuotedEscape', '\\r'), + hl('DoubleQuotedEscape', '\\t'), + hl('DoubleQuotedEscape', '\\\\'), + hl('DoubleQuote', '"'), + }) + check_parsing('"\\n\n"', { + -- 01234 + ast = { + fmtn('DoubleQuotedString', 'val="\\\n\\\n"', ':0:0:"\\n\n"'), + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedEscape', '\\n'), + hl('DoubleQuotedBody', '\n'), + hl('DoubleQuote', '"'), + }) + check_parsing('"\\x00"', { + -- 012345 + ast = { + fmtn('DoubleQuotedString', 'val="\\0"', ':0:0:"\\x00"'), + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedEscape', '\\x00'), + hl('DoubleQuote', '"'), + }) + check_parsing('"\\xFF"', { + -- 012345 + ast = { + fmtn('DoubleQuotedString', 'val="\255"', ':0:0:"\\xFF"'), + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedEscape', '\\xFF'), + hl('DoubleQuote', '"'), + }) + check_parsing('"\\xF"', { + -- 012345 + ast = { + fmtn('DoubleQuotedString', 'val="\\15"', ':0:0:"\\xF"'), + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedEscape', '\\xF'), + hl('DoubleQuote', '"'), + }) + check_parsing('"\\u00AB"', { + -- 01234567 + ast = { + fmtn('DoubleQuotedString', 'val="«"', ':0:0:"\\u00AB"'), + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedEscape', '\\u00AB'), + hl('DoubleQuote', '"'), + }) + check_parsing('"\\U000000AB"', { + -- 01234567 + ast = { + fmtn('DoubleQuotedString', 'val="«"', ':0:0:"\\U000000AB"'), + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedEscape', '\\U000000AB'), + hl('DoubleQuote', '"'), + }) + check_parsing('"\\x"', { + -- 0123 + ast = { + fmtn('DoubleQuotedString', 'val="x"', ':0:0:"\\x"'), + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedUnknownEscape', '\\x'), + hl('DoubleQuote', '"'), + }) + + check_parsing('"\\x', { + -- 012 + ast = { + fmtn('DoubleQuotedString', 'val="x"', ':0:0:"\\x'), + }, + err = { + arg = '"\\x', + msg = 'E114: Missing double quote: %.*s', + }, + }, { + hl('InvalidDoubleQuote', '"'), + hl('InvalidDoubleQuotedUnknownEscape', '\\x'), + }) + + check_parsing('"\\xF', { + -- 0123 + ast = { + fmtn('DoubleQuotedString', 'val="\\15"', ':0:0:"\\xF'), + }, + err = { + arg = '"\\xF', + msg = 'E114: Missing double quote: %.*s', + }, + }, { + hl('InvalidDoubleQuote', '"'), + hl('InvalidDoubleQuotedEscape', '\\xF'), + }) + + check_parsing('"\\u"', { + -- 0123 + ast = { + fmtn('DoubleQuotedString', 'val="u"', ':0:0:"\\u"'), + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedUnknownEscape', '\\u'), + hl('DoubleQuote', '"'), + }) + + check_parsing('"\\u', { + -- 012 + ast = { + fmtn('DoubleQuotedString', 'val="u"', ':0:0:"\\u'), + }, + err = { + arg = '"\\u', + msg = 'E114: Missing double quote: %.*s', + }, + }, { + hl('InvalidDoubleQuote', '"'), + hl('InvalidDoubleQuotedUnknownEscape', '\\u'), + }) + + check_parsing('"\\U', { + -- 012 + ast = { + fmtn('DoubleQuotedString', 'val="U"', ':0:0:"\\U'), + }, + err = { + arg = '"\\U', + msg = 'E114: Missing double quote: %.*s', + }, + }, { + hl('InvalidDoubleQuote', '"'), + hl('InvalidDoubleQuotedUnknownEscape', '\\U'), + }) + + check_parsing('"\\U"', { + -- 0123 + ast = { + fmtn('DoubleQuotedString', 'val="U"', ':0:0:"\\U"'), + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedUnknownEscape', '\\U'), + hl('DoubleQuote', '"'), + }) + + check_parsing('"\\xFX"', { + -- 012345 + ast = { + fmtn('DoubleQuotedString', 'val="\\15X"', ':0:0:"\\xFX"'), + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedEscape', '\\xF'), + hl('DoubleQuotedBody', 'X'), + hl('DoubleQuote', '"'), + }) + + check_parsing('"\\XFX"', { + -- 012345 + ast = { + fmtn('DoubleQuotedString', 'val="\\15X"', ':0:0:"\\XFX"'), + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedEscape', '\\XF'), + hl('DoubleQuotedBody', 'X'), + hl('DoubleQuote', '"'), + }) + + check_parsing('"\\xX"', { + -- 01234 + ast = { + fmtn('DoubleQuotedString', 'val="xX"', ':0:0:"\\xX"'), + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedUnknownEscape', '\\x'), + hl('DoubleQuotedBody', 'X'), + hl('DoubleQuote', '"'), + }) + + check_parsing('"\\XX"', { + -- 01234 + ast = { + fmtn('DoubleQuotedString', 'val="XX"', ':0:0:"\\XX"'), + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedUnknownEscape', '\\X'), + hl('DoubleQuotedBody', 'X'), + hl('DoubleQuote', '"'), + }) + + check_parsing('"\\uX"', { + -- 01234 + ast = { + fmtn('DoubleQuotedString', 'val="uX"', ':0:0:"\\uX"'), + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedUnknownEscape', '\\u'), + hl('DoubleQuotedBody', 'X'), + hl('DoubleQuote', '"'), + }) + + check_parsing('"\\UX"', { + -- 01234 + ast = { + fmtn('DoubleQuotedString', 'val="UX"', ':0:0:"\\UX"'), + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedUnknownEscape', '\\U'), + hl('DoubleQuotedBody', 'X'), + hl('DoubleQuote', '"'), + }) + + check_parsing('"\\x0X"', { + -- 012345 + ast = { + fmtn('DoubleQuotedString', 'val="\\0X"', ':0:0:"\\x0X"'), + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedEscape', '\\x0'), + hl('DoubleQuotedBody', 'X'), + hl('DoubleQuote', '"'), + }) + + check_parsing('"\\X0X"', { + -- 012345 + ast = { + fmtn('DoubleQuotedString', 'val="\\0X"', ':0:0:"\\X0X"'), + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedEscape', '\\X0'), + hl('DoubleQuotedBody', 'X'), + hl('DoubleQuote', '"'), + }) + + check_parsing('"\\u0X"', { + -- 012345 + ast = { + fmtn('DoubleQuotedString', 'val="\\0X"', ':0:0:"\\u0X"'), + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedEscape', '\\u0'), + hl('DoubleQuotedBody', 'X'), + hl('DoubleQuote', '"'), + }) + + check_parsing('"\\U0X"', { + -- 012345 + ast = { + fmtn('DoubleQuotedString', 'val="\\0X"', ':0:0:"\\U0X"'), + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedEscape', '\\U0'), + hl('DoubleQuotedBody', 'X'), + hl('DoubleQuote', '"'), + }) + + check_parsing('"\\x00X"', { + -- 0123456 + ast = { + fmtn('DoubleQuotedString', 'val="\\0X"', ':0:0:"\\x00X"'), + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedEscape', '\\x00'), + hl('DoubleQuotedBody', 'X'), + hl('DoubleQuote', '"'), + }) + + check_parsing('"\\X00X"', { + -- 0123456 + ast = { + fmtn('DoubleQuotedString', 'val="\\0X"', ':0:0:"\\X00X"'), + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedEscape', '\\X00'), + hl('DoubleQuotedBody', 'X'), + hl('DoubleQuote', '"'), + }) + + check_parsing('"\\u00X"', { + -- 0123456 + ast = { + fmtn('DoubleQuotedString', 'val="\\0X"', ':0:0:"\\u00X"'), + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedEscape', '\\u00'), + hl('DoubleQuotedBody', 'X'), + hl('DoubleQuote', '"'), + }) + + check_parsing('"\\U00X"', { + -- 0123456 + ast = { + fmtn('DoubleQuotedString', 'val="\\0X"', ':0:0:"\\U00X"'), + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedEscape', '\\U00'), + hl('DoubleQuotedBody', 'X'), + hl('DoubleQuote', '"'), + }) + + check_parsing('"\\u000X"', { + -- 01234567 + ast = { + fmtn('DoubleQuotedString', 'val="\\0X"', ':0:0:"\\u000X"'), + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedEscape', '\\u000'), + hl('DoubleQuotedBody', 'X'), + hl('DoubleQuote', '"'), + }) + + check_parsing('"\\U000X"', { + -- 01234567 + ast = { + fmtn('DoubleQuotedString', 'val="\\0X"', ':0:0:"\\U000X"'), + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedEscape', '\\U000'), + hl('DoubleQuotedBody', 'X'), + hl('DoubleQuote', '"'), + }) + + check_parsing('"\\u0000X"', { + -- 012345678 + ast = { + fmtn('DoubleQuotedString', 'val="\\0X"', ':0:0:"\\u0000X"'), + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedEscape', '\\u0000'), + hl('DoubleQuotedBody', 'X'), + hl('DoubleQuote', '"'), + }) + + check_parsing('"\\U0000X"', { + -- 012345678 + ast = { + fmtn('DoubleQuotedString', 'val="\\0X"', ':0:0:"\\U0000X"'), + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedEscape', '\\U0000'), + hl('DoubleQuotedBody', 'X'), + hl('DoubleQuote', '"'), + }) + + check_parsing('"\\U00000X"', { + -- 0123456789 + ast = { + fmtn('DoubleQuotedString', 'val="\\0X"', ':0:0:"\\U00000X"'), + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedEscape', '\\U00000'), + hl('DoubleQuotedBody', 'X'), + hl('DoubleQuote', '"'), + }) + + check_parsing('"\\U000000X"', { + -- 01234567890 + -- 0 1 + ast = { + fmtn('DoubleQuotedString', 'val="\\0X"', ':0:0:"\\U000000X"'), + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedEscape', '\\U000000'), + hl('DoubleQuotedBody', 'X'), + hl('DoubleQuote', '"'), + }) + + check_parsing('"\\U0000000X"', { + -- 012345678901 + -- 0 1 + ast = { + fmtn('DoubleQuotedString', 'val="\\0X"', ':0:0:"\\U0000000X"'), + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedEscape', '\\U0000000'), + hl('DoubleQuotedBody', 'X'), + hl('DoubleQuote', '"'), + }) + + check_parsing('"\\U00000000X"', { + -- 0123456789012 + -- 0 1 + ast = { + fmtn('DoubleQuotedString', 'val="\\0X"', ':0:0:"\\U00000000X"'), + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedEscape', '\\U00000000'), + hl('DoubleQuotedBody', 'X'), + hl('DoubleQuote', '"'), + }) + + check_parsing('"\\x000X"', { + -- 01234567 + ast = { + fmtn('DoubleQuotedString', 'val="\\0000X"', ':0:0:"\\x000X"'), + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedEscape', '\\x00'), + hl('DoubleQuotedBody', '0X'), + hl('DoubleQuote', '"'), + }) + + check_parsing('"\\X000X"', { + -- 01234567 + ast = { + fmtn('DoubleQuotedString', 'val="\\0000X"', ':0:0:"\\X000X"'), + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedEscape', '\\X00'), + hl('DoubleQuotedBody', '0X'), + hl('DoubleQuote', '"'), + }) + + check_parsing('"\\u00000X"', { + -- 0123456789 + ast = { + fmtn('DoubleQuotedString', 'val="\\0000X"', ':0:0:"\\u00000X"'), + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedEscape', '\\u0000'), + hl('DoubleQuotedBody', '0X'), + hl('DoubleQuote', '"'), + }) + + check_parsing('"\\U000000000X"', { + -- 01234567890123 + -- 0 1 + ast = { + fmtn('DoubleQuotedString', 'val="\\0000X"', ':0:0:"\\U000000000X"'), + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedEscape', '\\U00000000'), + hl('DoubleQuotedBody', '0X'), + hl('DoubleQuote', '"'), + }) + + check_parsing('"\\0"', { + -- 0123 + ast = { + fmtn('DoubleQuotedString', 'val="\\0"', ':0:0:"\\0"'), + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedEscape', '\\0'), + hl('DoubleQuote', '"'), + }) + + check_parsing('"\\00"', { + -- 01234 + ast = { + fmtn('DoubleQuotedString', 'val="\\0"', ':0:0:"\\00"'), + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedEscape', '\\00'), + hl('DoubleQuote', '"'), + }) + + check_parsing('"\\000"', { + -- 012345 + ast = { + fmtn('DoubleQuotedString', 'val="\\0"', ':0:0:"\\000"'), + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedEscape', '\\000'), + hl('DoubleQuote', '"'), + }) + + check_parsing('"\\0000"', { + -- 0123456 + ast = { + fmtn('DoubleQuotedString', 'val="\\0000"', ':0:0:"\\0000"'), + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedEscape', '\\000'), + hl('DoubleQuotedBody', '0'), + hl('DoubleQuote', '"'), + }) + + check_parsing('"\\8"', { + -- 0123 + ast = { + fmtn('DoubleQuotedString', 'val="8"', ':0:0:"\\8"'), + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedUnknownEscape', '\\8'), + hl('DoubleQuote', '"'), + }) + + check_parsing('"\\08"', { + -- 01234 + ast = { + fmtn('DoubleQuotedString', 'val="\\0008"', ':0:0:"\\08"'), + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedEscape', '\\0'), + hl('DoubleQuotedBody', '8'), + hl('DoubleQuote', '"'), + }) + + check_parsing('"\\008"', { + -- 012345 + ast = { + fmtn('DoubleQuotedString', 'val="\\0008"', ':0:0:"\\008"'), + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedEscape', '\\00'), + hl('DoubleQuotedBody', '8'), + hl('DoubleQuote', '"'), + }) + + check_parsing('"\\0008"', { + -- 0123456 + ast = { + fmtn('DoubleQuotedString', 'val="\\0008"', ':0:0:"\\0008"'), + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedEscape', '\\000'), + hl('DoubleQuotedBody', '8'), + hl('DoubleQuote', '"'), + }) + + check_parsing('"\\777"', { + -- 012345 + ast = { + fmtn('DoubleQuotedString', 'val="\255"', ':0:0:"\\777"'), + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedEscape', '\\777'), + hl('DoubleQuote', '"'), + }) + + check_parsing('"\\050"', { + -- 012345 + ast = { + fmtn('DoubleQuotedString', 'val="\40"', ':0:0:"\\050"'), + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedEscape', '\\050'), + hl('DoubleQuote', '"'), + }) + + check_parsing('"\\"', { + -- 012345 + ast = { + fmtn('DoubleQuotedString', 'val="\\21"', ':0:0:"\\"'), + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedEscape', '\\'), + hl('DoubleQuote', '"'), + }) + + check_parsing('"\\<', { + -- 012 + ast = { + fmtn('DoubleQuotedString', 'val="<"', ':0:0:"\\<'), + }, + err = { + arg = '"\\<', + msg = 'E114: Missing double quote: %.*s', + }, + }, { + hl('InvalidDoubleQuote', '"'), + hl('InvalidDoubleQuotedUnknownEscape', '\\<'), + }) + + check_parsing('"\\<"', { + -- 0123 + ast = { + fmtn('DoubleQuotedString', 'val="<"', ':0:0:"\\<"'), + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedUnknownEscape', '\\<'), + hl('DoubleQuote', '"'), + }) + + check_parsing('"\\ {b{3}: 4}[5]}()] += 6', { + -- 012345678901234567890123456 + -- 0 1 2 + ast = { + { + 'Assignment(Add):0:22: +=', + children = { + { + 'Subscript:0:1:[', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + { + 'Call:0:19:(', + children = { + { + fmtn('Lambda', '\\di', ':0:2:{'), + children = { + { + 'Arrow:0:3:->', + children = { + { + 'Subscript:0:15:[', + children = { + { + fmtn('DictLiteral', '-di', ':0:5: {'), + children = { + { + 'Colon:0:11::', + children = { + { + 'ComplexIdentifier:0:8:', + children = { + 'PlainIdentifier(scope=0,ident=b):0:7:b', + { + fmtn('CurlyBracesIdentifier', '--i', ':0:8:{'), + children = { + 'Integer(val=3):0:9:3', + }, + }, + }, + }, + 'Integer(val=4):0:12: 4', + }, + }, + }, + }, + 'Integer(val=5):0:16:5', + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + 'Integer(val=6):0:25: 6', + }, + }, + }, + }, { + hl('IdentifierName', 'a'), + hl('SubscriptBracket', '['), + hl('Lambda', '{'), + hl('Arrow', '->'), + hl('Dict', '{', 1), + hl('IdentifierName', 'b'), + hl('Curly', '{'), + hl('Number', '3'), + hl('Curly', '}'), + hl('Colon', ':'), + hl('Number', '4', 1), + hl('Dict', '}'), + hl('SubscriptBracket', '['), + hl('Number', '5'), + hl('SubscriptBracket', ']'), + hl('Lambda', '}'), + hl('CallingParenthesis', '('), + hl('CallingParenthesis', ')'), + hl('SubscriptBracket', ']'), + hl('AssignmentWithAddition', '+=', 1), + hl('Number', '6', 1), + }) + + check_asgn_parsing('a{1}.2[{-> {b{3}: 4}[5]}()]', { + -- 012345678901234567890123456 + -- 0 1 2 + ast = { + { + 'Subscript:0:6:[', + children = { + { + 'ConcatOrSubscript:0:4:.', + children = { + { + 'ComplexIdentifier:0:1:', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + { + fmtn('CurlyBracesIdentifier', '--i', ':0:1:{'), + children = { + 'Integer(val=1):0:2:1', + }, + }, + }, + }, + 'PlainKey(key=2):0:5:2', + }, + }, + { + 'Call:0:24:(', + children = { + { + fmtn('Lambda', '\\di', ':0:7:{'), + children = { + { + 'Arrow:0:8:->', + children = { + { + 'Subscript:0:20:[', + children = { + { + fmtn('DictLiteral', '-di', ':0:10: {'), + children = { + { + 'Colon:0:16::', + children = { + { + 'ComplexIdentifier:0:13:', + children = { + 'PlainIdentifier(scope=0,ident=b):0:12:b', + { + fmtn('CurlyBracesIdentifier', '--i', ':0:13:{'), + children = { + 'Integer(val=3):0:14:3', + }, + }, + }, + }, + 'Integer(val=4):0:17: 4', + }, + }, + }, + }, + 'Integer(val=5):0:21:5', + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, { + hl('IdentifierName', 'a'), + hl('Curly', '{'), + hl('Number', '1'), + hl('Curly', '}'), + hl('ConcatOrSubscript', '.'), + hl('IdentifierKey', '2'), + hl('SubscriptBracket', '['), + hl('Lambda', '{'), + hl('Arrow', '->'), + hl('Dict', '{', 1), + hl('IdentifierName', 'b'), + hl('Curly', '{'), + hl('Number', '3'), + hl('Curly', '}'), + hl('Colon', ':'), + hl('Number', '4', 1), + hl('Dict', '}'), + hl('SubscriptBracket', '['), + hl('Number', '5'), + hl('SubscriptBracket', ']'), + hl('Lambda', '}'), + hl('CallingParenthesis', '('), + hl('CallingParenthesis', ')'), + hl('SubscriptBracket', ']'), + }) + + check_asgn_parsing('a', { + -- 0 + ast = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + }, + }, { + hl('IdentifierName', 'a'), + }) + + check_asgn_parsing('{a}', { + -- 012 + ast = { + { + fmtn('CurlyBracesIdentifier', '--i', ':0:0:{'), + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + }, + }, + }, + }, { + hl('FigureBrace', '{'), + hl('IdentifierName', 'a'), + hl('Curly', '}'), + }) + + check_asgn_parsing('{a}b', { + -- 0123 + ast = { + { + 'ComplexIdentifier:0:3:', + children = { + { + fmtn('CurlyBracesIdentifier', '--i', ':0:0:{'), + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + }, + }, + 'PlainIdentifier(scope=0,ident=b):0:3:b', + }, + }, + }, + }, { + hl('FigureBrace', '{'), + hl('IdentifierName', 'a'), + hl('Curly', '}'), + hl('IdentifierName', 'b'), + }) + + check_asgn_parsing('a{b}c', { + -- 01234 + ast = { + { + 'ComplexIdentifier:0:1:', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + { + 'ComplexIdentifier:0:4:', + children = { + { + fmtn('CurlyBracesIdentifier', '--i', ':0:1:{'), + children = { + 'PlainIdentifier(scope=0,ident=b):0:2:b', + }, + }, + 'PlainIdentifier(scope=0,ident=c):0:4:c', + }, + }, + }, + }, + }, + }, { + hl('IdentifierName', 'a'), + hl('Curly', '{'), + hl('IdentifierName', 'b'), + hl('Curly', '}'), + hl('IdentifierName', 'c'), + }) + + check_asgn_parsing('a{b}c[0]', { + -- 01234567 + ast = { + { + 'Subscript:0:5:[', + children = { + { + 'ComplexIdentifier:0:1:', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + { + 'ComplexIdentifier:0:4:', + children = { + { + fmtn('CurlyBracesIdentifier', '--i', ':0:1:{'), + children = { + 'PlainIdentifier(scope=0,ident=b):0:2:b', + }, + }, + 'PlainIdentifier(scope=0,ident=c):0:4:c', + }, + }, + }, + }, + 'Integer(val=0):0:6:0', + }, + }, + }, + }, { + hl('IdentifierName', 'a'), + hl('Curly', '{'), + hl('IdentifierName', 'b'), + hl('Curly', '}'), + hl('IdentifierName', 'c'), + hl('SubscriptBracket', '['), + hl('Number', '0'), + hl('SubscriptBracket', ']'), + }) + + check_asgn_parsing('a{b}c.0', { + -- 0123456 + ast = { + { + 'ConcatOrSubscript:0:5:.', + children = { + { + 'ComplexIdentifier:0:1:', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + { + 'ComplexIdentifier:0:4:', + children = { + { + fmtn('CurlyBracesIdentifier', '--i', ':0:1:{'), + children = { + 'PlainIdentifier(scope=0,ident=b):0:2:b', + }, + }, + 'PlainIdentifier(scope=0,ident=c):0:4:c', + }, + }, + }, + }, + 'PlainKey(key=0):0:6:0', + }, + }, + }, + }, { + hl('IdentifierName', 'a'), + hl('Curly', '{'), + hl('IdentifierName', 'b'), + hl('Curly', '}'), + hl('IdentifierName', 'c'), + hl('ConcatOrSubscript', '.'), + hl('IdentifierKey', '0'), + }) + + check_asgn_parsing('[a{b}c[0].0]', { + -- 012345678901 + -- 0 1 + ast = { + { + 'ListLiteral:0:0:[', + children = { + { + 'ConcatOrSubscript:0:9:.', + children = { + { + 'Subscript:0:6:[', + children = { + { + 'ComplexIdentifier:0:2:', + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + { + 'ComplexIdentifier:0:5:', + children = { + { + fmtn('CurlyBracesIdentifier', '--i', ':0:2:{'), + children = { + 'PlainIdentifier(scope=0,ident=b):0:3:b', + }, + }, + 'PlainIdentifier(scope=0,ident=c):0:5:c', + }, + }, + }, + }, + 'Integer(val=0):0:7:0', + }, + }, + 'PlainKey(key=0):0:10:0', + }, + }, + }, + }, + }, + }, { + hl('List', '['), + hl('IdentifierName', 'a'), + hl('Curly', '{'), + hl('IdentifierName', 'b'), + hl('Curly', '}'), + hl('IdentifierName', 'c'), + hl('SubscriptBracket', '['), + hl('Number', '0'), + hl('SubscriptBracket', ']'), + hl('ConcatOrSubscript', '.'), + hl('IdentifierKey', '0'), + hl('List', ']'), + }) + + check_asgn_parsing('{a}{b}', { + -- 012345 + ast = { + { + 'ComplexIdentifier:0:3:', + children = { + { + fmtn('CurlyBracesIdentifier', '--i', ':0:0:{'), + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + }, + }, + { + fmtn('CurlyBracesIdentifier', '--i', ':0:3:{'), + children = { + 'PlainIdentifier(scope=0,ident=b):0:4:b', + }, + }, + }, + }, + }, + }, { + hl('FigureBrace', '{'), + hl('IdentifierName', 'a'), + hl('Curly', '}'), + hl('Curly', '{'), + hl('IdentifierName', 'b'), + hl('Curly', '}'), + }) + + check_asgn_parsing('a.b{c}{d}', { + -- 012345678 + ast = { + { + 'OpMissing:0:3:', + children = { + { + 'ConcatOrSubscript:0:1:.', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + 'PlainKey(key=b):0:2:b', + }, + }, + { + 'ComplexIdentifier:0:6:', + children = { + { + fmtn('CurlyBracesIdentifier', '--i', ':0:3:{'), + children = { + 'PlainIdentifier(scope=0,ident=c):0:4:c', + }, + }, + { + fmtn('CurlyBracesIdentifier', '--i', ':0:6:{'), + children = { + 'PlainIdentifier(scope=0,ident=d):0:7:d', + }, + }, + }, + }, + }, + }, + }, + err = { + arg = '{c}{d}', + msg = 'E15: Missing operator: %.*s', + }, + }, { + hl('IdentifierName', 'a'), + hl('ConcatOrSubscript', '.'), + hl('IdentifierKey', 'b'), + hl('InvalidFigureBrace', '{'), + hl('IdentifierName', 'c'), + hl('Curly', '}'), + hl('Curly', '{'), + hl('IdentifierName', 'd'), + hl('Curly', '}'), + }) + + check_asgn_parsing('[a] = 1', { + -- 0123456 + ast = { + { + 'Assignment(Plain):0:3: =', + children = { + { + 'ListLiteral:0:0:[', + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + }, + }, + 'Integer(val=1):0:5: 1', + }, + }, + }, + }, { + hl('List', '['), + hl('IdentifierName', 'a'), + hl('List', ']'), + hl('PlainAssignment', '=', 1), + hl('Number', '1', 1), + }) + + check_asgn_parsing('[a[b], [c, [d, [e]]]] = 1', { + -- 0123456789012345678901234 + -- 0 1 2 + ast = { + { + 'Assignment(Plain):0:21: =', + children = { + { + 'ListLiteral:0:0:[', + children = { + { + 'Comma:0:5:,', + children = { + { + 'Subscript:0:2:[', + children = { + 'PlainIdentifier(scope=0,ident=a):0:1:a', + 'PlainIdentifier(scope=0,ident=b):0:3:b', + }, + }, + { + 'ListLiteral:0:6: [', + children = { + { + 'Comma:0:9:,', + children = { + 'PlainIdentifier(scope=0,ident=c):0:8:c', + { + 'ListLiteral:0:10: [', + children = { + { + 'Comma:0:13:,', + children = { + 'PlainIdentifier(scope=0,ident=d):0:12:d', + { + 'ListLiteral:0:14: [', + children = { + 'PlainIdentifier(scope=0,ident=e):0:16:e', + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + 'Integer(val=1):0:23: 1', + }, + }, + }, + err = { + arg = '[c, [d, [e]]]] = 1', + msg = 'E475: Nested lists not allowed when assigning: %.*s', + }, + }, { + hl('List', '['), + hl('IdentifierName', 'a'), + hl('SubscriptBracket', '['), + hl('IdentifierName', 'b'), + hl('SubscriptBracket', ']'), + hl('Comma', ','), + hl('InvalidList', '[', 1), + hl('IdentifierName', 'c'), + hl('Comma', ','), + hl('InvalidList', '[', 1), + hl('IdentifierName', 'd'), + hl('Comma', ','), + hl('InvalidList', '[', 1), + hl('IdentifierName', 'e'), + hl('List', ']'), + hl('List', ']'), + hl('List', ']'), + hl('List', ']'), + hl('PlainAssignment', '=', 1), + hl('Number', '1', 1), + }) + + check_asgn_parsing('$X += 1', { + -- 0123456 + ast = { + { + 'Assignment(Add):0:2: +=', + children = { + 'Environment(ident=X):0:0:$X', + 'Integer(val=1):0:5: 1', + }, + }, + }, + }, { + hl('EnvironmentSigil', '$'), + hl('EnvironmentName', 'X'), + hl('AssignmentWithAddition', '+=', 1), + hl('Number', '1', 1), + }) + + check_asgn_parsing('@a .= 1', { + -- 0123456 + ast = { + { + 'Assignment(Concat):0:2: .=', + children = { + 'Register(name=a):0:0:@a', + 'Integer(val=1):0:5: 1', + }, + }, + }, + }, { + hl('Register', '@a'), + hl('AssignmentWithConcatenation', '.=', 1), + hl('Number', '1', 1), + }) + + check_asgn_parsing('&option -= 1', { + -- 012345678901 + -- 0 1 + ast = { + { + 'Assignment(Subtract):0:7: -=', + children = { + 'Option(scope=0,ident=option):0:0:&option', + 'Integer(val=1):0:10: 1', + }, + }, + }, + }, { + hl('OptionSigil', '&'), + hl('OptionName', 'option'), + hl('AssignmentWithSubtraction', '-=', 1), + hl('Number', '1', 1), + }) + + check_asgn_parsing('[$X, @a, &l:option] = [1, 2, 3]', { + -- 0123456789012345678901234567890 + -- 0 1 2 3 + ast = { + { + 'Assignment(Plain):0:19: =', + children = { + { + 'ListLiteral:0:0:[', + children = { + { + 'Comma:0:3:,', + children = { + 'Environment(ident=X):0:1:$X', + { + 'Comma:0:7:,', + children = { + 'Register(name=a):0:4: @a', + 'Option(scope=l,ident=option):0:8: &l:option', + }, + }, + }, + }, + }, + }, + { + 'ListLiteral:0:21: [', + children = { + { + 'Comma:0:24:,', + children = { + 'Integer(val=1):0:23:1', + { + 'Comma:0:27:,', + children = { + 'Integer(val=2):0:25: 2', + 'Integer(val=3):0:28: 3', + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, { + hl('List', '['), + hl('EnvironmentSigil', '$'), + hl('EnvironmentName', 'X'), + hl('Comma', ','), + hl('Register', '@a', 1), + hl('Comma', ','), + hl('OptionSigil', '&', 1), + hl('OptionScope', 'l'), + hl('OptionScopeDelimiter', ':'), + hl('OptionName', 'option'), + hl('List', ']'), + hl('PlainAssignment', '=', 1), + hl('List', '[', 1), + hl('Number', '1'), + hl('Comma', ','), + hl('Number', '2', 1), + hl('Comma', ','), + hl('Number', '3', 1), + hl('List', ']'), + }) + end) +end -- cgit From f20f97c936f1438589c8176f62ce69c26e255f85 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 19 Nov 2017 21:13:27 +0300 Subject: *: Fix linter errors --- test/functional/api/vim_spec.lua | 1 - test/helpers.lua | 2 +- test/unit/viml/expressions/parser_spec.lua | 10 +++++----- 3 files changed, 6 insertions(+), 7 deletions(-) (limited to 'test') diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua index 3939bc9b52..2572675a58 100644 --- a/test/functional/api/vim_spec.lua +++ b/test/functional/api/vim_spec.lua @@ -12,7 +12,6 @@ local request = helpers.request local meth_pcall = helpers.meth_pcall local command = helpers.command -local REMOVE_THIS = global_helpers.REMOVE_THIS local intchar2lua = global_helpers.intchar2lua local format_string = global_helpers.format_string local mergedicts_copy = global_helpers.mergedicts_copy diff --git a/test/helpers.lua b/test/helpers.lua index ada690a4d2..fc3936a4ce 100644 --- a/test/helpers.lua +++ b/test/helpers.lua @@ -522,7 +522,7 @@ local function fixtbl(tbl) end local function fixtbl_rec(tbl) - for k, v in pairs(tbl) do + for _, v in pairs(tbl) do if type(v) == 'table' then fixtbl_rec(v) end diff --git a/test/unit/viml/expressions/parser_spec.lua b/test/unit/viml/expressions/parser_spec.lua index 24f681f438..2ae235ca86 100644 --- a/test/unit/viml/expressions/parser_spec.lua +++ b/test/unit/viml/expressions/parser_spec.lua @@ -7,7 +7,6 @@ local make_enum_conv_tab = helpers.make_enum_conv_tab local child_call_once = helpers.child_call_once local alloc_log_new = helpers.alloc_log_new local kvi_destroy = helpers.kvi_destroy -local array_size = helpers.array_size local conv_enum = helpers.conv_enum local debug_log = helpers.debug_log local ptr2key = helpers.ptr2key @@ -26,7 +25,6 @@ local mergedicts_copy = global_helpers.mergedicts_copy local format_string = global_helpers.format_string local format_luav = global_helpers.format_luav local intchar2lua = global_helpers.intchar2lua -local REMOVE_THIS = global_helpers.REMOVE_THIS local dictdiff = global_helpers.dictdiff local lib = cimport('./src/nvim/viml/parser/expressions.h', @@ -147,12 +145,14 @@ child_call_once(function() -- nvim_hl_defs. eq(true, not not (nvim_hl_defs[grp_link] or predefined_hl_defs[grp_link])) + eq(false, not not (nvim_hl_defs[new_grp] + or predefined_hl_defs[new_grp])) nvim_hl_defs[new_grp] = {'link', grp_link} else local new_grp, grp_args = s:match('^(%w+) (.*)') neq(nil, new_grp) - eq(false, not not (nvim_hl_defs[grp_link] - or predefined_hl_defs[grp_link])) + eq(false, not not (nvim_hl_defs[new_grp] + or predefined_hl_defs[new_grp])) nvim_hl_defs[new_grp] = {'definition', grp_args} end end) @@ -206,7 +206,7 @@ local function format_check(expr, format_check_data, opts) -- That forces specific order. local zflags = opts.flags[1] local zdata = format_check_data[zflags] - local dig_len = 0 + local dig_len if opts.funcname then print(format_string('\n%s(%r, {', opts.funcname, expr)) dig_len = #opts.funcname + 2 -- cgit From ebb33eddd9ad0e9cec5013be2e37c8f9b0546c77 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 19 Nov 2017 21:40:34 +0300 Subject: tests: Stabilize float format and %e in format_luav and format_string --- test/functional/api/vim_spec.lua | 2 +- test/helpers.lua | 15 +++++++++++++-- test/unit/viml/expressions/parser_spec.lua | 2 +- 3 files changed, 15 insertions(+), 4 deletions(-) (limited to 'test') diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua index 2572675a58..5b5340d9e2 100644 --- a/test/functional/api/vim_spec.lua +++ b/test/functional/api/vim_spec.lua @@ -750,7 +750,7 @@ describe('api', function() typ = typ .. ('(val=%u)'):format(east_api_node.ivalue) east_api_node.ivalue = nil elseif typ == 'Float' then - typ = typ .. ('(val=%e)'):format(east_api_node.fvalue) + typ = typ .. format_string('(val=%e)', east_api_node.fvalue) east_api_node.fvalue = nil elseif typ == 'SingleQuotedString' or typ == 'DoubleQuotedString' then typ = format_string('%s(val=%q)', typ, east_api_node.svalue) diff --git a/test/helpers.lua b/test/helpers.lua index fc3936a4ce..cef05046d8 100644 --- a/test/helpers.lua +++ b/test/helpers.lua @@ -395,6 +395,13 @@ local function dedent(str, leave_indent) return str end +local function format_float(v) + -- On windows exponent appears to have three digits and not two + local ret = ('%.6e'):format(v) + local l, f, es, e = ret:match('^(%-?%d)%.(%d+)e([+%-])0*(%d%d+)$') + return l .. '.' .. f .. 'e' .. es .. e +end + local SUBTBL = { '\\000', '\\001', '\\002', '\\003', '\\004', '\\005', '\\006', '\\007', '\\008', '\\t', @@ -468,7 +475,7 @@ format_luav = function(v, indent, opts) if v % 1 == 0 then ret = ('%d'):format(v) else - ret = ('%e'):format(v) + ret = format_float(v) end elseif type(v) == 'nil' then ret = 'nil' @@ -501,7 +508,11 @@ local function format_string(fmt, ...) subfmt = subfmt:sub(1, -2) .. 's' arg = format_luav(arg) end - return subfmt:format(arg) + if subfmt == '%e' then + return format_float(arg) + else + return subfmt:format(arg) + end end) return ret end diff --git a/test/unit/viml/expressions/parser_spec.lua b/test/unit/viml/expressions/parser_spec.lua index 2ae235ca86..79b19de833 100644 --- a/test/unit/viml/expressions/parser_spec.lua +++ b/test/unit/viml/expressions/parser_spec.lua @@ -375,7 +375,7 @@ local function eastnode2lua(pstate, eastnode, checked_nodes) elseif typ == 'Integer' then typ = typ .. ('(val=%u)'):format(tonumber(eastnode.data.num.value)) elseif typ == 'Float' then - typ = typ .. ('(val=%e)'):format(tonumber(eastnode.data.flt.value)) + typ = typ .. format_string('(val=%e)', tonumber(eastnode.data.flt.value)) elseif typ == 'SingleQuotedString' or typ == 'DoubleQuotedString' then if eastnode.data.str.value == nil then typ = typ .. '(val=NULL)' -- cgit From 7c20f60b88b1df97af8ea4a6a5b9727c72748ec1 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 19 Nov 2017 21:46:54 +0300 Subject: unittests: Avoid infinite cycle somewhere because of init failure --- test/unit/helpers.lua | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) (limited to 'test') diff --git a/test/unit/helpers.lua b/test/unit/helpers.lua index 2c148630dd..7689c1824b 100644 --- a/test/unit/helpers.lua +++ b/test/unit/helpers.lua @@ -644,13 +644,15 @@ local function itp_child(wr, func) s = s:sub(1, hook_msglen - 2) sc.write(wr, '>' .. s .. (' '):rep(hook_msglen - 2 - #s) .. '\n') end - init() - collectgarbage('stop') - child_sethook(wr) - local err, emsg = pcall(func) - collectgarbage('restart') - collectgarbage() - debug.sethook() + local err, emsg = pcall(init) + if err then + collectgarbage('stop') + child_sethook(wr) + err, emsg = pcall(func) + collectgarbage('restart') + collectgarbage() + debug.sethook() + end emsg = tostring(emsg) sc.write(wr, trace_end_msg) if not err then -- cgit From 64158f2b0b8f0d2e058f7be6f0a0c3faa7de5f22 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 19 Nov 2017 22:32:02 +0300 Subject: unittests: Populate ARGTYPES in child process only --- test/unit/charset/vim_str2nr_spec.lua | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) (limited to 'test') diff --git a/test/unit/charset/vim_str2nr_spec.lua b/test/unit/charset/vim_str2nr_spec.lua index 22504649f6..1a0a000abb 100644 --- a/test/unit/charset/vim_str2nr_spec.lua +++ b/test/unit/charset/vim_str2nr_spec.lua @@ -3,6 +3,7 @@ local global_helpers = require('test.helpers') local itp = helpers.gen_itp(it) +local child_call_once = helpers.child_call_once local cimport = helpers.cimport local ffi = helpers.ffi @@ -11,12 +12,16 @@ local updated = global_helpers.updated local lib = cimport('./src/nvim/charset.h') -local ARGTYPES = { - num = ffi.typeof('varnumber_T[1]'), - unum = ffi.typeof('uvarnumber_T[1]'), - pre = ffi.typeof('int[1]'), - len = ffi.typeof('int[1]'), -} +local ARGTYPES + +child_call_once(function() + ARGTYPES = { + num = ffi.typeof('varnumber_T[1]'), + unum = ffi.typeof('uvarnumber_T[1]'), + pre = ffi.typeof('int[1]'), + len = ffi.typeof('int[1]'), + } +end) local icnt = -42 local ucnt = 4242 -- cgit From 05a3c12118a6dae0ac8f3603f9ee4d9fd9450cce Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 19 Nov 2017 23:36:40 +0300 Subject: unittests: Run vim_str2nr tests with GC enabled --- test/unit/charset/vim_str2nr_spec.lua | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'test') diff --git a/test/unit/charset/vim_str2nr_spec.lua b/test/unit/charset/vim_str2nr_spec.lua index 1a0a000abb..394934c339 100644 --- a/test/unit/charset/vim_str2nr_spec.lua +++ b/test/unit/charset/vim_str2nr_spec.lua @@ -56,6 +56,12 @@ local function test_vim_str2nr(s, what, exp, maxlen) end end +local _itp = itp +itp = function(...) + collectgarbage('restart') + _itp(...) +end + describe('vim_str2nr()', function() itp('works fine when it has nothing to do', function() test_vim_str2nr('', 0, {len = 0, num = 0, unum = 0, pre = 0}, 0) -- cgit From 17077b68133a62d0dc1b84cb48779464c117e028 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 26 Nov 2017 16:08:53 +0300 Subject: viml/parser/expressions: Make $ENV not depend on &isident --- test/functional/api/vim_spec.lua | 3 +++ 1 file changed, 3 insertions(+) (limited to 'test') diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua index 5b5340d9e2..841b7a584a 100644 --- a/test/functional/api/vim_spec.lua +++ b/test/functional/api/vim_spec.lua @@ -717,6 +717,9 @@ describe('api', function() end) describe('nvim_parse_expression', function() + before_each(function() + meths.set_option('isident', '') + end) local function simplify_east_api_node(line, east_api_node) if east_api_node == NIL then return nil -- cgit From cddf84c3982b8225f1592b6a61b63f8d1883ca94 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 26 Nov 2017 16:45:29 +0300 Subject: functests: Add some more tests --- test/functional/ui/cmdline_highlight_spec.lua | 87 ++++++++++++++++++++++++-- test/unit/viml/expressions/parser_tests.lua | 88 +++++++++++++++++++++++++++ 2 files changed, 171 insertions(+), 4 deletions(-) (limited to 'test') diff --git a/test/functional/ui/cmdline_highlight_spec.lua b/test/functional/ui/cmdline_highlight_spec.lua index 023673738d..54d27723f0 100644 --- a/test/functional/ui/cmdline_highlight_spec.lua +++ b/test/functional/ui/cmdline_highlight_spec.lua @@ -147,6 +147,10 @@ before_each(function() PE={bold = true, foreground = Screen.colors.SeaGreen4}, NUM={foreground = Screen.colors.Blue2}, NPAR={foreground = Screen.colors.Yellow}, + SQ={foreground = Screen.colors.Blue3}, + SB={foreground = Screen.colors.Blue4}, + E={foreground = Screen.colors.Red, background = Screen.colors.Blue}, + M={bold = true}, }) end) @@ -898,8 +902,83 @@ describe('Expressions coloring support', function() ={NUM:1}^ | ]]) end) - -- FIXME: Test expr coloring when using -u NORC and -u NONE. - -- FIXME: Test different ways of triggering expression highlighting (:=, - -- i=, :e, "=). - -- FIXME: Test with various invalid unicode and multibyte characters. + it('works correctly with non-ASCII and control characters', function() + meths.command('hi clear NVimStringBody') + meths.command('hi clear NVimStringQuote') + meths.command('hi clear NVimInvalid') + meths.command('hi NVimStringQuote guifg=Blue3') + meths.command('hi NVimStringBody guifg=Blue4') + meths.command('hi NVimInvalid guifg=Red guibg=Blue') + feed('i="«»"«»') + screen:expect([[ + | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + ={SQ:"}{SB:«»}{SQ:"}{E:«»}^ | + ]]) + feed('') + screen:expect([[ + ^ | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {M:-- INSERT --} | + ]]) + feed('') + screen:expect([[ + ^ | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + | + ]]) + feed(':e""') + -- TODO(ZyX-I): Parser highlighting should not override special character + -- highlighting. + screen:expect([[ + | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + ={SQ:"}{SB:^X}{SQ:"}{ERR:^X}^ | + ]]) + feed('') + screen:expect([[ + | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + :^ | + ]]) + funcs.setreg('a', {'\192'}) + feed('="a"a"foo"') + -- TODO(ZyX-I): Parser highlighting should not override special character + -- highlighting. + screen:expect([[ + | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + ={SQ:"}{SB:}{SQ:"}{E:"}{SB:foo}{E:"}^ | + ]]) + end) end) diff --git a/test/unit/viml/expressions/parser_tests.lua b/test/unit/viml/expressions/parser_tests.lua index f0c2723a38..4700b6ee42 100644 --- a/test/unit/viml/expressions/parser_tests.lua +++ b/test/unit/viml/expressions/parser_tests.lua @@ -8182,4 +8182,92 @@ return function(itp, _check_parsing, hl, fmtn) hl('List', ']'), }) end) + itp('works with non-ASCII characters', function() + check_parsing('"«»"«»', { + -- 013568 + ast = { + { + 'OpMissing:0:6:', + children = { + 'DoubleQuotedString(val="«»"):0:0:"«»"', + { + 'ComplexIdentifier:0:8:', + children = { + 'PlainIdentifier(scope=0,ident=«):0:6:«', + 'PlainIdentifier(scope=0,ident=»):0:8:»', + }, + }, + }, + }, + }, + err = { + arg = '«»', + msg = 'E15: Unidentified character: %.*s', + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedBody', '«»'), + hl('DoubleQuote', '"'), + hl('InvalidIdentifierName', '«'), + hl('InvalidIdentifierName', '»'), + }, { + [1] = { + ast = { + ast = { + 'DoubleQuotedString(val="«»"):0:0:"«»"', + }, + len = 6, + }, + hl_fs = { + [5] = REMOVE_THIS, + [4] = REMOVE_THIS, + }, + }, + }) + check_parsing('"\192"\192"foo"', { + -- 01 23 45678 + ast = { + { + 'OpMissing:0:3:', + children = { + 'DoubleQuotedString(val="\192"):0:0:"\192"', + { + 'OpMissing:0:4:', + children = { + 'PlainIdentifier(scope=0,ident=\192):0:3:\192', + 'DoubleQuotedString(val="foo"):0:4:"foo"', + }, + }, + }, + }, + }, + err = { + arg = '\192"foo"', + msg = 'E15: Unidentified character: %.*s', + }, + }, { + hl('DoubleQuote', '"'), + hl('DoubleQuotedBody', '\192'), + hl('DoubleQuote', '"'), + hl('InvalidIdentifierName', '\192'), + hl('InvalidDoubleQuote', '"'), + hl('InvalidDoubleQuotedBody', 'foo'), + hl('InvalidDoubleQuote', '"'), + }, { + [1] = { + ast = { + ast = { + 'DoubleQuotedString(val="\192"):0:0:"\192"', + }, + len = 3, + }, + hl_fs = { + [4] = REMOVE_THIS, + [5] = REMOVE_THIS, + [6] = REMOVE_THIS, + [7] = REMOVE_THIS, + }, + }, + }) + end) end -- cgit From 36a4f3a259ffa282129b18358cce4130397077c5 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 26 Nov 2017 16:57:42 +0300 Subject: viml/parser/expressions: Make sure that listed nodes may be present With the new test leaving `assert(false);` for any of the cases makes tests crash. --- test/unit/viml/expressions/parser_tests.lua | 44 +++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) (limited to 'test') diff --git a/test/unit/viml/expressions/parser_tests.lua b/test/unit/viml/expressions/parser_tests.lua index 4700b6ee42..e085d7e932 100644 --- a/test/unit/viml/expressions/parser_tests.lua +++ b/test/unit/viml/expressions/parser_tests.lua @@ -4228,6 +4228,50 @@ return function(itp, _check_parsing, hl, fmtn) hl('ConcatOrSubscript', '.'), hl('Number', '1', 1), }) + + check_parsing('a[1][2][3[4', { + -- 01234567890 + -- 0 1 + ast = { + { + 'Subscript:0:7:[', + children = { + { + 'Subscript:0:4:[', + children = { + { + 'Subscript:0:1:[', + children = { + 'PlainIdentifier(scope=0,ident=a):0:0:a', + 'Integer(val=1):0:2:1', + }, + }, + 'Integer(val=2):0:5:2', + }, + }, + { + 'Subscript:0:9:[', + children = { + 'Integer(val=3):0:8:3', + 'Integer(val=4):0:10:4', + }, + }, + }, + }, + }, + }, { + hl('IdentifierName', 'a'), + hl('SubscriptBracket', '['), + hl('Number', '1'), + hl('SubscriptBracket', ']'), + hl('SubscriptBracket', '['), + hl('Number', '2'), + hl('SubscriptBracket', ']'), + hl('SubscriptBracket', '['), + hl('Number', '3'), + hl('SubscriptBracket', '['), + hl('Number', '4'), + }) end) itp('works with bracket subscripts', function() check_parsing(':', { -- cgit From de45ec0146486c49719ff6f6dcceb4914b471c7a Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 30 Nov 2017 02:01:49 +0300 Subject: keymap: Do not use vim_isIDc in keymap.c Note: there are three changes to ascii_isident. Reverting first two (in find_special_key and first in get_special_key_code) normally fails the new test with empty &isident, but reverting the third does not. Hence adding `>` to &isident. Ref vim/vim#2389. --- test/functional/ex_cmds/map_spec.lua | 21 +++++++++++++++++++++ test/symbolic/klee/nvim/charset.c | 5 ----- test/symbolic/klee/nvim/keymap.c | 7 ++++--- 3 files changed, 25 insertions(+), 8 deletions(-) create mode 100644 test/functional/ex_cmds/map_spec.lua (limited to 'test') diff --git a/test/functional/ex_cmds/map_spec.lua b/test/functional/ex_cmds/map_spec.lua new file mode 100644 index 0000000000..b46f83405e --- /dev/null +++ b/test/functional/ex_cmds/map_spec.lua @@ -0,0 +1,21 @@ +local helpers = require("test.functional.helpers")(after_each) + +local eq = helpers.eq +local feed = helpers.feed +local meths = helpers.meths +local clear = helpers.clear +local command = helpers.command + +describe(':*map', function() + before_each(clear) + + it('are not affected by &isident', function() + meths.set_var('counter', 0) + command('nnoremap :let counter+=1') + meths.set_option('isident', ('%u'):format(('>'):byte())) + command('nnoremap :let counter+=1') + -- &isident used to disable keycode parsing here as well + feed('\24\25') + eq(4, meths.get_var('counter')) + end) +end) diff --git a/test/symbolic/klee/nvim/charset.c b/test/symbolic/klee/nvim/charset.c index 77f690f08d..95853a6834 100644 --- a/test/symbolic/klee/nvim/charset.c +++ b/test/symbolic/klee/nvim/charset.c @@ -6,11 +6,6 @@ #include "nvim/eval/typval.h" #include "nvim/vim.h" -bool vim_isIDc(int c) -{ - return ASCII_ISALNUM(c); -} - int hex2nr(int c) { if ((c >= 'a') && (c <= 'f')) { diff --git a/test/symbolic/klee/nvim/keymap.c b/test/symbolic/klee/nvim/keymap.c index ff5e46e75b..a341a73689 100644 --- a/test/symbolic/klee/nvim/keymap.c +++ b/test/symbolic/klee/nvim/keymap.c @@ -2,6 +2,7 @@ #include "nvim/types.h" #include "nvim/keymap.h" +#include "nvim/ascii.h" #include "nvim/eval/typval.h" #define MOD_KEYS_ENTRY_SIZE 5 @@ -294,12 +295,12 @@ int get_special_key_code(const char_u *name) for (int i = 0; key_names_table[i].name != NULL; i++) { const char *const table_name = key_names_table[i].name; int j; - for (j = 0; vim_isIDc(name[j]) && table_name[j] != NUL; j++) { + for (j = 0; ascii_isident(name[j]) && table_name[j] != NUL; j++) { if (TOLOWER_ASC(table_name[j]) != TOLOWER_ASC(name[j])) { break; } } - if (!vim_isIDc(name[j]) && table_name[j] == NUL) { + if (!ascii_isident(name[j]) && table_name[j] == NUL) { return key_names_table[i].key; } } @@ -386,7 +387,7 @@ int find_special_key(const char_u **srcp, const size_t src_len, int *const modp, // Find end of modifier list last_dash = src; - for (bp = src + 1; bp <= end && (*bp == '-' || vim_isIDc(*bp)); bp++) { + for (bp = src + 1; bp <= end && (*bp == '-' || ascii_isident(*bp)); bp++) { if (*bp == '-') { last_dash = bp; if (bp + 1 <= end) { -- cgit From 0b4054e043257137ccfd3b2207da48910ce32a5f Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 30 Nov 2017 11:44:48 +0300 Subject: unittests: Reduce memory used by vim_str2nr test --- test/README.md | 3 +++ test/unit/charset/vim_str2nr_spec.lua | 32 +++++++++++++++++++++++++++----- test/unit/helpers.lua | 2 ++ 3 files changed, 32 insertions(+), 5 deletions(-) (limited to 'test') diff --git a/test/README.md b/test/README.md index 6ad70e2f45..1cb814d85b 100644 --- a/test/README.md +++ b/test/README.md @@ -344,3 +344,6 @@ function calls, returns and lua source lines exuecuted. `NVIM_TEST_TRACE_ON_ERROR` (U) (1): makes unit tests yield trace on error in addition to regular error message. + +`NVIM_TEST_MAXTRACE` (U) (N): specifies maximum number of trace lines to keep. +Default is 1024. diff --git a/test/unit/charset/vim_str2nr_spec.lua b/test/unit/charset/vim_str2nr_spec.lua index 394934c339..a6fe613563 100644 --- a/test/unit/charset/vim_str2nr_spec.lua +++ b/test/unit/charset/vim_str2nr_spec.lua @@ -1,5 +1,6 @@ local helpers = require("test.unit.helpers")(after_each) local global_helpers = require('test.helpers') +local bit = require('bit') local itp = helpers.gen_itp(it) @@ -36,15 +37,36 @@ local function arginit(arg) end end +local function argreset(arg, args) + if arg == 'unum' then + ucnt = ucnt + 1 + args[arg][0] = ucnt + else + icnt = icnt - 1 + args[arg][0] = icnt + end +end + local function test_vim_str2nr(s, what, exp, maxlen) - local comb = {[''] = {}} + local comb = {[{}] = true} + local bits = {} for k, _ in pairs(exp) do - for ck, cv in pairs(comb) do - comb[ck .. ',' .. k] = updated(shallowcopy(cv), { [k] = arginit(k) }) - end + bits[#bits + 1] = k end maxlen = maxlen or #s - for _, cv in pairs(comb) do + local args = {} + for k, _ in pairs(ARGTYPES) do + args[k] = arginit(k) + end + for case = 0, ((2 ^ (#bits)) - 1) do + local cv = {} + for b = 0, (#bits - 1) do + if bit.band(case, (2 ^ b)) == 0 then + local k = bits[b + 1] + argreset(k, args) + cv[k] = args[k] + end + end lib.vim_str2nr(s, cv.pre, cv.len, what, cv.num, cv.unum, maxlen) for cck, ccv in pairs(cv) do if exp[cck] ~= tonumber(ccv[0]) then diff --git a/test/unit/helpers.lua b/test/unit/helpers.lua index c26b1c1bcb..b1e709c444 100644 --- a/test/unit/helpers.lua +++ b/test/unit/helpers.lua @@ -675,6 +675,7 @@ end local function check_child_err(rd) local trace = {} local did_traceline = false + local maxtrace = tonumber(os.getenv('NVIM_TEST_MAXTRACE')) or 1024 while true do local traceline = sc.read(rd, hook_msglen) if #traceline ~= hook_msglen then @@ -689,6 +690,7 @@ local function check_child_err(rd) break end trace[#trace + 1] = traceline + table.remove(trace, maxtrace + 1) end local res = sc.read(rd, 2) if #res ~= 2 then -- cgit From 5ab0f988caffad5e8c87a075cbd3f91f0f7e002c Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 30 Nov 2017 11:53:25 +0300 Subject: *: Replace all occurrences of NVim with Nvim --- test/functional/api/vim_spec.lua | 2 +- test/functional/ui/cmdline_highlight_spec.lua | 24 ++++++++++++------------ test/unit/viml/expressions/parser_spec.lua | 16 ++++++++-------- 3 files changed, 21 insertions(+), 21 deletions(-) (limited to 'test') diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua index 841b7a584a..ff28e3d133 100644 --- a/test/functional/api/vim_spec.lua +++ b/test/functional/api/vim_spec.lua @@ -885,7 +885,7 @@ describe('api', function() return function(next_col) local col = next_col + (shift or 0) return (('%s:%u:%u:%s'):format( - 'NVim' .. group, + 'Nvim' .. group, 0, col, str)), (col + #str) diff --git a/test/functional/ui/cmdline_highlight_spec.lua b/test/functional/ui/cmdline_highlight_spec.lua index 54d27723f0..73fe94c056 100644 --- a/test/functional/ui/cmdline_highlight_spec.lua +++ b/test/functional/ui/cmdline_highlight_spec.lua @@ -869,10 +869,10 @@ describe('Ex commands coloring support', function() end) describe('Expressions coloring support', function() it('works', function() - meths.command('hi clear NVimNumber') - meths.command('hi clear NVimNestingParenthesis') - meths.command('hi NVimNumber guifg=Blue2') - meths.command('hi NVimNestingParenthesis guifg=Yellow') + meths.command('hi clear NvimNumber') + meths.command('hi clear NvimNestingParenthesis') + meths.command('hi NvimNumber guifg=Blue2') + meths.command('hi NvimNestingParenthesis guifg=Yellow') feed(':echo =(((1)))') screen:expect([[ | @@ -888,8 +888,8 @@ describe('Expressions coloring support', function() it('does not use Nvim_color_expr', function() meths.set_var('Nvim_color_expr', 42) -- Used to error out due to failing to get callback. - meths.command('hi clear NVimNumber') - meths.command('hi NVimNumber guifg=Blue2') + meths.command('hi clear NvimNumber') + meths.command('hi NvimNumber guifg=Blue2') feed(':=1') screen:expect([[ | @@ -903,12 +903,12 @@ describe('Expressions coloring support', function() ]]) end) it('works correctly with non-ASCII and control characters', function() - meths.command('hi clear NVimStringBody') - meths.command('hi clear NVimStringQuote') - meths.command('hi clear NVimInvalid') - meths.command('hi NVimStringQuote guifg=Blue3') - meths.command('hi NVimStringBody guifg=Blue4') - meths.command('hi NVimInvalid guifg=Red guibg=Blue') + meths.command('hi clear NvimStringBody') + meths.command('hi clear NvimStringQuote') + meths.command('hi clear NvimInvalid') + meths.command('hi NvimStringQuote guifg=Blue3') + meths.command('hi NvimStringBody guifg=Blue4') + meths.command('hi NvimInvalid guifg=Red guibg=Blue') feed('i="«»"«»') screen:expect([[ | diff --git a/test/unit/viml/expressions/parser_spec.lua b/test/unit/viml/expressions/parser_spec.lua index 79b19de833..73388e5dd2 100644 --- a/test/unit/viml/expressions/parser_spec.lua +++ b/test/unit/viml/expressions/parser_spec.lua @@ -164,14 +164,14 @@ child_call_once(function() i = i + 1 end for k, _ in ipairs(nvim_hl_defs) do - eq('NVim', k:sub(1, 4)) - -- NVimInvalid + eq('Nvim', k:sub(1, 4)) + -- NvimInvalid -- 12345678901 local err, msg = pcall(function() if k:sub(5, 11) == 'Invalid' then - neq(nil, nvim_hl_defs['NVim' .. k:sub(12)]) + neq(nil, nvim_hl_defs['Nvim' .. k:sub(12)]) else - neq(nil, nvim_hl_defs['NVimInvalid' .. k:sub(5)]) + neq(nil, nvim_hl_defs['NvimInvalid' .. k:sub(5)]) end end) if not err then @@ -185,7 +185,7 @@ local function hls_to_hl_fs(hls) local ret = {} local next_col = 0 for i, v in ipairs(hls) do - local group, line, col, str = v:match('^NVim([a-zA-Z]+):(%d+):(%d+):(.*)$') + local group, line, col, str = v:match('^Nvim([a-zA-Z]+):(%d+):(%d+):(.*)$') col = tonumber(col) line = tonumber(line) assert(line == 0) @@ -521,12 +521,12 @@ describe('Expressions parser', function() end local function hl(group, str, shift) return function(next_col) - if nvim_hl_defs['NVim' .. group] == nil then - error(('Unknown group: NVim%s'):format(group)) + if nvim_hl_defs['Nvim' .. group] == nil then + error(('Unknown group: Nvim%s'):format(group)) end local col = next_col + (shift or 0) return (('%s:%u:%u:%s'):format( - 'NVim' .. group, + 'Nvim' .. group, 0, col, str)), (col + #str) -- cgit From d763d2fe7aae1c531c72c1d542cad2b19719929b Mon Sep 17 00:00:00 2001 From: FlorianGit Date: Thu, 9 Nov 2017 21:31:17 +0100 Subject: Viml: Make filter and map handle null list correct filter('v:_null_list, 'v:val') should return v:_null_list and a similar statement should hold for map. Changes after review * Test inserted in legacy test suite has been removed by reverting the commit adding it. * Change the fix to tv_copy the argument before returning. * Readd the two tests on crashes, and modified their expected return value. * Move the test from 'incorrect behaviour' section to 'correct behaviour section' * Add analogous tests for v:_null_dict Always copy list or dictionary to return variable If the type of input is correct (i.e. either a list or a dictionary), this should also be returned. --- test/functional/eval/null_spec.lua | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'test') diff --git a/test/functional/eval/null_spec.lua b/test/functional/eval/null_spec.lua index 6fd30caec9..b67158eb22 100644 --- a/test/functional/eval/null_spec.lua +++ b/test/functional/eval/null_spec.lua @@ -42,14 +42,6 @@ describe('NULL', function() describe('list', function() -- Incorrect behaviour - -- FIXME map() should not return 0 without error - null_expr_test('does not crash map()', 'map(L, "v:val")', 0, 0) - -- FIXME map() should not return 0 without error - null_expr_test('does not crash filter()', 'filter(L, "1")', 0, 0) - -- FIXME map() should at least return L - null_expr_test('makes map() return v:_null_list', 'map(L, "v:val") is# L', 0, 0) - -- FIXME filter() should at least return L - null_expr_test('makes filter() return v:_null_list', 'map(L, "1") is# L', 0, 0) -- FIXME add() should not return 1 at all null_expr_test('does not crash add()', 'add(L, 0)', 0, 1) null_expr_test('does not crash extend()', 'extend(L, [1])', 'E742: Cannot change value of extend() argument', 0) @@ -111,6 +103,8 @@ describe('NULL', function() null_expr_test('does not crash line()', 'line(L)', 0, 0) null_expr_test('does not crash count()', 'count(L, 1)', 0, 0) null_expr_test('does not crash cursor()', 'cursor(L)', 'E474: Invalid argument', -1) + null_expr_test('does not crash map()', 'map(L, "v:val")', 0, {}) + null_expr_test('does not crash filter()', 'filter(L, "1")', 0, {}) null_expr_test('is empty', 'empty(L)', 0, 1) null_expr_test('does not crash get()', 'get(L, 1, 10)', 0, 10) null_expr_test('has zero length', 'len(L)', 0, 0) @@ -126,6 +120,8 @@ describe('NULL', function() null_expr_test('is equal to itself', 'L == L', 0, 1) null_expr_test('is not not equal to itself', 'L != L', 0, 0) null_expr_test('counts correctly', 'count([L], L)', 0, 1) + null_expr_test('makes map() return v:_null_list', 'map(L, "v:val") is# L', 0, 1) + null_expr_test('makes filter() return v:_null_list', 'filter(L, "1") is# L', 0, 1) end) describe('dict', function() it('does not crash when indexing NULL dict', function() @@ -134,5 +130,9 @@ describe('NULL', function() end) null_expr_test('makes extend error out', 'extend(D, {})', 'E742: Cannot change value of extend() argument', 0) null_expr_test('makes extend do nothing', 'extend({1: 2}, D)', 0, {['1']=2}) + null_expr_test('does not crash map()', 'map(D, "v:val")', 0, {}) + null_expr_test('does not crash filter()', 'filter(D, "1")', 0, {}) + null_expr_test('makes map() return v:_null_dict', 'map(D, "v:val") is# D', 0, 1) + null_expr_test('makes filter() return v:_null_dict', 'filter(D, "1") is# D', 0, 1) end) end) -- cgit From fbdc3ac4efbc97e2965b6083d429beabe261461c Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 3 Dec 2017 20:22:09 +0300 Subject: tests: Fix linter errors --- test/helpers.lua | 1 + test/unit/charset/vim_str2nr_spec.lua | 5 ----- 2 files changed, 1 insertion(+), 5 deletions(-) (limited to 'test') diff --git a/test/helpers.lua b/test/helpers.lua index 3ec4aa511f..faf5c8e7f2 100644 --- a/test/helpers.lua +++ b/test/helpers.lua @@ -273,6 +273,7 @@ local deepcopy_funcs = { for k, v in pairs(orig) do copy[deepcopy(k)] = deepcopy(v) end + return copy end, number = id, string = id, diff --git a/test/unit/charset/vim_str2nr_spec.lua b/test/unit/charset/vim_str2nr_spec.lua index a6fe613563..891e6def09 100644 --- a/test/unit/charset/vim_str2nr_spec.lua +++ b/test/unit/charset/vim_str2nr_spec.lua @@ -1,5 +1,4 @@ local helpers = require("test.unit.helpers")(after_each) -local global_helpers = require('test.helpers') local bit = require('bit') local itp = helpers.gen_itp(it) @@ -8,9 +7,6 @@ local child_call_once = helpers.child_call_once local cimport = helpers.cimport local ffi = helpers.ffi -local shallowcopy = global_helpers.shallowcopy -local updated = global_helpers.updated - local lib = cimport('./src/nvim/charset.h') local ARGTYPES @@ -48,7 +44,6 @@ local function argreset(arg, args) end local function test_vim_str2nr(s, what, exp, maxlen) - local comb = {[{}] = true} local bits = {} for k, _ in pairs(exp) do bits[#bits + 1] = k -- cgit From a11751eae864b4373d4dfc52905b682ae6ed84d4 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sat, 2 Dec 2017 20:27:39 +0100 Subject: test: tui_spec: narrower scope for timeout tweaks --- test/functional/terminal/tui_spec.lua | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'test') diff --git a/test/functional/terminal/tui_spec.lua b/test/functional/terminal/tui_spec.lua index d5f6a21d1d..c33845e6d9 100644 --- a/test/functional/terminal/tui_spec.lua +++ b/test/functional/terminal/tui_spec.lua @@ -21,9 +21,6 @@ describe('tui', function() clear() screen = thelpers.screen_setup(0, '["'..nvim_prog ..'", "-u", "NONE", "-i", "NONE", "--cmd", "set noswapfile noshowcmd noruler undodir=. directory=. viewdir=. backupdir=."]') - -- right now pasting can be really slow in the TUI, especially in ASAN. - -- this will be fixed later but for now we require a high timeout. - screen.timeout = 60000 screen:expect([[ {1: } | {4:~ }| @@ -125,6 +122,9 @@ describe('tui', function() end) it('automatically sends for bracketed paste sequences', function() + -- Pasting can be really slow in the TUI, specially in ASAN. + -- This will be fixed later but for now we require a high timeout. + screen.timeout = 60000 feed_data('i\027[200~') screen:expect([[ {1: } | @@ -158,6 +158,8 @@ describe('tui', function() end) it('can handle arbitrarily long bursts of input', function() + -- Need extra time for this test, specially in ASAN. + screen.timeout = 60000 feed_command('set ruler') local t = {} for i = 1, 3000 do -- cgit From 175174597dfb773ebe967adcae10a7eb568c32c2 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sat, 2 Dec 2017 20:37:43 +0100 Subject: test: macOS 10.13: unibilium cannot find "xterm" terminfo On some macOS versions we can't find the terminfo for whatever reason, so just skip the test if it fails. --- test/functional/terminal/tui_spec.lua | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'test') diff --git a/test/functional/terminal/tui_spec.lua b/test/functional/terminal/tui_spec.lua index c33845e6d9..79439cc9e9 100644 --- a/test/functional/terminal/tui_spec.lua +++ b/test/functional/terminal/tui_spec.lua @@ -641,6 +641,7 @@ end) describe("tui 'term' option", function() local screen local is_bsd = not not string.find(string.lower(uname()), 'bsd') + local is_macos = not not string.find(string.lower(uname()), 'darwin') local function assert_term(term_envvar, term_expected) clear() @@ -666,8 +667,13 @@ describe("tui 'term' option", function() end) it('gets system-provided term if $TERM is valid', function() - if is_bsd then -- BSD lacks terminfo, we always use builtin there. + if is_bsd then -- BSD lacks terminfo, builtin is always used. assert_term("xterm", "builtin_xterm") + elseif is_macos then + local status, _ = pcall(assert_term, "xterm", "xterm") + if not status then + pending("macOS: unibilium could not find terminfo", function() end) + end else assert_term("xterm", "xterm") end -- cgit From 7f386b175c2e0f7b76a6e21e38bbbff2f6083d0c Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Mon, 4 Dec 2017 22:18:11 +0100 Subject: test: retry(): fix time calculation libuv caches the results of uv.now() until the next loop tick. If a test does not spin the libuv event loop, retry() enters an infinite cycle. --- test/functional/helpers.lua | 1 + 1 file changed, 1 insertion(+) (limited to 'test') diff --git a/test/functional/helpers.lua b/test/functional/helpers.lua index da334d4ac6..1709427d59 100644 --- a/test/functional/helpers.lua +++ b/test/functional/helpers.lua @@ -261,6 +261,7 @@ local function retry(max, max_ms, fn) if status then return result end + luv.update_time() -- Update cached value of luv.now() (libuv: uv_now()). if (max and tries >= max) or (luv.now() - start_time > timeout) then if type(result) == "string" then result = "\nretry() attempts: "..tostring(tries).."\n"..result -- cgit From 5f288220f93f61b3461814d7b93618fc4d9ab7df Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Mon, 4 Dec 2017 22:28:04 +0100 Subject: test: write_file(): support append-mode --- test/functional/helpers.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'test') diff --git a/test/functional/helpers.lua b/test/functional/helpers.lua index 1709427d59..f939567693 100644 --- a/test/functional/helpers.lua +++ b/test/functional/helpers.lua @@ -334,8 +334,8 @@ local function feed_command(...) end -- Dedent the given text and write it to the file name. -local function write_file(name, text, dont_dedent) - local file = io.open(name, 'w') +local function write_file(name, text, no_dedent, append) + local file = io.open(name, (append and 'a' or 'w')) if type(text) == 'table' then -- Byte blob local bytes = text @@ -343,7 +343,7 @@ local function write_file(name, text, dont_dedent) for _, char in ipairs(bytes) do text = ('%s%c'):format(text, char) end - elseif not dont_dedent then + elseif not no_dedent then text = dedent(text) end file:write(text) -- cgit From 837100fcb1c383054bd7c0b41d872377888d862e Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Mon, 4 Dec 2017 22:48:38 +0100 Subject: test/tui: -V3log logs terminfo values --- test/functional/terminal/tui_spec.lua | 52 +++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) (limited to 'test') diff --git a/test/functional/terminal/tui_spec.lua b/test/functional/terminal/tui_spec.lua index 79439cc9e9..39f4401462 100644 --- a/test/functional/terminal/tui_spec.lua +++ b/test/functional/terminal/tui_spec.lua @@ -4,6 +4,7 @@ local global_helpers = require('test.helpers') local uname = global_helpers.uname local helpers = require('test.functional.helpers')(after_each) local thelpers = require('test.functional.terminal.helpers') +local eq = helpers.eq local feed_data = thelpers.feed_data local feed_command = helpers.feed_command local clear = helpers.clear @@ -11,6 +12,8 @@ local nvim_dir = helpers.nvim_dir local retry = helpers.retry local nvim_prog = helpers.nvim_prog local nvim_set = helpers.nvim_set +local ok = helpers.ok +local read_file = helpers.read_file if helpers.pending_win32(pending) then return end @@ -680,3 +683,52 @@ describe("tui 'term' option", function() end) end) + +-- These tests require `thelpers` because --headless/--embed +-- does not initialize the TUI. +describe("tui", function() + local screen + local logfile = 'Xtest_tui_verbose_log' + after_each(function() + os.remove(logfile) + end) + + -- Runs (child) `nvim` in a TTY (:terminal), to start the builtin TUI. + local function nvim_tui(extra_args) + clear() + -- This is ugly because :term/termopen() forces TERM=xterm-256color. + -- TODO: Revisit this after jobstart/termopen accept `env` dict. + local cmd = string.format( + [=[['sh', '-c', 'LANG=C %s -u NONE -i NONE %s --cmd "%s"']]=], + nvim_prog, + extra_args or "", + nvim_set) + screen = thelpers.screen_setup(0, cmd) + end + + it('-V3log logs terminfo values', function() + nvim_tui('-V3'..logfile) + + -- Wait for TUI to start. + feed_data('Gitext') + screen:expect([[ + text{1: } | + {4:~ }| + {4:~ }| + {4:~ }| + {4:~ }| + {3:-- INSERT --} | + {3:-- TERMINAL --} | + ]]) + + -- Vim flushes the log file on exit. + feed_data('\33:q\n') + + retry(nil, 3000, function() -- Wait for log file to be flushed. + local log = read_file('Xtest_tui_verbose_log') or '' + eq('--- Terminal info --- {{{\n', string.match(log, '--- Terminal.-\n')) + ok(#log > 50) + end) + end) + +end) -- cgit From 2d4abc1caedf67487e100f5cef5eca78da68b3e7 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Mon, 4 Dec 2017 23:31:50 +0100 Subject: tui: flush -V3 ('verbose' >= 3) info ASAP --- test/functional/terminal/tui_spec.lua | 3 --- 1 file changed, 3 deletions(-) (limited to 'test') diff --git a/test/functional/terminal/tui_spec.lua b/test/functional/terminal/tui_spec.lua index 39f4401462..bf3c6bdb3a 100644 --- a/test/functional/terminal/tui_spec.lua +++ b/test/functional/terminal/tui_spec.lua @@ -721,9 +721,6 @@ describe("tui", function() {3:-- TERMINAL --} | ]]) - -- Vim flushes the log file on exit. - feed_data('\33:q\n') - retry(nil, 3000, function() -- Wait for log file to be flushed. local log = read_file('Xtest_tui_verbose_log') or '' eq('--- Terminal info --- {{{\n', string.match(log, '--- Terminal.-\n')) -- cgit From 9714b9f59013348b9d16f21822485a2316e93fd7 Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Tue, 5 Dec 2017 13:16:56 +0100 Subject: tests: cleanup ui/cmdline_spec.lua --- test/functional/ui/cmdline_spec.lua | 245 ++++++++++++++++++------------------ 1 file changed, 125 insertions(+), 120 deletions(-) (limited to 'test') diff --git a/test/functional/ui/cmdline_spec.lua b/test/functional/ui/cmdline_spec.lua index 0f8302b036..1b4f12a487 100644 --- a/test/functional/ui/cmdline_spec.lua +++ b/test/functional/ui/cmdline_spec.lua @@ -16,6 +16,11 @@ describe('external cmdline', function() cmdline, block = {}, nil screen = Screen.new(25, 5) screen:attach({rgb=true, ext_cmdline=true}) + screen:set_default_attr_ids({ + [1] = {bold = true, foreground = Screen.colors.Blue1}, + [2] = {reverse = true}, + [3] = {bold = true, reverse = true}, + }) screen:set_on_event_handler(function(name, data) if name == "cmdline_show" then local content, pos, firstc, prompt, indent, level = unpack(data) @@ -66,9 +71,9 @@ describe('external cmdline', function() feed(':') screen:expect([[ ^ | - ~ | - ~ | - ~ | + {1:~ }| + {1:~ }| + {1:~ }| | ]], nil, nil, function() eq(1, last_level) @@ -84,9 +89,9 @@ describe('external cmdline', function() feed('sign') screen:expect([[ ^ | - ~ | - ~ | - ~ | + {1:~ }| + {1:~ }| + {1:~ }| | ]], nil, nil, function() eq({{ @@ -101,9 +106,9 @@ describe('external cmdline', function() feed('') screen:expect([[ ^ | - ~ | - ~ | - ~ | + {1:~ }| + {1:~ }| + {1:~ }| | ]], nil, nil, function() eq({{ @@ -118,9 +123,9 @@ describe('external cmdline', function() feed('') screen:expect([[ ^ | - ~ | - ~ | - ~ | + {1:~ }| + {1:~ }| + {1:~ }| | ]], nil, nil, function() eq({{ @@ -135,9 +140,9 @@ describe('external cmdline', function() feed('') screen:expect([[ ^ | - ~ | - ~ | - ~ | + {1:~ }| + {1:~ }| + {1:~ }| | ]], nil, nil, function() eq({}, cmdline) @@ -148,9 +153,9 @@ describe('external cmdline', function() feed(':call input("input", "default")') screen:expect([[ ^ | - ~ | - ~ | - ~ | + {1:~ }| + {1:~ }| + {1:~ }| | ]], nil, nil, function() eq({{ @@ -164,9 +169,9 @@ describe('external cmdline', function() feed('') screen:expect([[ ^ | - ~ | - ~ | - ~ | + {1:~ }| + {1:~ }| + {1:~ }| | ]], nil, nil, function() eq({}, cmdline) @@ -178,9 +183,9 @@ describe('external cmdline', function() feed(':xx') screen:expect([[ ^ | - ~ | - ~ | - ~ | + {1:~ }| + {1:~ }| + {1:~ }| | ]], nil, nil, function() eq({{ @@ -196,9 +201,9 @@ describe('external cmdline', function() feed('=') screen:expect([[ ^ | - ~ | - ~ | - ~ | + {1:~ }| + {1:~ }| + {1:~ }| | ]], nil, nil, function() eq({{ @@ -234,9 +239,9 @@ describe('external cmdline', function() }} screen:expect([[ ^ | - ~ | - ~ | - ~ | + {1:~ }| + {1:~ }| + {1:~ }| | ]], nil, nil, function() eq(expectation, cmdline) @@ -249,9 +254,9 @@ describe('external cmdline', function() -- focus is at external cmdline anyway. screen:expect([[ | - ~ | - ~ | - ~ | + {1:~ }| + {1:~ }| + {1:~ }| ^ | ]], nil, nil, function() eq(expectation, cmdline) @@ -261,9 +266,9 @@ describe('external cmdline', function() feed('') screen:expect([[ | - ~ | - ~ | - ~ | + {1:~ }| + {1:~ }| + {1:~ }| ^ | ]], nil, nil, function() eq({{ @@ -278,9 +283,9 @@ describe('external cmdline', function() feed('') screen:expect([[ ^ | - ~ | - ~ | - ~ | + {1:~ }| + {1:~ }| + {1:~ }| | ]], nil, nil, function() eq({}, cmdline) @@ -291,9 +296,9 @@ describe('external cmdline', function() feed(':function Foo()') screen:expect([[ ^ | - ~ | - ~ | - ~ | + {1:~ }| + {1:~ }| + {1:~ }| | ]], nil, nil, function() eq({{ @@ -309,9 +314,9 @@ describe('external cmdline', function() feed('line1') screen:expect([[ ^ | - ~ | - ~ | - ~ | + {1:~ }| + {1:~ }| + {1:~ }| | ]], nil, nil, function() eq({{{{}, 'function Foo()'}}, @@ -322,9 +327,9 @@ describe('external cmdline', function() command("redraw!") screen:expect([[ | - ~ | - ~ | - ~ | + {1:~ }| + {1:~ }| + {1:~ }| ^ | ]], nil, nil, function() eq({{{{}, 'function Foo()'}}, @@ -335,9 +340,9 @@ describe('external cmdline', function() feed('endfunction') screen:expect([[ ^ | - ~ | - ~ | - ~ | + {1:~ }| + {1:~ }| + {1:~ }| | ]], nil, nil, function() eq(nil, block) @@ -348,9 +353,9 @@ describe('external cmdline', function() feed(':make') screen:expect([[ ^ | - ~ | - ~ | - ~ | + {1:~ }| + {1:~ }| + {1:~ }| | ]], nil, nil, function() eq({{ @@ -365,9 +370,9 @@ describe('external cmdline', function() feed('') screen:expect([[ | - [No Name] | - :make^ | - [Command Line] | + {2:[No Name] }| + {1::}make^ | + {3:[Command Line] }| | ]], nil, nil, function() eq({}, cmdline) @@ -377,9 +382,9 @@ describe('external cmdline', function() feed(':yank') screen:expect([[ | - [No Name] | - :make^ | - [Command Line] | + {2:[No Name] }| + {1::}make^ | + {3:[Command Line] }| | ]], nil, nil, function() eq({nil, { @@ -395,9 +400,9 @@ describe('external cmdline', function() command("redraw!") screen:expect([[ | - [No Name] | - :make | - [Command Line] | + {2:[No Name] }| + {1::}make | + {3:[Command Line] }| ^ | ]], nil, nil, function() eq({nil, { @@ -412,9 +417,9 @@ describe('external cmdline', function() feed("") screen:expect([[ | - [No Name] | - :make^ | - [Command Line] | + {2:[No Name] }| + {1::}make^ | + {3:[Command Line] }| | ]], nil, nil, function() eq({}, cmdline) @@ -423,9 +428,9 @@ describe('external cmdline', function() feed("") screen:expect([[ | - [No Name] | - :make^ | - [Command Line] | + {2:[No Name] }| + {1::}make^ | + {3:[Command Line] }| | ]], nil, nil, function() eq({{ @@ -441,9 +446,9 @@ describe('external cmdline', function() command("redraw!") screen:expect([[ | - ~ | - ~ | - ~ | + {1:~ }| + {1:~ }| + {1:~ }| ^ | ]], nil, nil, function() eq({{ @@ -460,9 +465,9 @@ describe('external cmdline', function() feed(":call inputsecret('secret:')abc123") screen:expect([[ ^ | - ~ | - ~ | - ~ | + {1:~ }| + {1:~ }| + {1:~ }| | ]], nil, nil, function() eq({{ @@ -476,50 +481,50 @@ describe('external cmdline', function() end) it('works with highlighted cmdline', function() - source([[ - highlight RBP1 guibg=Red - highlight RBP2 guibg=Yellow - highlight RBP3 guibg=Green - highlight RBP4 guibg=Blue - let g:NUM_LVLS = 4 - function RainBowParens(cmdline) - let ret = [] - let i = 0 - let lvl = 0 - while i < len(a:cmdline) - if a:cmdline[i] is# '(' - call add(ret, [i, i + 1, 'RBP' . ((lvl % g:NUM_LVLS) + 1)]) - let lvl += 1 - elseif a:cmdline[i] is# ')' - let lvl -= 1 - call add(ret, [i, i + 1, 'RBP' . ((lvl % g:NUM_LVLS) + 1)]) - endif - let i += 1 - endwhile - return ret - endfunction - map :let x = input({'prompt':'>','highlight':'RainBowParens'}) - "map :let x = input({'prompt':'>'}) - ]]) - screen:set_default_attr_ids({ - RBP1={background = Screen.colors.Red}, - RBP2={background = Screen.colors.Yellow}, - RBP3={background = Screen.colors.Green}, - RBP4={background = Screen.colors.Blue}, - EOB={bold = true, foreground = Screen.colors.Blue1}, - ERR={foreground = Screen.colors.Grey100, background = Screen.colors.Red}, - SK={foreground = Screen.colors.Blue}, - PE={bold = true, foreground = Screen.colors.SeaGreen4} - }) - feed('(a(b)a)') - screen:expect([[ - ^ | - {EOB:~ }| - {EOB:~ }| - {EOB:~ }| - | - ]], nil, nil, function() - expect_cmdline(1, '{RBP1:(}a{RBP2:(}b{RBP2:)}a{RBP1:)}') - end) + source([[ + highlight RBP1 guibg=Red + highlight RBP2 guibg=Yellow + highlight RBP3 guibg=Green + highlight RBP4 guibg=Blue + let g:NUM_LVLS = 4 + function RainBowParens(cmdline) + let ret = [] + let i = 0 + let lvl = 0 + while i < len(a:cmdline) + if a:cmdline[i] is# '(' + call add(ret, [i, i + 1, 'RBP' . ((lvl % g:NUM_LVLS) + 1)]) + let lvl += 1 + elseif a:cmdline[i] is# ')' + let lvl -= 1 + call add(ret, [i, i + 1, 'RBP' . ((lvl % g:NUM_LVLS) + 1)]) + endif + let i += 1 + endwhile + return ret + endfunction + map :let x = input({'prompt':'>','highlight':'RainBowParens'}) + "map :let x = input({'prompt':'>'}) + ]]) + screen:set_default_attr_ids({ + RBP1={background = Screen.colors.Red}, + RBP2={background = Screen.colors.Yellow}, + RBP3={background = Screen.colors.Green}, + RBP4={background = Screen.colors.Blue}, + EOB={bold = true, foreground = Screen.colors.Blue1}, + ERR={foreground = Screen.colors.Grey100, background = Screen.colors.Red}, + SK={foreground = Screen.colors.Blue}, + PE={bold = true, foreground = Screen.colors.SeaGreen4} + }) + feed('(a(b)a)') + screen:expect([[ + ^ | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + | + ]], nil, nil, function() + expect_cmdline(1, '{RBP1:(}a{RBP2:(}b{RBP2:)}a{RBP1:)}') + end) end) end) -- cgit From ba7d6a9e6b3c42f1996d05cec471b4842eeb8b66 Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Tue, 5 Dec 2017 13:27:06 +0100 Subject: ui: fix glitch with both ext_cmdline and cmd_wildmenu --- test/functional/ui/cmdline_spec.lua | 118 ++++++++++++++++++++++++++++++++++++ test/functional/ui/screen.lua | 4 ++ 2 files changed, 122 insertions(+) (limited to 'test') diff --git a/test/functional/ui/cmdline_spec.lua b/test/functional/ui/cmdline_spec.lua index 1b4f12a487..c9ee61fb1e 100644 --- a/test/functional/ui/cmdline_spec.lua +++ b/test/functional/ui/cmdline_spec.lua @@ -10,6 +10,8 @@ describe('external cmdline', function() local last_level = 0 local cmdline = {} local block = nil + local wild_items = nil + local wild_selected = nil before_each(function() clear() @@ -43,6 +45,12 @@ describe('external cmdline', function() block[#block+1] = data[1] elseif name == "cmdline_block_hide" then block = nil + elseif name == "wildmenu_show" then + wild_items = data[1] + elseif name == "wildmenu_select" then + wild_selected = data[1] + elseif name == "wildmenu_hide" then + wild_items, wild_selected = nil, nil end end) end) @@ -527,4 +535,114 @@ describe('external cmdline', function() expect_cmdline(1, '{RBP1:(}a{RBP2:(}b{RBP2:)}a{RBP1:)}') end) end) + + it('works together with ext_wildmenu', function() + local expected = { + 'define', + 'jump', + 'list', + 'place', + 'undefine', + 'unplace', + } + + command('set wildmode=full') + command('set wildmenu') + screen:set_option('ext_wildmenu', true) + feed(':sign ') + + screen:expect([[ + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + | + ]], nil, nil, function() + eq({{ + content = { { {}, "sign define"} }, + firstc = ":", + indent = 0, + pos = 11, + prompt = "" + }}, cmdline) + eq(expected, wild_items) + eq(0, wild_selected) + end) + + feed('') + screen:expect([[ + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + | + ]], nil, nil, function() + eq({{ + content = { { {}, "sign jump"} }, + firstc = ":", + indent = 0, + pos = 9, + prompt = "" + }}, cmdline) + eq(expected, wild_items) + eq(1, wild_selected) + end) + + feed('') + screen:expect([[ + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + | + ]], nil, nil, function() + eq({{ + content = { { {}, "sign "} }, + firstc = ":", + indent = 0, + pos = 5, + prompt = "" + }}, cmdline) + eq(expected, wild_items) + eq(-1, wild_selected) + end) + + feed('') + screen:expect([[ + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + | + ]], nil, nil, function() + eq({{ + content = { { {}, "sign define"} }, + firstc = ":", + indent = 0, + pos = 11, + prompt = "" + }}, cmdline) + eq(expected, wild_items) + eq(0, wild_selected) + end) + + feed('a') + screen:expect([[ + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + | + ]], nil, nil, function() + eq({{ + content = { { {}, "sign definea"} }, + firstc = ":", + indent = 0, + pos = 12, + prompt = "" + }}, cmdline) + eq(nil, wild_items) + eq(nil, wild_selected) + end) + end) end) diff --git a/test/functional/ui/screen.lua b/test/functional/ui/screen.lua index a6b7fb2997..075d8c40d7 100644 --- a/test/functional/ui/screen.lua +++ b/test/functional/ui/screen.lua @@ -176,6 +176,10 @@ function Screen:try_resize(columns, rows) self:sleep(0.1) end +function Screen:set_option(option, value) + uimeths.set_option(option, value) +end + -- Asserts that `expected` eventually matches the screen state. -- -- expected: Expected screen state (string). Each line represents a screen -- cgit From 274f32d42e61e6f6c76b9ca499f5b79f256a481a Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 10 Dec 2017 03:39:51 +0300 Subject: *: Start hiding list implementation Most of files, except for eval.c and eval/* were only processed by perl. --- test/functional/eval/null_spec.lua | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) (limited to 'test') diff --git a/test/functional/eval/null_spec.lua b/test/functional/eval/null_spec.lua index b67158eb22..992bc6b39f 100644 --- a/test/functional/eval/null_spec.lua +++ b/test/functional/eval/null_spec.lua @@ -42,10 +42,6 @@ describe('NULL', function() describe('list', function() -- Incorrect behaviour - -- FIXME add() should not return 1 at all - null_expr_test('does not crash add()', 'add(L, 0)', 0, 1) - null_expr_test('does not crash extend()', 'extend(L, [1])', 'E742: Cannot change value of extend() argument', 0) - null_expr_test('does not crash extend() (second position)', 'extend([1], L)', 0, {1}) -- FIXME should be accepted by inputlist() null_expr_test('is accepted as an empty list by inputlist()', '[feedkeys("\\n"), inputlist(L)]', 'E686: Argument of inputlist() must be a List', {0, 0}) @@ -53,18 +49,12 @@ describe('NULL', function() null_expr_test('is accepted as an empty list by writefile()', ('[writefile(L, "%s"), readfile("%s")]'):format(tmpfname, tmpfname), 'E484: Can\'t open file ' .. tmpfname, {0, {}}) - -- FIXME should give error message - null_expr_test('does not crash remove()', 'remove(L, 0)', 0, 0) -- FIXME should return 0 null_expr_test('is accepted by setqflist()', 'setqflist(L)', 0, -1) -- FIXME should return 0 null_expr_test('is accepted by setloclist()', 'setloclist(1, L)', 0, -1) -- FIXME should return 0 null_expr_test('is accepted by setmatches()', 'setmatches(L)', 0, -1) - -- FIXME should return empty list or error out - null_expr_test('is accepted by sort()', 'sort(L)', 0, 0) - -- FIXME Should return 1 - null_expr_test('is accepted by sort()', 'sort(L) is L', 0, 0) -- FIXME should not error out null_test('is accepted by :cexpr', 'cexpr L', 'Vim(cexpr):E777: String or List expected') -- FIXME should not error out @@ -80,13 +70,6 @@ describe('NULL', function() -- FIXME Should return 1 null_expr_test('is not locked', 'islocked("v:_null_list")', 0, 0) - -- Crashes - - -- null_expr_test('does not crash setreg', 'setreg("x", L)', 0, 0) - -- null_expr_test('does not crash setline', 'setline(1, L)', 0, 0) - -- null_expr_test('does not crash system()', 'system("cat", L)', 0, '') - -- null_expr_test('does not crash systemlist()', 'systemlist("cat", L)', 0, {}) - -- Correct behaviour null_expr_test('does not crash append()', 'append(1, L)', 0, 0, function() eq({''}, curbufmeths.get_lines(0, -1, false)) @@ -122,6 +105,22 @@ describe('NULL', function() null_expr_test('counts correctly', 'count([L], L)', 0, 1) null_expr_test('makes map() return v:_null_list', 'map(L, "v:val") is# L', 0, 1) null_expr_test('makes filter() return v:_null_list', 'filter(L, "1") is# L', 0, 1) + null_test('is treated by :let as empty list', ':let [l] = L', 'Vim(let):E688: More targets than List items') + -- FIXME fix test results + null_expr_test('does not crash add()', 'add(L, 0)', 0, 1) + null_expr_test('makes insert() error out', 'insert(L, 1)', '', nil) + null_expr_test('does not crash remove()', 'remove(L, 0)', 0, 0) + null_expr_test('makes reverse() error out', 'reverse(L)', '', nil) + null_expr_test('is accepted by sort()', 'sort(L)', 0, 0) + null_expr_test('makes sort() return itself', 'sort(L) is L', 0, 0) + null_expr_test('does not crash extend()', 'extend(L, [1])', 'E742: Cannot change value of extend() argument', 0) + null_expr_test('does not crash extend() (second position)', 'extend([1], L)', 0, {1}) + null_expr_test('makes join() return empty string', 'join(L, "")', 0, '') + null_expr_test('makes msgpackdump() return empty list', 'msgpackdump(L)', 0, {}) + null_expr_test('does not crash system()', 'system("cat", L)', 0, '') + null_expr_test('does not crash setreg', 'setreg("x", L)', 0, 0) + null_expr_test('does not crash systemlist()', 'systemlist("cat", L)', 0, {}) + null_expr_test('does not crash setline', 'setline(1, L)', 0, 0) end) describe('dict', function() it('does not crash when indexing NULL dict', function() -- cgit From 21745d72b8c24c7f19dea5d53384da4875c43e74 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 10 Dec 2017 03:50:52 +0300 Subject: eval: Fix inputlist() --- test/functional/eval/null_spec.lua | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'test') diff --git a/test/functional/eval/null_spec.lua b/test/functional/eval/null_spec.lua index 992bc6b39f..20f9c70a41 100644 --- a/test/functional/eval/null_spec.lua +++ b/test/functional/eval/null_spec.lua @@ -42,9 +42,6 @@ describe('NULL', function() describe('list', function() -- Incorrect behaviour - -- FIXME should be accepted by inputlist() - null_expr_test('is accepted as an empty list by inputlist()', - '[feedkeys("\\n"), inputlist(L)]', 'E686: Argument of inputlist() must be a List', {0, 0}) -- FIXME should be accepted by writefile(), return {0, {}} null_expr_test('is accepted as an empty list by writefile()', ('[writefile(L, "%s"), readfile("%s")]'):format(tmpfname, tmpfname), @@ -106,6 +103,7 @@ describe('NULL', function() null_expr_test('makes map() return v:_null_list', 'map(L, "v:val") is# L', 0, 1) null_expr_test('makes filter() return v:_null_list', 'filter(L, "1") is# L', 0, 1) null_test('is treated by :let as empty list', ':let [l] = L', 'Vim(let):E688: More targets than List items') + null_expr_test('is accepted as an empty list by inputlist()', '[feedkeys("\\n"), inputlist(L)]', 0, 0) -- FIXME fix test results null_expr_test('does not crash add()', 'add(L, 0)', 0, 1) null_expr_test('makes insert() error out', 'insert(L, 1)', '', nil) -- cgit From 5c1ddb5078c90f69c7225a7b2e74ccb914dcdd6a Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 10 Dec 2017 03:54:21 +0300 Subject: eval: Fix writefile() --- test/functional/eval/null_spec.lua | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'test') diff --git a/test/functional/eval/null_spec.lua b/test/functional/eval/null_spec.lua index 20f9c70a41..7452185208 100644 --- a/test/functional/eval/null_spec.lua +++ b/test/functional/eval/null_spec.lua @@ -42,10 +42,6 @@ describe('NULL', function() describe('list', function() -- Incorrect behaviour - -- FIXME should be accepted by writefile(), return {0, {}} - null_expr_test('is accepted as an empty list by writefile()', - ('[writefile(L, "%s"), readfile("%s")]'):format(tmpfname, tmpfname), - 'E484: Can\'t open file ' .. tmpfname, {0, {}}) -- FIXME should return 0 null_expr_test('is accepted by setqflist()', 'setqflist(L)', 0, -1) -- FIXME should return 0 @@ -104,6 +100,9 @@ describe('NULL', function() null_expr_test('makes filter() return v:_null_list', 'filter(L, "1") is# L', 0, 1) null_test('is treated by :let as empty list', ':let [l] = L', 'Vim(let):E688: More targets than List items') null_expr_test('is accepted as an empty list by inputlist()', '[feedkeys("\\n"), inputlist(L)]', 0, 0) + null_expr_test('is accepted as an empty list by writefile()', + ('[writefile(L, "%s"), readfile("%s")]'):format(tmpfname, tmpfname), + 0, {0, {}}) -- FIXME fix test results null_expr_test('does not crash add()', 'add(L, 0)', 0, 1) null_expr_test('makes insert() error out', 'insert(L, 1)', '', nil) -- cgit From abe38f7d26d68d7032ea391c039c56c8b87675a5 Mon Sep 17 00:00:00 2001 From: glacambre Date: Sun, 26 Nov 2017 13:29:32 +0100 Subject: window.c: do BufEnter in correct window after closing help #7431 closes #7429 Problem: after a help window was closed, a window was selected and its autocommands triggered. After that, restore_snapshot was called and the focused window changed, confusing the user. Solution: Add function get_snapshot_focus() that returns the window that holds the cursor in a snapshot. Use this function in win_close to make sure the right window is selected before any autocommand is triggered. --- test/functional/autocmd/bufenter_spec.lua | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'test') diff --git a/test/functional/autocmd/bufenter_spec.lua b/test/functional/autocmd/bufenter_spec.lua index fef9838050..e14ddb3316 100644 --- a/test/functional/autocmd/bufenter_spec.lua +++ b/test/functional/autocmd/bufenter_spec.lua @@ -31,4 +31,17 @@ describe('autocmd BufEnter', function() eq(1, eval("exists('g:dir_bufenter')")) -- Did BufEnter for the directory. eq(2, eval("bufnr('%')")) -- Switched to the dir buffer. end) + + it('triggered by ":split normal|:help|:bw"', function() + command("split normal") + command("wincmd j") + command("helptags runtime/doc") + command("help") + command("wincmd L") + command("autocmd BufEnter normal let g:bufentered = 1") + command("bw") + eq(1, eval('bufnr("%")')) -- The cursor is back to the bottom window + eq(0, eval("exists('g:bufentered')")) -- The autocmd hasn't been triggered + end) + end) -- cgit From ad9c2d3cb97bbc4ba83ce7d23c9df23f9d3d11db Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 14 Nov 2017 10:47:49 +0100 Subject: doc closes #7622 --- test/README.md | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) (limited to 'test') diff --git a/test/README.md b/test/README.md index 1cb814d85b..58aa6a06aa 100644 --- a/test/README.md +++ b/test/README.md @@ -168,16 +168,13 @@ minutes](http://learnxinyminutes.com/docs/lua/). Do not silently skip the test with `if-else`. If a functional test depends on some external factor (e.g. the existence of `md5sum` on `$PATH`), *and* you can't mock or fake the dependency, then skip the test via `pending()` if the - external factor is missing. This ensures that the *total* test-count (success - + fail + error + pending) is the same in all environments. + external factor is missing. This ensures that the *total* test-count + (success + fail + error + pending) is the same in all environments. - *Note:* `pending()` is ignored if it is missing an argument _unless_ it is - [contained in an `it()` - block](https://github.com/neovim/neovim/blob/d21690a66e7eb5ebef18046c7a79ef898966d786/test/functional/ex_cmds/grep_spec.lua#L11). - Provide empty function argument if the `pending()` call is outside of - `it()` + [contained in an `it()` block](https://github.com/neovim/neovim/blob/d21690a66e7eb5ebef18046c7a79ef898966d786/test/functional/ex_cmds/grep_spec.lua#L11). + Provide empty function argument if the `pending()` call is outside of `it()` ([example](https://github.com/neovim/neovim/commit/5c1dc0fbe7388528875aff9d7b5055ad718014de#diff-bf80b24c724b0004e8418102f68b0679R18)). -- Use `make testlint` for using the shipped luacheck program ([supported by - syntastic](https://github.com/scrooloose/syntastic/blob/d6b96c079be137c83009827b543a83aa113cc011/doc/syntastic-checkers.txt#L3546)) +- Use `make testlint` for using the shipped luacheck program ([supported by syntastic](https://github.com/scrooloose/syntastic/blob/d6b96c079be137c83009827b543a83aa113cc011/doc/syntastic-checkers.txt#L3546)) to lint all tests. ### Where tests go -- cgit From ac4bbf55f6d6b9b252dd90fe800626850022b690 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 10 Dec 2017 22:04:43 +0300 Subject: *: Hide list implementation in other files as well --- test/functional/eval/null_spec.lua | 2 ++ 1 file changed, 2 insertions(+) (limited to 'test') diff --git a/test/functional/eval/null_spec.lua b/test/functional/eval/null_spec.lua index 7452185208..d85f5297d2 100644 --- a/test/functional/eval/null_spec.lua +++ b/test/functional/eval/null_spec.lua @@ -118,6 +118,8 @@ describe('NULL', function() null_expr_test('does not crash setreg', 'setreg("x", L)', 0, 0) null_expr_test('does not crash systemlist()', 'systemlist("cat", L)', 0, {}) null_expr_test('does not crash setline', 'setline(1, L)', 0, 0) + null_test('does not make Neovim crash when v:oldfiles gets assigned to that', ':let v:oldfiles = L|oldfiles', 0) + -- FIXME Add test for complete(, L) end) describe('dict', function() it('does not crash when indexing NULL dict', function() -- cgit From f572bd7e4e15a99cc19244a4411c6a596309f489 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 10 Dec 2017 22:24:11 +0300 Subject: eval,functests: Fix tests and complete() and setline() behaviour --- test/functional/eval/null_spec.lua | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) (limited to 'test') diff --git a/test/functional/eval/null_spec.lua b/test/functional/eval/null_spec.lua index d85f5297d2..3ab9e746a4 100644 --- a/test/functional/eval/null_spec.lua +++ b/test/functional/eval/null_spec.lua @@ -67,6 +67,9 @@ describe('NULL', function() null_expr_test('does not crash append()', 'append(1, L)', 0, 0, function() eq({''}, curbufmeths.get_lines(0, -1, false)) end) + null_expr_test('does not crash setline()', 'setline(1, L)', 0, 0, function() + eq({''}, curbufmeths.get_lines(0, -1, false)) + end) null_expr_test('is identical to itself', 'L is L', 0, 1) null_expr_test('can be sliced', 'L[:]', 0, {}) null_expr_test('can be copied', 'copy(L)', 0, {}) @@ -99,17 +102,20 @@ describe('NULL', function() null_expr_test('makes map() return v:_null_list', 'map(L, "v:val") is# L', 0, 1) null_expr_test('makes filter() return v:_null_list', 'filter(L, "1") is# L', 0, 1) null_test('is treated by :let as empty list', ':let [l] = L', 'Vim(let):E688: More targets than List items') - null_expr_test('is accepted as an empty list by inputlist()', '[feedkeys("\\n"), inputlist(L)]', 0, 0) + null_expr_test('is accepted as an empty list by inputlist()', '[feedkeys("\\n"), inputlist(L)]', + 'Type number and or click with mouse (empty cancels): ', {0, 0}) null_expr_test('is accepted as an empty list by writefile()', ('[writefile(L, "%s"), readfile("%s")]'):format(tmpfname, tmpfname), 0, {0, {}}) - -- FIXME fix test results null_expr_test('does not crash add()', 'add(L, 0)', 0, 1) - null_expr_test('makes insert() error out', 'insert(L, 1)', '', nil) - null_expr_test('does not crash remove()', 'remove(L, 0)', 0, 0) - null_expr_test('makes reverse() error out', 'reverse(L)', '', nil) - null_expr_test('is accepted by sort()', 'sort(L)', 0, 0) - null_expr_test('makes sort() return itself', 'sort(L) is L', 0, 0) + null_expr_test('makes insert() error out', 'insert(L, 1)', + 'E742: Cannot change value of insert() argument', 0) + null_expr_test('does not crash remove()', 'remove(L, 0)', + 'E742: Cannot change value of remove() argument', 0) + null_expr_test('makes reverse() error out', 'reverse(L)', + 'E742: Cannot change value of reverse() argument', 0) + null_expr_test('makes sort() error out', 'sort(L)', + 'E742: Cannot change value of sort() argument', 0) null_expr_test('does not crash extend()', 'extend(L, [1])', 'E742: Cannot change value of extend() argument', 0) null_expr_test('does not crash extend() (second position)', 'extend([1], L)', 0, {1}) null_expr_test('makes join() return empty string', 'join(L, "")', 0, '') @@ -117,9 +123,12 @@ describe('NULL', function() null_expr_test('does not crash system()', 'system("cat", L)', 0, '') null_expr_test('does not crash setreg', 'setreg("x", L)', 0, 0) null_expr_test('does not crash systemlist()', 'systemlist("cat", L)', 0, {}) - null_expr_test('does not crash setline', 'setline(1, L)', 0, 0) null_test('does not make Neovim crash when v:oldfiles gets assigned to that', ':let v:oldfiles = L|oldfiles', 0) - -- FIXME Add test for complete(, L) + null_expr_test('does not make complete() crash or error out', + 'execute(":normal i\\=complete(1, L)[-1]\\n")', + '', '\n', function() + eq({''}, curbufmeths.get_lines(0, -1, false)) + end) end) describe('dict', function() it('does not crash when indexing NULL dict', function() -- cgit From 5008205a3e1eef61396e457e5091eb1341b98278 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 10 Dec 2017 22:28:18 +0300 Subject: eval: Fix setmatches(), setqflist() and setloclist() --- test/functional/eval/null_spec.lua | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'test') diff --git a/test/functional/eval/null_spec.lua b/test/functional/eval/null_spec.lua index 3ab9e746a4..30c6d3d450 100644 --- a/test/functional/eval/null_spec.lua +++ b/test/functional/eval/null_spec.lua @@ -42,12 +42,6 @@ describe('NULL', function() describe('list', function() -- Incorrect behaviour - -- FIXME should return 0 - null_expr_test('is accepted by setqflist()', 'setqflist(L)', 0, -1) - -- FIXME should return 0 - null_expr_test('is accepted by setloclist()', 'setloclist(1, L)', 0, -1) - -- FIXME should return 0 - null_expr_test('is accepted by setmatches()', 'setmatches(L)', 0, -1) -- FIXME should not error out null_test('is accepted by :cexpr', 'cexpr L', 'Vim(cexpr):E777: String or List expected') -- FIXME should not error out @@ -129,6 +123,9 @@ describe('NULL', function() '', '\n', function() eq({''}, curbufmeths.get_lines(0, -1, false)) end) + null_expr_test('is accepted by setmatches()', 'setmatches(L)', 0, 0) + null_expr_test('is accepted by setqflist()', 'setqflist(L)', 0, 0) + null_expr_test('is accepted by setloclist()', 'setloclist(1, L)', 0, 0) end) describe('dict', function() it('does not crash when indexing NULL dict', function() -- cgit From 83f77c80c084a0390b5a6efd364b33620c9f3d10 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 10 Dec 2017 22:33:05 +0300 Subject: quickfix: Fix :cexpr and :lexpr --- test/functional/eval/null_spec.lua | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) (limited to 'test') diff --git a/test/functional/eval/null_spec.lua b/test/functional/eval/null_spec.lua index 30c6d3d450..3f3217649b 100644 --- a/test/functional/eval/null_spec.lua +++ b/test/functional/eval/null_spec.lua @@ -40,14 +40,6 @@ describe('NULL', function() end) end describe('list', function() - -- Incorrect behaviour - - -- FIXME should not error out - null_test('is accepted by :cexpr', 'cexpr L', 'Vim(cexpr):E777: String or List expected') - -- FIXME should not error out - null_test('is accepted by :lexpr', 'lexpr L', 'Vim(lexpr):E777: String or List expected') - null_test('is accepted by :for', 'for x in L|throw x|endfor', 0) - -- Subjectable behaviour -- FIXME Should return 1 @@ -58,6 +50,7 @@ describe('NULL', function() null_expr_test('is not locked', 'islocked("v:_null_list")', 0, 0) -- Correct behaviour + null_test('is accepted by :for', 'for x in L|throw x|endfor', 0) null_expr_test('does not crash append()', 'append(1, L)', 0, 0, function() eq({''}, curbufmeths.get_lines(0, -1, false)) end) @@ -126,6 +119,8 @@ describe('NULL', function() null_expr_test('is accepted by setmatches()', 'setmatches(L)', 0, 0) null_expr_test('is accepted by setqflist()', 'setqflist(L)', 0, 0) null_expr_test('is accepted by setloclist()', 'setloclist(1, L)', 0, 0) + null_test('is accepted by :cexpr', 'cexpr L', 0) + null_test('is accepted by :lexpr', 'lexpr L', 0) end) describe('dict', function() it('does not crash when indexing NULL dict', function() -- cgit From 0b03ac2cb2399a850bb71d8c0a76d0893bf503cb Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 10 Dec 2017 22:34:32 +0300 Subject: functests: Mark islocked("v:_null_list") behaviour correct It is the same for other VAR_FIXED lists. --- test/functional/eval/null_spec.lua | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'test') diff --git a/test/functional/eval/null_spec.lua b/test/functional/eval/null_spec.lua index 3f3217649b..1c1dd6fd79 100644 --- a/test/functional/eval/null_spec.lua +++ b/test/functional/eval/null_spec.lua @@ -46,10 +46,9 @@ describe('NULL', function() null_expr_test('is equal to empty list', 'L == []', 0, 0) -- FIXME Should return 1 null_expr_test('is equal to empty list (reverse order)', '[] == L', 0, 0) - -- FIXME Should return 1 - null_expr_test('is not locked', 'islocked("v:_null_list")', 0, 0) -- Correct behaviour + null_expr_test('is not locked', 'islocked("v:_null_list")', 0, 0) null_test('is accepted by :for', 'for x in L|throw x|endfor', 0) null_expr_test('does not crash append()', 'append(1, L)', 0, 0, function() eq({''}, curbufmeths.get_lines(0, -1, false)) -- cgit From d11884db497114bc8ac5e33964ed81165f8a50fe Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 10 Dec 2017 23:02:19 +0300 Subject: eval: Fix uniq() crash in legacy test 055 --- test/functional/eval/null_spec.lua | 2 ++ 1 file changed, 2 insertions(+) (limited to 'test') diff --git a/test/functional/eval/null_spec.lua b/test/functional/eval/null_spec.lua index 1c1dd6fd79..64be8fcd11 100644 --- a/test/functional/eval/null_spec.lua +++ b/test/functional/eval/null_spec.lua @@ -102,6 +102,8 @@ describe('NULL', function() 'E742: Cannot change value of reverse() argument', 0) null_expr_test('makes sort() error out', 'sort(L)', 'E742: Cannot change value of sort() argument', 0) + null_expr_test('makes uniq() error out', 'uniq(L)', + 'E742: Cannot change value of uniq() argument', 0) null_expr_test('does not crash extend()', 'extend(L, [1])', 'E742: Cannot change value of extend() argument', 0) null_expr_test('does not crash extend() (second position)', 'extend([1], L)', 0, {1}) null_expr_test('makes join() return empty string', 'join(L, "")', 0, '') -- cgit From 622d355ab46b4d9282de9db6f25701882dd8f4ab Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 10 Dec 2017 23:16:00 +0300 Subject: functests: Add some more NULL tests --- test/functional/eval/null_spec.lua | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'test') diff --git a/test/functional/eval/null_spec.lua b/test/functional/eval/null_spec.lua index 64be8fcd11..14b2d964d5 100644 --- a/test/functional/eval/null_spec.lua +++ b/test/functional/eval/null_spec.lua @@ -40,6 +40,11 @@ describe('NULL', function() end) end describe('list', function() + -- Incorrect behaviour + -- FIXME Should error out with different message + null_test('makes :unlet act as if it is not a list', ':unlet L[0]', + 'Vim(unlet):E689: Can only index a List or Dictionary') + -- Subjectable behaviour -- FIXME Should return 1 @@ -48,6 +53,9 @@ describe('NULL', function() null_expr_test('is equal to empty list (reverse order)', '[] == L', 0, 0) -- Correct behaviour + null_expr_test('can be indexed with error message for empty list', 'L[0]', + 'E684: list index out of range: 0\nE15: Invalid expression: L[0]', nil) + null_expr_test('can be splice-indexed', 'L[:]', 0, {}) null_expr_test('is not locked', 'islocked("v:_null_list")', 0, 0) null_test('is accepted by :for', 'for x in L|throw x|endfor', 0) null_expr_test('does not crash append()', 'append(1, L)', 0, 0, function() -- cgit From ceb45a08858837319c8ea67b1aaeceaeb24c8510 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 11 Dec 2017 01:43:36 +0300 Subject: *: Fix test failures --- test/functional/eval/msgpack_functions_spec.lua | 2 +- test/functional/legacy/063_match_and_matchadd_spec.lua | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) (limited to 'test') diff --git a/test/functional/eval/msgpack_functions_spec.lua b/test/functional/eval/msgpack_functions_spec.lua index b241635dfe..258d6ee059 100644 --- a/test/functional/eval/msgpack_functions_spec.lua +++ b/test/functional/eval/msgpack_functions_spec.lua @@ -628,7 +628,7 @@ describe('msgpackdump() function', function() it('fails to dump a recursive (key) map in a special dict', function() command('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": []}') command('call add(todump._VAL, [todump, 0])') - eq('Vim(call):E5005: Unable to dump msgpackdump() argument, index 0: container references itself in index 1', + eq('Vim(call):E5005: Unable to dump msgpackdump() argument, index 0: container references itself in index 0', exc_exec('call msgpackdump([todump])')) end) diff --git a/test/functional/legacy/063_match_and_matchadd_spec.lua b/test/functional/legacy/063_match_and_matchadd_spec.lua index a505a2db30..518d79861b 100644 --- a/test/functional/legacy/063_match_and_matchadd_spec.lua +++ b/test/functional/legacy/063_match_and_matchadd_spec.lua @@ -114,9 +114,11 @@ describe('063: Test for ":match", "matchadd()" and related functions', function( command("call clearmatches()") eq('\nE714: List required', redir_exec("let rf1 = setmatches(0)")) eq(-1, eval('rf1')) - eq('\nE474: Invalid argument', redir_exec("let rf2 = setmatches([0])")) + eq('\nE474: List item 0 is either not a dictionary or an empty one', + redir_exec("let rf2 = setmatches([0])")) eq(-1, eval('rf2')) - eq('\nE474: Invalid argument', redir_exec("let rf3 = setmatches([{'wrong key': 'wrong value'}])")) + eq('\nE474: List item 0 is missing one of the required keys', + redir_exec("let rf3 = setmatches([{'wrong key': 'wrong value'}])")) eq(-1, eval('rf3')) -- Check that "matchaddpos()" positions matches correctly -- cgit From 1a961b57505f57130012fe4fcfda0e8009c8da45 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 11 Dec 2017 10:34:58 +0300 Subject: eval: Fix add() --- test/functional/eval/null_spec.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'test') diff --git a/test/functional/eval/null_spec.lua b/test/functional/eval/null_spec.lua index 14b2d964d5..afe999e1fa 100644 --- a/test/functional/eval/null_spec.lua +++ b/test/functional/eval/null_spec.lua @@ -101,7 +101,8 @@ describe('NULL', function() null_expr_test('is accepted as an empty list by writefile()', ('[writefile(L, "%s"), readfile("%s")]'):format(tmpfname, tmpfname), 0, {0, {}}) - null_expr_test('does not crash add()', 'add(L, 0)', 0, 1) + null_expr_test('makes add() error out', 'add(L, 0)', + 'E742: Cannot change value of add() argument', 1) null_expr_test('makes insert() error out', 'insert(L, 1)', 'E742: Cannot change value of insert() argument', 0) null_expr_test('does not crash remove()', 'remove(L, 0)', -- cgit From 34057045beca40406673ff421a4ef1e8e8c08853 Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Tue, 12 Dec 2017 18:23:19 +0100 Subject: ui: forward relevant option updates to UIs (#7520) also make termguicolors mutable after startup --- test/functional/terminal/tui_spec.lua | 53 ++++++++++++++++++++++++++++ test/functional/ui/options_spec.lua | 66 +++++++++++++++++++++++++++++++++++ test/functional/ui/screen.lua | 5 +++ 3 files changed, 124 insertions(+) create mode 100644 test/functional/ui/options_spec.lua (limited to 'test') diff --git a/test/functional/terminal/tui_spec.lua b/test/functional/terminal/tui_spec.lua index bf3c6bdb3a..745bfeecf9 100644 --- a/test/functional/terminal/tui_spec.lua +++ b/test/functional/terminal/tui_spec.lua @@ -4,6 +4,7 @@ local global_helpers = require('test.helpers') local uname = global_helpers.uname local helpers = require('test.functional.helpers')(after_each) local thelpers = require('test.functional.terminal.helpers') +local Screen = require('test.functional.ui.screen') local eq = helpers.eq local feed_data = thelpers.feed_data local feed_command = helpers.feed_command @@ -179,6 +180,58 @@ describe('tui', function() {3:-- TERMINAL --} | ]]) end) + + it('allows termguicolors to be set at runtime', function() + screen:set_option('rgb', true) + screen:set_default_attr_ids({ + [1] = {reverse = true}, + [2] = {foreground = 13, special = Screen.colors.Grey0}, + [3] = {special = Screen.colors.Grey0, bold = true, reverse = true}, + [4] = {bold = true}, + [5] = {special = Screen.colors.Grey0, reverse = true, foreground = 4}, + [6] = {foreground = 4, special = Screen.colors.Grey0}, + [7] = {special = Screen.colors.Grey0, reverse = true, foreground = Screen.colors.SeaGreen4}, + [8] = {foreground = Screen.colors.SeaGreen4, special = Screen.colors.Grey0}, + [9] = {special = Screen.colors.Grey0, bold = true, foreground = Screen.colors.Blue1}, + }) + + feed_data(':hi SpecialKey ctermfg=3 guifg=SeaGreen\n') + feed_data('i') + feed_data('\022\007') -- ctrl+g + feed_data('\028\014') -- crtl+\ ctrl+N + feed_data(':set termguicolors?\n') + screen:expect([[ + {5:^}{6:G} | + {2:~ }| + {2:~ }| + {2:~ }| + {3:[No Name] [+] }| + notermguicolors | + {4:-- TERMINAL --} | + ]]) + + feed_data(':set termguicolors\n') + screen:expect([[ + {7:^}{8:G} | + {9:~ }| + {9:~ }| + {9:~ }| + {3:[No Name] [+] }| + | + {4:-- TERMINAL --} | + ]]) + + feed_data(':set notermguicolors\n') + screen:expect([[ + {5:^}{6:G} | + {2:~ }| + {2:~ }| + {2:~ }| + {3:[No Name] [+] }| + | + {4:-- TERMINAL --} | + ]]) + end) end) describe('tui with non-tty file descriptors', function() diff --git a/test/functional/ui/options_spec.lua b/test/functional/ui/options_spec.lua new file mode 100644 index 0000000000..14f40b3ec1 --- /dev/null +++ b/test/functional/ui/options_spec.lua @@ -0,0 +1,66 @@ +local helpers = require('test.functional.helpers')(after_each) +local Screen = require('test.functional.ui.screen') +local clear = helpers.clear +local command = helpers.command +local eq = helpers.eq + +describe('ui receives option updates', function() + local screen + + before_each(function() + clear() + screen = Screen.new(20,5) + screen:attach() + end) + + after_each(function() + screen:detach() + end) + + local defaults = { + ambiwidth='single', + arabicshape=true, + emoji=true, + guifont='', + guifontset='', + guifontwide='', + showtabline=1, + termguicolors=false, + } + + it("for defaults", function() + screen:expect(function() + eq(defaults, screen.options) + end) + end) + + it("when setting options", function() + local changed = {} + for k,v in pairs(defaults) do + changed[k] = v + end + + command("set termguicolors") + changed.termguicolors = true + screen:expect(function() + eq(changed, screen.options) + end) + + command("set guifont=Comic\\ Sans") + changed.guifont = "Comic Sans" + screen:expect(function() + eq(changed, screen.options) + end) + + command("set showtabline=0") + changed.showtabline = 0 + screen:expect(function() + eq(changed, screen.options) + end) + + command("set all&") + screen:expect(function() + eq(defaults, screen.options) + end) + end) +end) diff --git a/test/functional/ui/screen.lua b/test/functional/ui/screen.lua index 075d8c40d7..696feabeed 100644 --- a/test/functional/ui/screen.lua +++ b/test/functional/ui/screen.lua @@ -137,6 +137,7 @@ function Screen.new(width, height) visual_bell = false, suspended = false, mode = 'normal', + options = {}, _default_attr_ids = nil, _default_attr_ignore = nil, _mouse_enabled = true, @@ -482,6 +483,10 @@ function Screen:_handle_set_icon(icon) self.icon = icon end +function Screen:_handle_option_set(name, value) + self.options[name] = value +end + function Screen:_clear_block(top, bot, left, right) for i = top, bot do self:_clear_row_section(i, left, right) -- cgit From c8a5d6181b19009e170a3497a30ce35cf288bddf Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 15 Dec 2017 02:39:46 +0300 Subject: *: Fix some problems found during review Still missing: problems in window.c, it should be possible to construct a test for them. --- test/functional/eval/interrupt_spec.lua | 61 +++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 test/functional/eval/interrupt_spec.lua (limited to 'test') diff --git a/test/functional/eval/interrupt_spec.lua b/test/functional/eval/interrupt_spec.lua new file mode 100644 index 0000000000..7f4ca95317 --- /dev/null +++ b/test/functional/eval/interrupt_spec.lua @@ -0,0 +1,61 @@ +local helpers = require('test.functional.helpers')(after_each) + +local command = helpers.command +local meths = helpers.meths +local clear = helpers.clear +local sleep = helpers.sleep +local wait = helpers.wait +local feed = helpers.feed +local eq = helpers.eq + +local dur +local min_dur = 8 +local len = 131072 + +describe('List support code', function() + if not pending('does not actually allows interrupting with just got_int', function() end) then return end + -- The following tests are confirmed to work with os_breakcheck() just before + -- `if (got_int) {break;}` in tv_list_copy and list_join_inner() and not to + -- work without. + setup(function() + clear() + dur = 0 + while true do + command(([[ + let rt = reltime() + let bl = range(%u) + let dur = reltimestr(reltime(rt)) + ]]):format(len)) + dur = tonumber(meths.get_var('dur')) + if dur >= min_dur then + -- print(('Using len %u, dur %g'):format(len, dur)) + break + else + len = len * 2 + end + end + end) + it('allows interrupting copy', function() + feed(':let t_rt = reltime():let t_bl = copy(bl)') + sleep(min_dur / 16 * 1000) + feed('') + wait() + command('let t_dur = reltimestr(reltime(t_rt))') + local t_dur = tonumber(meths.get_var('t_dur')) + if t_dur >= dur / 8 then + eq(nil, ('Took too long to cancel: %g >= %g'):format(t_dur, dur / 8)) + end + end) + it('allows interrupting join', function() + feed(':let t_rt = reltime():let t_j = join(bl)') + sleep(min_dur / 16 * 1000) + feed('') + wait() + command('let t_dur = reltimestr(reltime(t_rt))') + local t_dur = tonumber(meths.get_var('t_dur')) + print(('t_dur: %g'):format(t_dur)) + if t_dur >= dur / 8 then + eq(nil, ('Took too long to cancel: %g >= %g'):format(t_dur, dur / 8)) + end + end) +end) -- cgit From fb07391ce46b8bff90ef7ef073b75919a307e64c Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 15 Dec 2017 11:38:34 +0300 Subject: window: Fix matchaddpos() and enhance error reporting --- test/functional/eval/match_functions_spec.lua | 95 +++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) (limited to 'test') diff --git a/test/functional/eval/match_functions_spec.lua b/test/functional/eval/match_functions_spec.lua index 3150d89f62..0dc78de55e 100644 --- a/test/functional/eval/match_functions_spec.lua +++ b/test/functional/eval/match_functions_spec.lua @@ -1,9 +1,12 @@ local helpers = require('test.functional.helpers')(after_each) +local Screen = require('test.functional.ui.screen') local eq = helpers.eq local clear = helpers.clear local funcs = helpers.funcs +local meths = helpers.meths local command = helpers.command +local exc_exec = helpers.exc_exec before_each(clear) @@ -59,3 +62,95 @@ describe('matchadd()', function() }}, funcs.getmatches()) end) end) + +describe('matchaddpos()', function() + it('errors out on invalid input', function() + command('hi clear PreProc') + eq('Vim(let):E5030: Empty list at position 0', + exc_exec('let val = matchaddpos("PreProc", [[]])')) + eq('Vim(let):E5030: Empty list at position 1', + exc_exec('let val = matchaddpos("PreProc", [1, v:_null_list])')) + eq('Vim(let):E5031: List or number required at position 1', + exc_exec('let val = matchaddpos("PreProc", [1, v:_null_dict])')) + end) + it('works with 0 lnum', function() + command('hi clear PreProc') + eq(4, funcs.matchaddpos('PreProc', {1}, 3, 4)) + eq({{ + group='PreProc', + pos1 = {1}, + priority=3, + id=4, + }}, funcs.getmatches()) + funcs.matchdelete(4) + eq(4, funcs.matchaddpos('PreProc', {{0}, 1}, 3, 4)) + eq({{ + group='PreProc', + pos1 = {1}, + priority=3, + id=4, + }}, funcs.getmatches()) + funcs.matchdelete(4) + eq(4, funcs.matchaddpos('PreProc', {0, 1}, 3, 4)) + eq({{ + group='PreProc', + pos1 = {1}, + priority=3, + id=4, + }}, funcs.getmatches()) + end) + it('works with negative numbers', function() + command('hi clear PreProc') + eq(4, funcs.matchaddpos('PreProc', {-10, 1}, 3, 4)) + eq({{ + group='PreProc', + pos1 = {1}, + priority=3, + id=4, + }}, funcs.getmatches()) + funcs.matchdelete(4) + eq(4, funcs.matchaddpos('PreProc', {{-10}, 1}, 3, 4)) + eq({{ + group='PreProc', + pos1 = {1}, + priority=3, + id=4, + }}, funcs.getmatches()) + funcs.matchdelete(4) + eq(4, funcs.matchaddpos('PreProc', {{2, -1}, 1}, 3, 4)) + eq({{ + group='PreProc', + pos1 = {1}, + priority=3, + id=4, + }}, funcs.getmatches()) + funcs.matchdelete(4) + eq(4, funcs.matchaddpos('PreProc', {{2, 0, -1}, 1}, 3, 4)) + eq({{ + group='PreProc', + pos1 = {1}, + priority=3, + id=4, + }}, funcs.getmatches()) + end) + it('works with zero length', function() + local screen = Screen.new(40, 5) + screen:attach() + funcs.setline(1, 'abcdef') + command('hi PreProc guifg=Red') + eq(4, funcs.matchaddpos('PreProc', {{1, 2, 0}}, 3, 4)) + eq({{ + group='PreProc', + pos1 = {1, 2, 0}, + priority=3, + id=4, + }}, funcs.getmatches()) + screen:expect([[ + ^a{1:b}cdef | + {2:~ }| + {2:~ }| + {2:~ }| + | + ]], {[1] = {foreground = Screen.colors.Red}, [2] = {bold = true, foreground = Screen.colors.Blue1}}) + end) +end) -- cgit From 023631463cfe776f71492b8d853c473d0c547647 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 16 Dec 2017 16:14:53 +0300 Subject: functests: Fix linter error --- test/functional/eval/match_functions_spec.lua | 1 - 1 file changed, 1 deletion(-) (limited to 'test') diff --git a/test/functional/eval/match_functions_spec.lua b/test/functional/eval/match_functions_spec.lua index 0dc78de55e..7989b22b5e 100644 --- a/test/functional/eval/match_functions_spec.lua +++ b/test/functional/eval/match_functions_spec.lua @@ -4,7 +4,6 @@ local Screen = require('test.functional.ui.screen') local eq = helpers.eq local clear = helpers.clear local funcs = helpers.funcs -local meths = helpers.meths local command = helpers.command local exc_exec = helpers.exc_exec -- cgit From a1adfdc7d59979824addce2f519a527f9a5c0290 Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Wed, 29 Nov 2017 01:50:11 -0500 Subject: ci: nodejs client acceptance-test #7706 ci: install nodejs 8 in Appveyor, Travis provider: check node version for debug support Resolve https://github.com/neovim/neovim/pull/7577#issuecomment-350590592 for Unix. provider: test if nodejs in ci supports --inspect-brk nodejs host for neovim requires nodejs 6+ to work properly. nodejs 6.12+ or 7.6+ is required for debug support via `node --inspect-brk`. provider: run cli.js of nodejs host directly npm shims are useless because the user cannot set node to debug mode via --inspect-brk. This is problematic on Windows which use batchfiles and shell scripts to compensate for not supporting shebang. The patch uses `npm root -g` to get the absolute path of the global npm modules. If that fails, then the user did not install neovim npm package globally. Use that absolute path to find `neovim/bin/cli.js`, which is what the npm shim actually runs with node. glob() is for a simple file check in case bin/ is removed because the npm shims are ignored now. --- test/functional/helpers.lua | 2 +- test/functional/provider/nodejs_spec.lua | 70 ++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 test/functional/provider/nodejs_spec.lua (limited to 'test') diff --git a/test/functional/helpers.lua b/test/functional/helpers.lua index f939567693..bff8d065f8 100644 --- a/test/functional/helpers.lua +++ b/test/functional/helpers.lua @@ -614,7 +614,7 @@ local function new_pipename() end local function missing_provider(provider) - if provider == 'ruby' then + if provider == 'ruby' or provider == 'node' then local prog = funcs['provider#' .. provider .. '#Detect']() return prog == '' and (provider .. ' not detected') or false elseif provider == 'python' or provider == 'python3' then diff --git a/test/functional/provider/nodejs_spec.lua b/test/functional/provider/nodejs_spec.lua new file mode 100644 index 0000000000..9423243607 --- /dev/null +++ b/test/functional/provider/nodejs_spec.lua @@ -0,0 +1,70 @@ +local helpers = require('test.functional.helpers')(after_each) +local eq, clear = helpers.eq, helpers.clear +local missing_provider = helpers.missing_provider +local command = helpers.command +local write_file = helpers.write_file +local eval = helpers.eval +local sleep = helpers.sleep +local funcs = helpers.funcs +local retry = helpers.retry + +do + clear() + if missing_provider('node') then + pending( + "Cannot find the neovim nodejs host. Try :checkhealth", + function() end) + return + end +end + +before_each(function() + clear() +end) + +describe('nodejs', function() + it('can inspect', function() + eq(1, funcs['provider#node#can_inspect']()) + end) +end) + +describe('nodejs host', function() + teardown(function () + os.remove('Xtest-nodejs-hello.js') + os.remove('Xtest-nodejs-hello-plugin.js') + end) + + it('works', function() + local fname = 'Xtest-nodejs-hello.js' + write_file(fname, [[ + const socket = process.env.NVIM_LISTEN_ADDRESS; + const neovim = require('neovim'); + const nvim = neovim.attach({socket: socket}); + nvim.command('let g:job_out = "hello"'); + nvim.command('call jobstop(g:job_id)'); + ]]) + command('let g:job_id = jobstart(["node", "'..fname..'"])') + retry(nil, 1000, function() eq('hello', eval('g:job_out')) end) + end) + it('plugin works', function() + local fname = 'Xtest-nodejs-hello-plugin.js' + write_file(fname, [[ + const socket = process.env.NVIM_LISTEN_ADDRESS; + const neovim = require('neovim'); + const nvim = neovim.attach({socket: socket}); + + class TestPlugin { + hello() { + this.nvim.command('let g:job_out = "hello-plugin"') + } + } + + const PluginClass = neovim.Plugin(TestPlugin); + const plugin = new PluginClass(nvim); + plugin.hello(); + nvim.command('call jobstop(g:job_id)'); + ]]) + command('let g:job_id = jobstart(["node", "'..fname..'"])') + retry(nil, 1000, function() eq('hello-plugin', eval('g:job_out')) end) + end) +end) -- cgit From 5b692124cc94c8e5edc0c767e6a71887754643cd Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Sat, 16 Dec 2017 16:29:33 -0500 Subject: test: remove inspect test; set NODE_PATH in nodejs_spec.lua provider#node#can_inspect will fail on some systems because it is common to have old node versions in OS (any Linux OS that has LTS releases) and CI (Travis, Appveyor). NODE_PATH can be trivially set with VimL. Build scripts don't have to set it for the nodejs tests to work. NODE_PATH is optional to begin with and is used only as a workaround for the neovim node.js host. --- test/functional/provider/nodejs_spec.lua | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) (limited to 'test') diff --git a/test/functional/provider/nodejs_spec.lua b/test/functional/provider/nodejs_spec.lua index 9423243607..ef563dd0b0 100644 --- a/test/functional/provider/nodejs_spec.lua +++ b/test/functional/provider/nodejs_spec.lua @@ -4,8 +4,6 @@ local missing_provider = helpers.missing_provider local command = helpers.command local write_file = helpers.write_file local eval = helpers.eval -local sleep = helpers.sleep -local funcs = helpers.funcs local retry = helpers.retry do @@ -20,12 +18,7 @@ end before_each(function() clear() -end) - -describe('nodejs', function() - it('can inspect', function() - eq(1, funcs['provider#node#can_inspect']()) - end) + command([[let $NODE_PATH = get(split(system('npm root -g'), "\n"), 0, '')]]) end) describe('nodejs host', function() -- cgit From 103ff26c0ae76491c73a6c078b52f2e56af16fb8 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sun, 17 Dec 2017 15:53:11 +0100 Subject: provider/nodejs: check version in Detect() --- test/functional/provider/nodejs_spec.lua | 4 +--- test/functional/provider/ruby_spec.lua | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) (limited to 'test') diff --git a/test/functional/provider/nodejs_spec.lua b/test/functional/provider/nodejs_spec.lua index ef563dd0b0..0a12b1a154 100644 --- a/test/functional/provider/nodejs_spec.lua +++ b/test/functional/provider/nodejs_spec.lua @@ -9,9 +9,7 @@ local retry = helpers.retry do clear() if missing_provider('node') then - pending( - "Cannot find the neovim nodejs host. Try :checkhealth", - function() end) + pending("Missing nodejs host, or nodejs version is too old.", function()end) return end end diff --git a/test/functional/provider/ruby_spec.lua b/test/functional/provider/ruby_spec.lua index c70f90da1c..a2c6c6a10e 100644 --- a/test/functional/provider/ruby_spec.lua +++ b/test/functional/provider/ruby_spec.lua @@ -15,9 +15,7 @@ local missing_provider = helpers.missing_provider do clear() if missing_provider('ruby') then - pending( - "Cannot find the neovim RubyGem. Try :checkhealth", - function() end) + pending("Missing neovim RubyGem.", function() end) return end end -- cgit From 308dd53783314103710a37c9809eddb78279eab4 Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Mon, 27 Nov 2017 13:01:49 +0100 Subject: channel: check for existance before trying to set key This avoids an error message in async context, where it is not safe. --- test/functional/core/channels_spec.lua | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) (limited to 'test') diff --git a/test/functional/core/channels_spec.lua b/test/functional/core/channels_spec.lua index 765e3c5919..e9fc88c01b 100644 --- a/test/functional/core/channels_spec.lua +++ b/test/functional/core/channels_spec.lua @@ -246,6 +246,22 @@ describe('channels', function() eq({"notification", "exit", {id, 0, {'10 PRINT "NVIM"', '20 GOTO 10', ''}}}, next_msg()) + -- if dict is reused the new value is not stored, + -- but nvim also does not crash + command("let id = jobstart(['cat'], g:job_opts)") + id = eval("g:id") + + command([[call chansend(id, "cat text\n")]]) + sleep(10) + command("call chanclose(id, 'stdin')") + + -- old value was not overwritten + eq({"notification", "exit", {id, 0, {'10 PRINT "NVIM"', + '20 GOTO 10', ''}}}, next_msg()) + + -- and an error was thrown. + eq("E5210: dict key 'stdout' already set for buffered stream in channel "..id, eval('v:errmsg')) + -- reset dictionary source([[ let g:job_opts = { @@ -261,6 +277,5 @@ describe('channels', function() -- works correctly with no output eq({"notification", "exit", {id, 1, {''}}}, next_msg()) - end) end) -- cgit From 6b45dbca0429c52cb70908497bc7a0fed9db2d8d Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 24 Dec 2017 00:29:38 +0300 Subject: mark: Make sure that jumplist item will not have zero lnum Fixes #7169 --- test/functional/shada/helpers.lua | 26 ++++++++++++++++++-------- test/functional/shada/marks_spec.lua | 23 +++++++++++++++++++++++ 2 files changed, 41 insertions(+), 8 deletions(-) (limited to 'test') diff --git a/test/functional/shada/helpers.lua b/test/functional/shada/helpers.lua index 8e2c0cc1f6..b77e59682f 100644 --- a/test/functional/shada/helpers.lua +++ b/test/functional/shada/helpers.lua @@ -6,15 +6,14 @@ local write_file, merge_args = helpers.write_file, helpers.merge_args local mpack = require('mpack') local tmpname = helpers.tmpname() -local additional_cmd = '' +local append_argv = nil -local function nvim_argv(shada_file) +local function nvim_argv(shada_file, embed) local argv = {nvim_prog, '-u', 'NONE', '-i', shada_file or tmpname, '-N', '--cmd', 'set shortmess+=I background=light noswapfile', - '--cmd', additional_cmd, - '--embed'} - if helpers.prepend_argv then - return merge_args(helpers.prepend_argv, argv) + embed or '--embed'} + if helpers.prepend_argv or append_argv then + return merge_args(helpers.prepend_argv, argv, append_argv) else return argv end @@ -26,12 +25,21 @@ local reset = function(shada_file) end local set_additional_cmd = function(s) - additional_cmd = s + append_argv = {'--cmd', s} +end + +local function add_argv(...) + if select('#', ...) == 0 then + append_argv = nil + else + append_argv = {...} + end end local clear = function() + os.execute('cp ' .. tmpname .. ' /tmp/test.shada') os.remove(tmpname) - set_additional_cmd('') + append_argv = nil end local get_shada_rw = function(fname) @@ -80,7 +88,9 @@ end return { reset=reset, set_additional_cmd=set_additional_cmd, + add_argv=add_argv, clear=clear, get_shada_rw=get_shada_rw, read_shada_file=read_shada_file, + nvim_argv=nvim_argv, } diff --git a/test/functional/shada/marks_spec.lua b/test/functional/shada/marks_spec.lua index fa760ceb5b..4cceae1aa3 100644 --- a/test/functional/shada/marks_spec.lua +++ b/test/functional/shada/marks_spec.lua @@ -9,6 +9,8 @@ local shada_helpers = require('test.functional.shada.helpers') local reset, set_additional_cmd, clear = shada_helpers.reset, shada_helpers.set_additional_cmd, shada_helpers.clear +local add_argv = shada_helpers.add_argv +local nvim_argv = shada_helpers.nvim_argv local nvim_current_line = function() return curwinmeths.get_cursor()[1] @@ -17,8 +19,10 @@ end describe('ShaDa support code', function() local testfilename = 'Xtestfile-functional-shada-marks' local testfilename_2 = 'Xtestfile-functional-shada-marks-2' + local non_existent_testfilename = testfilename .. '.nonexistent' before_each(function() reset() + os.remove(non_existent_testfilename) local fd = io.open(testfilename, 'w') fd:write('test\n') fd:write('test2\n') @@ -214,4 +218,23 @@ describe('ShaDa support code', function() nvim_command('" sync 2') eq(2, nvim_current_line()) end) + + -- -c temporary sets lnum to zero to make `+/pat` work, so calling setpcmark() + -- during -c used to add item with zero lnum to jump list. + it('does not create incorrect file for non-existent buffers when writing from -c', + function() + add_argv('--cmd', 'silent edit ' .. non_existent_testfilename, '-c', 'qall') + local argv = nvim_argv(nil, '--headless') + eq('', funcs.system(argv)) + eq(0, exc_exec('rshada')) + end) + + it('does not create incorrect file for non-existent buffers opened from -c', + function() + add_argv('-c', 'silent edit ' .. non_existent_testfilename, + '-c', 'autocmd VimEnter * qall') + local argv = nvim_argv(nil, '--headless') + eq('', funcs.system(argv)) + eq(0, exc_exec('rshada')) + end) end) -- cgit From 0c533a488fbe453ae39017a586eff7813da8ed8a Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 24 Dec 2017 01:43:42 +0300 Subject: *: Remove most calls to tv_list_item_alloc Still left calls in eval/typval.c and test/unit/eval/helpers.lua. Latter is the only reason why function did not receive `static` modifier. --- test/unit/eval/typval_spec.lua | 60 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) (limited to 'test') diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index bec74f05fc..2ae69ec04b 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -678,6 +678,66 @@ describe('typval.c', function() eq({int(-100500), int(100500)}, typvalt2lua(l_tv)) end) end) + describe('tv()', function() + itp('works', function() + local l_tv = lua2typvalt(empty_list) + local l = l_tv.vval.v_list + + local l_l_tv = lua2typvalt(empty_list) + alloc_log:clear() + local l_l = l_l_tv.vval.v_list + eq(1, l_l.lv_refcount) + lib.tv_list_append_tv(l, l_l_tv) + eq(2, l_l.lv_refcount) + eq(l_l, l.lv_first.li_tv.vval.v_list) + alloc_log:check({ + a.li(l.lv_first), + }) + + local l_s_tv = lua2typvalt('test') + alloc_log:check({ + a.str(l_s_tv.vval.v_string, 'test'), + }) + lib.tv_list_append_tv(l, l_s_tv) + alloc_log:check({ + a.li(l.lv_last), + a.str(l.lv_last.li_tv.vval.v_string, 'test'), + }) + + eq({empty_list, 'test'}, typvalt2lua(l_tv)) + end) + end) + describe('owned tv()', function() + itp('works', function() + local l_tv = lua2typvalt(empty_list) + local l = l_tv.vval.v_list + + local l_l_tv = lua2typvalt(empty_list) + alloc_log:clear() + local l_l = l_l_tv.vval.v_list + eq(1, l_l.lv_refcount) + lib.tv_list_append_owned_tv(l, l_l_tv) + eq(1, l_l.lv_refcount) + l_l.lv_refcount = l_l.lv_refcount + 1 + eq(l_l, l.lv_first.li_tv.vval.v_list) + alloc_log:check({ + a.li(l.lv_first), + }) + + local l_s_tv = ffi.gc(lua2typvalt('test'), nil) + alloc_log:check({ + a.str(l_s_tv.vval.v_string, 'test'), + }) + lib.tv_list_append_owned_tv(l, l_s_tv) + eq(l_s_tv.vval.v_string, l.lv_last.li_tv.vval.v_string) + l_s_tv.vval.v_string = nil + alloc_log:check({ + a.li(l.lv_last), + }) + + eq({empty_list, 'test'}, typvalt2lua(l_tv)) + end) + end) end) describe('copy()', function() local function tv_list_copy(...) -- cgit From 6bf3dc77c484f757d37c22694e1278abc014278f Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 24 Dec 2017 01:52:11 +0300 Subject: eval/typval: Make tv_list_item_alloc static Better write this bit in lua then make reviewers or clint filter out tv_list_item_alloc(). --- test/unit/eval/helpers.lua | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'test') diff --git a/test/unit/eval/helpers.lua b/test/unit/eval/helpers.lua index d7399182f7..1a3210e1fd 100644 --- a/test/unit/eval/helpers.lua +++ b/test/unit/eval/helpers.lua @@ -7,7 +7,7 @@ local ffi = helpers.ffi local eq = helpers.eq local eval = cimport('./src/nvim/eval.h', './src/nvim/eval/typval.h', - './src/nvim/hashtab.h') + './src/nvim/hashtab.h', './src/nvim/memory.h') local null_string = {[true]='NULL string'} local null_list = {[true]='NULL list'} @@ -24,10 +24,14 @@ local nil_value = {[true]='nil'} local lua2typvalt +local function tv_list_item_alloc() + return ffi.cast('listitem_T*', eval.xmalloc(ffi.sizeof('listitem_T'))) +end + local function li_alloc(nogc) local gcfunc = eval.tv_list_item_free if nogc then gcfunc = nil end - local li = ffi.gc(eval.tv_list_item_alloc(), gcfunc) + local li = ffi.gc(tv_list_item_alloc(), gcfunc) li.li_next = nil li.li_prev = nil li.li_tv = {v_type=eval.VAR_UNKNOWN, v_lock=eval.VAR_UNLOCKED} @@ -41,7 +45,7 @@ local function populate_list(l, lua_l, processed) processed[lua_l] = l for i = 1, #lua_l do local item_tv = ffi.gc(lua2typvalt(lua_l[i], processed), nil) - local item_li = eval.tv_list_item_alloc() + local item_li = tv_list_item_alloc() item_li.li_tv = item_tv eval.tv_list_append(l, item_li) end -- cgit From 608c3d7baf2745b188bc42f9f45ad1bb188a4828 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 24 Dec 2017 02:11:46 +0300 Subject: eval/typval: Remove tv_list_item_free() as it is unused --- test/unit/eval/helpers.lua | 8 ++- test/unit/eval/typval_spec.lua | 115 +++++++++++++---------------------------- 2 files changed, 44 insertions(+), 79 deletions(-) (limited to 'test') diff --git a/test/unit/eval/helpers.lua b/test/unit/eval/helpers.lua index 1a3210e1fd..6babd4be77 100644 --- a/test/unit/eval/helpers.lua +++ b/test/unit/eval/helpers.lua @@ -28,8 +28,13 @@ local function tv_list_item_alloc() return ffi.cast('listitem_T*', eval.xmalloc(ffi.sizeof('listitem_T'))) end +local function tv_list_item_free(li) + eval.tv_clear(li.li_tv) + eval.xfree(li) +end + local function li_alloc(nogc) - local gcfunc = eval.tv_list_item_free + local gcfunc = tv_list_item_free if nogc then gcfunc = nil end local li = ffi.gc(tv_list_item_alloc(), gcfunc) li.li_next = nil @@ -537,6 +542,7 @@ return { typvalt=typvalt, li_alloc=li_alloc, + tv_list_item_free=tv_list_item_free, dict_iter=dict_iter, list_iter=list_iter, diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index 2ae69ec04b..0536839b71 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -41,6 +41,7 @@ local tbl2callback = eval_helpers.tbl2callback local dict_watchers = eval_helpers.dict_watchers local concat_tables = global_helpers.concat_tables +local map = global_helpers.map local lib = cimport('./src/nvim/eval/typval.h', './src/nvim/memory.h', './src/nvim/mbyte.h', './src/nvim/garray.h', @@ -121,118 +122,76 @@ end describe('typval.c', function() describe('list', function() describe('item', function() - describe('alloc()/free()', function() + describe('remove()', function() itp('works', function() - local li = li_alloc(true) - neq(nil, li) - lib.tv_list_item_free(li) - alloc_log:check({ - a.li(li), - a.freed(li), - }) - end) - itp('also frees the value', function() - local li - local s - local l - local tv - li = li_alloc(true) - li.li_tv.v_type = lib.VAR_NUMBER - li.li_tv.vval.v_number = 10 - lib.tv_list_item_free(li) - alloc_log:check({ - a.li(li), - a.freed(li), - }) - - li = li_alloc(true) - li.li_tv.v_type = lib.VAR_FLOAT - li.li_tv.vval.v_float = 10.5 - lib.tv_list_item_free(li) - alloc_log:check({ - a.li(li), - a.freed(li), - }) - - li = li_alloc(true) - li.li_tv.v_type = lib.VAR_STRING - li.li_tv.vval.v_string = nil - lib.tv_list_item_free(li) + local l = list(1, 2, 3, 4, 5, 6, 7) + neq(nil, l) + local lis = list_items(l) alloc_log:check({ - a.li(li), - a.freed(alloc_log.null), - a.freed(li), + a.list(l), + a.li(lis[1]), + a.li(lis[2]), + a.li(lis[3]), + a.li(lis[4]), + a.li(lis[5]), + a.li(lis[6]), + a.li(lis[7]), }) - li = li_alloc(true) - li.li_tv.v_type = lib.VAR_STRING - s = to_cstr_nofree('test') - li.li_tv.vval.v_string = s - lib.tv_list_item_free(li) + lib.tv_list_item_remove(l, lis[1]) alloc_log:check({ - a.li(li), - a.str(s, #('test')), - a.freed(s), - a.freed(li), + a.freed(table.remove(lis, 1)), }) + eq(lis, list_items(l)) - li = li_alloc(true) - li.li_tv.v_type = lib.VAR_LIST - l = ffi.gc(list(), nil) - l.lv_refcount = 2 - li.li_tv.vval.v_list = l - lib.tv_list_item_free(li) + lib.tv_list_item_remove(l, lis[6]) alloc_log:check({ - a.li(li), - a.list(l), - a.freed(li), + a.freed(table.remove(lis)), }) - eq(1, l.lv_refcount) + eq(lis, list_items(l)) - li = li_alloc(true) - tv = lua2typvalt({}) - tv.vval.v_dict.dv_refcount = 2 - li.li_tv = tv - lib.tv_list_item_free(li) + lib.tv_list_item_remove(l, lis[3]) alloc_log:check({ - a.li(li), - a.dict(tv.vval.v_dict), - a.freed(li), + a.freed(table.remove(lis, 3)), }) - eq(1, tv.vval.v_dict.dv_refcount) + eq(lis, list_items(l)) end) - end) - describe('remove()', function() - itp('works', function() - local l = list(1, 2, 3, 4, 5, 6, 7) + itp('also frees the value', function() + local l = list('a', 'b', 'c', 'd') neq(nil, l) local lis = list_items(l) alloc_log:check({ a.list(l), + a.str(lis[1].li_tv.vval.v_string, 1), a.li(lis[1]), + a.str(lis[2].li_tv.vval.v_string, 1), a.li(lis[2]), + a.str(lis[3].li_tv.vval.v_string, 1), a.li(lis[3]), + a.str(lis[4].li_tv.vval.v_string, 1), a.li(lis[4]), - a.li(lis[5]), - a.li(lis[6]), - a.li(lis[7]), }) + local strings = map(function(li) return li.li_tv.vval.v_string end, + lis) lib.tv_list_item_remove(l, lis[1]) alloc_log:check({ + a.freed(table.remove(strings, 1)), a.freed(table.remove(lis, 1)), }) eq(lis, list_items(l)) - lib.tv_list_item_remove(l, lis[6]) + lib.tv_list_item_remove(l, lis[2]) alloc_log:check({ - a.freed(table.remove(lis)), + a.freed(table.remove(strings, 2)), + a.freed(table.remove(lis, 2)), }) eq(lis, list_items(l)) - lib.tv_list_item_remove(l, lis[3]) + lib.tv_list_item_remove(l, lis[2]) alloc_log:check({ - a.freed(table.remove(lis, 3)), + a.freed(table.remove(strings, 2)), + a.freed(table.remove(lis, 2)), }) eq(lis, list_items(l)) end) -- cgit From ac55558c97d02f18b9a99cf2dd279451481c4a2f Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 24 Dec 2017 02:41:34 +0300 Subject: eval/typval: Make tv_list_item_remove return pointer to the next item --- test/unit/eval/typval_spec.lua | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'test') diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index 0536839b71..35b5596c63 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -138,19 +138,19 @@ describe('typval.c', function() a.li(lis[7]), }) - lib.tv_list_item_remove(l, lis[1]) + eq(lis[2], lib.tv_list_item_remove(l, lis[1])) alloc_log:check({ a.freed(table.remove(lis, 1)), }) eq(lis, list_items(l)) - lib.tv_list_item_remove(l, lis[6]) + eq(lis[7], lib.tv_list_item_remove(l, lis[6])) alloc_log:check({ a.freed(table.remove(lis)), }) eq(lis, list_items(l)) - lib.tv_list_item_remove(l, lis[3]) + eq(lis[4], lib.tv_list_item_remove(l, lis[3])) alloc_log:check({ a.freed(table.remove(lis, 3)), }) @@ -174,21 +174,21 @@ describe('typval.c', function() local strings = map(function(li) return li.li_tv.vval.v_string end, lis) - lib.tv_list_item_remove(l, lis[1]) + eq(lis[2], lib.tv_list_item_remove(l, lis[1])) alloc_log:check({ a.freed(table.remove(strings, 1)), a.freed(table.remove(lis, 1)), }) eq(lis, list_items(l)) - lib.tv_list_item_remove(l, lis[2]) + eq(lis[3], lib.tv_list_item_remove(l, lis[2])) alloc_log:check({ a.freed(table.remove(strings, 2)), a.freed(table.remove(lis, 2)), }) eq(lis, list_items(l)) - lib.tv_list_item_remove(l, lis[2]) + eq(nil, lib.tv_list_item_remove(l, lis[2])) alloc_log:check({ a.freed(table.remove(strings, 2)), a.freed(table.remove(lis, 2)), @@ -216,19 +216,19 @@ describe('typval.c', function() a.li(lis[7]), }) - lib.tv_list_item_remove(l, lis[4]) + eq(lis[5], lib.tv_list_item_remove(l, lis[4])) alloc_log:check({a.freed(lis[4])}) eq({lis[1], lis[5], lis[7]}, {lws[1].lw_item, lws[2].lw_item, lws[3].lw_item}) - lib.tv_list_item_remove(l, lis[2]) + eq(lis[3], lib.tv_list_item_remove(l, lis[2])) alloc_log:check({a.freed(lis[2])}) eq({lis[1], lis[5], lis[7]}, {lws[1].lw_item, lws[2].lw_item, lws[3].lw_item}) - lib.tv_list_item_remove(l, lis[7]) + eq(nil, lib.tv_list_item_remove(l, lis[7])) alloc_log:check({a.freed(lis[7])}) eq({lis[1], lis[5], nil}, {lws[1].lw_item, lws[2].lw_item, lws[3].lw_item == nil and nil}) - lib.tv_list_item_remove(l, lis[1]) + eq(lis[3], lib.tv_list_item_remove(l, lis[1])) alloc_log:check({a.freed(lis[1])}) eq({lis[3], lis[5], nil}, {lws[1].lw_item, lws[2].lw_item, lws[3].lw_item == nil and nil}) -- cgit From 67fa9e5237526b092c1ac672504e259db7f14126 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 24 Dec 2017 16:38:30 +0300 Subject: eval: Rename tv_list_remove_items() to tv_list_drop_items() tv_list_remove_items() may cause confusion with tv_list_item_remove() --- test/unit/eval/typval_spec.lua | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'test') diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index 35b5596c63..0b6c4e04cc 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -408,7 +408,7 @@ describe('typval.c', function() }) end) end) - describe('remove_items()', function() + describe('drop_items()', function() itp('works', function() local l_tv = lua2typvalt({1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}) local l = l_tv.vval.v_list @@ -421,19 +421,19 @@ describe('typval.c', function() } alloc_log:clear() - lib.tv_list_remove_items(l, lis[1], lis[3]) + lib.tv_list_drop_items(l, lis[1], lis[3]) eq({4, 5, 6, 7, 8, 9, 10, 11, 12, 13}, typvalt2lua(l_tv)) eq({lis[4], lis[7], lis[13]}, {lws[1].lw_item, lws[2].lw_item, lws[3].lw_item}) - lib.tv_list_remove_items(l, lis[11], lis[13]) + lib.tv_list_drop_items(l, lis[11], lis[13]) eq({4, 5, 6, 7, 8, 9, 10}, typvalt2lua(l_tv)) eq({lis[4], lis[7], nil}, {lws[1].lw_item, lws[2].lw_item, lws[3].lw_item == nil and nil}) - lib.tv_list_remove_items(l, lis[6], lis[8]) + lib.tv_list_drop_items(l, lis[6], lis[8]) eq({4, 5, 9, 10}, typvalt2lua(l_tv)) eq({lis[4], lis[9], nil}, {lws[1].lw_item, lws[2].lw_item, lws[3].lw_item == nil and nil}) - lib.tv_list_remove_items(l, lis[4], lis[10]) + lib.tv_list_drop_items(l, lis[4], lis[10]) eq(empty_list, typvalt2lua(l_tv)) eq({true, true, true}, {lws[1].lw_item == nil, lws[2].lw_item == nil, lws[3].lw_item == nil}) -- cgit From 32689aa5bed472ca981f323439b98e0911b403b9 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 24 Dec 2017 17:13:42 +0300 Subject: unittests: Remove start of trace, not end --- test/unit/helpers.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'test') diff --git a/test/unit/helpers.lua b/test/unit/helpers.lua index b1e709c444..589e026e5f 100644 --- a/test/unit/helpers.lua +++ b/test/unit/helpers.lua @@ -690,7 +690,9 @@ local function check_child_err(rd) break end trace[#trace + 1] = traceline - table.remove(trace, maxtrace + 1) + if #trace > maxtrace then + table.remove(trace, 1) + end end local res = sc.read(rd, 2) if #res ~= 2 then -- cgit From 2923e8533d1e28f9e771d2b4a7d279bfa266a480 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 24 Dec 2017 17:42:23 +0300 Subject: unittests: Do gc after reporting error, not before Reason: test may contain cleanup at the endwhich is needed for GC to work properly, but is not done if test fails. With collectgarbage() in former position it would crash when collecting garbage. --- test/unit/helpers.lua | 73 ++++++++++++++++++++++++++------------------------- 1 file changed, 37 insertions(+), 36 deletions(-) (limited to 'test') diff --git a/test/unit/helpers.lua b/test/unit/helpers.lua index 589e026e5f..87c838dece 100644 --- a/test/unit/helpers.lua +++ b/test/unit/helpers.lua @@ -650,8 +650,6 @@ local function itp_child(wr, func) collectgarbage('stop') child_sethook(wr) err, emsg = pcall(func) - collectgarbage('restart') - collectgarbage() debug.sethook() end emsg = tostring(emsg) @@ -662,14 +660,15 @@ local function itp_child(wr, func) end sc.write(wr, ('-\n%05u\n%s'):format(#emsg, emsg)) deinit() - sc.close(wr) - sc.exit(1) else sc.write(wr, '+\n') deinit() - sc.close(wr) - sc.exit(0) end + collectgarbage('restart') + collectgarbage() + sc.write(wr, '$\n') + sc.close(wr) + sc.exit(err and 0 or 1) end local function check_child_err(rd) @@ -695,41 +694,43 @@ local function check_child_err(rd) end end local res = sc.read(rd, 2) - if #res ~= 2 then - local error - if #trace == 0 then - error = '\nTest crashed, no trace available\n' - else - error = '\nTest crashed, trace:\n' .. tracehelp - for i = 1, #trace do - error = error .. trace[i] + if #res == 2 then + local err = '' + if res ~= '+\n' then + eq('-\n', res) + local len_s = sc.read(rd, 5) + local len = tonumber(len_s) + neq(0, len) + if os.getenv('NVIM_TEST_TRACE_ON_ERROR') == '1' and #trace ~= 0 then + err = '\nTest failed, trace:\n' .. tracehelp + for _, traceline in ipairs(trace) do + err = err .. traceline + end end + err = err .. sc.read(rd, len + 1) end - if not did_traceline then - error = error .. '\nNo end of trace occurred' - end - local cc_err, cc_emsg = pcall(check_cores, Paths.test_luajit_prg, true) - if not cc_err then - error = error .. '\ncheck_cores failed: ' .. cc_emsg + local eres = sc.read(rd, 2) + if eres ~= '$\n' then + if #trace == 0 then + err = '\nTest crashed, no trace available\n' + else + err = '\nTest crashed, trace:\n' .. tracehelp + for i = 1, #trace do + err = err .. trace[i] + end + end + if not did_traceline then + err = err .. '\nNo end of trace occurred' + end + local cc_err, cc_emsg = pcall(check_cores, Paths.test_luajit_prg, true) + if not cc_err then + err = err .. '\ncheck_cores failed: ' .. cc_emsg + end end - assert.just_fail(error) - end - if res == '+\n' then - return - end - eq('-\n', res) - local len_s = sc.read(rd, 5) - local len = tonumber(len_s) - neq(0, len) - local err = '' - if os.getenv('NVIM_TEST_TRACE_ON_ERROR') == '1' and #trace ~= 0 then - err = '\nTest failed, trace:\n' .. tracehelp - for _, traceline in ipairs(trace) do - err = err .. traceline + if err ~= '' then + assert.just_fail(err) end end - err = err .. sc.read(rd, len + 1) - assert.just_fail(err) end local function itp_parent(rd, pid, allow_failure) -- cgit From 7997147245da8c9e9fca4e1862d71bd6b28e1c06 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 24 Dec 2017 17:52:04 +0300 Subject: eval: Replace some tv_list_item_remove() calls There is nothing wrong with them, just it is generally better to remove a range then to remove items individually. --- test/unit/eval/typval_spec.lua | 71 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) (limited to 'test') diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index 0b6c4e04cc..6ca51fed85 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -444,6 +444,77 @@ describe('typval.c', function() alloc_log:check({}) end) end) + describe('remove_items()', function() + itp('works', function() + local l_tv = lua2typvalt({'1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13'}) + local l = l_tv.vval.v_list + local lis = list_items(l) + local strings = map(function(li) return li.li_tv.vval.v_string end, lis) + -- Three watchers: pointing to first, middle and last elements. + local lws = { + list_watch(l, lis[1]), + list_watch(l, lis[7]), + list_watch(l, lis[13]), + } + alloc_log:clear() + + lib.tv_list_remove_items(l, lis[1], lis[3]) + eq({'4', '5', '6', '7', '8', '9', '10', '11', '12', '13'}, typvalt2lua(l_tv)) + eq({lis[4], lis[7], lis[13]}, {lws[1].lw_item, lws[2].lw_item, lws[3].lw_item}) + alloc_log:check({ + a.freed(strings[1]), + a.freed(lis[1]), + a.freed(strings[2]), + a.freed(lis[2]), + a.freed(strings[3]), + a.freed(lis[3]), + }) + + lib.tv_list_remove_items(l, lis[11], lis[13]) + eq({'4', '5', '6', '7', '8', '9', '10'}, typvalt2lua(l_tv)) + eq({lis[4], lis[7], nil}, {lws[1].lw_item, lws[2].lw_item, lws[3].lw_item == nil and nil}) + alloc_log:check({ + a.freed(strings[11]), + a.freed(lis[11]), + a.freed(strings[12]), + a.freed(lis[12]), + a.freed(strings[13]), + a.freed(lis[13]), + }) + + lib.tv_list_remove_items(l, lis[6], lis[8]) + eq({'4', '5', '9', '10'}, typvalt2lua(l_tv)) + eq({lis[4], lis[9], nil}, {lws[1].lw_item, lws[2].lw_item, lws[3].lw_item == nil and nil}) + alloc_log:check({ + a.freed(strings[6]), + a.freed(lis[6]), + a.freed(strings[7]), + a.freed(lis[7]), + a.freed(strings[8]), + a.freed(lis[8]), + }) + + lib.tv_list_remove_items(l, lis[4], lis[10]) + eq(empty_list, typvalt2lua(l_tv)) + eq({true, true, true}, {lws[1].lw_item == nil, lws[2].lw_item == nil, lws[3].lw_item == nil}) + alloc_log:check({ + a.freed(strings[4]), + a.freed(lis[4]), + a.freed(strings[5]), + a.freed(lis[5]), + a.freed(strings[9]), + a.freed(lis[9]), + a.freed(strings[10]), + a.freed(lis[10]), + }) + + lib.tv_list_watch_remove(l, lws[1]) + lib.tv_list_watch_remove(l, lws[2]) + lib.tv_list_watch_remove(l, lws[3]) + + alloc_log:check({}) + end) + end) describe('insert', function() describe('()', function() itp('works', function() -- cgit From bc52ec61105160eb2b648af239e44cc594529fbf Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 24 Dec 2017 23:09:26 +0300 Subject: *: Fix linter errors --- test/unit/eval/typval_spec.lua | 2 -- 1 file changed, 2 deletions(-) (limited to 'test') diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index 6ca51fed85..b668144175 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -81,8 +81,6 @@ local function get_alloc_rets(exp_log, res) return exp_log end -local to_cstr_nofree = function(v) return lib.xstrdup(v) end - local alloc_log = alloc_log_new() before_each(function() -- cgit From 134c0f0bdb50b16d443b103fc1a81c9ae5c7c017 Mon Sep 17 00:00:00 2001 From: Gabriel Holodak Date: Fri, 1 Dec 2017 01:06:35 -0500 Subject: Add functional tests for man highlighting --- test/functional/plugin/man_spec.lua | 181 ++++++++++++++++++++++++++++++++++++ 1 file changed, 181 insertions(+) create mode 100644 test/functional/plugin/man_spec.lua (limited to 'test') diff --git a/test/functional/plugin/man_spec.lua b/test/functional/plugin/man_spec.lua new file mode 100644 index 0000000000..53384a9d47 --- /dev/null +++ b/test/functional/plugin/man_spec.lua @@ -0,0 +1,181 @@ +local helpers = require('test.functional.helpers')(after_each) +local plugin_helpers = require('test.functional.plugin.helpers') + +local Screen = require('test.functional.ui.screen') + +local buffer, command, eval = helpers.buffer, helpers.command, helpers.eval + +before_each(function() + plugin_helpers.reset() + helpers.clear() + command('syntax on') + command('set filetype=man') +end) + +describe('In autoload/man.vim', function() + describe('function man#highlight_formatted_text', function() + local screen + + before_each(function() + command('syntax off') -- Ignore syntax groups + screen = Screen.new(52, 5) + screen:attach() + end) + + after_each(function() + screen:detach() + end) + + local function expect(string) + screen:expect(string, + { + b = { bold = true }, + i = { italic = true }, + u = { underline = true }, + bi = { bold = true, italic = true }, + biu = { bold = true, italic = true, underline = true }, + }, + {{ bold = true, foreground = Screen.colors.Blue }}) + end + + local function expect_without_highlights(string) + screen:expect(string, nil, true) + end + + local function insert_lines(...) + buffer('set_lines', 0, 0, 1, false, { ... }) + end + + it('clears backspaces from text', function() + insert_lines( + "this i\bis\bs a\ba test", + "with _\bo_\bv_\be_\br_\bs_\bt_\br_\bu_\bc_\bk text" + ) + + expect_without_highlights([[ + ^this i^His^Hs a^Ha test | + with _^Ho_^Hv_^He_^Hr_^Hs_^Ht_^Hr_^Hu_^Hc_^Hk text | + ~ | + ~ | + | + ]]) + + eval('man#highlight_formatted_text()') + + expect_without_highlights([[ + ^this is a test | + with overstruck text | + ~ | + ~ | + | + ]]) + end) + + it('clears escape sequences from text', function() + insert_lines( + "this \027[1mis \027[3ma \027[4mtest\027[0m", + "\027[4mwith\027[24m \027[4mescaped\027[24m \027[4mtext\027[24m" + ) + + expect_without_highlights([[ + ^this ^[[1mis ^[[3ma ^[[4mtest^[[0m | + ^[[4mwith^[[24m ^[[4mescaped^[[24m ^[[4mtext^[[24m | + ~ | + ~ | + | + ]]) + + eval('man#highlight_formatted_text()') + + expect_without_highlights([[ + ^this is a test | + with escaped text | + ~ | + ~ | + | + ]]) + end) + + it('highlights overstruck text', function() + insert_lines( + "this i\bis\bs a\ba test", + "with _\bo_\bv_\be_\br_\bs_\bt_\br_\bu_\bc_\bk text" + ) + eval('man#highlight_formatted_text()') + + expect([[ + ^this {b:is} {b:a} test | + with {u:overstruck} text | + ~ | + ~ | + | + ]]) + end) + + it('highlights escape sequences in text', function() + insert_lines( + "this \027[1mis \027[3ma \027[4mtest\027[0m", + "\027[4mwith\027[24m \027[4mescaped\027[24m \027[4mtext\027[24m" + ) + eval('man#highlight_formatted_text()') + + expect([[ + ^this {b:is }{bi:a }{biu:test} | + {u:with} {u:escaped} {u:text} | + ~ | + ~ | + | + ]]) + end) + + it('highlights multibyte text', function() + insert_lines( + "this i\bis\bs あ\bあ test", + "with _\bö_\bv_\be_\br_\bs_\bt_\br_\bu_\bc_\bk te\027[3mxt¶\027[0m" + ) + eval('man#highlight_formatted_text()') + + expect([[ + ^this {b:is} {b:あ} test | + with {u:överstruck} te{i:xt¶} | + ~ | + ~ | + | + ]]) + end) + + it('highlights underscores based on context', function() + insert_lines( + "_\b_b\bbe\beg\bgi\bin\bns\bs", + "m\bmi\bid\bd_\b_d\bdl\ble\be", + "_\bm_\bi_\bd_\b__\bd_\bl_\be" + ) + eval('man#highlight_formatted_text()') + + expect([[ + {b:^_begins} | + {b:mid_dle} | + {u:mid_dle} | + ~ | + | + ]]) + end) + + it('highlights various bullet formats', function() + insert_lines( + "· ·\b·", + "+\bo", + "+\b+\bo\bo double" + ) + eval('man#highlight_formatted_text()') + + expect([[ + ^· {b:·} | + {b:·} | + {b:·} double | + ~ | + | + ]]) + end) + end) +end) -- cgit From eb44519b5debf740f692bb4ea19ad83b29749484 Mon Sep 17 00:00:00 2001 From: Gabriel Holodak Date: Sun, 24 Dec 2017 12:16:58 -0500 Subject: Address PR comments --- test/functional/plugin/man_spec.lua | 148 +++++++++++++----------------------- 1 file changed, 51 insertions(+), 97 deletions(-) (limited to 'test') diff --git a/test/functional/plugin/man_spec.lua b/test/functional/plugin/man_spec.lua index 53384a9d47..479fd6e7a5 100644 --- a/test/functional/plugin/man_spec.lua +++ b/test/functional/plugin/man_spec.lua @@ -3,7 +3,7 @@ local plugin_helpers = require('test.functional.plugin.helpers') local Screen = require('test.functional.ui.screen') -local buffer, command, eval = helpers.buffer, helpers.command, helpers.eval +local command, eval, rawfeed = helpers.command, helpers.eval, helpers.rawfeed before_each(function() plugin_helpers.reset() @@ -19,107 +19,64 @@ describe('In autoload/man.vim', function() before_each(function() command('syntax off') -- Ignore syntax groups screen = Screen.new(52, 5) - screen:attach() - end) - - after_each(function() - screen:detach() - end) - - local function expect(string) - screen:expect(string, - { + screen:set_default_attr_ids({ b = { bold = true }, i = { italic = true }, u = { underline = true }, bi = { bold = true, italic = true }, biu = { bold = true, italic = true, underline = true }, - }, - {{ bold = true, foreground = Screen.colors.Blue }}) - end - - local function expect_without_highlights(string) - screen:expect(string, nil, true) - end - - local function insert_lines(...) - buffer('set_lines', 0, 0, 1, false, { ... }) - end - - it('clears backspaces from text', function() - insert_lines( - "this i\bis\bs a\ba test", - "with _\bo_\bv_\be_\br_\bs_\bt_\br_\bu_\bc_\bk text" - ) - - expect_without_highlights([[ - ^this i^His^Hs a^Ha test | - with _^Ho_^Hv_^He_^Hr_^Hs_^Ht_^Hr_^Hu_^Hc_^Hk text | - ~ | - ~ | - | - ]]) - - eval('man#highlight_formatted_text()') + }) + screen:set_default_attr_ignore({ + { foreground = Screen.colors.Blue }, -- control chars + { bold = true, foreground = Screen.colors.Blue } -- empty line '~'s + }) + screen:attach() + end) - expect_without_highlights([[ - ^this is a test | - with overstruck text | - ~ | - ~ | - | - ]]) + after_each(function() + screen:detach() end) - it('clears escape sequences from text', function() - insert_lines( - "this \027[1mis \027[3ma \027[4mtest\027[0m", - "\027[4mwith\027[24m \027[4mescaped\027[24m \027[4mtext\027[24m" - ) + it('clears backspaces from text and adds highlights', function() + rawfeed([[ + ithis iiss aa test + with _o_v_e_r_s_t_r_u_c_k text]]) - expect_without_highlights([[ - ^this ^[[1mis ^[[3ma ^[[4mtest^[[0m | - ^[[4mwith^[[24m ^[[4mescaped^[[24m ^[[4mtext^[[24m | + screen:expect([[ + this i^His^Hs a^Ha test | + with _^Ho_^Hv_^He_^Hr_^Hs_^Ht_^Hr_^Hu_^Hc_^Hk tex^t | ~ | ~ | | ]]) - eval('man#highlight_formatted_text()') + eval('man#init_pager()') - expect_without_highlights([[ - ^this is a test | - with escaped text | + screen:expect([[ + ^this {b:is} {b:a} test | + with {u:overstruck} text | ~ | ~ | | ]]) end) - it('highlights overstruck text', function() - insert_lines( - "this i\bis\bs a\ba test", - "with _\bo_\bv_\be_\br_\bs_\bt_\br_\bu_\bc_\bk text" - ) - eval('man#highlight_formatted_text()') + it('clears escape sequences from text and adds highlights', function() + rawfeed([[ + ithis [1mis [3ma [4mtest[0m + [4mwith[24m [4mescaped[24m [4mtext[24m]]) - expect([[ - ^this {b:is} {b:a} test | - with {u:overstruck} text | + screen:expect([[ + this ^[[1mis ^[[3ma ^[[4mtest^[[0m | + ^[[4mwith^[[24m ^[[4mescaped^[[24m ^[[4mtext^[[24^m | ~ | ~ | | ]]) - end) - it('highlights escape sequences in text', function() - insert_lines( - "this \027[1mis \027[3ma \027[4mtest\027[0m", - "\027[4mwith\027[24m \027[4mescaped\027[24m \027[4mtext\027[24m" - ) - eval('man#highlight_formatted_text()') + eval('man#init_pager()') - expect([[ + screen:expect([[ ^this {b:is }{bi:a }{biu:test} | {u:with} {u:escaped} {u:text} | ~ | @@ -129,15 +86,14 @@ describe('In autoload/man.vim', function() end) it('highlights multibyte text', function() - insert_lines( - "this i\bis\bs あ\bあ test", - "with _\bö_\bv_\be_\br_\bs_\bt_\br_\bu_\bc_\bk te\027[3mxt¶\027[0m" - ) - eval('man#highlight_formatted_text()') + rawfeed([[ + ithis iiss ああ test + with _ö_v_e_r_s_t_r_u_̃_c_k te[3mxt¶[0m]]) + eval('man#init_pager()') - expect([[ + screen:expect([[ ^this {b:is} {b:あ} test | - with {u:överstruck} te{i:xt¶} | + with {u:överstrũck} te{i:xt¶} | ~ | ~ | | @@ -145,14 +101,13 @@ describe('In autoload/man.vim', function() end) it('highlights underscores based on context', function() - insert_lines( - "_\b_b\bbe\beg\bgi\bin\bns\bs", - "m\bmi\bid\bd_\b_d\bdl\ble\be", - "_\bm_\bi_\bd_\b__\bd_\bl_\be" - ) - eval('man#highlight_formatted_text()') - - expect([[ + rawfeed([[ + i__bbeeggiinnss + mmiidd__ddllee + _m_i_d___d_l_e]]) + eval('man#init_pager()') + + screen:expect([[ {b:^_begins} | {b:mid_dle} | {u:mid_dle} | @@ -162,14 +117,13 @@ describe('In autoload/man.vim', function() end) it('highlights various bullet formats', function() - insert_lines( - "· ·\b·", - "+\bo", - "+\b+\bo\bo double" - ) - eval('man#highlight_formatted_text()') - - expect([[ + rawfeed([[ + i· ·· + +o + ++oo double]]) + eval('man#init_pager()') + + screen:expect([[ ^· {b:·} | {b:·} | {b:·} double | -- cgit From e84e1b68c13443efc3e0c39a16c6a6945fbde20b Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Fri, 29 Dec 2017 20:35:52 +0100 Subject: Move applying of TabClosed to win_close_othertab --- test/functional/autocmd/tabclose_spec.lua | 44 +++++++++++++++++++++++++++---- 1 file changed, 39 insertions(+), 5 deletions(-) (limited to 'test') diff --git a/test/functional/autocmd/tabclose_spec.lua b/test/functional/autocmd/tabclose_spec.lua index 1431c69589..c412687b8c 100644 --- a/test/functional/autocmd/tabclose_spec.lua +++ b/test/functional/autocmd/tabclose_spec.lua @@ -2,10 +2,10 @@ local helpers = require('test.functional.helpers')(after_each) local clear, nvim, eq = helpers.clear, helpers.nvim, helpers.eq describe('TabClosed', function() + before_each(clear) describe('au TabClosed', function() describe('with * as ', function() it('matches when closing any tab', function() - clear() nvim('command', 'au! TabClosed * echom "tabclosed:".expand("").":".expand("").":".tabpagenr()') repeat nvim('command', 'tabnew') @@ -15,17 +15,51 @@ describe('TabClosed', function() eq("\ntabclosed:2:2:3", nvim('command_output', '2tabclose')) -- close tab 2, current tab is now 3 eq("\ntabclosed:1:1:2\ntabclosed:1:1:1", nvim('command_output', 'tabonly')) -- close tabs 1 and 2 end) + + it('is triggered when closing a window via bdelete from another tab', function() + nvim('command', 'au! TabClosed * echom "tabclosed:".expand("").":".expand("").":".tabpagenr()') + nvim('command', '1tabedit Xtestfile') + nvim('command', '1tabedit Xtestfile') + nvim('command', 'normal! 1gt') + eq({1, 3}, nvim('eval', '[tabpagenr(), tabpagenr("$")]')) + eq("\ntabclosed:2:2:1\ntabclosed:2:2:1", nvim('command_output', 'bdelete Xtestfile')) + eq({1, 1}, nvim('eval', '[tabpagenr(), tabpagenr("$")]')) + end) + + it('is triggered when closing a window via bdelete from current tab', function() + nvim('command', 'au! TabClosed * echom "tabclosed:".expand("").":".expand("").":".tabpagenr()') + nvim('command', 'file Xtestfile1') + nvim('command', '1tabedit Xtestfile2') + nvim('command', '1tabedit Xtestfile2') + + -- Only one tab is closed, and the alternate file is used for the other. + eq({2, 3}, nvim('eval', '[tabpagenr(), tabpagenr("$")]')) + eq("\ntabclosed:2:2:2", nvim('command_output', 'bdelete Xtestfile2')) + eq('Xtestfile1', nvim('eval', 'bufname("")')) + end) end) + describe('with NR as ', function() it('matches when closing a tab whose index is NR', function() + nvim('command', 'au! TabClosed * echom "tabclosed:".expand("").":".expand("").":".tabpagenr()') nvim('command', 'au! TabClosed 2 echom "tabclosed:match"') repeat nvim('command', 'tabnew') - until nvim('eval', 'tabpagenr()') == 5 -- current tab is now 5 + until nvim('eval', 'tabpagenr()') == 7 -- current tab is now 7 -- sanity check, we shouldn't match on tabs with numbers other than 2 - eq("\ntabclosed:5:5:4", nvim('command_output', 'tabclose')) - -- close tab page 2, current tab is now 3 - eq("\ntabclosed:2:2:3\ntabclosed:match", nvim('command_output', '2tabclose')) + eq("\ntabclosed:7:7:6", nvim('command_output', 'tabclose')) + -- close tab page 2, current tab is now 5 + eq("\ntabclosed:2:2:5\ntabclosed:match", nvim('command_output', '2tabclose')) + end) + end) + + describe('with close', function() + it('is triggered', function() + nvim('command', 'au! TabClosed * echom "tabclosed:".expand("").":".expand("").":".tabpagenr()') + nvim('command', 'tabedit Xtestfile') + eq({2, 2}, nvim('eval', '[tabpagenr(), tabpagenr("$")]')) + eq("\ntabclosed:2:2:1", nvim('command_output', 'close')) + eq({1, 1}, nvim('eval', '[tabpagenr(), tabpagenr("$")]')) end) end) end) -- cgit From 46f432074e739a0eca9bb204e9c7769935669dbd Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Fri, 29 Dec 2017 21:13:21 +0100 Subject: tests: termclose_spec: fix flaky SIGTERM test #7787 Followup to https://github.com/neovim/neovim/pull/7217. Build failure: https://travis-ci.org/neovim/neovim/jobs/322930672#L2958. --- test/functional/autocmd/termclose_spec.lua | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'test') diff --git a/test/functional/autocmd/termclose_spec.lua b/test/functional/autocmd/termclose_spec.lua index c6c30494dd..e64df502a6 100644 --- a/test/functional/autocmd/termclose_spec.lua +++ b/test/functional/autocmd/termclose_spec.lua @@ -4,6 +4,7 @@ local clear, command, nvim, nvim_dir = helpers.clear, helpers.command, helpers.nvim, helpers.nvim_dir local eval, eq, retry = helpers.eval, helpers.eq, helpers.retry +local ok = helpers.ok if helpers.pending_win32(pending) then return end @@ -41,7 +42,9 @@ describe('TermClose event', function() command('call jobstop(g:test_job)') retry(nil, nil, function() eq(1, eval('get(g:, "test_job_exited", 0)')) end) local duration = os.time() - start - eq(2, duration) + -- nvim starts sending SIGTERM after KILL_TIMEOUT_MS + ok(duration >= 2) + ok(duration <= 4) -- <= 2 + delta because of slow CI end) it('kills pty job trapping SIGHUP and SIGTERM', function() @@ -58,8 +61,8 @@ describe('TermClose event', function() retry(nil, nil, function() eq(1, eval('get(g:, "test_job_exited", 0)')) end) local duration = os.time() - start -- nvim starts sending kill after 2*KILL_TIMEOUT_MS - helpers.ok(4 <= duration) - helpers.ok(duration <= 7) -- <= 4 + delta because of slow CI + ok(duration >= 4) + ok(duration <= 7) -- <= 4 + delta because of slow CI end) it('reports the correct ', function() -- cgit From d55881d2783da4161a37ac39fcfb2c535a837a5e Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Wed, 4 Oct 2017 20:32:22 -0400 Subject: test: enable K_spec tests in Windows --- test/functional/normal/K_spec.lua | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'test') diff --git a/test/functional/normal/K_spec.lua b/test/functional/normal/K_spec.lua index 43e598633c..174313d80e 100644 --- a/test/functional/normal/K_spec.lua +++ b/test/functional/normal/K_spec.lua @@ -2,8 +2,6 @@ local helpers = require('test.functional.helpers')(after_each) local eq, clear, eval, feed = helpers.eq, helpers.clear, helpers.eval, helpers.feed -if helpers.pending_win32(pending) then return end - describe('K', function() local test_file = 'K_spec_out' before_each(function() @@ -29,7 +27,7 @@ describe('K', function() it("invokes non-prefixed 'keywordprg' as shell command", function() helpers.source([[ let @a='fnord' - set keywordprg=echo\ fnord\ >>]]) + set keywordprg=echo\ fnord>>]]) -- K on the text "K_spec_out" resolves to `!echo fnord >> K_spec_out`. feed('i'..test_file..'K') -- cgit From 7c4bb23ff38a459911a742657572bc273e528ddf Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Wed, 3 Jan 2018 19:57:22 +0100 Subject: defaults: do :filetype stuff unless explicitly "off" Until now, the default `:filetype ...` setup was skipped if the user config touched `:filetype` in any way (including implicitly via `:syntax on`). No one needs that, and it's very confusing. Instead, proceed with `:filetype ... on` unless the user explicitly called `:filetype ... off`. closes #7765 --- test/functional/options/defaults_spec.lua | 90 +++++++++++++++++-------------- 1 file changed, 50 insertions(+), 40 deletions(-) (limited to 'test') diff --git a/test/functional/options/defaults_spec.lua b/test/functional/options/defaults_spec.lua index b83b7b8eee..fd232cb4dd 100644 --- a/test/functional/options/defaults_spec.lua +++ b/test/functional/options/defaults_spec.lua @@ -11,15 +11,6 @@ local neq = helpers.neq local mkdir = helpers.mkdir local rmdir = helpers.rmdir -local function init_session(...) - local args = { helpers.nvim_prog, '-i', 'NONE', '--embed', - '--cmd', helpers.nvim_set } - for _, v in ipairs({...}) do - table.insert(args, v) - end - helpers.set_session(helpers.spawn(args)) -end - describe('startup defaults', function() describe(':filetype', function() if helpers.pending_win32(pending) then return end @@ -36,50 +27,70 @@ describe('startup defaults', function() ) end - it('enabled by `-u NORC`', function() - init_session('-u', 'NORC') + it('all ON after `-u NORC`', function() + clear('-u', 'NORC') expect_filetype( 'filetype detection:ON plugin:ON indent:ON |') end) - it('disabled by `-u NONE`', function() - init_session('-u', 'NONE') + it('all ON after `:syntax …` #7765', function() + clear('-u', 'NORC', '--cmd', 'syntax on') expect_filetype( - 'filetype detection:OFF plugin:OFF indent:OFF |') + 'filetype detection:ON plugin:ON indent:ON |') + clear('-u', 'NORC', '--cmd', 'syntax off') + expect_filetype( + 'filetype detection:ON plugin:ON indent:ON |') end) - it('overridden by early `filetype on`', function() - init_session('-u', 'NORC', '--cmd', 'filetype on') + it('all OFF after `-u NONE`', function() + clear('-u', 'NONE') expect_filetype( - 'filetype detection:ON plugin:OFF indent:OFF |') + 'filetype detection:OFF plugin:OFF indent:OFF |') end) - it('overridden by early `filetype plugin on`', function() - init_session('-u', 'NORC', '--cmd', 'filetype plugin on') + it('explicit OFF stays OFF', function() + clear('-u', 'NORC', '--cmd', + 'syntax off | filetype off | filetype plugin indent off') + expect_filetype( + 'filetype detection:OFF plugin:OFF indent:OFF |') + clear('-u', 'NORC', '--cmd', 'syntax off | filetype plugin indent off') + expect_filetype( + 'filetype detection:ON plugin:OFF indent:OFF |') + clear('-u', 'NORC', '--cmd', 'filetype indent off') expect_filetype( 'filetype detection:ON plugin:ON indent:OFF |') + clear('-u', 'NORC', '--cmd', 'syntax off | filetype off') + expect_filetype( + 'filetype detection:OFF plugin:(on) indent:(on) |') + -- Swap the order. + clear('-u', 'NORC', '--cmd', 'filetype off | syntax off') + expect_filetype( + 'filetype detection:OFF plugin:(on) indent:(on) |') end) - it('overridden by early `filetype indent on`', function() - init_session('-u', 'NORC', '--cmd', 'filetype indent on') + it('all ON after early `:filetype … on`', function() + -- `:filetype … on` should not change the defaults. #7765 + -- Only an explicit `:filetype … off` sets OFF. + + clear('-u', 'NORC', '--cmd', 'filetype on') expect_filetype( - 'filetype detection:ON plugin:OFF indent:ON |') + 'filetype detection:ON plugin:ON indent:ON |') + clear('-u', 'NORC', '--cmd', 'filetype plugin on') + expect_filetype( + 'filetype detection:ON plugin:ON indent:ON |') + clear('-u', 'NORC', '--cmd', 'filetype indent on') + expect_filetype( + 'filetype detection:ON plugin:ON indent:ON |') end) - it('adjusted by late `filetype off`', function() - init_session('-u', 'NORC', '-c', 'filetype off') + it('late `:filetype … off` stays OFF', function() + clear('-u', 'NORC', '-c', 'filetype off') expect_filetype( 'filetype detection:OFF plugin:(on) indent:(on) |') - end) - - it('adjusted by late `filetype plugin off`', function() - init_session('-u', 'NORC', '-c', 'filetype plugin off') + clear('-u', 'NORC', '-c', 'filetype plugin off') expect_filetype( 'filetype detection:ON plugin:OFF indent:ON |') - end) - - it('adjusted by late `filetype indent off`', function() - init_session('-u', 'NORC', '-c', 'filetype indent off') + clear('-u', 'NORC', '-c', 'filetype indent off') expect_filetype( 'filetype detection:ON plugin:ON indent:OFF |') end) @@ -87,22 +98,21 @@ describe('startup defaults', function() describe('syntax', function() it('enabled by `-u NORC`', function() - init_session('-u', 'NORC') + clear('-u', 'NORC') eq(1, eval('g:syntax_on')) end) it('disabled by `-u NONE`', function() - init_session('-u', 'NONE') + clear('-u', 'NONE') eq(0, eval('exists("g:syntax_on")')) end) - it('overridden by early `syntax off`', function() - init_session('-u', 'NORC', '--cmd', 'syntax off') + it('`:syntax off` stays off', function() + -- early + clear('-u', 'NORC', '--cmd', 'syntax off') eq(0, eval('exists("g:syntax_on")')) - end) - - it('adjusted by late `syntax off`', function() - init_session('-u', 'NORC', '-c', 'syntax off') + -- late + clear('-u', 'NORC', '-c', 'syntax off') eq(0, eval('exists("g:syntax_on")')) end) end) -- cgit From b616ef9b220fdf7a7b842a95a8a9d088cfbf472d Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Fri, 5 Jan 2018 10:54:29 +0100 Subject: tests: stderr output contains `cp` noise closes #7811 --- test/functional/shada/helpers.lua | 1 - 1 file changed, 1 deletion(-) (limited to 'test') diff --git a/test/functional/shada/helpers.lua b/test/functional/shada/helpers.lua index b77e59682f..1312d762d8 100644 --- a/test/functional/shada/helpers.lua +++ b/test/functional/shada/helpers.lua @@ -37,7 +37,6 @@ local function add_argv(...) end local clear = function() - os.execute('cp ' .. tmpname .. ' /tmp/test.shada') os.remove(tmpname) append_argv = nil end -- cgit From e9b5616eaf49755c972bcc46719f985f9ca1e15f Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Thu, 5 Oct 2017 12:38:10 -0400 Subject: win: enable tests in ex_terminal_spec --- test/functional/terminal/ex_terminal_spec.lua | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) (limited to 'test') diff --git a/test/functional/terminal/ex_terminal_spec.lua b/test/functional/terminal/ex_terminal_spec.lua index e015df10db..ee92ba6865 100644 --- a/test/functional/terminal/ex_terminal_spec.lua +++ b/test/functional/terminal/ex_terminal_spec.lua @@ -7,7 +7,6 @@ local retry = helpers.retry local iswin = helpers.iswin describe(':terminal', function() - if helpers.pending_win32(pending) then return end local screen before_each(function() @@ -24,7 +23,11 @@ describe(':terminal', function() echomsg "msg3" ]]) -- Invoke a command that emits frequent terminal activity. - feed_command([[terminal while true; do echo X; done]]) + if iswin() then + feed_command([[terminal for /L \\%I in (1,0,2) do echo \\%I]]) + else + feed_command([[terminal while true; do echo X; done]]) + end helpers.feed([[]]) wait() screen:sleep(10) -- Let some terminal activity happen. @@ -38,7 +41,11 @@ describe(':terminal', function() end) it("in normal-mode :split does not move cursor", function() - feed_command([[terminal while true; do echo foo; sleep .1; done]]) + if iswin() then + feed_command([[terminal for /L \\%I in (1,0,2) do ( echo foo & ping -w 100 -n 1 127.0.0.1 > nul )]]) + else + feed_command([[terminal while true; do echo foo; sleep .1; done]]) + end helpers.feed([[M]]) -- move cursor away from last line wait() eq(3, eval("line('$')")) -- window height -- cgit From 7311fb7cadff49422a15d3a40d7014000c8f4385 Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Thu, 19 Oct 2017 17:26:03 -0400 Subject: win: enable more functional tests - plugin/shada_spec.lua: Use \r\n as Windows EOL for tests on BufWriteCmd, FileWriteCmd, FileAppendCmd. Alternative is 'set fileformat=unix'. --- test/functional/options/autochdir_spec.lua | 5 ++--- test/functional/options/defaults_spec.lua | 2 -- test/functional/plugin/shada_spec.lua | 10 +++++----- test/functional/ui/bufhl_spec.lua | 2 -- test/functional/ui/inccommand_spec.lua | 12 ------------ test/functional/ui/input_spec.lua | 2 -- test/functional/ui/mouse_spec.lua | 2 -- test/functional/ui/searchhl_spec.lua | 2 -- test/functional/ui/sign_spec.lua | 2 -- test/functional/ui/syntax_conceal_spec.lua | 2 -- test/functional/viml/completion_spec.lua | 2 -- 11 files changed, 7 insertions(+), 36 deletions(-) (limited to 'test') diff --git a/test/functional/options/autochdir_spec.lua b/test/functional/options/autochdir_spec.lua index 209531515c..2fce0a5ed9 100644 --- a/test/functional/options/autochdir_spec.lua +++ b/test/functional/options/autochdir_spec.lua @@ -3,8 +3,6 @@ local clear = helpers.clear local eq = helpers.eq local getcwd = helpers.funcs.getcwd -if helpers.pending_win32(pending) then return end - describe("'autochdir'", function() it('given on the shell gets processed properly', function() local targetdir = 'test/functional/fixtures' @@ -12,9 +10,10 @@ describe("'autochdir'", function() -- By default 'autochdir' is off, thus getcwd() returns the repo root. clear(targetdir..'/tty-test.c') local rootdir = getcwd() + local expected = rootdir .. '/' .. targetdir -- With 'autochdir' on, we should get the directory of tty-test.c. clear('--cmd', 'set autochdir', targetdir..'/tty-test.c') - eq(rootdir..'/'..targetdir, getcwd()) + eq(helpers.iswin() and expected:gsub('/', '\\') or expected, getcwd()) end) end) diff --git a/test/functional/options/defaults_spec.lua b/test/functional/options/defaults_spec.lua index b83b7b8eee..68e94d1a85 100644 --- a/test/functional/options/defaults_spec.lua +++ b/test/functional/options/defaults_spec.lua @@ -22,8 +22,6 @@ end describe('startup defaults', function() describe(':filetype', function() - if helpers.pending_win32(pending) then return end - local function expect_filetype(expected) local screen = Screen.new(50, 4) screen:attach() diff --git a/test/functional/plugin/shada_spec.lua b/test/functional/plugin/shada_spec.lua index dbc78e63f0..57891a8229 100644 --- a/test/functional/plugin/shada_spec.lua +++ b/test/functional/plugin/shada_spec.lua @@ -43,8 +43,6 @@ local wshada, _, fname = get_shada_rw('Xtest-functional-plugin-shada.shada') local wshada_tmp, _, fname_tmp = get_shada_rw('Xtest-functional-plugin-shada.shada.tmp.f') -if helpers.pending_win32(pending) then return end - describe('In autoload/shada.vim', function() local epoch = os.date('%Y-%m-%dT%H:%M:%S', 0) before_each(function() @@ -2140,6 +2138,7 @@ end) describe('In plugin/shada.vim', function() local epoch = os.date('%Y-%m-%dT%H:%M:%S', 0) + local eol = helpers.iswin() and '\r\n' or '\n' before_each(function() reset() os.remove(fname) @@ -2279,7 +2278,7 @@ describe('In plugin/shada.vim', function() ' + f file name ["foo"]', ' + l line number 2', ' + c column -200', - }, '\n') .. '\n', io.open(fname .. '.tst'):read('*a')) + }, eol) .. eol, io.open(fname .. '.tst'):read('*a')) shada_eq({{ timestamp=0, type=8, @@ -2303,6 +2302,7 @@ describe('In plugin/shada.vim', function() describe('event FileWriteCmd', function() it('works', function() + if helpers.pending_win32(pending) then return end nvim('set_var', 'shada#add_own_header', 0) curbuf('set_lines', 0, 1, true, { 'Jump with timestamp ' .. epoch .. ':', @@ -2326,7 +2326,7 @@ describe('In plugin/shada.vim', function() 'Jump with timestamp ' .. epoch .. ':', ' % Key________ Description Value', ' + n name \'A\'', - }, '\n') .. '\n', io.open(fname .. '.tst'):read('*a')) + }, eol) .. eol, io.open(fname .. '.tst'):read('*a')) shada_eq({{ timestamp=0, type=8, @@ -2383,7 +2383,7 @@ describe('In plugin/shada.vim', function() ' + f file name ["foo"]', ' + l line number 2', ' + c column -200', - }, '\n') .. '\n', io.open(fname .. '.tst'):read('*a')) + }, eol) .. eol, io.open(fname .. '.tst'):read('*a')) shada_eq({{ timestamp=0, type=8, diff --git a/test/functional/ui/bufhl_spec.lua b/test/functional/ui/bufhl_spec.lua index 2143c01139..091c45596d 100644 --- a/test/functional/ui/bufhl_spec.lua +++ b/test/functional/ui/bufhl_spec.lua @@ -4,8 +4,6 @@ local Screen = require('test.functional.ui.screen') local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert local command, request, neq = helpers.command, helpers.request, helpers.neq -if helpers.pending_win32(pending) then return end - describe('Buffer highlighting', function() local screen local curbuf diff --git a/test/functional/ui/inccommand_spec.lua b/test/functional/ui/inccommand_spec.lua index 53fd17c309..4a8ebf6212 100644 --- a/test/functional/ui/inccommand_spec.lua +++ b/test/functional/ui/inccommand_spec.lua @@ -121,8 +121,6 @@ describe(":substitute, inccommand=split does not trigger preview", function() end) describe(":substitute, 'inccommand' preserves", function() - if helpers.pending_win32(pending) then return end - before_each(clear) it('listed buffers (:ls)', function() @@ -285,8 +283,6 @@ describe(":substitute, 'inccommand' preserves", function() end) describe(":substitute, 'inccommand' preserves undo", function() - if helpers.pending_win32(pending) then return end - local cases = { "", "split", "nosplit" } local substrings = { @@ -700,8 +696,6 @@ describe(":substitute, 'inccommand' preserves undo", function() end) describe(":substitute, inccommand=split", function() - if helpers.pending_win32(pending) then return end - local screen = Screen.new(30,15) before_each(function() @@ -1169,8 +1163,6 @@ describe(":substitute, inccommand=split", function() end) describe("inccommand=nosplit", function() - if helpers.pending_win32(pending) then return end - local screen = Screen.new(20,10) before_each(function() @@ -1356,8 +1348,6 @@ describe("inccommand=nosplit", function() end) describe(":substitute, 'inccommand' with a failing expression", function() - if helpers.pending_win32(pending) then return end - local screen = Screen.new(20,10) local cases = { "", "split", "nosplit" } @@ -1621,8 +1611,6 @@ describe("'inccommand' autocommands", function() end) describe("'inccommand' split windows", function() - if helpers.pending_win32(pending) then return end - local screen local function refresh() clear() diff --git a/test/functional/ui/input_spec.lua b/test/functional/ui/input_spec.lua index 29d974b709..f8842901c1 100644 --- a/test/functional/ui/input_spec.lua +++ b/test/functional/ui/input_spec.lua @@ -4,8 +4,6 @@ local feed, next_message, eq = helpers.feed, helpers.next_message, helpers.eq local expect = helpers.expect local Screen = require('test.functional.ui.screen') -if helpers.pending_win32(pending) then return end - describe('mappings', function() local cid diff --git a/test/functional/ui/mouse_spec.lua b/test/functional/ui/mouse_spec.lua index 3daf92eea0..3fdedeb073 100644 --- a/test/functional/ui/mouse_spec.lua +++ b/test/functional/ui/mouse_spec.lua @@ -4,8 +4,6 @@ local clear, feed, meths = helpers.clear, helpers.feed, helpers.meths local insert, feed_command = helpers.insert, helpers.feed_command local eq, funcs = helpers.eq, helpers.funcs -if helpers.pending_win32(pending) then return end - describe('ui/mouse/input', function() local screen diff --git a/test/functional/ui/searchhl_spec.lua b/test/functional/ui/searchhl_spec.lua index 11b18d015f..5af8b83a36 100644 --- a/test/functional/ui/searchhl_spec.lua +++ b/test/functional/ui/searchhl_spec.lua @@ -3,8 +3,6 @@ local Screen = require('test.functional.ui.screen') local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert local feed_command = helpers.feed_command -if helpers.pending_win32(pending) then return end - describe('search highlighting', function() local screen local colors = Screen.colors diff --git a/test/functional/ui/sign_spec.lua b/test/functional/ui/sign_spec.lua index e5c96f2ec0..c00d99cf90 100644 --- a/test/functional/ui/sign_spec.lua +++ b/test/functional/ui/sign_spec.lua @@ -2,8 +2,6 @@ local helpers = require('test.functional.helpers')(after_each) local Screen = require('test.functional.ui.screen') local clear, feed, command = helpers.clear, helpers.feed, helpers.command -if helpers.pending_win32(pending) then return end - describe('Signs', function() local screen diff --git a/test/functional/ui/syntax_conceal_spec.lua b/test/functional/ui/syntax_conceal_spec.lua index 28a104360d..e7a7004c1e 100644 --- a/test/functional/ui/syntax_conceal_spec.lua +++ b/test/functional/ui/syntax_conceal_spec.lua @@ -3,8 +3,6 @@ local Screen = require('test.functional.ui.screen') local clear, feed, command = helpers.clear, helpers.feed, helpers.command local insert = helpers.insert -if helpers.pending_win32(pending) then return end - describe('Screen', function() local screen diff --git a/test/functional/viml/completion_spec.lua b/test/functional/viml/completion_spec.lua index b70ef724b7..fbc7a527f8 100644 --- a/test/functional/viml/completion_spec.lua +++ b/test/functional/viml/completion_spec.lua @@ -5,8 +5,6 @@ local eval, eq, neq = helpers.eval, helpers.eq, helpers.neq local feed_command, source, expect = helpers.feed_command, helpers.source, helpers.expect local meths = helpers.meths -if helpers.pending_win32(pending) then return end - describe('completion', function() local screen -- cgit From 8d58012786b837380e6006d38580852d9506fbc8 Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Tue, 24 Oct 2017 17:38:18 -0400 Subject: test: use unix fileformat to test NULs on systemlist --- test/functional/eval/system_spec.lua | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'test') diff --git a/test/functional/eval/system_spec.lua b/test/functional/eval/system_spec.lua index 7e213e2156..96212bf538 100644 --- a/test/functional/eval/system_spec.lua +++ b/test/functional/eval/system_spec.lua @@ -442,11 +442,13 @@ describe('systemlist()', function() describe('with output containing NULs', function() local fname = 'Xtest' - before_each(create_file_with_nuls(fname)) + before_each(function() + command('set ff=unix') + create_file_with_nuls(fname)() + end) after_each(delete_file(fname)) it('replaces NULs by newline characters', function() - if helpers.pending_win32(pending) then return end eq({'part1\npart2\npart3'}, eval('systemlist("cat '..fname..'")')) end) end) -- cgit From d4485f7cc0e4bab7d8ca41590956fa96d31f6aab Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Mon, 6 Nov 2017 21:00:19 -0500 Subject: win: test: check non-shell system() --- test/functional/eval/system_spec.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'test') diff --git a/test/functional/eval/system_spec.lua b/test/functional/eval/system_spec.lua index 96212bf538..74c3b77d91 100644 --- a/test/functional/eval/system_spec.lua +++ b/test/functional/eval/system_spec.lua @@ -89,7 +89,9 @@ describe('system()', function() end) it('does NOT run in shell', function() - if not iswin() then + if iswin() then + eq("%PATH%\n", eval("system(['powershell', '-NoProfile', '-NoLogo', '-ExecutionPolicy', 'RemoteSigned', '-Command', 'echo', '%PATH%'])")) + else eq("* $PATH %PATH%\n", eval("system(['echo', '*', '$PATH', '%PATH%'])")) end end) -- cgit From ab1e11e44fd17ffbd4c1fdb26c2b03de3c6831ae Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Wed, 3 Jan 2018 00:18:34 -0500 Subject: test: win: yes is unavailable on Windows --- test/functional/eval/system_spec.lua | 1 + 1 file changed, 1 insertion(+) (limited to 'test') diff --git a/test/functional/eval/system_spec.lua b/test/functional/eval/system_spec.lua index 74c3b77d91..e2b12b6bc9 100644 --- a/test/functional/eval/system_spec.lua +++ b/test/functional/eval/system_spec.lua @@ -187,6 +187,7 @@ describe('system()', function() end) it('`yes` and is interrupted with CTRL-C', function() + if helpers.pending_win32(pending) then return end feed(':call system("yes")') screen:expect([[ | -- cgit From 8c2cb81d7ecb05f5c399696798690e1861c3b96a Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sun, 7 Jan 2018 16:20:37 +0100 Subject: test: set_shell_powershell(): update flags (#7819) --- test/functional/helpers.lua | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'test') diff --git a/test/functional/helpers.lua b/test/functional/helpers.lua index bff8d065f8..31a2c3b3ff 100644 --- a/test/functional/helpers.lua +++ b/test/functional/helpers.lua @@ -383,9 +383,8 @@ end local function set_shell_powershell() source([[ - set shell=powershell shellquote=\" shellpipe=\| shellredir=> - set shellcmdflag=\ -NoLogo\ -NoProfile\ -ExecutionPolicy\ RemoteSigned\ -Command - let &shellxquote=' ' + set shell=powershell shellquote=( shellpipe=\| shellredir=> shellxquote= + set shellcmdflag=-NoLogo\ -NoProfile\ -ExecutionPolicy\ RemoteSigned\ -Command ]]) end -- cgit From c095f83116eb8ef87983ca5fea61053755fbc4e5 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Fri, 5 Jan 2018 11:17:21 +0100 Subject: api: change nvim_command_output behavior Implement nvim_command_output with `execute({cmd},"silent")`. Behavior changes: - does not provoke any hit-enter prompt - no longer prepends a newline char - does not capture some noise (like the "[New File]" message, see the change to tabnewentered_spec.lua) Technically ("bug-for-bug") this a breaking change. But the previous behavior of nvim_command_output meant that it probably wasn't used for anything outside of tests. Also remove the undocumented `v:command_output` variable which was a hack introduced only for the purposes of nvim_command_output. closes #7726 --- test/functional/api/vim_spec.lua | 53 +++++++++++++++++++++++++- test/functional/autocmd/tabclose_spec.lua | 18 ++++----- test/functional/autocmd/tabnewentered_spec.lua | 8 ++-- test/functional/ex_cmds/sign_spec.lua | 4 +- test/functional/helpers.lua | 14 +++---- test/functional/ui/cmdline_highlight_spec.lua | 2 +- 6 files changed, 74 insertions(+), 25 deletions(-) (limited to 'test') diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua index ff28e3d133..d213c3c34e 100644 --- a/test/functional/api/vim_spec.lua +++ b/test/functional/api/vim_spec.lua @@ -37,7 +37,7 @@ describe('api', function() os.remove(fname) end) - it("VimL error: fails (VimL error), does NOT update v:errmsg", function() + it("parse error: fails (specific error), does NOT update v:errmsg", function() -- Most API methods return generic errors (or no error) if a VimL -- expression fails; nvim_command returns the VimL error details. local status, rv = pcall(nvim, "command", "bogus_command") @@ -45,6 +45,57 @@ describe('api', function() eq("E492:", string.match(rv, "E%d*:")) -- VimL error was returned. eq("", nvim("eval", "v:errmsg")) -- v:errmsg was not updated. end) + + it("runtime error: fails (specific error)", function() + local status, rv = pcall(nvim, "command_output", "buffer 23487") + eq(false, status) -- nvim_command() failed. + eq("E86: Buffer 23487 does not exist", string.match(rv, "E%d*:.*")) + eq("", nvim("eval", "v:errmsg")) -- v:errmsg was not updated. + end) + end) + + describe('nvim_command_output', function() + it('does not induce hit-enter prompt', function() + -- Induce a hit-enter prompt use nvim_input (non-blocking). + nvim('command', 'set cmdheight=1') + nvim('input', [[:echo "hi\nhi2"]]) + + -- Verify hit-enter prompt. + eq({mode='r', blocking=true}, nvim("get_mode")) + nvim('input', [[]]) + + -- Verify NO hit-enter prompt. + nvim('command_output', [[echo "hi\nhi2"]]) + eq({mode='n', blocking=false}, nvim("get_mode")) + end) + + it('returns command output', function() + eq('this is\nspinal tap', + nvim('command_output', [[echo "this is\nspinal tap"]])) + end) + + it('does not return shell |:!| output', function() + eq(':!echo "foo"\r\n', nvim('command_output', [[!echo "foo"]])) + end) + + it("parse error: fails (specific error), does NOT update v:errmsg", function() + local status, rv = pcall(nvim, "command_output", "bogus commannnd") + eq(false, status) -- nvim_command_output() failed. + eq("E492: Not an editor command: bogus commannnd", + string.match(rv, "E%d*:.*")) + eq("", nvim("eval", "v:errmsg")) -- v:errmsg was not updated. + -- Verify NO hit-enter prompt. + eq({mode='n', blocking=false}, nvim("get_mode")) + end) + + it("runtime error: fails (specific error)", function() + local status, rv = pcall(nvim, "command_output", "buffer 42") + eq(false, status) -- nvim_command_output() failed. + eq("E86: Buffer 42 does not exist", string.match(rv, "E%d*:.*")) + eq("", nvim("eval", "v:errmsg")) -- v:errmsg was not updated. + -- Verify NO hit-enter prompt. + eq({mode='n', blocking=false}, nvim("get_mode")) + end) end) describe('nvim_eval', function() diff --git a/test/functional/autocmd/tabclose_spec.lua b/test/functional/autocmd/tabclose_spec.lua index fb777e7eea..b7c33dc3d8 100644 --- a/test/functional/autocmd/tabclose_spec.lua +++ b/test/functional/autocmd/tabclose_spec.lua @@ -11,10 +11,10 @@ describe('TabClosed', function() repeat nvim('command', 'tabnew') until nvim('eval', 'tabpagenr()') == 6 -- current tab is now 6 - eq("\ntabclosed:6:6:5", nvim('command_output', 'tabclose')) -- close last 6, current tab is now 5 - eq("\ntabclosed:5:5:4", nvim('command_output', 'close')) -- close last window on tab, closes tab - eq("\ntabclosed:2:2:3", nvim('command_output', '2tabclose')) -- close tab 2, current tab is now 3 - eq("\ntabclosed:1:1:2\ntabclosed:1:1:1", nvim('command_output', 'tabonly')) -- close tabs 1 and 2 + eq("tabclosed:6:6:5", nvim('command_output', 'tabclose')) -- close last 6, current tab is now 5 + eq("tabclosed:5:5:4", nvim('command_output', 'close')) -- close last window on tab, closes tab + eq("tabclosed:2:2:3", nvim('command_output', '2tabclose')) -- close tab 2, current tab is now 3 + eq("tabclosed:1:1:2\ntabclosed:1:1:1", nvim('command_output', 'tabonly')) -- close tabs 1 and 2 end) it('is triggered when closing a window via bdelete from another tab', function() @@ -23,7 +23,7 @@ describe('TabClosed', function() nvim('command', '1tabedit Xtestfile') nvim('command', 'normal! 1gt') eq({1, 3}, nvim('eval', '[tabpagenr(), tabpagenr("$")]')) - eq("\ntabclosed:2:2:1\ntabclosed:2:2:1", nvim('command_output', 'bdelete Xtestfile')) + eq("tabclosed:2:2:1\ntabclosed:2:2:1", nvim('command_output', 'bdelete Xtestfile')) eq({1, 1}, nvim('eval', '[tabpagenr(), tabpagenr("$")]')) end) @@ -35,7 +35,7 @@ describe('TabClosed', function() -- Only one tab is closed, and the alternate file is used for the other. eq({2, 3}, nvim('eval', '[tabpagenr(), tabpagenr("$")]')) - eq("\ntabclosed:2:2:2", nvim('command_output', 'bdelete Xtestfile2')) + eq("tabclosed:2:2:2", nvim('command_output', 'bdelete Xtestfile2')) eq('Xtestfile1', nvim('eval', 'bufname("")')) end) end) @@ -48,9 +48,9 @@ describe('TabClosed', function() nvim('command', 'tabnew') until nvim('eval', 'tabpagenr()') == 7 -- current tab is now 7 -- sanity check, we shouldn't match on tabs with numbers other than 2 - eq("\ntabclosed:7:7:6", nvim('command_output', 'tabclose')) + eq("tabclosed:7:7:6", nvim('command_output', 'tabclose')) -- close tab page 2, current tab is now 5 - eq("\ntabclosed:2:2:5\ntabclosed:match", nvim('command_output', '2tabclose')) + eq("tabclosed:2:2:5\ntabclosed:match", nvim('command_output', '2tabclose')) end) end) @@ -59,7 +59,7 @@ describe('TabClosed', function() nvim('command', 'au! TabClosed * echom "tabclosed:".expand("").":".expand("").":".tabpagenr()') nvim('command', 'tabedit Xtestfile') eq({2, 2}, nvim('eval', '[tabpagenr(), tabpagenr("$")]')) - eq("\ntabclosed:2:2:1", nvim('command_output', 'close')) + eq("tabclosed:2:2:1", nvim('command_output', 'close')) eq({1, 1}, nvim('eval', '[tabpagenr(), tabpagenr("$")]')) end) end) diff --git a/test/functional/autocmd/tabnewentered_spec.lua b/test/functional/autocmd/tabnewentered_spec.lua index bdbe677132..59cac07b34 100644 --- a/test/functional/autocmd/tabnewentered_spec.lua +++ b/test/functional/autocmd/tabnewentered_spec.lua @@ -7,14 +7,14 @@ describe('TabNewEntered', function() it('matches when entering any new tab', function() clear() nvim('command', 'au! TabNewEntered * echom "tabnewentered:".tabpagenr().":".bufnr("")') - eq("\ntabnewentered:2:2", nvim('command_output', 'tabnew')) - eq("\n\"test.x2\" [New File]\ntabnewentered:3:3", nvim('command_output', 'tabnew test.x2')) + eq("tabnewentered:2:2", nvim('command_output', 'tabnew')) + eq("tabnewentered:3:3", nvim('command_output', 'tabnew test.x2')) end) end) describe('with FILE as ', function() it('matches when opening a new tab for FILE', function() nvim('command', 'au! TabNewEntered Xtest-tabnewentered echom "tabnewentered:match"') - eq('\n"Xtest-tabnewentered" [New File]\ntabnewentered:4:4\ntabnewentered:match', + eq('tabnewentered:4:4\ntabnewentered:match', nvim('command_output', 'tabnew Xtest-tabnewentered')) end) end) @@ -24,7 +24,7 @@ describe('TabNewEntered', function() nvim('command', 'au! TabNewEntered * echom "entered"') nvim('command', 'tabnew test.x2') nvim('command', 'split') - eq('\nentered', nvim('command_output', 'execute "normal \\T"')) + eq('entered', nvim('command_output', 'execute "normal \\T"')) end) end) end) diff --git a/test/functional/ex_cmds/sign_spec.lua b/test/functional/ex_cmds/sign_spec.lua index b37e6e8563..df0f5db860 100644 --- a/test/functional/ex_cmds/sign_spec.lua +++ b/test/functional/ex_cmds/sign_spec.lua @@ -16,8 +16,8 @@ describe('sign', function() nvim('command', 'sign place 34 line=3 name=Foo buffer='..buf2) -- now unplace without specifying a buffer nvim('command', 'sign unplace 34') - eq("\n--- Signs ---\n", nvim('command_output', 'sign place buffer='..buf1)) - eq("\n--- Signs ---\n", nvim('command_output', 'sign place buffer='..buf2)) + eq("--- Signs ---\n", nvim('command_output', 'sign place buffer='..buf1)) + eq("--- Signs ---\n", nvim('command_output', 'sign place buffer='..buf2)) end) end) end) diff --git a/test/functional/helpers.lua b/test/functional/helpers.lua index 31a2c3b3ff..dfc4694272 100644 --- a/test/functional/helpers.lua +++ b/test/functional/helpers.lua @@ -644,7 +644,7 @@ local function alter_slashes(obj) end local function hexdump(str) - local len = string.len( str ) + local len = string.len(str) local dump = "" local hex = "" local asc = "" @@ -652,22 +652,20 @@ local function hexdump(str) for i = 1, len do if 1 == i % 8 then dump = dump .. hex .. asc .. "\n" - hex = string.format( "%04x: ", i - 1 ) + hex = string.format("%04x: ", i - 1) asc = "" end - local ord = string.byte( str, i ) - hex = hex .. string.format( "%02x ", ord ) + local ord = string.byte(str, i) + hex = hex .. string.format("%02x ", ord) if ord >= 32 and ord <= 126 then - asc = asc .. string.char( ord ) + asc = asc .. string.char(ord) else asc = asc .. "." end end - return dump .. hex - .. string.rep( " ", 8 - len % 8 ) .. asc - + return dump .. hex .. string.rep(" ", 8 - len % 8) .. asc end local module = { diff --git a/test/functional/ui/cmdline_highlight_spec.lua b/test/functional/ui/cmdline_highlight_spec.lua index 73fe94c056..ffb6a26aef 100644 --- a/test/functional/ui/cmdline_highlight_spec.lua +++ b/test/functional/ui/cmdline_highlight_spec.lua @@ -849,7 +849,7 @@ describe('Ex commands coloring support', function() {EOB:~ }| | ]]) - eq('\nError detected while processing :\nE605: Exception not caught: 42', + eq('Error detected while processing :\nE605: Exception not caught: 42', meths.command_output('messages')) end) it('errors out when failing to get callback', function() -- cgit From 5055d4a755d3c100cddf51bded77abca04beb971 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 9 Jan 2018 10:36:25 +0100 Subject: api: nvim_command_output: direct impl --- test/functional/api/vim_spec.lua | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) (limited to 'test') diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua index d213c3c34e..39db831fe3 100644 --- a/test/functional/api/vim_spec.lua +++ b/test/functional/api/vim_spec.lua @@ -69,11 +69,36 @@ describe('api', function() eq({mode='n', blocking=false}, nvim("get_mode")) end) - it('returns command output', function() + it('captures command output', function() eq('this is\nspinal tap', nvim('command_output', [[echo "this is\nspinal tap"]])) end) + it('captures empty command output', function() + eq('', nvim('command_output', 'echo')) + end) + + it('captures single-char command output', function() + eq('x', nvim('command_output', 'echo "x"')) + end) + + it('captures multiple commands', function() + eq('foo\n 1 %a "[No Name]" line 1', + nvim('command_output', 'echo "foo" | ls')) + end) + + it('captures nested execute()', function() + eq('\nnested1\nnested2\n 1 %a "[No Name]" line 1', + nvim('command_output', + [[echo execute('echo "nested1\nnested2"') | ls]])) + end) + + it('captures nested nvim_command_output()', function() + eq('nested1\nnested2\n 1 %a "[No Name]" line 1', + nvim('command_output', + [[echo nvim_command_output('echo "nested1\nnested2"') | ls]])) + end) + it('does not return shell |:!| output', function() eq(':!echo "foo"\r\n', nvim('command_output', [[!echo "foo"]])) end) -- cgit From 9ea1752d60589e8fc5e7184144bc6d1c1b9f16a3 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 3 Jan 2018 00:00:16 +0300 Subject: *: Provide list length when allocating lists --- test/unit/eval/helpers.lua | 5 +++-- test/unit/eval/typval_spec.lua | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) (limited to 'test') diff --git a/test/unit/eval/helpers.lua b/test/unit/eval/helpers.lua index 6babd4be77..3d1c42c3a0 100644 --- a/test/unit/eval/helpers.lua +++ b/test/unit/eval/helpers.lua @@ -312,7 +312,7 @@ local lua2typvalt_type_tab = { processed[l].lv_refcount = processed[l].lv_refcount + 1 return typvalt(eval.VAR_LIST, {v_list=processed[l]}) end - local lst = populate_list(eval.tv_list_alloc(), l, processed) + local lst = populate_list(eval.tv_list_alloc(#l), l, processed) return typvalt(eval.VAR_LIST, {v_list=lst}) end, [dict_type] = function(l, processed) @@ -433,7 +433,8 @@ local function int(n) end local function list(...) - return populate_list(ffi.gc(eval.tv_list_alloc(), eval.tv_list_unref), + return populate_list(ffi.gc(eval.tv_list_alloc(select('#', ...)), + eval.tv_list_unref), {...}, {}) end diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index b668144175..919a42fbb9 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -2407,7 +2407,7 @@ describe('typval.c', function() describe('list ret()', function() itp('works', function() local rettv = typvalt(lib.VAR_UNKNOWN) - local l = lib.tv_list_alloc_ret(rettv) + local l = lib.tv_list_alloc_ret(rettv, 0) eq(empty_list, typvalt2lua(rettv)) eq(rettv.vval.v_list, l) end) -- cgit From f8f7f9d5f5aead86541ffcd12e291b6dfb6811e5 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Mon, 15 Jan 2018 23:14:20 +0100 Subject: vim-patch:8.0.0151,3,4 #7389 vim-patch:8.0.0151 Problem: To pass buffer content to system() and systemlist() one has to first create a string or list. Solution: Allow passing a buffer number. (LemonBoy, closes vim/vim#1240) https://github.com/vim/vim/commit/12c4492dd35e0cd83c8816be2ec849b836109882 vim-patch:8.0.0153 Problem: system() test fails on MS-Windows. Solution: Deal when extra space and CR. https://github.com/vim/vim/commit/9d9c35651712b88c81f1ae11091de1fd0bbbd35c vim-patch:8.0.0154 Problem: system() test fails on OS/X. Solution: Deal with leading spaces. https://github.com/vim/vim/commit/31f19ce0a052f7c76d44a9a190e468c79cf5d56d --- test/functional/eval/system_spec.lua | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'test') diff --git a/test/functional/eval/system_spec.lua b/test/functional/eval/system_spec.lua index e2b12b6bc9..4d1630042b 100644 --- a/test/functional/eval/system_spec.lua +++ b/test/functional/eval/system_spec.lua @@ -5,6 +5,7 @@ local eq, call, clear, eval, feed_command, feed, nvim = helpers.eq, helpers.call, helpers.clear, helpers.eval, helpers.feed_command, helpers.feed, helpers.nvim local command = helpers.command +local exc_exec = helpers.exc_exec local iswin = helpers.iswin local Screen = require('test.functional.ui.screen') @@ -274,9 +275,12 @@ describe('system()', function() end) end) - describe('input passed as Number', function() - it('stringifies the input', function() - eq('1', eval('system("cat", 1)')) + describe('Number input', function() + it('is treated as a buffer id', function() + command("put ='text in buffer 1'") + eq('\ntext in buffer 1\n', eval('system("cat", 1)')) + eq('Vim(echo):E86: Buffer 42 does not exist', + exc_exec('echo system("cat", 42)')) end) end) -- cgit From 514a51ef3e63362d9d96f23930bbde5cd8581da8 Mon Sep 17 00:00:00 2001 From: James McCoy Date: Mon, 15 Jan 2018 22:28:41 -0500 Subject: get_buffer_lines: Return a string, when requested, on invalid input Closes #7859 --- test/functional/eval/getline_spec.lua | 39 +++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 test/functional/eval/getline_spec.lua (limited to 'test') diff --git a/test/functional/eval/getline_spec.lua b/test/functional/eval/getline_spec.lua new file mode 100644 index 0000000000..8082745ec0 --- /dev/null +++ b/test/functional/eval/getline_spec.lua @@ -0,0 +1,39 @@ +local helpers = require('test.functional.helpers')(after_each) + +local call = helpers.call +local clear = helpers.clear +local eq = helpers.eq +local expect = helpers.expect + +describe('getline', function() + before_each(function() + clear() + call('setline', 1, {'a', 'b', 'c'}) + expect([[ + a + b + c]]) + end) + + it('returns empty string for invalid line', function() + eq('', call('getline', -1)) + eq('', call('getline', 0)) + eq('', call('getline', 4)) + end) + + it('returns empty list for invalid range', function() + eq({}, call('getline', 2, 1)) + eq({}, call('getline', -1, 1)) + eq({}, call('getline', 4, 4)) + end) + + it('returns value of valid line', function() + eq('b', call('getline', 2)) + eq('a', call('getline', '.')) + end) + + it('returns value of valid range', function() + eq({'a', 'b'}, call('getline', 1, 2)) + eq({'a', 'b', 'c'}, call('getline', 1, 4)) + end) +end) -- cgit From 1be315de37d663ba64c87ba7e647160f01ab48e2 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 16 Jan 2018 09:08:31 +0100 Subject: tui: final_column_wrap(): fix row calculation closes #7572 closes #7579 closes #7628 ASAN report: ==9500==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x6040000024c0 at pc 0x00000187d2ca bp 0x7fc3c6e58d10 sp 0x7fc3c6e58d08 READ of size 8 at 0x6040000024c0 thread T1 0 0x187d2c9 in ugrid_put /home/vagrant/neovim/build/../src/nvim/ugrid.c:107:17 1 0x1850adf in tui_put /home/vagrant/neovim/build/../src/nvim/tui/tui.c:1012:10 2 0x18a6ce6 in ui_bridge_put_event /home/vagrant/neovim/build/src/nvim/auto/ui_events_bridge.generated.h:154:3 3 0xa4dcda in multiqueue_process_events /home/vagrant/neovim/build/../src/nvim/event/multiqueue.c:150:7 4 0xa478bf in loop_poll_events /home/vagrant/neovim/build/../src/nvim/event/loop.c:63:3 5 0x185451c in tui_main /home/vagrant/neovim/build/../src/nvim/tui/tui.c:362:12 6 0x18a3080 in ui_thread_run /home/vagrant/neovim/build/../src/nvim/ui_bridge.c:106:3 7 0x7fc3caaac6b9 in start_thread (/lib/x86_64-linux-gnu/libpthread.so.0+0x76b9) 8 0x7fc3c9ca33dc in clone /build/glibc-bfm8X4/glibc-2.23/misc/../sysdeps/unix/sysv/linux/x86_64/clone.S:109 0x6040000024c0 is located 0 bytes to the right of 48-byte region [0x604000002490,0x6040000024c0) allocated by thread T1 here: 0 0x50e048 in malloc (/home/vagrant/neovim/build/bin/nvim+0x50e048) 1 0xf7ab71 in try_malloc /home/vagrant/neovim/build/../src/nvim/memory.c:87:15 2 0xf7ad99 in xmalloc /home/vagrant/neovim/build/../src/nvim/memory.c:121:15 3 0x187937b in ugrid_resize /home/vagrant/neovim/build/../src/nvim/ugrid.c:32:17 4 0x184be58 in tui_resize /home/vagrant/neovim/build/../src/nvim/tui/tui.c:770:3 5 0x18a3dc8 in ui_bridge_resize_event /home/vagrant/neovim/build/src/nvim/auto/ui_events_bridge.generated.h:4:3 6 0xa4dcda in multiqueue_process_events /home/vagrant/neovim/build/../src/nvim/event/multiqueue.c:150:7 7 0xa478bf in loop_poll_events /home/vagrant/neovim/build/../src/nvim/event/loop.c:63:3 8 0x185451c in tui_main /home/vagrant/neovim/build/../src/nvim/tui/tui.c:362:12 9 0x18a3080 in ui_thread_run /home/vagrant/neovim/build/../src/nvim/ui_bridge.c:106:3 10 0x7fc3caaac6b9 in start_thread (/lib/x86_64-linux-gnu/libpthread.so.0+0x76b9) Thread T1 created by T0 here: 0 0x4655ed in __interceptor_pthread_create (/home/vagrant/neovim/build/bin/nvim+0x4655ed) 1 0x1ad87b0 in uv_thread_create /home/vagrant/neovim/.deps/build/src/libuv/src/unix/thread.c:75 2 0x184b9aa in tui_start /home/vagrant/neovim/build/../src/nvim/tui/tui.c:159:10 3 0x188dd4c in ui_builtin_start /home/vagrant/neovim/build/../src/nvim/ui.c:125:3 4 0xe6d399 in main /home/vagrant/neovim/build/../src/nvim/main.c:457:5 5 0x7fc3c9bbc82f in __libc_start_main /build/glibc-bfm8X4/glibc-2.23/csu/../csu/libc-start.c:291 --- test/functional/terminal/tui_spec.lua | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) (limited to 'test') diff --git a/test/functional/terminal/tui_spec.lua b/test/functional/terminal/tui_spec.lua index 745bfeecf9..a62c03b70f 100644 --- a/test/functional/terminal/tui_spec.lua +++ b/test/functional/terminal/tui_spec.lua @@ -6,15 +6,18 @@ local helpers = require('test.functional.helpers')(after_each) local thelpers = require('test.functional.terminal.helpers') local Screen = require('test.functional.ui.screen') local eq = helpers.eq -local feed_data = thelpers.feed_data local feed_command = helpers.feed_command +local feed_data = thelpers.feed_data local clear = helpers.clear +local command = helpers.command +local eval = helpers.eval local nvim_dir = helpers.nvim_dir local retry = helpers.retry local nvim_prog = helpers.nvim_prog local nvim_set = helpers.nvim_set local ok = helpers.ok local read_file = helpers.read_file +local wait = helpers.wait if helpers.pending_win32(pending) then return end @@ -40,6 +43,28 @@ describe('tui', function() screen:detach() end) + it('rapid resize #7572 #7628', function() + -- Need buffer rows to provoke the behavior. + feed_data(":edit test/functional/fixtures/bigfile.txt:") + command('call jobresize(b:terminal_job_id, 58, 9)') + command('call jobresize(b:terminal_job_id, 62, 13)') + command('call jobresize(b:terminal_job_id, 100, 42)') + command('call jobresize(b:terminal_job_id, 37, 1000)') + -- Resize to <5 columns. + screen:try_resize(4, 44) + command('call jobresize(b:terminal_job_id, 4, 1000)') + -- Resize to 1 row, then to 1 column, then increase rows to 4. + screen:try_resize(44, 1) + command('call jobresize(b:terminal_job_id, 44, 1)') + screen:try_resize(1, 1) + command('call jobresize(b:terminal_job_id, 1, 1)') + screen:try_resize(1, 4) + command('call jobresize(b:terminal_job_id, 1, 4)') + screen:try_resize(57, 17) + command('call jobresize(b:terminal_job_id, 57, 17)') + eq(2, eval("1+1")) -- Still alive? + end) + it('accepts basic utf-8 input', function() feed_data('iabc\ntest1\ntest2') screen:expect([[ @@ -448,7 +473,7 @@ describe("tui 't_Co' (terminal colors)", function() nvim_set)) feed_data(":echo &t_Co\n") - helpers.wait() + wait() local tline if maxcolors == 8 or maxcolors == 16 then tline = "~ " -- cgit From bc17ad31dccb446fc24e0f6bb3cb2149ce951ae5 Mon Sep 17 00:00:00 2001 From: lePerdu Date: Wed, 18 Oct 2017 18:27:31 -0400 Subject: os/input.c: parse keycodes in non-string context #7411 cb02137dfac7 had two mistakes, of the same nature: trans_special() must be invoked with in_string=false unless the parsing context is a VimL string. replace_termcodes() and input_enqueue() are low-level mechanisms where VimL strings do not exist. keymap.c: adjust double-quote case to satisfy keymap_spec.lua closes #7410 --- test/functional/ex_cmds/map_spec.lua | 7 ++++ test/unit/keymap_spec.lua | 71 ++++++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+) create mode 100644 test/unit/keymap_spec.lua (limited to 'test') diff --git a/test/functional/ex_cmds/map_spec.lua b/test/functional/ex_cmds/map_spec.lua index b46f83405e..84d5bc2335 100644 --- a/test/functional/ex_cmds/map_spec.lua +++ b/test/functional/ex_cmds/map_spec.lua @@ -5,6 +5,7 @@ local feed = helpers.feed local meths = helpers.meths local clear = helpers.clear local command = helpers.command +local expect = helpers.expect describe(':*map', function() before_each(clear) @@ -18,4 +19,10 @@ describe(':*map', function() feed('\24\25') eq(4, meths.get_var('counter')) end) + + it(':imap ', function() + command('imap foo') + feed('i--') + expect('-foo-') + end) end) diff --git a/test/unit/keymap_spec.lua b/test/unit/keymap_spec.lua new file mode 100644 index 0000000000..595a19eb17 --- /dev/null +++ b/test/unit/keymap_spec.lua @@ -0,0 +1,71 @@ +local helpers = require("test.unit.helpers")(after_each) +local itp = helpers.gen_itp(it) + +local ffi = helpers.ffi +local eq = helpers.eq +local neq = helpers.neq + +local keymap = helpers.cimport("./src/nvim/keymap.h") + +describe('keymap.c', function() + + describe('find_special_key()', function() + local srcp = ffi.new('const unsigned char *[1]') + local modp = ffi.new('int[1]') + + itp('no keycode', function() + srcp[0] = 'abc' + eq(0, keymap.find_special_key(srcp, 3, modp, false, false, false)) + end) + + itp('keycode with multiple modifiers', function() + srcp[0] = '' + neq(0, keymap.find_special_key(srcp, 9, modp, false, false, false)) + neq(0, modp[0]) + end) + + itp('case-insensitive', function() + -- Compare other capitalizations to this. + srcp[0] = '' + local all_caps_key = + keymap.find_special_key(srcp, 5, modp, false, false, false) + local all_caps_mod = modp[0] + + srcp[0] = '' + eq(all_caps_key, + keymap.find_special_key(srcp, 5, modp, false, false, false)) + eq(all_caps_mod, modp[0]) + + srcp[0] = '' + eq(all_caps_key, + keymap.find_special_key(srcp, 5, modp, false, false, false)) + eq(all_caps_mod, modp[0]) + + srcp[0] = '' + eq(all_caps_key, + keymap.find_special_key(srcp, 5, modp, false, false, false)) + eq(all_caps_mod, modp[0]) + end) + + itp('double-quote in keycode #7411', function() + -- Unescaped with in_string=false + srcp[0] = '' + eq(string.byte('"'), + keymap.find_special_key(srcp, 5, modp, false, false, false)) + + -- Unescaped with in_string=true + eq(0, keymap.find_special_key(srcp, 5, modp, false, false, true)) + + -- Escaped with in_string=false + srcp[0] = '' + -- Should fail because the key is invalid + -- (more than 1 non-modifier character). + eq(0, keymap.find_special_key(srcp, 6, modp, false, false, false)) + + -- Escaped with in_string=true + eq(string.byte('"'), + keymap.find_special_key(srcp, 6, modp, false, false, true)) + end) + end) + +end) -- cgit From 12acf0f7a76160e33bb84876121d3a63dde3c252 Mon Sep 17 00:00:00 2001 From: George Zhao Date: Wed, 17 Jan 2018 19:40:31 +0800 Subject: Fix warning when assing size_t type value to uv_buf_t.len, convert type to ULONG on Windows. --- test/functional/fixtures/tty-test.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'test') diff --git a/test/functional/fixtures/tty-test.c b/test/functional/fixtures/tty-test.c index edcbe23f86..9c42ca28d0 100644 --- a/test/functional/fixtures/tty-test.c +++ b/test/functional/fixtures/tty-test.c @@ -94,7 +94,14 @@ static void read_cb(uv_stream_t *stream, ssize_t cnt, const uv_buf_t *buf) uv_tty_init(&write_loop, &out, fileno(stdout), 0); uv_write_t req; - uv_buf_t b = {.base = buf->base, .len = (size_t)cnt}; + uv_buf_t b = { + .base = buf->base, +#ifdef WIN32 + .len = (ULONG)cnt +#else + .len = (size_t)cnt +#endif + }; uv_write(&req, STRUCT_CAST(uv_stream_t, &out), &b, 1, NULL); uv_run(&write_loop, UV_RUN_DEFAULT); -- cgit From 15334dcd896812d01f81f7959a51a4c4f6929195 Mon Sep 17 00:00:00 2001 From: George Zhao Date: Wed, 17 Jan 2018 19:41:16 +0800 Subject: Fix warning unused static function --- test/functional/fixtures/tty-test.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'test') diff --git a/test/functional/fixtures/tty-test.c b/test/functional/fixtures/tty-test.c index 9c42ca28d0..4f0858acdb 100644 --- a/test/functional/fixtures/tty-test.c +++ b/test/functional/fixtures/tty-test.c @@ -41,6 +41,7 @@ static void walk_cb(uv_handle_t *handle, void *arg) } } +#ifndef WIN32 static void sig_handler(int signum) { switch (signum) { @@ -57,6 +58,7 @@ static void sig_handler(int signum) return; } } +#endif #ifdef WIN32 static void sigwinch_cb(uv_signal_t *handle, int signum) -- cgit From 72a7a884b4a5f1642726403a3cc997d82bb616f4 Mon Sep 17 00:00:00 2001 From: Marco Hinz Date: Thu, 18 Jan 2018 15:54:25 +0100 Subject: tests: :checkhealth completion --- test/functional/plugin/health_spec.lua | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'test') diff --git a/test/functional/plugin/health_spec.lua b/test/functional/plugin/health_spec.lua index 8ee0f258d0..f2d5e433db 100644 --- a/test/functional/plugin/health_spec.lua +++ b/test/functional/plugin/health_spec.lua @@ -6,6 +6,7 @@ local clear = helpers.clear local curbuf_contents = helpers.curbuf_contents local command = helpers.command local eq = helpers.eq +local getcompletion = helpers.funcs.getcompletion describe(':checkhealth', function() it("detects invalid $VIMRUNTIME", function() @@ -31,6 +32,11 @@ describe(':checkhealth', function() eq("ERROR: $VIM is invalid: zub", string.match(curbuf_contents(), "ERROR: $VIM .* zub")) end) + it('completions can be listed via getcompletion()', function() + clear() + eq('nvim', getcompletion('nvim', 'checkhealth')[1]) + eq('provider', getcompletion('prov', 'checkhealth')[1]) + end) end) describe('health.vim', function() -- cgit From b00fd496405e0864e3cb5cc8b62241fb9610480f Mon Sep 17 00:00:00 2001 From: ckelsel Date: Sat, 20 Jan 2018 15:47:56 +0800 Subject: vim-patch:8.0.0423: changing 'cinoptions' does not always work Problem: The effect of adding "vim/vim#" to 'cinoptions' is not always removed. (David Briscoe) Solution: Reset b_ind_hash_comment. (Christian Brabandt, closes vim/vim#1475) https://github.com/vim/vim/commit/6b64394f346594404cffb9591d71ac693040679f --- test/functional/legacy/003_cindent_spec.lua | 1 + 1 file changed, 1 insertion(+) (limited to 'test') diff --git a/test/functional/legacy/003_cindent_spec.lua b/test/functional/legacy/003_cindent_spec.lua index 58e87354fb..13726050b2 100644 --- a/test/functional/legacy/003_cindent_spec.lua +++ b/test/functional/legacy/003_cindent_spec.lua @@ -1,4 +1,5 @@ -- Test for 'cindent'. +-- For new tests, consider putting them in test_cindent.vim. -- -- There are 50+ test command blocks (the stuff between STARTTEST and ENDTEST) -- in the original test. These have been converted to "it" test cases here. -- cgit From eb4aab7173fa1733f77bb6d7117351b47ada6134 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sun, 21 Jan 2018 07:31:57 +0100 Subject: ui: forward 'linespace' option #7883 ref #7520 --- test/functional/ui/options_spec.lua | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'test') diff --git a/test/functional/ui/options_spec.lua b/test/functional/ui/options_spec.lua index 14f40b3ec1..bc72ca71aa 100644 --- a/test/functional/ui/options_spec.lua +++ b/test/functional/ui/options_spec.lua @@ -24,6 +24,7 @@ describe('ui receives option updates', function() guifont='', guifontset='', guifontwide='', + linespace=0, showtabline=1, termguicolors=false, } @@ -58,6 +59,18 @@ describe('ui receives option updates', function() eq(changed, screen.options) end) + command("set linespace=13") + changed.linespace = 13 + screen:expect(function() + eq(changed, screen.options) + end) + + command("set linespace=-11") + changed.linespace = -11 + screen:expect(function() + eq(changed, screen.options) + end) + command("set all&") screen:expect(function() eq(defaults, screen.options) -- cgit From 7dd97eb5976af71e50aba6f4a8ad31b48b23d460 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 23 Jan 2018 00:53:22 +0100 Subject: test/inccommand_spec: test the test (#7897) --- test/functional/ui/inccommand_spec.lua | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) (limited to 'test') diff --git a/test/functional/ui/inccommand_spec.lua b/test/functional/ui/inccommand_spec.lua index 4a8ebf6212..74ab9864f0 100644 --- a/test/functional/ui/inccommand_spec.lua +++ b/test/functional/ui/inccommand_spec.lua @@ -12,6 +12,7 @@ local insert = helpers.insert local meths = helpers.meths local neq = helpers.neq local ok = helpers.ok +local retry = helpers.retry local source = helpers.source local wait = helpers.wait local nvim = helpers.nvim @@ -91,22 +92,30 @@ local function common_setup(screen, inccommand, text) end end -describe(":substitute, inccommand=split does not trigger preview", function() +describe(":substitute, inccommand=split", function() before_each(function() clear() common_setup(nil, "split", default_text) end) - it("if invoked by a script ", function() + -- Test the tests: verify that the `1==bufnr('$')` assertion + -- in the "no preview" tests (below) actually means something. + it("previews interactive cmdline", function() + feed(':%s/tw/MO/g') + retry(nil, 1000, function() + eq(2, eval("bufnr('$')")) + end) + end) + + it("no preview if invoked by a script", function() source('%s/tw/MO/g') wait() eq(1, eval("bufnr('$')")) - -- sanity check: assert the buffer state expect(default_text:gsub("tw", "MO")) end) - it("if invoked by feedkeys()", function() + it("no preview if invoked by feedkeys()", function() -- in a script... source([[:call feedkeys(":%s/tw/MO/g\")]]) wait() @@ -114,7 +123,6 @@ describe(":substitute, inccommand=split does not trigger preview", function() feed([[:call feedkeys(":%s/tw/MO/g\")]]) wait() eq(1, eval("bufnr('$')")) - -- sanity check: assert the buffer state expect(default_text:gsub("tw", "MO")) end) -- cgit From 15119f943ab8c2d089f45969da06b6b58b4d0036 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 23 Jan 2018 21:33:44 +0100 Subject: test: system(): fix test For the test to be valid it should actually send input. ref #3529 ref #5241 --- test/functional/eval/system_spec.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'test') diff --git a/test/functional/eval/system_spec.lua b/test/functional/eval/system_spec.lua index 4d1630042b..66d569416e 100644 --- a/test/functional/eval/system_spec.lua +++ b/test/functional/eval/system_spec.lua @@ -251,7 +251,7 @@ describe('system()', function() end) it('to backgrounded command does not crash', function() -- This is indeterminate, just exercise the codepath. May get E5677. - feed_command('call system("cat - &")') + feed_command('call system("cat - &", "input")') local v_errnum = string.match(eval("v:errmsg"), "^E%d*:") if v_errnum then eq("E5677:", v_errnum) -- cgit From 3ff92ba1ee54a9400aac143f4d98d356dc6a8321 Mon Sep 17 00:00:00 2001 From: James McCoy Date: Tue, 23 Jan 2018 17:56:50 -0500 Subject: eval: save_tv_as_string: Correctly handle an empty string When tv_get_string_chk returns a non-NULL value, we have a valid string. Propagating an error state (*len = -1, NULL return) for an empty string is invalid. Closes #6554 --- test/functional/eval/system_spec.lua | 3 +++ 1 file changed, 3 insertions(+) (limited to 'test') diff --git a/test/functional/eval/system_spec.lua b/test/functional/eval/system_spec.lua index 66d569416e..77e7424452 100644 --- a/test/functional/eval/system_spec.lua +++ b/test/functional/eval/system_spec.lua @@ -258,6 +258,9 @@ describe('system()', function() end eq(2, eval("1+1")) -- Still alive? end) + it('works with an empty string', function() + eq("test\n", eval('system("echo test", "")')) + end) end) describe('passing a lot of input', function() -- cgit From 41394d82365b504c89bb4da9ed5adc11c6f619f0 Mon Sep 17 00:00:00 2001 From: Ömer Sinan Ağacan Date: Mon, 30 Oct 2017 11:07:35 +0300 Subject: vim-patch:8.0.1238 Problem: Incremental search only shows one match. Solution: When 'incsearch' and and 'hlsearch' are both set highlight all matches. (haya14busa, closes vim/vim#2198) https://github.com/vim/vim/commit/2e51d9a0972080b087d566608472928d5b7b35d7 --- test/functional/ui/searchhl_spec.lua | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) (limited to 'test') diff --git a/test/functional/ui/searchhl_spec.lua b/test/functional/ui/searchhl_spec.lua index 5af8b83a36..950989aab2 100644 --- a/test/functional/ui/searchhl_spec.lua +++ b/test/functional/ui/searchhl_spec.lua @@ -99,7 +99,7 @@ describe('search highlighting', function() feed("gg/li") screen:expect([[ the first {3:li}ne | - in a little file | + in a {2:li}ttle file | | {1:~ }| {1:~ }| @@ -132,7 +132,7 @@ describe('search highlighting', function() feed("/fir") screen:expect([[ the {3:fir}st line | - in a {2:lit}tle file | + in a little file | | {1:~ }| {1:~ }| @@ -144,13 +144,25 @@ describe('search highlighting', function() feed("/ttle") screen:expect([[ the first line | - in a {2:li}{3:ttle} file | + in a li{3:ttle} file | | {1:~ }| {1:~ }| {1:~ }| /ttle^ | ]]) + + -- cancelling search resets to the old search term + feed('') + screen:expect([[ + the first line | + in a {2:^lit}tle file | + | + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]) end) it('works with incsearch and offset', function() @@ -163,7 +175,7 @@ describe('search highlighting', function() feed("gg/mat/e") screen:expect([[ not the {3:mat}ch you're looking for | - the match is here | + the {2:mat}ch is here | {1:~ }| {1:~ }| {1:~ }| @@ -174,7 +186,7 @@ describe('search highlighting', function() -- Search with count and /e offset fixed in Vim patch 7.4.532. feed("2/mat/e") screen:expect([[ - not the match you're looking for | + not the {2:mat}ch you're looking for | the {3:mat}ch is here | {1:~ }| {1:~ }| -- cgit From 997fc8b13371fac89f8e445275c4aaad2afd37a4 Mon Sep 17 00:00:00 2001 From: Ömer Sinan Ağacan Date: Fri, 3 Nov 2017 10:42:26 +0300 Subject: Add tests --- test/functional/ui/searchhl_spec.lua | 80 ++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) (limited to 'test') diff --git a/test/functional/ui/searchhl_spec.lua b/test/functional/ui/searchhl_spec.lua index 950989aab2..9fe9c2e6bd 100644 --- a/test/functional/ui/searchhl_spec.lua +++ b/test/functional/ui/searchhl_spec.lua @@ -107,6 +107,29 @@ describe('search highlighting', function() /li^ | ]]) + -- check that consecutive matches are caught by C-g/C-t + feed("") + screen:expect([[ + the first {2:li}ne | + in a {3:li}ttle file | + | + {1:~ }| + {1:~ }| + {1:~ }| + /li^ | + ]]) + + feed("") + screen:expect([[ + the first {3:li}ne | + in a {2:li}ttle file | + | + {1:~ }| + {1:~ }| + {1:~ }| + /li^ | + ]]) + feed("t") screen:expect([[ the first line | @@ -163,6 +186,63 @@ describe('search highlighting', function() {1:~ }| | ]]) + + -- cancelling inc search restores the hl state + feed(':noh') + screen:expect([[ + the first line | + in a ^little file | + | + {1:~ }| + {1:~ }| + {1:~ }| + :noh | + ]]) + + feed('/first') + screen:expect([[ + the {3:first} line | + in a little file | + | + {1:~ }| + {1:~ }| + {1:~ }| + /first^ | + ]]) + feed('') + screen:expect([[ + the first line | + in a ^little file | + | + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]) + + -- test that pressing C-g in an empty command line does not move the cursor + feed('/') + screen:expect([[ + the first line | + in a little file | + | + {1:~ }| + {1:~ }| + {1:~ }| + /^ | + ]]) + + -- same, for C-t + feed('/') + screen:expect([[ + the first line | + in a little file | + | + {1:~ }| + {1:~ }| + {1:~ }| + /^ | + ]]) end) it('works with incsearch and offset', function() -- cgit From 9bc1410ee1a467a8058a8de585c0e68d64ef8521 Mon Sep 17 00:00:00 2001 From: Ömer Sinan Ağacan Date: Sun, 21 Jan 2018 15:09:17 +0300 Subject: vim-patch:8.0.1304: CTRL-G/CTRL-T don't work with incsearch and empty pattern Problem: CTRL-G/CTRL-T don't work with incsearch and empty pattern. Solution: Use the last search pattern. (Christian Brabandt, closes vim/vim#2292) https://github.com/vim/vim/commit/d0480097177369a6ed91d47aba189ae647afcd68 --- test/functional/ui/searchhl_spec.lua | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) (limited to 'test') diff --git a/test/functional/ui/searchhl_spec.lua b/test/functional/ui/searchhl_spec.lua index 9fe9c2e6bd..b15a577585 100644 --- a/test/functional/ui/searchhl_spec.lua +++ b/test/functional/ui/searchhl_spec.lua @@ -243,6 +243,30 @@ describe('search highlighting', function() {1:~ }| /^ | ]]) + + -- 8.0.1304, test that C-g and C-t works with incsearch and empty pattern + feed('/fi') + feed('//') + screen:expect([[ + the {3:fi}rst line | + in a little {2:fi}le | + | + {1:~ }| + {1:~ }| + {1:~ }| + //^ | + ]]) + + feed('') + screen:expect([[ + the {2:fi}rst line | + in a little {3:fi}le | + | + {1:~ }| + {1:~ }| + {1:~ }| + //^ | + ]]) end) it('works with incsearch and offset', function() -- cgit From f2ea15f36485b07a8754210c4ca5a08a0df076f0 Mon Sep 17 00:00:00 2001 From: Matthew Malcomson Date: Tue, 4 Apr 2017 14:07:08 +0100 Subject: Add some tests for 'langmap' --- test/functional/normal/langmap_spec.lua | 280 ++++++++++++++++++++++++++++++++ 1 file changed, 280 insertions(+) create mode 100644 test/functional/normal/langmap_spec.lua (limited to 'test') diff --git a/test/functional/normal/langmap_spec.lua b/test/functional/normal/langmap_spec.lua new file mode 100644 index 0000000000..e4349a22e7 --- /dev/null +++ b/test/functional/normal/langmap_spec.lua @@ -0,0 +1,280 @@ +local helpers = require('test.functional.helpers')(after_each) + +local eq, neq, call = helpers.eq, helpers.neq, helpers.call +local eval, feed, clear = helpers.eval, helpers.feed, helpers.clear +local command, insert, expect = helpers.command, helpers.insert, helpers.expect +local feed_command = helpers.feed_command +local curwin = helpers.curwin + +describe("'langmap'", function() + before_each(function() + clear() + insert('iii www') + command('set langmap=iw,wi') + feed('gg0') + end) + + it("converts keys in normal mode", function() + feed('ix') + expect('iii ww') + feed('whello') + expect('iii helloww') + end) + it("gives characters that are mapped by :nmap.", function() + command('map i 0x') + feed('w') + expect('ii www') + end) + describe("'langnoremap' option.", function() + before_each(function() + command('nmapclear') + end) + it("'langnoremap' is by default ON", function() + eq(eval('&langnoremap'), 1) + end) + it("Results of maps are not converted when 'langnoremap' ON.", + function() + command('nmap x i') + feed('xdl') + expect('dliii www') + end) + it("applies when deciding whether to map recursively", function() + command('nmap l i') + command('nmap w j') + feed('ll') + expect('liii www') + end) + it("does not stop applying 'langmap' on first character of a mapping", + function() + command('1t1') + command('1t1') + command('goto 1') + command('nmap w j') + feed('iiahello') + expect([[ + iii www + iii www + ihelloii www]]) + end) + it("Results of maps are converted when 'langnoremap' OFF.", + function() + command('set nolangnoremap') + command('nmap x i') + feed('xdl') + expect('iii ww') + end) + end) + -- e.g. CTRL-W_j , mj , 'j and "jp + it('conversions are applied to keys in middle of command', + function() + -- Works in middle of window command + feed('s') + local origwin = curwin() + feed('i') + neq(curwin(), origwin) + -- Works when setting a mark + feed('yy3p3gg0mwgg0mi') + eq(call('getpos', "'i"), {0, 3, 1, 0}) + eq(call('getpos', "'w"), {0, 1, 1, 0}) + feed('3dd') + -- Works when moving to a mark + feed("'i") + eq(call('getpos', '.'), {0, 1, 1, 0}) + -- Works when selecting a register + feed('qillqqwhhq') + eq(eval('@i'), 'hh') + eq(eval('@w'), 'll') + feed('ai') + expect('illii www') + feed('"ip') + expect('illllii www') + -- Works with i_CTRL-O + feed('0aihi') + expect('illllii hiwww') + end) + + describe('exceptions', function() + -- All "command characters" that 'langmap' does not apply to. + -- These tests consist of those places where some subset of ASCII + -- characters define certain commands, yet 'langmap' is not applied to + -- them. + -- n.b. I think these shouldn't be exceptions. + it(':s///c confirmation', function() + command('set langmap=yn,ny') + feed('qa') + feed_command('s/i/w/gc') + feed('yynq') + expect('wwi www') + feed('u@a') + expect('wwi www') + eq(eval('@a'), ':s/i/w/gc\ryyn') + end) + it('insert-mode CTRL-G', function() + command('set langmap=jk,kj') + command('d') + insert([[ + hello + hello + hello]]) + expect([[ + hello + hello + hello]]) + feed('qa') + feed('gg3|ahellojx') + feed('q') + expect([[ + helhellolo + helxlo + hello]]) + eq(eval('@a'), 'gg3|ahellojx') + end) + it('command-line CTRL-\\', function() + command('set langmap=en,ne') + feed(':e\'hello\'\rput =""') + expect([[ + iii www + hello]]) + end) + it('command-line CTRL-R', function() + helpers.source([[ + let i_value = 0 + let j_value = 0 + call setreg('i', 'i_value') + call setreg('j', 'j_value') + set langmap=ij,ji + ]]) + feed(':let i=1') + eq(eval('i_value'), 1) + eq(eval('j_value'), 0) + end) + -- it('-- More -- prompt', function() + -- -- The 'b' 'j' 'd' 'f' commands at the -- More -- prompt + -- end) + it('ask yes/no after backwards range', function() + command('set langmap=yn,ny') + feed('dd') + insert([[ + hello + there + these + are + some + lines + ]]) + feed_command('4,2d') + feed('n') + expect([[ + hello + there + these + are + some + lines + ]]) + end) + it('prompt for number', function() + command('set langmap=12,21') + helpers.source([[ + let gotten_one = 0 + function Map() + let answer = inputlist(['a', '1.', '2.', '3.']) + if answer == 1 + let g:gotten_one = 1 + endif + endfunction + nnoremap x :call Map() + ]]) + feed('x1') + eq(eval('gotten_one'), 1) + command('let g:gotten_one = 0') + feed_command('call Map()') + feed('1') + eq(eval('gotten_one'), 1) + end) + end) + it('conversions are not applied during setreg()', + function() + call('setreg', 'i', 'ww') + eq(eval('@i'), 'ww') + end) + it('conversions not applied in insert mode', function() + feed('aiiiwww') + expect('iiiiwwwii www') + end) + it('conversions not applied in search mode', function() + feed('/iiix') + expect('ii www') + end) + it('conversions not applied in cmdline mode', function() + feed(':call append(1, "iii")') + expect([[ + iii www + iii]]) + end) + + local function testrecording(command_string, expect_string, setup_function) + if setup_function then setup_function() end + feed('qa' .. command_string .. 'q') + expect(expect_string) + eq(helpers.funcs.nvim_replace_termcodes(command_string, true, true, true), + eval('@a')) + if setup_function then setup_function() end + -- n.b. may need nvim_replace_termcodes() here. + feed('@a') + expect(expect_string) + end + + local function local_setup() + -- Can't use `insert` as it uses `i` and we've swapped the meaning of that + -- with the `langmap` setting. + command('%d') + command("put ='hello'") + command('1d') + end + + it('does not affect recording special keys', function() + testrecording('A', 'hell', local_setup) + testrecording('>>', 'hello', local_setup) + command('nnoremap \\ x') + testrecording('\\', 'ello', local_setup) + testrecording('A', 'hello', local_setup) + end) + pending('Translates modified keys correctly', function() + command('nnoremap x') + command('nnoremap l') + testrecording('', 'ello', local_setup) + testrecording('x', 'hllo', local_setup) + end) + pending('handles multi-byte characters', function() + command('set langmap=ïx') + testrecording('ï', 'ello', local_setup) + -- The test below checks that what's recorded is correct. + -- It doesn't check the behaviour, as in order to cause some behaviour we + -- need to map the multi-byte character, and there is a known bug + -- preventing this from working (see the test below). + command('set langmap=xï') + testrecording('x', 'hello', local_setup) + end) + pending('handles multibyte mappings', function() + -- See this vim issue for the problem, may as well add a test. + -- https://github.com/vim/vim/issues/297 + command('set langmap=ïx') + command('nnoremap x diw') + testrecording('ï', '', local_setup) + command('set nolangnoremap') + command('set langmap=xï') + command('nnoremap ï ix') + testrecording('x', 'xhello', local_setup) + end) + -- This test is to ensure the behaviour doesn't change from what's already + -- around. I (hardenedapple) personally think this behaviour should be + -- changed. + it('treats control modified keys as characters', function() + command('nnoremap iw') + command('nnoremap ii') + testrecording('', 'whello', local_setup) + testrecording('', 'ihello', local_setup) + end) + +end) -- cgit From 499c9a15531b7a0e9736a395e8d401ceab3d24d2 Mon Sep 17 00:00:00 2001 From: George Zhao Date: Wed, 24 Jan 2018 18:01:14 +0800 Subject: test/win: fix some environment assumptions #7912 fix #7909 fix #7910 --- test/functional/core/job_spec.lua | 3 ++- test/functional/eval/hostname_spec.lua | 5 ++++- test/functional/helpers.lua | 6 ++++++ 3 files changed, 12 insertions(+), 2 deletions(-) (limited to 'test') diff --git a/test/functional/core/job_spec.lua b/test/functional/core/job_spec.lua index e957650c88..a02d36c939 100644 --- a/test/functional/core/job_spec.lua +++ b/test/functional/core/job_spec.lua @@ -9,6 +9,7 @@ local command = helpers.command local wait = helpers.wait local iswin = helpers.iswin local get_pathsep = helpers.get_pathsep +local pathroot = helpers.pathroot local nvim_set = helpers.nvim_set local expect_twostreams = helpers.expect_twostreams local Screen = require('test.functional.ui.screen') @@ -63,7 +64,7 @@ describe('jobs', function() nvim('command', "let j = jobstart('pwd', g:job_opts)") end eq({'notification', 'stdout', - {0, {(iswin() and [[C:\]] or '/'), ''}}}, next_msg()) + {0, {pathroot(), ''}}}, next_msg()) eq({'notification', 'stdout', {0, {''}}}, next_msg()) eq({'notification', 'exit', {0, 0}}, next_msg()) end) diff --git a/test/functional/eval/hostname_spec.lua b/test/functional/eval/hostname_spec.lua index 6d5b64b929..6112cf64e3 100644 --- a/test/functional/eval/hostname_spec.lua +++ b/test/functional/eval/hostname_spec.lua @@ -1,7 +1,9 @@ local helpers = require('test.functional.helpers')(after_each) +local eq = helpers.eq local ok = helpers.ok local call = helpers.call local clear = helpers.clear +local iswin = helpers.iswin describe('hostname()', function() before_each(clear) @@ -11,7 +13,8 @@ describe('hostname()', function() ok(string.len(actual) > 0) if call('executable', 'hostname') == 1 then local expected = string.gsub(call('system', 'hostname'), '[\n\r]', '') - helpers.eq(expected, actual) + eq((iswin() and expected:upper() or expected), + (iswin() and actual:upper() or actual)) end end) end) diff --git a/test/functional/helpers.lua b/test/functional/helpers.lua index dfc4694272..f0e47481da 100644 --- a/test/functional/helpers.lua +++ b/test/functional/helpers.lua @@ -603,6 +603,11 @@ local function get_pathsep() return funcs.fnamemodify('.', ':p'):sub(-1) end +local function pathroot() + local pathsep = package.config:sub(1,1) + return iswin() and (nvim_dir:sub(1,2)..pathsep) or '/' +end + -- Returns a valid, platform-independent $NVIM_LISTEN_ADDRESS. -- Useful for communicating with child instances. local function new_pipename() @@ -736,6 +741,7 @@ local module = { meth_pcall = meth_pcall, NIL = mpack.NIL, get_pathsep = get_pathsep, + pathroot = pathroot, missing_provider = missing_provider, alter_slashes = alter_slashes, hexdump = hexdump, -- cgit From afbdafffc2980c2879393ef03fc3edac49282ea7 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 9 Jan 2018 01:06:59 +0100 Subject: test: fnamemodify() --- test/functional/eval/fnamemodify_spec.lua | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 test/functional/eval/fnamemodify_spec.lua (limited to 'test') diff --git a/test/functional/eval/fnamemodify_spec.lua b/test/functional/eval/fnamemodify_spec.lua new file mode 100644 index 0000000000..4f86626a1e --- /dev/null +++ b/test/functional/eval/fnamemodify_spec.lua @@ -0,0 +1,21 @@ +local helpers = require('test.functional.helpers')(after_each) +local clear = helpers.clear +local eq = helpers.eq +local iswin = helpers.iswin +local fnamemodify = helpers.funcs.fnamemodify + +describe('fnamemodify()', function() + before_each(clear) + + it('works', function() + if iswin() then + eq([[C:\]], fnamemodify([[\]], ':p:h')) + eq([[C:\]], fnamemodify([[\]], ':p')) + eq([[C:/]], fnamemodify([[/]], ':p:h')) + eq([[C:/]], fnamemodify([[/]], ':p')) + else + eq('/', fnamemodify([[/]], ':p:h')) + eq('/', fnamemodify([[/]], ':p')) + end + end) +end) -- cgit From a619c3fcf92e45e27066c472c3c9a0a1a2901b4d Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Thu, 11 Jan 2018 11:05:15 -0500 Subject: test: win: add tests for shellslash --- test/functional/eval/fnamemodify_spec.lua | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'test') diff --git a/test/functional/eval/fnamemodify_spec.lua b/test/functional/eval/fnamemodify_spec.lua index 4f86626a1e..f3705d6c99 100644 --- a/test/functional/eval/fnamemodify_spec.lua +++ b/test/functional/eval/fnamemodify_spec.lua @@ -3,6 +3,7 @@ local clear = helpers.clear local eq = helpers.eq local iswin = helpers.iswin local fnamemodify = helpers.funcs.fnamemodify +local command = helpers.command describe('fnamemodify()', function() before_each(clear) @@ -11,6 +12,11 @@ describe('fnamemodify()', function() if iswin() then eq([[C:\]], fnamemodify([[\]], ':p:h')) eq([[C:\]], fnamemodify([[\]], ':p')) + eq([[C:\]], fnamemodify([[/]], ':p:h')) + eq([[C:\]], fnamemodify([[/]], ':p')) + command('set shellslash') + eq([[C:/]], fnamemodify([[\]], ':p:h')) + eq([[C:/]], fnamemodify([[\]], ':p')) eq([[C:/]], fnamemodify([[/]], ':p:h')) eq([[C:/]], fnamemodify([[/]], ':p')) else -- cgit From 534abe4aafc2fb75a100c6193a8fdf5bfa0838dc Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Thu, 11 Jan 2018 17:31:29 -0500 Subject: test: win: get current network drive via io.popen --- test/functional/eval/fnamemodify_spec.lua | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) (limited to 'test') diff --git a/test/functional/eval/fnamemodify_spec.lua b/test/functional/eval/fnamemodify_spec.lua index f3705d6c99..21e670c41c 100644 --- a/test/functional/eval/fnamemodify_spec.lua +++ b/test/functional/eval/fnamemodify_spec.lua @@ -9,16 +9,22 @@ describe('fnamemodify()', function() before_each(clear) it('works', function() + local drive_f = io.popen('cd', 'r') + local drive = string.gsub(drive_f:read('*a'), '[\n\r]', '') + drive_f:close() + if iswin() then - eq([[C:\]], fnamemodify([[\]], ':p:h')) - eq([[C:\]], fnamemodify([[\]], ':p')) - eq([[C:\]], fnamemodify([[/]], ':p:h')) - eq([[C:\]], fnamemodify([[/]], ':p')) + local root = drive..[[\]] + eq(root, fnamemodify([[\]], ':p:h')) + eq(root, fnamemodify([[\]], ':p')) + eq(root, fnamemodify([[/]], ':p:h')) + eq(root, fnamemodify([[/]], ':p')) command('set shellslash') - eq([[C:/]], fnamemodify([[\]], ':p:h')) - eq([[C:/]], fnamemodify([[\]], ':p')) - eq([[C:/]], fnamemodify([[/]], ':p:h')) - eq([[C:/]], fnamemodify([[/]], ':p')) + root = drive..[[/]] + eq(root, fnamemodify([[\]], ':p:h')) + eq(root, fnamemodify([[\]], ':p')) + eq(root, fnamemodify([[/]], ':p:h')) + eq(root, fnamemodify([[/]], ':p')) else eq('/', fnamemodify([[/]], ':p:h')) eq('/', fnamemodify([[/]], ':p')) -- cgit From eb59dd654759c3ecde7bea22e2651713e29d0912 Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Thu, 11 Jan 2018 19:16:39 -0500 Subject: fixup: get network drive only, not entire path --- test/functional/eval/fnamemodify_spec.lua | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'test') diff --git a/test/functional/eval/fnamemodify_spec.lua b/test/functional/eval/fnamemodify_spec.lua index 21e670c41c..2225c5da7c 100644 --- a/test/functional/eval/fnamemodify_spec.lua +++ b/test/functional/eval/fnamemodify_spec.lua @@ -9,11 +9,10 @@ describe('fnamemodify()', function() before_each(clear) it('works', function() - local drive_f = io.popen('cd', 'r') - local drive = string.gsub(drive_f:read('*a'), '[\n\r]', '') - drive_f:close() - if iswin() then + local drive_f = io.popen('for %P in (%CD%) do @echo %~dP', 'r') + local drive = string.gsub(drive_f:read('*a'), '[\n\r]', '') + drive_f:close() local root = drive..[[\]] eq(root, fnamemodify([[\]], ':p:h')) eq(root, fnamemodify([[\]], ':p')) -- cgit From 984a93df96f91af8343d62c145aee802cc1638f4 Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Sat, 14 Oct 2017 23:59:09 -0400 Subject: win: enable legacy/fnamemodify_spec.lua --- test/functional/legacy/fnamemodify_spec.lua | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) (limited to 'test') diff --git a/test/functional/legacy/fnamemodify_spec.lua b/test/functional/legacy/fnamemodify_spec.lua index d8ecbfe058..d8f812d77e 100644 --- a/test/functional/legacy/fnamemodify_spec.lua +++ b/test/functional/legacy/fnamemodify_spec.lua @@ -4,8 +4,6 @@ local helpers = require('test.functional.helpers')(after_each) local clear, source = helpers.clear, helpers.source local call, eq, nvim = helpers.call, helpers.eq, helpers.meths -if helpers.pending_win32(pending) then return end - local function expected_empty() eq({}, nvim.get_vvar('errors')) end @@ -16,17 +14,27 @@ describe('filename modifiers', function() source([=[ func Test_fnamemodify() - let tmpdir = resolve('/tmp') + if has('win32') + set shellslash + let tmpdir = expand('$LOCALAPPDATA/Temp') + call assert_equal(tmpdir, expand('$USERPROFILE/AppData/Local/Temp')) + else + let tmpdir = resolve('/tmp') + set shell=sh + endif + call assert_true(isdirectory(tmpdir)) execute 'cd '. tmpdir - set shell=sh - set shellslash let $HOME=fnamemodify('.', ':p:h:h:h') call assert_equal('/', fnamemodify('.', ':p')[-1:]) call assert_equal('p', fnamemodify('.', ':p:h')[-1:]) call assert_equal('t', fnamemodify('test.out', ':p')[-1:]) call assert_equal('test.out', fnamemodify('test.out', ':.')) call assert_equal('../testdir/a', fnamemodify('../testdir/a', ':.')) - call assert_equal('test.out', fnamemodify('test.out', ':~')) + if has('win32') + call assert_equal('~/Local/Temp/test.out', fnamemodify('test.out', ':~')) + else + call assert_equal('test.out', fnamemodify('test.out', ':~')) + endif call assert_equal('../testdir/a', fnamemodify('../testdir/a', ':~')) call assert_equal('a', fnamemodify('../testdir/a', ':t')) call assert_equal('', fnamemodify('.', ':p:t')) @@ -53,8 +61,10 @@ describe('filename modifiers', function() quit call assert_equal("'abc\ndef'", fnamemodify("abc\ndef", ':S')) - set shell=tcsh - call assert_equal("'abc\\\ndef'", fnamemodify("abc\ndef", ':S')) + if executable('tcsh') + set shell=tcsh + call assert_equal("'abc\\\ndef'", fnamemodify("abc\ndef", ':S')) + endif endfunc func Test_expand() -- cgit From 5a39d2d00e398b404b4db7ec45b2f490d5bb845e Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Sat, 13 Jan 2018 08:25:11 -0500 Subject: test: fnamemodify with :8 filename modifier --- test/functional/eval/fnamemodify_spec.lua | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'test') diff --git a/test/functional/eval/fnamemodify_spec.lua b/test/functional/eval/fnamemodify_spec.lua index 2225c5da7c..52dfe1a55f 100644 --- a/test/functional/eval/fnamemodify_spec.lua +++ b/test/functional/eval/fnamemodify_spec.lua @@ -4,10 +4,19 @@ local eq = helpers.eq local iswin = helpers.iswin local fnamemodify = helpers.funcs.fnamemodify local command = helpers.command +local write_file = helpers.write_file describe('fnamemodify()', function() + setup(function() + write_file('Xtest-fnamemodify.txt', [[foobar]]) + end) + before_each(clear) + teardown(function() + os.remove('Xtest-fnamemodify.txt') + end) + it('works', function() if iswin() then local drive_f = io.popen('for %P in (%CD%) do @echo %~dP', 'r') @@ -29,4 +38,8 @@ describe('fnamemodify()', function() eq('/', fnamemodify([[/]], ':p')) end end) + + it(':8 works', function() + eq('Xtest-fnamemodify.txt', fnamemodify([[Xtest-fnamemodify.txt]], ':8')) + end) end) -- cgit From c08c09add745a7cfba0428d7cfd2238d3cb1474b Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Mon, 15 Jan 2018 22:43:59 -0500 Subject: test: try $TMPDIR for temporary directory --- test/functional/legacy/fnamemodify_spec.lua | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) (limited to 'test') diff --git a/test/functional/legacy/fnamemodify_spec.lua b/test/functional/legacy/fnamemodify_spec.lua index d8f812d77e..c1a50387a3 100644 --- a/test/functional/legacy/fnamemodify_spec.lua +++ b/test/functional/legacy/fnamemodify_spec.lua @@ -16,12 +16,10 @@ describe('filename modifiers', function() func Test_fnamemodify() if has('win32') set shellslash - let tmpdir = expand('$LOCALAPPDATA/Temp') - call assert_equal(tmpdir, expand('$USERPROFILE/AppData/Local/Temp')) else - let tmpdir = resolve('/tmp') set shell=sh endif + let tmpdir = $TMPDIR call assert_true(isdirectory(tmpdir)) execute 'cd '. tmpdir let $HOME=fnamemodify('.', ':p:h:h:h') @@ -30,11 +28,7 @@ describe('filename modifiers', function() call assert_equal('t', fnamemodify('test.out', ':p')[-1:]) call assert_equal('test.out', fnamemodify('test.out', ':.')) call assert_equal('../testdir/a', fnamemodify('../testdir/a', ':.')) - if has('win32') - call assert_equal('~/Local/Temp/test.out', fnamemodify('test.out', ':~')) - else - call assert_equal('test.out', fnamemodify('test.out', ':~')) - endif + call assert_equal('test.out', fnamemodify('test.out', ':~')) call assert_equal('../testdir/a', fnamemodify('../testdir/a', ':~')) call assert_equal('a', fnamemodify('../testdir/a', ':t')) call assert_equal('', fnamemodify('.', ':p:t')) -- cgit From 28236867a0aa0e4dbb8e6c3f5b1d1c6ccbb2c7e1 Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Mon, 15 Jan 2018 23:45:02 -0500 Subject: test: fix failed test cases with tmpdir = $TMPDIR --- test/functional/legacy/fnamemodify_spec.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'test') diff --git a/test/functional/legacy/fnamemodify_spec.lua b/test/functional/legacy/fnamemodify_spec.lua index c1a50387a3..e86c7ba27c 100644 --- a/test/functional/legacy/fnamemodify_spec.lua +++ b/test/functional/legacy/fnamemodify_spec.lua @@ -22,13 +22,13 @@ describe('filename modifiers', function() let tmpdir = $TMPDIR call assert_true(isdirectory(tmpdir)) execute 'cd '. tmpdir - let $HOME=fnamemodify('.', ':p:h:h:h') + let $HOME=fnamemodify('.', ':p:h:h') call assert_equal('/', fnamemodify('.', ':p')[-1:]) - call assert_equal('p', fnamemodify('.', ':p:h')[-1:]) + call assert_equal('r', fnamemodify('.', ':p:h')[-1:]) call assert_equal('t', fnamemodify('test.out', ':p')[-1:]) call assert_equal('test.out', fnamemodify('test.out', ':.')) call assert_equal('../testdir/a', fnamemodify('../testdir/a', ':.')) - call assert_equal('test.out', fnamemodify('test.out', ':~')) + call assert_equal('~/Xtest-tmpdir/test.out', fnamemodify('test.out', ':~')) call assert_equal('../testdir/a', fnamemodify('../testdir/a', ':~')) call assert_equal('a', fnamemodify('../testdir/a', ':t')) call assert_equal('', fnamemodify('.', ':p:t')) -- cgit From 7ac21332cf8ba5a4d3222d4a7fa1c760ef531f17 Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Tue, 16 Jan 2018 08:41:31 -0500 Subject: Revert "test: fix failed test cases with tmpdir = $TMPDIR" This reverts commit f7fe3012204169f22412194a78f196ffc72bb8c3. Fails on QuickBuild because it uses a non-local path. Need a environment-agnostic solution --- test/functional/legacy/fnamemodify_spec.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'test') diff --git a/test/functional/legacy/fnamemodify_spec.lua b/test/functional/legacy/fnamemodify_spec.lua index e86c7ba27c..c1a50387a3 100644 --- a/test/functional/legacy/fnamemodify_spec.lua +++ b/test/functional/legacy/fnamemodify_spec.lua @@ -22,13 +22,13 @@ describe('filename modifiers', function() let tmpdir = $TMPDIR call assert_true(isdirectory(tmpdir)) execute 'cd '. tmpdir - let $HOME=fnamemodify('.', ':p:h:h') + let $HOME=fnamemodify('.', ':p:h:h:h') call assert_equal('/', fnamemodify('.', ':p')[-1:]) - call assert_equal('r', fnamemodify('.', ':p:h')[-1:]) + call assert_equal('p', fnamemodify('.', ':p:h')[-1:]) call assert_equal('t', fnamemodify('test.out', ':p')[-1:]) call assert_equal('test.out', fnamemodify('test.out', ':.')) call assert_equal('../testdir/a', fnamemodify('../testdir/a', ':.')) - call assert_equal('~/Xtest-tmpdir/test.out', fnamemodify('test.out', ':~')) + call assert_equal('test.out', fnamemodify('test.out', ':~')) call assert_equal('../testdir/a', fnamemodify('../testdir/a', ':~')) call assert_equal('a', fnamemodify('../testdir/a', ':t')) call assert_equal('', fnamemodify('.', ':p:t')) -- cgit From 5c09f37d4ac09ce32852b729869c9745d99627ec Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Tue, 16 Jan 2018 09:02:11 -0500 Subject: test: fix failed tests with $TMPDIR in QuickBuild --- test/functional/legacy/fnamemodify_spec.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'test') diff --git a/test/functional/legacy/fnamemodify_spec.lua b/test/functional/legacy/fnamemodify_spec.lua index c1a50387a3..0364316a6a 100644 --- a/test/functional/legacy/fnamemodify_spec.lua +++ b/test/functional/legacy/fnamemodify_spec.lua @@ -24,11 +24,11 @@ describe('filename modifiers', function() execute 'cd '. tmpdir let $HOME=fnamemodify('.', ':p:h:h:h') call assert_equal('/', fnamemodify('.', ':p')[-1:]) - call assert_equal('p', fnamemodify('.', ':p:h')[-1:]) + call assert_equal(tmpdir[strchars(tmpdir) - 1], fnamemodify('.', ':p:h')[-1:]) call assert_equal('t', fnamemodify('test.out', ':p')[-1:]) call assert_equal('test.out', fnamemodify('test.out', ':.')) call assert_equal('../testdir/a', fnamemodify('../testdir/a', ':.')) - call assert_equal('test.out', fnamemodify('test.out', ':~')) + call assert_equal(fnamemodify(tmpdir, ':~').'/test.out', fnamemodify('test.out', ':~')) call assert_equal('../testdir/a', fnamemodify('../testdir/a', ':~')) call assert_equal('a', fnamemodify('../testdir/a', ':t')) call assert_equal('', fnamemodify('.', ':p:t')) -- cgit From 0578087e5ec928e89643f97fc0b82ed8363e4e09 Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Tue, 16 Jan 2018 09:44:26 -0500 Subject: test: osx: try resolve($TMPDIR) --- test/functional/legacy/fnamemodify_spec.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'test') diff --git a/test/functional/legacy/fnamemodify_spec.lua b/test/functional/legacy/fnamemodify_spec.lua index 0364316a6a..7e859bf0cf 100644 --- a/test/functional/legacy/fnamemodify_spec.lua +++ b/test/functional/legacy/fnamemodify_spec.lua @@ -19,7 +19,7 @@ describe('filename modifiers', function() else set shell=sh endif - let tmpdir = $TMPDIR + let tmpdir = resolve($TMPDIR) call assert_true(isdirectory(tmpdir)) execute 'cd '. tmpdir let $HOME=fnamemodify('.', ':p:h:h:h') -- cgit From 1813c53e8d5d11dd0584a6f2acc628c5605d92bf Mon Sep 17 00:00:00 2001 From: Tommy Allen Date: Sat, 27 Jan 2018 02:04:32 -0500 Subject: Updated tests --- test/functional/ui/mouse_spec.lua | 439 ++++++++++++++++++++++++++------------ 1 file changed, 307 insertions(+), 132 deletions(-) (limited to 'test') diff --git a/test/functional/ui/mouse_spec.lua b/test/functional/ui/mouse_spec.lua index 3fdedeb073..13820af3b8 100644 --- a/test/functional/ui/mouse_spec.lua +++ b/test/functional/ui/mouse_spec.lua @@ -750,17 +750,19 @@ describe('ui/mouse/input', function() feed_command('set concealcursor=n') feed_command('set nowrap') - feed_command('syntax match NonText "\\" conceal') - feed_command('syntax match NonText "\\cs\\|g." conceal cchar=X') - feed_command('syntax match NonText "\\%(lo\\|cl\\)." conceal') - feed_command('syntax match NonText "Lo" conceal cchar=Y') + feed_command('set shiftwidth=2 tabstop=4 list listchars=tab:>-') + feed_command('syntax match NonText "\\*" conceal') + feed_command('syntax match NonText "cats" conceal cchar=X') + feed_command('syntax match NonText "x" conceal cchar=>') + -- First column is there to retain the tabs. insert([[ - Lorem ipsum dolor sit amet, consetetur sadipscing elitr. - Stet clita kasd gubergren, no sea takimata sanctus est. + |Section *t1* + | *t2* *t3* *t4* + |x 私は猫が大好き *cats* ✨🐈✨ ]]) - feed('gg') + feed('ggGxgg') end) it('(level 1) click on non-wrapped lines', function() @@ -768,93 +770,138 @@ describe('ui/mouse/input', function() feed('<0,0>') screen:expect([[ - {c:^Y}rem ip{c:X}um do{c: } {c:X}it {c: }, con| - {c:X}tet {c: }ta ka{c:X}d {c:X}ber{c:X}en, no| + ^Section{0:>>--->--->---}{c: }t1{c: } | + {0:>--->--->---} {c: }t2{c: } {c: }t3{c: } {c: }| + {c:>} 私は猫が大好き{0:>---}{c: X } {0:>}| | {0:~ }| - {0:~ }| {0:~ }| | ]]) feed('<1,0>') screen:expect([[ - {c:Y}^rem ip{c:X}um do{c: } {c:X}it {c: }, con| - {c:X}tet {c: }ta ka{c:X}d {c:X}ber{c:X}en, no| + S^ection{0:>>--->--->---}{c: }t1{c: } | + {0:>--->--->---} {c: }t2{c: } {c: }t3{c: } {c: }| + {c:>} 私は猫が大好き{0:>---}{c: X } {0:>}| | {0:~ }| + {0:~ }| + | + ]]) + + feed('<21,0>') + screen:expect([[ + Section{0:>>--->--->---}{c: }^t1{c: } | + {0:>--->--->---} {c: }t2{c: } {c: }t3{c: } {c: }| + {c:>} 私は猫が大好き{0:>---}{c: X } {0:>}| + | {0:~ }| {0:~ }| | ]]) - feed('<15,0>') + feed('<21,1>') screen:expect([[ - {c:Y}rem ip{c:X}um do{c: } {c:^X}it {c: }, con| - {c:X}tet {c: }ta ka{c:X}d {c:X}ber{c:X}en, no| + Section{0:>>--->--->---}{c: }t1{c: } | + {0:>--->--->---} {c: }t2{c: } {c: }t^3{c: } {c: }| + {c:>} 私は猫が大好き{0:>---}{c: X } {0:>}| | {0:~ }| + {0:~ }| + | + ]]) + + feed('<0,2>') + screen:expect([[ + Section{0:>>--->--->---}{c: }t1{c: } | + {0:>--->--->---} {c: }t2{c: } {c: }t3{c: } {c: }| + {c:^>} 私は猫が大好き{0:>---}{c: X } {0:>}| + | {0:~ }| {0:~ }| | ]]) - feed('<15,1>') + feed('<7,2>') screen:expect([[ - {c:Y}rem ip{c:X}um do{c: } {c:X}it {c: }, con| - {c:X}tet {c: }ta ka{c:X}d {c:X}^ber{c:X}en, no| + Section{0:>>--->--->---}{c: }t1{c: } | + {0:>--->--->---} {c: }t2{c: } {c: }t3{c: } {c: }| + {c:>} 私は^猫が大好き{0:>---}{c: X } {0:>}| | {0:~ }| + {0:~ }| + | + ]]) + + feed('<21,2>') + screen:expect([[ + Section{0:>>--->--->---}{c: }t1{c: } | + {0:>--->--->---} {c: }t2{c: } {c: }t3{c: } {c: }| + {c:>} 私は猫が大好き{0:>---}{c: ^X } {0:>}| + | {0:~ }| {0:~ }| | ]]) + end) -- level 1 - non wrapped it('(level 1) click on wrapped lines', function() feed_command('let &conceallevel=1', 'let &wrap=1', 'echo') - feed('<0,0>') + feed('<24,1>') screen:expect([[ - {c:^Y}rem ip{c:X}um do{c: } {c:X}it {c: } | - , con{c:X}etetur {c:X}adip{c:X}cin{c:X} | - elitr. | - {c:X}tet {c: }ta ka{c:X}d {c:X}ber{c:X}en | - , no {c:X}ea takimata {c:X}anctu{c:X}| - e{c:X}t. | + Section{0:>>--->--->---}{c: }t1{c: } | + {0:>--->--->---} {c: }t2{c: } {c: }t3{c: } {c:^ }| + t4{c: } | + {c:>} 私は猫が大好き{0:>---}{c: X} | + {c: } ✨🐈✨ | + | + | + ]]) + + feed('<0,2>') + screen:expect([[ + Section{0:>>--->--->---}{c: }t1{c: } | + {0:>--->--->---} {c: }t2{c: } {c: }t3{c: } {c: }| + ^t4{c: } | + {c:>} 私は猫が大好き{0:>---}{c: X} | + {c: } ✨🐈✨ | + | | ]]) - feed('<6,1>') + feed('<8,3>') screen:expect([[ - {c:Y}rem ip{c:X}um do{c: } {c:X}it {c: } | - , con{c:X}^etetur {c:X}adip{c:X}cin{c:X} | - elitr. | - {c:X}tet {c: }ta ka{c:X}d {c:X}ber{c:X}en | - , no {c:X}ea takimata {c:X}anctu{c:X}| - e{c:X}t. | + Section{0:>>--->--->---}{c: }t1{c: } | + {0:>--->--->---} {c: }t2{c: } {c: }t3{c: } {c: }| + t4{c: } | + {c:>} 私は猫^が大好き{0:>---}{c: X} | + {c: } ✨🐈✨ | + | | ]]) - feed('<15,1>') + feed('<21,3>') screen:expect([[ - {c:Y}rem ip{c:X}um do{c: } {c:X}it {c: } | - , con{c:X}etetur {c:X}a^dip{c:X}cin{c:X} | - elitr. | - {c:X}tet {c: }ta ka{c:X}d {c:X}ber{c:X}en | - , no {c:X}ea takimata {c:X}anctu{c:X}| - e{c:X}t. | + Section{0:>>--->--->---}{c: }t1{c: } | + {0:>--->--->---} {c: }t2{c: } {c: }t3{c: } {c: }| + t4{c: } | + {c:>} 私は猫が大好き{0:>---}{c: ^X} | + {c: } ✨🐈✨ | + | | ]]) - feed('<15,3>') + feed('<4,4>') screen:expect([[ - {c:Y}rem ip{c:X}um do{c: } {c:X}it {c: } | - , con{c:X}etetur {c:X}adip{c:X}cin{c:X} | - elitr. | - {c:X}tet {c: }ta ka{c:X}d {c:X}^ber{c:X}en | - , no {c:X}ea takimata {c:X}anctu{c:X}| - e{c:X}t. | + Section{0:>>--->--->---}{c: }t1{c: } | + {0:>--->--->---} {c: }t2{c: } {c: }t3{c: } {c: }| + t4{c: } | + {c:>} 私は猫が大好き{0:>---}{c: X} | + {c: } ✨^🐈✨ | + | | ]]) end) -- level 1 - wrapped @@ -863,45 +910,67 @@ describe('ui/mouse/input', function() it('(level 2) click on non-wrapped lines', function() feed_command('let &conceallevel=2', 'echo') - feed('<0,0>') + feed('<20,0>') screen:expect([[ - {c:^Y}rem ip{c:X}um do {c:X}it , con{c:X}e| - {c:X}tet ta ka{c:X}d {c:X}ber{c:X}en, no | + Section{0:>>--->--->---}^t1 | + {0:>--->--->---} t2 t3 t4 | + {c:>} 私は猫が大好き{0:>---}{c:X} ✨{0:>}| | {0:~ }| - {0:~ }| {0:~ }| | ]]) - feed('<1,0>') + feed('<14,1>') screen:expect([[ - {c:Y}^rem ip{c:X}um do {c:X}it , con{c:X}e| - {c:X}tet ta ka{c:X}d {c:X}ber{c:X}en, no | + Section{0:>>--->--->---}t1 | + {0:>--->--->---} ^t2 t3 t4 | + {c:>} 私は猫が大好き{0:>---}{c:X} ✨{0:>}| | {0:~ }| - {0:~ }| {0:~ }| | ]]) - feed('<15,0>') + feed('<18,1>') screen:expect([[ - {c:Y}rem ip{c:X}um do {c:X}^it , con{c:X}e| - {c:X}tet ta ka{c:X}d {c:X}ber{c:X}en, no | + Section{0:>>--->--->---}t1 | + {0:>--->--->---} t2 t^3 t4 | + {c:>} 私は猫が大好き{0:>---}{c:X} ✨{0:>}| | {0:~ }| + {0:~ }| + | + ]]) + + feed('<0,2>') -- Weirdness + screen:expect([[ + Section{0:>>--->--->---}t1 | + {0:>--->--->---} t2 t3 t4 | + {c:^>} 私は猫が大好き{0:>---}{c:X} ✨{0:>}| + | {0:~ }| {0:~ }| | ]]) - feed('<15,1>') + feed('<8,2>') screen:expect([[ - {c:Y}rem ip{c:X}um do {c:X}it , con{c:X}e| - {c:X}tet ta ka{c:X}d {c:X}b^er{c:X}en, no | + Section{0:>>--->--->---}t1 | + {0:>--->--->---} t2 t3 t4 | + {c:>} 私は猫^が大好き{0:>---}{c:X} ✨{0:>}| | {0:~ }| + {0:~ }| + | + ]]) + + feed('<20,2>') + screen:expect([[ + Section{0:>>--->--->---}t1 | + {0:>--->--->---} t2 t3 t4 | + {c:>} 私は猫が大好き{0:>---}{c:^X} ✨{0:>}| + | {0:~ }| {0:~ }| | @@ -911,47 +980,108 @@ describe('ui/mouse/input', function() it('(level 2) click on wrapped lines', function() feed_command('let &conceallevel=2', 'let &wrap=1', 'echo') - feed('<0,0>') + feed('<20,0>') + screen:expect([[ + Section{0:>>--->--->---}^t1 | + {0:>--->--->---} t2 t3 | + t4 | + {c:>} 私は猫が大好き{0:>---}{c:X} | + ✨🐈✨ | + | + | + ]]) + + feed('<14,1>') + screen:expect([[ + Section{0:>>--->--->---}t1 | + {0:>--->--->---} ^t2 t3 | + t4 | + {c:>} 私は猫が大好き{0:>---}{c:X} | + ✨🐈✨ | + | + | + ]]) + + feed('<18,1>') + screen:expect([[ + Section{0:>>--->--->---}t1 | + {0:>--->--->---} t2 t^3 | + t4 | + {c:>} 私は猫が大好き{0:>---}{c:X} | + ✨🐈✨ | + | + | + ]]) + + -- NOTE: The click would ideally be on the 't' in 't4', but wrapping + -- caused the invisible '*' right before 't4' to remain on the previous + -- screen line. This is being treated as expected because fixing this is + -- out of scope for mouse clicks. Should the wrapping behavior of + -- concealed characters change in the future, this case should be + -- reevaluated. + feed('<0,2>') screen:expect([[ - {c:^Y}rem ip{c:X}um do {c:X}it | - , con{c:X}etetur {c:X}adip{c:X}cin{c:X} | - elitr. | - {c:X}tet ta ka{c:X}d {c:X}ber{c:X}en | - , no {c:X}ea takimata {c:X}anctu{c:X}| - e{c:X}t. | + Section{0:>>--->--->---}t1 | + {0:>--->--->---} t2 t3 ^ | + t4 | + {c:>} 私は猫が大好き{0:>---}{c:X} | + ✨🐈✨ | + | | ]]) - feed('<6,1>') + feed('<1,2>') screen:expect([[ - {c:Y}rem ip{c:X}um do {c:X}it | - , con{c:X}^etetur {c:X}adip{c:X}cin{c:X} | - elitr. | - {c:X}tet ta ka{c:X}d {c:X}ber{c:X}en | - , no {c:X}ea takimata {c:X}anctu{c:X}| - e{c:X}t. | + Section{0:>>--->--->---}t1 | + {0:>--->--->---} t2 t3 | + t^4 | + {c:>} 私は猫が大好き{0:>---}{c:X} | + ✨🐈✨ | + | | ]]) - feed('<15,1>') + feed('<0,3>') screen:expect([[ - {c:Y}rem ip{c:X}um do {c:X}it | - , con{c:X}etetur {c:X}a^dip{c:X}cin{c:X} | - elitr. | - {c:X}tet ta ka{c:X}d {c:X}ber{c:X}en | - , no {c:X}ea takimata {c:X}anctu{c:X}| - e{c:X}t. | + Section{0:>>--->--->---}t1 | + {0:>--->--->---} t2 t3 | + t4 | + {c:^>} 私は猫が大好き{0:>---}{c:X} | + ✨🐈✨ | + | | ]]) - feed('<15,3>') + feed('<20,3>') screen:expect([[ - {c:Y}rem ip{c:X}um do {c:X}it | - , con{c:X}etetur {c:X}adip{c:X}cin{c:X} | - elitr. | - {c:X}tet ta ka{c:X}d {c:X}b^er{c:X}en | - , no {c:X}ea takimata {c:X}anctu{c:X}| - e{c:X}t. | + Section{0:>>--->--->---}t1 | + {0:>--->--->---} t2 t3 | + t4 | + {c:>} 私は猫が大好き{0:>---}{c:^X} | + ✨🐈✨ | + | + | + ]]) + + feed('<1,4>') + screen:expect([[ + Section{0:>>--->--->---}t1 | + {0:>--->--->---} t2 t3 | + t4 | + {c:>} 私は猫が大好き{0:>---}{c:X} | + ^✨🐈✨ | + | + | + ]]) + + feed('<5,4>') + screen:expect([[ + Section{0:>>--->--->---}t1 | + {0:>--->--->---} t2 t3 | + t4 | + {c:>} 私は猫が大好き{0:>---}{c:X} | + ✨🐈^✨ | + | | ]]) end) -- level 2 - wrapped @@ -960,46 +1090,46 @@ describe('ui/mouse/input', function() it('(level 3) click on non-wrapped lines', function() feed_command('let &conceallevel=3', 'echo') - feed('<0,0>') + feed('<0,2>') screen:expect([[ - ^rem ipum do it , conetetu| - tet ta kad beren, no ea t| + Section{0:>>--->--->---}t1 | + {0:>--->--->---} t2 t3 t4 | + ^ 私は猫が大好き{0:>----} ✨🐈| | {0:~ }| - {0:~ }| {0:~ }| | ]]) - feed('<1,0>') + feed('<1,2>') screen:expect([[ - r^em ipum do it , conetetu| - tet ta kad beren, no ea t| + Section{0:>>--->--->---}t1 | + {0:>--->--->---} t2 t3 t4 | + ^私は猫が大好き{0:>----} ✨🐈| | {0:~ }| - {0:~ }| {0:~ }| | ]]) - feed('<15,0>') + feed('<13,2>') screen:expect([[ - rem ipum do it ^, conetetu| - tet ta kad beren, no ea t| + Section{0:>>--->--->---}t1 | + {0:>--->--->---} t2 t3 t4 | + 私は猫が大好^き{0:>----} ✨🐈| | {0:~ }| - {0:~ }| {0:~ }| | ]]) - feed('<15,1>') + feed('<20,2>') screen:expect([[ - rem ipum do it , conetetu| - tet ta kad bere^n, no ea t| + Section{0:>>--->--->---}t1 | + {0:>--->--->---} t2 t3 t4 | + 私は猫が大好き{0:>----}^ ✨🐈| | {0:~ }| - {0:~ }| {0:~ }| | ]]) @@ -1008,49 +1138,94 @@ describe('ui/mouse/input', function() it('(level 3) click on wrapped lines', function() feed_command('let &conceallevel=3', 'let &wrap=1', 'echo') - feed('<0,0>') + feed('<14,1>') + screen:expect([[ + Section{0:>>--->--->---}t1 | + {0:>--->--->---} ^t2 t3 | + t4 | + 私は猫が大好き{0:>----} | + ✨🐈✨ | + | + | + ]]) + + feed('<18,1>') screen:expect([[ - ^rem ipum do it | - , conetetur adipcin | - elitr. | - tet ta kad beren | - , no ea takimata anctu | - et. | + Section{0:>>--->--->---}t1 | + {0:>--->--->---} t2 t^3 | + t4 | + 私は猫が大好き{0:>----} | + ✨🐈✨ | + | | ]]) - feed('<6,1>') + feed('<1,2>') screen:expect([[ - rem ipum do it | - , cone^tetur adipcin | - elitr. | - tet ta kad beren | - , no ea takimata anctu | - et. | + Section{0:>>--->--->---}t1 | + {0:>--->--->---} t2 t3 | + t^4 | + 私は猫が大好き{0:>----} | + ✨🐈✨ | + | | ]]) - feed('<15,1>') + feed('<0,3>') screen:expect([[ - rem ipum do it | - , conetetur adi^pcin | - elitr. | - tet ta kad beren | - , no ea takimata anctu | - et. | + Section{0:>>--->--->---}t1 | + {0:>--->--->---} t2 t3 | + t4 | + ^ 私は猫が大好き{0:>----} | + ✨🐈✨ | + | | ]]) - feed('<15,3>') + feed('<20,3>') screen:expect([[ - rem ipum do it | - , conetetur adipcin | - elitr. | - tet ta kad bere^n | - , no ea takimata anctu | - et. | + Section{0:>>--->--->---}t1 | + {0:>--->--->---} t2 t3 | + t4 | + 私は猫が大好き{0:>----}^ | + ✨🐈✨ | + | | ]]) + + feed('<1,4>') + screen:expect([[ + Section{0:>>--->--->---}t1 | + {0:>--->--->---} t2 t3 | + t4 | + 私は猫が大好き{0:>----} | + ^✨🐈✨ | + | + | + ]]) + + feed('<3,4>') + screen:expect([[ + Section{0:>>--->--->---}t1 | + {0:>--->--->---} t2 t3 | + t4 | + 私は猫が大好き{0:>----} | + ✨^🐈✨ | + | + | + ]]) + + feed('<5,4>') + screen:expect([[ + Section{0:>>--->--->---}t1 | + {0:>--->--->---} t2 t3 | + t4 | + 私は猫が大好き{0:>----} | + ✨🐈^✨ | + | + | + ]]) + end) -- level 3 - wrapped end) end) -- cgit From 41b3c7850f839b46affe35bf2de92bc18113b0f7 Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Mon, 29 Jan 2018 00:05:25 -0500 Subject: test: use helpers.pathroot() to avoid a syscall --- test/functional/eval/fnamemodify_spec.lua | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) (limited to 'test') diff --git a/test/functional/eval/fnamemodify_spec.lua b/test/functional/eval/fnamemodify_spec.lua index 52dfe1a55f..fe6b50a544 100644 --- a/test/functional/eval/fnamemodify_spec.lua +++ b/test/functional/eval/fnamemodify_spec.lua @@ -18,24 +18,18 @@ describe('fnamemodify()', function() end) it('works', function() + local root = helpers.pathroot() + eq(root, fnamemodify([[/]], ':p:h')) + eq(root, fnamemodify([[/]], ':p')) if iswin() then - local drive_f = io.popen('for %P in (%CD%) do @echo %~dP', 'r') - local drive = string.gsub(drive_f:read('*a'), '[\n\r]', '') - drive_f:close() - local root = drive..[[\]] eq(root, fnamemodify([[\]], ':p:h')) eq(root, fnamemodify([[\]], ':p')) - eq(root, fnamemodify([[/]], ':p:h')) - eq(root, fnamemodify([[/]], ':p')) command('set shellslash') - root = drive..[[/]] + root = string.sub(root, 1, -2)..'/' eq(root, fnamemodify([[\]], ':p:h')) eq(root, fnamemodify([[\]], ':p')) eq(root, fnamemodify([[/]], ':p:h')) eq(root, fnamemodify([[/]], ':p')) - else - eq('/', fnamemodify([[/]], ':p:h')) - eq('/', fnamemodify([[/]], ':p')) end end) -- cgit From 2a1a624878ec70caf1e61b7440a6b9e4dce6c36f Mon Sep 17 00:00:00 2001 From: KunMing Xie Date: Wed, 31 Jan 2018 03:15:05 +0800 Subject: vim-patch:8.0.0443: terminal width is set to 80 in test3 (#7933) Problem: Terminal width is set to 80 in test3. Solution: Instead of setting 'columns' set 'wrapmargin' depending on 'columns. https://github.com/vim/vim/commit/38a3d6c9601b637a28f399059263300e9f65eba4 --- test/functional/legacy/003_cindent_spec.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'test') diff --git a/test/functional/legacy/003_cindent_spec.lua b/test/functional/legacy/003_cindent_spec.lua index 13726050b2..1cede8a7d7 100644 --- a/test/functional/legacy/003_cindent_spec.lua +++ b/test/functional/legacy/003_cindent_spec.lua @@ -1957,7 +1957,8 @@ describe('cindent', function() } ]=]) - feed_command('set tw=0 wm=60 columns=80 noai fo=croq') + feed_command('set tw=0 noai fo=croq') + feed_command('let &wm = &columns - 20') feed_command('/serious/e') feed('a about life, the universe, and the rest') -- cgit From 5d2dd2ebe28c31f223d77355a8f9d40adfb41c82 Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Wed, 27 Sep 2017 13:38:24 -0500 Subject: win: has("wsl") on Windows Subsystem for Linux #7330 Per CMAKE docs, CMAKE_HOST_SYSTEM_VERSION is the result of `uname -r`: https://cmake.org/cmake/help/v3.4/variable/CMAKE_HOST_SYSTEM_VERSION.html?highlight=uname A numeric version string for the system. On systems that support uname, this variable is set to the output of uname -r. On other systems this is set to major-minor version numbers. On Windows it is something like "6.1", so it won't match ".*-Microsoft". Closes #7329 --- test/functional/eval/has_spec.lua | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'test') diff --git a/test/functional/eval/has_spec.lua b/test/functional/eval/has_spec.lua index 78c4e08fde..a3af2d1a20 100644 --- a/test/functional/eval/has_spec.lua +++ b/test/functional/eval/has_spec.lua @@ -57,4 +57,10 @@ describe('has()', function() eq(0, funcs.has("unnamedplus")) end end) + + it('"wsl"', function() + if 1 == funcs.has('win32') or 1 == funcs.has('mac') then + eq(0, funcs.has('wsl')) + end + end) end) -- cgit From 649123d07c13d3314b7f97abdd3d8094b32fbefe Mon Sep 17 00:00:00 2001 From: KunMing Xie Date: Wed, 31 Jan 2018 04:21:29 +0800 Subject: vim-patch:8.0.0358,8.0.0359 (#7832) vim-patch:8.0.0358: invalid memory access in C-indent code Problem: Invalid memory access in C-indent code. Solution: Don't go over end of empty line. (Dominique Pelle, closes vim/vim#1492) https://github.com/vim/vim/commit/60629d642541a089c322e65963c0a77e5f77eb79 vim-patch:8.0.0359: 'number' and 'relativenumber' are not properly tested Problem: 'number' and 'relativenumber' are not properly tested. Solution: Add tests, change old style to new style tests. (Ozaki Kiichi, closes vim/vim#1447) https://github.com/vim/vim/commit/dc9a081712ec8c140e6d4909e9f6b03a629d32d3 --- .../legacy/089_number_relnumber_findfile_spec.lua | 116 --------------------- 1 file changed, 116 deletions(-) delete mode 100644 test/functional/legacy/089_number_relnumber_findfile_spec.lua (limited to 'test') diff --git a/test/functional/legacy/089_number_relnumber_findfile_spec.lua b/test/functional/legacy/089_number_relnumber_findfile_spec.lua deleted file mode 100644 index 6708fd50b7..0000000000 --- a/test/functional/legacy/089_number_relnumber_findfile_spec.lua +++ /dev/null @@ -1,116 +0,0 @@ --- - Some tests for setting 'number' and 'relativenumber' --- This is not all that useful now that the options are no longer reset when --- setting the other. - -local helpers = require('test.functional.helpers')(after_each) -local feed = helpers.feed -local clear, expect, source = helpers.clear, helpers.expect, helpers.source - -describe("setting 'number' and 'relativenumber'", function() - setup(clear) - - it('is working', function() - source([[ - set hidden nu rnu - redir @a | set nu? | set rnu? | redir END - e! xx - redir @b | set nu? | set rnu? | redir END - e! # - $put ='results:' - $put a - $put b - - set nonu nornu - setglobal nu - setlocal rnu - redir @c | setglobal nu? | redir END - set nonu nornu - setglobal rnu - setlocal nu - redir @d | setglobal rnu? | redir END - $put =':setlocal must NOT reset the other global value' - $put c - $put d - - set nonu nornu - setglobal nu - setglobal rnu - redir @e | setglobal nu? | redir END - set nonu nornu - setglobal rnu - setglobal nu - redir @f | setglobal rnu? | redir END - $put =':setglobal MUST reset the other global value' - $put e - $put f - - set nonu nornu - set nu - set rnu - redir @g | setglobal nu? | redir END - set nonu nornu - set rnu - set nu - redir @h | setglobal rnu? | redir END - $put =':set MUST reset the other global value' - $put g - $put h - ]]) - - -- Remove empty line - feed('ggdd') - - -- Assert buffer contents. - expect([[ - results: - - number - relativenumber - - number - relativenumber - :setlocal must NOT reset the other global value - - number - - relativenumber - :setglobal MUST reset the other global value - - number - - relativenumber - :set MUST reset the other global value - - number - - relativenumber]]) - end) -end) - --- - Some tests for findfile() function -describe('findfile', function() - setup(clear) - - it('is working', function() - -- Assume test is being run from project root - source([[ - $put ='Testing findfile' - $put ='' - set ssl - $put =findfile('vim.c','src/nvim/ap*') - cd src/nvim - $put =findfile('vim.c','ap*') - $put =findfile('vim.c','api') - ]]) - - -- Remove empty line - feed('ggdd') - - expect([[ - Testing findfile - - src/nvim/api/vim.c - api/vim.c - api/vim.c]]) - end) -end) -- cgit From 648fed975eb8ddde9c5cbc0f859d06deebf80dd9 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Wed, 31 Jan 2018 10:25:51 +0100 Subject: os_system(): do not set up input stream for empty string #7951 Test failure: test/functional/eval/system_spec.lua: "works with an empty string" E5677: Error writing input to shell-command: EPIPE ref https://github.com/neovim/neovim/pull/6558#issuecomment-361061035 ref #6554 --- test/functional/eval/system_spec.lua | 1 + 1 file changed, 1 insertion(+) (limited to 'test') diff --git a/test/functional/eval/system_spec.lua b/test/functional/eval/system_spec.lua index 77e7424452..7fe79d4351 100644 --- a/test/functional/eval/system_spec.lua +++ b/test/functional/eval/system_spec.lua @@ -260,6 +260,7 @@ describe('system()', function() end) it('works with an empty string', function() eq("test\n", eval('system("echo test", "")')) + eq(2, eval("1+1")) -- Still alive? end) end) -- cgit From f8010ea3eca77961312afeb96234678447ce17e4 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Thu, 1 Feb 2018 03:12:37 +0100 Subject: test: robust cleanup, unique filenames #7950 (#7950) Use unique filenames to avoid test conflicts. Use read_file() instead of io.popen(), to ensures the file is closed. Use helpers.rmdir(), it is far more robust than lfs. closes #7911 --- test/functional/eval/buf_functions_spec.lua | 5 +++-- test/functional/ex_cmds/mksession_spec.lua | 3 ++- test/functional/ex_cmds/mkview_spec.lua | 2 +- test/functional/legacy/008_autocommands_spec.lua | 10 ++++++---- test/functional/legacy/096_location_list_spec.lua | 14 +++++++------- test/functional/plugin/shada_spec.lua | 11 +++++------ 6 files changed, 24 insertions(+), 21 deletions(-) (limited to 'test') diff --git a/test/functional/eval/buf_functions_spec.lua b/test/functional/eval/buf_functions_spec.lua index db50874c53..7de58766b9 100644 --- a/test/functional/eval/buf_functions_spec.lua +++ b/test/functional/eval/buf_functions_spec.lua @@ -14,6 +14,7 @@ local curbufmeths = helpers.curbufmeths local curwinmeths = helpers.curwinmeths local curtabmeths = helpers.curtabmeths local get_pathsep = helpers.get_pathsep +local rmdir = helpers.rmdir local fname = 'Xtest-functional-eval-buf_functions' local fname2 = fname .. '.2' @@ -61,7 +62,7 @@ describe('bufname() function', function() lfs.mkdir(dirname) end) after_each(function() - lfs.rmdir(dirname) + rmdir(dirname) end) it('returns expected buffer name', function() eq('', funcs.bufname('%')) -- Buffer has no name yet @@ -143,7 +144,7 @@ describe('bufwinnr() function', function() lfs.mkdir(dirname) end) after_each(function() - lfs.rmdir(dirname) + rmdir(dirname) end) it('returns expected window number', function() eq(1, funcs.bufwinnr('%')) diff --git a/test/functional/ex_cmds/mksession_spec.lua b/test/functional/ex_cmds/mksession_spec.lua index 5d658f10bb..a5b327095e 100644 --- a/test/functional/ex_cmds/mksession_spec.lua +++ b/test/functional/ex_cmds/mksession_spec.lua @@ -6,6 +6,7 @@ local command = helpers.command local get_pathsep = helpers.get_pathsep local eq = helpers.eq local funcs = helpers.funcs +local rmdir = helpers.rmdir local file_prefix = 'Xtest-functional-ex_cmds-mksession_spec' @@ -20,7 +21,7 @@ describe(':mksession', function() after_each(function() os.remove(session_file) - lfs.rmdir(tab_dir) + rmdir(tab_dir) end) it('restores tab-local working directories', function() diff --git a/test/functional/ex_cmds/mkview_spec.lua b/test/functional/ex_cmds/mkview_spec.lua index 97a49dbbd5..fef8065b2e 100644 --- a/test/functional/ex_cmds/mkview_spec.lua +++ b/test/functional/ex_cmds/mkview_spec.lua @@ -24,7 +24,7 @@ describe(':mkview', function() after_each(function() -- Remove any views created in the view directory rmdir(view_dir) - lfs.rmdir(local_dir) + rmdir(local_dir) end) it('viewoption curdir restores local current directory', function() diff --git a/test/functional/legacy/008_autocommands_spec.lua b/test/functional/legacy/008_autocommands_spec.lua index 7474f1e068..453638ce45 100644 --- a/test/functional/legacy/008_autocommands_spec.lua +++ b/test/functional/legacy/008_autocommands_spec.lua @@ -5,9 +5,10 @@ local helpers = require('test.functional.helpers')(after_each) local feed, source = helpers.feed, helpers.source local clear, feed_command, expect, eq, eval = helpers.clear, helpers.feed_command, helpers.expect, helpers.eq, helpers.eval local write_file, wait, dedent = helpers.write_file, helpers.wait, helpers.dedent -local io = require('io') +local read_file = helpers.read_file describe('autocommands that delete and unload buffers:', function() + local test_file = 'Xtest-008_autocommands.out' local text1 = dedent([[ start of Xxx1 test @@ -18,7 +19,7 @@ describe('autocommands that delete and unload buffers:', function() write_file('Xxx2', text2..'\n') end) teardown(function() - os.remove('test.out') + os.remove(test_file) os.remove('Xxx1') os.remove('Xxx2') end) @@ -65,7 +66,8 @@ describe('autocommands that delete and unload buffers:', function() endwhile endfunc func WriteToOut() - edit! test.out + edit! ]]..test_file..[[ + $put ='VimLeave done' write endfunc @@ -86,6 +88,6 @@ describe('autocommands that delete and unload buffers:', function() feed_command('q') wait() eq('VimLeave done', - string.match(io.open('test.out', 'r'):read('*all'), "^%s*(.-)%s*$")) + string.match(read_file(test_file), "^%s*(.-)%s*$")) end) end) diff --git a/test/functional/legacy/096_location_list_spec.lua b/test/functional/legacy/096_location_list_spec.lua index 85c4fe0ec4..b21a2085f6 100644 --- a/test/functional/legacy/096_location_list_spec.lua +++ b/test/functional/legacy/096_location_list_spec.lua @@ -11,10 +11,10 @@ local source = helpers.source local clear, command, expect = helpers.clear, helpers.command, helpers.expect describe('location list', function() + local test_file = 'Xtest-096_location_list.out' setup(clear) - teardown(function() - os.remove('test.out') + os.remove(test_file) end) it('is working', function() @@ -70,9 +70,9 @@ describe('location list', function() endfor ]]) - -- Set up the result buffer "test.out". + -- Set up the result buffer. command('enew') - command('w! test.out') + command('w! '..test_file) command('b 1') -- Test A. @@ -99,7 +99,7 @@ describe('location list', function() command([[let locationListFileName = substitute(getline(line('.')), '\([^|]*\)|.*', '\1', '')]]) command('wincmd n') command('wincmd K') - command('b test.out') + command('b '..test_file) -- Prepare test output and write it to the result buffer. command([[let fileName = substitute(fileName, '\\', '/', 'g')]]) @@ -132,7 +132,7 @@ describe('location list', function() command('let numberOfWindowsOpen = winnr("$")') command('wincmd n') command('wincmd K') - command('b test.out') + command('b '..test_file) -- Prepare test output and write it to the result buffer. command('call append(line("$"), "Test B:")') @@ -170,7 +170,7 @@ describe('location list', function() command('let bufferName = expand("%")') command('wincmd n') command('wincmd K') - command('b test.out') + command('b '..test_file) -- Prepare test output and write it to the result buffer. command([[let bufferName = substitute(bufferName, '\\', '/', 'g')]]) diff --git a/test/functional/plugin/shada_spec.lua b/test/functional/plugin/shada_spec.lua index 5a064a759f..5a5b4df1ef 100644 --- a/test/functional/plugin/shada_spec.lua +++ b/test/functional/plugin/shada_spec.lua @@ -3,6 +3,7 @@ local eq, nvim_eval, nvim_command, nvim, exc_exec, funcs, nvim_feed, curbuf = helpers.eq, helpers.eval, helpers.command, helpers.nvim, helpers.exc_exec, helpers.funcs, helpers.feed, helpers.curbuf local neq = helpers.neq +local read_file = helpers.read_file local mpack = require('mpack') @@ -2152,9 +2153,7 @@ describe('plugin/shada.vim', function() end) local shada_eq = function(expected, fname_) - local fd = io.open(fname_) - local mpack_result = fd:read('*a') - fd:close() + local mpack_result = read_file(fname_) mpack_eq(expected, mpack_result) end @@ -2278,7 +2277,7 @@ describe('plugin/shada.vim', function() ' + f file name ["foo"]', ' + l line number 2', ' + c column -200', - }, eol) .. eol, io.open(fname .. '.tst'):read('*a')) + }, eol) .. eol, read_file(fname .. '.tst')) shada_eq({{ timestamp=0, type=8, @@ -2326,7 +2325,7 @@ describe('plugin/shada.vim', function() 'Jump with timestamp ' .. epoch .. ':', ' % Key________ Description Value', ' + n name \'A\'', - }, eol) .. eol, io.open(fname .. '.tst'):read('*a')) + }, eol) .. eol, read_file(fname .. '.tst')) shada_eq({{ timestamp=0, type=8, @@ -2383,7 +2382,7 @@ describe('plugin/shada.vim', function() ' + f file name ["foo"]', ' + l line number 2', ' + c column -200', - }, eol) .. eol, io.open(fname .. '.tst'):read('*a')) + }, eol) .. eol, read_file(fname .. '.tst')) shada_eq({{ timestamp=0, type=8, -- cgit From e243dbdc328b53926009153bd7fca32ebfc1abb2 Mon Sep 17 00:00:00 2001 From: James McCoy Date: Thu, 1 Feb 2018 14:30:31 -0500 Subject: test: man_spec: Fix use of nested [[ quoting Lua (not LuaJIT) complains about the "^[[" strings inside the expect, since it sees them as nested quotes. Change the quoting to [=[ ]=] to avoid the issue. --- test/functional/plugin/man_spec.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'test') diff --git a/test/functional/plugin/man_spec.lua b/test/functional/plugin/man_spec.lua index dc189b8f8e..e5da7932a5 100644 --- a/test/functional/plugin/man_spec.lua +++ b/test/functional/plugin/man_spec.lua @@ -66,13 +66,13 @@ describe(':Man', function() ithis [1mis [3ma [4mtest[0m [4mwith[24m [4mescaped[24m [4mtext[24m]]) - screen:expect([[ + screen:expect([=[ this ^[[1mis ^[[3ma ^[[4mtest^[[0m | ^[[4mwith^[[24m ^[[4mescaped^[[24m ^[[4mtext^[[24^m | ~ | ~ | | - ]]) + ]=]) eval('man#init_pager()') -- cgit From a2dfeb8a16117b51744ef3c37392a61396aa3ae5 Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 2 Feb 2018 00:55:22 +0300 Subject: functests: Improve error reporting in _check_parsing function May be needed for unit tests as well though. --- test/functional/api/vim_spec.lua | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) (limited to 'test') diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua index 39db831fe3..a92acd36b1 100644 --- a/test/functional/api/vim_spec.lua +++ b/test/functional/api/vim_spec.lua @@ -951,9 +951,20 @@ describe('api', function() end end) if not err then - msg = format_string('Error while processing test (%r, %s):\n%s', - str, FLAGS_TO_STR[flags], msg) - error(msg) + if type(msg) == 'table' then + local merr, new_msg = pcall( + format_string, 'table error:\n%s\n\n(%r)', msg.message, msg) + if merr then + msg = new_msg + else + msg = format_string('table error without .message:\n(%r)', + msg) + end + elseif type(msg) ~= 'string' then + msg = format_string('non-string non-table error:\n%r', msg) + end + error(format_string('Error while processing test (%r, %s):\n%s', + str, FLAGS_TO_STR[flags], msg)) end end end -- cgit From 2316a38dd117b0bbd3b6ebb04856029c227633f6 Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 2 Feb 2018 01:23:20 +0300 Subject: tests: Make format_string('%q', ...) output more stable It appears to be different on lua and luajit. --- test/helpers.lua | 23 ++++++----- test/unit/viml/expressions/parser_tests.lua | 60 ++++++++++++++--------------- 2 files changed, 44 insertions(+), 39 deletions(-) (limited to 'test') diff --git a/test/helpers.lua b/test/helpers.lua index faf5c8e7f2..3e7e70fe07 100644 --- a/test/helpers.lua +++ b/test/helpers.lua @@ -423,11 +423,13 @@ format_luav = function(v, indent, opts) if opts.literal_strings then ret = v else - ret = tostring(v):gsub('[\'\\]', '\\%0'):gsub( - '[%z\1-\31]', function(match) - return SUBTBL[match:byte() + 1] - end) - ret = '\'' .. ret .. '\'' + quote = opts.dquote_strings and '"' or '\'' + ret = quote .. tostring(v):gsub( + opts.dquote_strings and '["\\]' or '[\'\\]', + '\\%0'):gsub( + '[%z\1-\31]', function(match) + return SUBTBL[match:byte() + 1] + end) .. quote end elseif type(v) == 'table' then if v == REMOVE_THIS then @@ -490,11 +492,14 @@ local function format_string(fmt, ...) if subfmt:sub(-1) ~= '%' then arg = getarg() end - if subfmt:sub(-1) == 'r' then - -- %r is like %q, but it is supposed to single-quote strings and not - -- double-quote them, and also work not only for strings. + if subfmt:sub(-1) == 'r' or subfmt:sub(-1) == 'q' then + -- %r is like built-in %q, but it is supposed to single-quote strings and + -- not double-quote them, and also work not only for strings. + -- Builtin %q is replaced here as it gives invalid and inconsistent with + -- luajit results for e.g. "\e" on lua: luajit transforms that into `\27`, + -- lua leaves as-is. + arg = format_luav(arg, nil, {dquote_strings = (subfmt:sub(-1) == 'q')}) subfmt = subfmt:sub(1, -2) .. 's' - arg = format_luav(arg) end if subfmt == '%e' then return format_float(arg) diff --git a/test/unit/viml/expressions/parser_tests.lua b/test/unit/viml/expressions/parser_tests.lua index e085d7e932..da61672bb1 100644 --- a/test/unit/viml/expressions/parser_tests.lua +++ b/test/unit/viml/expressions/parser_tests.lua @@ -5025,7 +5025,7 @@ return function(itp, _check_parsing, hl, fmtn) -- 0123456789012345 -- 0 1 ast = { - [[DoubleQuotedString(val="\8\27\12\13\9\\"):0:0:"\b\e\f\r\t\\"]], + [[DoubleQuotedString(val="\008\027\012\r\t\\"):0:0:"\b\e\f\r\t\\"]], }, }, { hl('DoubleQuote', '"'), @@ -5040,7 +5040,7 @@ return function(itp, _check_parsing, hl, fmtn) check_parsing('"\\n\n"', { -- 01234 ast = { - fmtn('DoubleQuotedString', 'val="\\\n\\\n"', ':0:0:"\\n\n"'), + fmtn('DoubleQuotedString', 'val="\\n\\n"', ':0:0:"\\n\n"'), }, }, { hl('DoubleQuote', '"'), @@ -5051,7 +5051,7 @@ return function(itp, _check_parsing, hl, fmtn) check_parsing('"\\x00"', { -- 012345 ast = { - fmtn('DoubleQuotedString', 'val="\\0"', ':0:0:"\\x00"'), + fmtn('DoubleQuotedString', 'val="\\000"', ':0:0:"\\x00"'), }, }, { hl('DoubleQuote', '"'), @@ -5071,7 +5071,7 @@ return function(itp, _check_parsing, hl, fmtn) check_parsing('"\\xF"', { -- 012345 ast = { - fmtn('DoubleQuotedString', 'val="\\15"', ':0:0:"\\xF"'), + fmtn('DoubleQuotedString', 'val="\\015"', ':0:0:"\\xF"'), }, }, { hl('DoubleQuote', '"'), @@ -5126,7 +5126,7 @@ return function(itp, _check_parsing, hl, fmtn) check_parsing('"\\xF', { -- 0123 ast = { - fmtn('DoubleQuotedString', 'val="\\15"', ':0:0:"\\xF'), + fmtn('DoubleQuotedString', 'val="\\015"', ':0:0:"\\xF'), }, err = { arg = '"\\xF', @@ -5190,7 +5190,7 @@ return function(itp, _check_parsing, hl, fmtn) check_parsing('"\\xFX"', { -- 012345 ast = { - fmtn('DoubleQuotedString', 'val="\\15X"', ':0:0:"\\xFX"'), + fmtn('DoubleQuotedString', 'val="\\015X"', ':0:0:"\\xFX"'), }, }, { hl('DoubleQuote', '"'), @@ -5202,7 +5202,7 @@ return function(itp, _check_parsing, hl, fmtn) check_parsing('"\\XFX"', { -- 012345 ast = { - fmtn('DoubleQuotedString', 'val="\\15X"', ':0:0:"\\XFX"'), + fmtn('DoubleQuotedString', 'val="\\015X"', ':0:0:"\\XFX"'), }, }, { hl('DoubleQuote', '"'), @@ -5262,7 +5262,7 @@ return function(itp, _check_parsing, hl, fmtn) check_parsing('"\\x0X"', { -- 012345 ast = { - fmtn('DoubleQuotedString', 'val="\\0X"', ':0:0:"\\x0X"'), + fmtn('DoubleQuotedString', 'val="\\000X"', ':0:0:"\\x0X"'), }, }, { hl('DoubleQuote', '"'), @@ -5274,7 +5274,7 @@ return function(itp, _check_parsing, hl, fmtn) check_parsing('"\\X0X"', { -- 012345 ast = { - fmtn('DoubleQuotedString', 'val="\\0X"', ':0:0:"\\X0X"'), + fmtn('DoubleQuotedString', 'val="\\000X"', ':0:0:"\\X0X"'), }, }, { hl('DoubleQuote', '"'), @@ -5286,7 +5286,7 @@ return function(itp, _check_parsing, hl, fmtn) check_parsing('"\\u0X"', { -- 012345 ast = { - fmtn('DoubleQuotedString', 'val="\\0X"', ':0:0:"\\u0X"'), + fmtn('DoubleQuotedString', 'val="\\000X"', ':0:0:"\\u0X"'), }, }, { hl('DoubleQuote', '"'), @@ -5298,7 +5298,7 @@ return function(itp, _check_parsing, hl, fmtn) check_parsing('"\\U0X"', { -- 012345 ast = { - fmtn('DoubleQuotedString', 'val="\\0X"', ':0:0:"\\U0X"'), + fmtn('DoubleQuotedString', 'val="\\000X"', ':0:0:"\\U0X"'), }, }, { hl('DoubleQuote', '"'), @@ -5310,7 +5310,7 @@ return function(itp, _check_parsing, hl, fmtn) check_parsing('"\\x00X"', { -- 0123456 ast = { - fmtn('DoubleQuotedString', 'val="\\0X"', ':0:0:"\\x00X"'), + fmtn('DoubleQuotedString', 'val="\\000X"', ':0:0:"\\x00X"'), }, }, { hl('DoubleQuote', '"'), @@ -5322,7 +5322,7 @@ return function(itp, _check_parsing, hl, fmtn) check_parsing('"\\X00X"', { -- 0123456 ast = { - fmtn('DoubleQuotedString', 'val="\\0X"', ':0:0:"\\X00X"'), + fmtn('DoubleQuotedString', 'val="\\000X"', ':0:0:"\\X00X"'), }, }, { hl('DoubleQuote', '"'), @@ -5334,7 +5334,7 @@ return function(itp, _check_parsing, hl, fmtn) check_parsing('"\\u00X"', { -- 0123456 ast = { - fmtn('DoubleQuotedString', 'val="\\0X"', ':0:0:"\\u00X"'), + fmtn('DoubleQuotedString', 'val="\\000X"', ':0:0:"\\u00X"'), }, }, { hl('DoubleQuote', '"'), @@ -5346,7 +5346,7 @@ return function(itp, _check_parsing, hl, fmtn) check_parsing('"\\U00X"', { -- 0123456 ast = { - fmtn('DoubleQuotedString', 'val="\\0X"', ':0:0:"\\U00X"'), + fmtn('DoubleQuotedString', 'val="\\000X"', ':0:0:"\\U00X"'), }, }, { hl('DoubleQuote', '"'), @@ -5358,7 +5358,7 @@ return function(itp, _check_parsing, hl, fmtn) check_parsing('"\\u000X"', { -- 01234567 ast = { - fmtn('DoubleQuotedString', 'val="\\0X"', ':0:0:"\\u000X"'), + fmtn('DoubleQuotedString', 'val="\\000X"', ':0:0:"\\u000X"'), }, }, { hl('DoubleQuote', '"'), @@ -5370,7 +5370,7 @@ return function(itp, _check_parsing, hl, fmtn) check_parsing('"\\U000X"', { -- 01234567 ast = { - fmtn('DoubleQuotedString', 'val="\\0X"', ':0:0:"\\U000X"'), + fmtn('DoubleQuotedString', 'val="\\000X"', ':0:0:"\\U000X"'), }, }, { hl('DoubleQuote', '"'), @@ -5382,7 +5382,7 @@ return function(itp, _check_parsing, hl, fmtn) check_parsing('"\\u0000X"', { -- 012345678 ast = { - fmtn('DoubleQuotedString', 'val="\\0X"', ':0:0:"\\u0000X"'), + fmtn('DoubleQuotedString', 'val="\\000X"', ':0:0:"\\u0000X"'), }, }, { hl('DoubleQuote', '"'), @@ -5394,7 +5394,7 @@ return function(itp, _check_parsing, hl, fmtn) check_parsing('"\\U0000X"', { -- 012345678 ast = { - fmtn('DoubleQuotedString', 'val="\\0X"', ':0:0:"\\U0000X"'), + fmtn('DoubleQuotedString', 'val="\\000X"', ':0:0:"\\U0000X"'), }, }, { hl('DoubleQuote', '"'), @@ -5406,7 +5406,7 @@ return function(itp, _check_parsing, hl, fmtn) check_parsing('"\\U00000X"', { -- 0123456789 ast = { - fmtn('DoubleQuotedString', 'val="\\0X"', ':0:0:"\\U00000X"'), + fmtn('DoubleQuotedString', 'val="\\000X"', ':0:0:"\\U00000X"'), }, }, { hl('DoubleQuote', '"'), @@ -5419,7 +5419,7 @@ return function(itp, _check_parsing, hl, fmtn) -- 01234567890 -- 0 1 ast = { - fmtn('DoubleQuotedString', 'val="\\0X"', ':0:0:"\\U000000X"'), + fmtn('DoubleQuotedString', 'val="\\000X"', ':0:0:"\\U000000X"'), }, }, { hl('DoubleQuote', '"'), @@ -5432,7 +5432,7 @@ return function(itp, _check_parsing, hl, fmtn) -- 012345678901 -- 0 1 ast = { - fmtn('DoubleQuotedString', 'val="\\0X"', ':0:0:"\\U0000000X"'), + fmtn('DoubleQuotedString', 'val="\\000X"', ':0:0:"\\U0000000X"'), }, }, { hl('DoubleQuote', '"'), @@ -5445,7 +5445,7 @@ return function(itp, _check_parsing, hl, fmtn) -- 0123456789012 -- 0 1 ast = { - fmtn('DoubleQuotedString', 'val="\\0X"', ':0:0:"\\U00000000X"'), + fmtn('DoubleQuotedString', 'val="\\000X"', ':0:0:"\\U00000000X"'), }, }, { hl('DoubleQuote', '"'), @@ -5506,7 +5506,7 @@ return function(itp, _check_parsing, hl, fmtn) check_parsing('"\\0"', { -- 0123 ast = { - fmtn('DoubleQuotedString', 'val="\\0"', ':0:0:"\\0"'), + fmtn('DoubleQuotedString', 'val="\\000"', ':0:0:"\\0"'), }, }, { hl('DoubleQuote', '"'), @@ -5517,7 +5517,7 @@ return function(itp, _check_parsing, hl, fmtn) check_parsing('"\\00"', { -- 01234 ast = { - fmtn('DoubleQuotedString', 'val="\\0"', ':0:0:"\\00"'), + fmtn('DoubleQuotedString', 'val="\\000"', ':0:0:"\\00"'), }, }, { hl('DoubleQuote', '"'), @@ -5528,7 +5528,7 @@ return function(itp, _check_parsing, hl, fmtn) check_parsing('"\\000"', { -- 012345 ast = { - fmtn('DoubleQuotedString', 'val="\\0"', ':0:0:"\\000"'), + fmtn('DoubleQuotedString', 'val="\\000"', ':0:0:"\\000"'), }, }, { hl('DoubleQuote', '"'), @@ -5620,7 +5620,7 @@ return function(itp, _check_parsing, hl, fmtn) check_parsing('"\\"', { -- 012345 ast = { - fmtn('DoubleQuotedString', 'val="\\21"', ':0:0:"\\"'), + fmtn('DoubleQuotedString', 'val="\\021"', ':0:0:"\\"'), }, }, { hl('DoubleQuote', '"'), @@ -7119,7 +7119,7 @@ return function(itp, _check_parsing, hl, fmtn) 'Or:0:0:|', children = { 'Missing:0:0:', - fmtn('DoubleQuotedString', 'val="\\27"', ':0:1:"\\e"'), + fmtn('DoubleQuotedString', 'val="\\027"', ':0:1:"\\e"'), }, }, }, @@ -7180,9 +7180,9 @@ return function(itp, _check_parsing, hl, fmtn) hl('InvalidDoubleQuotedUnknownEscape', '\\<'), }) check_parsing('"\\1', { - -- 012 + -- 01 2 ast = { - fmtn('DoubleQuotedString', 'val="\\1"', ':0:0:"\\1'), + fmtn('DoubleQuotedString', 'val="\\001"', ':0:0:"\\1'), }, err = { arg = '"\\1', -- cgit From de10ea55f370cd57b79d23c2ae09bebf154a0f1a Mon Sep 17 00:00:00 2001 From: James McCoy Date: Fri, 2 Feb 2018 08:57:17 -0500 Subject: lint --- test/helpers.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'test') diff --git a/test/helpers.lua b/test/helpers.lua index 3e7e70fe07..1c64f41b65 100644 --- a/test/helpers.lua +++ b/test/helpers.lua @@ -423,7 +423,7 @@ format_luav = function(v, indent, opts) if opts.literal_strings then ret = v else - quote = opts.dquote_strings and '"' or '\'' + local quote = opts.dquote_strings and '"' or '\'' ret = quote .. tostring(v):gsub( opts.dquote_strings and '["\\]' or '[\'\\]', '\\%0'):gsub( @@ -493,7 +493,7 @@ local function format_string(fmt, ...) arg = getarg() end if subfmt:sub(-1) == 'r' or subfmt:sub(-1) == 'q' then - -- %r is like built-in %q, but it is supposed to single-quote strings and + -- %r is like built-in %q, but it is supposed to single-quote strings and -- not double-quote them, and also work not only for strings. -- Builtin %q is replaced here as it gives invalid and inconsistent with -- luajit results for e.g. "\e" on lua: luajit transforms that into `\27`, -- cgit From 9af14506e5ae90a3a62814e20d00765fd65d6294 Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Sat, 13 Jan 2018 10:14:41 +0100 Subject: shell: add test for binary and multibyte output Also update existing tests for new (vim-compatible) newline behavior --- test/functional/ex_cmds/bang_filter_spec.lua | 44 +++++++++++++++++++-------- test/functional/fixtures/shell_data.txt | Bin 0 -> 50 bytes test/functional/ui/output_spec.lua | 4 +-- 3 files changed, 33 insertions(+), 15 deletions(-) create mode 100644 test/functional/fixtures/shell_data.txt (limited to 'test') diff --git a/test/functional/ex_cmds/bang_filter_spec.lua b/test/functional/ex_cmds/bang_filter_spec.lua index aaec983b73..636d732161 100644 --- a/test/functional/ex_cmds/bang_filter_spec.lua +++ b/test/functional/ex_cmds/bang_filter_spec.lua @@ -3,13 +3,14 @@ local helpers = require('test.functional.helpers')(after_each) local feed, command, clear = helpers.feed, helpers.command, helpers.clear local mkdir, write_file, rmdir = helpers.mkdir, helpers.write_file, helpers.rmdir +local feed_command = helpers.feed_command if helpers.pending_win32(pending) then return end local Screen = require('test.functional.ui.screen') -describe('issues', function() +describe(':! command', function() local screen before_each(function() @@ -19,7 +20,12 @@ describe('issues', function() write_file('bang_filter_spec/f1', 'f1') write_file('bang_filter_spec/f2', 'f2') write_file('bang_filter_spec/f3', 'f3') - screen = Screen.new() + screen = Screen.new(53,10) + screen:set_default_attr_ids({ + [1] = {bold = true, foreground = Screen.colors.Blue1}, + [2] = {foreground = Screen.colors.Blue1}, + [3] = {bold = true, foreground = Screen.colors.SeaGreen4}, + }) screen:attach() end) @@ -27,25 +33,37 @@ describe('issues', function() rmdir('bang_filter_spec') end) - it('#3269 Last line of shell output is not truncated', function() + it("doesn't truncate Last line of shell output #3269", function() command([[nnoremap \l :!ls bang_filter_spec]]) feed([[\l]]) screen:expect([[ - ~ | - ~ | - ~ | - ~ | - ~ | - ~ | - ~ | - ~ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| :!ls bang_filter_spec | - | f1 | f2 | f3 | - Press ENTER or type command to continue^ | + | + {3:Press ENTER or type command to continue}^ | ]]) end) + it('handles binary and multibyte data', function() + feed_command('!cat test/functional/fixtures/shell_data.txt') + screen:expect([[ + {1:~ }| + {1:~ }| + {1:~ }| + :!cat test/functional/fixtures/shell_data.txt | + {2:^@^A^B^C^D^E^F^G^H} | + {2:^N^O^P^Q^R^S^T^U^V^W^X^Y^Z^[^\^]^^^_} | + ö 한글 {2:} | + t {2:} | + | + {3:Press ENTER or type command to continue}^ | + ]]) + end) + end) diff --git a/test/functional/fixtures/shell_data.txt b/test/functional/fixtures/shell_data.txt new file mode 100644 index 0000000000..ef3506c5b1 Binary files /dev/null and b/test/functional/fixtures/shell_data.txt differ diff --git a/test/functional/ui/output_spec.lua b/test/functional/ui/output_spec.lua index c6d564e8dc..da3f474e08 100644 --- a/test/functional/ui/output_spec.lua +++ b/test/functional/ui/output_spec.lua @@ -33,8 +33,8 @@ describe("shell command :!", function() {4:~ }| {4:~ }| {4:~ }| + {4:~ }| :!printf foo; sleep 200 | - | foo | {3:-- TERMINAL --} | ]]) @@ -56,11 +56,11 @@ describe("shell command :!", function() -- Final chunk of output should always be displayed, never skipped. -- (Throttling is non-deterministic, this test is merely a sanity check.) screen:expect([[ - XXXXXXXXXX 2996 | XXXXXXXXXX 2997 | XXXXXXXXXX 2998 | XXXXXXXXXX 2999 | XXXXXXXXXX 3000 | + | {10:Press ENTER or type command to continue}{1: } | {3:-- TERMINAL --} | ]]) -- cgit From 4e7d85e6356d88944a60c447a9754b8b37407c12 Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Sun, 21 Jan 2018 19:58:19 +0100 Subject: shell: update `execute('!cmd')` test to new behavior And similarly nvim_command_output test --- test/functional/api/vim_spec.lua | 6 ++++-- test/functional/eval/execute_spec.lua | 10 +++++++--- 2 files changed, 11 insertions(+), 5 deletions(-) (limited to 'test') diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua index a92acd36b1..0a0cb2e91c 100644 --- a/test/functional/api/vim_spec.lua +++ b/test/functional/api/vim_spec.lua @@ -15,6 +15,7 @@ local command = helpers.command local intchar2lua = global_helpers.intchar2lua local format_string = global_helpers.format_string local mergedicts_copy = global_helpers.mergedicts_copy +local uname = global_helpers.uname describe('api', function() before_each(clear) @@ -99,8 +100,9 @@ describe('api', function() [[echo nvim_command_output('echo "nested1\nnested2"') | ls]])) end) - it('does not return shell |:!| output', function() - eq(':!echo "foo"\r\n', nvim('command_output', [[!echo "foo"]])) + it('returns shell |:!| output', function() + local win_lf = (uname() == 'Windows' and '\r') or '' + eq(':!echo foo\r\n\nfoo'..win_lf..'\n', nvim('command_output', [[!echo foo]])) end) it("parse error: fails (specific error), does NOT update v:errmsg", function() diff --git a/test/functional/eval/execute_spec.lua b/test/functional/eval/execute_spec.lua index 91966ed3dd..c866359520 100644 --- a/test/functional/eval/execute_spec.lua +++ b/test/functional/eval/execute_spec.lua @@ -1,4 +1,5 @@ local helpers = require('test.functional.helpers')(after_each) +local global_helpers = require('test.helpers') local eq = helpers.eq local eval = helpers.eval local clear = helpers.clear @@ -9,6 +10,7 @@ local funcs = helpers.funcs local Screen = require('test.functional.ui.screen') local command = helpers.command local feed = helpers.feed +local uname = global_helpers.uname describe('execute()', function() before_each(clear) @@ -118,9 +120,11 @@ describe('execute()', function() feed('') end) - -- This matches Vim behavior. - it('does not capture shell-command output', function() - eq('\n:!echo "foo"\13\n', funcs.execute('!echo "foo"')) + -- This deviates from vim behavior, but is consistent + -- with how nvim currently displays the output. + it('does capture shell-command output', function() + local win_lf = (uname() == 'Windows' and '\13') or '' + eq('\n:!echo foo\r\n\nfoo'..win_lf..'\n', funcs.execute('!echo foo')) end) describe('{silent} argument', function() -- cgit From 6744f48d884902483f95c9b89877da36032e2677 Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Sat, 13 Jan 2018 10:15:23 +0100 Subject: tests: cleanup bufhl test --- test/functional/ui/bufhl_spec.lua | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) (limited to 'test') diff --git a/test/functional/ui/bufhl_spec.lua b/test/functional/ui/bufhl_spec.lua index 091c45596d..5b38921e50 100644 --- a/test/functional/ui/bufhl_spec.lua +++ b/test/functional/ui/bufhl_spec.lua @@ -2,11 +2,11 @@ local helpers = require('test.functional.helpers')(after_each) local Screen = require('test.functional.ui.screen') local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert -local command, request, neq = helpers.command, helpers.request, helpers.neq +local command, neq = helpers.command, helpers.neq +local curbufmeths = helpers.curbufmeths describe('Buffer highlighting', function() local screen - local curbuf before_each(function() clear() @@ -25,21 +25,14 @@ describe('Buffer highlighting', function() [9] = {foreground = Screen.colors.SlateBlue, underline = true}, [10] = {foreground = Screen.colors.Red} }) - curbuf = request('nvim_get_current_buf') end) after_each(function() screen:detach() end) - local function add_hl(...) - return request('nvim_buf_add_highlight', curbuf, ...) - end - - local function clear_hl(...) - return request('nvim_buf_clear_highlight', curbuf, ...) - end - + local add_hl = curbufmeths.add_highlight + local clear_hl = curbufmeths.clear_highlight it('works', function() insert([[ -- cgit From d73dd1588ce6be4aa90b83fdcf4efc31ef743aec Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Mon, 5 Feb 2018 23:38:30 +0100 Subject: :terminal Enter/Leave should not increment jumplist The old behavior is probably not justified, for the usual reason: terminal buffers may have interactive processes, so cursor placement is arbitrary, therefore tracking it in the jumplist is useless (or worse). N.B.: per the docstring for `checkpcmark()` it looks like we were calling `checkpcmark()` and `setpcmark()` in the wrong order. closes #3723 --- test/functional/terminal/ex_terminal_spec.lua | 50 ++++++++++++++++++++++----- 1 file changed, 41 insertions(+), 9 deletions(-) (limited to 'test') diff --git a/test/functional/terminal/ex_terminal_spec.lua b/test/functional/terminal/ex_terminal_spec.lua index ee92ba6865..0a30b9bb02 100644 --- a/test/functional/terminal/ex_terminal_spec.lua +++ b/test/functional/terminal/ex_terminal_spec.lua @@ -2,8 +2,11 @@ local helpers = require('test.functional.helpers')(after_each) local Screen = require('test.functional.ui.screen') local clear, wait, nvim = helpers.clear, helpers.wait, helpers.nvim local nvim_dir, source, eq = helpers.nvim_dir, helpers.source, helpers.eq +local feed = helpers.feed local feed_command, eval = helpers.feed_command, helpers.eval +local funcs = helpers.funcs local retry = helpers.retry +local ok = helpers.ok local iswin = helpers.iswin describe(':terminal', function() @@ -24,13 +27,16 @@ describe(':terminal', function() ]]) -- Invoke a command that emits frequent terminal activity. if iswin() then - feed_command([[terminal for /L \\%I in (1,0,2) do echo \\%I]]) + feed_command([[terminal for /L \%I in (1,0,2) do echo \%I]]) else feed_command([[terminal while true; do echo X; done]]) end - helpers.feed([[]]) + feed([[]]) wait() - screen:sleep(10) -- Let some terminal activity happen. + -- Wait for some terminal activity. + retry(nil, 4000, function() + ok(funcs.line('$') > 6) + end) feed_command("messages") screen:expect([[ msg1 | @@ -46,7 +52,7 @@ describe(':terminal', function() else feed_command([[terminal while true; do echo foo; sleep .1; done]]) end - helpers.feed([[M]]) -- move cursor away from last line + feed([[M]]) -- move cursor away from last line wait() eq(3, eval("line('$')")) -- window height eq(2, eval("line('.')")) -- cursor is in the middle @@ -56,6 +62,32 @@ describe(':terminal', function() eq(2, eval("line('.')")) -- cursor stays where we put it end) + it('Enter/Leave does not increment jumplist #3723', function() + feed_command('terminal') + local function enter_and_leave() + local lines_before = funcs.line('$') + -- Create a new line (in the shell). For a normal buffer this + -- increments the jumplist; for a terminal-buffer it should not. #3723 + feed('i') + wait() + feed('') + wait() + feed([[]]) + wait() + -- Wait for >=1 lines to be created. + retry(nil, 4000, function() + ok(funcs.line('$') > lines_before) + end) + end + enter_and_leave() + enter_and_leave() + enter_and_leave() + ok(funcs.line('$') > 6) -- Verify assumption. + local jumps = funcs.split(funcs.execute('jumps'), '\n') + eq(' jump line col file/text', jumps[1]) + eq(3, #jumps) + end) + end) describe(':terminal (with fake shell)', function() @@ -151,12 +183,12 @@ describe(':terminal (with fake shell)', function() it('ignores writes if the backing stream closes', function() terminal_with_fake_shell() - helpers.feed('iiXXXXXXX') + feed('iiXXXXXXX') wait() -- Race: Though the shell exited (and streams were closed by SIGCHLD -- handler), :terminal cleanup is pending on the main-loop. -- This write should be ignored (not crash, #5445). - helpers.feed('iiYYYYYYY') + feed('iiYYYYYYY') eq(2, eval("1+1")) -- Still alive? end) @@ -175,7 +207,7 @@ describe(':terminal (with fake shell)', function() :terminal | ]]) eq('term://', string.match(eval('bufname("%")'), "^term://")) - helpers.feed([[]]) + feed([[]]) feed_command([[find */shadacat.py]]) if iswin() then eq('scripts\\shadacat.py', eval('bufname("%")')) @@ -192,9 +224,9 @@ describe(':terminal (with fake shell)', function() [Process exited 0] | :terminal echo "scripts/shadacat.py" | ]]) - helpers.feed([[]]) + feed([[]]) eq('term://', string.match(eval('bufname("%")'), "^term://")) - helpers.feed([[ggf"lgf]]) + feed([[ggf"lgf]]) eq('scripts/shadacat.py', eval('bufname("%")')) end) -- cgit From 352a51e8313d05c4701f468a79540a2410c77b23 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Wed, 10 May 2017 15:04:49 +0200 Subject: test: :! print binary data, control chars closes #5442 closes #4142 ref #6618 ref #4376 ref #7844 ref #2958 ref #4338 --- test/functional/api/vim_spec.lua | 4 +- test/functional/eval/execute_spec.lua | 5 +-- test/functional/ui/output_spec.lua | 70 ++++++++++++++++++++++++++++++++++- 3 files changed, 72 insertions(+), 7 deletions(-) (limited to 'test') diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua index 0a0cb2e91c..1faed4e9b1 100644 --- a/test/functional/api/vim_spec.lua +++ b/test/functional/api/vim_spec.lua @@ -11,11 +11,11 @@ local funcs = helpers.funcs local request = helpers.request local meth_pcall = helpers.meth_pcall local command = helpers.command +local iswin = helpers.iswin local intchar2lua = global_helpers.intchar2lua local format_string = global_helpers.format_string local mergedicts_copy = global_helpers.mergedicts_copy -local uname = global_helpers.uname describe('api', function() before_each(clear) @@ -101,7 +101,7 @@ describe('api', function() end) it('returns shell |:!| output', function() - local win_lf = (uname() == 'Windows' and '\r') or '' + local win_lf = iswin() and '\r' or '' eq(':!echo foo\r\n\nfoo'..win_lf..'\n', nvim('command_output', [[!echo foo]])) end) diff --git a/test/functional/eval/execute_spec.lua b/test/functional/eval/execute_spec.lua index c866359520..183884a51e 100644 --- a/test/functional/eval/execute_spec.lua +++ b/test/functional/eval/execute_spec.lua @@ -1,5 +1,4 @@ local helpers = require('test.functional.helpers')(after_each) -local global_helpers = require('test.helpers') local eq = helpers.eq local eval = helpers.eval local clear = helpers.clear @@ -10,7 +9,7 @@ local funcs = helpers.funcs local Screen = require('test.functional.ui.screen') local command = helpers.command local feed = helpers.feed -local uname = global_helpers.uname +local iswin = helpers.iswin describe('execute()', function() before_each(clear) @@ -123,7 +122,7 @@ describe('execute()', function() -- This deviates from vim behavior, but is consistent -- with how nvim currently displays the output. it('does capture shell-command output', function() - local win_lf = (uname() == 'Windows' and '\13') or '' + local win_lf = iswin() and '\13' or '' eq('\n:!echo foo\r\n\nfoo'..win_lf..'\n', funcs.execute('!echo foo')) end) diff --git a/test/functional/ui/output_spec.lua b/test/functional/ui/output_spec.lua index da3f474e08..c42c0b26c6 100644 --- a/test/functional/ui/output_spec.lua +++ b/test/functional/ui/output_spec.lua @@ -1,9 +1,14 @@ +local Screen = require('test.functional.ui.screen') local session = require('test.functional.helpers')(after_each) local child_session = require('test.functional.terminal.helpers') - -if session.pending_win32(pending) then return end +local eq = session.eq +local eval = session.eval +local feed = session.feed +local iswin = session.iswin describe("shell command :!", function() + if session.pending_win32(pending) then return end + local screen before_each(function() session.clear() @@ -66,3 +71,64 @@ describe("shell command :!", function() ]]) end) end) + +describe("shell command :!", function() + before_each(function() + session.clear() + end) + + it("cat a binary file #4142", function() + feed(":exe 'silent !cat '.shellescape(v:progpath)") + eq(2, eval('1+1')) -- Still alive? + end) + + it([[display \x08 char #4142]], function() + feed(":silent !echo \08") + eq(2, eval('1+1')) -- Still alive? + end) + + it([[handles control codes]], function() + if iswin() then + pending('missing printf', function() end) + return + end + local screen = Screen.new(50, 4) + screen:attach() + -- Print TAB chars. #2958 + feed([[:!printf '1\t2\t3']]) + screen:expect([[ + ~ | + :!printf '1\t2\t3' | + 1 2 3 | + Press ENTER or type command to continue^ | + ]]) + feed([[]]) + -- Print BELL control code. #4338 + feed([[:!printf '\x07\x07\x07\x07text']]) + screen:expect([[ + ~ | + :!printf '\x07\x07\x07\x07text' | + ^G^G^G^Gtext | + Press ENTER or type command to continue^ | + ]]) + feed([[]]) + -- Print BS control code. + feed([[:echo system('printf ''\x08\n''')]]) + screen:expect([[ + ~ | + ^H | + | + Press ENTER or type command to continue^ | + ]]) + feed([[]]) + -- Print LF control code. + feed([[:!printf '\n']]) + screen:expect([[ + :!printf '\n' | + | + | + Press ENTER or type command to continue^ | + ]]) + feed([[]]) + end) +end) -- cgit From a6136e8b0be70efb42801267d11a9e5edca5e377 Mon Sep 17 00:00:00 2001 From: Michael Brailsford Date: Tue, 30 Jan 2018 13:05:59 -0600 Subject: screen.c: resolve neovim issue #7937 --- test/functional/ui/spell_spec.lua | 45 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 test/functional/ui/spell_spec.lua (limited to 'test') diff --git a/test/functional/ui/spell_spec.lua b/test/functional/ui/spell_spec.lua new file mode 100644 index 0000000000..968547491e --- /dev/null +++ b/test/functional/ui/spell_spec.lua @@ -0,0 +1,45 @@ +-- Test for scenarios involving 'spell' + +local helpers = require('test.functional.helpers')(after_each) +local Screen = require('test.functional.ui.screen') + +describe('combining long lines', function() + local screen + + before_each(function() + helpers.clear() + screen = Screen.new(80, 8) + screen:attach() + screen:set_default_attr_ids( { + [0] = {bold=true, foreground=Screen.colors.Blue}, + [1] = {special = Screen.colors.Red, undercurl = true} + }) + end) + + after_each(function() + screen:detach() + end) + + it('#7937 successfully joins lines with "set spell"', function() + helpers.feed_command('set spell') + helpers.insert([[ + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod + tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, + quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo + consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse + cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat + non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. + ]]) + helpers.feed('ggJJJJJJ0') + screen:expect([[ + {1:^Lorem} {1:ipsum} dolor sit {1:amet}, {1:consectetur} {1:adipiscing} {1:elit}, {1:sed} do {1:eiusmod} {1:tempor} {1:i}| + {1:ncididunt} {1:ut} {1:labore} {1:et} {1:dolore} {1:magna} {1:aliqua}. {1:Ut} {1:enim} ad minim {1:veniam}, {1:quis} {1:nostru}| + {1:d} {1:exercitation} {1:ullamco} {1:laboris} {1:nisi} {1:ut} {1:aliquip} ex ea {1:commodo} {1:consequat}. {1:Duis} {1:aut}| + {1:e} {1:irure} dolor in {1:reprehenderit} in {1:voluptate} {1:velit} {1:esse} {1:cillum} {1:dolore} {1:eu} {1:fugiat} {1:n}| + {1:ulla} {1:pariatur}. {1:Excepteur} {1:sint} {1:occaecat} {1:cupidatat} non {1:proident}, {1:sunt} in culpa {1:qui}| + {1:officia} {1:deserunt} {1:mollit} {1:anim} id est {1:laborum}. | + {0:~ }| + | + ]]) + end) +end) -- cgit From a265334406ca9ec84732acd1fde93300ce00310b Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Thu, 8 Feb 2018 01:11:54 +0100 Subject: defaults: sidescroll=1 ref #6289 --- test/functional/ui/highlight_spec.lua | 1 + test/functional/ui/mouse_spec.lua | 2 ++ 2 files changed, 3 insertions(+) (limited to 'test') diff --git a/test/functional/ui/highlight_spec.lua b/test/functional/ui/highlight_spec.lua index 2252e3580f..95fb2ce21c 100644 --- a/test/functional/ui/highlight_spec.lua +++ b/test/functional/ui/highlight_spec.lua @@ -905,6 +905,7 @@ describe("'winhighlight' highlight", function() end) it('background applies also to non-text', function() + command('set sidescroll=0') insert('Lorem ipsum dolor sit amet ') command('set shiftwidth=2') feed('>>') diff --git a/test/functional/ui/mouse_spec.lua b/test/functional/ui/mouse_spec.lua index 13820af3b8..e5708d51ef 100644 --- a/test/functional/ui/mouse_spec.lua +++ b/test/functional/ui/mouse_spec.lua @@ -3,6 +3,7 @@ local Screen = require('test.functional.ui.screen') local clear, feed, meths = helpers.clear, helpers.feed, helpers.meths local insert, feed_command = helpers.insert, helpers.feed_command local eq, funcs = helpers.eq, helpers.funcs +local command = helpers.command describe('ui/mouse/input', function() local screen @@ -706,6 +707,7 @@ describe('ui/mouse/input', function() end) it('horizontal scrolling', function() + command('set sidescroll=0') feed(":set nowrap") feed("a 20Ab") -- cgit From 60ce7d9e0a0606e20a17f90d78e9d8319114273b Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Wed, 7 Feb 2018 10:23:19 +0100 Subject: shell: support bell --- test/functional/ex_cmds/bang_filter_spec.lua | 8 ++++++-- test/functional/ui/output_spec.lua | 7 +++++-- 2 files changed, 11 insertions(+), 4 deletions(-) (limited to 'test') diff --git a/test/functional/ex_cmds/bang_filter_spec.lua b/test/functional/ex_cmds/bang_filter_spec.lua index 636d732161..3559828aa8 100644 --- a/test/functional/ex_cmds/bang_filter_spec.lua +++ b/test/functional/ex_cmds/bang_filter_spec.lua @@ -4,6 +4,7 @@ local helpers = require('test.functional.helpers')(after_each) local feed, command, clear = helpers.feed, helpers.command, helpers.clear local mkdir, write_file, rmdir = helpers.mkdir, helpers.write_file, helpers.rmdir local feed_command = helpers.feed_command +local eq = helpers.eq if helpers.pending_win32(pending) then return end @@ -52,18 +53,21 @@ describe(':! command', function() it('handles binary and multibyte data', function() feed_command('!cat test/functional/fixtures/shell_data.txt') + screen.bell = false screen:expect([[ {1:~ }| {1:~ }| {1:~ }| :!cat test/functional/fixtures/shell_data.txt | - {2:^@^A^B^C^D^E^F^G^H} | + {2:^@^A^B^C^D^E^F^H} | {2:^N^O^P^Q^R^S^T^U^V^W^X^Y^Z^[^\^]^^^_} | ö 한글 {2:} | t {2:} | | {3:Press ENTER or type command to continue}^ | - ]]) + ]], nil, nil, function() + eq(true, screen.bell) + end) end) end) diff --git a/test/functional/ui/output_spec.lua b/test/functional/ui/output_spec.lua index c42c0b26c6..de0b8ba69b 100644 --- a/test/functional/ui/output_spec.lua +++ b/test/functional/ui/output_spec.lua @@ -104,13 +104,16 @@ describe("shell command :!", function() ]]) feed([[]]) -- Print BELL control code. #4338 + screen.bell = false feed([[:!printf '\x07\x07\x07\x07text']]) screen:expect([[ ~ | :!printf '\x07\x07\x07\x07text' | - ^G^G^G^Gtext | + text | Press ENTER or type command to continue^ | - ]]) + ]], nil, nil, function() + eq(true, screen.bell) + end) feed([[]]) -- Print BS control code. feed([[:echo system('printf ''\x08\n''')]]) -- cgit From 01cdeff62656225ebbac237fb7c212d622ac43d3 Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Wed, 7 Feb 2018 10:33:05 +0100 Subject: tests: integrate ex_cmds/bang_filter_spec into ui/output_spec they test the same thing. Filtering is tested elsewhere. --- test/functional/ex_cmds/bang_filter_spec.lua | 73 ------------------------ test/functional/ui/output_spec.lua | 84 ++++++++++++++++++++++++---- 2 files changed, 73 insertions(+), 84 deletions(-) delete mode 100644 test/functional/ex_cmds/bang_filter_spec.lua (limited to 'test') diff --git a/test/functional/ex_cmds/bang_filter_spec.lua b/test/functional/ex_cmds/bang_filter_spec.lua deleted file mode 100644 index 3559828aa8..0000000000 --- a/test/functional/ex_cmds/bang_filter_spec.lua +++ /dev/null @@ -1,73 +0,0 @@ --- Specs for bang/filter commands - -local helpers = require('test.functional.helpers')(after_each) -local feed, command, clear = helpers.feed, helpers.command, helpers.clear -local mkdir, write_file, rmdir = helpers.mkdir, helpers.write_file, helpers.rmdir -local feed_command = helpers.feed_command -local eq = helpers.eq - -if helpers.pending_win32(pending) then return end - -local Screen = require('test.functional.ui.screen') - - -describe(':! command', function() - local screen - - before_each(function() - clear() - rmdir('bang_filter_spec') - mkdir('bang_filter_spec') - write_file('bang_filter_spec/f1', 'f1') - write_file('bang_filter_spec/f2', 'f2') - write_file('bang_filter_spec/f3', 'f3') - screen = Screen.new(53,10) - screen:set_default_attr_ids({ - [1] = {bold = true, foreground = Screen.colors.Blue1}, - [2] = {foreground = Screen.colors.Blue1}, - [3] = {bold = true, foreground = Screen.colors.SeaGreen4}, - }) - screen:attach() - end) - - after_each(function() - rmdir('bang_filter_spec') - end) - - it("doesn't truncate Last line of shell output #3269", function() - command([[nnoremap \l :!ls bang_filter_spec]]) - feed([[\l]]) - screen:expect([[ - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - :!ls bang_filter_spec | - f1 | - f2 | - f3 | - | - {3:Press ENTER or type command to continue}^ | - ]]) - end) - - it('handles binary and multibyte data', function() - feed_command('!cat test/functional/fixtures/shell_data.txt') - screen.bell = false - screen:expect([[ - {1:~ }| - {1:~ }| - {1:~ }| - :!cat test/functional/fixtures/shell_data.txt | - {2:^@^A^B^C^D^E^F^H} | - {2:^N^O^P^Q^R^S^T^U^V^W^X^Y^Z^[^\^]^^^_} | - ö 한글 {2:} | - t {2:} | - | - {3:Press ENTER or type command to continue}^ | - ]], nil, nil, function() - eq(true, screen.bell) - end) - end) - -end) diff --git a/test/functional/ui/output_spec.lua b/test/functional/ui/output_spec.lua index de0b8ba69b..4e79c2e6cb 100644 --- a/test/functional/ui/output_spec.lua +++ b/test/functional/ui/output_spec.lua @@ -1,19 +1,23 @@ local Screen = require('test.functional.ui.screen') -local session = require('test.functional.helpers')(after_each) +local helpers = require('test.functional.helpers')(after_each) local child_session = require('test.functional.terminal.helpers') -local eq = session.eq -local eval = session.eval -local feed = session.feed -local iswin = session.iswin +local mkdir, write_file, rmdir = helpers.mkdir, helpers.write_file, helpers.rmdir +local eq = helpers.eq +local eval = helpers.eval +local feed = helpers.feed +local feed_command = helpers.feed_command +local iswin = helpers.iswin +local clear = helpers.clear +local command = helpers.command describe("shell command :!", function() - if session.pending_win32(pending) then return end + if helpers.pending_win32(pending) then return end local screen before_each(function() - session.clear() - screen = child_session.screen_setup(0, '["'..session.nvim_prog.. - '", "-u", "NONE", "-i", "NONE", "--cmd", "'..session.nvim_set..'"]') + clear() + screen = child_session.screen_setup(0, '["'..helpers.nvim_prog.. + '", "-u", "NONE", "-i", "NONE", "--cmd", "'..helpers.nvim_set..'"]') screen:expect([[ {1: } | {4:~ }| @@ -46,7 +50,7 @@ describe("shell command :!", function() end) it("throttles shell-command output greater than ~10KB", function() - if os.getenv("TRAVIS") and session.os_name() == "osx" then + if os.getenv("TRAVIS") and helpers.os_name() == "osx" then pending("[Unreliable on Travis macOS.]", function() end) return end @@ -74,7 +78,7 @@ end) describe("shell command :!", function() before_each(function() - session.clear() + clear() end) it("cat a binary file #4142", function() @@ -134,4 +138,62 @@ describe("shell command :!", function() ]]) feed([[]]) end) + + describe('', function() + local screen + before_each(function() + rmdir('bang_filter_spec') + mkdir('bang_filter_spec') + write_file('bang_filter_spec/f1', 'f1') + write_file('bang_filter_spec/f2', 'f2') + write_file('bang_filter_spec/f3', 'f3') + screen = Screen.new(53,10) + screen:set_default_attr_ids({ + [1] = {bold = true, foreground = Screen.colors.Blue1}, + [2] = {foreground = Screen.colors.Blue1}, + [3] = {bold = true, foreground = Screen.colors.SeaGreen4}, + }) + screen:attach() + end) + + after_each(function() + rmdir('bang_filter_spec') + end) + + it("doesn't truncate Last line of shell output #3269", function() + command([[nnoremap \l :!ls bang_filter_spec]]) + feed([[\l]]) + screen:expect([[ + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + :!ls bang_filter_spec | + f1 | + f2 | + f3 | + | + {3:Press ENTER or type command to continue}^ | + ]]) + end) + + it('handles binary and multibyte data', function() + feed_command('!cat test/functional/fixtures/shell_data.txt') + screen.bell = false + screen:expect([[ + {1:~ }| + {1:~ }| + {1:~ }| + :!cat test/functional/fixtures/shell_data.txt | + {2:^@^A^B^C^D^E^F^H} | + {2:^N^O^P^Q^R^S^T^U^V^W^X^Y^Z^[^\^]^^^_} | + ö 한글 {2:} | + t {2:} | + | + {3:Press ENTER or type command to continue}^ | + ]], nil, nil, function() + eq(true, screen.bell) + end) + end) + end) end) -- cgit From f75c4b39ece7b5f760892c1e18af449c5bb270c7 Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Thu, 8 Feb 2018 15:11:56 +0100 Subject: shell: handle split-up UTF-8 sequences --- test/functional/fixtures/shell-test.c | 23 +++++++++++++++++++++++ test/functional/ui/output_spec.lua | 25 +++++++++++++++++++++++++ 2 files changed, 48 insertions(+) (limited to 'test') diff --git a/test/functional/fixtures/shell-test.c b/test/functional/fixtures/shell-test.c index 8dbec2aaee..38695ce76b 100644 --- a/test/functional/fixtures/shell-test.c +++ b/test/functional/fixtures/shell-test.c @@ -4,6 +4,13 @@ #include #include #include +#include + +static void wait(void) +{ + fflush(stdout); + usleep(10*1000); +} static void help(void) { @@ -61,6 +68,22 @@ int main(int argc, char **argv) for (uint8_t i = 0; i < number; i++) { printf("%d: %s\n", (int) i, argv[3]); } + } else if (strcmp(argv[1], "UTF-8") == 0) { + // test split-up UTF-8 sequence + printf("\xc3"); wait(); + printf("\xa5\n"); wait(); + + // split up a 2+2 grapheme clusters all possible ways + printf("ref: \xc3\xa5\xcc\xb2\n"); wait(); + + printf("1: \xc3"); wait(); + printf("\xa5\xcc\xb2\n"); wait(); + + printf("2: \xc3\xa5"); wait(); + printf("\xcc\xb2\n"); wait(); + + printf("3: \xc3\xa5\xcc"); wait(); + printf("\xb2\n"); wait(); } else { fprintf(stderr, "Unknown first argument\n"); return 3; diff --git a/test/functional/ui/output_spec.lua b/test/functional/ui/output_spec.lua index 4e79c2e6cb..14f2091046 100644 --- a/test/functional/ui/output_spec.lua +++ b/test/functional/ui/output_spec.lua @@ -9,6 +9,7 @@ local feed_command = helpers.feed_command local iswin = helpers.iswin local clear = helpers.clear local command = helpers.command +local nvim_dir = helpers.nvim_dir describe("shell command :!", function() if helpers.pending_win32(pending) then return end @@ -195,5 +196,29 @@ describe("shell command :!", function() eq(true, screen.bell) end) end) + + it('handles multibyte sequences split over buffer boundaries', function() + command('cd '..nvim_dir) + local cmd + if iswin() then + cmd = '!shell-test UTF-8 ' + else + cmd = '!./shell-test UTF-8' + end + feed_command(cmd) + -- Note: only the first example of split composed char works + screen:expect([[ + {1:~ }| + {1:~ }| + :]]..cmd..[[ | + å | + ref: å̲ | + 1: å̲ | + 2: å ̲ | + 3: å ̲ | + | + {3:Press ENTER or type command to continue}^ | + ]]) + end) end) end) -- cgit From c03a847884c017a78e099beaa1b252e5f940c8e0 Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Tue, 3 Oct 2017 17:39:17 -0400 Subject: win: enable backtick_expansion and shell output tests --- test/functional/eval/backtick_expansion_spec.lua | 16 ++++++++++++---- test/functional/ui/output_spec.lua | 9 +++++++-- 2 files changed, 19 insertions(+), 6 deletions(-) (limited to 'test') diff --git a/test/functional/eval/backtick_expansion_spec.lua b/test/functional/eval/backtick_expansion_spec.lua index 81e8e295fa..b1b44cfa8b 100644 --- a/test/functional/eval/backtick_expansion_spec.lua +++ b/test/functional/eval/backtick_expansion_spec.lua @@ -21,11 +21,19 @@ describe("backtick expansion", function() end) it("with default 'shell'", function() - if helpers.pending_win32(pending) then return end -- Need win32 shell fixes - command(":silent args `echo ***2`") + if helpers.iswin() then + command(":silent args `dir /b *2`") + else + command(":silent args `echo ***2`") + end eq({ "file2", }, eval("argv()")) - command(":silent args `echo */*4`") - eq({ "subdir/file4", }, eval("argv()")) + if helpers.iswin() then + command(":silent args `dir /s/b *4`") + eq({ "subdir\\file4", }, eval("map(argv(), 'fnamemodify(v:val, \":.\")')")) + else + command(":silent args `echo */*4`") + eq({ "subdir/file4", }, eval("argv()")) + end end) it("with shell=fish", function() diff --git a/test/functional/ui/output_spec.lua b/test/functional/ui/output_spec.lua index 14f2091046..4246020fab 100644 --- a/test/functional/ui/output_spec.lua +++ b/test/functional/ui/output_spec.lua @@ -162,14 +162,19 @@ describe("shell command :!", function() end) it("doesn't truncate Last line of shell output #3269", function() - command([[nnoremap \l :!ls bang_filter_spec]]) + command(helpers.iswin() + and [[nnoremap \l :!dir /b bang_filter_spec]] + or [[nnoremap \l :!ls bang_filter_spec]]) + local result = (helpers.iswin() + and [[:!dir /b bang_filter_spec]] + or [[:!ls bang_filter_spec ]]) feed([[\l]]) screen:expect([[ {1:~ }| {1:~ }| {1:~ }| {1:~ }| - :!ls bang_filter_spec | + ]]..result..[[ | f1 | f2 | f3 | -- cgit From 5d8da126d0b5ab7f550a74264ba434a2ad04280e Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Tue, 6 Feb 2018 19:46:45 +0100 Subject: ui/tui: highlighting refactor Make HlAttr contain highlighting state for both color modes (cterm and rgb). This allows us to implement termguicolors completely in the TUI. Simplify some logic duplicated between ui.c and screen.c. Also avoid some superfluous highlighting reset events. --- test/functional/terminal/tui_spec.lua | 4 ++-- test/functional/ui/screen.lua | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) (limited to 'test') diff --git a/test/functional/terminal/tui_spec.lua b/test/functional/terminal/tui_spec.lua index a62c03b70f..171745eb57 100644 --- a/test/functional/terminal/tui_spec.lua +++ b/test/functional/terminal/tui_spec.lua @@ -242,7 +242,7 @@ describe('tui', function() {9:~ }| {9:~ }| {3:[No Name] [+] }| - | + :set termguicolors | {4:-- TERMINAL --} | ]]) @@ -253,7 +253,7 @@ describe('tui', function() {2:~ }| {2:~ }| {3:[No Name] [+] }| - | + :set notermguicolors | {4:-- TERMINAL --} | ]]) end) diff --git a/test/functional/ui/screen.lua b/test/functional/ui/screen.lua index 696feabeed..52e108f389 100644 --- a/test/functional/ui/screen.lua +++ b/test/functional/ui/screen.lua @@ -455,6 +455,9 @@ function Screen:_handle_visual_bell() self.visual_bell = true end +function Screen:_handle_default_colors_set() +end + function Screen:_handle_update_fg(fg) self._fg = fg end -- cgit From 7d12597d29e60239fd5c586417b48982893c174e Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Thu, 1 Feb 2018 23:50:12 +0100 Subject: vim-patch:8.0.0692: CTRL-G with 'incsearch' and ? goes in the wrong direction Problem: Using CTRL-G with 'incsearch' and ? goes in the wrong direction. (Ramel Eshed) Solution: Adjust search_start. (Christian Brabandt) https://github.com/vim/vim/commit/da5116da4586fc714434411d2cccb066caa3723e --- test/functional/legacy/search_spec.lua | 110 +++++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) (limited to 'test') diff --git a/test/functional/legacy/search_spec.lua b/test/functional/legacy/search_spec.lua index 5f71861821..277d8d6c7f 100644 --- a/test/functional/legacy/search_spec.lua +++ b/test/functional/legacy/search_spec.lua @@ -6,6 +6,7 @@ local eq = helpers.eq local eval = helpers.eval local feed = helpers.feed local funcs = helpers.funcs +local wait = helpers.wait describe('search cmdline', function() local screen @@ -471,4 +472,113 @@ describe('search cmdline', function() coladd = 0, skipcol = 0, curswant = 0}, funcs.winsaveview()) end) + + it("CTRL-G with 'incsearch' and ? goes in the right direction", function() + -- oldtest: Test_search_cmdline4(). + screen:detach() + screen = Screen.new(40, 4) + screen:attach() + screen:set_default_attr_ids({ + inc = {reverse = true}, + err = { foreground = Screen.colors.Grey100, background = Screen.colors.Red }, + more = { bold = true, foreground = Screen.colors.SeaGreen4 }, + tilde = { bold = true, foreground = Screen.colors.Blue1 }, + }) + command('enew!') + funcs.setline(1, {' 1 the first', ' 2 the second', ' 3 the third'}) + command('set laststatus=0 shortmess+=s') + command('set incsearch') + command('$') + -- Send the input in chunks, so the cmdline logic regards it as + -- "interactive". This mimics Vim's test_override("char_avail"). + -- (See legacy test: test_search.vim) + feed('?the') + wait() + feed('') + wait() + feed('') + screen:expect([[ + 1 the first | + 2 the second | + 3 ^the third | + ?the | + ]]) + + command('$') + feed('?the') + wait() + feed('') + wait() + feed('') + wait() + feed('') + screen:expect([[ + 1 ^the first | + 2 the second | + 3 the third | + ?the | + ]]) + + command('$') + feed('?the') + wait() + feed('') + wait() + feed('') + wait() + feed('') + wait() + feed('') + screen:expect([[ + 1 the first | + 2 ^the second | + 3 the third | + ?the | + ]]) + + command('$') + feed('?the') + wait() + feed('') + wait() + feed('') + screen:expect([[ + 1 ^the first | + 2 the second | + 3 the third | + ?the | + ]]) + + command('$') + feed('?the') + wait() + feed('') + wait() + feed('') + wait() + feed('') + screen:expect([[ + 1 the first | + 2 the second | + 3 ^the third | + ?the | + ]]) + + command('$') + feed('?the') + wait() + feed('') + wait() + feed('') + wait() + feed('') + wait() + feed('') + screen:expect([[ + 1 the first | + 2 ^the second | + 3 the third | + ?the | + ]]) + end) end) -- cgit From 4b7f7be3018563905d55197b43707ebe4ca12e78 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sat, 3 Feb 2018 12:47:12 +0100 Subject: test: port Test_edit_08() to Lua test Test_edit_08() depends on special-case handling in has_compl_option() and redrawing() which are in Vim but are not wanted in Nvim. Using a Lua test instead of depending on workarounds in the core to make the VimL test work. --- test/functional/legacy/edit_spec.lua | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 test/functional/legacy/edit_spec.lua (limited to 'test') diff --git a/test/functional/legacy/edit_spec.lua b/test/functional/legacy/edit_spec.lua new file mode 100644 index 0000000000..91d602924c --- /dev/null +++ b/test/functional/legacy/edit_spec.lua @@ -0,0 +1,25 @@ +-- Test for edit functions +-- See also: src/nvim/testdir/test_edit.vim + +local helpers = require('test.functional.helpers')(after_each) +local source = helpers.source +local eq, eval = helpers.eq, helpers.eval +local funcs = helpers.funcs +local clear = helpers.clear + +describe('edit', function() + before_each(clear) + + it('reset insertmode from i_ctrl-r_=', function() + source([=[ + call setline(1, ['abc']) + call cursor(1, 4) + call feedkeys(":set im\ZZZ\=setbufvar(1,'&im', 0)\",'tnix') + ]=]) + eq({'abZZZc'}, funcs.getline(1,'$')) + eq({0, 1, 1, 0}, funcs.getpos('.')) + eq(0, eval('&im')) + end) + +end) + -- cgit From 9bf9cc69c1c0b8402c575c164ea4ef792dd7bb90 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sun, 11 Feb 2018 16:06:34 +0100 Subject: test/arglist_spec: update to Vim 8.0.0721 behavior --- test/functional/legacy/arglist_spec.lua | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'test') diff --git a/test/functional/legacy/arglist_spec.lua b/test/functional/legacy/arglist_spec.lua index 191f145095..ec754a533b 100644 --- a/test/functional/legacy/arglist_spec.lua +++ b/test/functional/legacy/arglist_spec.lua @@ -222,20 +222,19 @@ describe('argument list commands', function() eq({'a', 'b'}, eval('argv()')) eq('b', eval('expand("%:t")')) command('argedit a') - eq({'a', 'b'}, eval('argv()')) + eq({'a', 'b', 'a'}, eval('argv()')) eq('a', eval('expand("%:t")')) command('argedit c') - eq({'a', 'c', 'b'}, eval('argv()')) + eq({'a', 'b', 'a', 'c'}, eval('argv()')) command('0argedit x') - eq({'x', 'a', 'c', 'b'}, eval('argv()')) + eq({'x', 'a', 'b', 'a', 'c'}, eval('argv()')) command('enew! | set modified') assert_fails('argedit y', 'E37:') command('argedit! y') - eq({'x', 'y', 'a', 'c', 'b'}, eval('argv()')) + eq({'x', 'y', 'y', 'a', 'b', 'a', 'c'}, eval('argv()')) command('%argd') - -- Nvim allows unescaped spaces in filename on all platforms. #6010 command('argedit a b') - eq({'a b'}, eval('argv()')) + eq({'a', 'b'}, eval('argv()')) end) it('test for :argdelete command', function() -- cgit From 0f1bc5ddceb50ca8f96d91aabf8157d9758af0cd Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Mon, 12 Feb 2018 04:19:03 +0100 Subject: test/python: less-noisy Python skip-message Developer can use :checkhealth to get more details, don't need to blast the details in the skip-message every time. --- test/functional/provider/python3_spec.lua | 7 ++----- test/functional/provider/python_spec.lua | 7 ++----- 2 files changed, 4 insertions(+), 10 deletions(-) (limited to 'test') diff --git a/test/functional/provider/python3_spec.lua b/test/functional/provider/python3_spec.lua index aa50f53451..f06728ec0e 100644 --- a/test/functional/provider/python3_spec.lua +++ b/test/functional/provider/python3_spec.lua @@ -7,11 +7,8 @@ local missing_provider = helpers.missing_provider do clear() - local err = missing_provider('python3') - if err then - pending( - 'Python 3 (or the Python 3 neovim module) is broken or missing:\n' .. err, - function() end) + if missing_provider('python3') then + pending('Python 3 (or the neovim module) is broken/missing', function() end) return end end diff --git a/test/functional/provider/python_spec.lua b/test/functional/provider/python_spec.lua index 25f5e0a6d0..2fa74e9644 100644 --- a/test/functional/provider/python_spec.lua +++ b/test/functional/provider/python_spec.lua @@ -16,11 +16,8 @@ local missing_provider = helpers.missing_provider do clear() - local err = missing_provider('python') - if err then - pending( - 'Python 2 (or the Python 2 neovim module) is broken or missing:\n' .. err, - function() end) + if missing_provider('python') then + pending('Python 2 (or the neovim module) is broken/missing', function() end) return end end -- cgit From 6e5cb0debd23693175bd05409d3f1af4015567df Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Tue, 13 Feb 2018 13:45:49 +0100 Subject: ui: refactor ui options --- test/functional/api/version_spec.lua | 16 ++++++++++-- test/functional/eval/api_functions_spec.lua | 3 ++- test/functional/eval/msgpack_functions_spec.lua | 3 ++- test/functional/ui/options_spec.lua | 33 ++++++++++++++++++++++++- 4 files changed, 50 insertions(+), 5 deletions(-) (limited to 'test') diff --git a/test/functional/api/version_spec.lua b/test/functional/api/version_spec.lua index d23f058f69..7bf54c0d1e 100644 --- a/test/functional/api/version_spec.lua +++ b/test/functional/api/version_spec.lua @@ -1,6 +1,7 @@ local helpers = require('test.functional.helpers')(after_each) local mpack = require('mpack') local clear, funcs, eq = helpers.clear, helpers.funcs, helpers.eq +local call = helpers.call local function read_mpack_file(fname) local fd = io.open(fname, 'rb') @@ -18,7 +19,7 @@ describe("api_info()['version']", function() before_each(clear) it("returns API level", function() - local version = helpers.call('api_info')['version'] + local version = call('api_info')['version'] local current = version['api_level'] local compat = version['api_compatible'] eq("number", type(current)) @@ -27,7 +28,7 @@ describe("api_info()['version']", function() end) it("returns Nvim version", function() - local version = helpers.call('api_info')['version'] + local version = call('api_info')['version'] local major = version['major'] local minor = version['minor'] local patch = version['patch'] @@ -147,3 +148,14 @@ describe("api functions", function() end) end) + +describe("ui_options in metadata", function() + it('are correct', function() + -- TODO(bfredl) once a release freezes this into metadata, + -- instead check that all old options are present + local api = helpers.call('api_info') + local options = api.ui_options + eq({'rgb', 'ext_cmdline', 'ext_popupmenu', + 'ext_tabline', 'ext_wildmenu'}, options) + end) +end) diff --git a/test/functional/eval/api_functions_spec.lua b/test/functional/eval/api_functions_spec.lua index fea4a87a26..6f440c7d82 100644 --- a/test/functional/eval/api_functions_spec.lua +++ b/test/functional/eval/api_functions_spec.lua @@ -106,7 +106,8 @@ describe('api functions', function() it('have metadata accessible with api_info()', function() local api_keys = eval("sort(keys(api_info()))") - eq({'error_types', 'functions', 'types', 'ui_events', 'version'}, api_keys) + eq({'error_types', 'functions', 'types', + 'ui_events', 'ui_options', 'version'}, api_keys) end) it('are highlighted by vim.vim syntax file', function() diff --git a/test/functional/eval/msgpack_functions_spec.lua b/test/functional/eval/msgpack_functions_spec.lua index 258d6ee059..a8a413f68b 100644 --- a/test/functional/eval/msgpack_functions_spec.lua +++ b/test/functional/eval/msgpack_functions_spec.lua @@ -463,7 +463,8 @@ describe('msgpackparse() function', function() eval(cmd) eval(cmd) -- do it again (try to force segfault) local api_info = eval(cmd) -- do it again - eq({'error_types', 'functions', 'types', 'ui_events', 'version'}, api_info) + eq({'error_types', 'functions', 'types', + 'ui_events', 'ui_options', 'version'}, api_info) end) it('fails when called with no arguments', function() diff --git a/test/functional/ui/options_spec.lua b/test/functional/ui/options_spec.lua index bc72ca71aa..62b08c0967 100644 --- a/test/functional/ui/options_spec.lua +++ b/test/functional/ui/options_spec.lua @@ -10,7 +10,6 @@ describe('ui receives option updates', function() before_each(function() clear() screen = Screen.new(20,5) - screen:attach() end) after_each(function() @@ -27,15 +26,21 @@ describe('ui receives option updates', function() linespace=0, showtabline=1, termguicolors=false, + ext_cmdline=false, + ext_popupmenu=false, + ext_tabline=false, + ext_wildmenu=false, } it("for defaults", function() + screen:attach() screen:expect(function() eq(defaults, screen.options) end) end) it("when setting options", function() + screen:attach() local changed = {} for k,v in pairs(defaults) do changed[k] = v @@ -76,4 +81,30 @@ describe('ui receives option updates', function() eq(defaults, screen.options) end) end) + + it('with UI extensions', function() + local changed = {} + for k,v in pairs(defaults) do + changed[k] = v + end + + screen:attach({ext_cmdline=true, ext_wildmenu=true}) + changed.ext_cmdline = true + changed.ext_wildmenu = true + screen:expect(function() + eq(changed, screen.options) + end) + + screen:set_option('ext_popupmenu', true) + changed.ext_popupmenu = true + screen:expect(function() + eq(changed, screen.options) + end) + + screen:set_option('ext_wildmenu', false) + changed.ext_wildmenu = false + screen:expect(function() + eq(changed, screen.options) + end) + end) end) -- cgit From d9497053e84aaf3dabecd362da19913811e8c22e Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Tue, 13 Feb 2018 19:47:27 +0100 Subject: ex_getln: clear cmdline_block after it's freed --- test/functional/ui/cmdline_spec.lua | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) (limited to 'test') diff --git a/test/functional/ui/cmdline_spec.lua b/test/functional/ui/cmdline_spec.lua index d15afc81bb..f8680678ef 100644 --- a/test/functional/ui/cmdline_spec.lua +++ b/test/functional/ui/cmdline_spec.lua @@ -348,6 +348,35 @@ describe('external cmdline', function() { { {}, ' line1'} } }, block) end) + feed('endfunction') + screen:expect([[ + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + | + ]], nil, nil, function() + eq(nil, block) + end) + + -- Try once more, to check buffer is reinitialized. #8007 + feed(':function Bar()') + screen:expect([[ + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + | + ]], nil, nil, function() + eq({{ + content = { { {}, "" } }, + firstc = ":", + indent = 2, + pos = 0, + prompt = "", + }}, cmdline) + eq({ { { {}, 'function Bar()'} } }, block) + end) feed('endfunction') screen:expect([[ -- cgit From e9134421ab8f72393d469d9d7793d4a75984cb93 Mon Sep 17 00:00:00 2001 From: Nimit Bhardwaj Date: Wed, 14 Feb 2018 19:48:01 +0530 Subject: vim-patch-8.0.0649 and vim-patch-8.0.0650: autocmd open help 2 times --- test/functional/autocmd/filetype_spec.lua | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 test/functional/autocmd/filetype_spec.lua (limited to 'test') diff --git a/test/functional/autocmd/filetype_spec.lua b/test/functional/autocmd/filetype_spec.lua new file mode 100644 index 0000000000..e6fa7ab6bb --- /dev/null +++ b/test/functional/autocmd/filetype_spec.lua @@ -0,0 +1,16 @@ +local helpers = require('test.functional.helpers')(after_each) + +local eval = helpers.eval +local clear = helpers.clear +local command = helpers.command + +describe('autocmd FileType', function() + before_each(clear) + + it("is triggered by :help only once", function() + command("let g:foo = 0") + command("autocmd FileType help let g:foo = g:foo + 1") + command("help help") + assert.same(1, eval('g:foo')) + end) +end) -- cgit From e72ecdb7ca1face1d16e622824aa7bd3c98f5673 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Fri, 16 Feb 2018 19:42:05 +0100 Subject: test/util: expect_msg_seq() job_spec.lua on AppVeyor (Windows) often fails like this: FAILED ] C:/projects/neovim/test/functional\core\job_spec.lua @ 72: jobs changes to given `cwd` directory C:/projects/neovim/test/functional\core\job_spec.lua:81: Expected objects to be the same. Passed in: (table) { [1] = 'notification' [2] = 'stdout' *[3] = { [1] = 0 *[2] = { [1] = 'C:\projects\neovim\Xtest-tmpdir\nvimmSjq1S\0' } } } Expected: (table) { [1] = 'notification' [2] = 'stdout' *[3] = { [1] = 0 *[2] = { [1] = 'C:\projects\neovim\Xtest-tmpdir\nvimmSjq1S\0' *[2] = '' } } } stack traceback: Message chunking is non-deterministic, so we need to try different variants. --- test/functional/core/job_spec.lua | 27 +++++-- test/functional/helpers.lua | 163 ++++++++++++++++++++++++-------------- test/helpers.lua | 84 +++++++++++++++----- 3 files changed, 188 insertions(+), 86 deletions(-) (limited to 'test') diff --git a/test/functional/core/job_spec.lua b/test/functional/core/job_spec.lua index a02d36c939..73d437169a 100644 --- a/test/functional/core/job_spec.lua +++ b/test/functional/core/job_spec.lua @@ -12,6 +12,7 @@ local get_pathsep = helpers.get_pathsep local pathroot = helpers.pathroot local nvim_set = helpers.nvim_set local expect_twostreams = helpers.expect_twostreams +local expect_msg_seq = helpers.expect_msg_seq local Screen = require('test.functional.ui.screen') describe('jobs', function() @@ -78,9 +79,18 @@ describe('jobs', function() else nvim('command', "let j = jobstart('pwd', g:job_opts)") end - eq({'notification', 'stdout', {0, {dir, ''}}}, next_msg()) - eq({'notification', 'stdout', {0, {''}}}, next_msg()) - eq({'notification', 'exit', {0, 0}}, next_msg()) + expect_msg_seq( + { {'notification', 'stdout', {0, {dir, ''} } }, + {'notification', 'stdout', {0, {''} } }, + {'notification', 'exit', {0, 0} } + }, + -- Alternative sequence: + { {'notification', 'stdout', {0, {dir} } }, + {'notification', 'stdout', {0, {'', ''} } }, + {'notification', 'stdout', {0, {''} } }, + {'notification', 'exit', {0, 0} } + } + ) rmdir(dir) end) @@ -308,8 +318,15 @@ describe('jobs', function() nvim('command', 'unlet g:job_opts.on_exit') nvim('command', 'let g:job_opts.user = 5') nvim('command', [[call jobstart('echo "foo"', g:job_opts)]]) - eq({'notification', 'stdout', {5, {'foo', ''}}}, next_msg()) - eq({'notification', 'stdout', {5, {''}}}, next_msg()) + expect_msg_seq( + { {'notification', 'stdout', {5, {'foo', ''} } }, + {'notification', 'stdout', {5, {''} } } + }, + -- Alternative sequence: + { {'notification', 'stdout', {5, {'foo'} } }, + {'notification', 'stdout', {5, {'', ''} } } + } + ) end) it('will pass return code with the exit event', function() diff --git a/test/functional/helpers.lua b/test/functional/helpers.lua index f0e47481da..3f3cdb56ba 100644 --- a/test/functional/helpers.lua +++ b/test/functional/helpers.lua @@ -14,10 +14,12 @@ local check_cores = global_helpers.check_cores local check_logs = global_helpers.check_logs local neq = global_helpers.neq local eq = global_helpers.eq +local eq_any = global_helpers.eq_any local ok = global_helpers.ok local map = global_helpers.map local filter = global_helpers.filter local dedent = global_helpers.dedent +local table_flatten = global_helpers.table_flatten local start_dir = lfs.currentdir() -- XXX: NVIM_PROG takes precedence, QuickBuild sets it. @@ -96,8 +98,8 @@ local function request(method, ...) return rv end -local function next_message() - return session:next_message() +local function next_message(timeout) + return session:next_message(timeout) end local function expect_twostreams(msgs1, msgs2) @@ -116,6 +118,46 @@ local function expect_twostreams(msgs1, msgs2) end end +-- Expects a sequence of next_message() results. If multiple sequences are +-- passed they are tried until one succeeds, in order of shortest to longest. +local function expect_msg_seq(...) + if select('#', ...) < 1 then + error('need at least 1 argument') + end + local seqs = {...} + table.sort(seqs, function(a, b) -- Sort ascending, by (shallow) length. + return #a < #b + end) + + local actual_seq = {} + local final_error = '' + local function cat_err(err1, err2) + if err1 == nil then + return err2 + end + return string.format('%s\n%s\n%s', err1, string.rep('=', 78), err2) + end + for anum = 1, #seqs do + local expected_seq = seqs[anum] + -- Collect enough messages to compare the next expected sequence. + while #actual_seq < #expected_seq do + local msg = next_message(10000) -- Big timeout for ASAN/valgrind. + if msg == nil then + error(cat_err(final_error, + string.format('got %d messages, expected %d', + #actual_seq, #expected_seq))) + end + table.insert(actual_seq, msg) + end + local status, result = pcall(eq, expected_seq, actual_seq) + if status then + return result + end + final_error = cat_err(final_error, result) + end + error(final_error) +end + local function call_and_stop_on_error(...) local status, result = copcall(...) -- luacheck: ignore if not status then @@ -674,78 +716,81 @@ local function hexdump(str) end local module = { - prepend_argv = prepend_argv, + NIL = mpack.NIL, + alter_slashes = alter_slashes, + buffer = buffer, + bufmeths = bufmeths, + call = nvim_call, clear = clear, + command = nvim_command, connect = connect, - retry = retry, - spawn = spawn, + curbuf = curbuf, + curbuf_contents = curbuf_contents, + curbufmeths = curbufmeths, + curtab = curtab, + curtabmeths = curtabmeths, + curwin = curwin, + curwinmeths = curwinmeths, dedent = dedent, - source = source, - rawfeed = rawfeed, - insert = insert, - iswin = iswin, - feed = feed, - feed_command = feed_command, - eval = nvim_eval, - call = nvim_call, - command = nvim_command, - request = request, - next_message = next_message, - expect_twostreams = expect_twostreams, - run = run, - stop = stop, eq = eq, - neq = neq, + eq_any = eq_any, + eval = nvim_eval, + exc_exec = exc_exec, expect = expect, expect_any = expect_any, - ok = ok, - map = map, + expect_msg_seq = expect_msg_seq, + expect_twostreams = expect_twostreams, + feed = feed, + feed_command = feed_command, filter = filter, + funcs = funcs, + get_pathsep = get_pathsep, + hexdump = hexdump, + insert = insert, + iswin = iswin, + map = map, + merge_args = merge_args, + meth_pcall = meth_pcall, + meths = meths, + missing_provider = missing_provider, + mkdir = lfs.mkdir, + neq = neq, + new_pipename = new_pipename, + next_message = next_message, nvim = nvim, + nvim_argv = nvim_argv, nvim_async = nvim_async, + nvim_dir = nvim_dir, nvim_prog = nvim_prog, - nvim_argv = nvim_argv, nvim_set = nvim_set, - nvim_dir = nvim_dir, - buffer = buffer, - window = window, - tabpage = tabpage, - curbuf = curbuf, - curwin = curwin, - curtab = curtab, - curbuf_contents = curbuf_contents, - wait = wait, - sleep = sleep, - set_session = set_session, - write_file = write_file, - read_file = read_file, + ok = ok, os_name = os_name, - rmdir = rmdir, - mkdir = lfs.mkdir, - exc_exec = exc_exec, - redir_exec = redir_exec, - merge_args = merge_args, - funcs = funcs, - meths = meths, - bufmeths = bufmeths, - winmeths = winmeths, - tabmeths = tabmeths, - uimeths = uimeths, - curbufmeths = curbufmeths, - curwinmeths = curwinmeths, - curtabmeths = curtabmeths, + pathroot = pathroot, pending_win32 = pending_win32, - skip_fragile = skip_fragile, + prepend_argv = prepend_argv, + rawfeed = rawfeed, + read_file = read_file, + redir_exec = redir_exec, + request = request, + retry = retry, + rmdir = rmdir, + run = run, + set_session = set_session, set_shell_powershell = set_shell_powershell, + skip_fragile = skip_fragile, + sleep = sleep, + source = source, + spawn = spawn, + stop = stop, + table_flatten = table_flatten, + tabmeths = tabmeths, + tabpage = tabpage, tmpname = tmpname, - meth_pcall = meth_pcall, - NIL = mpack.NIL, - get_pathsep = get_pathsep, - pathroot = pathroot, - missing_provider = missing_provider, - alter_slashes = alter_slashes, - hexdump = hexdump, - new_pipename = new_pipename, + uimeths = uimeths, + wait = wait, + window = window, + winmeths = winmeths, + write_file = write_file, } return function(after_each) diff --git a/test/helpers.lua b/test/helpers.lua index 1c64f41b65..ee7dc5dfe5 100644 --- a/test/helpers.lua +++ b/test/helpers.lua @@ -7,13 +7,33 @@ local check_logs_useless_lines = { ['See README_MISSING_SYSCALL_OR_IOCTL for guidance']=3, } -local eq = function(exp, act) - return assert.are.same(exp, act) +local function eq(expected, actual) + return assert.are.same(expected, actual) end -local neq = function(exp, act) +-- Tries multiple accepted ("expected") values until one succeeds. +-- First N-1 arguments are the accepted values. +-- Last (Nth) argument is the "actual" value to be compared. +local function eq_any(...) + if select('#', ...) < 2 then + error('need at least 2 arguments') + end + local args = {...} + local actual = args[#args] + local final_error = '' + for anum = 1, select('#', ...) - 1 do + local accepted = args[anum] + local status, result = pcall(eq, accepted, actual) + if status then + return result + end + final_error = final_error..tostring(result) + end + error(final_error) +end +local function neq(exp, act) return assert.are_not.same(exp, act) end -local ok = function(res) +local function ok(res) return assert.is_true(res) end @@ -534,30 +554,50 @@ local function fixtbl_rec(tbl) return fixtbl(tbl) end +-- From https://github.com/premake/premake-core/blob/master/src/base/table.lua +local function table_flatten(arr) + local result = {} + local function _table_flatten(_arr) + local n = #_arr + for i = 1, n do + local v = _arr[i] + if type(v) == "table" then + _table_flatten(v) + elseif v then + table.insert(result, v) + end + end + end + _table_flatten(arr) + return result +end + return { - eq = eq, - neq = neq, - ok = ok, - check_logs = check_logs, - uname = uname, - tmpname = tmpname, - map = map, - filter = filter, - glob = glob, - check_cores = check_cores, - hasenv = hasenv, - which = which, - shallowcopy = shallowcopy, - deepcopy = deepcopy, - mergedicts_copy = mergedicts_copy, - dictdiff = dictdiff, REMOVE_THIS = REMOVE_THIS, + check_cores = check_cores, + check_logs = check_logs, concat_tables = concat_tables, dedent = dedent, + deepcopy = deepcopy, + dictdiff = dictdiff, + eq = eq, + eq_any = eq_any, + filter = filter, + fixtbl = fixtbl, + fixtbl_rec = fixtbl_rec, format_luav = format_luav, format_string = format_string, + glob = glob, + hasenv = hasenv, intchar2lua = intchar2lua, + map = map, + mergedicts_copy = mergedicts_copy, + neq = neq, + ok = ok, + shallowcopy = shallowcopy, + table_flatten = table_flatten, + tmpname = tmpname, + uname = uname, updated = updated, - fixtbl = fixtbl, - fixtbl_rec = fixtbl_rec, + which = which, } -- cgit From 7973847d025aa7af431f1e92c1ee977ceb0eb7e5 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sun, 18 Feb 2018 19:22:44 +0100 Subject: test/util: remove eq_any() It was added in the parent commit, but ended up not being used. And I can't think of a case where it will be used: instead we would probably want to generalize expect_msg_seq() if necessary. --- test/functional/helpers.lua | 2 -- test/helpers.lua | 25 ++----------------------- 2 files changed, 2 insertions(+), 25 deletions(-) (limited to 'test') diff --git a/test/functional/helpers.lua b/test/functional/helpers.lua index 3f3cdb56ba..a1da55ab1c 100644 --- a/test/functional/helpers.lua +++ b/test/functional/helpers.lua @@ -14,7 +14,6 @@ local check_cores = global_helpers.check_cores local check_logs = global_helpers.check_logs local neq = global_helpers.neq local eq = global_helpers.eq -local eq_any = global_helpers.eq_any local ok = global_helpers.ok local map = global_helpers.map local filter = global_helpers.filter @@ -733,7 +732,6 @@ local module = { curwinmeths = curwinmeths, dedent = dedent, eq = eq, - eq_any = eq_any, eval = nvim_eval, exc_exec = exc_exec, expect = expect, diff --git a/test/helpers.lua b/test/helpers.lua index ee7dc5dfe5..91ceed4df1 100644 --- a/test/helpers.lua +++ b/test/helpers.lua @@ -10,28 +10,8 @@ local check_logs_useless_lines = { local function eq(expected, actual) return assert.are.same(expected, actual) end --- Tries multiple accepted ("expected") values until one succeeds. --- First N-1 arguments are the accepted values. --- Last (Nth) argument is the "actual" value to be compared. -local function eq_any(...) - if select('#', ...) < 2 then - error('need at least 2 arguments') - end - local args = {...} - local actual = args[#args] - local final_error = '' - for anum = 1, select('#', ...) - 1 do - local accepted = args[anum] - local status, result = pcall(eq, accepted, actual) - if status then - return result - end - final_error = final_error..tostring(result) - end - error(final_error) -end -local function neq(exp, act) - return assert.are_not.same(exp, act) +local function neq(expected, actual) + return assert.are_not.same(expected, actual) end local function ok(res) return assert.is_true(res) @@ -581,7 +561,6 @@ return { deepcopy = deepcopy, dictdiff = dictdiff, eq = eq, - eq_any = eq_any, filter = filter, fixtbl = fixtbl, fixtbl_rec = fixtbl_rec, -- cgit From 00665d3c701ef1b6b276f750a772a6aa8a42c8c1 Mon Sep 17 00:00:00 2001 From: Shougo Date: Mon, 19 Feb 2018 07:56:59 +0900 Subject: vim-patch:8.0.1493: completion items cannot be annotated (#8003) Problem: Completion items cannot be annotated. Solution: Add a "user_data" entry to the completion item. (Ben Jackson, coses vim/vim#2608, closes vim/vim#2508) https://github.com/vim/vim/commit/9b56a57cdae31f7a2c85d440392bf63d3253a158 --- test/functional/viml/completion_spec.lua | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'test') diff --git a/test/functional/viml/completion_spec.lua b/test/functional/viml/completion_spec.lua index fbc7a527f8..216ccb3744 100644 --- a/test/functional/viml/completion_spec.lua +++ b/test/functional/viml/completion_spec.lua @@ -59,7 +59,8 @@ describe('completion', function() it('returns expected dict in normal completion', function() feed('ifooo') eq('foo', eval('getline(2)')) - eq({word = 'foo', abbr = '', menu = '', info = '', kind = ''}, + eq({word = 'foo', abbr = '', menu = '', + info = '', kind = '', user_data = ''}, eval('v:completed_item')) end) it('is readonly', function() @@ -84,13 +85,18 @@ describe('completion', function() feed_command('let v:completed_item.kind = "bar"') neq(nil, string.find(eval('v:errmsg'), '^E46: ')) feed_command('let v:errmsg = ""') + + feed_command('let v:completed_item.user_data = "bar"') + neq(nil, string.find(eval('v:errmsg'), '^E46: ')) + feed_command('let v:errmsg = ""') end) it('returns expected dict in omni completion', function() source([[ function! TestOmni(findstart, base) abort return a:findstart ? 0 : [{'word': 'foo', 'abbr': 'bar', \ 'menu': 'baz', 'info': 'foobar', 'kind': 'foobaz'}, - \ {'word': 'word', 'abbr': 'abbr', 'menu': 'menu', 'info': 'info', 'kind': 'kind'}] + \ {'word': 'word', 'abbr': 'abbr', 'menu': 'menu', + \ 'info': 'info', 'kind': 'kind'}] endfunction setlocal omnifunc=TestOmni ]]) @@ -107,7 +113,7 @@ describe('completion', function() {3:-- Omni completion (^O^N^P) }{4:match 1 of 2} | ]]) eq({word = 'foo', abbr = 'bar', menu = 'baz', - info = 'foobar', kind = 'foobaz'}, + info = 'foobar', kind = 'foobaz', user_data = ''}, eval('v:completed_item')) end) end) -- cgit From 3e19e18f4cfa0f9c578b6e306a0d887551893b3c Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Fri, 6 Oct 2017 22:37:48 -0400 Subject: win: enable job tests that use jobstart only - echo "" does not hang in powershell - cmd.exe's echo command does not hang. - job tests default to powershell (WHY?) - wait 5 seconds for powershell to create an empty file - powershell is slow - cannot reliably validate the id returned by jobstart via jobpid, jobstop - if using cmd.exe, waiting for a second should be enough - remaining job tests are unreliable in Windows because any build can pass/fail for same conditions without changes, especially if the error is in stderr --- test/functional/core/job_spec.lua | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) (limited to 'test') diff --git a/test/functional/core/job_spec.lua b/test/functional/core/job_spec.lua index 73d437169a..5f39040a6f 100644 --- a/test/functional/core/job_spec.lua +++ b/test/functional/core/job_spec.lua @@ -60,7 +60,7 @@ describe('jobs', function() it('changes to given / directory', function() nvim('command', "let g:job_opts.cwd = '/'") if iswin() then - nvim('command', "let j = jobstart('pwd|%{$_.Path}', g:job_opts)") + nvim('command', "let j = jobstart('(Get-Location).Path', g:job_opts)") else nvim('command', "let j = jobstart('pwd', g:job_opts)") end @@ -75,7 +75,7 @@ describe('jobs', function() mkdir(dir) nvim('command', "let g:job_opts.cwd = '" .. dir .. "'") if iswin() then - nvim('command', "let j = jobstart('pwd|%{$_.Path}', g:job_opts)") + nvim('command', "let j = jobstart('(Get-Location).Path', g:job_opts)") else nvim('command', "let j = jobstart('pwd', g:job_opts)") end @@ -115,13 +115,13 @@ describe('jobs', function() end) it('returns -1 when target is not executable #5465', function() - if helpers.pending_win32(pending) then return end local function new_job() return eval([[jobstart('')]]) end local executable_jobid = new_job() - local nonexecutable_jobid = eval( - "jobstart(['./test/functional/fixtures/non_executable.txt'])") + local nonexecutable_jobid = eval("jobstart(['"..(iswin() + and './test/functional/fixtures' + or './test/functional/fixtures/non_executable.txt').."'])") eq(-1, nonexecutable_jobid) -- Should _not_ throw an error. eq("", eval("v:errmsg")) @@ -133,11 +133,10 @@ describe('jobs', function() -- TODO: hangs on Windows if helpers.pending_win32(pending) then return end nvim('command', "let g:job_opts.on_stderr = function('OnEvent')") - nvim('command', "call jobstart('echo', g:job_opts)") + nvim('command', [[call jobstart('echo ""', g:job_opts)]]) expect_twostreams({{'notification', 'stdout', {0, {'', ''}}}, {'notification', 'stdout', {0, {''}}}}, {{'notification', 'stderr', {0, {''}}}}) - eq({'notification', 'exit', {0, 0}}, next_msg()) end) @@ -253,7 +252,6 @@ describe('jobs', function() end) it('will not leak memory if we leave a job running', function() - if helpers.pending_win32(pending) then return end -- TODO: Need `cat`. nvim('command', "call jobstart(['cat', '-'], g:job_opts)") end) @@ -300,11 +298,7 @@ describe('jobs', function() end) it('can omit options', function() - if helpers.pending_win32(pending) then return end - neq(0, nvim('eval', 'delete(".Xtestjob")')) - nvim('command', "call jobstart(['touch', '.Xtestjob'])") - nvim('command', "sleep 100m") - eq(0, nvim('eval', 'delete(".Xtestjob")')) + ok(eval([[jobstart('echo ""')]]) > 0) end) it('can omit data callbacks', function() @@ -348,7 +342,6 @@ describe('jobs', function() end) it('can redefine callbacks being used by a job', function() - if helpers.pending_win32(pending) then return end -- TODO: Need `cat`. local screen = Screen.new() screen:attach() screen:set_default_attr_ids({ @@ -363,7 +356,7 @@ describe('jobs', function() \ 'on_stderr': function('g:JobHandler'), \ 'on_exit': function('g:JobHandler') \ } - let job = jobstart('cat -', g:callbacks) + let job = jobstart(['cat', '-'], g:callbacks) ]]) wait() source([[ -- cgit From 0fd899aa077b9a70932bfc3b191e780716b4b4d5 Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Wed, 11 Oct 2017 21:25:54 -0400 Subject: win: enable legacy test 025 --- test/functional/legacy/025_jump_tag_hidden_spec.lua | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) (limited to 'test') diff --git a/test/functional/legacy/025_jump_tag_hidden_spec.lua b/test/functional/legacy/025_jump_tag_hidden_spec.lua index 0d51b4da26..dd89a3680e 100644 --- a/test/functional/legacy/025_jump_tag_hidden_spec.lua +++ b/test/functional/legacy/025_jump_tag_hidden_spec.lua @@ -5,8 +5,6 @@ local helpers = require('test.functional.helpers')(after_each) local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert local feed_command, expect = helpers.feed_command, helpers.expect -if helpers.pending_win32(pending) then return end - describe('jump to a tag with hidden set', function() setup(clear) @@ -25,12 +23,17 @@ describe('jump to a tag with hidden set', function() feed_command('set hidden') -- Create a link from test25.dir to the current directory. - feed_command('!rm -f test25.dir') - feed_command('!ln -s . test25.dir') + if helpers.iswin() then + feed_command('!rd /q/s test25.dir') + feed_command('!mklink /j test25.dir .') + else + feed_command('!rm -f test25.dir') + feed_command('!ln -s . test25.dir') + end -- Create tags.text, with the current directory name inserted. feed_command('/tags line') - feed_command('r !pwd') + feed_command('r !' .. (helpers.iswin() and 'cd' or 'pwd')) feed('d$/test') feed('hP:.w! tags.test') @@ -39,7 +42,13 @@ describe('jump to a tag with hidden set', function() -- space will then be eaten by hit-return, instead of moving the cursor to 'd'. feed_command('set tags=tags.test') feed('G x:yank a') - feed_command('!rm -f Xxx test25.dir tags.test') + feed_command("call delete('tags.test')") + feed_command("call delete('Xxx')") + if helpers.iswin() then + feed_command('!rd /q test25.dir') + else + feed_command('!rm -f test25.dir') + end -- Put @a and remove empty line feed_command('%d') -- cgit From bde32edefe0654907a4bea353cdad0aa85919e91 Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Wed, 11 Oct 2017 22:56:11 -0400 Subject: win: enable legacy test 097 --- test/functional/legacy/097_glob_path_spec.lua | 70 +++++++++++++++++++-------- 1 file changed, 50 insertions(+), 20 deletions(-) (limited to 'test') diff --git a/test/functional/legacy/097_glob_path_spec.lua b/test/functional/legacy/097_glob_path_spec.lua index 6b63a317f1..907f0665ae 100644 --- a/test/functional/legacy/097_glob_path_spec.lua +++ b/test/functional/legacy/097_glob_path_spec.lua @@ -6,15 +6,19 @@ local helpers = require('test.functional.helpers')(after_each) local clear = helpers.clear local command, expect = helpers.command, helpers.expect -if helpers.pending_win32(pending) then return end - describe('glob() and globpath()', function() setup(clear) setup(function() - os.execute("mkdir -p sautest/autoload") - os.execute("touch sautest/autoload/Test104.vim") - os.execute("touch sautest/autoload/footest.vim") + if helpers.iswin() then + os.execute("md sautest\\autoload") + os.execute(".>sautest\\autoload\\Test104.vim 2>nul") + os.execute(".>sautest\\autoload\\footest.vim 2>nul") + else + os.execute("mkdir -p sautest/autoload") + os.execute("touch sautest/autoload/Test104.vim") + os.execute("touch sautest/autoload/footest.vim") + end end) it('is working', function() @@ -24,29 +28,55 @@ describe('glob() and globpath()', function() -- Consistent sorting of file names command('set nofileignorecase') - command([[$put =glob('Xxx\{')]]) - command([[$put =glob('Xxx\$')]]) + if helpers.iswin() then + command([[$put =glob('Xxx{')]]) + command([[$put =glob('Xxx$')]]) + + command('silent w! Xxx{') + command([[w! Xxx$]]) + command([[$put =glob('Xxx{')]]) + command([[$put =glob('Xxx$')]]) + + command([[$put =string(globpath('sautest\autoload', '*.vim'))]]) + command([[$put =string(globpath('sautest\autoload', '*.vim', 0, 1))]]) + expect([=[ + + - command('silent w! Xxx{') - command([[w! Xxx\$]]) - command([[$put =glob('Xxx\{')]]) - command([[$put =glob('Xxx\$')]]) + Xxx{ + Xxx$ + 'sautest\autoload\Test104.vim + sautest\autoload\footest.vim' + ['sautest\autoload\Test104.vim', 'sautest\autoload\footest.vim']]=]) + else + command([[$put =glob('Xxx\{')]]) + command([[$put =glob('Xxx\$')]]) - command("$put =string(globpath('sautest/autoload', '*.vim'))") - command("$put =string(globpath('sautest/autoload', '*.vim', 0, 1))") + command('silent w! Xxx{') + command([[w! Xxx\$]]) + command([[$put =glob('Xxx\{')]]) + command([[$put =glob('Xxx\$')]]) - expect([=[ + command("$put =string(globpath('sautest/autoload', '*.vim'))") + command("$put =string(globpath('sautest/autoload', '*.vim', 0, 1))") + expect([=[ - Xxx{ - Xxx$ - 'sautest/autoload/Test104.vim - sautest/autoload/footest.vim' - ['sautest/autoload/Test104.vim', 'sautest/autoload/footest.vim']]=]) + Xxx{ + Xxx$ + 'sautest/autoload/Test104.vim + sautest/autoload/footest.vim' + ['sautest/autoload/Test104.vim', 'sautest/autoload/footest.vim']]=]) + end end) teardown(function() - os.execute("rm -rf sautest Xxx{ Xxx$") + if helpers.iswin() then + os.execute('del /q/f Xxx{ Xxx$') + os.execute('rd /q sautest') + else + os.execute("rm -rf sautest Xxx{ Xxx$") + end end) end) -- cgit From 4f65cd7c0a6386c3554eaf133ca9dd885ec016fe Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Thu, 12 Oct 2017 22:55:14 -0400 Subject: win: enable legacy/delete_spec.lua --- test/functional/legacy/delete_spec.lua | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) (limited to 'test') diff --git a/test/functional/legacy/delete_spec.lua b/test/functional/legacy/delete_spec.lua index aeaab335e8..5ef456bfe3 100644 --- a/test/functional/legacy/delete_spec.lua +++ b/test/functional/legacy/delete_spec.lua @@ -2,8 +2,6 @@ local helpers = require('test.functional.helpers')(after_each) local clear, source = helpers.clear, helpers.source local eq, eval, command = helpers.eq, helpers.eval, helpers.command -if helpers.pending_win32(pending) then return end - describe('Test for delete()', function() before_each(clear) @@ -48,7 +46,11 @@ describe('Test for delete()', function() split Xfile call setline(1, ['a', 'b']) wq - silent !ln -s Xfile Xlink + if has('win32') + silent !mklink Xlink Xfile + else + silent !ln -s Xfile Xlink + endif ]]) -- Delete the link, not the file eq(0, eval("delete('Xlink')")) @@ -58,7 +60,11 @@ describe('Test for delete()', function() it('symlink directory delete', function() command("call mkdir('Xdir1')") - command("silent !ln -s Xdir1 Xlink") + if helpers.iswin() then + command("silent !mklink /j Xlink Xdir1") + else + command("silent !ln -s Xdir1 Xlink") + end eq(1, eval("isdirectory('Xdir1')")) eq(1, eval("isdirectory('Xlink')")) -- Delete the link, not the directory @@ -78,7 +84,11 @@ describe('Test for delete()', function() w Xdir3/subdir/Xfile w Xdir4/Xfile close - silent !ln -s ../Xdir4 Xdir3/Xlink + if has('win32') + silent !mklink /j Xdir3\Xlink Xdir4 + else + silent !ln -s ../Xdir4 Xdir3/Xlink + endif ]]) eq(1, eval("isdirectory('Xdir3')")) -- cgit From f4d82c1438c2197d893f678b2fc66cc5dcbc6447 Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Sat, 14 Oct 2017 22:08:50 -0400 Subject: win: enable legacy test 011 --- test/functional/legacy/011_autocommands_spec.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'test') diff --git a/test/functional/legacy/011_autocommands_spec.lua b/test/functional/legacy/011_autocommands_spec.lua index d969a8bd37..c2667d28d2 100644 --- a/test/functional/legacy/011_autocommands_spec.lua +++ b/test/functional/legacy/011_autocommands_spec.lua @@ -18,10 +18,9 @@ local clear, feed_command, expect, eq, neq, dedent, write_file, feed = helpers.clear, helpers.feed_command, helpers.expect, helpers.eq, helpers.neq, helpers.dedent, helpers.write_file, helpers.feed -if helpers.pending_win32(pending) then return end - local function has_gzip() - return os.execute('gzip --help >/dev/null 2>&1') == 0 + local null = helpers.iswin() and 'nul' or '/dev/null' + return os.execute('gzip --help >' .. null .. ' 2>&1') == 0 end local function prepare_gz_file(name, text) @@ -142,6 +141,7 @@ describe('file reading, writing and bufnew and filter autocommands', function() end) it('FilterReadPre, FilterReadPost', function() + if helpers.pending_win32(pending) then return end -- Write a special input file for this test block. write_file('test.out', dedent([[ startstart -- cgit From e55de56a99ac74d30665af6ab400e759ed8b3b40 Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Fri, 13 Oct 2017 11:18:49 -0400 Subject: win: enable legacy/packadd_spec.lua --- test/functional/legacy/packadd_spec.lua | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) (limited to 'test') diff --git a/test/functional/legacy/packadd_spec.lua b/test/functional/legacy/packadd_spec.lua index 2dfd36142b..fb308475c0 100644 --- a/test/functional/legacy/packadd_spec.lua +++ b/test/functional/legacy/packadd_spec.lua @@ -9,17 +9,15 @@ local function expected_empty() eq({}, nvim.get_vvar('errors')) end -if helpers.pending_win32(pending) then return end - describe('packadd', function() before_each(function() clear() source([=[ func SetUp() - let s:topdir = expand('%:p:h') . '/Xdir' + let s:topdir = expand(expand('%:p:h') . '/Xdir') exe 'set packpath=' . s:topdir - let s:plugdir = s:topdir . '/pack/mine/opt/mytest' + let s:plugdir = expand(s:topdir . '/pack/mine/opt/mytest') endfunc func TearDown() @@ -52,8 +50,8 @@ describe('packadd', function() call assert_equal(77, g:plugin_also_works) call assert_true(17, g:ftdetect_works) call assert_true(len(&rtp) > len(rtp)) - call assert_true(&rtp =~ (s:plugdir . '\($\|,\)')) - call assert_true(&rtp =~ (s:plugdir . '/after$')) + call assert_true(&rtp =~ (escape(s:plugdir, '\') . '\($\|,\)')) + call assert_true(&rtp =~ escape(expand(s:plugdir . '/after$'), '\')) " Check exception call assert_fails("packadd directorynotfound", 'E919:') @@ -74,7 +72,7 @@ describe('packadd', function() packadd! mytest call assert_true(len(&rtp) > len(rtp)) - call assert_true(&rtp =~ (s:plugdir . '\($\|,\)')) + call assert_true(&rtp =~ (escape(s:plugdir, '\') . '\($\|,\)')) call assert_equal(0, g:plugin_works) " check the path is not added twice @@ -84,17 +82,18 @@ describe('packadd', function() endfunc func Test_packadd_symlink_dir() - if !has('unix') - return - endif - let top2_dir = s:topdir . '/Xdir2' - let real_dir = s:topdir . '/Xsym' + let top2_dir = expand(s:topdir . '/Xdir2') + let real_dir = expand(s:topdir . '/Xsym') call mkdir(real_dir, 'p') - exec "silent! !ln -s Xsym" top2_dir - let &rtp = top2_dir . ',' . top2_dir . '/after' + if has('win32') + exec "silent! !mklink /d" top2_dir "Xsym" + else + exec "silent! !ln -s Xsym" top2_dir + endif + let &rtp = top2_dir . ',' . expand(top2_dir . '/after') let &packpath = &rtp - let s:plugdir = top2_dir . '/pack/mine/opt/mytest' + let s:plugdir = expand(top2_dir . '/pack/mine/opt/mytest') call mkdir(s:plugdir . '/plugin', 'p') exe 'split ' . s:plugdir . '/plugin/test.vim' @@ -105,7 +104,7 @@ describe('packadd', function() packadd mytest " Must have been inserted in the middle, not at the end - call assert_true(&rtp =~ '/pack/mine/opt/mytest,') + call assert_true(&rtp =~ escape(expand('/pack/mine/opt/mytest').',', '\')) call assert_equal(44, g:plugin_works) " No change when doing it again. @@ -115,7 +114,7 @@ describe('packadd', function() set rtp& let rtp = &rtp - exec "silent !rm" top2_dir + exec "silent !" (has('win32') ? "rd /q/s" : "rm") top2_dir endfunc func Test_packloadall() -- cgit From 3c0cc9c2fb3b4982bf39ded27fa3bdd454cb9af0 Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Thu, 19 Oct 2017 00:28:37 -0400 Subject: win: enable legacy/wordcount_spec.lua --- test/functional/legacy/wordcount_spec.lua | 2 -- 1 file changed, 2 deletions(-) (limited to 'test') diff --git a/test/functional/legacy/wordcount_spec.lua b/test/functional/legacy/wordcount_spec.lua index 5412903866..0c8bd2cdcc 100644 --- a/test/functional/legacy/wordcount_spec.lua +++ b/test/functional/legacy/wordcount_spec.lua @@ -6,8 +6,6 @@ local clear, command = helpers.clear, helpers.command local eq, eval = helpers.eq, helpers.eval local wait = helpers.wait -if helpers.pending_win32(pending) then return end - describe('wordcount', function() before_each(clear) -- cgit From c5a7f451ce2385c1d687fe2b5fe331c9ebc0d618 Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Thu, 19 Oct 2017 00:47:46 -0400 Subject: win: enable legacy/getcwd_spec.lua --- test/functional/legacy/getcwd_spec.lua | 2 -- 1 file changed, 2 deletions(-) (limited to 'test') diff --git a/test/functional/legacy/getcwd_spec.lua b/test/functional/legacy/getcwd_spec.lua index 8fb31ccd22..eae13da528 100644 --- a/test/functional/legacy/getcwd_spec.lua +++ b/test/functional/legacy/getcwd_spec.lua @@ -4,8 +4,6 @@ local helpers = require('test.functional.helpers')(after_each) local eq, eval, source = helpers.eq, helpers.eval, helpers.source local call, clear, command = helpers.call, helpers.clear, helpers.command -if helpers.pending_win32(pending) then return end - describe('getcwd', function() before_each(clear) -- cgit From 18a53b65028869a7397711698c3aebaa86a441cc Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Thu, 19 Oct 2017 01:38:30 -0400 Subject: win: enable legacy test 30 --- test/functional/legacy/030_fileformats_spec.lua | 2 -- 1 file changed, 2 deletions(-) (limited to 'test') diff --git a/test/functional/legacy/030_fileformats_spec.lua b/test/functional/legacy/030_fileformats_spec.lua index 7384fdf847..2fd51602d8 100644 --- a/test/functional/legacy/030_fileformats_spec.lua +++ b/test/functional/legacy/030_fileformats_spec.lua @@ -5,8 +5,6 @@ local feed, clear, command = helpers.feed, helpers.clear, helpers.command local eq, write_file = helpers.eq, helpers.write_file local wait = helpers.wait -if helpers.pending_win32(pending) then return end - describe('fileformats option', function() setup(function() clear() -- cgit From 10fbae086a205651b5ec76251f5bb4a4a6deaf06 Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Thu, 19 Oct 2017 09:51:25 -0400 Subject: win: enable legacy/arglist_spec.lua --- test/functional/legacy/arglist_spec.lua | 2 -- 1 file changed, 2 deletions(-) (limited to 'test') diff --git a/test/functional/legacy/arglist_spec.lua b/test/functional/legacy/arglist_spec.lua index ec754a533b..bd65e549ef 100644 --- a/test/functional/legacy/arglist_spec.lua +++ b/test/functional/legacy/arglist_spec.lua @@ -4,8 +4,6 @@ local helpers = require('test.functional.helpers')(after_each) local clear, command, eq = helpers.clear, helpers.command, helpers.eq local eval, exc_exec, neq = helpers.eval, helpers.exc_exec, helpers.neq -if helpers.pending_win32(pending) then return end - describe('argument list commands', function() before_each(clear) -- cgit From 44dc8b475300f5fecf8d959c51a53f841802d188 Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Thu, 19 Oct 2017 10:05:59 -0400 Subject: win: enable legacy test 093 --- test/functional/legacy/093_mksession_cursor_cols_latin1_spec.lua | 2 -- 1 file changed, 2 deletions(-) (limited to 'test') diff --git a/test/functional/legacy/093_mksession_cursor_cols_latin1_spec.lua b/test/functional/legacy/093_mksession_cursor_cols_latin1_spec.lua index b1221ff8b6..f09fd9a6e5 100644 --- a/test/functional/legacy/093_mksession_cursor_cols_latin1_spec.lua +++ b/test/functional/legacy/093_mksession_cursor_cols_latin1_spec.lua @@ -7,8 +7,6 @@ local helpers = require('test.functional.helpers')(after_each) local feed, insert = helpers.feed, helpers.insert local clear, feed_command, expect = helpers.clear, helpers.feed_command, helpers.expect -if helpers.pending_win32(pending) then return end - describe('store cursor position in session file in Latin-1', function() setup(clear) -- cgit From 2943056f75a5ead14cd4d370215821a229cfb49e Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Thu, 19 Oct 2017 10:30:12 -0400 Subject: win: enable legacy test 107 --- test/functional/legacy/107_adjust_window_and_contents_spec.lua | 2 -- 1 file changed, 2 deletions(-) (limited to 'test') diff --git a/test/functional/legacy/107_adjust_window_and_contents_spec.lua b/test/functional/legacy/107_adjust_window_and_contents_spec.lua index 836a0f8f24..239f60341a 100644 --- a/test/functional/legacy/107_adjust_window_and_contents_spec.lua +++ b/test/functional/legacy/107_adjust_window_and_contents_spec.lua @@ -8,8 +8,6 @@ local clear = helpers.clear local insert = helpers.insert local command = helpers.command -if helpers.pending_win32(pending) then return end - describe('107', function() setup(clear) -- cgit From f1e6828b7bf45e3d3ea9acfba6a3e49938caa132 Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Thu, 19 Oct 2017 10:59:44 -0400 Subject: win: enable legacy test 059 --- test/functional/legacy/059_utf8_spell_checking_spec.lua | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) (limited to 'test') diff --git a/test/functional/legacy/059_utf8_spell_checking_spec.lua b/test/functional/legacy/059_utf8_spell_checking_spec.lua index 120e469ab2..8630ac58ef 100644 --- a/test/functional/legacy/059_utf8_spell_checking_spec.lua +++ b/test/functional/legacy/059_utf8_spell_checking_spec.lua @@ -5,8 +5,6 @@ local feed, insert, source = helpers.feed, helpers.insert, helpers.source local clear, feed_command, expect = helpers.clear, helpers.feed_command, helpers.expect local write_file, call = helpers.write_file, helpers.call -if helpers.pending_win32(pending) then return end - local function write_latin1(name, text) text = call('iconv', text, 'utf-8', 'latin-1') write_file(name, text) @@ -507,8 +505,13 @@ describe("spell checking with 'encoding' set to utf-8", function() -- Vim function in the original legacy test. local function test_one(aff, dic) -- Generate a .spl file from a .dic and .aff file. - os.execute('cp -f Xtest'..aff..'.aff Xtest.aff') - os.execute('cp -f Xtest'..dic..'.dic Xtest.dic') + if helpers.iswin() then + os.execute('copy /y Xtest'..aff..'.aff Xtest.aff') + os.execute('copy /y Xtest'..dic..'.dic Xtest.dic') + else + os.execute('cp -f Xtest'..aff..'.aff Xtest.aff') + os.execute('cp -f Xtest'..dic..'.dic Xtest.dic') + end source([[ set spellfile= function! SpellDumpNoShow() @@ -559,7 +562,11 @@ describe("spell checking with 'encoding' set to utf-8", function() feed_command([[$put =soundfold('kóopërÿnôven')]]) feed_command([[$put =soundfold('oeverloos gezwets edale')]]) -- And now with SAL instead of SOFO items; test automatic reloading. - os.execute('cp -f Xtest-sal.aff Xtest.aff') + if helpers.iswin() then + os.execute('copy /y Xtest-sal.aff Xtest.aff') + else + os.execute('cp -f Xtest-sal.aff Xtest.aff') + end feed_command('mkspell! Xtest Xtest') feed_command([[$put =soundfold('goobledygoook')]]) feed_command([[$put =soundfold('kóopërÿnôven')]]) -- cgit From 6beb7ee77ae7d0fdddc640b16eb8894a7c3a1883 Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Fri, 20 Oct 2017 21:31:05 -0400 Subject: win: enable legacy test 051 --- test/functional/legacy/051_highlight_spec.lua | 2 -- 1 file changed, 2 deletions(-) (limited to 'test') diff --git a/test/functional/legacy/051_highlight_spec.lua b/test/functional/legacy/051_highlight_spec.lua index 2ef74196ee..40f70de2ec 100644 --- a/test/functional/legacy/051_highlight_spec.lua +++ b/test/functional/legacy/051_highlight_spec.lua @@ -8,8 +8,6 @@ local eq = helpers.eq local wait = helpers.wait local exc_exec = helpers.exc_exec -if helpers.pending_win32(pending) then return end - describe(':highlight', function() setup(clear) -- cgit From 55ce6bfffb3fc55353d4cd344963a545f5444ec4 Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Thu, 5 Oct 2017 11:29:54 -0400 Subject: test: enable ex_cmds/write_spec.lua in Windows --- test/functional/ex_cmds/write_spec.lua | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) (limited to 'test') diff --git a/test/functional/ex_cmds/write_spec.lua b/test/functional/ex_cmds/write_spec.lua index 863d439080..301e72bb42 100644 --- a/test/functional/ex_cmds/write_spec.lua +++ b/test/functional/ex_cmds/write_spec.lua @@ -10,8 +10,6 @@ local feed_command = helpers.feed_command local funcs = helpers.funcs local meths = helpers.meths -if helpers.pending_win32(pending) then return end - local fname = 'Xtest-functional-ex_cmds-write' local fname_bak = fname .. '~' local fname_broken = fname_bak .. 'broken' @@ -36,7 +34,11 @@ describe(':write', function() it('&backupcopy=auto preserves symlinks', function() command('set backupcopy=auto') write_file('test_bkc_file.txt', 'content0') - command("silent !ln -s test_bkc_file.txt test_bkc_link.txt") + if helpers.iswin() then + command("silent !mklink test_bkc_link.txt test_bkc_file.txt") + else + command("silent !ln -s test_bkc_file.txt test_bkc_link.txt") + end source([[ edit test_bkc_link.txt call setline(1, ['content1']) @@ -49,7 +51,11 @@ describe(':write', function() it('&backupcopy=no replaces symlink with new file', function() command('set backupcopy=no') write_file('test_bkc_file.txt', 'content0') - command("silent !ln -s test_bkc_file.txt test_bkc_link.txt") + if helpers.iswin() then + command("silent !mklink test_bkc_link.txt test_bkc_file.txt") + else + command("silent !ln -s test_bkc_file.txt test_bkc_link.txt") + end source([[ edit test_bkc_link.txt call setline(1, ['content1']) @@ -82,8 +88,11 @@ describe(':write', function() command('let $HOME=""') eq(funcs.fnamemodify('.', ':p:h'), funcs.fnamemodify('.', ':p:h:~')) -- Message from check_overwrite - eq(('\nE17: "'..funcs.fnamemodify('.', ':p:h')..'" is a directory'), - redir_exec('write .')) + -- FIXME: 'E13: File exists (add ! to override)' in Windows + if not helpers.iswin() then + eq(('\nE17: "'..funcs.fnamemodify('.', ':p:h')..'" is a directory'), + redir_exec('write .')) + end meths.set_option('writeany', true) -- Message from buf_write eq(('\nE502: "." is a directory'), @@ -100,9 +109,16 @@ describe(':write', function() funcs.setfperm(fname, 'r--------') eq('Vim(write):E505: "Xtest-functional-ex_cmds-write" is read-only (add ! to override)', exc_exec('write')) - os.remove(fname) - os.remove(fname_bak) + if helpers.iswin() then + eq(0, os.execute('del /q/f ' .. fname)) + eq(0, os.execute('rd /q/s ' .. fname_bak)) + else + eq(true, os.remove(fname)) + eq(true, os.remove(fname_bak)) + end write_file(fname_bak, 'TTYX') + -- FIXME: exc_exec('write!') outputs 0 in Windows + if helpers.iswin() then return end lfs.link(fname_bak .. ('/xxxxx'):rep(20), fname, true) eq('Vim(write):E166: Can\'t open linked file for writing', exc_exec('write!')) -- cgit From df99ab461ee2a515b82b53573de741b6d2024644 Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Wed, 1 Nov 2017 17:40:59 -0400 Subject: test: enable legacy/fixeol_spec in Windows Try nvim's delete() for cross-platform file remove in Windows --- test/functional/legacy/fixeol_spec.lua | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) (limited to 'test') diff --git a/test/functional/legacy/fixeol_spec.lua b/test/functional/legacy/fixeol_spec.lua index 801451b300..50236e8617 100644 --- a/test/functional/legacy/fixeol_spec.lua +++ b/test/functional/legacy/fixeol_spec.lua @@ -4,15 +4,14 @@ local helpers = require('test.functional.helpers')(after_each) local feed = helpers.feed local clear, feed_command, expect = helpers.clear, helpers.feed_command, helpers.expect -if helpers.pending_win32(pending) then return end - describe('fixeol', function() local function rmtestfiles() - os.remove('test.out') - os.remove('XXEol') - os.remove('XXNoEol') - os.remove('XXTestEol') - os.remove('XXTestNoEol') + feed_command('%bwipeout!') + feed_command('call delete("test.out")') + feed_command('call delete("XXEol")') + feed_command('call delete("XXNoEol")') + feed_command('call delete("XXTestEol")') + feed_command('call delete("XXTestNoEol")') end setup(function() clear() -- cgit From d80bf3c656cc4b64005cbc149a764a5c238203ad Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Sat, 11 Nov 2017 03:14:44 -0500 Subject: test: enable ex_cmds/cd_spec.lua on Windows --- test/functional/ex_cmds/cd_spec.lua | 47 ++++++++++++++++++++----------------- test/functional/helpers.lua | 2 +- 2 files changed, 26 insertions(+), 23 deletions(-) (limited to 'test') diff --git a/test/functional/ex_cmds/cd_spec.lua b/test/functional/ex_cmds/cd_spec.lua index 059cb26d5d..d45f46ed3d 100644 --- a/test/functional/ex_cmds/cd_spec.lua +++ b/test/functional/ex_cmds/cd_spec.lua @@ -8,8 +8,7 @@ local call = helpers.call local clear = helpers.clear local command = helpers.command local exc_exec = helpers.exc_exec - -if helpers.pending_win32(pending) then return end +local pathsep = helpers.get_pathsep() -- These directories will be created for testing local directories = { @@ -75,8 +74,8 @@ for _, cmd in ipairs {'cd', 'chdir'} do eq(0, lwd(globalwin, tabnr)) -- Window with local dir reports as such - eq(globalDir .. '/' .. directories.window, cwd(localwin)) - eq(globalDir .. '/' .. directories.window, cwd(localwin, tabnr)) + eq(globalDir .. pathsep .. directories.window, cwd(localwin)) + eq(globalDir .. pathsep .. directories.window, cwd(localwin, tabnr)) eq(1, lwd(localwin)) eq(1, lwd(localwin, tabnr)) @@ -86,7 +85,7 @@ for _, cmd in ipairs {'cd', 'chdir'} do eq(0, lwd(globalwin, tabnr)) -- From new tab page, local window reports as such - eq(globalDir .. '/' .. directories.window, cwd(localwin, tabnr)) + eq(globalDir .. pathsep .. directories.window, cwd(localwin, tabnr)) eq(1, lwd(localwin, tabnr)) end) @@ -109,14 +108,14 @@ for _, cmd in ipairs {'cd', 'chdir'} do eq(0, lwd(-1, globaltab)) -- new tab reports local - eq(globalDir .. '/' .. directories.tab, cwd(-1, 0)) - eq(globalDir .. '/' .. directories.tab, cwd(-1, localtab)) + eq(globalDir .. pathsep .. directories.tab, cwd(-1, 0)) + eq(globalDir .. pathsep .. directories.tab, cwd(-1, localtab)) eq(1, lwd(-1, 0)) eq(1, lwd(-1, localtab)) command('tabnext') -- From original tab page, local reports as such - eq(globalDir .. '/' .. directories.tab, cwd(-1, localtab)) + eq(globalDir .. pathsep .. directories.tab, cwd(-1, localtab)) eq(1, lwd(-1, localtab)) end) end) @@ -147,17 +146,17 @@ for _, cmd in ipairs {'cd', 'chdir'} do -- Create a new tab and change directory command('tabnew') command('silent t' .. cmd .. ' ' .. directories.tab) - eq(globalDir .. '/' .. directories.tab, tcwd()) + eq(globalDir .. pathsep .. directories.tab, tcwd()) -- Create a new tab and verify it has inherited the directory command('tabnew') - eq(globalDir .. '/' .. directories.tab, tcwd()) + eq(globalDir .. pathsep .. directories.tab, tcwd()) -- Change tab and change back, verify that directories are correct command('tabnext') eq(globalDir, tcwd()) command('tabprevious') - eq(globalDir .. '/' .. directories.tab, tcwd()) + eq(globalDir .. pathsep .. directories.tab, tcwd()) end) end) @@ -173,7 +172,7 @@ for _, cmd in ipairs {'cd', 'chdir'} do -- Change tab-local working directory and verify it is different command('silent t' .. cmd .. ' ' .. directories.tab) - eq(globalDir .. '/' .. directories.tab, cwd()) + eq(globalDir .. pathsep .. directories.tab, cwd()) eq(cwd(), tcwd()) -- working directory maches tab directory eq(1, tlwd()) eq(cwd(), wcwd()) -- still no window-directory @@ -183,16 +182,16 @@ for _, cmd in ipairs {'cd', 'chdir'} do command('new') eq(1, tlwd()) -- Still tab-local working directory eq(0, wlwd()) -- Still no window-local working directory - eq(globalDir .. '/' .. directories.tab, cwd()) + eq(globalDir .. pathsep .. directories.tab, cwd()) command('silent l' .. cmd .. ' ../' .. directories.window) - eq(globalDir .. '/' .. directories.window, cwd()) - eq(globalDir .. '/' .. directories.tab, tcwd()) + eq(globalDir .. pathsep .. directories.window, cwd()) + eq(globalDir .. pathsep .. directories.tab, tcwd()) eq(1, wlwd()) -- Verify the first window still has the tab local directory command('wincmd w') - eq(globalDir .. '/' .. directories.tab, cwd()) - eq(globalDir .. '/' .. directories.tab, tcwd()) + eq(globalDir .. pathsep .. directories.tab, cwd()) + eq(globalDir .. pathsep .. directories.tab, tcwd()) eq(0, wlwd()) -- No window-local directory -- Change back to initial tab and verify working directory has stayed @@ -203,10 +202,10 @@ for _, cmd in ipairs {'cd', 'chdir'} do -- Verify global changes don't affect local ones command('silent ' .. cmd .. ' ' .. directories.global) - eq(globalDir .. '/' .. directories.global, cwd()) + eq(globalDir .. pathsep .. directories.global, cwd()) command('tabnext') - eq(globalDir .. '/' .. directories.tab, cwd()) - eq(globalDir .. '/' .. directories.tab, tcwd()) + eq(globalDir .. pathsep .. directories.tab, cwd()) + eq(globalDir .. pathsep .. directories.tab, tcwd()) eq(0, wlwd()) -- Still no window-local directory in this window -- Unless the global change happened in a tab with local directory @@ -220,9 +219,9 @@ for _, cmd in ipairs {'cd', 'chdir'} do -- But not in a window with its own local directory command('tabnext | wincmd w') - eq(globalDir .. '/' .. directories.window, cwd() ) + eq(globalDir .. pathsep .. directories.window, cwd() ) eq(0 , tlwd()) - eq(globalDir .. '/' .. directories.window, wcwd()) + eq(globalDir .. pathsep .. directories.window, wcwd()) end) end) end @@ -280,6 +279,10 @@ describe("getcwd()", function () end) it("returns empty string if working directory does not exist", function() + if helpers.iswin() then + pending('[Cannot delete working directory in Windows]') + return + end command("cd "..directories.global) command("call delete('../"..directories.global.."', 'd')") eq("", helpers.eval("getcwd()")) diff --git a/test/functional/helpers.lua b/test/functional/helpers.lua index a1da55ab1c..1f53200dd8 100644 --- a/test/functional/helpers.lua +++ b/test/functional/helpers.lua @@ -641,7 +641,7 @@ local function redir_exec(cmd) end local function get_pathsep() - return funcs.fnamemodify('.', ':p'):sub(-1) + return iswin() and '\\' or '/' end local function pathroot() -- cgit From 8dcfd58e2c20adea1eb69a97dead7a45d5beb170 Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Thu, 15 Feb 2018 14:13:47 -0500 Subject: test: win: enable termclose_spec.lua --- test/functional/autocmd/termclose_spec.lua | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'test') diff --git a/test/functional/autocmd/termclose_spec.lua b/test/functional/autocmd/termclose_spec.lua index e64df502a6..9918cbe4fa 100644 --- a/test/functional/autocmd/termclose_spec.lua +++ b/test/functional/autocmd/termclose_spec.lua @@ -5,8 +5,8 @@ local clear, command, nvim, nvim_dir = local eval, eq, retry = helpers.eval, helpers.eq, helpers.retry local ok = helpers.ok +local iswin = helpers.iswin -if helpers.pending_win32(pending) then return end describe('TermClose event', function() before_each(function() @@ -23,7 +23,7 @@ describe('TermClose event', function() end) it('triggers when long-running terminal job gets stopped', function() - nvim('set_option', 'shell', 'sh') + nvim('set_option', 'shell', iswin() and 'cmd.exe' or 'sh') command('autocmd TermClose * let g:test_termclose = 23') command('terminal') command('call jobstop(b:terminal_job_id)') @@ -31,6 +31,7 @@ describe('TermClose event', function() end) it('kills job trapping SIGTERM', function() + if helpers.pending_win32(pending) then return end nvim('set_option', 'shell', 'sh') nvim('set_option', 'shellcmdflag', '-c') command([[ let g:test_job = jobstart('trap "" TERM && echo 1 && sleep 60', { ]] @@ -48,6 +49,7 @@ describe('TermClose event', function() end) it('kills pty job trapping SIGHUP and SIGTERM', function() + if helpers.pending_win32(pending) then return end nvim('set_option', 'shell', 'sh') nvim('set_option', 'shellcmdflag', '-c') command([[ let g:test_job = jobstart('trap "" HUP TERM && echo 1 && sleep 60', { ]] -- cgit From 07dfe0f5ea31521679926b7aeacde5e3944246ea Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Sat, 17 Feb 2018 00:36:59 -0500 Subject: test: win: enable ui/wildmode_spec.lua --- test/functional/ui/wildmode_spec.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'test') diff --git a/test/functional/ui/wildmode_spec.lua b/test/functional/ui/wildmode_spec.lua index 042969357e..946194ed13 100644 --- a/test/functional/ui/wildmode_spec.lua +++ b/test/functional/ui/wildmode_spec.lua @@ -60,15 +60,15 @@ describe("'wildmenu'", function() command('set wildmenu wildmode=full') command('set scrollback=4') if iswin() then - if helpers.pending_win32(pending) then return end - -- feed([[:terminal 1,2,3,4,5 | foreach-object -process {echo $_; sleep 0.1}]]) + helpers.set_shell_powershell() + feed([[:terminal for ($i = 1; $i -le 5000; $i++) {Write-Output foo foo foo; Start-Sleep -Milliseconds 100}]]) else feed([[:terminal for i in $(seq 1 5000); do printf 'foo\nfoo\nfoo\n'; sleep 0.1; done]]) end feed([[gg]]) feed([[:sign ]]) -- Invoke wildmenu. - screen:sleep(50) -- Allow some terminal output. + screen:sleep(iswin() and 500 or 50) -- Allow some terminal output. screen:expect([[ foo | foo | @@ -80,7 +80,7 @@ describe("'wildmenu'", function() -- cmdline CTRL-D display should also be preserved. feed([[]]) feed([[:sign ]]) -- Invoke cmdline CTRL-D. - screen:sleep(50) -- Allow some terminal output. + screen:sleep(iswin() and 500 or 50) -- Allow some terminal output. screen:expect([[ :sign | define place | -- cgit From 795da343bbf99c4b77d10cd5035c29df23f113ae Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Sat, 17 Feb 2018 02:25:51 -0500 Subject: test: win: emulate yes with for loop --- test/functional/eval/system_spec.lua | 12 ++++++++---- test/functional/ui/wildmode_spec.lua | 7 +++---- 2 files changed, 11 insertions(+), 8 deletions(-) (limited to 'test') diff --git a/test/functional/eval/system_spec.lua b/test/functional/eval/system_spec.lua index 7fe79d4351..446afefb59 100644 --- a/test/functional/eval/system_spec.lua +++ b/test/functional/eval/system_spec.lua @@ -188,8 +188,9 @@ describe('system()', function() end) it('`yes` and is interrupted with CTRL-C', function() - if helpers.pending_win32(pending) then return end - feed(':call system("yes")') + feed(':call system("' .. (iswin() + and 'for /L %I in (1,0,2) do @echo y' + or 'yes') .. '")') screen:expect([[ | ~ | @@ -204,8 +205,11 @@ describe('system()', function() ~ | ~ | ~ | - :call system("yes") | - ]]) +]] .. (iswin() + and [[ + :call system("for /L %I in (1,0,2) do @echo y") |]] + or [[ + :call system("yes") |]])) feed('') screen:expect([[ ^ | diff --git a/test/functional/ui/wildmode_spec.lua b/test/functional/ui/wildmode_spec.lua index 946194ed13..c6ddc78618 100644 --- a/test/functional/ui/wildmode_spec.lua +++ b/test/functional/ui/wildmode_spec.lua @@ -60,15 +60,14 @@ describe("'wildmenu'", function() command('set wildmenu wildmode=full') command('set scrollback=4') if iswin() then - helpers.set_shell_powershell() - feed([[:terminal for ($i = 1; $i -le 5000; $i++) {Write-Output foo foo foo; Start-Sleep -Milliseconds 100}]]) + feed([[:terminal for /L \%I in (1,1,5000) do @(echo foo & echo foo & echo foo)]]) else feed([[:terminal for i in $(seq 1 5000); do printf 'foo\nfoo\nfoo\n'; sleep 0.1; done]]) end feed([[gg]]) feed([[:sign ]]) -- Invoke wildmenu. - screen:sleep(iswin() and 500 or 50) -- Allow some terminal output. + screen:sleep(50) -- Allow some terminal output. screen:expect([[ foo | foo | @@ -80,7 +79,7 @@ describe("'wildmenu'", function() -- cmdline CTRL-D display should also be preserved. feed([[]]) feed([[:sign ]]) -- Invoke cmdline CTRL-D. - screen:sleep(iswin() and 500 or 50) -- Allow some terminal output. + screen:sleep(50) -- Allow some terminal output. screen:expect([[ :sign | define place | -- cgit From 7fa69fb288d9fcf66c2fe504d760cc7a23da7256 Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Mon, 19 Feb 2018 07:09:46 -0500 Subject: Resolve issues mentioned in PR review --- test/functional/core/job_spec.lua | 4 ---- test/functional/ex_cmds/cd_spec.lua | 1 - test/functional/ex_cmds/write_spec.lua | 1 - 3 files changed, 6 deletions(-) (limited to 'test') diff --git a/test/functional/core/job_spec.lua b/test/functional/core/job_spec.lua index 5f39040a6f..50feb41d1a 100644 --- a/test/functional/core/job_spec.lua +++ b/test/functional/core/job_spec.lua @@ -297,10 +297,6 @@ describe('jobs', function() eq({'notification', 'exit', {data, 0}}, next_msg()) end) - it('can omit options', function() - ok(eval([[jobstart('echo ""')]]) > 0) - end) - it('can omit data callbacks', function() nvim('command', 'unlet g:job_opts.on_stdout') nvim('command', 'let g:job_opts.user = 5') diff --git a/test/functional/ex_cmds/cd_spec.lua b/test/functional/ex_cmds/cd_spec.lua index d45f46ed3d..bc2b365b30 100644 --- a/test/functional/ex_cmds/cd_spec.lua +++ b/test/functional/ex_cmds/cd_spec.lua @@ -280,7 +280,6 @@ describe("getcwd()", function () it("returns empty string if working directory does not exist", function() if helpers.iswin() then - pending('[Cannot delete working directory in Windows]') return end command("cd "..directories.global) diff --git a/test/functional/ex_cmds/write_spec.lua b/test/functional/ex_cmds/write_spec.lua index 301e72bb42..bcf83698bb 100644 --- a/test/functional/ex_cmds/write_spec.lua +++ b/test/functional/ex_cmds/write_spec.lua @@ -88,7 +88,6 @@ describe(':write', function() command('let $HOME=""') eq(funcs.fnamemodify('.', ':p:h'), funcs.fnamemodify('.', ':p:h:~')) -- Message from check_overwrite - -- FIXME: 'E13: File exists (add ! to override)' in Windows if not helpers.iswin() then eq(('\nE17: "'..funcs.fnamemodify('.', ':p:h')..'" is a directory'), redir_exec('write .')) -- cgit From 04fdbfe17d5c8242f54459472120177188949f82 Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Tue, 20 Feb 2018 11:37:55 +0100 Subject: jobwait: return -2 on interrupt also with timeout --- test/functional/core/job_spec.lua | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'test') diff --git a/test/functional/core/job_spec.lua b/test/functional/core/job_spec.lua index a02d36c939..65ec198a08 100644 --- a/test/functional/core/job_spec.lua +++ b/test/functional/core/job_spec.lua @@ -491,7 +491,7 @@ describe('jobs', function() eq({'notification', 'wait', {{-3, 5}}}, next_msg()) end) - it('will return -2 when interrupted', function() + it('will return -2 when interrupted without timeout', function() feed_command('call rpcnotify(g:channel, "ready") | '.. 'call rpcnotify(g:channel, "wait", '.. 'jobwait([jobstart("sleep 10; exit 55")]))') @@ -500,6 +500,15 @@ describe('jobs', function() eq({'notification', 'wait', {{-2}}}, next_msg()) end) + it('will return -2 when interrupted with timeout', function() + feed_command('call rpcnotify(g:channel, "ready") | '.. + 'call rpcnotify(g:channel, "wait", '.. + 'jobwait([jobstart("sleep 10; exit 55")], 10000))') + eq({'notification', 'ready', {}}, next_msg()) + feed('') + eq({'notification', 'wait', {{-2}}}, next_msg()) + end) + it('can be called recursively', function() if helpers.pending_win32(pending) then return end -- TODO: Need `cat`. source([[ -- cgit From 384a39479a0b70abf9cd6ced0b5f1d53cd817c11 Mon Sep 17 00:00:00 2001 From: Matthieu Coudron Date: Fri, 9 Feb 2018 08:44:24 +0900 Subject: 'fillchars': fix defaults logic; handle ambiwidth=double #7986 Update tests. --- test/functional/ex_cmds/drop_spec.lua | 32 +++---- test/functional/options/defaults_spec.lua | 36 +++++++- test/functional/terminal/mouse_spec.lua | 80 ++++++++--------- test/functional/ui/highlight_spec.lua | 60 ++++++------- test/functional/ui/inccommand_spec.lua | 68 +++++++------- test/functional/ui/mouse_spec.lua | 48 +++++----- test/functional/ui/screen_basic_spec.lua | 144 +++++++++++++++--------------- 7 files changed, 251 insertions(+), 217 deletions(-) (limited to 'test') diff --git a/test/functional/ex_cmds/drop_spec.lua b/test/functional/ex_cmds/drop_spec.lua index 9105b84367..30dbd27d37 100644 --- a/test/functional/ex_cmds/drop_spec.lua +++ b/test/functional/ex_cmds/drop_spec.lua @@ -44,14 +44,14 @@ describe(":drop", function() feed_command("edit tmp2") feed_command("drop tmp1") screen:expect([[ - {2:|}^ | - {0:~ }{2:|}{0:~ }| - {0:~ }{2:|}{0:~ }| - {0:~ }{2:|}{0:~ }| - {0:~ }{2:|}{0:~ }| - {0:~ }{2:|}{0:~ }| - {0:~ }{2:|}{0:~ }| - {0:~ }{2:|}{0:~ }| + {2:│}^ | + {0:~ }{2:│}{0:~ }| + {0:~ }{2:│}{0:~ }| + {0:~ }{2:│}{0:~ }| + {0:~ }{2:│}{0:~ }| + {0:~ }{2:│}{0:~ }| + {0:~ }{2:│}{0:~ }| + {0:~ }{2:│}{0:~ }| {2:tmp2 }{1:tmp1 }| :drop tmp1 | ]]) @@ -64,14 +64,14 @@ describe(":drop", function() feed("iABC") feed_command("drop tmp3") screen:expect([[ - ^ {2:|} | - {0:~ }{2:|}{0:~ }| - {0:~ }{2:|}{0:~ }| - {0:~ }{2:|}{0:~ }| - {1:tmp3 }{2:|}{0:~ }| - ABC {2:|}{0:~ }| - {0:~ }{2:|}{0:~ }| - {0:~ }{2:|}{0:~ }| + ^ {2:│} | + {0:~ }{2:│}{0:~ }| + {0:~ }{2:│}{0:~ }| + {0:~ }{2:│}{0:~ }| + {1:tmp3 }{2:│}{0:~ }| + ABC {2:│}{0:~ }| + {0:~ }{2:│}{0:~ }| + {0:~ }{2:│}{0:~ }| {2:tmp2 [+] tmp1 }| "tmp3" [New File] | ]]) diff --git a/test/functional/options/defaults_spec.lua b/test/functional/options/defaults_spec.lua index 89d4e56d04..9e29baba2d 100644 --- a/test/functional/options/defaults_spec.lua +++ b/test/functional/options/defaults_spec.lua @@ -7,6 +7,7 @@ local command = helpers.command local clear = helpers.clear local eval = helpers.eval local eq = helpers.eq +local insert = helpers.insert local neq = helpers.neq local mkdir = helpers.mkdir local rmdir = helpers.rmdir @@ -115,7 +116,40 @@ describe('startup defaults', function() end) end) - describe('packpath', function() + describe("'fillchars'", function() + it('vert/fold flags', function() + clear() + local screen = Screen.new(50, 5) + screen:attach() + command('set laststatus=0') + insert([[ + 1 + 2 + 3 + 4]]) + command('normal! ggjzfj') + command('vsp') + screen:expect([[ + 1 │1 | + ^+-- 2 lines: 2··········│+-- 2 lines: 2·········| + 4 │4 | + ~ │~ | + | + ]]) + + -- ambiwidth=double defaults to single-byte fillchars. + command('set ambiwidth=double') + screen:expect([[ + 1 |1 | + ^+-- 2 lines: 2----------|+-- 2 lines: 2---------| + 4 |4 | + ~ |~ | + | + ]]) + end) + end) + + describe("'packpath'", function() it('defaults to &runtimepath', function() eq(meths.get_option('runtimepath'), meths.get_option('packpath')) end) diff --git a/test/functional/terminal/mouse_spec.lua b/test/functional/terminal/mouse_spec.lua index 5e5558ee0a..a21d9f0a56 100644 --- a/test/functional/terminal/mouse_spec.lua +++ b/test/functional/terminal/mouse_spec.lua @@ -98,41 +98,41 @@ describe('terminal mouse', function() before_each(function() feed(':vsp') screen:expect([[ - line28 |line28 | - line29 |line29 | - line30 |line30 | - rows: 5, cols: 24 |rows: 5, cols: 24 | - {2:^ } |{2: } | + line28 │line28 | + line29 │line29 | + line30 │line30 | + rows: 5, cols: 24 │rows: 5, cols: 24 | + {2:^ } │{2: } | ========== ========== | :vsp | ]]) feed(':enew | set number') screen:expect([[ - {7: 1 }^ |line28 | - {4:~ }|line29 | - {4:~ }|line30 | - {4:~ }|rows: 5, cols: 24 | - {4:~ }|{2: } | + {7: 1 }^ │line28 | + {4:~ }│line29 | + {4:~ }│line30 | + {4:~ }│rows: 5, cols: 24 | + {4:~ }│{2: } | ========== ========== | :enew | set number | ]]) feed('30iline\n') screen:expect([[ - {7: 27 }line |line28 | - {7: 28 }line |line29 | - {7: 29 }line |line30 | - {7: 30 }line |rows: 5, cols: 24 | - {7: 31 }^ |{2: } | + {7: 27 }line │line28 | + {7: 28 }line │line29 | + {7: 29 }line │line30 | + {7: 30 }line │rows: 5, cols: 24 | + {7: 31 }^ │{2: } | ========== ========== | | ]]) feed('li') screen:expect([[ - {7: 27 }line |line28 | - {7: 28 }line |line29 | - {7: 29 }line |line30 | - {7: 30 }line |rows: 5, cols: 24 | - {7: 31 } |{1: } | + {7: 27 }line │line28 | + {7: 28 }line │line29 | + {7: 29 }line │line30 | + {7: 30 }line │rows: 5, cols: 24 | + {7: 31 } │{1: } | ========== ========== | {3:-- TERMINAL --} | ]]) @@ -140,11 +140,11 @@ describe('terminal mouse', function() thelpers.enable_mouse() thelpers.feed_data('mouse enabled\n') screen:expect([[ - {7: 27 }line |line29 | - {7: 28 }line |line30 | - {7: 29 }line |rows: 5, cols: 24 | - {7: 30 }line |mouse enabled | - {7: 31 } |{1: } | + {7: 27 }line │line29 | + {7: 28 }line │line30 | + {7: 29 }line │rows: 5, cols: 24 | + {7: 30 }line │mouse enabled | + {7: 31 } │{1: } | ========== ========== | {3:-- TERMINAL --} | ]]) @@ -153,21 +153,21 @@ describe('terminal mouse', function() it('wont lose focus if another window is scrolled', function() feed('<0,0><0,0>') screen:expect([[ - {7: 21 }line |line29 | - {7: 22 }line |line30 | - {7: 23 }line |rows: 5, cols: 24 | - {7: 24 }line |mouse enabled | - {7: 25 }line |{1: } | + {7: 21 }line │line29 | + {7: 22 }line │line30 | + {7: 23 }line │rows: 5, cols: 24 | + {7: 24 }line │mouse enabled | + {7: 25 }line │{1: } | ========== ========== | {3:-- TERMINAL --} | ]]) feed('<0,0>') screen:expect([[ - {7: 26 }line |line29 | - {7: 27 }line |line30 | - {7: 28 }line |rows: 5, cols: 24 | - {7: 29 }line |mouse enabled | - {7: 30 }line |{1: } | + {7: 26 }line │line29 | + {7: 27 }line │line30 | + {7: 28 }line │rows: 5, cols: 24 | + {7: 29 }line │mouse enabled | + {7: 30 }line │{1: } | ========== ========== | {3:-- TERMINAL --} | ]]) @@ -176,11 +176,11 @@ describe('terminal mouse', function() it('will lose focus if another window is clicked', function() feed('<5,1>') screen:expect([[ - {7: 27 }line |line29 | - {7: 28 }l^ine |line30 | - {7: 29 }line |rows: 5, cols: 24 | - {7: 30 }line |mouse enabled | - {7: 31 } |{2: } | + {7: 27 }line │line29 | + {7: 28 }l^ine │line30 | + {7: 29 }line │rows: 5, cols: 24 | + {7: 30 }line │mouse enabled | + {7: 31 } │{2: } | ========== ========== | | ]]) diff --git a/test/functional/ui/highlight_spec.lua b/test/functional/ui/highlight_spec.lua index 95fb2ce21c..6f1b31964b 100644 --- a/test/functional/ui/highlight_spec.lua +++ b/test/functional/ui/highlight_spec.lua @@ -108,12 +108,12 @@ describe('highlight defaults', function() }) feed_command('sp', 'vsp', 'vsp') screen:expect([[ - ^ {2:|} {2:|} | - {0:~ }{2:|}{0:~ }{2:|}{0:~ }| - {0:~ }{2:|}{0:~ }{2:|}{0:~ }| - {0:~ }{2:|}{0:~ }{2:|}{0:~ }| - {0:~ }{2:|}{0:~ }{2:|}{0:~ }| - {0:~ }{2:|}{0:~ }{2:|}{0:~ }| + ^ {2:│} {2:│} | + {0:~ }{2:│}{0:~ }{2:│}{0:~ }| + {0:~ }{2:│}{0:~ }{2:│}{0:~ }| + {0:~ }{2:│}{0:~ }{2:│}{0:~ }| + {0:~ }{2:│}{0:~ }{2:│}{0:~ }| + {0:~ }{2:│}{0:~ }{2:│}{0:~ }| {1:[No Name] }{2:[No Name] [No Name] }| | {0:~ }| @@ -126,12 +126,12 @@ describe('highlight defaults', function() -- navigate to verify that the attributes are properly moved feed('j') screen:expect([[ - {2:|} {2:|} | - {0:~ }{2:|}{0:~ }{2:|}{0:~ }| - {0:~ }{2:|}{0:~ }{2:|}{0:~ }| - {0:~ }{2:|}{0:~ }{2:|}{0:~ }| - {0:~ }{2:|}{0:~ }{2:|}{0:~ }| - {0:~ }{2:|}{0:~ }{2:|}{0:~ }| + {2:│} {2:│} | + {0:~ }{2:│}{0:~ }{2:│}{0:~ }| + {0:~ }{2:│}{0:~ }{2:│}{0:~ }| + {0:~ }{2:│}{0:~ }{2:│}{0:~ }| + {0:~ }{2:│}{0:~ }{2:│}{0:~ }| + {0:~ }{2:│}{0:~ }{2:│}{0:~ }| {2:[No Name] [No Name] [No Name] }| ^ | {0:~ }| @@ -146,12 +146,12 @@ describe('highlight defaults', function() -- (upstream vim has the same behavior) feed('kl') screen:expect([[ - {2:|}^ {2:|} | - {0:~ }{2:|}{0:~ }{2:|}{0:~ }| - {0:~ }{2:|}{0:~ }{2:|}{0:~ }| - {0:~ }{2:|}{0:~ }{2:|}{0:~ }| - {0:~ }{2:|}{0:~ }{2:|}{0:~ }| - {0:~ }{2:|}{0:~ }{2:|}{0:~ }| + {2:│}^ {2:│} | + {0:~ }{2:│}{0:~ }{2:│}{0:~ }| + {0:~ }{2:│}{0:~ }{2:│}{0:~ }| + {0:~ }{2:│}{0:~ }{2:│}{0:~ }| + {0:~ }{2:│}{0:~ }{2:│}{0:~ }| + {0:~ }{2:│}{0:~ }{2:│}{0:~ }| {2:[No Name] }{1:[No Name] }{2:[No Name] }| | {0:~ }| @@ -163,12 +163,12 @@ describe('highlight defaults', function() ]]) feed('l') screen:expect([[ - {2:|} {2:|}^ | - {0:~ }{2:|}{0:~ }{2:|}{0:~ }| - {0:~ }{2:|}{0:~ }{2:|}{0:~ }| - {0:~ }{2:|}{0:~ }{2:|}{0:~ }| - {0:~ }{2:|}{0:~ }{2:|}{0:~ }| - {0:~ }{2:|}{0:~ }{2:|}{0:~ }| + {2:│} {2:│}^ | + {0:~ }{2:│}{0:~ }{2:│}{0:~ }| + {0:~ }{2:│}{0:~ }{2:│}{0:~ }| + {0:~ }{2:│}{0:~ }{2:│}{0:~ }| + {0:~ }{2:│}{0:~ }{2:│}{0:~ }| + {0:~ }{2:│}{0:~ }{2:│}{0:~ }| {2:[No Name] [No Name] }{1:[No Name] }| | {0:~ }| @@ -180,12 +180,12 @@ describe('highlight defaults', function() ]]) feed('hh') screen:expect([[ - ^ {2:|} {2:|} | - {0:~ }{2:|}{0:~ }{2:|}{0:~ }| - {0:~ }{2:|}{0:~ }{2:|}{0:~ }| - {0:~ }{2:|}{0:~ }{2:|}{0:~ }| - {0:~ }{2:|}{0:~ }{2:|}{0:~ }| - {0:~ }{2:|}{0:~ }{2:|}{0:~ }| + ^ {2:│} {2:│} | + {0:~ }{2:│}{0:~ }{2:│}{0:~ }| + {0:~ }{2:│}{0:~ }{2:│}{0:~ }| + {0:~ }{2:│}{0:~ }{2:│}{0:~ }| + {0:~ }{2:│}{0:~ }{2:│}{0:~ }| + {0:~ }{2:│}{0:~ }{2:│}{0:~ }| {1:[No Name] }{2:[No Name] [No Name] }| | {0:~ }| diff --git a/test/functional/ui/inccommand_spec.lua b/test/functional/ui/inccommand_spec.lua index 74ab9864f0..8b0cb82365 100644 --- a/test/functional/ui/inccommand_spec.lua +++ b/test/functional/ui/inccommand_spec.lua @@ -1638,26 +1638,26 @@ describe("'inccommand' split windows", function() feed_command("split") feed(":%s/tw") screen:expect([[ - Inc substitution on {10:|}Inc substitution on| - {12:tw}o lines {10:|}{12:tw}o lines | - {10:|} | - {15:~ }{10:|}{15:~ }| - {15:~ }{10:|}{15:~ }| - {15:~ }{10:|}{15:~ }| - {15:~ }{10:|}{15:~ }| - {15:~ }{10:|}{15:~ }| - {15:~ }{10:|}{15:~ }| - {15:~ }{10:|}{15:~ }| - {15:~ }{10:|}{15:~ }| - {15:~ }{10:|}{15:~ }| - {15:~ }{10:|}{15:~ }| - {15:~ }{10:|}{15:~ }| - {11:[No Name] [+] }{10:|}{15:~ }| - Inc substitution on {10:|}{15:~ }| - {12:tw}o lines {10:|}{15:~ }| - {10:|}{15:~ }| - {15:~ }{10:|}{15:~ }| - {15:~ }{10:|}{15:~ }| + Inc substitution on {10:│}Inc substitution on| + {12:tw}o lines {10:│}{12:tw}o lines | + {10:│} | + {15:~ }{10:│}{15:~ }| + {15:~ }{10:│}{15:~ }| + {15:~ }{10:│}{15:~ }| + {15:~ }{10:│}{15:~ }| + {15:~ }{10:│}{15:~ }| + {15:~ }{10:│}{15:~ }| + {15:~ }{10:│}{15:~ }| + {15:~ }{10:│}{15:~ }| + {15:~ }{10:│}{15:~ }| + {15:~ }{10:│}{15:~ }| + {15:~ }{10:│}{15:~ }| + {11:[No Name] [+] }{10:│}{15:~ }| + Inc substitution on {10:│}{15:~ }| + {12:tw}o lines {10:│}{15:~ }| + {10:│}{15:~ }| + {15:~ }{10:│}{15:~ }| + {15:~ }{10:│}{15:~ }| {10:[No Name] [+] [No Name] [+] }| |2| {12:tw}o lines | {15:~ }| @@ -1677,20 +1677,20 @@ describe("'inccommand' split windows", function() feed(":%s/tw") screen:expect([[ - Inc substitution on {10:|}Inc substitution on| - {12:tw}o lines {10:|}{12:tw}o lines | - {10:|} | - {15:~ }{10:|}{15:~ }| - {15:~ }{10:|}{15:~ }| - {15:~ }{10:|}{15:~ }| - {15:~ }{10:|}{15:~ }| - {15:~ }{10:|}{15:~ }| - {15:~ }{10:|}{15:~ }| - {15:~ }{10:|}{15:~ }| - {15:~ }{10:|}{15:~ }| - {15:~ }{10:|}{15:~ }| - {15:~ }{10:|}{15:~ }| - {15:~ }{10:|}{15:~ }| + Inc substitution on {10:│}Inc substitution on| + {12:tw}o lines {10:│}{12:tw}o lines | + {10:│} | + {15:~ }{10:│}{15:~ }| + {15:~ }{10:│}{15:~ }| + {15:~ }{10:│}{15:~ }| + {15:~ }{10:│}{15:~ }| + {15:~ }{10:│}{15:~ }| + {15:~ }{10:│}{15:~ }| + {15:~ }{10:│}{15:~ }| + {15:~ }{10:│}{15:~ }| + {15:~ }{10:│}{15:~ }| + {15:~ }{10:│}{15:~ }| + {15:~ }{10:│}{15:~ }| {11:[No Name] [+] }{10:[No Name] [+] }| Inc substitution on | {12:tw}o lines | diff --git a/test/functional/ui/mouse_spec.lua b/test/functional/ui/mouse_spec.lua index e5708d51ef..05d5a6409a 100644 --- a/test/functional/ui/mouse_spec.lua +++ b/test/functional/ui/mouse_spec.lua @@ -638,12 +638,12 @@ describe('ui/mouse/input', function() screen:try_resize(53, 14) feed_command('sp', 'vsp') screen:expect([[ - lines {4:|}lines | - to {4:|}to | - test {4:|}test | - mouse scrolling {4:|}mouse scrolling | - ^ {4:|} | - {0:~ }{4:|}{0:~ }| + lines {4:│}lines | + to {4:│}to | + test {4:│}test | + mouse scrolling {4:│}mouse scrolling | + ^ {4:│} | + {0:~ }{4:│}{0:~ }| {5:[No Name] [+] }{4:[No Name] [+] }| to | test | @@ -655,12 +655,12 @@ describe('ui/mouse/input', function() ]]) feed('<0,0>') screen:expect([[ - mouse scrolling {4:|}lines | - ^ {4:|}to | - {0:~ }{4:|}test | - {0:~ }{4:|}mouse scrolling | - {0:~ }{4:|} | - {0:~ }{4:|}{0:~ }| + mouse scrolling {4:│}lines | + ^ {4:│}to | + {0:~ }{4:│}test | + {0:~ }{4:│}mouse scrolling | + {0:~ }{4:│} | + {0:~ }{4:│}{0:~ }| {5:[No Name] [+] }{4:[No Name] [+] }| to | test | @@ -672,12 +672,12 @@ describe('ui/mouse/input', function() ]]) feed('<27,0>') screen:expect([[ - mouse scrolling {4:|}text | - ^ {4:|}with | - {0:~ }{4:|}many | - {0:~ }{4:|}lines | - {0:~ }{4:|}to | - {0:~ }{4:|}test | + mouse scrolling {4:│}text | + ^ {4:│}with | + {0:~ }{4:│}many | + {0:~ }{4:│}lines | + {0:~ }{4:│}to | + {0:~ }{4:│}test | {5:[No Name] [+] }{4:[No Name] [+] }| to | test | @@ -689,12 +689,12 @@ describe('ui/mouse/input', function() ]]) feed('<27,7>') screen:expect([[ - mouse scrolling {4:|}text | - ^ {4:|}with | - {0:~ }{4:|}many | - {0:~ }{4:|}lines | - {0:~ }{4:|}to | - {0:~ }{4:|}test | + mouse scrolling {4:│}text | + ^ {4:│}with | + {0:~ }{4:│}many | + {0:~ }{4:│}lines | + {0:~ }{4:│}to | + {0:~ }{4:│}test | {5:[No Name] [+] }{4:[No Name] [+] }| Inserting | text | diff --git a/test/functional/ui/screen_basic_spec.lua b/test/functional/ui/screen_basic_spec.lua index b31d9cb32f..bbdf576641 100644 --- a/test/functional/ui/screen_basic_spec.lua +++ b/test/functional/ui/screen_basic_spec.lua @@ -189,12 +189,12 @@ describe('Screen', function() command('vsp') command('vsp') screen:expect([[ - ^ {3:|} {3:|} | - {0:~ }{3:|}{0:~ }{3:|}{0:~ }| - {0:~ }{3:|}{0:~ }{3:|}{0:~ }| - {0:~ }{3:|}{0:~ }{3:|}{0:~ }| - {0:~ }{3:|}{0:~ }{3:|}{0:~ }| - {0:~ }{3:|}{0:~ }{3:|}{0:~ }| + ^ {3:│} {3:│} | + {0:~ }{3:│}{0:~ }{3:│}{0:~ }| + {0:~ }{3:│}{0:~ }{3:│}{0:~ }| + {0:~ }{3:│}{0:~ }{3:│}{0:~ }| + {0:~ }{3:│}{0:~ }{3:│}{0:~ }| + {0:~ }{3:│}{0:~ }{3:│}{0:~ }| {1:[No Name] }{3:[No Name] [No Name] }| | {0:~ }| @@ -206,12 +206,12 @@ describe('Screen', function() ]]) insert('hello') screen:expect([[ - hell^o {3:|}hello {3:|}hello | - {0:~ }{3:|}{0:~ }{3:|}{0:~ }| - {0:~ }{3:|}{0:~ }{3:|}{0:~ }| - {0:~ }{3:|}{0:~ }{3:|}{0:~ }| - {0:~ }{3:|}{0:~ }{3:|}{0:~ }| - {0:~ }{3:|}{0:~ }{3:|}{0:~ }| + hell^o {3:│}hello {3:│}hello | + {0:~ }{3:│}{0:~ }{3:│}{0:~ }| + {0:~ }{3:│}{0:~ }{3:│}{0:~ }| + {0:~ }{3:│}{0:~ }{3:│}{0:~ }| + {0:~ }{3:│}{0:~ }{3:│}{0:~ }| + {0:~ }{3:│}{0:~ }{3:│}{0:~ }| {1:[No Name] [+] }{3:[No Name] [+] [No Name] [+] }| hello | {0:~ }| @@ -232,12 +232,12 @@ describe('Screen', function() command('vsp') insert('hello') screen:expect([[ - hell^o {3:|}hello {3:|}hello | - {0:~ }{3:|}{0:~ }{3:|}{0:~ }| - {0:~ }{3:|}{0:~ }{3:|}{0:~ }| - {0:~ }{3:|}{0:~ }{3:|}{0:~ }| - {0:~ }{3:|}{0:~ }{3:|}{0:~ }| - {0:~ }{3:|}{0:~ }{3:|}{0:~ }| + hell^o {3:│}hello {3:│}hello | + {0:~ }{3:│}{0:~ }{3:│}{0:~ }| + {0:~ }{3:│}{0:~ }{3:│}{0:~ }| + {0:~ }{3:│}{0:~ }{3:│}{0:~ }| + {0:~ }{3:│}{0:~ }{3:│}{0:~ }| + {0:~ }{3:│}{0:~ }{3:│}{0:~ }| {1:[No Name] [+] }{3:[No Name] [+] [No Name] [+] }| hello | {0:~ }| @@ -269,12 +269,12 @@ describe('Screen', function() command('tabprevious') screen:expect([[ {2: }{6:4}{2:+ [No Name] }{4: + [No Name] }{3: }{4:X}| - hell^o {3:|}hello {3:|}hello | - {0:~ }{3:|}{0:~ }{3:|}{0:~ }| - {0:~ }{3:|}{0:~ }{3:|}{0:~ }| - {0:~ }{3:|}{0:~ }{3:|}{0:~ }| - {0:~ }{3:|}{0:~ }{3:|}{0:~ }| - {0:~ }{3:|}{0:~ }{3:|}{0:~ }| + hell^o {3:│}hello {3:│}hello | + {0:~ }{3:│}{0:~ }{3:│}{0:~ }| + {0:~ }{3:│}{0:~ }{3:│}{0:~ }| + {0:~ }{3:│}{0:~ }{3:│}{0:~ }| + {0:~ }{3:│}{0:~ }{3:│}{0:~ }| + {0:~ }{3:│}{0:~ }{3:│}{0:~ }| {1:[No Name] [+] }{3:[No Name] [+] [No Name] [+] }| hello | {0:~ }| @@ -398,12 +398,12 @@ describe('Screen', function() command('vsp') command('vsp') screen:expect([[ - and {3:|}and {3:|}and | - clearing {3:|}clearing {3:|}clearing | - in {3:|}in {3:|}in | - split {3:|}split {3:|}split | - windows {3:|}windows {3:|}windows | - ^ {3:|} {3:|} | + and {3:│}and {3:│}and | + clearing {3:│}clearing {3:│}clearing | + in {3:│}in {3:│}in | + split {3:│}split {3:│}split | + windows {3:│}windows {3:│}windows | + ^ {3:│} {3:│} | {1:[No Name] [+] }{3:[No Name] [+] [No Name] [+] }| clearing | in | @@ -418,12 +418,12 @@ describe('Screen', function() it('only affects the current scroll region', function() feed('6k') screen:expect([[ - ^scrolling {3:|}and {3:|}and | - and {3:|}clearing {3:|}clearing | - clearing {3:|}in {3:|}in | - in {3:|}split {3:|}split | - split {3:|}windows {3:|}windows | - windows {3:|} {3:|} | + ^scrolling {3:│}and {3:│}and | + and {3:│}clearing {3:│}clearing | + clearing {3:│}in {3:│}in | + in {3:│}split {3:│}split | + split {3:│}windows {3:│}windows | + windows {3:│} {3:│} | {1:[No Name] [+] }{3:[No Name] [+] [No Name] [+] }| clearing | in | @@ -435,12 +435,12 @@ describe('Screen', function() ]]) feed('l') screen:expect([[ - scrolling {3:|}and {3:|}and | - and {3:|}clearing {3:|}clearing | - clearing {3:|}in {3:|}in | - in {3:|}split {3:|}split | - split {3:|}windows {3:|}windows | - windows {3:|}^ {3:|} | + scrolling {3:│}and {3:│}and | + and {3:│}clearing {3:│}clearing | + clearing {3:│}in {3:│}in | + in {3:│}split {3:│}split | + split {3:│}windows {3:│}windows | + windows {3:│}^ {3:│} | {3:[No Name] [+] }{1:[No Name] [+] }{3: Date: Sun, 25 Feb 2018 04:23:12 -0500 Subject: unittest: Ignore all _Float-prefixed types (#8067) Previously, we ignored only _Float128. But glibc 2.27 added _Float32 and _Float32x. Rather than play whack-a-mole, ignore everything. --- test/unit/helpers.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'test') diff --git a/test/unit/helpers.lua b/test/unit/helpers.lua index 87c838dece..5cc2be50b1 100644 --- a/test/unit/helpers.lua +++ b/test/unit/helpers.lua @@ -138,7 +138,7 @@ local function filter_complex_blocks(body) for line in body:gmatch("[^\r\n]+") do if not (string.find(line, "(^)", 1, true) ~= nil or string.find(line, "_ISwupper", 1, true) - or string.find(line, "_Float128") + or string.find(line, "_Float") or string.find(line, "msgpack_zone_push_finalizer") or string.find(line, "msgpack_unpacker_reserve_buffer") or string.find(line, "UUID_NULL") -- static const uuid_t UUID_NULL = {...} -- cgit From 9f994bb69925ed943b17d03f2cad7d5e2c21e992 Mon Sep 17 00:00:00 2001 From: geekodour Date: Fri, 16 Feb 2018 14:10:40 +0530 Subject: api: nvim_list_uis #8004 ref #7438 closes #4842 --- test/functional/api/vim_spec.lua | 41 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) (limited to 'test') diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua index 1faed4e9b1..bd56161a54 100644 --- a/test/functional/api/vim_spec.lua +++ b/test/functional/api/vim_spec.lua @@ -745,7 +745,7 @@ describe('api', function() end) end) - describe('list_runtime_paths', function() + describe('nvim_list_runtime_paths', function() it('returns nothing with empty &runtimepath', function() meths.set_option('runtimepath', '') eq({}, meths.list_runtime_paths()) @@ -998,4 +998,43 @@ describe('api', function() it, _check_parsing, hl, fmtn) end) + describe('nvim_list_uis', function() + it('returns empty if --headless', function() + -- --embed implies --headless. + eq({}, nvim("list_uis")) + end) + it('returns attached UIs', function() + local screen = Screen.new(20, 4) + screen:attach() + local expected = { + { + ext_cmdline = false, + ext_popupmenu = false, + ext_tabline = false, + ext_wildmenu = false, + height = 4, + rgb = true, + width = 20, + } + } + eq(expected, nvim("list_uis")) + + screen:detach() + screen = Screen.new(44, 99) + screen:attach({ rgb = false }) + expected = { + { + ext_cmdline = false, + ext_popupmenu = false, + ext_tabline = false, + ext_wildmenu = false, + height = 99, + rgb = false, + width = 44, + } + } + eq(expected, nvim("list_uis")) + end) + end) + end) -- cgit From cee9a8ce8a0a6bfdadec7f95466bec73a8cc0bdc Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Tue, 27 Feb 2018 10:02:04 +0100 Subject: message: don't output unprintable chars to screen fixes #7586 #8070 --- test/functional/ui/input_spec.lua | 90 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) (limited to 'test') diff --git a/test/functional/ui/input_spec.lua b/test/functional/ui/input_spec.lua index f8842901c1..7d5521485c 100644 --- a/test/functional/ui/input_spec.lua +++ b/test/functional/ui/input_spec.lua @@ -2,6 +2,7 @@ local helpers = require('test.functional.helpers')(after_each) local clear, feed_command, nvim = helpers.clear, helpers.feed_command, helpers.nvim local feed, next_message, eq = helpers.feed, helpers.next_message, helpers.eq local expect = helpers.expect +local write_file = helpers.write_file local Screen = require('test.functional.ui.screen') describe('mappings', function() @@ -124,3 +125,92 @@ describe('input utf sequences that contain CSI/K_SPECIAL', function() expect('…') end) end) + +describe('input non-printable chars', function() + it("doesn't crash when echoing them back", function() + write_file("Xtest-overwrite", [[foobar]]) + clear() + local screen = Screen.new(60,8) + screen:set_default_attr_ids({ + [1] = {bold = true, foreground = Screen.colors.Blue1}, + [2] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red}, + [3] = {bold = true, foreground = Screen.colors.SeaGreen4} + }) + screen:attach() + + feed_command("e Xtest-overwrite") + screen:expect([[ + ^foobar | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + "Xtest-overwrite" [noeol] 1L, 6C | + ]]) + + -- The timestamp is in second resolution, wait two seconds to be sure. + screen:sleep(2000) + write_file("Xtest-overwrite", [[smurf]]) + feed_command("w") + screen:expect([[ + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + "Xtest-overwrite" | + {2:WARNING: The file has been changed since reading it!!!} | + {3:Do you really want to write to it (y/n)?}^ | + ]]) + + feed("u") + screen:expect([[ + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + "Xtest-overwrite" | + {2:WARNING: The file has been changed since reading it!!!} | + {3:Do you really want to write to it (y/n)?}u | + {3:Do you really want to write to it (y/n)?}^ | + ]]) + + feed("\005") + screen:expect([[ + {1:~ }| + {1:~ }| + {1:~ }| + "Xtest-overwrite" | + {2:WARNING: The file has been changed since reading it!!!} | + {3:Do you really want to write to it (y/n)?}u | + {3:Do you really want to write to it (y/n)?} | + {3:Do you really want to write to it (y/n)?}^ | + ]]) + + feed("n") + screen:expect([[ + {1:~ }| + {1:~ }| + "Xtest-overwrite" | + {2:WARNING: The file has been changed since reading it!!!} | + {3:Do you really want to write to it (y/n)?}u | + {3:Do you really want to write to it (y/n)?} | + {3:Do you really want to write to it (y/n)?}n | + {3:Press ENTER or type command to continue}^ | + ]]) + + feed("") + screen:expect([[ + ^foobar | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]) + end) +end) -- cgit From 773f23e00d5a4af836a854702ffb9e69fcaf61ff Mon Sep 17 00:00:00 2001 From: b-r-o-c-k Date: Sun, 4 Mar 2018 17:44:23 -0600 Subject: build/msvc: Make shell-test work MSVC doesn't have unistd.h or usleep() so it was replaced with the Sleep() WinAPI function. --- test/functional/fixtures/shell-test.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'test') diff --git a/test/functional/fixtures/shell-test.c b/test/functional/fixtures/shell-test.c index 38695ce76b..cc447d903f 100644 --- a/test/functional/fixtures/shell-test.c +++ b/test/functional/fixtures/shell-test.c @@ -4,7 +4,12 @@ #include #include #include +#ifdef WIN32 +#include +#define usleep(usecs) Sleep(usecs/1000) +#else #include +#endif static void wait(void) { -- cgit From c29a82c45f7be0f7ea46311e16a46a94f2173b98 Mon Sep 17 00:00:00 2001 From: b-r-o-c-k Date: Tue, 6 Mar 2018 20:38:10 -0600 Subject: build/msvc: Make shell-test fix only apply to MSCV --- test/functional/fixtures/shell-test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'test') diff --git a/test/functional/fixtures/shell-test.c b/test/functional/fixtures/shell-test.c index cc447d903f..a744d5df46 100644 --- a/test/functional/fixtures/shell-test.c +++ b/test/functional/fixtures/shell-test.c @@ -4,7 +4,7 @@ #include #include #include -#ifdef WIN32 +#ifdef _MSC_VER #include #define usleep(usecs) Sleep(usecs/1000) #else -- cgit From 2bf08691609c763cc8a87e38ba24596ff8622a79 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Thu, 8 Mar 2018 20:28:51 +0100 Subject: test: handle non-deterministic message cadence --- test/functional/core/job_spec.lua | 44 ++++++++++++++++++++++++++++++++------- 1 file changed, 37 insertions(+), 7 deletions(-) (limited to 'test') diff --git a/test/functional/core/job_spec.lua b/test/functional/core/job_spec.lua index e3c93d4b5a..e260069ea7 100644 --- a/test/functional/core/job_spec.lua +++ b/test/functional/core/job_spec.lua @@ -292,8 +292,16 @@ describe('jobs', function() nvim('command', 'let g:job_opts.user = {"n": 5, "s": "str", "l": [1]}') nvim('command', [[call jobstart('echo "foo"', g:job_opts)]]) local data = {n = 5, s = 'str', l = {1}} - eq({'notification', 'stdout', {data, {'foo', ''}}}, next_msg()) - eq({'notification', 'stdout', {data, {''}}}, next_msg()) + expect_msg_seq( + { {'notification', 'stdout', {data, {'foo', ''}}}, + {'notification', 'stdout', {data, {''}}}, + }, + -- Alternative sequence: + { {'notification', 'stdout', {data, {'foo'}}}, + {'notification', 'stdout', {data, {'', ''}}}, + {'notification', 'stdout', {data, {''}}}, + } + ) eq({'notification', 'exit', {data, 0}}, next_msg()) end) @@ -310,11 +318,12 @@ describe('jobs', function() nvim('command', [[call jobstart('echo "foo"', g:job_opts)]]) expect_msg_seq( { {'notification', 'stdout', {5, {'foo', ''} } }, - {'notification', 'stdout', {5, {''} } } + {'notification', 'stdout', {5, {''} } }, }, -- Alternative sequence: { {'notification', 'stdout', {5, {'foo'} } }, - {'notification', 'stdout', {5, {'', ''} } } + {'notification', 'stdout', {5, {'', ''} } }, + {'notification', 'stdout', {5, {''} } }, } ) end) @@ -417,7 +426,14 @@ describe('jobs', function() let g:job_opts = {'on_stdout': Callback} call jobstart('echo "some text"', g:job_opts) ]]) - eq({'notification', '1', {'foo', 'bar', {'some text', ''}, 'stdout'}}, next_msg()) + expect_msg_seq( + { {'notification', '1', {'foo', 'bar', {'some text', ''}, 'stdout'}}, + }, + -- Alternative sequence: + { {'notification', '1', {'foo', 'bar', {'some text'}, 'stdout'}}, + {'notification', '1', {'foo', 'bar', {'', ''}, 'stdout'}}, + } + ) end) it('jobstart() works with closures', function() @@ -430,7 +446,14 @@ describe('jobs', function() let g:job_opts = {'on_stdout': MkFun()} call jobstart('echo "some text"', g:job_opts) ]]) - eq({'notification', '1', {'foo', 'bar', {'some text', ''}, 'stdout'}}, next_msg()) + expect_msg_seq( + { {'notification', '1', {'foo', 'bar', {'some text', ''}, 'stdout'}}, + }, + -- Alternative sequence: + { {'notification', '1', {'foo', 'bar', {'some text'}, 'stdout'}}, + {'notification', '1', {'foo', 'bar', {'', ''}, 'stdout'}}, + } + ) end) it('jobstart() works when closure passed directly to `jobstart`', function() @@ -438,7 +461,14 @@ describe('jobs', function() let g:job_opts = {'on_stdout': {id, data, event -> rpcnotify(g:channel, '1', 'foo', 'bar', Normalize(data), event)}} call jobstart('echo "some text"', g:job_opts) ]]) - eq({'notification', '1', {'foo', 'bar', {'some text', ''}, 'stdout'}}, next_msg()) + expect_msg_seq( + { {'notification', '1', {'foo', 'bar', {'some text', ''}, 'stdout'}}, + }, + -- Alternative sequence: + { {'notification', '1', {'foo', 'bar', {'some text'}, 'stdout'}}, + {'notification', '1', {'foo', 'bar', {'', ''}, 'stdout'}}, + } + ) end) describe('jobwait', function() -- cgit From fd4021387e696c7a2da4ad776789cfbb938d5332 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Fri, 9 Mar 2018 00:24:38 +0100 Subject: test: rename next_message() to next_msg() --- test/functional/api/server_notifications_spec.lua | 16 ++++++++-------- test/functional/api/server_requests_spec.lua | 10 +++++----- test/functional/autocmd/cmdline_spec.lua | 2 +- test/functional/core/channels_spec.lua | 2 +- test/functional/core/job_spec.lua | 2 +- test/functional/ex_cmds/dict_notifications_spec.lua | 2 +- test/functional/helpers.lua | 10 +++++----- test/functional/ui/input_spec.lua | 4 ++-- 8 files changed, 24 insertions(+), 24 deletions(-) (limited to 'test') diff --git a/test/functional/api/server_notifications_spec.lua b/test/functional/api/server_notifications_spec.lua index 9d7cfb9b78..1d64ae7103 100644 --- a/test/functional/api/server_notifications_spec.lua +++ b/test/functional/api/server_notifications_spec.lua @@ -1,7 +1,7 @@ local helpers = require('test.functional.helpers')(after_each) -local eq, clear, eval, command, nvim, next_message = +local eq, clear, eval, command, nvim, next_msg = helpers.eq, helpers.clear, helpers.eval, helpers.command, helpers.nvim, - helpers.next_message + helpers.next_msg local meths = helpers.meths describe('notify', function() @@ -15,10 +15,10 @@ describe('notify', function() describe('passing a valid channel id', function() it('sends the notification/args to the corresponding channel', function() eval('rpcnotify('..channel..', "test-event", 1, 2, 3)') - eq({'notification', 'test-event', {1, 2, 3}}, next_message()) + eq({'notification', 'test-event', {1, 2, 3}}, next_msg()) command('au FileType lua call rpcnotify('..channel..', "lua!")') command('set filetype=lua') - eq({'notification', 'lua!', {}}, next_message()) + eq({'notification', 'lua!', {}}, next_msg()) end) end) @@ -28,13 +28,13 @@ describe('notify', function() eval('rpcnotify(0, "event1", 1, 2, 3)') eval('rpcnotify(0, "event2", 4, 5, 6)') eval('rpcnotify(0, "event2", 7, 8, 9)') - eq({'notification', 'event2', {4, 5, 6}}, next_message()) - eq({'notification', 'event2', {7, 8, 9}}, next_message()) + eq({'notification', 'event2', {4, 5, 6}}, next_msg()) + eq({'notification', 'event2', {7, 8, 9}}, next_msg()) nvim('unsubscribe', 'event2') nvim('subscribe', 'event1') eval('rpcnotify(0, "event2", 10, 11, 12)') eval('rpcnotify(0, "event1", 13, 14, 15)') - eq({'notification', 'event1', {13, 14, 15}}, next_message()) + eq({'notification', 'event1', {13, 14, 15}}, next_msg()) end) it('does not crash for deeply nested variable', function() @@ -42,7 +42,7 @@ describe('notify', function() local nest_level = 1000 meths.command(('call map(range(%u), "extend(g:, {\'l\': [g:l]})")'):format(nest_level - 1)) eval('rpcnotify('..channel..', "event", g:l)') - local msg = next_message() + local msg = next_msg() eq('notification', msg[1]) eq('event', msg[2]) local act_ret = msg[3] diff --git a/test/functional/api/server_requests_spec.lua b/test/functional/api/server_requests_spec.lua index 37ac532d18..18229b54ff 100644 --- a/test/functional/api/server_requests_spec.lua +++ b/test/functional/api/server_requests_spec.lua @@ -6,7 +6,7 @@ local Paths = require('test.config.paths') local clear, nvim, eval = helpers.clear, helpers.nvim, helpers.eval local eq, neq, run, stop = helpers.eq, helpers.neq, helpers.run, helpers.stop local nvim_prog, command, funcs = helpers.nvim_prog, helpers.command, helpers.funcs -local source, next_message = helpers.source, helpers.next_message +local source, next_msg = helpers.source, helpers.next_msg local ok = helpers.ok local meths = helpers.meths local spawn, nvim_argv = helpers.spawn, helpers.nvim_argv @@ -258,12 +258,12 @@ describe('server -> client', function() it('rpc and text stderr can be combined', function() eq("ok",funcs.rpcrequest(jobid, "poll")) funcs.rpcnotify(jobid, "ping") - eq({'notification', 'pong', {}}, next_message()) + eq({'notification', 'pong', {}}, next_msg()) eq("done!",funcs.rpcrequest(jobid, "write_stderr", "fluff\n")) - eq({'notification', 'stderr', {0, {'fluff', ''}}}, next_message()) + eq({'notification', 'stderr', {0, {'fluff', ''}}}, next_msg()) funcs.rpcrequest(jobid, "exit") - eq({'notification', 'stderr', {0, {''}}}, next_message()) - eq({'notification', 'exit', {0, 0}}, next_message()) + eq({'notification', 'stderr', {0, {''}}}, next_msg()) + eq({'notification', 'exit', {0, 0}}, next_msg()) end) end) diff --git a/test/functional/autocmd/cmdline_spec.lua b/test/functional/autocmd/cmdline_spec.lua index 8d56687f7d..8ea086fb46 100644 --- a/test/functional/autocmd/cmdline_spec.lua +++ b/test/functional/autocmd/cmdline_spec.lua @@ -5,7 +5,7 @@ local clear = helpers.clear local command = helpers.command local eq = helpers.eq local expect = helpers.expect -local next_msg = helpers.next_message +local next_msg = helpers.next_msg local feed = helpers.feed local meths = helpers.meths diff --git a/test/functional/core/channels_spec.lua b/test/functional/core/channels_spec.lua index e9fc88c01b..f79f208b69 100644 --- a/test/functional/core/channels_spec.lua +++ b/test/functional/core/channels_spec.lua @@ -1,7 +1,7 @@ local helpers = require('test.functional.helpers')(after_each) local clear, eq, eval, next_msg, ok, source = helpers.clear, helpers.eq, - helpers.eval, helpers.next_message, helpers.ok, helpers.source + helpers.eval, helpers.next_msg, helpers.ok, helpers.source local command, funcs, meths = helpers.command, helpers.funcs, helpers.meths local sleep = helpers.sleep local spawn, nvim_argv = helpers.spawn, helpers.nvim_argv diff --git a/test/functional/core/job_spec.lua b/test/functional/core/job_spec.lua index e260069ea7..c5326aedfe 100644 --- a/test/functional/core/job_spec.lua +++ b/test/functional/core/job_spec.lua @@ -2,7 +2,7 @@ local helpers = require('test.functional.helpers')(after_each) local clear, eq, eval, exc_exec, feed_command, feed, insert, neq, next_msg, nvim, nvim_dir, ok, source, write_file, mkdir, rmdir = helpers.clear, helpers.eq, helpers.eval, helpers.exc_exec, helpers.feed_command, helpers.feed, - helpers.insert, helpers.neq, helpers.next_message, helpers.nvim, + helpers.insert, helpers.neq, helpers.next_msg, helpers.nvim, helpers.nvim_dir, helpers.ok, helpers.source, helpers.write_file, helpers.mkdir, helpers.rmdir local command = helpers.command diff --git a/test/functional/ex_cmds/dict_notifications_spec.lua b/test/functional/ex_cmds/dict_notifications_spec.lua index e3b4a1c504..3d550588e7 100644 --- a/test/functional/ex_cmds/dict_notifications_spec.lua +++ b/test/functional/ex_cmds/dict_notifications_spec.lua @@ -1,6 +1,6 @@ local helpers = require('test.functional.helpers')(after_each) local clear, nvim, source = helpers.clear, helpers.nvim, helpers.source -local eq, next_msg = helpers.eq, helpers.next_message +local eq, next_msg = helpers.eq, helpers.next_msg local exc_exec = helpers.exc_exec local command = helpers.command local eval = helpers.eval diff --git a/test/functional/helpers.lua b/test/functional/helpers.lua index 1f53200dd8..72b086c574 100644 --- a/test/functional/helpers.lua +++ b/test/functional/helpers.lua @@ -97,14 +97,14 @@ local function request(method, ...) return rv end -local function next_message(timeout) +local function next_msg(timeout) return session:next_message(timeout) end local function expect_twostreams(msgs1, msgs2) local pos1, pos2 = 1, 1 while pos1 <= #msgs1 or pos2 <= #msgs2 do - local msg = next_message() + local msg = next_msg() if pos1 <= #msgs1 and pcall(eq, msgs1[pos1], msg) then pos1 = pos1 + 1 elseif pos2 <= #msgs2 then @@ -117,7 +117,7 @@ local function expect_twostreams(msgs1, msgs2) end end --- Expects a sequence of next_message() results. If multiple sequences are +-- Expects a sequence of next_msg() results. If multiple sequences are -- passed they are tried until one succeeds, in order of shortest to longest. local function expect_msg_seq(...) if select('#', ...) < 1 then @@ -140,7 +140,7 @@ local function expect_msg_seq(...) local expected_seq = seqs[anum] -- Collect enough messages to compare the next expected sequence. while #actual_seq < #expected_seq do - local msg = next_message(10000) -- Big timeout for ASAN/valgrind. + local msg = next_msg(10000) -- Big timeout for ASAN/valgrind. if msg == nil then error(cat_err(final_error, string.format('got %d messages, expected %d', @@ -754,7 +754,7 @@ local module = { mkdir = lfs.mkdir, neq = neq, new_pipename = new_pipename, - next_message = next_message, + next_msg = next_msg, nvim = nvim, nvim_argv = nvim_argv, nvim_async = nvim_async, diff --git a/test/functional/ui/input_spec.lua b/test/functional/ui/input_spec.lua index 7d5521485c..8e62a37ef1 100644 --- a/test/functional/ui/input_spec.lua +++ b/test/functional/ui/input_spec.lua @@ -1,6 +1,6 @@ local helpers = require('test.functional.helpers')(after_each) local clear, feed_command, nvim = helpers.clear, helpers.feed_command, helpers.nvim -local feed, next_message, eq = helpers.feed, helpers.next_message, helpers.eq +local feed, next_msg, eq = helpers.feed, helpers.next_msg, helpers.eq local expect = helpers.expect local write_file = helpers.write_file local Screen = require('test.functional.ui.screen') @@ -16,7 +16,7 @@ describe('mappings', function() local check_mapping = function(mapping, expected) feed(mapping) - eq({'notification', 'mapped', {expected}}, next_message()) + eq({'notification', 'mapped', {expected}}, next_msg()) end before_each(function() -- cgit From 496b0f944fcbfd84db9e2dd625c6b756a3d1e467 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Fri, 9 Mar 2018 00:29:20 +0100 Subject: test: next_msg(): default `timeout` to 10s Infinite timeout results in hangs which waste time. If some test needs longer than 10s to wait for a message, it should specify the timeout explicitly. --- test/functional/helpers.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'test') diff --git a/test/functional/helpers.lua b/test/functional/helpers.lua index 72b086c574..b8d912114d 100644 --- a/test/functional/helpers.lua +++ b/test/functional/helpers.lua @@ -98,7 +98,7 @@ local function request(method, ...) end local function next_msg(timeout) - return session:next_message(timeout) + return session:next_message(timeout and timeout or 10000) end local function expect_twostreams(msgs1, msgs2) -- cgit From cca407b43e62d5e1abdfc59ae47b0e24eacc5807 Mon Sep 17 00:00:00 2001 From: Marco Hinz Date: Fri, 16 Mar 2018 07:29:20 +0100 Subject: DirChanged: support (#8140) --- test/functional/autocmd/dirchanged_spec.lua | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'test') diff --git a/test/functional/autocmd/dirchanged_spec.lua b/test/functional/autocmd/dirchanged_spec.lua index 63cf0bc410..7979e91a4c 100644 --- a/test/functional/autocmd/dirchanged_spec.lua +++ b/test/functional/autocmd/dirchanged_spec.lua @@ -154,4 +154,11 @@ describe('autocmd DirChanged', function() eq('Failed to change directory', string.match(err, ': (.*)')) eq({cwd=dirs[2], scope='global'}, eval('g:ev')) end) + + it('works when local to buffer', function() + command('let g:triggered = 0') + command('autocmd DirChanged let g:triggered = 1') + command('cd '..dirs[1]) + eq(1, eval('g:triggered')) + end) end) -- cgit From de475154770ef27374747b5401ef9fcbbe5f9a81 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 6 Mar 2018 05:22:48 +0100 Subject: test: use luv.now() instead of os.time() --- test/functional/autocmd/termclose_spec.lua | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) (limited to 'test') diff --git a/test/functional/autocmd/termclose_spec.lua b/test/functional/autocmd/termclose_spec.lua index 9918cbe4fa..0804579a4f 100644 --- a/test/functional/autocmd/termclose_spec.lua +++ b/test/functional/autocmd/termclose_spec.lua @@ -1,3 +1,4 @@ +local luv = require('luv') local helpers = require('test.functional.helpers')(after_each) local clear, command, nvim, nvim_dir = @@ -39,13 +40,15 @@ describe('TermClose event', function() .. [[ 'on_exit': {-> execute('let g:test_job_exited = 1')}}) ]]) retry(nil, nil, function() eq(1, eval('get(g:, "test_job_started", 0)')) end) - local start = os.time() + luv.update_time() + local start = luv.now() command('call jobstop(g:test_job)') retry(nil, nil, function() eq(1, eval('get(g:, "test_job_exited", 0)')) end) - local duration = os.time() - start - -- nvim starts sending SIGTERM after KILL_TIMEOUT_MS - ok(duration >= 2) - ok(duration <= 4) -- <= 2 + delta because of slow CI + luv.update_time() + local duration = luv.now() - start + -- Nvim begins SIGTERM after KILL_TIMEOUT_MS. + ok(duration >= 2000) + ok(duration <= 4000) -- Epsilon for slow CI end) it('kills pty job trapping SIGHUP and SIGTERM', function() @@ -58,13 +61,15 @@ describe('TermClose event', function() .. [[ 'on_exit': {-> execute('let g:test_job_exited = 1')}}) ]]) retry(nil, nil, function() eq(1, eval('get(g:, "test_job_started", 0)')) end) - local start = os.time() + luv.update_time() + local start = luv.now() command('call jobstop(g:test_job)') retry(nil, nil, function() eq(1, eval('get(g:, "test_job_exited", 0)')) end) - local duration = os.time() - start - -- nvim starts sending kill after 2*KILL_TIMEOUT_MS - ok(duration >= 4) - ok(duration <= 7) -- <= 4 + delta because of slow CI + luv.update_time() + local duration = luv.now() - start + -- Nvim begins SIGKILL after (2 * KILL_TIMEOUT_MS). + ok(duration >= 4000) + ok(duration <= 7000) -- Epsilon for slow CI end) it('reports the correct ', function() -- cgit From dbad797edd4636f830abd7ade1138a1a27ac30d2 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sun, 11 Mar 2018 21:47:12 +0100 Subject: API: nvim_get_proc_children() ref https://github.com/libuv/libuv/pull/836 --- test/functional/api/proc_spec.lua | 53 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 test/functional/api/proc_spec.lua (limited to 'test') diff --git a/test/functional/api/proc_spec.lua b/test/functional/api/proc_spec.lua new file mode 100644 index 0000000000..eee54f9465 --- /dev/null +++ b/test/functional/api/proc_spec.lua @@ -0,0 +1,53 @@ +local helpers = require('test.functional.helpers')(after_each) + +local clear, eq = helpers.clear, helpers.eq +local funcs = helpers.funcs +local nvim_argv = helpers.nvim_argv +local request = helpers.request +local retry = helpers.retry + +describe('api', function() + before_each(clear) + + describe('nvim_get_proc_children', function() + it('returns child process ids', function() + local this_pid = funcs.getpid() + + local job1 = funcs.jobstart(nvim_argv) + retry(nil, nil, function() + eq(1, #request('nvim_get_proc_children', this_pid)) + end) + + local job2 = funcs.jobstart(nvim_argv) + retry(nil, nil, function() + eq(2, #request('nvim_get_proc_children', this_pid)) + end) + + funcs.jobstop(job1) + retry(nil, nil, function() + eq(1, #request('nvim_get_proc_children', this_pid)) + end) + + funcs.jobstop(job2) + retry(nil, nil, function() + eq(0, #request('nvim_get_proc_children', this_pid)) + end) + end) + + it('validates input', function() + local status, rv = pcall(request, "nvim_get_proc_children", -1) + eq(false, status) + eq("Invalid pid: -1", string.match(rv, "Invalid.*")) + + status, rv = pcall(request, "nvim_get_proc_children", 0) + eq(false, status) + eq("Invalid pid: 0", string.match(rv, "Invalid.*")) + + -- Assume PID 99999999 does not exist. + status, rv = pcall(request, "nvim_get_proc_children", 99999999) + eq(true, status) + eq({}, rv) + end) + end) + +end) -- cgit From a034d4b69d6032b3431c10b8a11c998551700fc2 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Fri, 16 Mar 2018 05:13:38 +0100 Subject: API: nvim_get_proc() TODO: "exepath" field (win32: QueryFullProcessImageName()) On unix-likes `ps` is used because the platform-specific APIs are a nightmare. For reference, below is a (incomplete) attempt: diff --git a/src/nvim/os/process.c b/src/nvim/os/process.c index 09769925aca5..99afbbf290c1 100644 --- a/src/nvim/os/process.c +++ b/src/nvim/os/process.c @@ -208,3 +210,60 @@ int os_proc_children(int ppid, int **proc_list, size_t *proc_count) return 0; } +/// Gets various properties of the process identified by `pid`. +/// +/// @param pid Process to inspect. +/// @return Map of process properties, empty on error. +Dictionary os_proc_info(int pid) +{ + Dictionary pinfo = ARRAY_DICT_INIT; +#ifdef WIN32 + +#elif defined(__APPLE__) + char buf[PROC_PIDPATHINFO_MAXSIZE]; + if (proc_pidpath(pid, buf, sizeof(buf))) { + name = getName(buf); + PUT(pinfo, "exepath", STRING_OBJ(cstr_to_string(buf))); + return name; + } else { + ILOG("proc_pidpath() failed for pid: %d", pid); + } +#elif defined(BSD) +# if defined(__FreeBSD__) +# define KP_COMM(o) o.ki_comm +# else +# define KP_COMM(o) o.p_comm +# endif + struct kinfo_proc *proc = kinfo_getproc(pid); + if (proc) { + PUT(pinfo, "name", cstr_to_string(KP_COMM(proc))); + xfree(proc); + } else { + ILOG("kinfo_getproc() failed for pid: %d", pid); + } + +#elif defined(__linux__) + char fname[256] = { 0 }; + char buf[MAXPATHL]; + snprintf(fname, sizeof(fname), "/proc/%d/comm", pid); + FILE *fp = fopen(fname, "r"); + // FileDescriptor *f = file_open_new(&error, fname, kFileReadOnly, 0); + // ptrdiff_t file_read(FileDescriptor *const fp, char *const ret_buf, + // const size_t size) + if (fp == NULL) { + ILOG("fopen() of /proc/%d/comm failed", pid); + } else { + size_t n = fread(buf, sizeof(char), sizeof(buf) - 1, fp); + if (n == 0) { + WLOG("fread() of /proc/%d/comm failed", pid); + } else { + size_t end = MIN(sizeof(buf) - 1, n); + end = (end > 0 && buf[end - 1] == '\n') ? end - 1 : end; + buf[end] = '\0'; + PUT(pinfo, "name", STRING_OBJ(cstr_to_string(buf))); + } + } + fclose(fp); +#endif + return pinfo; +} --- test/functional/api/proc_spec.lua | 34 +++++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) (limited to 'test') diff --git a/test/functional/api/proc_spec.lua b/test/functional/api/proc_spec.lua index eee54f9465..d99c26b6c2 100644 --- a/test/functional/api/proc_spec.lua +++ b/test/functional/api/proc_spec.lua @@ -1,10 +1,14 @@ local helpers = require('test.functional.helpers')(after_each) -local clear, eq = helpers.clear, helpers.eq +local clear = helpers.clear +local eq = helpers.eq local funcs = helpers.funcs +local iswin = helpers.iswin local nvim_argv = helpers.nvim_argv +local ok = helpers.ok local request = helpers.request local retry = helpers.retry +local NIL = helpers.NIL describe('api', function() before_each(clear) @@ -43,11 +47,35 @@ describe('api', function() eq(false, status) eq("Invalid pid: 0", string.match(rv, "Invalid.*")) - -- Assume PID 99999999 does not exist. - status, rv = pcall(request, "nvim_get_proc_children", 99999999) + -- Assume PID 99999 does not exist. + status, rv = pcall(request, "nvim_get_proc_children", 99999) eq(true, status) eq({}, rv) end) end) + describe('nvim_get_proc', function() + it('returns process info', function() + local pid = funcs.getpid() + local pinfo = request('nvim_get_proc', pid) + eq((iswin() and 'nvim.exe' or 'nvim'), pinfo.name) + ok(pinfo.pid == pid) + ok(type(pinfo.ppid) == 'number' and pinfo.ppid ~= pid) + end) + + it('validates input', function() + local status, rv = pcall(request, "nvim_get_proc", -1) + eq(false, status) + eq("Invalid pid: -1", string.match(rv, "Invalid.*")) + + status, rv = pcall(request, "nvim_get_proc", 0) + eq(false, status) + eq("Invalid pid: 0", string.match(rv, "Invalid.*")) + + -- Assume PID 99999 does not exist. + status, rv = pcall(request, "nvim_get_proc", 99999) + eq(true, status) + eq(NIL, rv) + end) + end) end) -- cgit From 167898a5177de6d2fa6ae553fc3305d399b453d3 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sun, 18 Mar 2018 00:09:28 +0100 Subject: test: jobstop() kills entire process tree Test correctly fails before 8d90171f8be6. ref #6530 --- test/functional/core/job_spec.lua | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) (limited to 'test') diff --git a/test/functional/core/job_spec.lua b/test/functional/core/job_spec.lua index c5326aedfe..aaa35f6c62 100644 --- a/test/functional/core/job_spec.lua +++ b/test/functional/core/job_spec.lua @@ -6,6 +6,10 @@ local clear, eq, eval, exc_exec, feed_command, feed, insert, neq, next_msg, nvim helpers.nvim_dir, helpers.ok, helpers.source, helpers.write_file, helpers.mkdir, helpers.rmdir local command = helpers.command +local funcs = helpers.funcs +local retry = helpers.retry +local meths = helpers.meths +local NIL = helpers.NIL local wait = helpers.wait local iswin = helpers.iswin local get_pathsep = helpers.get_pathsep @@ -636,6 +640,40 @@ describe('jobs', function() ok(string.find(err, "E475: Invalid argument: job cannot have both 'pty' and 'rpc' options set") ~= nil) end) + it('jobstop() kills entire process tree #6530', function() + -- XXX: Using `nvim` isn't a good test, it reaps its children on exit. + -- local c = 'call jobstart([v:progpath, "-u", "NONE", "-i", "NONE", "--headless"])' + -- local j = eval("jobstart([v:progpath, '-u', 'NONE', '-i', 'NONE', '--headless', '-c', '" + -- ..c.."', '-c', '"..c.."'])") + + -- Create child with several descendants. + local j = (iswin() + and eval([=[jobstart('start /b cmd /c "ping 127.0.0.1 -n 1 -w 30000 > NUL"]=] + ..[=[ & start /b cmd /c "ping 127.0.0.1 -n 1 -w 40000 > NUL"]=] + ..[=[ & start /b cmd /c "ping 127.0.0.1 -n 1 -w 50000 > NUL"')]=]) + or eval("jobstart('sleep 30 | sleep 30 | sleep 30')")) + local ppid = funcs.jobpid(j) + local children + retry(nil, nil, function() + children = meths.get_proc_children(ppid) + eq(3, #children) + end) + -- Assert that nvim_get_proc() sees the children. + for _, child_pid in ipairs(children) do + local info = meths.get_proc(child_pid) + -- eq((iswin() and 'nvim.exe' or 'nvim'), info.name) + eq(ppid, info.ppid) + end + -- Kill the root of the tree. + funcs.jobstop(j) + -- Assert that the children were killed. + retry(nil, nil, function() + for _, child_pid in ipairs(children) do + eq(NIL, meths.get_proc(child_pid)) + end + end) + end) + describe('running tty-test program', function() if helpers.pending_win32(pending) then return end local function next_chunk() -- cgit From ae409b5042abdbec67305a063cf921ddffcce0c8 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sun, 18 Mar 2018 17:15:06 +0100 Subject: test/win: use cmd.exe for test Can revert this after #8120. --- test/functional/core/job_spec.lua | 2 ++ 1 file changed, 2 insertions(+) (limited to 'test') diff --git a/test/functional/core/job_spec.lua b/test/functional/core/job_spec.lua index aaa35f6c62..6d4cadbdc8 100644 --- a/test/functional/core/job_spec.lua +++ b/test/functional/core/job_spec.lua @@ -641,6 +641,8 @@ describe('jobs', function() end) it('jobstop() kills entire process tree #6530', function() + command('set shell& shellcmdflag& shellquote& shellpipe& shellredir& shellxquote&') + -- XXX: Using `nvim` isn't a good test, it reaps its children on exit. -- local c = 'call jobstart([v:progpath, "-u", "NONE", "-i", "NONE", "--headless"])' -- local j = eval("jobstart([v:progpath, '-u', 'NONE', '-i', 'NONE', '--headless', '-c', '" -- cgit From d407a48665d8f8e1e42eb1060ea245d979419605 Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Mon, 14 Mar 2016 23:58:47 +0100 Subject: getchar: implement key to invoke command in any mode --- test/functional/ex_cmds/cmd_map_spec.lua | 771 +++++++++++++++++++++++++++++++ 1 file changed, 771 insertions(+) create mode 100644 test/functional/ex_cmds/cmd_map_spec.lua (limited to 'test') diff --git a/test/functional/ex_cmds/cmd_map_spec.lua b/test/functional/ex_cmds/cmd_map_spec.lua new file mode 100644 index 0000000000..5b7f0942b1 --- /dev/null +++ b/test/functional/ex_cmds/cmd_map_spec.lua @@ -0,0 +1,771 @@ +local helpers = require('test.functional.helpers')(after_each) +local clear = helpers.clear +local feed_command = helpers.feed_command +local feed = helpers.feed +local eq = helpers.eq +local expect = helpers.expect +local eval = helpers.eval +local funcs = helpers.funcs +local insert = helpers.insert +local exc_exec = helpers.exc_exec +local Screen = require('test.functional.ui.screen') + +describe('mappings with ', function() + local screen + local function cmdmap(lhs, rhs) + feed_command('noremap '..lhs..' '..rhs..'') + feed_command('noremap! '..lhs..' '..rhs..'') + end + + before_each(function() + clear() + screen = Screen.new(65, 8) + screen:set_default_attr_ids({ + [1] = {bold = true, foreground = Screen.colors.Blue1}, + [2] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red}, + [3] = {bold = true, foreground = Screen.colors.SeaGreen4}, + [4] = {bold = true}, + [5] = {background = Screen.colors.LightGrey}, + [6] = {foreground = Screen.colors.Blue1}, + }) + screen:attach() + + cmdmap('', 'let m = mode(1)') + cmdmap('', 'normal! ww') + cmdmap('', 'normal! "ay') + cmdmap('', 'throw "very error"') + feed_command([[ + function! TextObj() + if mode() !=# "v" + normal! v + end + call cursor(1,3) + normal! o + call cursor(2,4) + endfunction]]) + cmdmap('', 'call TextObj()') + insert([[ + some short lines + of test text]]) + feed('gg') + cmdmap('', 'startinsert') + cmdmap('', 'stopinsert') + feed_command("abbr foo let g:y = 17bar") + end) + + it('can be displayed', function() + feed_command('map ') + screen:expect([[ + ^some short lines | + of test text | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {6:} {6:*} {6:}let m = mode(1){6:} | + ]]) + end) + + it('handles invalid mappings', function() + feed_command('let x = 0') + feed_command('noremap let x = 1') + feed('') + screen:expect([[ + ^some short lines | + of test text | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {2:E5521: mapping must end with before second } | + ]]) + + feed_command('noremap let x = 2') + feed('') + screen:expect([[ + ^some short lines | + of test text | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {2:E5522: mapping must not include key} | + ]]) + + feed_command('noremap let x = 3') + feed('') + screen:expect([[ + ^some short lines | + of test text | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {2:E5520: mapping must end with } | + ]]) + eq(0, eval('x')) + end) + + it('works in various modes and sees correct `mode()` value', function() + -- normal mode + feed('') + eq('n', eval('m')) + + -- visual mode + feed('v') + eq('v', eval('m')) + -- didn't leave visual mode + eq('v', eval('mode(1)')) + feed('') + eq('n', eval('mode(1)')) + + -- visual mapping in select mode + feed('gh') + eq('v', eval('m')) + -- didn't leave select mode + eq('s', eval('mode(1)')) + feed('') + eq('n', eval('mode(1)')) + + -- select mode mapping + feed_command('snoremap let m = mode(1)') + feed('gh') + eq('s', eval('m')) + -- didn't leave select mode + eq('s', eval('mode(1)')) + feed('') + eq('n', eval('mode(1)')) + + -- operator-pending mode + feed("d") + eq('no', eval('m')) + -- did leave operator-pending mode + eq('n', eval('mode(1)')) + + --insert mode + feed('i') + eq('i', eval('m')) + eq('i', eval('mode(1)')) + + -- replace mode + feed("") + eq('R', eval('m')) + eq('R', eval('mode(1)')) + feed('') + eq('n', eval('mode(1)')) + + -- virtual replace mode + feed("gR") + eq('Rv', eval('m')) + eq('Rv', eval('mode(1)')) + feed('') + eq('n', eval('mode(1)')) + + -- langmap works, but is not distinguished in mode(1) + feed(":set iminsert=1i") + eq('i', eval('m')) + eq('i', eval('mode(1)')) + feed('') + eq('n', eval('mode(1)')) + + feed(':') + eq('c', eval('m')) + eq('c', eval('mode(1)')) + feed('') + eq('n', eval('mode(1)')) + + -- terminal mode + feed_command('tnoremap let m = mode(1)') + feed_command('split | terminal') + feed('i') + eq('t', eval('mode(1)')) + feed('') + eq('t', eval('m')) + eq('t', eval('mode(1)')) + end) + + it('works in normal mode', function() + cmdmap('', 'let s = [mode(1), v:count, v:register]') + + -- check v:count and v:register works + feed('') + eq({'n', 0, '"'}, eval('s')) + feed('7') + eq({'n', 7, '"'}, eval('s')) + feed('"e') + eq({'n', 0, 'e'}, eval('s')) + feed('5"k') + eq({'n', 5, 'k'}, eval('s')) + feed('"+2') + eq({'n', 2, '+'}, eval('s')) + + -- text object enters visual mode + feed('') + screen:expect([[ + so{5:me short lines} | + {5:of }^test text | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {4:-- VISUAL --} | + ]]) + feed('') + + -- startinsert + feed('') + eq('i', eval('mode(1)')) + feed('') + + eq('n', eval('mode(1)')) + cmdmap(',a', 'call feedkeys("aalpha") \\| let g:a = getline(2)') + cmdmap(',b', 'call feedkeys("abeta", "x") \\| let g:b = getline(2)') + + feed(',a') + screen:expect([[ + some short lines | + of alpha^test text | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {4:-- INSERT --} | + ]]) + -- feedkeys were not executed immediately + eq({'n', 'of test text'}, eval('[m,a]')) + eq('i', eval('mode(1)')) + feed('') + + feed(',b') + screen:expect([[ + some short lines | + of alphabet^atest text | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]) + -- feedkeys(..., 'x') was executed immediately, but insert mode gets aborted + eq({'n', 'of alphabetatest text'}, eval('[m,b]')) + eq('n', eval('mode(1)')) + end) + + it('works in :normal command', function() + feed_command('noremap ,x call append(1, "xx")\\| call append(1, "aa")') + feed_command('noremap ,f nosuchcommand') + feed_command('noremap ,e throw "very error"\\| call append(1, "yy")') + feed_command('noremap ,m echoerr "The message."\\| call append(1, "zz")') + feed_command('noremap ,w for i in range(5)\\|if i==1\\|echoerr "Err"\\|endif\\|call append(1, i)\\|endfor') + + feed(":normal ,x") + screen:expect([[ + ^some short lines | + aa | + xx | + of test text | + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]) + + eq('Vim:E492: Not an editor command: nosuchcommand', exc_exec("normal ,f")) + eq('very error', exc_exec("normal ,e")) + eq('Vim(echoerr):The message.', exc_exec("normal ,m")) + feed('w') + screen:expect([[ + some ^short lines | + aa | + xx | + of test text | + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]) + + feed_command(':%d') + eq('Vim(echoerr):Err', exc_exec("normal ,w")) + screen:expect([[ + ^ | + 0 | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + --No lines in buffer-- | + ]]) + + feed_command(':%d') + feed_command(':normal ,w') + screen:expect([[ + ^ | + 4 | + 3 | + 2 | + 1 | + 0 | + {1:~ }| + {2:Err} | + ]]) + end) + + it('works in visual mode', function() + -- can extend visual mode + feed('v') + screen:expect([[ + {5:some short }^lines | + of test text | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {4:-- VISUAL --} | + ]]) + eq('v', funcs.mode(1)) + + -- can invoke operator, ending visual mode + feed('') + eq('n', funcs.mode(1)) + eq({'some short l'}, funcs.getreg('a',1,1)) + + -- error doesn't interrupt visual mode + feed('ggvw') + screen:expect([[ + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {2:Error detected while processing :} | + {2:E605: Exception not caught: very error} | + {3:Press ENTER or type command to continue}^ | + ]]) + feed('') + eq('E605: Exception not caught: very error', eval('v:errmsg')) + -- still in visual mode, was consumed by the error prompt + screen:expect([[ + {5:some }^short lines | + of test text | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {4:-- VISUAL --} | + ]]) + eq('v', funcs.mode(1)) + feed('') + screen:expect([[ + so{5:me short lines} | + {5:of }^test text | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {4:-- VISUAL --} | + ]]) + eq('v', funcs.mode(1)) + + -- startinsert gives "-- (insert) VISUAL --" mode + feed('') + screen:expect([[ + so{5:me short lines} | + {5:of }^test text | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {4:-- (insert) VISUAL --} | + ]]) + eq('v', eval('mode(1)')) + feed('') + eq('i', eval('mode(1)')) + end) + + it('works in select mode', function() + feed_command('snoremap throw "very error"') + feed_command('snoremap normal! "by') + -- can extend select mode + feed('gh') + screen:expect([[ + {5:some short }^lines | + of test text | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {4:-- SELECT --} | + ]]) + eq('s', funcs.mode(1)) + + -- visual mapping in select mode restart selct mode after operator + feed('') + eq('s', funcs.mode(1)) + eq({'some short l'}, funcs.getreg('a',1,1)) + + -- select mode mapping works, and does not restart select mode + feed('') + eq('n', funcs.mode(1)) + eq({'some short l'}, funcs.getreg('b',1,1)) + + -- error doesn't interrupt temporary visual mode + feed('ggvw') + screen:expect([[ + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {2:Error detected while processing :} | + {2:E605: Exception not caught: very error} | + {3:Press ENTER or type command to continue}^ | + ]]) + feed('') + eq('E605: Exception not caught: very error', eval('v:errmsg')) + -- still in visual mode, was consumed by the error prompt + screen:expect([[ + {5:some }^short lines | + of test text | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {4:-- VISUAL --} | + ]]) + -- quirk: restoration of select mode is not performed + eq('v', funcs.mode(1)) + + -- error doesn't interrupt select mode + feed('ggvw') + screen:expect([[ + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {2:Error detected while processing :} | + {2:E605: Exception not caught: very error} | + {3:Press ENTER or type command to continue}^ | + ]]) + feed('') + eq('E605: Exception not caught: very error', eval('v:errmsg')) + -- still in select mode, was consumed by the error prompt + screen:expect([[ + {5:some }^short lines | + of test text | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {4:-- SELECT --} | + ]]) + -- quirk: restoration of select mode is not performed + eq('s', funcs.mode(1)) + + feed('') + screen:expect([[ + so{5:me short lines} | + {5:of }^test text | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {4:-- SELECT --} | + ]]) + eq('s', funcs.mode(1)) + + -- startinsert gives "-- SELECT (insert) --" mode + feed('') + screen:expect([[ + so{5:me short lines} | + {5:of }^test text | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {4:-- (insert) SELECT --} | + ]]) + eq('s', eval('mode(1)')) + feed('') + eq('i', eval('mode(1)')) + end) + + + it('works in operator-pending mode', function() + feed('d') + expect([[ + lines + of test text]]) + eq({'some short '}, funcs.getreg('"',1,1)) + feed('.') + expect([[ + test text]]) + eq({'lines', 'of '}, funcs.getreg('"',1,1)) + feed('uu') + expect([[ + some short lines + of test text]]) + + -- error aborts operator-pending, operator not performed + feed('d') + screen:expect([[ + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {2:Error detected while processing :} | + {2:E605: Exception not caught: very error} | + {3:Press ENTER or type command to continue}^ | + ]]) + feed('') + eq('E605: Exception not caught: very error', eval('v:errmsg')) + expect([[ + some short lines + of test text]]) + + feed('"bd') + expect([[ + soest text]]) + eq(funcs.getreg('b',1,1), {'me short lines', 'of t'}) + + -- startinsert aborts operator + feed('d') + eq('i', eval('mode(1)')) + expect([[ + soest text]]) + end) + + it('works in insert mode', function() + + -- works the same as ww + feed('iindeed little ') + screen:expect([[ + indeed some short little ^lines | + of test text | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {4:-- INSERT --} | + ]]) + + feed('') + screen:expect([[ + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {2:Error detected while processing :} | + {2:E605: Exception not caught: very error} | + {3:Press ENTER or type command to continue}^ | + ]]) + + + feed('') + eq('E605: Exception not caught: very error', eval('v:errmsg')) + -- still in insert + screen:expect([[ + indeed some short little ^lines | + of test text | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {4:-- INSERT --} | + ]]) + eq('i', eval('mode(1)')) + + -- When entering visual mode from InsertEnter autocmd, an async event, or + -- a mapping, vim ends up in undocumented "INSERT VISUAL" mode. If a + -- vim patch decides to disable this mode, this test is expected to fail. + feed('stuff ') + screen:expect([[ + in{5:deed some short little lines} | + {5:of stuff }^test text | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {4:-- INSERT VISUAL --} | + ]]) + expect([[ + indeed some short little lines + of stuff test text]]) + + feed('') + eq(funcs.getreg('a',1,1), {'deed some short little lines', 'of stuff t'}) + + -- still in insert + screen:expect([[ + in^deed some short little lines | + of stuff test text | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {4:-- INSERT --} | + ]]) + eq('i', eval('mode(1)')) + + -- also works as part of abbreviation + feed('foo ') + screen:expect([[ + in bar ^deed some short little lines | + of stuff test text | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {4:-- INSERT --} | + ]]) + eq(17, eval('g:y')) + + -- :startinsert does nothing + feed('') + eq('i', eval('mode(1)')) + + -- :stopinsert works + feed('') + eq('n', eval('mode(1)')) + end) + + it('works in cmdline mode', function() + cmdmap('', 'call setcmdpos(2)') + feed(':text') + eq('c', eval('m')) + -- didn't leave cmdline mode + eq('c', eval('mode(1)')) + feed('') + eq('n', eval('mode(1)')) + screen:expect([[ + ^some short lines | + of test text | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {2:E492: Not an editor command: text} | + ]]) + + feed(':echo 2') + screen:expect([[ + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + :echo 2 | + {2:Error detected while processing :} | + {2:E605: Exception not caught: very error} | + :echo 2^ | + ]]) + eq('E605: Exception not caught: very error', eval('v:errmsg')) + -- didn't leave cmdline mode + eq('c', eval('mode(1)')) + feed('+2') + screen:expect([[ + {1:~ }| + {1:~ }| + {1:~ }| + :echo 2 | + {2:Error detected while processing :} | + {2:E605: Exception not caught: very error} | + 4 | + {3:Press ENTER or type command to continue}^ | + ]]) + -- however, message scrolling may cause extra CR prompt + -- This is consistent with output from async events. + feed('') + screen:expect([[ + ^some short lines | + of test text | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]) + eq('n', eval('mode(1)')) + + feed(':let g:x = 3') + screen:expect([[ + some short lines | + of test text | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + :let g:x = 3^ | + ]]) + feed('+2') + -- cursor was moved in the background + screen:expect([[ + some short ^lines | + of test text | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + :let g:x = 3+2 | + ]]) + eq(5, eval('g:x')) + + feed(':let g:y = 7') + screen:expect([[ + some short lines | + of test text | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + :let g:y = 7^ | + ]]) + eq('c', eval('mode(1)')) + feed('+2') + -- startinsert takes effect after leaving cmdline mode + screen:expect([[ + some short ^lines | + of test text | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {4:-- INSERT --} | + ]]) + eq('i', eval('mode(1)')) + eq(9, eval('g:y')) + + end) + +end) + -- cgit From 189c5abeba4fb508d879ebbf5fa07965c4092cf2 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 20 Mar 2018 23:57:41 +0100 Subject: provider/RPC: apply_autocmds_group(): fix double-free During provider dispatch, eval_call_provider() saves global state--including pointers, such as `autocmd_fname`--into `provider_caller_scope` which is later restored by f_rpcrequest(). But `autocmd_fname` is special-cased in eval_vars(), for performance (see Vim patch 7.2.021; this is also the singular purpose of the `autocmd_fname_full` global. Yay!) If eval_vars() frees `autocmd_fname` then its provider-RPC-scoped alias becomes a problem. Solution: Don't free autocmd_fname in eval_vars(), just copy into it. closes #5245 closes #5617 Reference ------------------------------------------------------------------------ Vim patch 7.2.021 https://github.com/vim/vim/commit/f6dad43c98f47da1ff9d8c99b320fc3674f83c63 Problem: When executing autocommands getting the full file name may be slow. (David Kotchan) Solution: Postpone calling FullName_save() until autocmd_fname is used. vim_dev discussion (2008): "Problem with CursorMoved AutoCommand when Editing Files on a Remote WIndows Share" https://groups.google.com/d/msg/vim_dev/kj95weZa_eE/GTgj4aq5sIgJ --- test/functional/provider/python3_spec.lua | 19 ++++++++++++++++++- test/functional/provider/ruby_spec.lua | 22 ++++++++++++++++------ 2 files changed, 34 insertions(+), 7 deletions(-) (limited to 'test') diff --git a/test/functional/provider/python3_spec.lua b/test/functional/provider/python3_spec.lua index f06728ec0e..93ac3ae017 100644 --- a/test/functional/provider/python3_spec.lua +++ b/test/functional/provider/python3_spec.lua @@ -3,6 +3,7 @@ local eval, command, feed = helpers.eval, helpers.command, helpers.feed local eq, clear, insert = helpers.eq, helpers.clear, helpers.insert local expect, write_file = helpers.expect, helpers.write_file local feed_command = helpers.feed_command +local source = helpers.source local missing_provider = helpers.missing_provider do @@ -13,7 +14,7 @@ do end end -describe('python3 commands and functions', function() +describe('python3 provider', function() before_each(function() clear() command('python3 import vim') @@ -82,4 +83,20 @@ describe('python3 commands and functions', function() it('py3eval', function() eq({1, 2, {['key'] = 'val'}}, eval([[py3eval('[1, 2, {"key": "val"}]')]])) end) + + it('RPC call to expand("") during BufDelete #5245 #5617', function() + source([=[ + python3 << EOF + import vim + def foo(): + vim.eval('expand(":p")') + vim.eval('bufnr(expand(":p"))') + EOF + autocmd BufDelete * python3 foo() + autocmd BufUnload * python3 foo()]=]) + feed_command("exe 'split' tempname()") + feed_command("bwipeout!") + feed_command('help help') + eq(2, eval('1+1')) -- Still alive? + end) end) diff --git a/test/functional/provider/ruby_spec.lua b/test/functional/provider/ruby_spec.lua index a2c6c6a10e..e049ac7c41 100644 --- a/test/functional/provider/ruby_spec.lua +++ b/test/functional/provider/ruby_spec.lua @@ -1,16 +1,18 @@ local helpers = require('test.functional.helpers')(after_each) +local clear = helpers.clear +local command = helpers.command +local curbufmeths = helpers.curbufmeths local eq = helpers.eq +local eval = helpers.eval +local expect = helpers.expect local feed = helpers.feed -local clear = helpers.clear +local feed_command = helpers.feed_command local funcs = helpers.funcs -local meths = helpers.meths local insert = helpers.insert -local expect = helpers.expect -local command = helpers.command -local write_file = helpers.write_file -local curbufmeths = helpers.curbufmeths +local meths = helpers.meths local missing_provider = helpers.missing_provider +local write_file = helpers.write_file do clear() @@ -90,3 +92,11 @@ describe(':rubydo command', function() eq(false, curbufmeths.get_option('modified')) end) end) + +describe('ruby provider', function() + it('RPC call to expand("") during BufDelete #5245 #5617', function() + command([=[autocmd BufDelete * ruby VIM::evaluate('expand("")')]=]) + feed_command('help help') + eq(2, eval('1+1')) -- Still alive? + end) +end) -- cgit From 998a16c926623a667ecb0228f4a6cd8ba1e90201 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sat, 24 Mar 2018 11:21:20 +0100 Subject: refactor/rename: path_is_absolute() --- test/unit/os/fs_spec.lua | 2 +- test/unit/path_spec.lua | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) (limited to 'test') diff --git a/test/unit/os/fs_spec.lua b/test/unit/os/fs_spec.lua index bc39507c08..78455ee324 100644 --- a/test/unit/os/fs_spec.lua +++ b/test/unit/os/fs_spec.lua @@ -199,7 +199,7 @@ describe('fs.c', function() itp('returns the absolute path when given an executable inside $PATH', function() local fullpath = exe('ls') - eq(1, fs.path_is_absolute_path(to_cstr(fullpath))) + eq(1, fs.path_is_absolute(to_cstr(fullpath))) end) itp('returns the absolute path when given an executable relative to the current dir', function() diff --git a/test/unit/path_spec.lua b/test/unit/path_spec.lua index ed597eaed7..1bd20b1da9 100644 --- a/test/unit/path_spec.lua +++ b/test/unit/path_spec.lua @@ -585,22 +585,22 @@ describe('path.c', function() end) end) - describe('path_is_absolute_path', function() - local function path_is_absolute_path(filename) + describe('path_is_absolute', function() + local function path_is_absolute(filename) filename = to_cstr(filename) - return cimp.path_is_absolute_path(filename) + return cimp.path_is_absolute(filename) end itp('returns true if filename starts with a slash', function() - eq(OK, path_is_absolute_path('/some/directory/')) + eq(OK, path_is_absolute('/some/directory/')) end) itp('returns true if filename starts with a tilde', function() - eq(OK, path_is_absolute_path('~/in/my/home~/directory')) + eq(OK, path_is_absolute('~/in/my/home~/directory')) end) itp('returns false if filename starts not with slash nor tilde', function() - eq(FAIL, path_is_absolute_path('not/in/my/home~/directory')) + eq(FAIL, path_is_absolute('not/in/my/home~/directory')) end) end) end) -- cgit From 7ae41442088a4b1b1cd324b683defd87998ba75c Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sat, 24 Mar 2018 13:56:55 +0100 Subject: refactor/rename: path_try_shorten_fname() --- test/unit/path_spec.lua | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'test') diff --git a/test/unit/path_spec.lua b/test/unit/path_spec.lua index 1bd20b1da9..e8ce660ce1 100644 --- a/test/unit/path_spec.lua +++ b/test/unit/path_spec.lua @@ -261,7 +261,7 @@ describe('path.c', function() end) end) -describe('path_shorten_fname_if_possible', function() +describe('path_try_shorten_fname', function() local cwd = lfs.currentdir() before_each(function() @@ -273,22 +273,22 @@ describe('path_shorten_fname_if_possible', function() lfs.rmdir('ut_directory') end) - describe('path_shorten_fname_if_possible', function() + describe('path_try_shorten_fname', function() itp('returns shortened path if possible', function() lfs.chdir('ut_directory') local full = to_cstr(lfs.currentdir() .. '/subdir/file.txt') - eq('subdir/file.txt', (ffi.string(cimp.path_shorten_fname_if_possible(full)))) + eq('subdir/file.txt', (ffi.string(cimp.path_try_shorten_fname(full)))) end) itp('returns `full_path` if a shorter version is not possible', function() local old = lfs.currentdir() lfs.chdir('ut_directory') local full = old .. '/subdir/file.txt' - eq(full, (ffi.string(cimp.path_shorten_fname_if_possible(to_cstr(full))))) + eq(full, (ffi.string(cimp.path_try_shorten_fname(to_cstr(full))))) end) itp('returns NULL if `full_path` is NULL', function() - eq(NULL, (cimp.path_shorten_fname_if_possible(NULL))) + eq(NULL, (cimp.path_try_shorten_fname(NULL))) end) end) end) -- cgit From 131aad953c007d382cbff1d2560471b29975da87 Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Sat, 30 Sep 2017 21:31:31 -0400 Subject: win: defaults: 'shellcmdflag', 'shellxquote' #7343 closes #7698 Wrapping a command in double-quotes allows cmd.exe to safely dequote the entire command as if the user entered the entire command in an interactive prompt. This reduces the need to escape nested and uneven double quotes. The `/s` flag of cmd.exe makes the behaviour more reliable: :set shellcmdflag=/s\ /c Before this patch, cmd.exe cannot use cygwin echo.exe (as opposed to cmd.exe `echo` builtin) even if it is wrapped in double quotes. Example: :: internal echo > cmd /s /c " echo foo\:bar" " foo\:bar" :: cygwin echo.exe > cmd /s /c " "echo" foo\:bar" " foo:bar --- test/functional/eval/system_spec.lua | 40 ++++++++++++++++++++------- test/functional/terminal/edit_spec.lua | 1 + test/functional/terminal/ex_terminal_spec.lua | 5 ++++ 3 files changed, 36 insertions(+), 10 deletions(-) (limited to 'test') diff --git a/test/functional/eval/system_spec.lua b/test/functional/eval/system_spec.lua index 446afefb59..201426c40b 100644 --- a/test/functional/eval/system_spec.lua +++ b/test/functional/eval/system_spec.lua @@ -120,33 +120,47 @@ describe('system()', function() end end) - describe('executes shell function if passed a string', function() + describe('executes shell function', function() local screen before_each(function() - clear() - screen = Screen.new() - screen:attach() + clear() + screen = Screen.new() + screen:attach() end) after_each(function() - screen:detach() + screen:detach() end) if iswin() then + local function test_more() + eq('root = true', eval([[get(split(system('"more" ".editorconfig"'), "\n"), 0, '')]])) + end + local function test_shell_unquoting() + eval([[system('"ping" "-n" "1" "127.0.0.1"')]]) + eq(0, eval('v:shell_error')) + eq('"a b"\n', eval([[system('cmd /s/c "cmd /s/c "cmd /s/c "echo "a b""""')]])) + eq('"a b"\n', eval([[system('powershell -NoProfile -NoLogo -ExecutionPolicy RemoteSigned -Command echo ''\^"a b\^"''')]])) + end + it('with shell=cmd.exe', function() command('set shell=cmd.exe') eq('""\n', eval([[system('echo ""')]])) eq('"a b"\n', eval([[system('echo "a b"')]])) eq('a \nb\n', eval([[system('echo a & echo b')]])) eq('a \n', eval([[system('echo a 2>&1')]])) + test_more() eval([[system('cd "C:\Program Files"')]]) eq(0, eval('v:shell_error')) + test_shell_unquoting() end) it('with shell=cmd', function() command('set shell=cmd') eq('"a b"\n', eval([[system('echo "a b"')]])) + test_more() + test_shell_unquoting() end) it('with shell=$COMSPEC', function() @@ -154,6 +168,8 @@ describe('system()', function() if comspecshell == 'cmd.exe' then command('set shell=$COMSPEC') eq('"a b"\n', eval([[system('echo "a b"')]])) + test_more() + test_shell_unquoting() else pending('$COMSPEC is not cmd.exe: ' .. comspecshell) end @@ -187,7 +203,7 @@ describe('system()', function() ]]) end) - it('`yes` and is interrupted with CTRL-C', function() + it('`yes` interrupted with CTRL-C', function() feed(':call system("' .. (iswin() and 'for /L %I in (1,0,2) do @echo y' or 'yes') .. '")') @@ -239,6 +255,8 @@ describe('system()', function() end end) it('to backgrounded command does not crash', function() + -- cmd.exe doesn't background a command with & + if iswin() then return end -- This is indeterminate, just exercise the codepath. May get E5677. feed_command('call system("echo -n echoed &")') local v_errnum = string.match(eval("v:errmsg"), "^E%d*:") @@ -254,6 +272,8 @@ describe('system()', function() eq("input", eval('system("cat -", "input")')) end) it('to backgrounded command does not crash', function() + -- cmd.exe doesn't background a command with & + if iswin() then return end -- This is indeterminate, just exercise the codepath. May get E5677. feed_command('call system("cat - &", "input")') local v_errnum = string.match(eval("v:errmsg"), "^E%d*:") @@ -299,7 +319,7 @@ describe('system()', function() after_each(delete_file(fname)) it('replaces NULs by SOH characters', function() - eq('part1\001part2\001part3\n', eval('system("cat '..fname..'")')) + eq('part1\001part2\001part3\n', eval([[system('"cat" "]]..fname..[["')]])) end) end) @@ -366,7 +386,7 @@ describe('systemlist()', function() end end) - describe('exectues shell function', function() + describe('executes shell function', function() local screen before_each(function() @@ -399,7 +419,7 @@ describe('systemlist()', function() ]]) end) - it('`yes` and is interrupted with CTRL-C', function() + it('`yes` interrupted with CTRL-C', function() feed(':call systemlist("yes | xargs")') screen:expect([[ | @@ -464,7 +484,7 @@ describe('systemlist()', function() after_each(delete_file(fname)) it('replaces NULs by newline characters', function() - eq({'part1\npart2\npart3'}, eval('systemlist("cat '..fname..'")')) + eq({'part1\npart2\npart3'}, eval([[systemlist('"cat" "]]..fname..[["')]])) end) end) diff --git a/test/functional/terminal/edit_spec.lua b/test/functional/terminal/edit_spec.lua index d2b2d8a60c..84d7ae6e9c 100644 --- a/test/functional/terminal/edit_spec.lua +++ b/test/functional/terminal/edit_spec.lua @@ -36,6 +36,7 @@ describe(':edit term://*', function() local scr = get_screen(columns, lines) local rep = 'a' meths.set_option('shellcmdflag', 'REP ' .. rep) + command('set shellxquote=') -- win: avoid extra quotes local rep_size = rep:byte() -- 'a' => 97 local sb = 10 command('autocmd TermOpen * :setlocal scrollback='..tostring(sb) diff --git a/test/functional/terminal/ex_terminal_spec.lua b/test/functional/terminal/ex_terminal_spec.lua index 0a30b9bb02..4f22f7385d 100644 --- a/test/functional/terminal/ex_terminal_spec.lua +++ b/test/functional/terminal/ex_terminal_spec.lua @@ -8,6 +8,7 @@ local funcs = helpers.funcs local retry = helpers.retry local ok = helpers.ok local iswin = helpers.iswin +local command = helpers.command describe(':terminal', function() local screen @@ -143,6 +144,7 @@ describe(':terminal (with fake shell)', function() end) it('executes a given command through the shell', function() + command('set shellxquote=') -- win: avoid extra quotes terminal_with_fake_shell('echo hi') screen:expect([[ ^ready $ echo hi | @@ -154,6 +156,7 @@ describe(':terminal (with fake shell)', function() it("executes a given command through the shell, when 'shell' has arguments", function() nvim('set_option', 'shell', nvim_dir..'/shell-test -t jeff') + command('set shellxquote=') -- win: avoid extra quotes terminal_with_fake_shell('echo hi') screen:expect([[ ^jeff $ echo hi | @@ -164,6 +167,7 @@ describe(':terminal (with fake shell)', function() end) it('allows quotes and slashes', function() + command('set shellxquote=') -- win: avoid extra quotes terminal_with_fake_shell([[echo 'hello' \ "world"]]) screen:expect([[ ^ready $ echo 'hello' \ "world" | @@ -217,6 +221,7 @@ describe(':terminal (with fake shell)', function() end) it('works with gf', function() + command('set shellxquote=') -- win: avoid extra quotes terminal_with_fake_shell([[echo "scripts/shadacat.py"]]) screen:expect([[ ^ready $ echo "scripts/shadacat.py" | -- cgit From 8d64a2fb1dc2ae05ab3458ce15d4f18202337dd0 Mon Sep 17 00:00:00 2001 From: Nimit Bhardwaj Date: Sat, 24 Mar 2018 15:47:36 +0530 Subject: test: lua test for vim-patch:8.0.0184 https://github.com/vim/vim/commit/2b7bc567b9238aaac682236cb4f727d0376e1302 --- test/functional/core/exit_spec.lua | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'test') diff --git a/test/functional/core/exit_spec.lua b/test/functional/core/exit_spec.lua index 188a6a2c11..80c65e4544 100644 --- a/test/functional/core/exit_spec.lua +++ b/test/functional/core/exit_spec.lua @@ -1,6 +1,7 @@ local helpers = require('test.functional.helpers')(after_each) local command = helpers.command +local feed_command = helpers.feed_command local eval = helpers.eval local eq = helpers.eq local run = helpers.run @@ -33,6 +34,18 @@ describe('v:exiting', function() end run(on_request, nil, on_setup) end) + it('is 0 on exit from ex-mode involving try-catch', function() + local function on_setup() + command('autocmd VimLeavePre * call rpcrequest('..cid..', "")') + command('autocmd VimLeave * call rpcrequest('..cid..', "")') + feed_command('call feedkey("Q")','try', 'call NoFunction()', 'catch', 'echo "bye"', 'endtry', 'quit') + end + local function on_request() + eq(0, eval('v:exiting')) + return '' + end + run(on_request, nil, on_setup) + end) end) describe(':cquit', function() -- cgit From 79b4b6fc86497fcb7b3f986e98cbf03f93b5b082 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 21 Jan 2018 03:27:48 +0300 Subject: *: Make sure that !did_throw implies !current_exception Fixes #7876 --- test/functional/ui/cmdline_highlight_spec.lua | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'test') diff --git a/test/functional/ui/cmdline_highlight_spec.lua b/test/functional/ui/cmdline_highlight_spec.lua index ffb6a26aef..f9cec903a0 100644 --- a/test/functional/ui/cmdline_highlight_spec.lua +++ b/test/functional/ui/cmdline_highlight_spec.lua @@ -737,6 +737,22 @@ describe('Command-line coloring', function() feed('') eq('', meths.get_var('out')) end) + it('does not crash when callback has caught not-a-editor-command exception', + function() + source([[ + function CaughtExc(cmdline) abort + try + gibberish + catch + " Do nothing + endtry + return [] + endfunction + ]]) + set_color_cb('CaughtExc') + start_prompt('1') + eq(1, meths.eval('1')) + end) end) describe('Ex commands coloring support', function() it('works', function() -- cgit From ab6051331cebcde712ccc547b550d2a5d42cd587 Mon Sep 17 00:00:00 2001 From: b-r-o-c-k Date: Mon, 26 Mar 2018 10:54:44 -0500 Subject: build/msvc: Fix check for cksum on Windows --- test/functional/legacy/077_mf_hash_grow_spec.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'test') diff --git a/test/functional/legacy/077_mf_hash_grow_spec.lua b/test/functional/legacy/077_mf_hash_grow_spec.lua index c692127213..4719a3ecbf 100644 --- a/test/functional/legacy/077_mf_hash_grow_spec.lua +++ b/test/functional/legacy/077_mf_hash_grow_spec.lua @@ -18,7 +18,8 @@ describe('mf_hash_grow()', function() setup(clear) -- Check to see if cksum exists, otherwise skip the test - if os.execute('which cksum 2>&1 > /dev/null') ~= 0 then + local null = helpers.iswin() and 'nul' or '/dev/null' + if os.execute('cksum --help >' .. null .. ' 2>&1') ~= 0 then pending('was not tested because cksum was not found', function() end) else it('is working', function() -- cgit From 17ea0f2214f13b11305e380d9e93e7577b7675aa Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 8 Jan 2017 05:23:45 +0300 Subject: functests: Fix existing functional tests --- test/functional/shada/merging_spec.lua | 27 ++++++++++++++++----------- test/functional/shada/shada_spec.lua | 6 +++--- 2 files changed, 19 insertions(+), 14 deletions(-) (limited to 'test') diff --git a/test/functional/shada/merging_spec.lua b/test/functional/shada/merging_spec.lua index 7a15c8908b..1a289a2de7 100644 --- a/test/functional/shada/merging_spec.lua +++ b/test/functional/shada/merging_spec.lua @@ -563,13 +563,14 @@ describe('ShaDa marks support code', function() nvim_command('normal! `A') eq('-', funcs.fnamemodify(curbufmeths.get_name(), ':t')) eq(0, exc_exec('wshada ' .. shada_fname)) - local found = 0 + local found = {} for _, v in ipairs(read_shada_file(shada_fname)) do - if v.type == 7 and v.value.f == '' .. mock_file_path .. '-' then - found = found + 1 + if v.type == 7 and v.value.f == mock_file_path .. '-' then + local name = ('%c'):format(v.value.n) + found[name] = (found[name] or 0) + 1 end end - eq(1, found) + eq({['0']=1, A=1}, found) end) it('uses last A mark with eq timestamp from instance when writing', @@ -580,13 +581,14 @@ describe('ShaDa marks support code', function() nvim_command('normal! `A') eq('-', funcs.fnamemodify(curbufmeths.get_name(), ':t')) eq(0, exc_exec('wshada ' .. shada_fname)) - local found = 0 + local found = {} for _, v in ipairs(read_shada_file(shada_fname)) do if v.type == 7 and v.value.f == mock_file_path .. '-' then - found = found + 1 + local name = ('%c'):format(v.value.n) + found[name] = (found[name] or 0) + 1 end end - eq(1, found) + eq({['0']=1, A=1}, found) end) it('uses last A mark with gt timestamp from file when writing', @@ -597,13 +599,16 @@ describe('ShaDa marks support code', function() nvim_command('normal! `A') eq('-', funcs.fnamemodify(curbufmeths.get_name(), ':t')) eq(0, exc_exec('wshada ' .. shada_fname)) - local found = 0 + local found = {} for _, v in ipairs(read_shada_file(shada_fname)) do - if v.type == 7 and v.value.f == '' .. mock_file_path .. '?' then - found = found + 1 + if v.type == 7 then + local name = ('%c'):format(v.value.n) + local t = found[name] or {} + t[v.value.f] = (t[v.value.f] or 0) + 1 + found[name] = t end end - eq(1, found) + eq({['0']={['/a/b/-']=1}, A={['/a/b/?']=1}}, found) end) it('uses last a mark with gt timestamp from instance when reading', diff --git a/test/functional/shada/shada_spec.lua b/test/functional/shada/shada_spec.lua index ca44026852..720855860a 100644 --- a/test/functional/shada/shada_spec.lua +++ b/test/functional/shada/shada_spec.lua @@ -181,13 +181,13 @@ describe('ShaDa support code', function() nvim_command('set shada+=%') nvim_command('wshada! ' .. shada_fname) local readme_fname = funcs.resolve(paths.test_source_path) .. '/README.md' - eq({[7]=1, [8]=2, [9]=1, [10]=4, [11]=1}, find_file(readme_fname)) + eq({[7]=2, [8]=2, [9]=1, [10]=4, [11]=1}, find_file(readme_fname)) nvim_command('set shada+=r~') nvim_command('wshada! ' .. shada_fname) eq({}, find_file(readme_fname)) nvim_command('set shada-=r~') nvim_command('wshada! ' .. shada_fname) - eq({[7]=1, [8]=2, [9]=1, [10]=4, [11]=1}, find_file(readme_fname)) + eq({[7]=2, [8]=2, [9]=1, [10]=4, [11]=1}, find_file(readme_fname)) nvim_command('set shada+=r' .. funcs.escape( funcs.escape(paths.test_source_path, '$~'), ' "\\,')) nvim_command('wshada! ' .. shada_fname) @@ -206,7 +206,7 @@ describe('ShaDa support code', function() nvim_command('undo') nvim_command('set shada+=%') nvim_command('wshada! ' .. shada_fname) - eq({[7]=1, [8]=2, [9]=1, [10]=4, [11]=2}, find_file(fname)) + eq({[7]=2, [8]=2, [9]=1, [10]=4, [11]=2}, find_file(fname)) nvim_command('set shada+=r' .. pwd .. '/АБВ') nvim_command('wshada! ' .. shada_fname) eq({}, find_file(fname)) -- cgit From 920c582320085fa4bf0cf467fff572af47126ff7 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 27 Mar 2018 01:30:11 +0300 Subject: test/helpers: Support booleans --- test/helpers.lua | 2 ++ 1 file changed, 2 insertions(+) (limited to 'test') diff --git a/test/helpers.lua b/test/helpers.lua index 91ceed4df1..7cce6d8c55 100644 --- a/test/helpers.lua +++ b/test/helpers.lua @@ -469,6 +469,8 @@ format_luav = function(v, indent, opts) end elseif type(v) == 'nil' then ret = 'nil' + elseif type(v) == 'boolean' then + ret = (v and 'true' or 'false') else print(type(v)) -- Not implemented yet -- cgit From 8d5a46e77b1e0c77296f1d0d192e7906dd37c0d7 Mon Sep 17 00:00:00 2001 From: Nimit Bhardwaj Date: Wed, 28 Feb 2018 22:11:37 +0530 Subject: TUI: implement "standout" attribute #8081 closes #8054 --- test/functional/api/highlight_spec.lua | 9 +++++++++ test/functional/ui/highlight_spec.lua | 28 ++++++++++++++++++++++++++-- 2 files changed, 35 insertions(+), 2 deletions(-) (limited to 'test') diff --git a/test/functional/api/highlight_spec.lua b/test/functional/api/highlight_spec.lua index 2297a0760f..fed53a3dfd 100644 --- a/test/functional/api/highlight_spec.lua +++ b/test/functional/api/highlight_spec.lua @@ -99,5 +99,14 @@ describe('highlight api',function() eq(false, err) eq('Invalid highlight name: ', string.match(emsg, 'Invalid.*')) + + -- Test "standout" attribute. #8054 + eq({ underline = true, }, + meths.get_hl_by_name('cursorline', 0)); + command('hi CursorLine cterm=standout,underline term=standout,underline gui=standout,underline') + command('set cursorline') + eq({ underline = true, standout = true, }, + meths.get_hl_by_name('cursorline', 0)); + end) end) diff --git a/test/functional/ui/highlight_spec.lua b/test/functional/ui/highlight_spec.lua index 6f1b31964b..e874c45272 100644 --- a/test/functional/ui/highlight_spec.lua +++ b/test/functional/ui/highlight_spec.lua @@ -312,7 +312,7 @@ describe('highlight defaults', function() end) end) -describe('guisp (special/undercurl)', function() +describe('highlight', function() local screen before_each(function() @@ -321,7 +321,31 @@ describe('guisp (special/undercurl)', function() screen:attach() end) - it('can be set and is applied like foreground or background', function() + it('cterm=standout gui=standout', function() + screen:detach() + screen = Screen.new(20,5) + screen:attach() + screen:set_default_attr_ids({ + [1] = {bold = true, foreground = Screen.colors.Blue1}, + [2] = {standout = true, bold = true, underline = true, + background = Screen.colors.Gray90, foreground = Screen.colors.Blue1}, + [3] = {standout = true, underline = true, + background = Screen.colors.Gray90} + }) + feed_command('hi CursorLine cterm=standout,underline gui=standout,underline') + feed_command('set cursorline') + feed_command('set listchars=space:.,eol:¬,tab:>-,extends:>,precedes:<,trail:* list') + feed('i\t abcd \t abcd k') + screen:expect([[ + {1:>-------.}abcd{1:*¬} | + {2:^>-------.}{3:abcd}{2:*¬}{3: }| + {1:¬} | + {1:~ }| + | + ]]) + end) + + it('guisp (special/undercurl)', function() feed_command('syntax on') feed_command('syn keyword TmpKeyword neovim') feed_command('syn keyword TmpKeyword1 special') -- cgit From 98e71123900fbdf26a16a43297a1f58118cde41b Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Sat, 31 Mar 2018 11:12:27 +0200 Subject: msg: do not scroll entire screen (#8088) --- test/functional/api/vim_spec.lua | 15 ++--- test/functional/autocmd/cmdline_spec.lua | 9 +-- .../clipboard/clipboard_provider_spec.lua | 28 +++++---- test/functional/eval/execute_spec.lua | 16 +++-- test/functional/eval/input_spec.lua | 25 ++++---- test/functional/ex_cmds/cmd_map_spec.lua | 43 ++++++------- test/functional/ex_cmds/oldfiles_spec.lua | 1 + test/functional/lua/overrides_spec.lua | 1 + test/functional/ui/cmdline_highlight_spec.lua | 1 + test/functional/ui/cursor_spec.lua | 4 +- test/functional/ui/highlight_spec.lua | 71 ++++++++++++++++++++++ test/functional/ui/inccommand_spec.lua | 1 + test/functional/ui/input_spec.lua | 2 + test/functional/ui/mouse_spec.lua | 1 + test/functional/ui/output_spec.lua | 18 +++--- test/functional/ui/screen_basic_spec.lua | 29 ++++++++- 16 files changed, 190 insertions(+), 75 deletions(-) (limited to 'test') diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua index bd56161a54..7ac20a99af 100644 --- a/test/functional/api/vim_spec.lua +++ b/test/functional/api/vim_spec.lua @@ -579,7 +579,8 @@ describe('api', function() screen:set_default_attr_ids({ [0] = {bold=true, foreground=Screen.colors.Blue}, [1] = {foreground = Screen.colors.White, background = Screen.colors.Red}, - [2] = {bold = true, foreground = Screen.colors.SeaGreen} + [2] = {bold = true, foreground = Screen.colors.SeaGreen}, + [3] = {bold = true, reverse = true}, }) end) @@ -600,11 +601,11 @@ describe('api', function() it('shows return prompt when more than &cmdheight lines', function() nvim_async('err_write', 'something happened\nvery bad\n') screen:expect([[ + | {0:~ }| {0:~ }| {0:~ }| - {0:~ }| - {0:~ }| + {3: }| {1:something happened} | {1:very bad} | {2:Press ENTER or type command to continue}^ | @@ -614,9 +615,9 @@ describe('api', function() it('shows return prompt after all lines are shown', function() nvim_async('err_write', 'FAILURE\nERROR\nEXCEPTION\nTRACEBACK\n') screen:expect([[ + | {0:~ }| - {0:~ }| - {0:~ }| + {3: }| {1:FAILURE} | {1:ERROR} | {1:EXCEPTION} | @@ -644,11 +645,11 @@ describe('api', function() -- shows up to &cmdheight lines nvim_async('err_write', 'more fail\ntoo fail\n') screen:expect([[ + | {0:~ }| {0:~ }| {0:~ }| - {0:~ }| - {0:~ }| + {3: }| {1:more fail} | {1:too fail} | {2:Press ENTER or type command to continue}^ | diff --git a/test/functional/autocmd/cmdline_spec.lua b/test/functional/autocmd/cmdline_spec.lua index 8ea086fb46..3f0504d02f 100644 --- a/test/functional/autocmd/cmdline_spec.lua +++ b/test/functional/autocmd/cmdline_spec.lua @@ -59,24 +59,25 @@ describe('cmdline autocommands', function() [1] = {bold = true, foreground = Screen.colors.Blue1}, [2] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red}, [3] = {bold = true, foreground = Screen.colors.SeaGreen4}, + [4] = {bold = true, reverse = true}, }) command("autocmd CmdlineEnter * echoerr 'FAIL'") command("autocmd CmdlineLeave * echoerr 'very error'") feed(':') screen:expect([[ + | {1:~ }| {1:~ }| {1:~ }| - {1:~ }| - {1:~ }| + {4: }| : | {2:E5500: autocmd has thrown an exception: Vim(echoerr):FAIL} | :^ | ]]) feed("put ='lorem ipsum'") screen:expect([[ - {1:~ }| - {1:~ }| + | + {4: }| : | {2:E5500: autocmd has thrown an exception: Vim(echoerr):FAIL} | :put ='lorem ipsum' | diff --git a/test/functional/clipboard/clipboard_provider_spec.lua b/test/functional/clipboard/clipboard_provider_spec.lua index a3ea3b568f..a40c080a6d 100644 --- a/test/functional/clipboard/clipboard_provider_spec.lua +++ b/test/functional/clipboard/clipboard_provider_spec.lua @@ -83,7 +83,14 @@ local function basic_register_test(noblock) end describe('clipboard', function() - before_each(clear) + local screen + + before_each(function() + clear() + screen = Screen.new(72, 4) + screen:attach() + command("set display-=msgsep") + end) it('unnamed register works without provider', function() eq('"', eval('v:register')) @@ -92,8 +99,6 @@ describe('clipboard', function() it('`:redir @+>` with invalid g:clipboard shows exactly one error #7184', function() - local screen = Screen.new(72, 4) - screen:attach() command("let g:clipboard = 'bogus'") feed_command('redir @+> | :silent echo system("cat CONTRIBUTING.md") | redir END') screen:expect([[ @@ -106,8 +111,6 @@ describe('clipboard', function() it('`:redir @+>|bogus_cmd|redir END` + invalid g:clipboard must not recurse #7184', function() - local screen = Screen.new(72, 4) - screen:attach() command("let g:clipboard = 'bogus'") feed_command('redir @+> | bogus_cmd | redir END') screen:expect([[ @@ -123,8 +126,6 @@ describe('clipboard', function() eq('', eval('provider#clipboard#Executable()')) eq('clipboard: invalid g:clipboard', eval('provider#clipboard#Error()')) - local screen = Screen.new(72, 4) - screen:attach() command("let g:clipboard = 'bogus'") -- Explicit clipboard attempt, should show a hint message. feed_command('let @+="foo"') @@ -493,10 +494,10 @@ describe('clipboard', function() feed_command("let g:test_clip['+'] = ['such', 'plus', 'stuff']") feed_command("registers") screen:expect([[ - ~ | - ~ | - ~ | - ~ | + | + {0:~ }| + {0:~ }| + {4: }| :registers | {1:--- Registers ---} | "* some{2:^J}star data{2:^J} | @@ -504,10 +505,11 @@ describe('clipboard', function() ": let g:test_clip['+'] = ['such', 'plus', 'stuff'] | {3:Press ENTER or type command to continue}^ | ]], { + [0] = {bold = true, foreground = Screen.colors.Blue}, [1] = {bold = true, foreground = Screen.colors.Fuchsia}, [2] = {foreground = Screen.colors.Blue}, - [3] = {bold = true, foreground = Screen.colors.SeaGreen}}, - {{bold = true, foreground = Screen.colors.Blue}}) + [3] = {bold = true, foreground = Screen.colors.SeaGreen}, + [4] = {bold = true, reverse = true}}) feed('') -- clear out of Press ENTER screen end) diff --git a/test/functional/eval/execute_spec.lua b/test/functional/eval/execute_spec.lua index 183884a51e..925e311c7d 100644 --- a/test/functional/eval/execute_spec.lua +++ b/test/functional/eval/execute_spec.lua @@ -106,16 +106,22 @@ describe('execute()', function() end) it('does not corrupt the command display #5422', function() - local screen = Screen.new(70, 5) + local screen = Screen.new(70, 7) screen:attach() feed(':echo execute("hi ErrorMsg")') screen:expect([[ - ~ | - ~ | + | + {1:~ }| + {1:~ }| + {2: }| :echo execute("hi ErrorMsg") | ErrorMsg xxx ctermfg=15 ctermbg=1 guifg=White guibg=Red | - Press ENTER or type command to continue^ | - ]]) + {3:Press ENTER or type command to continue}^ | + ]], { + [1] = {bold = true, foreground = Screen.colors.Blue1}, + [2] = {bold = true, reverse = true}, + [3] = {bold = true, foreground = Screen.colors.SeaGreen4}, + }) feed('') end) diff --git a/test/functional/eval/input_spec.lua b/test/functional/eval/input_spec.lua index 1e6b107c60..777f49462d 100644 --- a/test/functional/eval/input_spec.lua +++ b/test/functional/eval/input_spec.lua @@ -58,6 +58,7 @@ before_each(function() RBP2={background=Screen.colors.Yellow}, RBP3={background=Screen.colors.Green}, RBP4={background=Screen.colors.Blue}, + SEP={bold = true, reverse = true}, }) end) @@ -65,9 +66,9 @@ describe('input()', function() it('works with multiline prompts', function() feed([[:call input("Test\nFoo")]]) screen:expect([[ + | {EOB:~ }| - {EOB:~ }| - {EOB:~ }| + {SEP: }| Test | Foo^ | ]]) @@ -75,9 +76,9 @@ describe('input()', function() it('works with multiline prompts and :echohl', function() feed([[:echohl Test | call input("Test\nFoo")]]) screen:expect([[ + | {EOB:~ }| - {EOB:~ }| - {EOB:~ }| + {SEP: }| {T:Test} | {T:Foo}^ | ]]) @@ -242,17 +243,17 @@ describe('input()', function() it('is not hidden by :silent', function() feed([[:silent call input('Foo: ')]]) screen:expect([[ + | {EOB:~ }| - {EOB:~ }| - {EOB:~ }| + {SEP: }| Foo: ^ | | ]]) feed('Bar') screen:expect([[ + | {EOB:~ }| - {EOB:~ }| - {EOB:~ }| + {SEP: }| Foo: Bar^ | | ]]) @@ -263,9 +264,9 @@ describe('inputdialog()', function() it('works with multiline prompts', function() feed([[:call inputdialog("Test\nFoo")]]) screen:expect([[ + | {EOB:~ }| - {EOB:~ }| - {EOB:~ }| + {SEP: }| Test | Foo^ | ]]) @@ -273,9 +274,9 @@ describe('inputdialog()', function() it('works with multiline prompts and :echohl', function() feed([[:echohl Test | call inputdialog("Test\nFoo")]]) screen:expect([[ + | {EOB:~ }| - {EOB:~ }| - {EOB:~ }| + {SEP: }| {T:Test} | {T:Foo}^ | ]]) diff --git a/test/functional/ex_cmds/cmd_map_spec.lua b/test/functional/ex_cmds/cmd_map_spec.lua index 5b7f0942b1..77d025dcc7 100644 --- a/test/functional/ex_cmds/cmd_map_spec.lua +++ b/test/functional/ex_cmds/cmd_map_spec.lua @@ -27,6 +27,7 @@ describe('mappings with ', function() [4] = {bold = true}, [5] = {background = Screen.colors.LightGrey}, [6] = {foreground = Screen.colors.Blue1}, + [7] = {bold = true, reverse = true}, }) screen:attach() @@ -342,11 +343,11 @@ describe('mappings with ', function() -- error doesn't interrupt visual mode feed('ggvw') screen:expect([[ + {5:some }short lines | + of test text | {1:~ }| {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| + {7: }| {2:Error detected while processing :} | {2:E605: Exception not caught: very error} | {3:Press ENTER or type command to continue}^ | @@ -425,11 +426,11 @@ describe('mappings with ', function() -- error doesn't interrupt temporary visual mode feed('ggvw') screen:expect([[ + {5:some }short lines | + of test text | {1:~ }| {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| + {7: }| {2:Error detected while processing :} | {2:E605: Exception not caught: very error} | {3:Press ENTER or type command to continue}^ | @@ -453,11 +454,11 @@ describe('mappings with ', function() -- error doesn't interrupt select mode feed('ggvw') screen:expect([[ + {5:some }short lines | + of test text | {1:~ }| {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| + {7: }| {2:Error detected while processing :} | {2:E605: Exception not caught: very error} | {3:Press ENTER or type command to continue}^ | @@ -527,11 +528,11 @@ describe('mappings with ', function() -- error aborts operator-pending, operator not performed feed('d') screen:expect([[ + some short lines | + of test text | {1:~ }| {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| + {7: }| {2:Error detected while processing :} | {2:E605: Exception not caught: very error} | {3:Press ENTER or type command to continue}^ | @@ -571,11 +572,11 @@ describe('mappings with ', function() feed('') screen:expect([[ + indeed some short little lines | + of test text | {1:~ }| {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| + {7: }| {2:Error detected while processing :} | {2:E605: Exception not caught: very error} | {3:Press ENTER or type command to continue}^ | @@ -675,10 +676,10 @@ describe('mappings with ', function() feed(':echo 2') screen:expect([[ + some short lines | + of test text | {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| + {7: }| :echo 2 | {2:Error detected while processing :} | {2:E605: Exception not caught: very error} | @@ -689,9 +690,9 @@ describe('mappings with ', function() eq('c', eval('mode(1)')) feed('+2') screen:expect([[ - {1:~ }| - {1:~ }| - {1:~ }| + some short lines | + of test text | + {7: }| :echo 2 | {2:Error detected while processing :} | {2:E605: Exception not caught: very error} | diff --git a/test/functional/ex_cmds/oldfiles_spec.lua b/test/functional/ex_cmds/oldfiles_spec.lua index 4002855c24..448326cdfb 100644 --- a/test/functional/ex_cmds/oldfiles_spec.lua +++ b/test/functional/ex_cmds/oldfiles_spec.lua @@ -29,6 +29,7 @@ describe(':oldfiles', function() it('shows most recently used files', function() local screen = Screen.new(100, 5) screen:attach() + feed_command("set display-=msgsep") feed_command('edit testfile1') feed_command('edit testfile2') feed_command('wshada') diff --git a/test/functional/lua/overrides_spec.lua b/test/functional/lua/overrides_spec.lua index 8ca5fe57ba..007d40874f 100644 --- a/test/functional/lua/overrides_spec.lua +++ b/test/functional/lua/overrides_spec.lua @@ -87,6 +87,7 @@ describe('debug.debug', function() E = {foreground = Screen.colors.Grey100, background = Screen.colors.Red}, cr = {bold = true, foreground = Screen.colors.SeaGreen4}, }) + command("set display-=msgsep") end) it('works', function() command([[lua diff --git a/test/functional/ui/cmdline_highlight_spec.lua b/test/functional/ui/cmdline_highlight_spec.lua index f9cec903a0..3c316d1cfa 100644 --- a/test/functional/ui/cmdline_highlight_spec.lua +++ b/test/functional/ui/cmdline_highlight_spec.lua @@ -24,6 +24,7 @@ before_each(function() clear() screen = Screen.new(40, 8) screen:attach() + command("set display-=msgsep") source([[ highlight RBP1 guibg=Red highlight RBP2 guibg=Yellow diff --git a/test/functional/ui/cursor_spec.lua b/test/functional/ui/cursor_spec.lua index b47210a777..812c095add 100644 --- a/test/functional/ui/cursor_spec.lua +++ b/test/functional/ui/cursor_spec.lua @@ -194,8 +194,8 @@ describe('ui/cursor', function() if m.blinkoff then m.blinkoff = 400 end if m.blinkwait then m.blinkwait = 700 end end - if m.hl_id then m.hl_id = 48 end - if m.id_lm then m.id_lm = 49 end + if m.hl_id then m.hl_id = 49 end + if m.id_lm then m.id_lm = 50 end end -- Assert the new expectation. diff --git a/test/functional/ui/highlight_spec.lua b/test/functional/ui/highlight_spec.lua index e874c45272..364e0eea7f 100644 --- a/test/functional/ui/highlight_spec.lua +++ b/test/functional/ui/highlight_spec.lua @@ -94,6 +94,7 @@ describe('highlight defaults', function() clear() screen = Screen.new() screen:attach() + command("set display-=msgsep") end) after_each(function() @@ -674,6 +675,76 @@ describe("'listchars' highlight", function() end) end) +describe("MsgSeparator highlight and msgsep fillchar", function() + before_each(clear) + it("works", function() + local screen = Screen.new(50,5) + screen:set_default_attr_ids({ + [1] = {bold=true, foreground=Screen.colors.Blue}, + [2] = {bold=true, reverse=true}, + [3] = {bold = true, foreground = Screen.colors.SeaGreen4}, + [4] = {background = Screen.colors.Cyan, bold = true, reverse = true}, + [5] = {bold = true, background = Screen.colors.Magenta} + }) + screen:attach() + + -- defaults + feed_command("ls") + screen:expect([[ + | + {2: }| + :ls | + 1 %a "[No Name]" line 1 | + {3:Press ENTER or type command to continue}^ | + ]]) + feed('') + + feed_command("set fillchars+=msgsep:-") + feed_command("ls") + screen:expect([[ + | + {2:--------------------------------------------------}| + :ls | + 1 %a "[No Name]" line 1 | + {3:Press ENTER or type command to continue}^ | + ]]) + + -- linked to StatusLine per default + feed_command("hi StatusLine guibg=Cyan") + feed_command("ls") + screen:expect([[ + | + {4:--------------------------------------------------}| + :ls | + 1 %a "[No Name]" line 1 | + {3:Press ENTER or type command to continue}^ | + ]]) + + -- but can be unlinked + feed_command("hi clear MsgSeparator") + feed_command("hi MsgSeparator guibg=Magenta gui=bold") + feed_command("ls") + screen:expect([[ + | + {5:--------------------------------------------------}| + :ls | + 1 %a "[No Name]" line 1 | + {3:Press ENTER or type command to continue}^ | + ]]) + + -- when display doesn't contain msgsep, these options have no effect + feed_command("set display-=msgsep") + feed_command("ls") + screen:expect([[ + {1:~ }| + {1:~ }| + :ls | + 1 %a "[No Name]" line 1 | + {3:Press ENTER or type command to continue}^ | + ]]) + end) +end) + describe("'winhighlight' highlight", function() local screen diff --git a/test/functional/ui/inccommand_spec.lua b/test/functional/ui/inccommand_spec.lua index 8b0cb82365..27e4066d9f 100644 --- a/test/functional/ui/inccommand_spec.lua +++ b/test/functional/ui/inccommand_spec.lua @@ -63,6 +63,7 @@ local function common_setup(screen, inccommand, text) command("syntax on") command("set nohlsearch") command("hi Substitute guifg=red guibg=yellow") + command("set display-=msgsep") screen:attach() screen:set_default_attr_ids({ [1] = {foreground = Screen.colors.Fuchsia}, diff --git a/test/functional/ui/input_spec.lua b/test/functional/ui/input_spec.lua index 8e62a37ef1..9bfc87d835 100644 --- a/test/functional/ui/input_spec.lua +++ b/test/functional/ui/input_spec.lua @@ -1,6 +1,7 @@ local helpers = require('test.functional.helpers')(after_each) local clear, feed_command, nvim = helpers.clear, helpers.feed_command, helpers.nvim local feed, next_msg, eq = helpers.feed, helpers.next_msg, helpers.eq +local command = helpers.command local expect = helpers.expect local write_file = helpers.write_file local Screen = require('test.functional.ui.screen') @@ -137,6 +138,7 @@ describe('input non-printable chars', function() [3] = {bold = true, foreground = Screen.colors.SeaGreen4} }) screen:attach() + command("set display-=msgsep") feed_command("e Xtest-overwrite") screen:expect([[ diff --git a/test/functional/ui/mouse_spec.lua b/test/functional/ui/mouse_spec.lua index 05d5a6409a..debd324977 100644 --- a/test/functional/ui/mouse_spec.lua +++ b/test/functional/ui/mouse_spec.lua @@ -26,6 +26,7 @@ describe('ui/mouse/input', function() [4] = {reverse = true}, [5] = {bold = true, reverse = true}, }) + command("set display-=msgsep") feed('itestingmousesupport and selection') screen:expect([[ testing | diff --git a/test/functional/ui/output_spec.lua b/test/functional/ui/output_spec.lua index 4246020fab..02ca566bd8 100644 --- a/test/functional/ui/output_spec.lua +++ b/test/functional/ui/output_spec.lua @@ -40,10 +40,10 @@ describe("shell command :!", function() -- to avoid triggering a UI flush. child_session.feed_data(":!printf foo; sleep 200\n") screen:expect([[ + | {4:~ }| {4:~ }| - {4:~ }| - {4:~ }| + {5: }| :!printf foo; sleep 200 | foo | {3:-- TERMINAL --} | @@ -99,6 +99,7 @@ describe("shell command :!", function() end local screen = Screen.new(50, 4) screen:attach() + command("set display-=msgsep") -- Print TAB chars. #2958 feed([[:!printf '1\t2\t3']]) screen:expect([[ @@ -153,6 +154,7 @@ describe("shell command :!", function() [1] = {bold = true, foreground = Screen.colors.Blue1}, [2] = {foreground = Screen.colors.Blue1}, [3] = {bold = true, foreground = Screen.colors.SeaGreen4}, + [4] = {bold = true, reverse = true}, }) screen:attach() end) @@ -170,10 +172,10 @@ describe("shell command :!", function() or [[:!ls bang_filter_spec ]]) feed([[\l]]) screen:expect([[ + | {1:~ }| {1:~ }| - {1:~ }| - {1:~ }| + {4: }| ]]..result..[[ | f1 | f2 | @@ -187,9 +189,9 @@ describe("shell command :!", function() feed_command('!cat test/functional/fixtures/shell_data.txt') screen.bell = false screen:expect([[ + | {1:~ }| - {1:~ }| - {1:~ }| + {4: }| :!cat test/functional/fixtures/shell_data.txt | {2:^@^A^B^C^D^E^F^H} | {2:^N^O^P^Q^R^S^T^U^V^W^X^Y^Z^[^\^]^^^_} | @@ -213,8 +215,8 @@ describe("shell command :!", function() feed_command(cmd) -- Note: only the first example of split composed char works screen:expect([[ - {1:~ }| - {1:~ }| + | + {4: }| :]]..cmd..[[ | å | ref: å̲ | diff --git a/test/functional/ui/screen_basic_spec.lua b/test/functional/ui/screen_basic_spec.lua index bbdf576641..563d04416a 100644 --- a/test/functional/ui/screen_basic_spec.lua +++ b/test/functional/ui/screen_basic_spec.lua @@ -355,7 +355,8 @@ describe('Screen', function() ]]) end) - it('execute command with multi-line output', function() + it('execute command with multi-line output without msgsep', function() + command("set display-=msgsep") feed(':ls') screen:expect([[ {0:~ }| @@ -375,6 +376,28 @@ describe('Screen', function() ]]) feed('') -- skip the "Press ENTER..." state or tests will hang end) + + it('execute command with multi-line output and with msgsep', function() + command("set display+=msgsep") + feed(':ls') + screen:expect([[ + | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {1: }| + :ls | + 1 %a "[No Name]" line 1 | + {7:Press ENTER or type command to continue}^ | + ]]) + feed('') -- skip the "Press ENTER..." state or tests will hang + end) end) describe('scrolling and clearing', function() @@ -573,6 +596,7 @@ describe('Screen', function() command('nnoremap :echo "TEST"') feed(':ls') screen:expect([[ + | {0:~ }| {0:~ }| {0:~ }| @@ -582,8 +606,7 @@ describe('Screen', function() {0:~ }| {0:~ }| {0:~ }| - {0:~ }| - {0:~ }| + {1: }| :ls | 1 %a "[No Name]" line 1 | {7:Press ENTER or type command to continue}^ | -- cgit From 1ac1f520f02afc934523ce76a99a3d17d0d6a670 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 1 Apr 2018 20:04:35 +0300 Subject: functests: Add test for merging with file with only numeric mark Known to cause memory leak, but not an expected crash. --- test/functional/shada/merging_spec.lua | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'test') diff --git a/test/functional/shada/merging_spec.lua b/test/functional/shada/merging_spec.lua index 1a289a2de7..5d486bbd93 100644 --- a/test/functional/shada/merging_spec.lua +++ b/test/functional/shada/merging_spec.lua @@ -525,6 +525,22 @@ describe('ShaDa marks support code', function() eq('-', funcs.fnamemodify(curbufmeths.get_name(), ':t')) end) + it('can merge with file with mark 9 as the only numeric mark', function() + wshada('\007\001\018\131\162mX\195\161f\196\006' .. mock_file_path .. '-\161n9') + eq(0, exc_exec(sdrcmd())) + nvim_command('normal! `9oabc') + eq('-', funcs.fnamemodify(curbufmeths.get_name(), ':t')) + eq(0, exc_exec('wshada ' .. shada_fname)) + local found = {} + for _, v in ipairs(read_shada_file(shada_fname)) do + if v.type == 7 and v.value.f == mock_file_path .. '-' then + local name = ('%c'):format(v.value.n) + found[name] = (found[name] or 0) + 1 + end + end + eq({['0']=1, ['1']=1}, found) + end) + it('uses last A mark with gt timestamp from file when reading with !', function() wshada('\007\001\018\131\162mX\195\161f\196\006' .. mock_file_path .. '-\161nA') -- cgit From dd1b493f7560244162f4051443fb42dabaee0254 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 1 Apr 2018 21:29:47 +0300 Subject: shada: Fix some memory leaks and completely ignore numbered mark names Problems: - In two places in shada_read_when_writing() memory just was not freed. Both places were verified to cause test failures. - Numbered marks got assigned incorrect (off-by-one compared to position in the array) numbers in replace_numbered_mark. - It was possible to have non-continuously populated array of numbered marks which messed up code for merging them. (Note about tests: marks with additional data are always compared different when merging, that caused some confusion regarding why test did not work the way I expected.) --- test/functional/shada/merging_spec.lua | 65 +++++++++++++++++++++++++++++++++- 1 file changed, 64 insertions(+), 1 deletion(-) (limited to 'test') diff --git a/test/functional/shada/merging_spec.lua b/test/functional/shada/merging_spec.lua index 5d486bbd93..fdd35b70b3 100644 --- a/test/functional/shada/merging_spec.lua +++ b/test/functional/shada/merging_spec.lua @@ -526,7 +526,7 @@ describe('ShaDa marks support code', function() end) it('can merge with file with mark 9 as the only numeric mark', function() - wshada('\007\001\018\131\162mX\195\161f\196\006' .. mock_file_path .. '-\161n9') + wshada('\007\001\014\130\161f\196\006' .. mock_file_path .. '-\161n9') eq(0, exc_exec(sdrcmd())) nvim_command('normal! `9oabc') eq('-', funcs.fnamemodify(curbufmeths.get_name(), ':t')) @@ -541,6 +541,69 @@ describe('ShaDa marks support code', function() eq({['0']=1, ['1']=1}, found) end) + it('removes duplicates while merging', function() + wshada('\007\001\014\130\161f\196\006' .. mock_file_path .. '-\161n9' + .. '\007\001\014\130\161f\196\006' .. mock_file_path .. '-\161n9') + eq(0, exc_exec(sdrcmd())) + eq(0, exc_exec('wshada ' .. shada_fname)) + local found = 0 + for _, v in ipairs(read_shada_file(shada_fname)) do + if v.type == 7 and v.value.f == mock_file_path .. '-' then + print(require('test.helpers').format_luav(v)) + found = found + 1 + end + end + eq(1, found) + end) + + it('does not leak when no append is performed due to too many marks', + function() + wshada('\007\002\018\131\162mX\195\161f\196\006' .. mock_file_path .. 'a\161n0' + .. '\007\002\018\131\162mX\195\161f\196\006' .. mock_file_path .. 'b\161n1' + .. '\007\002\018\131\162mX\195\161f\196\006' .. mock_file_path .. 'c\161n2' + .. '\007\002\018\131\162mX\195\161f\196\006' .. mock_file_path .. 'd\161n3' + .. '\007\002\018\131\162mX\195\161f\196\006' .. mock_file_path .. 'e\161n4' + .. '\007\002\018\131\162mX\195\161f\196\006' .. mock_file_path .. 'f\161n5' + .. '\007\002\018\131\162mX\195\161f\196\006' .. mock_file_path .. 'g\161n6' + .. '\007\002\018\131\162mX\195\161f\196\006' .. mock_file_path .. 'h\161n7' + .. '\007\002\018\131\162mX\195\161f\196\006' .. mock_file_path .. 'i\161n8' + .. '\007\002\018\131\162mX\195\161f\196\006' .. mock_file_path .. 'j\161n9' + .. '\007\001\018\131\162mX\195\161f\196\006' .. mock_file_path .. 'k\161n9') + eq(0, exc_exec(sdrcmd())) + eq(0, exc_exec('wshada ' .. shada_fname)) + local found = {} + for _, v in ipairs(read_shada_file(shada_fname)) do + if v.type == 7 and v.value.f:sub(1, #mock_file_path) == mock_file_path then + found[#found + 1] = v.value.f:sub(#v.value.f) + end + end + eq({'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'}, found) + end) + + it('does not leak when last mark in file removes some of the earlier ones', + function() + wshada('\007\002\018\131\162mX\195\161f\196\006' .. mock_file_path .. 'a\161n0' + .. '\007\002\018\131\162mX\195\161f\196\006' .. mock_file_path .. 'b\161n1' + .. '\007\002\018\131\162mX\195\161f\196\006' .. mock_file_path .. 'c\161n2' + .. '\007\002\018\131\162mX\195\161f\196\006' .. mock_file_path .. 'd\161n3' + .. '\007\002\018\131\162mX\195\161f\196\006' .. mock_file_path .. 'e\161n4' + .. '\007\002\018\131\162mX\195\161f\196\006' .. mock_file_path .. 'f\161n5' + .. '\007\002\018\131\162mX\195\161f\196\006' .. mock_file_path .. 'g\161n6' + .. '\007\002\018\131\162mX\195\161f\196\006' .. mock_file_path .. 'h\161n7' + .. '\007\002\018\131\162mX\195\161f\196\006' .. mock_file_path .. 'i\161n8' + .. '\007\002\018\131\162mX\195\161f\196\006' .. mock_file_path .. 'j\161n9' + .. '\007\003\018\131\162mX\195\161f\196\006' .. mock_file_path .. 'k\161n9') + eq(0, exc_exec(sdrcmd())) + eq(0, exc_exec('wshada ' .. shada_fname)) + local found = {} + for _, v in ipairs(read_shada_file(shada_fname)) do + if v.type == 7 and v.value.f:sub(1, #mock_file_path) == mock_file_path then + found[#found + 1] = v.value.f:sub(#v.value.f) + end + end + eq({'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'k'}, found) + end) + it('uses last A mark with gt timestamp from file when reading with !', function() wshada('\007\001\018\131\162mX\195\161f\196\006' .. mock_file_path .. '-\161nA') -- cgit From 5d9bb16d66043f10dc14435c0594ca6f31b1795f Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 2 Apr 2018 11:14:11 +0300 Subject: functests: Use proper path in `eq()` --- test/functional/shada/merging_spec.lua | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'test') diff --git a/test/functional/shada/merging_spec.lua b/test/functional/shada/merging_spec.lua index fdd35b70b3..a628baff53 100644 --- a/test/functional/shada/merging_spec.lua +++ b/test/functional/shada/merging_spec.lua @@ -670,8 +670,7 @@ describe('ShaDa marks support code', function() eq({['0']=1, A=1}, found) end) - it('uses last A mark with gt timestamp from file when writing', - function() + it('uses last A mark with gt timestamp from file when writing', function() wshada('\007\001\018\131\162mX\195\161f\196\006' .. mock_file_path .. '-\161nA') eq(0, exc_exec(sdrcmd())) wshada('\007\002\018\131\162mX\195\161f\196\006' .. mock_file_path .. '?\161nA') @@ -687,7 +686,7 @@ describe('ShaDa marks support code', function() found[name] = t end end - eq({['0']={['/a/b/-']=1}, A={['/a/b/?']=1}}, found) + eq({['0']={[mock_file_path .. '-']=1}, A={[mock_file_path .. '?']=1}}, found) end) it('uses last a mark with gt timestamp from instance when reading', -- cgit From 60e96a45b4f41ff73ba59fec9c9bfa87195d1207 Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Mon, 2 Apr 2018 14:21:14 +0200 Subject: screen: winhl=Normal:Background should not override syntax (#8093) fixes #7375 --- test/functional/ui/highlight_spec.lua | 36 +++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) (limited to 'test') diff --git a/test/functional/ui/highlight_spec.lua b/test/functional/ui/highlight_spec.lua index 364e0eea7f..ab3b1c3cac 100644 --- a/test/functional/ui/highlight_spec.lua +++ b/test/functional/ui/highlight_spec.lua @@ -778,6 +778,9 @@ describe("'winhighlight' highlight", function() [22] = {bold = true, foreground = Screen.colors.SeaGreen4}, [23] = {background = Screen.colors.LightMagenta}, [24] = {background = Screen.colors.WebGray}, + [25] = {bold = true, foreground = Screen.colors.Green1}, + [26] = {background = Screen.colors.Red}, + [27] = {background = Screen.colors.DarkBlue, bold = true, foreground = Screen.colors.Green1}, }) command("hi Background1 guibg=DarkBlue") command("hi Background2 guibg=DarkGreen") @@ -1047,6 +1050,39 @@ describe("'winhighlight' highlight", function() ]]) end) + it("background doesn't override syntax background", function() + command('syntax on') + command('syntax keyword Foobar foobar') + command('syntax keyword Article the') + command('hi Foobar guibg=#FF0000') + command('hi Article guifg=#00FF00 gui=bold') + insert('the foobar was foobar') + screen:expect([[ + {25:the} {26:foobar} was {26:fooba}| + {26:^r} | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + | + ]]) + + -- winhl=Normal:Group with background doesn't override syntax background, + -- but does combine with syntax foreground. + command('set winhl=Normal:Background1') + screen:expect([[ + {27:the}{1: }{26:foobar}{1: was }{26:fooba}| + {26:^r}{1: }| + {2:~ }| + {2:~ }| + {2:~ }| + {2:~ }| + {2:~ }| + | + ]]) + end) + it('can override NonText, Conceal and EndOfBuffer', function() curbufmeths.set_lines(0,-1,true, {"raa\000"}) command('call matchaddpos("Conceal", [[1,2]], 0, -1, {"conceal": "#"})') -- cgit From 224ebc0078f29ac3189d7fbd9a59b386ae2ee303 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Wed, 4 Apr 2018 01:08:07 +0200 Subject: insert-mode: interpret unmapped META as ESC closes #2454 closes #8213 ref #7972 --- test/functional/insert/insert_spec.lua | 41 +++++++++++++++++++++++++++ test/functional/insert/last_inserted_spec.lua | 22 -------------- test/functional/terminal/tui_spec.lua | 14 ++++----- 3 files changed, 46 insertions(+), 31 deletions(-) create mode 100644 test/functional/insert/insert_spec.lua delete mode 100644 test/functional/insert/last_inserted_spec.lua (limited to 'test') diff --git a/test/functional/insert/insert_spec.lua b/test/functional/insert/insert_spec.lua new file mode 100644 index 0000000000..427954f5a6 --- /dev/null +++ b/test/functional/insert/insert_spec.lua @@ -0,0 +1,41 @@ +local helpers = require('test.functional.helpers')(after_each) +local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert +local command = helpers.command +local eq = helpers.eq +local expect = helpers.expect +local funcs = helpers.funcs + +describe('insert-mode', function() + before_each(function() + clear() + end) + + it('CTRL-@', function() + -- Inserts last-inserted text, leaves insert-mode. + insert('hello') + feed('ix') + expect('hellhello') + + -- C-Space is the same as C-@. + -- CTRL-SPC inserts last-inserted text, leaves insert-mode. + feed('ix') + expect('hellhellhello') + + -- CTRL-A inserts last inserted text + feed('ix') + expect('hellhellhellhelloxo') + end) + + it('ALT/META #8213', function() + -- Mapped ALT-chord behaves as mapped. + command('inoremap meta-l') + command('inoremap alt-j') + feed('i xxx a') + expect('meta-l xxx alt-j') + eq({ 0, 1, 14, 0, }, funcs.getpos('.')) + -- Unmapped ALT-chord behaves as ESC+c. + command('iunmap ') + feed('0i') + eq({ 0, 1, 2, 0, }, funcs.getpos('.')) + end) +end) diff --git a/test/functional/insert/last_inserted_spec.lua b/test/functional/insert/last_inserted_spec.lua deleted file mode 100644 index dce23a3790..0000000000 --- a/test/functional/insert/last_inserted_spec.lua +++ /dev/null @@ -1,22 +0,0 @@ -local helpers = require('test.functional.helpers')(after_each) -local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert -local expect = helpers.expect - -clear() - -describe('insert-mode', function() - it('CTRL-@ inserts last-inserted text, leaves insert-mode', function() - insert('hello') - feed('ix') - expect('hellhello') - end) - -- C-Space is the same as C-@ - it('CTRL-SPC inserts last-inserted text, leaves insert-mode', function() - feed('ix') - expect('hellhellhello') - end) - it('CTRL-A inserts last inserted text', function() - feed('ix') - expect('hellhellhellhelloxo') - end) -end) diff --git a/test/functional/terminal/tui_spec.lua b/test/functional/terminal/tui_spec.lua index 171745eb57..2f9abfd3f6 100644 --- a/test/functional/terminal/tui_spec.lua +++ b/test/functional/terminal/tui_spec.lua @@ -115,16 +115,12 @@ describe('tui', function() ]]) end) - it('does not mangle unmapped ALT-key chord', function() - -- Vim represents ALT/META by setting the "high bit" of the modified key; - -- we do _not_. #3982 - -- - -- Example: for input ALT+j: - -- * Vim (Nvim prior to #3982) sets high-bit, inserts "ê". - -- * Nvim (after #3982) inserts "j". - feed_data('i\027j') + it('interprets ESC+key as ALT chord', function() + -- Vim represents ALT/META by setting the "high bit" of the modified key: + -- ALT+j inserts "ê". Nvim does not (#3982). + feed_data('i\022\027j') screen:expect([[ - j{1: } | + {1: } | {4:~ }| {4:~ }| {4:~ }| -- cgit From fa6415f13f526220237cff6c6f0055aaad2cdd9e Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sun, 8 Apr 2018 03:01:15 +0200 Subject: test/API: validate channel arg (#8245) --- test/functional/api/ui_spec.lua | 39 ++++++++++++++++++++++++++++++++ test/functional/ui/screen_basic_spec.lua | 19 ---------------- 2 files changed, 39 insertions(+), 19 deletions(-) create mode 100644 test/functional/api/ui_spec.lua (limited to 'test') diff --git a/test/functional/api/ui_spec.lua b/test/functional/api/ui_spec.lua new file mode 100644 index 0000000000..32aae5d8f2 --- /dev/null +++ b/test/functional/api/ui_spec.lua @@ -0,0 +1,39 @@ +local helpers = require('test.functional.helpers')(after_each) +local Screen = require('test.functional.ui.screen') +local clear = helpers.clear +local eq = helpers.eq +local eval = helpers.eval +local request = helpers.request + +describe('nvim_ui_attach()', function() + before_each(function() + clear() + end) + it('handles very large width/height #2180', function() + local screen = Screen.new(999, 999) + screen:attach() + eq(999, eval('&lines')) + eq(999, eval('&columns')) + end) + it('invalid option returns error', function() + local screen = Screen.new() + local status, rv = pcall(function() screen:attach({foo={'foo'}}) end) + eq(false, status) + eq('No such UI option', rv:match("No such .*")) + end) + it('validates channel arg', function() + assert.has_error(function() request('nvim_ui_try_resize', 40, 10) end, + 'UI not attached to channel: 1') + assert.has_error(function() request('nvim_ui_set_option', 'rgb', true) end, + 'UI not attached to channel: 1') + assert.has_error(function() request('nvim_ui_detach') end, + 'UI not attached to channel: 1') + + local screen = Screen.new() + screen:attach({rgb=false}) + assert.has_error(function() + request('nvim_ui_attach', 40, 10, { rgb=false }) + end, + 'UI already attached to channel: 1') + end) +end) diff --git a/test/functional/ui/screen_basic_spec.lua b/test/functional/ui/screen_basic_spec.lua index 563d04416a..8a1f9b0d19 100644 --- a/test/functional/ui/screen_basic_spec.lua +++ b/test/functional/ui/screen_basic_spec.lua @@ -4,7 +4,6 @@ local spawn, set_session, clear = helpers.spawn, helpers.set_session, helpers.cl local feed, command = helpers.feed, helpers.command local insert = helpers.insert local eq = helpers.eq -local eval = helpers.eval local iswin = helpers.iswin describe('screen', function() @@ -631,21 +630,3 @@ describe('Screen', function() end) end) end) - -describe('nvim_ui_attach()', function() - before_each(function() - clear() - end) - it('handles very large width/height #2180', function() - local screen = Screen.new(999, 999) - screen:attach() - eq(999, eval('&lines')) - eq(999, eval('&columns')) - end) - it('invalid option returns error', function() - local screen = Screen.new() - local status, rv = pcall(function() screen:attach({foo={'foo'}}) end) - eq(false, status) - eq('No such ui option', rv:match("No such .*")) - end) -end) -- cgit From b11b681289582472340e3af72ea116c4ba354066 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Wed, 11 Apr 2018 01:43:15 +0200 Subject: test/util: matches() --- test/functional/helpers.lua | 2 ++ test/helpers.lua | 7 +++++++ 2 files changed, 9 insertions(+) (limited to 'test') diff --git a/test/functional/helpers.lua b/test/functional/helpers.lua index b8d912114d..84ba6b6853 100644 --- a/test/functional/helpers.lua +++ b/test/functional/helpers.lua @@ -16,6 +16,7 @@ local neq = global_helpers.neq local eq = global_helpers.eq local ok = global_helpers.ok local map = global_helpers.map +local matches = global_helpers.matches local filter = global_helpers.filter local dedent = global_helpers.dedent local table_flatten = global_helpers.table_flatten @@ -747,6 +748,7 @@ local module = { insert = insert, iswin = iswin, map = map, + matches = matches, merge_args = merge_args, meth_pcall = meth_pcall, meths = meths, diff --git a/test/helpers.lua b/test/helpers.lua index 7cce6d8c55..0b6a10a29a 100644 --- a/test/helpers.lua +++ b/test/helpers.lua @@ -16,6 +16,12 @@ end local function ok(res) return assert.is_true(res) end +local function matches(pat, actual) + if nil ~= string.match(actual, pat) then + return true + end + error(string.format('Pattern does not match.\nPattern:\n%s\nActual:\n%s', pat, actual)) +end -- initial_path: directory to recurse into -- re: include pattern (string) @@ -572,6 +578,7 @@ return { hasenv = hasenv, intchar2lua = intchar2lua, map = map, + matches = matches, mergedicts_copy = mergedicts_copy, neq = neq, ok = ok, -- cgit From 9f598e57656fe072114b9f1e73d3051423e13234 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sun, 8 Apr 2018 13:06:26 +0200 Subject: serverstop(): return FALSE for invalid address --- test/functional/eval/server_spec.lua | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'test') diff --git a/test/functional/eval/server_spec.lua b/test/functional/eval/server_spec.lua index 393616838e..edd63fe83c 100644 --- a/test/functional/eval/server_spec.lua +++ b/test/functional/eval/server_spec.lua @@ -11,21 +11,21 @@ local function clear_serverlist() end end -describe('serverstart(), serverstop()', function() +describe('server', function() before_each(clear) - it('sets $NVIM_LISTEN_ADDRESS on first invocation', function() + it('serverstart() sets $NVIM_LISTEN_ADDRESS on first invocation', function() -- Unset $NVIM_LISTEN_ADDRESS command('let $NVIM_LISTEN_ADDRESS = ""') local s = eval('serverstart()') assert(s ~= nil and s:len() > 0, "serverstart() returned empty") eq(s, eval('$NVIM_LISTEN_ADDRESS')) - command("call serverstop('"..s.."')") + eq(1, eval("serverstop('"..s.."')")) eq('', eval('$NVIM_LISTEN_ADDRESS')) end) - it('sets v:servername _only_ on nvim startup unless all servers are stopped', + it('serverstart() sets v:servername at startup or if all servers were stopped', function() local initial_server = meths.get_vvar('servername') assert(initial_server ~= nil and initial_server:len() > 0, @@ -38,11 +38,11 @@ describe('serverstart(), serverstop()', function() neq(initial_server, s) -- serverstop() does _not_ modify v:servername... - funcs.serverstop(s) + eq(1, funcs.serverstop(s)) eq(initial_server, meths.get_vvar('servername')) -- ...unless we stop _all_ servers. - funcs.serverstop(funcs.serverlist()[1]) + eq(1, funcs.serverstop(funcs.serverlist()[1])) eq('', meths.get_vvar('servername')) -- v:servername will take the next available server. @@ -53,9 +53,9 @@ describe('serverstart(), serverstop()', function() eq(servername, meths.get_vvar('servername')) end) - it('serverstop() ignores invalid input', function() - command("call serverstop('')") - command("call serverstop('bogus-socket-name')") + it('serverstop() returns false for invalid input', function() + eq(0, eval("serverstop('')")) + eq(0, eval("serverstop('bogus-socket-name')")) end) it('parses endpoints correctly', function() @@ -120,7 +120,7 @@ describe('serverlist()', function() -- The new servers should be at the end of the list. for i = 1, #servs do eq(servs[i], new_servs[i + n]) - command("call serverstop('"..servs[i].."')") + eq(1, eval("serverstop('"..servs[i].."')")) end -- After serverstop() the servers should NOT be in the list. eq(n, eval('len(serverlist())')) -- cgit From 507bda1c95cdac2886f64e59aa6bbf85c3fac389 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sun, 8 Apr 2018 17:20:25 +0200 Subject: server: introduce --listen, deprecate $NVIM_LISTEN_ADDRESS --- test/functional/eval/server_spec.lua | 38 ++++++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 10 deletions(-) (limited to 'test') diff --git a/test/functional/eval/server_spec.lua b/test/functional/eval/server_spec.lua index edd63fe83c..a2644498f2 100644 --- a/test/functional/eval/server_spec.lua +++ b/test/functional/eval/server_spec.lua @@ -1,14 +1,14 @@ - local helpers = require('test.functional.helpers')(after_each) local eq, neq, eval = helpers.eq, helpers.neq, helpers.eval local command = helpers.command local clear, funcs, meths = helpers.clear, helpers.funcs, helpers.meths +local ok = helpers.ok local os_name = helpers.os_name local function clear_serverlist() - for _, server in pairs(funcs.serverlist()) do - funcs.serverstop(server) - end + for _, server in pairs(funcs.serverlist()) do + funcs.serverstop(server) + end end describe('server', function() @@ -96,16 +96,12 @@ describe('server', function() funcs.serverstart('127.0.0.1:65536') -- invalid port eq({}, funcs.serverlist()) end) -end) -describe('serverlist()', function() - before_each(clear) - - it('returns the list of servers', function() + it('serverlist() returns the list of servers', function() -- There should already be at least one server. local n = eval('len(serverlist())') - -- Add a few + -- Add some servers. local servs = (os_name() == 'windows' and { [[\\.\pipe\Xtest-pipe0934]], [[\\.\pipe\Xtest-pipe4324]] } or { [[Xtest-pipe0934]], [[Xtest-pipe4324]] }) @@ -126,3 +122,25 @@ describe('serverlist()', function() eq(n, eval('len(serverlist())')) end) end) + +describe('startup --listen', function() + it('validates', function() + clear() + + local cmd = { unpack(helpers.nvim_argv) } + table.insert(cmd, '--listen') + eq('nvim: Argument missing after: "--listen"', + string.match(funcs.system(cmd), '.-n"')) + + cmd = { unpack(helpers.nvim_argv) } + table.insert(cmd, '--listen2') + eq('nvim: Garbage after option argument: "--listen2"', + string.match(funcs.system(cmd), '.-2"')) + end) + + it('sets v:servername, overrides $NVIM_LISTEN_ADDRESS', function() + clear({ env={ NVIM_LISTEN_ADDRESS='Xtest-env-pipe' }, + args={ '--listen', 'Xtest-listen-pipe' } }) + eq('Xtest-listen-pipe', meths.get_vvar('servername')) + end) +end) -- cgit From 704ba4151e7f67999510ee0ac19fdabb595d530c Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Wed, 11 Apr 2018 00:01:43 +0200 Subject: server: init v:servername if $NVIM_LISTEN_ADDRESS is invalid Before this change, if $NVIM_LISTEN_ADDRESS was invalid, v:servername was left empty. --- test/functional/eval/server_spec.lua | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) (limited to 'test') diff --git a/test/functional/eval/server_spec.lua b/test/functional/eval/server_spec.lua index a2644498f2..4e4aed864b 100644 --- a/test/functional/eval/server_spec.lua +++ b/test/functional/eval/server_spec.lua @@ -2,8 +2,9 @@ local helpers = require('test.functional.helpers')(after_each) local eq, neq, eval = helpers.eq, helpers.neq, helpers.eval local command = helpers.command local clear, funcs, meths = helpers.clear, helpers.funcs, helpers.meths +local iswin = helpers.iswin local ok = helpers.ok -local os_name = helpers.os_name +local matches = helpers.matches local function clear_serverlist() for _, server in pairs(funcs.serverlist()) do @@ -25,7 +26,15 @@ describe('server', function() eq('', eval('$NVIM_LISTEN_ADDRESS')) end) - it('serverstart() sets v:servername at startup or if all servers were stopped', + it('sets new v:servername if $NVIM_LISTEN_ADDRESS is invalid', function() + clear({env={NVIM_LISTEN_ADDRESS='.'}}) + eq('.', eval('$NVIM_LISTEN_ADDRESS')) + local servers = funcs.serverlist() + eq(1, #servers) + ok(string.len(servers[1]) > 4) -- Like /tmp/nvim…/… or \\.\pipe\… + end) + + it('sets v:servername at startup or if all servers were stopped', function() local initial_server = meths.get_vvar('servername') assert(initial_server ~= nil and initial_server:len() > 0, @@ -46,9 +55,8 @@ describe('server', function() eq('', meths.get_vvar('servername')) -- v:servername will take the next available server. - local servername = (os_name() == 'windows' - and [[\\.\pipe\Xtest-functional-server-pipe]] - or 'Xtest-functional-server-socket') + local servername = (iswin() and [[\\.\pipe\Xtest-functional-server-pipe]] + or 'Xtest-functional-server-socket') funcs.serverstart(servername) eq(servername, meths.get_vvar('servername')) end) @@ -102,7 +110,7 @@ describe('server', function() local n = eval('len(serverlist())') -- Add some servers. - local servs = (os_name() == 'windows' + local servs = (iswin() and { [[\\.\pipe\Xtest-pipe0934]], [[\\.\pipe\Xtest-pipe4324]] } or { [[Xtest-pipe0934]], [[Xtest-pipe4324]] }) for _, s in ipairs(servs) do @@ -129,18 +137,18 @@ describe('startup --listen', function() local cmd = { unpack(helpers.nvim_argv) } table.insert(cmd, '--listen') - eq('nvim: Argument missing after: "--listen"', - string.match(funcs.system(cmd), '.-n"')) + matches('nvim.*: Argument missing after: "%-%-listen"', funcs.system(cmd)) cmd = { unpack(helpers.nvim_argv) } table.insert(cmd, '--listen2') - eq('nvim: Garbage after option argument: "--listen2"', - string.match(funcs.system(cmd), '.-2"')) + matches('nvim.*: Garbage after option argument: "%-%-listen2"', funcs.system(cmd)) end) it('sets v:servername, overrides $NVIM_LISTEN_ADDRESS', function() + local addr = (iswin() and [[\\.\pipe\Xtest-listen-pipe]] + or 'Xtest-listen-pipe') clear({ env={ NVIM_LISTEN_ADDRESS='Xtest-env-pipe' }, - args={ '--listen', 'Xtest-listen-pipe' } }) - eq('Xtest-listen-pipe', meths.get_vvar('servername')) + args={ '--listen', addr } }) + eq(addr, meths.get_vvar('servername')) end) end) -- cgit From 87f4d2592c2ba164683b1f0f813cf9f029a0abfb Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Wed, 11 Apr 2018 22:07:00 +0200 Subject: test/util: expect_err() (#8257) other cleanup, ref #8245 --- test/functional/api/ui_spec.lua | 26 ++++++++++++-------------- test/functional/helpers.lua | 2 ++ test/functional/ui/input_spec.lua | 4 ++++ test/helpers.lua | 8 ++++++++ 4 files changed, 26 insertions(+), 14 deletions(-) (limited to 'test') diff --git a/test/functional/api/ui_spec.lua b/test/functional/api/ui_spec.lua index 32aae5d8f2..b028a50b02 100644 --- a/test/functional/api/ui_spec.lua +++ b/test/functional/api/ui_spec.lua @@ -3,6 +3,8 @@ local Screen = require('test.functional.ui.screen') local clear = helpers.clear local eq = helpers.eq local eval = helpers.eval +local expect_err = helpers.expect_err +local meths = helpers.meths local request = helpers.request describe('nvim_ui_attach()', function() @@ -16,24 +18,20 @@ describe('nvim_ui_attach()', function() eq(999, eval('&columns')) end) it('invalid option returns error', function() - local screen = Screen.new() - local status, rv = pcall(function() screen:attach({foo={'foo'}}) end) - eq(false, status) - eq('No such UI option', rv:match("No such .*")) + expect_err('No such UI option: foo', + meths.ui_attach, 80, 24, { foo={'foo'} }) end) it('validates channel arg', function() - assert.has_error(function() request('nvim_ui_try_resize', 40, 10) end, - 'UI not attached to channel: 1') - assert.has_error(function() request('nvim_ui_set_option', 'rgb', true) end, - 'UI not attached to channel: 1') - assert.has_error(function() request('nvim_ui_detach') end, - 'UI not attached to channel: 1') + expect_err('UI not attached to channel: 1', + request, 'nvim_ui_try_resize', 40, 10) + expect_err('UI not attached to channel: 1', + request, 'nvim_ui_set_option', 'rgb', true) + expect_err('UI not attached to channel: 1', + request, 'nvim_ui_detach') local screen = Screen.new() screen:attach({rgb=false}) - assert.has_error(function() - request('nvim_ui_attach', 40, 10, { rgb=false }) - end, - 'UI already attached to channel: 1') + expect_err('UI already attached to channel: 1', + request, 'nvim_ui_attach', 40, 10, { rgb=false }) end) end) diff --git a/test/functional/helpers.lua b/test/functional/helpers.lua index 84ba6b6853..bf11042dd6 100644 --- a/test/functional/helpers.lua +++ b/test/functional/helpers.lua @@ -14,6 +14,7 @@ local check_cores = global_helpers.check_cores local check_logs = global_helpers.check_logs local neq = global_helpers.neq local eq = global_helpers.eq +local expect_err = global_helpers.expect_err local ok = global_helpers.ok local map = global_helpers.map local matches = global_helpers.matches @@ -737,6 +738,7 @@ local module = { exc_exec = exc_exec, expect = expect, expect_any = expect_any, + expect_err = expect_err, expect_msg_seq = expect_msg_seq, expect_twostreams = expect_twostreams, feed = feed, diff --git a/test/functional/ui/input_spec.lua b/test/functional/ui/input_spec.lua index 9bfc87d835..3dd9a2506e 100644 --- a/test/functional/ui/input_spec.lua +++ b/test/functional/ui/input_spec.lua @@ -128,6 +128,10 @@ describe('input utf sequences that contain CSI/K_SPECIAL', function() end) describe('input non-printable chars', function() + after_each(function() + os.remove('Xtest-overwrite') + end) + it("doesn't crash when echoing them back", function() write_file("Xtest-overwrite", [[foobar]]) clear() diff --git a/test/helpers.lua b/test/helpers.lua index 0b6a10a29a..e0645d083f 100644 --- a/test/helpers.lua +++ b/test/helpers.lua @@ -22,6 +22,13 @@ local function matches(pat, actual) end error(string.format('Pattern does not match.\nPattern:\n%s\nActual:\n%s', pat, actual)) end +-- Expect an error matching pattern `pat`. +local function expect_err(pat, ...) + local fn = select(1, ...) + local fn_args = {...} + table.remove(fn_args, 1) + assert.error_matches(function() return fn(unpack(fn_args)) end, pat) +end -- initial_path: directory to recurse into -- re: include pattern (string) @@ -569,6 +576,7 @@ return { deepcopy = deepcopy, dictdiff = dictdiff, eq = eq, + expect_err = expect_err, filter = filter, fixtbl = fixtbl, fixtbl_rec = fixtbl_rec, -- cgit From aea079a25df1df5c94e97adf3c92ead397168e27 Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Sun, 7 Jan 2018 10:16:39 +0100 Subject: channels: delay free so that libuv can cleanup handles add test for a crash this caused --- test/functional/core/job_spec.lua | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'test') diff --git a/test/functional/core/job_spec.lua b/test/functional/core/job_spec.lua index 6d4cadbdc8..24bff423df 100644 --- a/test/functional/core/job_spec.lua +++ b/test/functional/core/job_spec.lua @@ -640,6 +640,21 @@ describe('jobs', function() ok(string.find(err, "E475: Invalid argument: job cannot have both 'pty' and 'rpc' options set") ~= nil) end) + it('does not crash when repeatedly failing to start shell', function() + source([[ + set shell=nosuchshell + func! DoIt() + call jobstart('true') + call jobstart('true') + endfunc + ]]) + -- The crash only triggered if both jobs are cleaned up on the same event + -- loop tick. This is also prevented by try-block, so feed must be used. + feed_command("call DoIt()") + feed('') -- press RETURN + eq(2,eval('1+1')) + end) + it('jobstop() kills entire process tree #6530', function() command('set shell& shellcmdflag& shellquote& shellpipe& shellredir& shellxquote&') -- cgit From 1c3a849881f45e2acb08ce99e5b33dd753f61d5d Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Fri, 13 Apr 2018 00:49:37 +0200 Subject: API/nvim_command_output: handle :echon capture (#8265) ref https://github.com/neovim/python-client/pull/290 --- test/functional/api/vim_spec.lua | 2 ++ 1 file changed, 2 insertions(+) (limited to 'test') diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua index 7ac20a99af..718294d941 100644 --- a/test/functional/api/vim_spec.lua +++ b/test/functional/api/vim_spec.lua @@ -73,6 +73,8 @@ describe('api', function() it('captures command output', function() eq('this is\nspinal tap', nvim('command_output', [[echo "this is\nspinal tap"]])) + eq('no line ending!', + nvim('command_output', [[echon "no line ending!"]])) end) it('captures empty command output', function() -- cgit