local t = require('test.testutil') local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') local t_shada = require('test.functional.shada.testutil') local clear = n.clear local eq, api, nvim_eval, nvim_command, exc_exec, fn, nvim_feed = t.eq, n.api, n.eval, n.command, n.exc_exec, n.fn, n.feed local neq = t.neq local read_file = t.read_file local get_shada_rw = t_shada.get_shada_rw local function reset(shada_file) clear { args = { '-u', 'NORC', '-i', shada_file or 'NONE' } } end local mpack_eq = function(expected, mpack_result) local mpack_keys = { 'type', 'timestamp', 'length', 'value' } local unpack = vim.mpack.Unpacker() local actual = {} local cur, val local i = 0 local off = 1 while off <= #mpack_result do val, off = unpack(mpack_result, off) if i % 4 == 0 then cur = {} actual[#actual + 1] = cur end local key = mpack_keys[(i % 4) + 1] if key ~= 'length' then if key == 'timestamp' and math.abs(val - os.time()) < 2 then val = 'current' end cur[key] = val end i = i + 1 end eq(expected, actual) end 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') describe('autoload/shada.vim', function() local epoch = os.date('%Y-%m-%dT%H:%M:%S', 0) before_each(function() reset() nvim_command([[ function ModifyVal(val) if type(a:val) == type([]) if len(a:val) == 2 && type(a:val[0]) == type('') && a:val[0][0] is# '!' && has_key(v:msgpack_types, a:val[0][1:]) return {'_TYPE': v:msgpack_types[ a:val[0][1:] ], '_VAL': a:val[1]} else return map(copy(a:val), 'ModifyVal(v:val)') endif elseif type(a:val) == type({}) let keys = sort(keys(a:val)) let ret = {'_TYPE': v:msgpack_types.map, '_VAL': []} for key in keys let k = {'_TYPE': v:msgpack_types.string, '_VAL': split(key, "\n", 1)} let v = ModifyVal(a:val[key]) call add(ret._VAL, [k, v]) unlet v endfor return ret elseif type(a:val) == type('') return {'_TYPE': v:msgpack_types.string, '_VAL': split(a:val, "\n", 1)} else return a:val endif endfunction ]]) end) local sp = function(typ, val) return ('{"_TYPE": v:msgpack_types.%s, "_VAL": %s}'):format(typ, val) end describe('function shada#mpack_to_sd', function() local mpack2sd = function(arg) return ('shada#mpack_to_sd(%s)'):format(arg) end it('works', function() eq({}, nvim_eval(mpack2sd('[]'))) eq({ { type = 1, timestamp = 5, length = 1, data = 7 } }, nvim_eval(mpack2sd('[1, 5, 1, 7]'))) eq({ { type = 1, timestamp = 5, length = 1, data = 7 }, { type = 1, timestamp = 10, length = 1, data = 5 }, }, nvim_eval(mpack2sd('[1, 5, 1, 7, 1, 10, 1, 5]'))) eq( 'zero-uint:Entry 1 has type element which is zero', exc_exec('call ' .. mpack2sd('[0, 5, 1, 7]')) ) eq( 'zero-uint:Entry 1 has type element which is zero', exc_exec('call ' .. mpack2sd(('[%s, 5, 1, 7]'):format(sp('integer', '[1, 0, 0, 0]')))) ) eq( 'not-uint:Entry 1 has timestamp element which is not an unsigned integer', exc_exec('call ' .. mpack2sd('[1, -1, 1, 7]')) ) eq( 'not-uint:Entry 1 has length element which is not an unsigned integer', exc_exec('call ' .. mpack2sd('[1, 1, -1, 7]')) ) eq( 'not-uint:Entry 1 has type element which is not an unsigned integer', exc_exec('call ' .. mpack2sd('["", 1, -1, 7]')) ) end) end) describe('function shada#sd_to_strings', function() local sd2strings_eq = function(expected, arg) if type(arg) == 'table' then eq(expected, fn['shada#sd_to_strings'](arg)) else eq(expected, nvim_eval(('shada#sd_to_strings(%s)'):format(arg))) end end it('works with empty input', function() sd2strings_eq({}, '[]') end) it('works with unknown items', function() sd2strings_eq({ 'Unknown (0x64) with timestamp ' .. epoch .. ':', ' = 100', }, { { type = 100, timestamp = 0, length = 1, data = 100 } }) sd2strings_eq( { 'Unknown (0x4000001180000006) with timestamp ' .. epoch .. ':', ' = 100', }, ('[{"type": %s, "timestamp": 0, "length": 1, "data": 100}]'):format( sp('integer', '[1, 1, 35, 6]') ) ) end) it('works with multiple unknown items', function() sd2strings_eq({ 'Unknown (0x64) with timestamp ' .. epoch .. ':', ' = 100', 'Unknown (0x65) with timestamp ' .. epoch .. ':', ' = 500', }, { { type = 100, timestamp = 0, length = 1, data = 100 }, { type = 101, timestamp = 0, length = 1, data = 500 }, }) end) it('works with header items', function() sd2strings_eq({ 'Header with timestamp ' .. epoch .. ':', ' % Key______ Value', ' + generator "test"', }, { { type = 1, timestamp = 0, data = { generator = 'test' } } }) sd2strings_eq({ 'Header with timestamp ' .. epoch .. ':', ' % Key Description Value', ' + a 1', ' + b 2', ' + c column 3', ' + d 4', }, { { type = 1, timestamp = 0, data = { a = 1, b = 2, c = 3, d = 4 } } }) sd2strings_eq({ 'Header with timestamp ' .. epoch .. ':', ' % Key Value', ' + t "test"', }, { { type = 1, timestamp = 0, data = { t = 'test' } } }) sd2strings_eq({ 'Header with timestamp ' .. epoch .. ':', ' # Unexpected type: array instead of map', ' = [1, 2, 3]', }, { { type = 1, timestamp = 0, data = { 1, 2, 3 } } }) end) it('processes standard keys correctly, even in header', function() sd2strings_eq( { 'Header with timestamp ' .. epoch .. ':', ' % Key Description________ Value', ' + c column 0', ' + f file name "/tmp/foo"', ' + l line number 10', " + n name '@'", ' + rc contents ["abc", "def"]', ' + rt type CHARACTERWISE', ' + ru is_unnamed FALSE', ' + rw block width 10', ' + sb search backward TRUE', ' + sc smartcase value FALSE', ' + se place cursor at end TRUE', ' + sh v:hlsearch value TRUE', ' + sl has line offset FALSE', ' + sm magic value TRUE', ' + so offset value 10', ' + sp pattern "100"', ' + ss is :s pattern TRUE', ' + su is last used FALSE', }, ([[ [{'type': 1, 'timestamp': 0, 'data': { 'sm': {'_TYPE': v:msgpack_types.boolean, '_VAL': 1}, 'sc': {'_TYPE': v:msgpack_types.boolean, '_VAL': 0}, 'sl': {'_TYPE': v:msgpack_types.boolean, '_VAL': 0}, 'se': {'_TYPE': v:msgpack_types.boolean, '_VAL': 1}, 'sb': {'_TYPE': v:msgpack_types.boolean, '_VAL': 1}, 'so': 10, 'su': {'_TYPE': v:msgpack_types.boolean, '_VAL': 0}, 'ss': {'_TYPE': v:msgpack_types.boolean, '_VAL': 1}, 'sh': {'_TYPE': v:msgpack_types.boolean, '_VAL': 1}, 'sp': '100', 'rt': 0, 'rw': 10, 'rc': ['abc', 'def'], 'ru': {'_TYPE': v:msgpack_types.boolean, '_VAL': 0}, 'n': 0x40, 'l': 10, 'c': 0, 'f': '/tmp/foo', }}] ]]):gsub('\n', '') ) sd2strings_eq( { 'Header with timestamp ' .. epoch .. ':', ' % Key Description____ Value', ' # Expected integer', ' + c column "abc"', ' # Expected no NUL bytes', ' + f file name "abc\\0def"', ' # Value is negative', ' + l line number -10', ' # Value is negative', ' + n name -64', ' # Expected array value', ' + rc contents "10"', ' # Unexpected enum value: expected one of ' .. '0 (CHARACTERWISE), 1 (LINEWISE), 2 (BLOCKWISE)', ' + rt type 10', ' # Expected boolean', ' + ru is_unnamed 10', ' # Expected boolean', ' + sc smartcase value NIL', ' # Expected boolean', ' + sm magic value "TRUE"', ' # Expected integer', ' + so offset value "TRUE"', ' + sp pattern "abc"', }, ([[ [{'type': 1, 'timestamp': 0, 'data': { 'sm': 'TRUE', 'sc': {'_TYPE': v:msgpack_types.nil, '_VAL': 0}, 'so': 'TRUE', 'sp': {'_TYPE': v:msgpack_types.string, '_VAL': ["abc"]}, 'rt': 10, 'rc': '10', 'ru': 10, 'n': -0x40, 'l': -10, 'c': 'abc', 'f': {'_TYPE': v:msgpack_types.string, '_VAL': ["abc\ndef"]}, }}] ]]):gsub('\n', '') ) sd2strings_eq( { 'Header with timestamp ' .. epoch .. ':', ' % Key Description Value', ' # Expected no NUL bytes', ' + f file name "abc\\0def"', ' + rc contents ["abc", "abc"]', ' # Expected integer', ' + rt type "ABC"', }, ([[ [{'type': 1, 'timestamp': 0, 'data': { 'rt': 'ABC', 'rc': ["abc", {'_TYPE': v:msgpack_types.string, '_VAL': ["abc"]}], 'f': {'_TYPE': v:msgpack_types.string, '_VAL': ["abc\ndef"]}, }}] ]]):gsub('\n', '') ) sd2strings_eq( { 'Header with timestamp ' .. epoch .. ':', ' % Key Description Value', ' # Expected no NUL bytes', ' + rc contents ["abc", "a\\nd\\0"]', }, ([[ [{'type': 1, 'timestamp': 0, 'data': { 'rc': ["abc", {'_TYPE': v:msgpack_types.string, '_VAL': ["a", "d\n"]}], }}] ]]):gsub('\n', '') ) end) it('works with search pattern items', function() sd2strings_eq({ 'Search pattern with timestamp ' .. epoch .. ':', ' # Unexpected type: array instead of map', ' = [1, 2, 3]', }, { { type = 2, timestamp = 0, data = { 1, 2, 3 } } }) sd2strings_eq( { 'Search pattern with timestamp ' .. epoch .. ':', ' % Key Description________ Value', ' + sp pattern "abc"', ' + sh v:hlsearch value FALSE', ' + ss is :s pattern FALSE', ' + sb search backward FALSE', ' + sm magic value TRUE', ' + sc smartcase value FALSE', ' + sl has line offset FALSE', ' + se place cursor at end FALSE', ' + so offset value 0', ' + su is last used TRUE', }, ([[ [{'type': 2, 'timestamp': 0, 'data': { 'sp': 'abc', }}] ]]):gsub('\n', '') ) sd2strings_eq( { 'Search pattern with timestamp ' .. epoch .. ':', ' % Key Description________ Value', ' + sp pattern "abc"', ' + sh v:hlsearch value FALSE', ' + ss is :s pattern FALSE', ' + sb search backward FALSE', ' + sm magic value TRUE', ' + sc smartcase value FALSE', ' + sl has line offset FALSE', ' + se place cursor at end FALSE', ' + so offset value 0', ' + su is last used TRUE', ' + sX NIL', ' + sY NIL', ' + sZ NIL', }, ([[ [{'type': 2, 'timestamp': 0, 'data': { 'sp': 'abc', 'sZ': {'_TYPE': v:msgpack_types.nil, '_VAL': 0}, 'sY': {'_TYPE': v:msgpack_types.nil, '_VAL': 0}, 'sX': {'_TYPE': v:msgpack_types.nil, '_VAL': 0}, }}] ]]):gsub('\n', '') ) sd2strings_eq( { 'Search pattern with timestamp ' .. epoch .. ':', ' % Key Description________ Value', ' + sp pattern "abc"', ' + sh v:hlsearch value FALSE', ' + ss is :s pattern FALSE', ' + sb search backward FALSE', ' + sm magic value TRUE', ' + sc smartcase value FALSE', ' + sl has line offset FALSE', ' + se place cursor at end FALSE', ' + so offset value 0', ' + su is last used TRUE', }, ([[ [{'type': 2, 'timestamp': 0, 'data': { 'sp': 'abc', 'sh': {'_TYPE': v:msgpack_types.boolean, '_VAL': 0}, 'ss': {'_TYPE': v:msgpack_types.boolean, '_VAL': 0}, 'sb': {'_TYPE': v:msgpack_types.boolean, '_VAL': 0}, 'sm': {'_TYPE': v:msgpack_types.boolean, '_VAL': 1}, 'sc': {'_TYPE': v:msgpack_types.boolean, '_VAL': 0}, 'sl': {'_TYPE': v:msgpack_types.boolean, '_VAL': 0}, 'se': {'_TYPE': v:msgpack_types.boolean, '_VAL': 0}, 'so': 0, 'su': {'_TYPE': v:msgpack_types.boolean, '_VAL': 1}, }}] ]]):gsub('\n', '') ) sd2strings_eq( { 'Search pattern with timestamp ' .. epoch .. ':', ' % Key Description________ Value', ' # Required key missing: sp', ' + sh v:hlsearch value FALSE', ' + ss is :s pattern FALSE', ' + sb search backward FALSE', ' + sm magic value TRUE', ' + sc smartcase value FALSE', ' + sl has line offset FALSE', ' + se place cursor at end FALSE', ' + so offset value 0', ' + su is last used TRUE', }, ([[ [{'type': 2, 'timestamp': 0, 'data': { }}] ]]):gsub('\n', '') ) sd2strings_eq( { 'Search pattern with timestamp ' .. epoch .. ':', ' % Key Description________ Value', ' + sp pattern ""', ' + sh v:hlsearch value TRUE', ' + ss is :s pattern TRUE', ' + sb search backward TRUE', ' + sm magic value FALSE', ' + sc smartcase value TRUE', ' + sl has line offset TRUE', ' + se place cursor at end TRUE', ' + so offset value -10', ' + su is last used FALSE', }, ([[ [{'type': 2, 'timestamp': 0, 'data': { 'sp': '', 'sh': {'_TYPE': v:msgpack_types.boolean, '_VAL': 1}, 'ss': {'_TYPE': v:msgpack_types.boolean, '_VAL': 1}, 'sb': {'_TYPE': v:msgpack_types.boolean, '_VAL': 1}, 'sm': {'_TYPE': v:msgpack_types.boolean, '_VAL': 0}, 'sc': {'_TYPE': v:msgpack_types.boolean, '_VAL': 1}, 'sl': {'_TYPE': v:msgpack_types.boolean, '_VAL': 1}, 'se': {'_TYPE': v:msgpack_types.boolean, '_VAL': 1}, 'so': -10, 'su': {'_TYPE': v:msgpack_types.boolean, '_VAL': 0}, }}] ]]):gsub('\n', '') ) sd2strings_eq( { 'Search pattern with timestamp ' .. epoch .. ':', ' % Key Description________ Value', ' # Expected binary string', ' + sp pattern 0', ' # Expected boolean', ' + sh v:hlsearch value 0', ' # Expected boolean', ' + ss is :s pattern 0', ' # Expected boolean', ' + sb search backward 0', ' # Expected boolean', ' + sm magic value 0', ' # Expected boolean', ' + sc smartcase value 0', ' # Expected boolean', ' + sl has line offset 0', ' # Expected boolean', ' + se place cursor at end 0', ' # Expected integer', ' + so offset value ""', ' # Expected boolean', ' + su is last used 0', }, ([[ [{'type': 2, 'timestamp': 0, 'data': { 'sp': 0, 'sh': 0, 'ss': 0, 'sb': 0, 'sm': 0, 'sc': 0, 'sl': 0, 'se': 0, 'so': '', 'su': 0, }}] ]]):gsub('\n', '') ) end) it('works with replacement string items', function() sd2strings_eq({ 'Replacement string with timestamp ' .. epoch .. ':', ' # Unexpected type: map instead of array', ' = {"a": [10]}', }, { { type = 3, timestamp = 0, data = { a = { 10 } } } }) sd2strings_eq( { 'Replacement string with timestamp ' .. epoch .. ':', ' @ Description__________ Value', ' # Expected more elements in list', }, ([[ [{'type': 3, 'timestamp': 0, 'data': [ ]}] ]]):gsub('\n', '') ) sd2strings_eq( { 'Replacement string with timestamp ' .. epoch .. ':', ' @ Description__________ Value', ' # Expected binary string', ' - :s replacement string 0', }, ([[ [{'type': 3, 'timestamp': 0, 'data': [ 0, ]}] ]]):gsub('\n', '') ) sd2strings_eq( { 'Replacement string with timestamp ' .. epoch .. ':', ' @ Description__________ Value', ' # Expected no NUL bytes', ' - :s replacement string "abc\\0def"', }, ([[ [{'type': 3, 'timestamp': 0, 'data': [ {'_TYPE': v:msgpack_types.string, '_VAL': ["abc\ndef"]}, ]}] ]]):gsub('\n', '') ) sd2strings_eq( { 'Replacement string with timestamp ' .. epoch .. ':', ' @ Description__________ Value', ' - :s replacement string "abc\\ndef"', }, ([[ [{'type': 3, 'timestamp': 0, 'data': [ {'_TYPE': v:msgpack_types.string, '_VAL': ["abc", "def"]}, ]}] ]]):gsub('\n', '') ) sd2strings_eq( { 'Replacement string with timestamp ' .. epoch .. ':', ' @ Description__________ Value', ' - :s replacement string "abc\\ndef"', ' - 0', }, ([[ [{'type': 3, 'timestamp': 0, 'data': [ {'_TYPE': v:msgpack_types.string, '_VAL': ["abc", "def"]}, 0, ]}] ]]):gsub('\n', '') ) end) it('works with history entry items', function() sd2strings_eq({ 'History entry with timestamp ' .. epoch .. ':', ' # Unexpected type: map instead of array', ' = {"a": [10]}', }, { { type = 4, timestamp = 0, data = { a = { 10 } } } }) sd2strings_eq( { 'History entry with timestamp ' .. epoch .. ':', ' @ Description_ Value', ' # Expected more elements in list', }, ([[ [{'type': 4, 'timestamp': 0, 'data': [ ]}] ]]):gsub('\n', '') ) sd2strings_eq( { 'History entry with timestamp ' .. epoch .. ':', ' @ Description_ Value', ' # Expected integer', ' - history type ""', ' # Expected more elements in list', }, ([[ [{'type': 4, 'timestamp': 0, 'data': [ '', ]}] ]]):gsub('\n', '') ) sd2strings_eq( { 'History entry with timestamp ' .. epoch .. ':', ' @ Description_ Value', ' # Unexpected enum value: expected one of 0 (CMD), 1 (SEARCH), ' .. '2 (EXPR), 3 (INPUT), 4 (DEBUG)', ' - history type 5', ' - contents ""', }, ([[ [{'type': 4, 'timestamp': 0, 'data': [ 5, '' ]}] ]]):gsub('\n', '') ) sd2strings_eq( { 'History entry with timestamp ' .. epoch .. ':', ' @ Description_ Value', ' # Unexpected enum value: expected one of 0 (CMD), 1 (SEARCH), ' .. '2 (EXPR), 3 (INPUT), 4 (DEBUG)', ' - history type 5', ' - contents ""', ' - 32', }, ([[ [{'type': 4, 'timestamp': 0, 'data': [ 5, '', 0x20 ]}] ]]):gsub('\n', '') ) sd2strings_eq( { 'History entry with timestamp ' .. epoch .. ':', ' @ Description_ Value', ' - history type CMD', ' - contents ""', ' - 32', }, ([[ [{'type': 4, 'timestamp': 0, 'data': [ 0, '', 0x20 ]}] ]]):gsub('\n', '') ) sd2strings_eq( { 'History entry with timestamp ' .. epoch .. ':', ' @ Description_ Value', ' - history type SEARCH', ' - contents ""', " - separator ' '", }, ([[ [{'type': 4, 'timestamp': 0, 'data': [ 1, '', 0x20 ]}] ]]):gsub('\n', '') ) sd2strings_eq( { 'History entry with timestamp ' .. epoch .. ':', ' @ Description_ Value', ' - history type SEARCH', ' - contents ""', ' # Expected more elements in list', }, ([[ [{'type': 4, 'timestamp': 0, 'data': [ 1, '', ]}] ]]):gsub('\n', '') ) sd2strings_eq( { 'History entry with timestamp ' .. epoch .. ':', ' @ Description_ Value', ' - history type EXPR', ' - contents ""', }, ([[ [{'type': 4, 'timestamp': 0, 'data': [ 2, '', ]}] ]]):gsub('\n', '') ) sd2strings_eq( { 'History entry with timestamp ' .. epoch .. ':', ' @ Description_ Value', ' - history type INPUT', ' - contents ""', }, ([[ [{'type': 4, 'timestamp': 0, 'data': [ 3, '', ]}] ]]):gsub('\n', '') ) sd2strings_eq( { 'History entry with timestamp ' .. epoch .. ':', ' @ Description_ Value', ' - history type DEBUG', ' - contents ""', }, ([[ [{'type': 4, 'timestamp': 0, 'data': [ 4, '', ]}] ]]):gsub('\n', '') ) sd2strings_eq( { 'History entry with timestamp ' .. epoch .. ':', ' @ Description_ Value', ' - history type DEBUG', ' # Expected binary string', ' - contents 10', }, ([[ [{'type': 4, 'timestamp': 0, 'data': [ 4, 10, ]}] ]]):gsub('\n', '') ) sd2strings_eq( { 'History entry with timestamp ' .. epoch .. ':', ' @ Description_ Value', ' - history type DEBUG', ' # Expected no NUL bytes', ' - contents "abc\\0def"', }, ([[ [{'type': 4, 'timestamp': 0, 'data': [ 4, {'_TYPE': v:msgpack_types.string, '_VAL': ["abc\ndef"]}, ]}] ]]):gsub('\n', '') ) sd2strings_eq( { 'History entry with timestamp ' .. epoch .. ':', ' @ Description_ Value', ' - history type SEARCH', ' - contents "abc"', ' # Expected integer', ' - separator ""', }, ([[ [{'type': 4, 'timestamp': 0, 'data': [ 1, 'abc', '', ]}] ]]):gsub('\n', '') ) sd2strings_eq( { 'History entry with timestamp ' .. epoch .. ':', ' @ Description_ Value', ' - history type SEARCH', ' - contents "abc"', ' # Value is negative', ' - separator -1', }, ([[ [{'type': 4, 'timestamp': 0, 'data': [ 1, 'abc', -1, ]}] ]]):gsub('\n', '') ) -- Regression: NUL separator must be properly supported sd2strings_eq( { 'History entry with timestamp ' .. epoch .. ':', ' @ Description_ Value', ' - history type SEARCH', ' - contents ""', " - separator '\\0'", }, ([[ [{'type': 4, 'timestamp': 0, 'data': [ 1, '', 0x0 ]}] ]]):gsub('\n', '') ) end) it('works with register items', function() sd2strings_eq({ 'Register with timestamp ' .. epoch .. ':', ' # Unexpected type: array instead of map', ' = [1, 2, 3]', }, { { type = 5, timestamp = 0, data = { 1, 2, 3 } } }) sd2strings_eq( { 'Register with timestamp ' .. epoch .. ':', ' % Key Description Value', ' # Required key missing: n', ' # Required key missing: rc', ' + rw block width 0', ' + rt type CHARACTERWISE', ' + ru is_unnamed FALSE', }, ([[ [{'type': 5, 'timestamp': 0, 'data': { }}] ]]):gsub('\n', '') ) sd2strings_eq( { 'Register with timestamp ' .. epoch .. ':', ' % Key Description Value', " + n name ' '", ' # Required key missing: rc', ' + rw block width 0', ' + rt type CHARACTERWISE', ' + ru is_unnamed FALSE', }, ([[ [{'type': 5, 'timestamp': 0, 'data': { 'n': 0x20, }}] ]]):gsub('\n', '') ) sd2strings_eq( { 'Register with timestamp ' .. epoch .. ':', ' % Key Description Value', " + n name ' '", ' + rc contents ["abc", "def"]', ' + rw block width 0', ' + rt type CHARACTERWISE', ' + ru is_unnamed FALSE', }, ([[ [{'type': 5, 'timestamp': 0, 'data': { 'n': 0x20, 'rc': ["abc", "def"], 'ru': {'_TYPE': v:msgpack_types.boolean, '_VAL': 0}, }}] ]]):gsub('\n', '') ) sd2strings_eq( { 'Register with timestamp ' .. epoch .. ':', ' % Key Description Value', " + n name ' '", ' + rc contents @', ' | - "abcdefghijklmnopqrstuvwxyz"', ' | - "abcdefghijklmnopqrstuvwxyz"', ' + rw block width 0', ' + rt type CHARACTERWISE', ' + ru is_unnamed TRUE', }, ([[ [{'type': 5, 'timestamp': 0, 'data': { 'n': 0x20, 'rc': ['abcdefghijklmnopqrstuvwxyz', 'abcdefghijklmnopqrstuvwxyz'], 'ru': {'_TYPE': v:msgpack_types.boolean, '_VAL': 1}, }}] ]]):gsub('\n', '') ) sd2strings_eq( { 'Register with timestamp ' .. epoch .. ':', ' % Key Description Value', " + n name ' '", ' + rc contents @', ' | - "abcdefghijklmnopqrstuvwxyz"', ' | - "abcdefghijklmnopqrstuvwxyz"', ' + rw block width 0', ' + rt type CHARACTERWISE', ' + ru is_unnamed FALSE', }, ([[ [{'type': 5, 'timestamp': 0, 'data': { 'n': 0x20, 'rc': ['abcdefghijklmnopqrstuvwxyz', 'abcdefghijklmnopqrstuvwxyz'], 'rw': 0, 'rt': 0, }}] ]]):gsub('\n', '') ) sd2strings_eq( { 'Register with timestamp ' .. epoch .. ':', ' % Key Description Value', " + n name ' '", ' + rc contents @', ' | - "abcdefghijklmnopqrstuvwxyz"', ' | - "abcdefghijklmnopqrstuvwxyz"', ' + rw block width 5', ' + rt type LINEWISE', ' + ru is_unnamed FALSE', }, ([[ [{'type': 5, 'timestamp': 0, 'data': { 'n': 0x20, 'rc': ['abcdefghijklmnopqrstuvwxyz', 'abcdefghijklmnopqrstuvwxyz'], 'rw': 5, 'rt': 1, }}] ]]):gsub('\n', '') ) sd2strings_eq( { 'Register with timestamp ' .. epoch .. ':', ' % Key Description Value', " + n name ' '", ' + rc contents @', ' | - "abcdefghijklmnopqrstuvwxyz"', ' | - "abcdefghijklmnopqrstuvwxyz"', ' # Expected integer', ' + rw block width ""', ' + rt type BLOCKWISE', ' # Expected boolean', ' + ru is_unnamed ""', }, ([[ [{'type': 5, 'timestamp': 0, 'data': { 'n': 0x20, 'rc': ['abcdefghijklmnopqrstuvwxyz', 'abcdefghijklmnopqrstuvwxyz'], 'rw': "", 'rt': 2, 'ru': "" }}] ]]):gsub('\n', '') ) sd2strings_eq( { 'Register with timestamp ' .. epoch .. ':', ' % Key Description Value', " + n name ' '", ' # Expected array value', ' + rc contents 0', ' # Value is negative', ' + rw block width -1', ' # Unexpected enum value: expected one of 0 (CHARACTERWISE), ' .. '1 (LINEWISE), 2 (BLOCKWISE)', ' + rt type 10', ' # Expected boolean', ' + ru is_unnamed ["abc", "def"]', }, ([[ [{'type': 5, 'timestamp': 0, 'data': { 'n': 0x20, 'rc': 0, 'rw': -1, 'rt': 10, 'ru': ['abc', 'def'], }}] ]]):gsub('\n', '') ) sd2strings_eq( { 'Register with timestamp ' .. epoch .. ':', ' % Key Description Value', " + n name ' '", ' + rc contents @', ' | - "abcdefghijklmnopqrstuvwxyz"', ' | - "abcdefghijklmnopqrstuvwxyz"', ' + rw block width 5', ' + rt type LINEWISE', ' # Expected boolean', ' + ru is_unnamed 0', }, ([[ [{'type': 5, 'timestamp': 0, 'data': { 'n': 0x20, 'rc': ['abcdefghijklmnopqrstuvwxyz', 'abcdefghijklmnopqrstuvwxyz'], 'rw': 5, 'rt': 1, 'ru': 0, }}] ]]):gsub('\n', '') ) end) it('works with variable items', function() sd2strings_eq({ 'Variable with timestamp ' .. epoch .. ':', ' # Unexpected type: map instead of array', ' = {"a": [10]}', }, { { type = 6, timestamp = 0, data = { a = { 10 } } } }) sd2strings_eq( { 'Variable with timestamp ' .. epoch .. ':', ' @ Description Value', ' # Expected more elements in list', }, ([[ [{'type': 6, 'timestamp': 0, 'data': [ ]}] ]]):gsub('\n', '') ) sd2strings_eq( { 'Variable with timestamp ' .. epoch .. ':', ' @ Description Value', ' # Expected binary string', ' - name 1', ' # Expected more elements in list', }, ([[ [{'type': 6, 'timestamp': 0, 'data': [ 1 ]}] ]]):gsub('\n', '') ) sd2strings_eq( { 'Variable with timestamp ' .. epoch .. ':', ' @ Description Value', ' # Expected no NUL bytes', ' - name "\\0"', ' # Expected more elements in list', }, ([[ [{'type': 6, 'timestamp': 0, 'data': [ {'_TYPE': v:msgpack_types.string, '_VAL': ["\n"]}, ]}] ]]):gsub('\n', '') ) sd2strings_eq( { 'Variable with timestamp ' .. epoch .. ':', ' @ Description Value', ' - name "foo"', ' # Expected more elements in list', }, ([[ [{'type': 6, 'timestamp': 0, 'data': [ {'_TYPE': v:msgpack_types.string, '_VAL': ["foo"]}, ]}] ]]):gsub('\n', '') ) sd2strings_eq( { 'Variable with timestamp ' .. epoch .. ':', ' @ Description Value', ' - name "foo"', ' - value NIL', }, ([[ [{'type': 6, 'timestamp': 0, 'data': [ {'_TYPE': v:msgpack_types.string, '_VAL': ["foo"]}, {'_TYPE': v:msgpack_types.nil, '_VAL': ["foo"]}, ]}] ]]):gsub('\n', '') ) sd2strings_eq( { 'Variable with timestamp ' .. epoch .. ':', ' @ Description Value', ' - name "foo"', ' - value NIL', ' - NIL', }, ([[ [{'type': 6, 'timestamp': 0, 'data': [ {'_TYPE': v:msgpack_types.string, '_VAL': ["foo"]}, {'_TYPE': v:msgpack_types.nil, '_VAL': ["foo"]}, {'_TYPE': v:msgpack_types.nil, '_VAL': ["foo"]}, ]}] ]]):gsub('\n', '') ) end) it('works with global mark items', function() sd2strings_eq({ 'Global mark with timestamp ' .. epoch .. ':', ' # Unexpected type: array instead of map', ' = [1, 2, 3]', }, { { type = 7, timestamp = 0, data = { 1, 2, 3 } } }) sd2strings_eq( { 'Global mark with timestamp ' .. epoch .. ':', ' % Key Description Value', ' # Required key missing: n', ' # Required key missing: f', ' + l line number 1', ' + c column 0', }, ([[ [{'type': 7, 'timestamp': 0, 'data': { }}] ]]):gsub('\n', '') ) sd2strings_eq( { 'Global mark with timestamp ' .. epoch .. ':', ' % Key Description Value', ' # Expected integer', ' + n name "foo"', ' # Required key missing: f', ' + l line number 1', ' + c column 0', }, ([[ [{'type': 7, 'timestamp': 0, 'data': { 'n': 'foo', }}] ]]):gsub('\n', '') ) sd2strings_eq( { 'Global mark with timestamp ' .. epoch .. ':', ' % Key Description Value', ' # Required key missing: n', ' + f file name "foo"', ' + l line number 1', ' + c column 0', }, ([[ [{'type': 7, 'timestamp': 0, 'data': { 'f': 'foo', }}] ]]):gsub('\n', '') ) sd2strings_eq( { 'Global mark with timestamp ' .. epoch .. ':', ' % Key Description Value', ' # Value is negative', ' + n name -10', ' # Expected no NUL bytes', ' + f file name "\\0"', ' + l line number 1', ' + c column 0', }, ([[ [{'type': 7, 'timestamp': 0, 'data': { 'n': -10, 'f': {'_TYPE': v:msgpack_types.string, '_VAL': ["\n"]}, }}] ]]):gsub('\n', '') ) sd2strings_eq( { 'Global mark with timestamp ' .. epoch .. ':', ' % Key Description Value', " + n name '\\20'", ' + f file name "foo"', ' # Value is negative', ' + l line number -10', ' # Value is negative', ' + c column -10', }, ([[ [{'type': 7, 'timestamp': 0, 'data': { 'n': 20, 'f': 'foo', 'l': -10, 'c': -10, }}] ]]):gsub('\n', '') ) sd2strings_eq( { 'Global mark with timestamp ' .. epoch .. ':', ' % Key Description Value', ' + n name 128', ' + f file name "foo"', ' + l line number 1', ' + c column 0', }, ([[ [{'type': 7, 'timestamp': 0, 'data': { 'n': 128, 'f': 'foo', }}] ]]):gsub('\n', '') ) sd2strings_eq( { 'Global mark with timestamp ' .. epoch .. ':', ' % Key Description Value', " + n name '\\20'", ' + f file name "foo"', ' # Expected integer', ' + l line number "FOO"', ' # Expected integer', ' + c column "foo"', ' + mX 10', }, ([[ [{'type': 7, 'timestamp': 0, 'data': { 'n': 20, 'f': 'foo', 'l': 'FOO', 'c': 'foo', 'mX': 10, }}] ]]):gsub('\n', '') ) sd2strings_eq( { 'Global mark with timestamp ' .. epoch .. ':', ' % Key________ Description Value', " + n name 'A'", ' + f file name "foo"', ' + l line number 2', ' + c column 200', ' + mX 10', ' + mYYYYYYYYYY 10', }, ([[ [{'type': 7, 'timestamp': 0, 'data': { 'n': char2nr('A'), 'f': 'foo', 'l': 2, 'c': 200, 'mX': 10, 'mYYYYYYYYYY': 10, }}] ]]):gsub('\n', '') ) end) it('works with jump items', function() sd2strings_eq({ 'Jump with timestamp ' .. epoch .. ':', ' # Unexpected type: array instead of map', ' = [1, 2, 3]', }, { { type = 8, timestamp = 0, data = { 1, 2, 3 } } }) sd2strings_eq( { 'Jump with timestamp ' .. epoch .. ':', ' % Key Description Value', ' # Required key missing: f', ' + l line number 1', ' + c column 0', }, ([[ [{'type': 8, 'timestamp': 0, 'data': { }}] ]]):gsub('\n', '') ) sd2strings_eq( { 'Jump with timestamp ' .. epoch .. ':', ' % Key Description Value', ' # Required key missing: f', ' + l line number 1', ' + c column 0', ' # Expected integer', ' + n name "foo"', }, ([[ [{'type': 8, 'timestamp': 0, 'data': { 'n': 'foo', }}] ]]):gsub('\n', '') ) sd2strings_eq( { 'Jump with timestamp ' .. epoch .. ':', ' % Key Description Value', ' + f file name "foo"', ' + l line number 1', ' + c column 0', }, ([[ [{'type': 8, 'timestamp': 0, 'data': { 'f': 'foo', }}] ]]):gsub('\n', '') ) sd2strings_eq( { 'Jump with timestamp ' .. epoch .. ':', ' % Key Description Value', ' # Expected no NUL bytes', ' + f file name "\\0"', ' + l line number 1', ' + c column 0', ' # Value is negative', ' + n name -10', }, ([[ [{'type': 8, 'timestamp': 0, 'data': { 'n': -10, 'f': {'_TYPE': v:msgpack_types.string, '_VAL': ["\n"]}, }}] ]]):gsub('\n', '') ) sd2strings_eq( { 'Jump with timestamp ' .. epoch .. ':', ' % Key Description Value', ' + f file name "foo"', ' # Value is negative', ' + l line number -10', ' # Value is negative', ' + c column -10', }, ([[ [{'type': 8, 'timestamp': 0, 'data': { 'f': 'foo', 'l': -10, 'c': -10, }}] ]]):gsub('\n', '') ) sd2strings_eq( { 'Jump with timestamp ' .. epoch .. ':', ' % Key Description Value', ' + f file name "foo"', ' # Expected integer', ' + l line number "FOO"', ' # Expected integer', ' + c column "foo"', ' + mX 10', }, ([[ [{'type': 8, 'timestamp': 0, 'data': { 'f': 'foo', 'l': 'FOO', 'c': 'foo', 'mX': 10, }}] ]]):gsub('\n', '') ) sd2strings_eq( { 'Jump with timestamp ' .. epoch .. ':', ' % Key________ Description Value', ' + f file name "foo"', ' + l line number 2', ' + c column 200', ' + mX 10', ' + mYYYYYYYYYY 10', " + n name ' '", }, ([[ [{'type': 8, 'timestamp': 0, 'data': { 'n': 0x20, 'f': 'foo', 'l': 2, 'c': 200, 'mX': 10, 'mYYYYYYYYYY': 10, }}] ]]):gsub('\n', '') ) end) it('works with buffer list items', function() sd2strings_eq({ 'Buffer list with timestamp ' .. epoch .. ':', ' # Unexpected type: map instead of array', ' = {"a": [10]}', }, { { type = 9, timestamp = 0, data = { a = { 10 } } } }) sd2strings_eq({ 'Buffer list with timestamp ' .. epoch .. ':', ' # Expected array of maps', ' = [[], []]', }, { { type = 9, timestamp = 0, data = { {}, {} } } }) sd2strings_eq({ 'Buffer list with timestamp ' .. epoch .. ':', ' # Expected array of maps', ' = [{"a": 10}, []]', }, { { type = 9, timestamp = 0, data = { { a = 10 }, {} } } }) sd2strings_eq({ 'Buffer list with timestamp ' .. epoch .. ':', ' % Key Description Value', ' # Required key missing: f', ' + l line number 1', ' + c column 0', ' + a 10', }, { { type = 9, timestamp = 0, data = { { a = 10 } } } }) sd2strings_eq({ 'Buffer list with timestamp ' .. epoch .. ':', ' % Key Description Value', ' # Required key missing: f', ' # Expected integer', ' + l line number "10"', ' # Expected integer', ' + c column "10"', ' + a 10', }, { { type = 9, timestamp = 0, data = { { l = '10', c = '10', a = 10 } } } }) sd2strings_eq({ 'Buffer list with timestamp ' .. epoch .. ':', ' % Key Description Value', ' # Required key missing: f', ' + l line number 10', ' + c column 10', ' + a 10', }, { { type = 9, timestamp = 0, data = { { l = 10, c = 10, a = 10 } } } }) sd2strings_eq({ 'Buffer list with timestamp ' .. epoch .. ':', ' % Key Description Value', ' # Required key missing: f', ' # Value is negative', ' + l line number -10', ' # Value is negative', ' + c column -10', }, { { type = 9, timestamp = 0, data = { { l = -10, c = -10 } } } }) sd2strings_eq({ 'Buffer list with timestamp ' .. epoch .. ':', ' % Key Description Value', ' + f file name "abc"', ' + l line number 1', ' + c column 0', }, { { type = 9, timestamp = 0, data = { { f = 'abc' } } } }) sd2strings_eq({ 'Buffer list with timestamp ' .. epoch .. ':', ' % Key Description Value', ' # Expected binary string', ' + f file name 10', ' + l line number 1', ' + c column 0', '', ' % Key Description Value', ' # Expected binary string', ' + f file name 20', ' + l line number 1', ' + c column 0', }, { { type = 9, timestamp = 0, data = { { f = 10 }, { f = 20 } } } }) sd2strings_eq( { 'Buffer list with timestamp ' .. epoch .. ':', ' % Key Description Value', ' # Expected binary string', ' + f file name 10', ' + l line number 1', ' + c column 0', '', ' % Key Description Value', ' # Expected no NUL bytes', ' + f file name "\\0"', ' + l line number 1', ' + c column 0', }, ([[ [{'type': 9, 'timestamp': 0, 'data': [ {'f': 10}, {'f': {'_TYPE': v:msgpack_types.string, '_VAL': ["\n"]}}, ]}] ]]):gsub('\n', '') ) end) it('works with local mark items', function() sd2strings_eq({ 'Local mark with timestamp ' .. epoch .. ':', ' # Unexpected type: array instead of map', ' = [1, 2, 3]', }, { { type = 10, timestamp = 0, data = { 1, 2, 3 } } }) sd2strings_eq( { 'Local mark with timestamp ' .. epoch .. ':', ' % Key Description Value', ' # Required key missing: f', " + n name '\"'", ' + l line number 1', ' + c column 0', }, ([[ [{'type': 10, 'timestamp': 0, 'data': { }}] ]]):gsub('\n', '') ) sd2strings_eq( { 'Local mark with timestamp ' .. epoch .. ':', ' % Key Description Value', ' # Required key missing: f', ' # Expected integer', ' + n name "foo"', ' + l line number 1', ' + c column 0', }, ([[ [{'type': 10, 'timestamp': 0, 'data': { 'n': 'foo', }}] ]]):gsub('\n', '') ) sd2strings_eq( { 'Local mark with timestamp ' .. epoch .. ':', ' % Key Description Value', ' + f file name "foo"', " + n name '\"'", ' + l line number 1', ' + c column 0', }, ([[ [{'type': 10, 'timestamp': 0, 'data': { 'f': 'foo', }}] ]]):gsub('\n', '') ) sd2strings_eq( { 'Local mark with timestamp ' .. epoch .. ':', ' % Key Description Value', ' # Expected no NUL bytes', ' + f file name "\\0"', ' # Value is negative', ' + n name -10', ' + l line number 1', ' + c column 0', }, ([[ [{'type': 10, 'timestamp': 0, 'data': { 'n': -10, 'f': {'_TYPE': v:msgpack_types.string, '_VAL': ["\n"]}, }}] ]]):gsub('\n', '') ) sd2strings_eq( { 'Local mark with timestamp ' .. epoch .. ':', ' % Key Description Value', ' + f file name "foo"', " + n name '\\20'", ' # Value is negative', ' + l line number -10', ' # Value is negative', ' + c column -10', }, ([[ [{'type': 10, 'timestamp': 0, 'data': { 'n': 20, 'f': 'foo', 'l': -10, 'c': -10, }}] ]]):gsub('\n', '') ) sd2strings_eq( { 'Local mark with timestamp ' .. epoch .. ':', ' % Key Description Value', ' + f file name "foo"', " + n name '\\20'", ' # Expected integer', ' + l line number "FOO"', ' # Expected integer', ' + c column "foo"', ' + mX 10', }, ([[ [{'type': 10, 'timestamp': 0, 'data': { 'n': 20, 'f': 'foo', 'l': 'FOO', 'c': 'foo', 'mX': 10, }}] ]]):gsub('\n', '') ) sd2strings_eq( { 'Local mark with timestamp ' .. epoch .. ':', ' % Key________ Description Value', ' + f file name "foo"', " + n name 'a'", ' + l line number 2', ' + c column 200', ' + mX 10', ' + mYYYYYYYYYY 10', }, ([[ [{'type': 10, 'timestamp': 0, 'data': { 'n': char2nr('a'), 'f': 'foo', 'l': 2, 'c': 200, 'mX': 10, 'mYYYYYYYYYY': 10, }}] ]]):gsub('\n', '') ) end) it('works with change items', function() sd2strings_eq({ 'Change with timestamp ' .. epoch .. ':', ' # Unexpected type: array instead of map', ' = [1, 2, 3]', }, { { type = 11, timestamp = 0, data = { 1, 2, 3 } } }) sd2strings_eq( { 'Change with timestamp ' .. epoch .. ':', ' % Key Description Value', ' # Required key missing: f', ' + l line number 1', ' + c column 0', }, ([[ [{'type': 11, 'timestamp': 0, 'data': { }}] ]]):gsub('\n', '') ) sd2strings_eq( { 'Change with timestamp ' .. epoch .. ':', ' % Key Description Value', ' # Required key missing: f', ' + l line number 1', ' + c column 0', ' # Expected integer', ' + n name "foo"', }, ([[ [{'type': 11, 'timestamp': 0, 'data': { 'n': 'foo', }}] ]]):gsub('\n', '') ) sd2strings_eq( { 'Change with timestamp ' .. epoch .. ':', ' % Key Description Value', ' + f file name "foo"', ' + l line number 1', ' + c column 0', }, ([[ [{'type': 11, 'timestamp': 0, 'data': { 'f': 'foo', }}] ]]):gsub('\n', '') ) sd2strings_eq( { 'Change with timestamp ' .. epoch .. ':', ' % Key Description Value', ' # Expected no NUL bytes', ' + f file name "\\0"', ' + l line number 1', ' + c column 0', ' # Value is negative', ' + n name -10', }, ([[ [{'type': 11, 'timestamp': 0, 'data': { 'n': -10, 'f': {'_TYPE': v:msgpack_types.string, '_VAL': ["\n"]}, }}] ]]):gsub('\n', '') ) sd2strings_eq( { 'Change with timestamp ' .. epoch .. ':', ' % Key Description Value', ' + f file name "foo"', ' # Value is negative', ' + l line number -10', ' # Value is negative', ' + c column -10', }, ([[ [{'type': 11, 'timestamp': 0, 'data': { 'f': 'foo', 'l': -10, 'c': -10, }}] ]]):gsub('\n', '') ) sd2strings_eq( { 'Change with timestamp ' .. epoch .. ':', ' % Key Description Value', ' + f file name "foo"', ' # Expected integer', ' + l line number "FOO"', ' # Expected integer', ' + c column "foo"', ' + mX 10', }, ([[ [{'type': 11, 'timestamp': 0, 'data': { 'f': 'foo', 'l': 'FOO', 'c': 'foo', 'mX': 10, }}] ]]):gsub('\n', '') ) sd2strings_eq( { 'Change with timestamp ' .. epoch .. ':', ' % Key________ Description Value', ' + f file name "foo"', ' + l line number 2', ' + c column 200', ' + mX 10', ' + mYYYYYYYYYY 10', " + n name ' '", }, ([[ [{'type': 11, 'timestamp': 0, 'data': { 'n': 0x20, 'f': 'foo', 'l': 2, 'c': 200, 'mX': 10, 'mYYYYYYYYYY': 10, }}] ]]):gsub('\n', '') ) end) end) describe('function shada#get_strings', function() it('works', function() eq({ 'Header with timestamp ' .. epoch .. ':', ' % Key Value', }, nvim_eval('shada#get_strings(msgpackdump([1, 0, 0, {}]))')) end) end) describe('function shada#strings_to_sd', function() local strings2sd_eq = function(expected, input) api.nvim_set_var('__input', input) nvim_command( 'let g:__actual = map(shada#strings_to_sd(g:__input), ' .. '"filter(v:val, \\"v:key[0] isnot# \'_\' ' .. '&& v:key isnot# \'length\'\\")")' ) -- print() if type(expected) == 'table' then api.nvim_set_var('__expected', expected) nvim_command('let g:__expected = ModifyVal(g:__expected)') expected = 'g:__expected' -- print(nvim_eval('msgpack#string(g:__expected)')) end -- print(nvim_eval('msgpack#string(g:__actual)')) eq(1, nvim_eval(('msgpack#equal(%s, g:__actual)'):format(expected))) if type(expected) == 'table' then nvim_command('unlet g:__expected') end nvim_command('unlet g:__input') nvim_command('unlet g:__actual') end it('works with multiple items', function() strings2sd_eq({ { type = 11, timestamp = 0, data = { f = 'foo', l = 2, c = 200, mX = 10, mYYYYYYYYYY = 10, n = (' '):byte(), }, }, { type = 1, timestamp = 0, data = { c = 'abc', f = { '!string', { 'abc\ndef' } }, l = -10, n = -64, rc = '10', rt = 10, sc = { '!nil', 0 }, sm = 'TRUE', so = 'TRUE', sp = { '!string', { 'abc' } }, }, }, }, { 'Change with timestamp ' .. epoch .. ':', ' % Key________ Description Value', ' + f file name "foo"', ' + l line number 2', ' + c column 200', ' + mX 10', ' + mYYYYYYYYYY 10', " + n name ' '", 'Header with timestamp ' .. epoch .. ':', ' % Key Description____ Value', ' # Expected integer', ' + c column "abc"', ' # Expected no NUL bytes', ' + f file name "abc\\0def"', ' # Value is negative', ' + l line number -10', ' # Value is negative', ' + n name -64', ' # Expected array value', ' + rc contents "10"', ' # Unexpected enum value: expected one of ' .. '0 (CHARACTERWISE), 1 (LINEWISE), 2 (BLOCKWISE)', ' + rt type 10', ' # Expected boolean', ' + sc smartcase value NIL', ' # Expected boolean', ' + sm magic value "TRUE"', ' # Expected integer', ' + so offset value "TRUE"', ' # Expected binary string', ' + sp pattern ="abc"', }) end) it('works with empty list', function() strings2sd_eq({}, {}) end) it('works with header items', function() strings2sd_eq({ { type = 1, timestamp = 0, data = { generator = 'test', } } }, { 'Header with timestamp ' .. epoch .. ':', ' % Key______ Value', ' + generator "test"', }) strings2sd_eq( { { type = 1, timestamp = 0, data = { 1, 2, 3, } } }, { 'Header with timestamp ' .. epoch .. ':', ' # Unexpected type: array instead of map', ' = [1, 2, 3]', } ) strings2sd_eq({ { type = 1, timestamp = 0, data = { a = 1, b = 2, c = 3, d = 4, }, }, }, { 'Header with timestamp ' .. epoch .. ':', ' % Key Description Value', ' + a 1', ' + b 2', ' + c column 3', ' + d 4', }) strings2sd_eq({ { type = 1, timestamp = 0, data = { c = 'abc', f = { '!string', { 'abc\ndef' } }, l = -10, n = -64, rc = '10', rt = 10, sc = { '!nil', 0 }, sm = 'TRUE', so = 'TRUE', sp = { '!string', { 'abc' } }, }, }, }, { 'Header with timestamp ' .. epoch .. ':', ' % Key Description____ Value', ' # Expected integer', ' + c column "abc"', ' # Expected no NUL bytes', ' + f file name "abc\\0def"', ' # Value is negative', ' + l line number -10', ' # Value is negative', ' + n name -64', ' # Expected array value', ' + rc contents "10"', ' # Unexpected enum value: expected one of ' .. '0 (CHARACTERWISE), 1 (LINEWISE), 2 (BLOCKWISE)', ' + rt type 10', ' # Expected boolean', ' + sc smartcase value NIL', ' # Expected boolean', ' + sm magic value "TRUE"', ' # Expected integer', ' + so offset value "TRUE"', ' # Expected binary string', ' + sp pattern ="abc"', }) end) it('works with search pattern items', function() strings2sd_eq( { { type = 2, timestamp = 0, data = { 1, 2, 3, } } }, { 'Search pattern with timestamp ' .. epoch .. ':', ' # Unexpected type: array instead of map', ' = [1, 2, 3]', } ) strings2sd_eq({ { type = 2, timestamp = 0, data = { sp = 'abc', } } }, { 'Search pattern with timestamp ' .. epoch .. ':', ' % Key Description________ Value', ' + sp pattern "abc"', ' + sh v:hlsearch value FALSE', ' + ss is :s pattern FALSE', ' + sm magic value TRUE', ' + sc smartcase value FALSE', ' + sl has line offset FALSE', ' + se place cursor at end FALSE', ' + so offset value 0', ' + su is last used TRUE', }) strings2sd_eq({ { type = 2, timestamp = 0, data = { sp = 'abc', sX = { '!nil', 0 }, sY = { '!nil', 0 }, sZ = { '!nil', 0 }, }, }, }, { 'Search pattern with timestamp ' .. epoch .. ':', ' % Key Description________ Value', ' + sp pattern "abc"', ' + sh v:hlsearch value FALSE', ' + ss is :s pattern FALSE', ' + sm magic value TRUE', ' + sc smartcase value FALSE', ' + sl has line offset FALSE', ' + se place cursor at end FALSE', ' + so offset value 0', ' + su is last used TRUE', ' + sX NIL', ' + sY NIL', ' + sZ NIL', }) strings2sd_eq({ { type = 2, timestamp = 0, data = { '!map', {} } } }, { 'Search pattern with timestamp ' .. epoch .. ':', ' % Key Description________ Value', ' # Required key missing: sp', ' + sh v:hlsearch value FALSE', ' + ss is :s pattern FALSE', ' + sm magic value TRUE', ' + sc smartcase value FALSE', ' + sl has line offset FALSE', ' + se place cursor at end FALSE', ' + so offset value 0', ' + su is last used TRUE', }) strings2sd_eq({ { type = 2, timestamp = 0, data = { sp = '', sh = { '!boolean', 1 }, ss = { '!boolean', 1 }, sc = { '!boolean', 1 }, sl = { '!boolean', 1 }, se = { '!boolean', 1 }, sm = { '!boolean', 0 }, su = { '!boolean', 0 }, so = -10, }, }, }, { 'Search pattern with timestamp ' .. epoch .. ':', ' % Key Description________ Value', ' + sp pattern ""', ' + sh v:hlsearch value TRUE', ' + ss is :s pattern TRUE', ' + sm magic value FALSE', ' + sc smartcase value TRUE', ' + sl has line offset TRUE', ' + se place cursor at end TRUE', ' + so offset value -10', ' + su is last used FALSE', }) strings2sd_eq({ { type = 2, timestamp = 0, data = { sp = 0, sh = 0, ss = 0, sc = 0, sl = 0, se = 0, sm = 0, su = 0, so = '', }, }, }, { 'Search pattern with timestamp ' .. epoch .. ':', ' % Key Description________ Value', ' # Expected binary string', ' + sp pattern 0', ' # Expected boolean', ' + sh v:hlsearch value 0', ' # Expected boolean', ' + ss is :s pattern 0', ' # Expected boolean', ' + sm magic value 0', ' # Expected boolean', ' + sc smartcase value 0', ' # Expected boolean', ' + sl has line offset 0', ' # Expected boolean', ' + se place cursor at end 0', ' # Expected integer', ' + so offset value ""', ' # Expected boolean', ' + su is last used 0', }) end) it('works with replacement string items', function() strings2sd_eq({ { type = 3, timestamp = 0, data = { a = { 10 }, } } }, { 'Replacement string with timestamp ' .. epoch .. ':', ' # Unexpected type: map instead of array', ' = {"a": [10]}', }) strings2sd_eq({ { type = 3, timestamp = 0, data = {} } }, { 'Replacement string with timestamp ' .. epoch .. ':', ' @ Description__________ Value', ' # Expected more elements in list', }) strings2sd_eq({ { type = 3, timestamp = 0, data = { 0, } } }, { 'Replacement string with timestamp ' .. epoch .. ':', ' @ Description__________ Value', ' # Expected binary string', ' - :s replacement string 0', }) strings2sd_eq( { { type = 3, timestamp = 0, data = { 'abc\ndef', 0, } } }, { 'Replacement string with timestamp ' .. epoch .. ':', ' @ Description__________ Value', ' - :s replacement string "abc\\ndef"', ' - 0', } ) strings2sd_eq({ { type = 3, timestamp = 0, data = { 'abc\ndef', } } }, { 'Replacement string with timestamp ' .. epoch .. ':', ' @ Description__________ Value', ' - :s replacement string "abc\\ndef"', }) end) it('works with history entry items', function() strings2sd_eq({ { type = 4, timestamp = 0, data = { a = { 10 }, } } }, { 'History entry with timestamp ' .. epoch .. ':', ' # Unexpected type: map instead of array', ' = {"a": [10]}', }) strings2sd_eq({ { type = 4, timestamp = 0, data = {} } }, { 'History entry with timestamp ' .. epoch .. ':', ' @ Description_ Value', ' # Expected more elements in list', }) strings2sd_eq({ { type = 4, timestamp = 0, data = { '', } } }, { 'History entry with timestamp ' .. epoch .. ':', ' @ Description_ Value', ' # Expected integer', ' - history type ""', ' # Expected more elements in list', }) strings2sd_eq({ { type = 4, timestamp = 0, data = { 5, '', } } }, { 'History entry with timestamp ' .. epoch .. ':', ' @ Description_ Value', ' # Unexpected enum value: expected one of 0 (CMD), 1 (SEARCH), ' .. '2 (EXPR), 3 (INPUT), 4 (DEBUG)', ' - history type 5', ' - contents ""', }) strings2sd_eq( { { type = 4, timestamp = 0, data = { 5, '', 32, } } }, { 'History entry with timestamp ' .. epoch .. ':', ' @ Description_ Value', ' # Unexpected enum value: expected one of 0 (CMD), 1 (SEARCH), ' .. '2 (EXPR), 3 (INPUT), 4 (DEBUG)', ' - history type 5', ' - contents ""', ' - 32', } ) strings2sd_eq( { { type = 4, timestamp = 0, data = { 0, '', 32, } } }, { 'History entry with timestamp ' .. epoch .. ':', ' @ Description_ Value', ' - history type CMD', ' - contents ""', ' - 32', } ) strings2sd_eq( { { type = 4, timestamp = 0, data = { 1, '', 32, } } }, { 'History entry with timestamp ' .. epoch .. ':', ' @ Description_ Value', ' - history type SEARCH', ' - contents ""', " - separator ' '", } ) strings2sd_eq({ { type = 4, timestamp = 0, data = { 1, '', } } }, { 'History entry with timestamp ' .. epoch .. ':', ' @ Description_ Value', ' - history type SEARCH', ' - contents ""', ' # Expected more elements in list', }) strings2sd_eq({ { type = 4, timestamp = 0, data = { 2, '', } } }, { 'History entry with timestamp ' .. epoch .. ':', ' @ Description_ Value', ' - history type EXPR', ' - contents ""', }) strings2sd_eq({ { type = 4, timestamp = 0, data = { 3, '', } } }, { 'History entry with timestamp ' .. epoch .. ':', ' @ Description_ Value', ' - history type INPUT', ' - contents ""', }) strings2sd_eq({ { type = 4, timestamp = 0, data = { 4, '', } } }, { 'History entry with timestamp ' .. epoch .. ':', ' @ Description_ Value', ' - history type DEBUG', ' - contents ""', }) end) it('works with register items', function() strings2sd_eq( { { type = 5, timestamp = 0, data = { 1, 2, 3, } } }, { 'Register with timestamp ' .. epoch .. ':', ' # Unexpected type: array instead of map', ' = [1, 2, 3]', } ) strings2sd_eq({ { type = 5, timestamp = 0, data = { '!map', {} } } }, { 'Register with timestamp ' .. epoch .. ':', ' % Key Description Value', ' # Required key missing: n', ' # Required key missing: rc', ' + rw block width 0', ' + rt type CHARACTERWISE', }) strings2sd_eq({ { type = 5, timestamp = 0, data = { n = (' '):byte(), } } }, { 'Register with timestamp ' .. epoch .. ':', ' % Key Description Value', " + n name ' '", ' # Required key missing: rc', ' + rw block width 0', ' + rt type CHARACTERWISE', }) strings2sd_eq({ { type = 5, timestamp = 0, data = { n = (' '):byte(), rc = { 'abc', 'def' }, }, }, }, { 'Register with timestamp ' .. epoch .. ':', ' % Key Description Value', " + n name ' '", ' + rc contents ["abc", "def"]', ' + rw block width 0', ' + rt type CHARACTERWISE', }) strings2sd_eq({ { type = 5, timestamp = 0, data = { n = (' '):byte(), rc = { 'abcdefghijklmnopqrstuvwxyz', 'abcdefghijklmnopqrstuvwxyz' }, }, }, }, { 'Register with timestamp ' .. epoch .. ':', ' % Key Description Value', " + n name ' '", ' + rc contents @', ' | - "abcdefghijklmnopqrstuvwxyz"', ' | - "abcdefghijklmnopqrstuvwxyz"', ' + rw block width 0', ' + rt type CHARACTERWISE', }) strings2sd_eq({ { type = 5, timestamp = 0, data = { n = (' '):byte(), rc = { 'abcdefghijklmnopqrstuvwxyz', 'abcdefghijklmnopqrstuvwxyz' }, rw = 5, rt = 1, }, }, }, { 'Register with timestamp ' .. epoch .. ':', ' % Key Description Value', " + n name ' '", ' + rc contents @', ' | - "abcdefghijklmnopqrstuvwxyz"', ' | - "abcdefghijklmnopqrstuvwxyz"', ' + rw block width 5', ' + rt type LINEWISE', }) strings2sd_eq({ { type = 5, timestamp = 0, data = { n = (' '):byte(), rc = { 'abcdefghijklmnopqrstuvwxyz', 'abcdefghijklmnopqrstuvwxyz' }, rw = 5, rt = 2, }, }, }, { 'Register with timestamp ' .. epoch .. ':', ' % Key Description Value', " + n name ' '", ' + rc contents @', ' | - "abcdefghijklmnopqrstuvwxyz"', ' | - "abcdefghijklmnopqrstuvwxyz"', ' + rw block width 5', ' + rt type BLOCKWISE', }) strings2sd_eq({ { type = 5, timestamp = 0, data = { n = (' '):byte(), rc = 0, rw = -1, rt = 10, }, }, }, { 'Register with timestamp ' .. epoch .. ':', ' % Key Description Value', " + n name ' '", ' # Expected array value', ' + rc contents 0', ' # Value is negative', ' + rw block width -1', ' # Unexpected enum value: expected one of 0 (CHARACTERWISE), ' .. '1 (LINEWISE), 2 (BLOCKWISE)', ' + rt type 10', }) end) it('works with variable items', function() strings2sd_eq({ { type = 6, timestamp = 0, data = { a = { 10 }, } } }, { 'Variable with timestamp ' .. epoch .. ':', ' # Unexpected type: map instead of array', ' = {"a": [10]}', }) strings2sd_eq({ { type = 6, timestamp = 0, data = {} } }, { 'Variable with timestamp ' .. epoch .. ':', ' @ Description Value', ' # Expected more elements in list', }) strings2sd_eq({ { type = 6, timestamp = 0, data = { 'foo', } } }, { 'Variable with timestamp ' .. epoch .. ':', ' @ Description Value', ' - name "foo"', ' # Expected more elements in list', }) strings2sd_eq({ { type = 6, timestamp = 0, data = { 'foo', { '!nil', 0 }, }, }, }, { 'Variable with timestamp ' .. epoch .. ':', ' @ Description Value', ' - name "foo"', ' - value NIL', }) strings2sd_eq({ { type = 6, timestamp = 0, data = { 'foo', { '!nil', 0 }, { '!nil', 0 }, }, }, }, { 'Variable with timestamp ' .. epoch .. ':', ' @ Description Value', ' - name "foo"', ' - value NIL', ' - NIL', }) end) it('works with global mark items', function() strings2sd_eq( { { type = 7, timestamp = 0, data = { 1, 2, 3, } } }, { 'Global mark with timestamp ' .. epoch .. ':', ' # Unexpected type: array instead of map', ' = [1, 2, 3]', } ) strings2sd_eq({ { type = 7, timestamp = 0, data = { n = ('A'):byte(), f = 'foo', l = 2, c = 200, mX = 10, mYYYYYYYYYY = 10, }, }, }, { 'Global mark with timestamp ' .. epoch .. ':', ' % Key________ Description Value', " + n name 'A'", ' + f file name "foo"', ' + l line number 2', ' + c column 200', ' + mX 10', ' + mYYYYYYYYYY 10', }) end) it('works with jump items', function() strings2sd_eq( { { type = 8, timestamp = 0, data = { 1, 2, 3, } } }, { 'Jump with timestamp ' .. epoch .. ':', ' # Unexpected type: array instead of map', ' = [1, 2, 3]', } ) strings2sd_eq({ { type = 8, timestamp = 0, data = { n = ('A'):byte(), f = 'foo', l = 2, c = 200, mX = 10, mYYYYYYYYYY = 10, }, }, }, { 'Jump with timestamp ' .. epoch .. ':', ' % Key________ Description Value', " + n name 'A'", ' + f file name "foo"', ' + l line number 2', ' + c column 200', ' + mX 10', ' + mYYYYYYYYYY 10', }) end) it('works with buffer list items', function() strings2sd_eq({ { type = 9, timestamp = 0, data = { a = { 10 }, } } }, { 'Buffer list with timestamp ' .. epoch .. ':', ' # Unexpected type: map instead of array', ' = {"a": [10]}', }) strings2sd_eq( { { type = 9, timestamp = 0, data = { { a = 10 }, {}, } } }, { 'Buffer list with timestamp ' .. epoch .. ':', ' # Expected array of maps', ' = [{"a": 10}, []]', } ) strings2sd_eq({ { type = 9, timestamp = 0, data = { { a = 10 }, } } }, { 'Buffer list with timestamp ' .. epoch .. ':', ' % Key Description Value', ' # Required key missing: f', ' + l line number 1', ' + c column 0', ' + a 10', }) strings2sd_eq({ { type = 9, timestamp = 0, data = { { l = '10', c = '10', a = 10 }, }, }, }, { 'Buffer list with timestamp ' .. epoch .. ':', ' % Key Description Value', ' # Required key missing: f', ' # Expected integer', ' + l line number "10"', ' # Expected integer', ' + c column "10"', ' + a 10', }) strings2sd_eq( { { type = 9, timestamp = 0, data = { { l = 10, c = 10, a = 10 }, } } }, { 'Buffer list with timestamp ' .. epoch .. ':', ' % Key Description Value', ' # Required key missing: f', ' + l line number 10', ' + c column 10', ' + a 10', } ) strings2sd_eq( { { type = 9, timestamp = 0, data = { { l = -10, c = -10 }, } } }, { 'Buffer list with timestamp ' .. epoch .. ':', ' % Key Description Value', ' # Required key missing: f', ' # Value is negative', ' + l line number -10', ' # Value is negative', ' + c column -10', } ) strings2sd_eq({ { type = 9, timestamp = 0, data = { { f = 'abc' }, } } }, { 'Buffer list with timestamp ' .. epoch .. ':', ' % Key Description Value', ' + f file name "abc"', ' + l line number 1', ' + c column 0', }) strings2sd_eq({ { type = 9, timestamp = 0, data = { { f = 10 }, { f = 20 }, }, }, }, { 'Buffer list with timestamp ' .. epoch .. ':', ' % Key Description Value', ' # Expected binary string', " + f file name '\\10'", ' + l line number 1', ' + c column 0', '', ' % Key Description Value', ' # Expected binary string', " + f file name '\\20'", ' + l line number 1', ' + c column 0', }) strings2sd_eq({ { type = 9, timestamp = 0, data = { { f = 10 }, { f = { '!string', { '\n' } } }, }, }, }, { 'Buffer list with timestamp ' .. epoch .. ':', ' % Key Description Value', ' # Expected binary string', " + f file name '\\10'", ' + l line number 1', ' + c column 0', '', ' % Key Description Value', ' # Expected no NUL bytes', ' + f file name "\\0"', ' + l line number 1', ' + c column 0', }) end) it('works with local mark items', function() strings2sd_eq( { { type = 10, timestamp = 0, data = { 1, 2, 3, } } }, { 'Local mark with timestamp ' .. epoch .. ':', ' # Unexpected type: array instead of map', ' = [1, 2, 3]', } ) strings2sd_eq({ { type = 10, timestamp = 0, data = { n = ('A'):byte(), f = 'foo', l = 2, c = 200, mX = 10, mYYYYYYYYYY = 10, }, }, }, { 'Local mark with timestamp ' .. epoch .. ':', ' % Key________ Description Value', " + n name 'A'", ' + f file name "foo"', ' + l line number 2', ' + c column 200', ' + mX 10', ' + mYYYYYYYYYY 10', }) end) it('works with change items', function() strings2sd_eq( { { type = 11, timestamp = 0, data = { 1, 2, 3, } } }, { 'Change with timestamp ' .. epoch .. ':', ' # Unexpected type: array instead of map', ' = [1, 2, 3]', } ) strings2sd_eq({ { type = 11, timestamp = 0, data = { n = ('A'):byte(), f = 'foo', l = 2, c = 200, mX = 10, mYYYYYYYYYY = 10, }, }, }, { 'Change with timestamp ' .. epoch .. ':', ' % Key________ Description Value', " + n name 'A'", ' + f file name "foo"', ' + l line number 2', ' + c column 200', ' + mX 10', ' + mYYYYYYYYYY 10', }) end) end) describe('function shada#get_binstrings', function() local getbstrings_eq = function(expected, input) local result = fn['shada#get_binstrings'](input) for i, s in ipairs(result) do result[i] = s:gsub('\n', '\0') end local mpack_result = table.concat(result, '\n') return mpack_eq(expected, mpack_result) end it('works', function() local version = api.nvim_get_vvar('version') getbstrings_eq({ { timestamp = 'current', type = 1, value = { generator = 'shada.vim', version = version, }, }, }, {}) getbstrings_eq({ { timestamp = 'current', type = 1, value = { generator = 'shada.vim', version = version, }, }, { timestamp = 0, type = 1, value = { generator = 'test' } }, }, { 'Header with timestamp ' .. epoch .. ':', ' % Key______ Value', ' + generator "test"', }) api.nvim_set_var('shada#add_own_header', 1) getbstrings_eq({ { timestamp = 'current', type = 1, value = { generator = 'shada.vim', version = version, }, }, }, {}) getbstrings_eq({ { timestamp = 'current', type = 1, value = { generator = 'shada.vim', version = version, }, }, { timestamp = 0, type = 1, value = { generator = 'test' } }, }, { 'Header with timestamp ' .. epoch .. ':', ' % Key______ Value', ' + generator "test"', }) api.nvim_set_var('shada#add_own_header', 0) getbstrings_eq({}, {}) getbstrings_eq({ { timestamp = 0, type = 1, value = { generator = 'test' } } }, { 'Header with timestamp ' .. epoch .. ':', ' % Key______ Value', ' + generator "test"', }) api.nvim_set_var('shada#keep_old_header', 0) getbstrings_eq({}, { 'Header with timestamp ' .. epoch .. ':', ' % Key______ Value', ' + generator "test"', }) getbstrings_eq({ { type = 3, timestamp = 0, value = { 'abc\ndef' } }, { type = 3, timestamp = 0, value = { 'abc\ndef' } }, { type = 3, timestamp = 0, value = { 'abc\ndef' } }, }, { 'Replacement string with timestamp ' .. epoch .. ':', ' @ Description__________ Value', ' - :s replacement string "abc\\ndef"', 'Replacement string with timestamp ' .. epoch .. ':', ' @ Description__________ Value', ' - :s replacement string "abc\\ndef"', 'Replacement string with timestamp ' .. epoch .. ':', ' @ Description__________ Value', ' - :s replacement string "abc\\ndef"', }) end) end) end) describe('plugin/shada.vim', function() local epoch = os.date('%Y-%m-%dT%H:%M:%S', 0) local eol = t.is_os('win') and '\r\n' or '\n' before_each(function() -- Note: reset() is called explicitly in each test. os.remove(fname) os.remove(fname .. '.tst') os.remove(fname_tmp) end) teardown(function() os.remove(fname) os.remove(fname .. '.tst') os.remove(fname_tmp) end) local shada_eq = function(expected, fname_) local mpack_result = read_file(fname_) mpack_eq(expected, mpack_result) end it('event BufReadCmd', function() reset() wshada('\004\000\009\147\000\196\002ab\196\001a') wshada_tmp('\004\000\009\147\000\196\002ab\196\001b') local bufread_commands = api.nvim_get_autocmds({ group = 'ShaDaCommands', event = 'BufReadCmd' }) eq(2, #bufread_commands--[[, vim.inspect(bufread_commands) ]]) -- Need to set nohidden so that the buffer containing 'fname' is not unloaded -- after loading 'fname_tmp', otherwise the '++opt not supported' test below -- won't work since the BufReadCmd autocmd won't be triggered. nvim_command('set nohidden') nvim_command('edit ' .. fname) eq({ 'History entry with timestamp ' .. epoch .. ':', ' @ Description_ Value', ' - history type CMD', ' - contents "ab"', ' - "a"', }, nvim_eval('getline(1, "$")')) eq(false, api.nvim_get_option_value('modified', {})) eq('shada', api.nvim_get_option_value('filetype', {})) nvim_command('edit ' .. fname_tmp) eq({ 'History entry with timestamp ' .. epoch .. ':', ' @ Description_ Value', ' - history type CMD', ' - contents "ab"', ' - "b"', }, nvim_eval('getline(1, "$")')) eq(false, api.nvim_get_option_value('modified', {})) eq('shada', api.nvim_get_option_value('filetype', {})) eq('++opt not supported', exc_exec('edit ++enc=latin1 ' .. fname)) neq({ 'History entry with timestamp ' .. epoch .. ':', ' @ Description_ Value', ' - history type CMD', ' - contents "ab"', ' - "a"', }, nvim_eval('getline(1, "$")')) neq(true, api.nvim_get_option_value('modified', {})) end) it('event FileReadCmd', function() reset() wshada('\004\000\009\147\000\196\002ab\196\001a') wshada_tmp('\004\000\009\147\000\196\002ab\196\001b') nvim_command('$read ' .. fname) eq({ '', 'History entry with timestamp ' .. epoch .. ':', ' @ Description_ Value', ' - history type CMD', ' - contents "ab"', ' - "a"', }, nvim_eval('getline(1, "$")')) eq(true, api.nvim_get_option_value('modified', {})) neq('shada', api.nvim_get_option_value('filetype', {})) nvim_command('1,$read ' .. fname_tmp) eq({ '', 'History entry with timestamp ' .. epoch .. ':', ' @ Description_ Value', ' - history type CMD', ' - contents "ab"', ' - "a"', 'History entry with timestamp ' .. epoch .. ':', ' @ Description_ Value', ' - history type CMD', ' - contents "ab"', ' - "b"', }, nvim_eval('getline(1, "$")')) eq(true, api.nvim_get_option_value('modified', {})) neq('shada', api.nvim_get_option_value('filetype', {})) api.nvim_set_option_value('modified', false, {}) eq('++opt not supported', exc_exec('$read ++enc=latin1 ' .. fname)) eq({ '', 'History entry with timestamp ' .. epoch .. ':', ' @ Description_ Value', ' - history type CMD', ' - contents "ab"', ' - "a"', 'History entry with timestamp ' .. epoch .. ':', ' @ Description_ Value', ' - history type CMD', ' - contents "ab"', ' - "b"', }, nvim_eval('getline(1, "$")')) neq(true, api.nvim_get_option_value('modified', {})) end) it('event BufWriteCmd', function() reset() api.nvim_set_var('shada#add_own_header', 0) api.nvim_buf_set_lines(0, 0, 1, true, { 'Jump with timestamp ' .. epoch .. ':', ' % Key________ Description Value', " + n name 'A'", ' + f file name ["foo"]', ' + l line number 2', ' + c column -200', 'Jump with timestamp ' .. epoch .. ':', ' % Key________ Description Value', " + n name 'A'", ' + f file name ["foo"]', ' + l line number 2', ' + c column -200', }) nvim_command('w ' .. fname .. '.tst') nvim_command('w ' .. fname) nvim_command('w ' .. fname_tmp) eq('++opt not supported', exc_exec('w! ++enc=latin1 ' .. fname)) eq(table.concat({ 'Jump with timestamp ' .. epoch .. ':', ' % Key________ Description Value', " + n name 'A'", ' + f file name ["foo"]', ' + l line number 2', ' + c column -200', 'Jump with timestamp ' .. epoch .. ':', ' % Key________ Description Value', " + n name 'A'", ' + f file name ["foo"]', ' + l line number 2', ' + c column -200', }, eol) .. eol, read_file(fname .. '.tst')) shada_eq({ { timestamp = 0, type = 8, value = { c = -200, f = { 'foo' }, l = 2, n = ('A'):byte() }, }, { timestamp = 0, type = 8, value = { c = -200, f = { 'foo' }, l = 2, n = ('A'):byte() }, }, }, fname) shada_eq({ { timestamp = 0, type = 8, value = { c = -200, f = { 'foo' }, l = 2, n = ('A'):byte() }, }, { timestamp = 0, type = 8, value = { c = -200, f = { 'foo' }, l = 2, n = ('A'):byte() }, }, }, fname_tmp) end) it('event FileWriteCmd', function() reset() api.nvim_set_var('shada#add_own_header', 0) api.nvim_buf_set_lines(0, 0, 1, true, { 'Jump with timestamp ' .. epoch .. ':', ' % Key________ Description Value', " + n name 'A'", ' + f file name ["foo"]', ' + l line number 2', ' + c column -200', 'Jump with timestamp ' .. epoch .. ':', ' % Key________ Description Value', " + n name 'A'", ' + f file name ["foo"]', ' + l line number 2', ' + c column -200', }) nvim_command('1,3w ' .. fname .. '.tst') nvim_command('1,3w ' .. fname) nvim_command('1,3w ' .. fname_tmp) eq('++opt not supported', exc_exec('1,3w! ++enc=latin1 ' .. fname)) eq(table.concat({ 'Jump with timestamp ' .. epoch .. ':', ' % Key________ Description Value', " + n name 'A'", }, eol) .. eol, read_file(fname .. '.tst')) shada_eq( { { timestamp = 0, type = 8, value = { n = ('A'):byte() }, } }, fname ) shada_eq( { { timestamp = 0, type = 8, value = { n = ('A'):byte() }, } }, fname_tmp ) end) it('event FileAppendCmd', function() reset() api.nvim_set_var('shada#add_own_header', 0) api.nvim_buf_set_lines(0, 0, 1, true, { 'Jump with timestamp ' .. epoch .. ':', ' % Key________ Description Value', " + n name 'A'", ' + f file name ["foo"]', ' + l line number 2', ' + c column -200', 'Jump with timestamp ' .. epoch .. ':', ' % Key________ Description Value', " + n name 'A'", ' + f file name ["foo"]', ' + l line number 2', ' + c column -200', }) fn.writefile({ '' }, fname .. '.tst', 'b') fn.writefile({ '' }, fname, 'b') fn.writefile({ '' }, fname_tmp, 'b') nvim_command('1,3w >> ' .. fname .. '.tst') nvim_command('1,3w >> ' .. fname) nvim_command('1,3w >> ' .. fname_tmp) nvim_command('w >> ' .. fname .. '.tst') nvim_command('w >> ' .. fname) nvim_command('w >> ' .. fname_tmp) eq('++opt not supported', exc_exec('1,3w! ++enc=latin1 >> ' .. fname)) eq(table.concat({ 'Jump with timestamp ' .. epoch .. ':', ' % Key________ Description Value', " + n name 'A'", 'Jump with timestamp ' .. epoch .. ':', ' % Key________ Description Value', " + n name 'A'", ' + f file name ["foo"]', ' + l line number 2', ' + c column -200', 'Jump with timestamp ' .. epoch .. ':', ' % Key________ Description Value', " + n name 'A'", ' + f file name ["foo"]', ' + l line number 2', ' + c column -200', }, eol) .. eol, read_file(fname .. '.tst')) shada_eq({ { timestamp = 0, type = 8, value = { n = ('A'):byte() }, }, { timestamp = 0, type = 8, value = { c = -200, f = { 'foo' }, l = 2, n = ('A'):byte() }, }, { timestamp = 0, type = 8, value = { c = -200, f = { 'foo' }, l = 2, n = ('A'):byte() }, }, }, fname) shada_eq({ { timestamp = 0, type = 8, value = { n = ('A'):byte() }, }, { timestamp = 0, type = 8, value = { c = -200, f = { 'foo' }, l = 2, n = ('A'):byte() }, }, { timestamp = 0, type = 8, value = { c = -200, f = { 'foo' }, l = 2, n = ('A'):byte() }, }, }, fname_tmp) end) it('event SourceCmd', function() reset(fname) finally(function() nvim_command('set shadafile=NONE') -- Avoid writing shada file on exit end) wshada('\004\000\006\146\000\196\002ab') wshada_tmp('\004\001\006\146\000\196\002bc') eq(0, exc_exec('source ' .. fname)) eq(0, exc_exec('source ' .. fname_tmp)) eq('bc', fn.histget(':', -1)) eq('ab', fn.histget(':', -2)) end) end) describe('ftplugin/shada.vim', function() local epoch = os.date('%Y-%m-%dT%H:%M:%S', 0) before_each(reset) it('sets indentexpr correctly', function() nvim_command('filetype plugin indent on') nvim_command('setlocal filetype=shada') fn.setline(1, { 'Jump with timestamp ' .. epoch .. ':', '% Key________ Description Value', "+ n name 'A'", '+ f file name "foo"', '+ l line number 2', '+ c column 200', '+ mX 10', '+ mYYYYYYYYYY 10', 'Register with timestamp ' .. epoch .. ':', '% Key Description Value', "+ n name ' '", '+ rc contents @', '| - "abcdefghijklmnopqrstuvwxyz"', '| - "abcdefghijklmnopqrstuvwxyz"', '+ rw block width 0', '+ rt type CHARACTERWISE', 'Replacement string with timestamp ' .. epoch .. ':', ' @ Description__________ Value', ' - :s replacement string "abc\\ndef"', ' Buffer list with timestamp ' .. epoch .. ':', ' # Expected array of maps', '= [{"a": 10}, []]', ' Buffer list with timestamp ' .. epoch .. ':', ' % Key Description Value', ' # Expected binary string', '+ f file name 10', ' + l line number 1', ' + c column 0', '', ' % Key Description Value', ' # Expected binary string', ' + f file name 20', '+ l line number 1', ' + c column 0', }) nvim_command('normal! gg=G') eq({ 'Jump with timestamp ' .. epoch .. ':', ' % Key________ Description Value', " + n name 'A'", ' + f file name "foo"', ' + l line number 2', ' + c column 200', ' + mX 10', ' + mYYYYYYYYYY 10', 'Register with timestamp ' .. epoch .. ':', ' % Key Description Value', " + n name ' '", ' + rc contents @', ' | - "abcdefghijklmnopqrstuvwxyz"', ' | - "abcdefghijklmnopqrstuvwxyz"', ' + rw block width 0', ' + rt type CHARACTERWISE', 'Replacement string with timestamp ' .. epoch .. ':', ' @ Description__________ Value', ' - :s replacement string "abc\\ndef"', 'Buffer list with timestamp ' .. epoch .. ':', ' # Expected array of maps', ' = [{"a": 10}, []]', 'Buffer list with timestamp ' .. epoch .. ':', ' % Key Description Value', ' # Expected binary string', ' + f file name 10', ' + l line number 1', ' + c column 0', '', ' % Key Description Value', ' # Expected binary string', ' + f file name 20', ' + l line number 1', ' + c column 0', }, fn.getline(1, fn.line('$'))) end) it('sets options correctly', function() nvim_command('filetype plugin indent on') nvim_command('setlocal filetype=shada') eq(true, api.nvim_get_option_value('expandtab', {})) eq(2, api.nvim_get_option_value('tabstop', {})) eq(2, api.nvim_get_option_value('softtabstop', {})) eq(2, api.nvim_get_option_value('shiftwidth', {})) end) it('sets indentkeys correctly', function() nvim_command('filetype plugin indent on') nvim_command('setlocal filetype=shada') fn.setline(1, ' Replacement with timestamp ' .. epoch) nvim_feed('ggA:\027') eq('Replacement with timestamp ' .. epoch .. ':', api.nvim_buf_get_lines(0, 0, 1, true)[1]) nvim_feed('o-\027') eq({ ' -' }, api.nvim_buf_get_lines(0, 1, 2, true)) nvim_feed('ggO+\027') eq({ '+' }, api.nvim_buf_get_lines(0, 0, 1, true)) nvim_feed('GO*\027') eq({ ' *' }, api.nvim_buf_get_lines(0, 2, 3, true)) nvim_feed('ggO /\027') eq({ ' /' }, api.nvim_buf_get_lines(0, 0, 1, true)) nvim_feed('ggOx\027') eq({ 'x' }, api.nvim_buf_get_lines(0, 0, 1, true)) end) end) describe('syntax/shada.vim', function() local epoch = os.date('!%Y-%m-%dT%H:%M:%S', 0) before_each(reset) it('works', function() nvim_command('syntax on') nvim_command('setlocal syntax=shada') nvim_command('set laststatus&') local screen = Screen.new(60, 37) screen:set_default_attr_ids { [1] = { bold = true, foreground = Screen.colors.Brown }, [2] = { foreground = tonumber('0x6a0dad') }, [3] = { foreground = Screen.colors.Fuchsia }, [4] = { foreground = Screen.colors.Blue1 }, [5] = { bold = true, foreground = Screen.colors.SeaGreen4 }, [6] = { foreground = Screen.colors.SlateBlue }, [7] = { bold = true, reverse = true }, [8] = { bold = true, foreground = Screen.colors.Blue }, } api.nvim_buf_set_lines(0, 0, 1, true, { 'Header with timestamp ' .. epoch .. ':', ' % Key Value', ' + t "test"', 'Jump with timestamp ' .. epoch .. ':', ' % Key________ Description Value', " + n name 'A'", ' + f file name ["foo"]', ' + l line number 2', ' + c column -200', 'Register with timestamp ' .. epoch .. ':', ' % Key Description Value', ' + rc contents @', ' | - {"abcdefghijklmnopqrstuvwxyz": 1.0}', ' + rt type CHARACTERWISE', ' + rt type LINEWISE', ' + rt type BLOCKWISE', 'Replacement string with timestamp ' .. epoch .. ':', ' @ Description__________ Value', ' - :s replacement string CMD', ' - :s replacement string SEARCH', ' - :s replacement string EXPR', ' - :s replacement string INPUT', ' - :s replacement string DEBUG', 'Buffer list with timestamp ' .. epoch .. ':', ' # Expected array of maps', ' = [{"a": +(10)"ac\\0df\\ngi\\"tt\\.", TRUE: FALSE}, [NIL, +(-10)""]]', 'Buffer list with timestamp ' .. epoch .. ':', ' % Key Description Value', '', ' % Key Description Value', 'Header with timestamp ' .. epoch .. ':', ' % Key Description________ Value', ' + se place cursor at end TRUE', }) screen:expect { grid = [=[ {1:^Header} with timestamp 1970{1:-}01{1:-}01{1:T}00{1::}00{1::}00: | {2: % Key Value} | {1: +} {3:t } {1:"}{3:test}{1:"} | {1:Jump} with timestamp 1970{1:-}01{1:-}01{1:T}00{1::}00{1::}00: | {2: % Key________ Description Value} | {1: +} {3:n } {4:name } {3:'A'} | {1: +} {3:f } {4:file name } {1:["}{3:foo}{1:"]} | {1: +} {3:l } {4:line number} {3:2} | {1: +} {3:c } {4:column } {3:-200} | {1:Register} with timestamp 1970{1:-}01{1:-}01{1:T}00{1::}00{1::}00: | {2: % Key Description Value} | {1: +} {3:rc } {4:contents } {1:@} | {1: | -} {1:{"}{3:abcdefghijklmnopqrstuvwxyz}{1:":} {3:1.0}{1:}} | {1: +} {3:rt } {4:type } {1:CHARACTERWISE} | {1: +} {3:rt } {4:type } {1:LINEWISE} | {1: +} {3:rt } {4:type } {1:BLOCKWISE} | {1:Replacement string} with timestamp 1970{1:-}01{1:-}01{1:T}00{1::}00{1::}00: | {2: @ Description__________ Value} | {1: -} {4::s replacement string} {1:CMD} | {1: -} {4::s replacement string} {1:SEARCH} | {1: -} {4::s replacement string} {1:EXPR} | {1: -} {4::s replacement string} {1:INPUT} | {1: -} {4::s replacement string} {1:DEBUG} | {1:Buffer list} with timestamp 1970{1:-}01{1:-}01{1:T}00{1::}00{1::}00: | {4: # Expected array of maps} | = {1:[{"}{3:a}{1:":} {1:+(}{5:10}{1:)"}{3:ac}{6:\0}{3:df}{6:\n}{3:gi}{6:\"}{3:tt\.}{1:",} {1:TRUE:} {1:FALSE},} {1:[NIL,} {1:+(}{5:-1}| {5:0}{1:)""]]} | {1:Buffer list} with timestamp 1970{1:-}01{1:-}01{1:T}00{1::}00{1::}00: | {2: % Key Description Value} | | {2: % Key Description Value} | {1:Header} with timestamp 1970{1:-}01{1:-}01{1:T}00{1::}00{1::}00: | {2: % Key Description________ Value} | {1: +} {3:se } {4:place cursor at end} {1:TRUE} | {8:~ }| {7:[No Name] [+] }| | ]=], } nvim_command([[ function GetSyntax() let lines = [] for l in range(1, line('$')) let columns = [] let line = getline(l) for c in range(1, col([l, '$']) - 1) let synstack = map(synstack(l, c), 'synIDattr(v:val, "name")') if !empty(columns) && columns[-1][0] ==# synstack let columns[-1][1] .= line[c - 1] else call add(columns, [ synstack, line[c - 1] ]) endif endfor call add(lines, columns) endfor return lines endfunction ]]) local hname = function(s) return { { 'ShaDaEntryHeader', 'ShaDaEntryName' }, s } end local h = function(s) return { { 'ShaDaEntryHeader' }, s } end local htsnum = function(s) return { { 'ShaDaEntryHeader', 'ShaDaEntryTimestamp', 'ShaDaEntryTimestampNumber' }, s, } end local synhtssep = function(s) return { { 'ShaDaEntryHeader', 'ShaDaEntryTimestamp' }, s } end local synepoch = { year = htsnum(os.date('%Y', 0)), month = htsnum(os.date('%m', 0)), day = htsnum(os.date('%d', 0)), hour = htsnum(os.date('!%H', 0)), minute = htsnum(os.date('%M', 0)), second = htsnum(os.date('%S', 0)), } local msh = function(s) return { { 'ShaDaEntryMapShort', 'ShaDaEntryMapHeader' }, s, } end local mlh = function(s) return { { 'ShaDaEntryMapLong', 'ShaDaEntryMapHeader' }, s } end local ah = function(s) return { { 'ShaDaEntryArray', 'ShaDaEntryArrayHeader' }, s } end -- luacheck: ignore local mses = function(s) return { { 'ShaDaEntryMapShort', 'ShaDaEntryMapShortEntryStart', }, s, } end local mles = function(s) return { { 'ShaDaEntryMapLong', 'ShaDaEntryMapLongEntryStart' }, s, } end local act = fn.GetSyntax() local ms = function(syn) return { { 'ShaDaEntryMap' .. syn, 'ShaDaEntryMap' .. syn .. 'EntryStart' }, ' + ', } end local as = function() return { { 'ShaDaEntryArray', 'ShaDaEntryArrayEntryStart' }, ' - ' } end local ad = function(s) return { { 'ShaDaEntryArray', 'ShaDaEntryArrayDescription' }, s, } end local mbas = function(syn) return { { 'ShaDaEntryMap' .. syn, 'ShaDaEntryMapBinArrayStart' }, ' | - ', } end local msk = function(s) return { { 'ShaDaEntryMapShort', 'ShaDaEntryMapShortKey' }, s, } end local mlk = function(s) return { { 'ShaDaEntryMapLong', 'ShaDaEntryMapLongKey' }, s, } end local mld = function(s) return { { 'ShaDaEntryMapLong', 'ShaDaEntryMapLongDescription' }, s, } end local c = function(s) return { { 'ShaDaComment' }, s } end local exp = { { hname('Header'), h(' with timestamp '), synepoch.year, synhtssep('-'), synepoch.month, synhtssep('-'), synepoch.day, synhtssep('T'), synepoch.hour, synhtssep(':'), synepoch.minute, synhtssep(':'), synepoch.second, h(':'), }, { msh(' % Key Value'), }, { ms('Short'), msk('t '), { { 'ShaDaEntryMapShort', 'ShaDaMsgpackBinaryString', 'ShaDaMsgpackStringQuotes' }, '"', }, { { 'ShaDaEntryMapShort', 'ShaDaMsgpackBinaryString' }, 'test' }, { { 'ShaDaEntryMapShort', 'ShaDaMsgpackStringQuotes' }, '"' }, }, { hname('Jump'), h(' with timestamp '), synepoch.year, synhtssep('-'), synepoch.month, synhtssep('-'), synepoch.day, synhtssep('T'), synepoch.hour, synhtssep(':'), synepoch.minute, synhtssep(':'), synepoch.second, h(':'), }, { mlh(' % Key________ Description Value'), }, { ms('Long'), mlk('n '), mld('name '), { { 'ShaDaEntryMapLong', 'ShaDaMsgpackCharacter' }, "'A'" }, }, { ms('Long'), mlk('f '), mld('file name '), { { 'ShaDaEntryMapLong', 'ShaDaMsgpackArray', 'ShaDaMsgpackArrayBraces' }, '[' }, { { 'ShaDaEntryMapLong', 'ShaDaMsgpackArray', 'ShaDaMsgpackBinaryString', 'ShaDaMsgpackStringQuotes', }, '"', }, { { 'ShaDaEntryMapLong', 'ShaDaMsgpackArray', 'ShaDaMsgpackBinaryString' }, 'foo' }, { { 'ShaDaEntryMapLong', 'ShaDaMsgpackArray', 'ShaDaMsgpackStringQuotes' }, '"' }, { { 'ShaDaEntryMapLong', 'ShaDaMsgpackArrayBraces' }, ']' }, }, { ms('Long'), mlk('l '), mld('line number '), { { 'ShaDaEntryMapLong', 'ShaDaMsgpackInteger' }, '2' }, }, { ms('Long'), mlk('c '), mld('column '), { { 'ShaDaEntryMapLong', 'ShaDaMsgpackInteger' }, '-200' }, }, { hname('Register'), h(' with timestamp '), synepoch.year, synhtssep('-'), synepoch.month, synhtssep('-'), synepoch.day, synhtssep('T'), synepoch.hour, synhtssep(':'), synepoch.minute, synhtssep(':'), synepoch.second, h(':'), }, { mlh(' % Key Description Value'), }, { ms('Long'), mlk('rc '), mld('contents '), { { 'ShaDaEntryMapLong', 'ShaDaMsgpackMultilineArray' }, '@' }, }, { mbas('Long'), { { 'ShaDaEntryMapLong', 'ShaDaMsgpackMap', 'ShaDaMsgpackMapBraces' }, '{' }, { { 'ShaDaEntryMapLong', 'ShaDaMsgpackMap', 'ShaDaMsgpackBinaryString', 'ShaDaMsgpackStringQuotes', }, '"', }, { { 'ShaDaEntryMapLong', 'ShaDaMsgpackMap', 'ShaDaMsgpackBinaryString' }, 'abcdefghijklmnopqrstuvwxyz', }, { { 'ShaDaEntryMapLong', 'ShaDaMsgpackMap', 'ShaDaMsgpackStringQuotes' }, '"' }, { { 'ShaDaEntryMapLong', 'ShaDaMsgpackMap', 'ShaDaMsgpackColon' }, ':' }, { { 'ShaDaEntryMapLong', 'ShaDaMsgpackMap' }, ' ' }, { { 'ShaDaEntryMapLong', 'ShaDaMsgpackMap', 'ShaDaMsgpackFloat' }, '1.0' }, { { 'ShaDaEntryMapLong', 'ShaDaMsgpackMapBraces' }, '}' }, }, { ms('Long'), mlk('rt '), mld('type '), { { 'ShaDaEntryMapLong', 'ShaDaMsgpackShaDaKeyword' }, 'CHARACTERWISE' }, }, { ms('Long'), mlk('rt '), mld('type '), { { 'ShaDaEntryMapLong', 'ShaDaMsgpackShaDaKeyword' }, 'LINEWISE' }, }, { ms('Long'), mlk('rt '), mld('type '), { { 'ShaDaEntryMapLong', 'ShaDaMsgpackShaDaKeyword' }, 'BLOCKWISE' }, }, { hname('Replacement string'), h(' with timestamp '), synepoch.year, synhtssep('-'), synepoch.month, synhtssep('-'), synepoch.day, synhtssep('T'), synepoch.hour, synhtssep(':'), synepoch.minute, synhtssep(':'), synepoch.second, h(':'), }, { ah(' @ Description__________ Value'), }, { as(), ad(':s replacement string '), { { 'ShaDaEntryArray', 'ShaDaMsgpackShaDaKeyword' }, 'CMD' }, }, { as(), ad(':s replacement string '), { { 'ShaDaEntryArray', 'ShaDaMsgpackShaDaKeyword' }, 'SEARCH' }, }, { as(), ad(':s replacement string '), { { 'ShaDaEntryArray', 'ShaDaMsgpackShaDaKeyword' }, 'EXPR' }, }, { as(), ad(':s replacement string '), { { 'ShaDaEntryArray', 'ShaDaMsgpackShaDaKeyword' }, 'INPUT' }, }, { as(), ad(':s replacement string '), { { 'ShaDaEntryArray', 'ShaDaMsgpackShaDaKeyword' }, 'DEBUG' }, }, { hname('Buffer list'), h(' with timestamp '), synepoch.year, synhtssep('-'), synepoch.month, synhtssep('-'), synepoch.day, synhtssep('T'), synepoch.hour, synhtssep(':'), synepoch.minute, synhtssep(':'), synepoch.second, h(':'), }, { c(' # Expected array of maps'), }, { { { 'ShaDaEntryRawMsgpack' }, ' = ' }, { { 'ShaDaMsgpackArray', 'ShaDaMsgpackArrayBraces' }, '[' }, { { 'ShaDaMsgpackArray', 'ShaDaMsgpackMap', 'ShaDaMsgpackMapBraces' }, '{' }, { { 'ShaDaMsgpackArray', 'ShaDaMsgpackMap', 'ShaDaMsgpackBinaryString', 'ShaDaMsgpackStringQuotes', }, '"', }, { { 'ShaDaMsgpackArray', 'ShaDaMsgpackMap', 'ShaDaMsgpackBinaryString' }, 'a' }, { { 'ShaDaMsgpackArray', 'ShaDaMsgpackMap', 'ShaDaMsgpackStringQuotes' }, '"' }, { { 'ShaDaMsgpackArray', 'ShaDaMsgpackMap', 'ShaDaMsgpackColon' }, ':' }, { { 'ShaDaMsgpackArray', 'ShaDaMsgpackMap' }, ' ' }, { { 'ShaDaMsgpackArray', 'ShaDaMsgpackMap', 'ShaDaMsgpackExt' }, '+(' }, { { 'ShaDaMsgpackArray', 'ShaDaMsgpackMap', 'ShaDaMsgpackExt', 'ShaDaMsgpackExtType', }, '10', }, { { 'ShaDaMsgpackArray', 'ShaDaMsgpackMap', 'ShaDaMsgpackExt' }, ')' }, { { 'ShaDaMsgpackArray', 'ShaDaMsgpackMap', 'ShaDaMsgpackBinaryString', 'ShaDaMsgpackStringQuotes', }, '"', }, { { 'ShaDaMsgpackArray', 'ShaDaMsgpackMap', 'ShaDaMsgpackBinaryString' }, 'ac' }, { { 'ShaDaMsgpackArray', 'ShaDaMsgpackMap', 'ShaDaMsgpackBinaryString', 'ShaDaMsgpackBinaryStringEscape', }, '\\0', }, { { 'ShaDaMsgpackArray', 'ShaDaMsgpackMap', 'ShaDaMsgpackBinaryString' }, 'df' }, { { 'ShaDaMsgpackArray', 'ShaDaMsgpackMap', 'ShaDaMsgpackBinaryString', 'ShaDaMsgpackBinaryStringEscape', }, '\\n', }, { { 'ShaDaMsgpackArray', 'ShaDaMsgpackMap', 'ShaDaMsgpackBinaryString' }, 'gi' }, { { 'ShaDaMsgpackArray', 'ShaDaMsgpackMap', 'ShaDaMsgpackBinaryString', 'ShaDaMsgpackBinaryStringEscape', }, '\\"', }, { { 'ShaDaMsgpackArray', 'ShaDaMsgpackMap', 'ShaDaMsgpackBinaryString' }, 'tt\\.' }, { { 'ShaDaMsgpackArray', 'ShaDaMsgpackMap', 'ShaDaMsgpackStringQuotes' }, '"' }, { { 'ShaDaMsgpackArray', 'ShaDaMsgpackMap', 'ShaDaMsgpackComma' }, ',' }, { { 'ShaDaMsgpackArray', 'ShaDaMsgpackMap' }, ' ' }, { { 'ShaDaMsgpackArray', 'ShaDaMsgpackMap', 'ShaDaMsgpackKeyword' }, 'TRUE' }, { { 'ShaDaMsgpackArray', 'ShaDaMsgpackMap', 'ShaDaMsgpackColon' }, ':' }, { { 'ShaDaMsgpackArray', 'ShaDaMsgpackMap' }, ' ' }, { { 'ShaDaMsgpackArray', 'ShaDaMsgpackMap', 'ShaDaMsgpackKeyword' }, 'FALSE' }, { { 'ShaDaMsgpackArray', 'ShaDaMsgpackMapBraces' }, '}' }, { { 'ShaDaMsgpackArray', 'ShaDaMsgpackComma' }, ',' }, { { 'ShaDaMsgpackArray' }, ' ' }, { { 'ShaDaMsgpackArray', 'ShaDaMsgpackArray', 'ShaDaMsgpackArrayBraces' }, '[' }, { { 'ShaDaMsgpackArray', 'ShaDaMsgpackArray', 'ShaDaMsgpackKeyword' }, 'NIL' }, { { 'ShaDaMsgpackArray', 'ShaDaMsgpackArray', 'ShaDaMsgpackComma' }, ',' }, { { 'ShaDaMsgpackArray', 'ShaDaMsgpackArray' }, ' ' }, { { 'ShaDaMsgpackArray', 'ShaDaMsgpackArray', 'ShaDaMsgpackExt' }, '+(' }, { { 'ShaDaMsgpackArray', 'ShaDaMsgpackArray', 'ShaDaMsgpackExt', 'ShaDaMsgpackExtType', }, '-10', }, { { 'ShaDaMsgpackArray', 'ShaDaMsgpackArray', 'ShaDaMsgpackExt' }, ')' }, { { 'ShaDaMsgpackArray', 'ShaDaMsgpackArray', 'ShaDaMsgpackBinaryString', 'ShaDaMsgpackStringQuotes', }, '"', }, { { 'ShaDaMsgpackArray', 'ShaDaMsgpackArray', 'ShaDaMsgpackStringQuotes' }, '"' }, { { 'ShaDaMsgpackArray', 'ShaDaMsgpackArrayBraces' }, ']' }, { { 'ShaDaMsgpackArrayBraces' }, ']' }, }, { hname('Buffer list'), h(' with timestamp '), synepoch.year, synhtssep('-'), synepoch.month, synhtssep('-'), synepoch.day, synhtssep('T'), synepoch.hour, synhtssep(':'), synepoch.minute, synhtssep(':'), synepoch.second, h(':'), }, { mlh(' % Key Description Value'), }, {}, { mlh(' % Key Description Value'), }, { hname('Header'), h(' with timestamp '), synepoch.year, synhtssep('-'), synepoch.month, synhtssep('-'), synepoch.day, synhtssep('T'), synepoch.hour, synhtssep(':'), synepoch.minute, synhtssep(':'), synepoch.second, h(':'), }, { mlh(' % Key Description________ Value'), }, { mles(' + '), mlk('se '), mld('place cursor at end '), { { 'ShaDaEntryMapLong', 'ShaDaMsgpackKeyword' }, 'TRUE' }, }, } eq(exp, act) end) end)