neovim

Neovim text editor
git clone https://git.dasho.dev/neovim.git
Log | Files | Refs | README

shada_spec.lua (117866B)


      1 local t = require('test.testutil')
      2 local n = require('test.functional.testnvim')()
      3 local Screen = require('test.functional.ui.screen')
      4 local t_shada = require('test.functional.shada.testutil')
      5 
      6 local clear = n.clear
      7 local eq, api, nvim_eval, nvim_command, exc_exec, fn, nvim_feed =
      8  t.eq, n.api, n.eval, n.command, n.exc_exec, n.fn, n.feed
      9 local neq = t.neq
     10 local read_file = t.read_file
     11 
     12 local get_shada_rw = t_shada.get_shada_rw
     13 
     14 local function reset(shada_file)
     15  clear { args = { '-u', 'NORC', '-i', shada_file or 'NONE' } }
     16 end
     17 
     18 local mpack_eq = function(expected, mpack_result)
     19  local mpack_keys = { 'type', 'timestamp', 'length', 'value' }
     20 
     21  local unpack = vim.mpack.Unpacker()
     22  local actual = {}
     23  local cur, val
     24  local i = 0
     25  local off = 1
     26  while off <= #mpack_result do
     27    val, off = unpack(mpack_result, off)
     28    if i % 4 == 0 then
     29      cur = {}
     30      actual[#actual + 1] = cur
     31    end
     32    local key = mpack_keys[(i % 4) + 1]
     33    if key ~= 'length' then
     34      if key == 'timestamp' and math.abs(val - os.time()) < 2 then
     35        val = 'current'
     36      end
     37      cur[key] = val
     38    end
     39    i = i + 1
     40  end
     41  eq(expected, actual)
     42 end
     43 
     44 local wshada, _, fname = get_shada_rw('Xtest-functional-plugin-shada.shada')
     45 
     46 local wshada_tmp, _, fname_tmp = get_shada_rw('Xtest-functional-plugin-shada.shada.tmp.f')
     47 
     48 describe('autoload/shada.vim', function()
     49  local epoch = os.date('%Y-%m-%dT%H:%M:%S', 0)
     50  setup(function()
     51    reset()
     52    nvim_command([[
     53    function ModifyVal(val)
     54      if type(a:val) == type([])
     55        if len(a:val) == 2 && type(a:val[0]) == type('') && a:val[0][0] is# '!' && has_key(v:msgpack_types, a:val[0][1:])
     56          return {'_TYPE': v:msgpack_types[ a:val[0][1:] ], '_VAL': a:val[1]}
     57        else
     58          return map(copy(a:val), 'ModifyVal(v:val)')
     59        endif
     60      elseif type(a:val) == type({})
     61        let keys = sort(keys(a:val))
     62        let ret = {'_TYPE': v:msgpack_types.map, '_VAL': []}
     63        for key in keys
     64          let k = {'_TYPE': v:msgpack_types.string, '_VAL': split(key, "\n", 1)}
     65          let v = ModifyVal(a:val[key])
     66          call add(ret._VAL, [k, v])
     67          unlet v
     68        endfor
     69        return ret
     70      elseif type(a:val) == type('')
     71        return {'_TYPE': v:msgpack_types.string, '_VAL': split(a:val, "\n", 1)}
     72      else
     73        return a:val
     74      endif
     75    endfunction
     76    ]])
     77  end)
     78 
     79  local sp = function(typ, val)
     80    return ('{"_TYPE": v:msgpack_types.%s, "_VAL": %s}'):format(typ, val)
     81  end
     82 
     83  describe('function shada#mpack_to_sd', function()
     84    local mpack2sd = function(arg)
     85      return ('shada#mpack_to_sd(%s)'):format(arg)
     86    end
     87 
     88    it('works', function()
     89      eq({}, nvim_eval(mpack2sd('[]')))
     90      eq({ { type = 1, timestamp = 5, length = 1, data = 7 } }, nvim_eval(mpack2sd('[1, 5, 1, 7]')))
     91      eq({
     92        { type = 1, timestamp = 5, length = 1, data = 7 },
     93        { type = 1, timestamp = 10, length = 1, data = 5 },
     94      }, nvim_eval(mpack2sd('[1, 5, 1, 7, 1, 10, 1, 5]')))
     95      eq(
     96        'zero-uint:Entry 1 has type element which is zero',
     97        exc_exec('call ' .. mpack2sd('[0, 5, 1, 7]'))
     98      )
     99      eq(
    100        'zero-uint:Entry 1 has type element which is zero',
    101        exc_exec('call ' .. mpack2sd(('[%s, 5, 1, 7]'):format(sp('integer', '[1, 0, 0, 0]'))))
    102      )
    103      eq(
    104        'not-uint:Entry 1 has timestamp element which is not an unsigned integer',
    105        exc_exec('call ' .. mpack2sd('[1, -1, 1, 7]'))
    106      )
    107      eq(
    108        'not-uint:Entry 1 has length element which is not an unsigned integer',
    109        exc_exec('call ' .. mpack2sd('[1, 1, -1, 7]'))
    110      )
    111      eq(
    112        'not-uint:Entry 1 has type element which is not an unsigned integer',
    113        exc_exec('call ' .. mpack2sd('["", 1, -1, 7]'))
    114      )
    115    end)
    116  end)
    117 
    118  describe('function shada#sd_to_strings', function()
    119    local sd2strings_eq = function(expected, arg)
    120      if type(arg) == 'table' then
    121        eq(expected, fn['shada#sd_to_strings'](arg))
    122      else
    123        eq(expected, nvim_eval(('shada#sd_to_strings(%s)'):format(arg)))
    124      end
    125    end
    126 
    127    it('works with empty input', function()
    128      sd2strings_eq({}, '[]')
    129    end)
    130 
    131    it('works with unknown items', function()
    132      sd2strings_eq({
    133        'Unknown (0x64) with timestamp ' .. epoch .. ':',
    134        '  = 100',
    135      }, { { type = 100, timestamp = 0, length = 1, data = 100 } })
    136 
    137      sd2strings_eq(
    138        {
    139          'Unknown (0x4000001180000006) with timestamp ' .. epoch .. ':',
    140          '  = 100',
    141        },
    142        ('[{"type": %s, "timestamp": 0, "length": 1, "data": 100}]'):format(
    143          sp('integer', '[1, 1, 35, 6]')
    144        )
    145      )
    146    end)
    147 
    148    it('works with multiple unknown items', function()
    149      sd2strings_eq({
    150        'Unknown (0x64) with timestamp ' .. epoch .. ':',
    151        '  = 100',
    152        'Unknown (0x65) with timestamp ' .. epoch .. ':',
    153        '  = 500',
    154      }, {
    155        { type = 100, timestamp = 0, length = 1, data = 100 },
    156        { type = 101, timestamp = 0, length = 1, data = 500 },
    157      })
    158    end)
    159 
    160    it('works with header items', function()
    161      sd2strings_eq({
    162        'Header with timestamp ' .. epoch .. ':',
    163        '  % Key______  Value',
    164        '  + generator  "test"',
    165      }, { { type = 1, timestamp = 0, data = { generator = 'test' } } })
    166      sd2strings_eq({
    167        'Header with timestamp ' .. epoch .. ':',
    168        '  % Key  Description  Value',
    169        '  + a                 1',
    170        '  + b                 2',
    171        '  + c    column       3',
    172        '  + d                 4',
    173      }, { { type = 1, timestamp = 0, data = { a = 1, b = 2, c = 3, d = 4 } } })
    174      sd2strings_eq({
    175        'Header with timestamp ' .. epoch .. ':',
    176        '  % Key  Value',
    177        '  + t    "test"',
    178      }, { { type = 1, timestamp = 0, data = { t = 'test' } } })
    179      sd2strings_eq({
    180        'Header with timestamp ' .. epoch .. ':',
    181        '  # Unexpected type: array instead of map',
    182        '  = [1, 2, 3]',
    183      }, { { type = 1, timestamp = 0, data = { 1, 2, 3 } } })
    184    end)
    185 
    186    it('processes standard keys correctly, even in header', function()
    187      sd2strings_eq(
    188        {
    189          'Header with timestamp ' .. epoch .. ':',
    190          '  % Key  Description________  Value',
    191          '  + c    column               0',
    192          '  + f    file name            "/tmp/foo"',
    193          '  + l    line number          10',
    194          "  + n    name                 '@'",
    195          '  + rc   contents             ["abc", "def"]',
    196          '  + rt   type                 CHARACTERWISE',
    197          '  + ru   is_unnamed           FALSE',
    198          '  + rw   block width          10',
    199          '  + sb   search backward      TRUE',
    200          '  + sc   smartcase value      FALSE',
    201          '  + se   place cursor at end  TRUE',
    202          '  + sh   v:hlsearch value     TRUE',
    203          '  + sl   has line offset      FALSE',
    204          '  + sm   magic value          TRUE',
    205          '  + so   offset value         10',
    206          '  + sp   pattern              "100"',
    207          '  + ss   is :s pattern        TRUE',
    208          '  + su   is last used         FALSE',
    209        },
    210        ([[ [{'type': 1, 'timestamp': 0, 'data': {
    211        'sm': {'_TYPE': v:msgpack_types.boolean, '_VAL': 1},
    212        'sc': {'_TYPE': v:msgpack_types.boolean, '_VAL': 0},
    213        'sl': {'_TYPE': v:msgpack_types.boolean, '_VAL': 0},
    214        'se': {'_TYPE': v:msgpack_types.boolean, '_VAL': 1},
    215        'sb': {'_TYPE': v:msgpack_types.boolean, '_VAL': 1},
    216        'so': 10,
    217        'su': {'_TYPE': v:msgpack_types.boolean, '_VAL': 0},
    218        'ss': {'_TYPE': v:msgpack_types.boolean, '_VAL': 1},
    219        'sh': {'_TYPE': v:msgpack_types.boolean, '_VAL': 1},
    220        'sp': '100',
    221        'rt': 0,
    222        'rw': 10,
    223        'rc': ['abc', 'def'],
    224        'ru': {'_TYPE': v:msgpack_types.boolean, '_VAL': 0},
    225        'n': 0x40,
    226        'l': 10,
    227        'c': 0,
    228        'f': '/tmp/foo',
    229      }}] ]]):gsub('\n', '')
    230      )
    231      sd2strings_eq(
    232        {
    233          'Header with timestamp ' .. epoch .. ':',
    234          '  % Key  Description____  Value',
    235          '  # Expected integer',
    236          '  + c    column           "abc"',
    237          '  # Expected no NUL bytes',
    238          '  + f    file name        "abc\\0def"',
    239          '  # Value is negative',
    240          '  + l    line number      -10',
    241          '  # Value is negative',
    242          '  + n    name             -64',
    243          '  # Expected array value',
    244          '  + rc   contents         "10"',
    245          '  # Unexpected enum value: expected one of '
    246            .. '0 (CHARACTERWISE), 1 (LINEWISE), 2 (BLOCKWISE)',
    247          '  + rt   type             10',
    248          '  # Expected boolean',
    249          '  + ru   is_unnamed       10',
    250          '  # Expected boolean',
    251          '  + sc   smartcase value  NIL',
    252          '  # Expected boolean',
    253          '  + sm   magic value      "TRUE"',
    254          '  # Expected integer',
    255          '  + so   offset value     "TRUE"',
    256          '  + sp   pattern          "abc"',
    257        },
    258        ([[ [{'type': 1, 'timestamp': 0, 'data': {
    259        'sm': 'TRUE',
    260        'sc': {'_TYPE': v:msgpack_types.nil, '_VAL': 0},
    261        'so': 'TRUE',
    262        'sp': {'_TYPE': v:msgpack_types.string, '_VAL': ["abc"]},
    263        'rt': 10,
    264        'rc': '10',
    265        'ru': 10,
    266        'n': -0x40,
    267        'l': -10,
    268        'c': 'abc',
    269        'f': {'_TYPE': v:msgpack_types.string, '_VAL': ["abc\ndef"]},
    270      }}] ]]):gsub('\n', '')
    271      )
    272      sd2strings_eq(
    273        {
    274          'Header with timestamp ' .. epoch .. ':',
    275          '  % Key  Description  Value',
    276          '  # Expected no NUL bytes',
    277          '  + f    file name    "abc\\0def"',
    278          '  + rc   contents     ["abc", "abc"]',
    279          '  # Expected integer',
    280          '  + rt   type         "ABC"',
    281        },
    282        ([[ [{'type': 1, 'timestamp': 0, 'data': {
    283        'rt': 'ABC',
    284        'rc': ["abc", {'_TYPE': v:msgpack_types.string, '_VAL': ["abc"]}],
    285        'f': {'_TYPE': v:msgpack_types.string, '_VAL': ["abc\ndef"]},
    286      }}] ]]):gsub('\n', '')
    287      )
    288      sd2strings_eq(
    289        {
    290          'Header with timestamp ' .. epoch .. ':',
    291          '  % Key  Description  Value',
    292          '  # Expected no NUL bytes',
    293          '  + rc   contents     ["abc", "a\\nd\\0"]',
    294        },
    295        ([[ [{'type': 1, 'timestamp': 0, 'data': {
    296        'rc': ["abc", {'_TYPE': v:msgpack_types.string, '_VAL': ["a", "d\n"]}],
    297      }}] ]]):gsub('\n', '')
    298      )
    299    end)
    300 
    301    it('works with search pattern items', function()
    302      sd2strings_eq({
    303        'Search pattern with timestamp ' .. epoch .. ':',
    304        '  # Unexpected type: array instead of map',
    305        '  = [1, 2, 3]',
    306      }, { { type = 2, timestamp = 0, data = { 1, 2, 3 } } })
    307      sd2strings_eq(
    308        {
    309          'Search pattern with timestamp ' .. epoch .. ':',
    310          '  % Key  Description________  Value',
    311          '  + sp   pattern              "abc"',
    312          '  + sh   v:hlsearch value     FALSE',
    313          '  + ss   is :s pattern        FALSE',
    314          '  + sb   search backward      FALSE',
    315          '  + sm   magic value          TRUE',
    316          '  + sc   smartcase value      FALSE',
    317          '  + sl   has line offset      FALSE',
    318          '  + se   place cursor at end  FALSE',
    319          '  + so   offset value         0',
    320          '  + su   is last used         TRUE',
    321        },
    322        ([[ [{'type': 2, 'timestamp': 0, 'data': {
    323        'sp': 'abc',
    324      }}] ]]):gsub('\n', '')
    325      )
    326      sd2strings_eq(
    327        {
    328          'Search pattern with timestamp ' .. epoch .. ':',
    329          '  % Key  Description________  Value',
    330          '  + sp   pattern              "abc"',
    331          '  + sh   v:hlsearch value     FALSE',
    332          '  + ss   is :s pattern        FALSE',
    333          '  + sb   search backward      FALSE',
    334          '  + sm   magic value          TRUE',
    335          '  + sc   smartcase value      FALSE',
    336          '  + sl   has line offset      FALSE',
    337          '  + se   place cursor at end  FALSE',
    338          '  + so   offset value         0',
    339          '  + su   is last used         TRUE',
    340          '  + sX                        NIL',
    341          '  + sY                        NIL',
    342          '  + sZ                        NIL',
    343        },
    344        ([[ [{'type': 2, 'timestamp': 0, 'data': {
    345        'sp': 'abc',
    346        'sZ': {'_TYPE': v:msgpack_types.nil, '_VAL': 0},
    347        'sY': {'_TYPE': v:msgpack_types.nil, '_VAL': 0},
    348        'sX': {'_TYPE': v:msgpack_types.nil, '_VAL': 0},
    349      }}] ]]):gsub('\n', '')
    350      )
    351      sd2strings_eq(
    352        {
    353          'Search pattern with timestamp ' .. epoch .. ':',
    354          '  % Key  Description________  Value',
    355          '  + sp   pattern              "abc"',
    356          '  + sh   v:hlsearch value     FALSE',
    357          '  + ss   is :s pattern        FALSE',
    358          '  + sb   search backward      FALSE',
    359          '  + sm   magic value          TRUE',
    360          '  + sc   smartcase value      FALSE',
    361          '  + sl   has line offset      FALSE',
    362          '  + se   place cursor at end  FALSE',
    363          '  + so   offset value         0',
    364          '  + su   is last used         TRUE',
    365        },
    366        ([[ [{'type': 2, 'timestamp': 0, 'data': {
    367        'sp': 'abc',
    368        'sh': {'_TYPE': v:msgpack_types.boolean, '_VAL': 0},
    369        'ss': {'_TYPE': v:msgpack_types.boolean, '_VAL': 0},
    370        'sb': {'_TYPE': v:msgpack_types.boolean, '_VAL': 0},
    371        'sm': {'_TYPE': v:msgpack_types.boolean, '_VAL': 1},
    372        'sc': {'_TYPE': v:msgpack_types.boolean, '_VAL': 0},
    373        'sl': {'_TYPE': v:msgpack_types.boolean, '_VAL': 0},
    374        'se': {'_TYPE': v:msgpack_types.boolean, '_VAL': 0},
    375        'so': 0,
    376        'su': {'_TYPE': v:msgpack_types.boolean, '_VAL': 1},
    377      }}] ]]):gsub('\n', '')
    378      )
    379      sd2strings_eq(
    380        {
    381          'Search pattern with timestamp ' .. epoch .. ':',
    382          '  % Key  Description________  Value',
    383          '  # Required key missing: sp',
    384          '  + sh   v:hlsearch value     FALSE',
    385          '  + ss   is :s pattern        FALSE',
    386          '  + sb   search backward      FALSE',
    387          '  + sm   magic value          TRUE',
    388          '  + sc   smartcase value      FALSE',
    389          '  + sl   has line offset      FALSE',
    390          '  + se   place cursor at end  FALSE',
    391          '  + so   offset value         0',
    392          '  + su   is last used         TRUE',
    393        },
    394        ([[ [{'type': 2, 'timestamp': 0, 'data': {
    395      }}] ]]):gsub('\n', '')
    396      )
    397      sd2strings_eq(
    398        {
    399          'Search pattern with timestamp ' .. epoch .. ':',
    400          '  % Key  Description________  Value',
    401          '  + sp   pattern              ""',
    402          '  + sh   v:hlsearch value     TRUE',
    403          '  + ss   is :s pattern        TRUE',
    404          '  + sb   search backward      TRUE',
    405          '  + sm   magic value          FALSE',
    406          '  + sc   smartcase value      TRUE',
    407          '  + sl   has line offset      TRUE',
    408          '  + se   place cursor at end  TRUE',
    409          '  + so   offset value         -10',
    410          '  + su   is last used         FALSE',
    411        },
    412        ([[ [{'type': 2, 'timestamp': 0, 'data': {
    413        'sp': '',
    414        'sh': {'_TYPE': v:msgpack_types.boolean, '_VAL': 1},
    415        'ss': {'_TYPE': v:msgpack_types.boolean, '_VAL': 1},
    416        'sb': {'_TYPE': v:msgpack_types.boolean, '_VAL': 1},
    417        'sm': {'_TYPE': v:msgpack_types.boolean, '_VAL': 0},
    418        'sc': {'_TYPE': v:msgpack_types.boolean, '_VAL': 1},
    419        'sl': {'_TYPE': v:msgpack_types.boolean, '_VAL': 1},
    420        'se': {'_TYPE': v:msgpack_types.boolean, '_VAL': 1},
    421        'so': -10,
    422        'su': {'_TYPE': v:msgpack_types.boolean, '_VAL': 0},
    423      }}] ]]):gsub('\n', '')
    424      )
    425      sd2strings_eq(
    426        {
    427          'Search pattern with timestamp ' .. epoch .. ':',
    428          '  % Key  Description________  Value',
    429          '  # Expected binary string',
    430          '  + sp   pattern              0',
    431          '  # Expected boolean',
    432          '  + sh   v:hlsearch value     0',
    433          '  # Expected boolean',
    434          '  + ss   is :s pattern        0',
    435          '  # Expected boolean',
    436          '  + sb   search backward      0',
    437          '  # Expected boolean',
    438          '  + sm   magic value          0',
    439          '  # Expected boolean',
    440          '  + sc   smartcase value      0',
    441          '  # Expected boolean',
    442          '  + sl   has line offset      0',
    443          '  # Expected boolean',
    444          '  + se   place cursor at end  0',
    445          '  # Expected integer',
    446          '  + so   offset value         ""',
    447          '  # Expected boolean',
    448          '  + su   is last used         0',
    449        },
    450        ([[ [{'type': 2, 'timestamp': 0, 'data': {
    451        'sp': 0,
    452        'sh': 0,
    453        'ss': 0,
    454        'sb': 0,
    455        'sm': 0,
    456        'sc': 0,
    457        'sl': 0,
    458        'se': 0,
    459        'so': '',
    460        'su': 0,
    461      }}] ]]):gsub('\n', '')
    462      )
    463    end)
    464 
    465    it('works with replacement string items', function()
    466      sd2strings_eq({
    467        'Replacement string with timestamp ' .. epoch .. ':',
    468        '  # Unexpected type: map instead of array',
    469        '  = {"a": [10]}',
    470      }, { { type = 3, timestamp = 0, data = { a = { 10 } } } })
    471      sd2strings_eq(
    472        {
    473          'Replacement string with timestamp ' .. epoch .. ':',
    474          '  @ Description__________  Value',
    475          '  # Expected more elements in list',
    476        },
    477        ([[ [{'type': 3, 'timestamp': 0, 'data': [
    478      ]}] ]]):gsub('\n', '')
    479      )
    480      sd2strings_eq(
    481        {
    482          'Replacement string with timestamp ' .. epoch .. ':',
    483          '  @ Description__________  Value',
    484          '  # Expected binary string',
    485          '  - :s replacement string  0',
    486        },
    487        ([[ [{'type': 3, 'timestamp': 0, 'data': [
    488        0,
    489      ]}] ]]):gsub('\n', '')
    490      )
    491      sd2strings_eq(
    492        {
    493          'Replacement string with timestamp ' .. epoch .. ':',
    494          '  @ Description__________  Value',
    495          '  # Expected no NUL bytes',
    496          '  - :s replacement string  "abc\\0def"',
    497        },
    498        ([[ [{'type': 3, 'timestamp': 0, 'data': [
    499        {'_TYPE': v:msgpack_types.string, '_VAL': ["abc\ndef"]},
    500      ]}] ]]):gsub('\n', '')
    501      )
    502      sd2strings_eq(
    503        {
    504          'Replacement string with timestamp ' .. epoch .. ':',
    505          '  @ Description__________  Value',
    506          '  - :s replacement string  "abc\\ndef"',
    507        },
    508        ([[ [{'type': 3, 'timestamp': 0, 'data': [
    509        {'_TYPE': v:msgpack_types.string, '_VAL': ["abc", "def"]},
    510      ]}] ]]):gsub('\n', '')
    511      )
    512      sd2strings_eq(
    513        {
    514          'Replacement string with timestamp ' .. epoch .. ':',
    515          '  @ Description__________  Value',
    516          '  - :s replacement string  "abc\\ndef"',
    517          '  -                        0',
    518        },
    519        ([[ [{'type': 3, 'timestamp': 0, 'data': [
    520        {'_TYPE': v:msgpack_types.string, '_VAL': ["abc", "def"]},
    521        0,
    522      ]}] ]]):gsub('\n', '')
    523      )
    524    end)
    525 
    526    it('works with history entry items', function()
    527      sd2strings_eq({
    528        'History entry with timestamp ' .. epoch .. ':',
    529        '  # Unexpected type: map instead of array',
    530        '  = {"a": [10]}',
    531      }, { { type = 4, timestamp = 0, data = { a = { 10 } } } })
    532      sd2strings_eq(
    533        {
    534          'History entry with timestamp ' .. epoch .. ':',
    535          '  @ Description_  Value',
    536          '  # Expected more elements in list',
    537        },
    538        ([[ [{'type': 4, 'timestamp': 0, 'data': [
    539      ]}] ]]):gsub('\n', '')
    540      )
    541      sd2strings_eq(
    542        {
    543          'History entry with timestamp ' .. epoch .. ':',
    544          '  @ Description_  Value',
    545          '  # Expected integer',
    546          '  - history type  ""',
    547          '  # Expected more elements in list',
    548        },
    549        ([[ [{'type': 4, 'timestamp': 0, 'data': [
    550        '',
    551      ]}] ]]):gsub('\n', '')
    552      )
    553      sd2strings_eq(
    554        {
    555          'History entry with timestamp ' .. epoch .. ':',
    556          '  @ Description_  Value',
    557          '  # Unexpected enum value: expected one of 0 (CMD), 1 (SEARCH), '
    558            .. '2 (EXPR), 3 (INPUT), 4 (DEBUG)',
    559          '  - history type  5',
    560          '  - contents      ""',
    561        },
    562        ([[ [{'type': 4, 'timestamp': 0, 'data': [
    563        5,
    564        ''
    565      ]}] ]]):gsub('\n', '')
    566      )
    567      sd2strings_eq(
    568        {
    569          'History entry with timestamp ' .. epoch .. ':',
    570          '  @ Description_  Value',
    571          '  # Unexpected enum value: expected one of 0 (CMD), 1 (SEARCH), '
    572            .. '2 (EXPR), 3 (INPUT), 4 (DEBUG)',
    573          '  - history type  5',
    574          '  - contents      ""',
    575          '  -               32',
    576        },
    577        ([[ [{'type': 4, 'timestamp': 0, 'data': [
    578        5,
    579        '',
    580        0x20
    581      ]}] ]]):gsub('\n', '')
    582      )
    583      sd2strings_eq(
    584        {
    585          'History entry with timestamp ' .. epoch .. ':',
    586          '  @ Description_  Value',
    587          '  - history type  CMD',
    588          '  - contents      ""',
    589          '  -               32',
    590        },
    591        ([[ [{'type': 4, 'timestamp': 0, 'data': [
    592        0,
    593        '',
    594        0x20
    595      ]}] ]]):gsub('\n', '')
    596      )
    597      sd2strings_eq(
    598        {
    599          'History entry with timestamp ' .. epoch .. ':',
    600          '  @ Description_  Value',
    601          '  - history type  SEARCH',
    602          '  - contents      ""',
    603          "  - separator     ' '",
    604        },
    605        ([[ [{'type': 4, 'timestamp': 0, 'data': [
    606        1,
    607        '',
    608        0x20
    609      ]}] ]]):gsub('\n', '')
    610      )
    611      sd2strings_eq(
    612        {
    613          'History entry with timestamp ' .. epoch .. ':',
    614          '  @ Description_  Value',
    615          '  - history type  SEARCH',
    616          '  - contents      ""',
    617          '  # Expected more elements in list',
    618        },
    619        ([[ [{'type': 4, 'timestamp': 0, 'data': [
    620        1,
    621        '',
    622      ]}] ]]):gsub('\n', '')
    623      )
    624      sd2strings_eq(
    625        {
    626          'History entry with timestamp ' .. epoch .. ':',
    627          '  @ Description_  Value',
    628          '  - history type  EXPR',
    629          '  - contents      ""',
    630        },
    631        ([[ [{'type': 4, 'timestamp': 0, 'data': [
    632        2,
    633        '',
    634      ]}] ]]):gsub('\n', '')
    635      )
    636      sd2strings_eq(
    637        {
    638          'History entry with timestamp ' .. epoch .. ':',
    639          '  @ Description_  Value',
    640          '  - history type  INPUT',
    641          '  - contents      ""',
    642        },
    643        ([[ [{'type': 4, 'timestamp': 0, 'data': [
    644        3,
    645        '',
    646      ]}] ]]):gsub('\n', '')
    647      )
    648      sd2strings_eq(
    649        {
    650          'History entry with timestamp ' .. epoch .. ':',
    651          '  @ Description_  Value',
    652          '  - history type  DEBUG',
    653          '  - contents      ""',
    654        },
    655        ([[ [{'type': 4, 'timestamp': 0, 'data': [
    656        4,
    657        '',
    658      ]}] ]]):gsub('\n', '')
    659      )
    660      sd2strings_eq(
    661        {
    662          'History entry with timestamp ' .. epoch .. ':',
    663          '  @ Description_  Value',
    664          '  - history type  DEBUG',
    665          '  # Expected binary string',
    666          '  - contents      10',
    667        },
    668        ([[ [{'type': 4, 'timestamp': 0, 'data': [
    669        4,
    670        10,
    671      ]}] ]]):gsub('\n', '')
    672      )
    673      sd2strings_eq(
    674        {
    675          'History entry with timestamp ' .. epoch .. ':',
    676          '  @ Description_  Value',
    677          '  - history type  DEBUG',
    678          '  # Expected no NUL bytes',
    679          '  - contents      "abc\\0def"',
    680        },
    681        ([[ [{'type': 4, 'timestamp': 0, 'data': [
    682        4,
    683        {'_TYPE': v:msgpack_types.string, '_VAL': ["abc\ndef"]},
    684      ]}] ]]):gsub('\n', '')
    685      )
    686      sd2strings_eq(
    687        {
    688          'History entry with timestamp ' .. epoch .. ':',
    689          '  @ Description_  Value',
    690          '  - history type  SEARCH',
    691          '  - contents      "abc"',
    692          '  # Expected integer',
    693          '  - separator     ""',
    694        },
    695        ([[ [{'type': 4, 'timestamp': 0, 'data': [
    696        1,
    697        'abc',
    698        '',
    699      ]}] ]]):gsub('\n', '')
    700      )
    701      sd2strings_eq(
    702        {
    703          'History entry with timestamp ' .. epoch .. ':',
    704          '  @ Description_  Value',
    705          '  - history type  SEARCH',
    706          '  - contents      "abc"',
    707          '  # Value is negative',
    708          '  - separator     -1',
    709        },
    710        ([[ [{'type': 4, 'timestamp': 0, 'data': [
    711        1,
    712        'abc',
    713        -1,
    714      ]}] ]]):gsub('\n', '')
    715      )
    716      -- Regression: NUL separator must be properly supported
    717      sd2strings_eq(
    718        {
    719          'History entry with timestamp ' .. epoch .. ':',
    720          '  @ Description_  Value',
    721          '  - history type  SEARCH',
    722          '  - contents      ""',
    723          "  - separator     '\\0'",
    724        },
    725        ([[ [{'type': 4, 'timestamp': 0, 'data': [
    726        1,
    727        '',
    728        0x0
    729      ]}] ]]):gsub('\n', '')
    730      )
    731    end)
    732 
    733    it('works with register items', function()
    734      sd2strings_eq({
    735        'Register with timestamp ' .. epoch .. ':',
    736        '  # Unexpected type: array instead of map',
    737        '  = [1, 2, 3]',
    738      }, { { type = 5, timestamp = 0, data = { 1, 2, 3 } } })
    739      sd2strings_eq(
    740        {
    741          'Register with timestamp ' .. epoch .. ':',
    742          '  % Key  Description  Value',
    743          '  # Required key missing: n',
    744          '  # Required key missing: rc',
    745          '  + rw   block width  0',
    746          '  + rt   type         CHARACTERWISE',
    747          '  + ru   is_unnamed   FALSE',
    748        },
    749        ([[ [{'type': 5, 'timestamp': 0, 'data': {
    750      }}] ]]):gsub('\n', '')
    751      )
    752      sd2strings_eq(
    753        {
    754          'Register with timestamp ' .. epoch .. ':',
    755          '  % Key  Description  Value',
    756          "  + n    name         ' '",
    757          '  # Required key missing: rc',
    758          '  + rw   block width  0',
    759          '  + rt   type         CHARACTERWISE',
    760          '  + ru   is_unnamed   FALSE',
    761        },
    762        ([[ [{'type': 5, 'timestamp': 0, 'data': {
    763        'n': 0x20,
    764      }}] ]]):gsub('\n', '')
    765      )
    766      sd2strings_eq(
    767        {
    768          'Register with timestamp ' .. epoch .. ':',
    769          '  % Key  Description  Value',
    770          "  + n    name         ' '",
    771          '  + rc   contents     ["abc", "def"]',
    772          '  + rw   block width  0',
    773          '  + rt   type         CHARACTERWISE',
    774          '  + ru   is_unnamed   FALSE',
    775        },
    776        ([[ [{'type': 5, 'timestamp': 0, 'data': {
    777        'n': 0x20,
    778        'rc': ["abc", "def"],
    779        'ru': {'_TYPE': v:msgpack_types.boolean, '_VAL': 0},
    780      }}] ]]):gsub('\n', '')
    781      )
    782      sd2strings_eq(
    783        {
    784          'Register with timestamp ' .. epoch .. ':',
    785          '  % Key  Description  Value',
    786          "  + n    name         ' '",
    787          '  + rc   contents     @',
    788          '  | - "abcdefghijklmnopqrstuvwxyz"',
    789          '  | - "abcdefghijklmnopqrstuvwxyz"',
    790          '  + rw   block width  0',
    791          '  + rt   type         CHARACTERWISE',
    792          '  + ru   is_unnamed   TRUE',
    793        },
    794        ([[ [{'type': 5, 'timestamp': 0, 'data': {
    795        'n': 0x20,
    796        'rc': ['abcdefghijklmnopqrstuvwxyz', 'abcdefghijklmnopqrstuvwxyz'],
    797        'ru': {'_TYPE': v:msgpack_types.boolean, '_VAL': 1},
    798      }}] ]]):gsub('\n', '')
    799      )
    800      sd2strings_eq(
    801        {
    802          'Register with timestamp ' .. epoch .. ':',
    803          '  % Key  Description  Value',
    804          "  + n    name         ' '",
    805          '  + rc   contents     @',
    806          '  | - "abcdefghijklmnopqrstuvwxyz"',
    807          '  | - "abcdefghijklmnopqrstuvwxyz"',
    808          '  + rw   block width  0',
    809          '  + rt   type         CHARACTERWISE',
    810          '  + ru   is_unnamed   FALSE',
    811        },
    812        ([[ [{'type': 5, 'timestamp': 0, 'data': {
    813        'n': 0x20,
    814        'rc': ['abcdefghijklmnopqrstuvwxyz', 'abcdefghijklmnopqrstuvwxyz'],
    815        'rw': 0,
    816        'rt': 0,
    817      }}] ]]):gsub('\n', '')
    818      )
    819      sd2strings_eq(
    820        {
    821          'Register with timestamp ' .. epoch .. ':',
    822          '  % Key  Description  Value',
    823          "  + n    name         ' '",
    824          '  + rc   contents     @',
    825          '  | - "abcdefghijklmnopqrstuvwxyz"',
    826          '  | - "abcdefghijklmnopqrstuvwxyz"',
    827          '  + rw   block width  5',
    828          '  + rt   type         LINEWISE',
    829          '  + ru   is_unnamed   FALSE',
    830        },
    831        ([[ [{'type': 5, 'timestamp': 0, 'data': {
    832        'n': 0x20,
    833        'rc': ['abcdefghijklmnopqrstuvwxyz', 'abcdefghijklmnopqrstuvwxyz'],
    834        'rw': 5,
    835        'rt': 1,
    836      }}] ]]):gsub('\n', '')
    837      )
    838      sd2strings_eq(
    839        {
    840          'Register with timestamp ' .. epoch .. ':',
    841          '  % Key  Description  Value',
    842          "  + n    name         ' '",
    843          '  + rc   contents     @',
    844          '  | - "abcdefghijklmnopqrstuvwxyz"',
    845          '  | - "abcdefghijklmnopqrstuvwxyz"',
    846          '  # Expected integer',
    847          '  + rw   block width  ""',
    848          '  + rt   type         BLOCKWISE',
    849          '  # Expected boolean',
    850          '  + ru   is_unnamed   ""',
    851        },
    852        ([[ [{'type': 5, 'timestamp': 0, 'data': {
    853        'n': 0x20,
    854        'rc': ['abcdefghijklmnopqrstuvwxyz', 'abcdefghijklmnopqrstuvwxyz'],
    855        'rw': "",
    856        'rt': 2,
    857        'ru': ""
    858      }}] ]]):gsub('\n', '')
    859      )
    860      sd2strings_eq(
    861        {
    862          'Register with timestamp ' .. epoch .. ':',
    863          '  % Key  Description  Value',
    864          "  + n    name         ' '",
    865          '  # Expected array value',
    866          '  + rc   contents     0',
    867          '  # Value is negative',
    868          '  + rw   block width  -1',
    869          '  # Unexpected enum value: expected one of 0 (CHARACTERWISE), '
    870            .. '1 (LINEWISE), 2 (BLOCKWISE)',
    871          '  + rt   type         10',
    872          '  # Expected boolean',
    873          '  + ru   is_unnamed   ["abc", "def"]',
    874        },
    875        ([[ [{'type': 5, 'timestamp': 0, 'data': {
    876        'n': 0x20,
    877        'rc': 0,
    878        'rw': -1,
    879        'rt': 10,
    880        'ru': ['abc', 'def'],
    881      }}] ]]):gsub('\n', '')
    882      )
    883      sd2strings_eq(
    884        {
    885          'Register with timestamp ' .. epoch .. ':',
    886          '  % Key  Description  Value',
    887          "  + n    name         ' '",
    888          '  + rc   contents     @',
    889          '  | - "abcdefghijklmnopqrstuvwxyz"',
    890          '  | - "abcdefghijklmnopqrstuvwxyz"',
    891          '  + rw   block width  5',
    892          '  + rt   type         LINEWISE',
    893          '  # Expected boolean',
    894          '  + ru   is_unnamed   0',
    895        },
    896        ([[ [{'type': 5, 'timestamp': 0, 'data': {
    897        'n': 0x20,
    898        'rc': ['abcdefghijklmnopqrstuvwxyz', 'abcdefghijklmnopqrstuvwxyz'],
    899        'rw': 5,
    900        'rt': 1,
    901        'ru': 0,
    902      }}] ]]):gsub('\n', '')
    903      )
    904    end)
    905 
    906    it('works with variable items', function()
    907      sd2strings_eq({
    908        'Variable with timestamp ' .. epoch .. ':',
    909        '  # Unexpected type: map instead of array',
    910        '  = {"a": [10]}',
    911      }, { { type = 6, timestamp = 0, data = { a = { 10 } } } })
    912      sd2strings_eq(
    913        {
    914          'Variable with timestamp ' .. epoch .. ':',
    915          '  @ Description  Value',
    916          '  # Expected more elements in list',
    917        },
    918        ([[ [{'type': 6, 'timestamp': 0, 'data': [
    919      ]}] ]]):gsub('\n', '')
    920      )
    921      sd2strings_eq(
    922        {
    923          'Variable with timestamp ' .. epoch .. ':',
    924          '  @ Description  Value',
    925          '  # Expected binary string',
    926          '  - name         1',
    927          '  # Expected more elements in list',
    928        },
    929        ([[ [{'type': 6, 'timestamp': 0, 'data': [
    930        1
    931      ]}] ]]):gsub('\n', '')
    932      )
    933      sd2strings_eq(
    934        {
    935          'Variable with timestamp ' .. epoch .. ':',
    936          '  @ Description  Value',
    937          '  # Expected no NUL bytes',
    938          '  - name         "\\0"',
    939          '  # Expected more elements in list',
    940        },
    941        ([[ [{'type': 6, 'timestamp': 0, 'data': [
    942        {'_TYPE': v:msgpack_types.string, '_VAL': ["\n"]},
    943      ]}] ]]):gsub('\n', '')
    944      )
    945      sd2strings_eq(
    946        {
    947          'Variable with timestamp ' .. epoch .. ':',
    948          '  @ Description  Value',
    949          '  - name         "foo"',
    950          '  # Expected more elements in list',
    951        },
    952        ([[ [{'type': 6, 'timestamp': 0, 'data': [
    953        {'_TYPE': v:msgpack_types.string, '_VAL': ["foo"]},
    954      ]}] ]]):gsub('\n', '')
    955      )
    956      sd2strings_eq(
    957        {
    958          'Variable with timestamp ' .. epoch .. ':',
    959          '  @ Description  Value',
    960          '  - name         "foo"',
    961          '  - value        NIL',
    962        },
    963        ([[ [{'type': 6, 'timestamp': 0, 'data': [
    964        {'_TYPE': v:msgpack_types.string, '_VAL': ["foo"]},
    965        {'_TYPE': v:msgpack_types.nil, '_VAL': ["foo"]},
    966      ]}] ]]):gsub('\n', '')
    967      )
    968      sd2strings_eq(
    969        {
    970          'Variable with timestamp ' .. epoch .. ':',
    971          '  @ Description  Value',
    972          '  - name         "foo"',
    973          '  - value        NIL',
    974          '  -              NIL',
    975        },
    976        ([[ [{'type': 6, 'timestamp': 0, 'data': [
    977        {'_TYPE': v:msgpack_types.string, '_VAL': ["foo"]},
    978        {'_TYPE': v:msgpack_types.nil, '_VAL': ["foo"]},
    979        {'_TYPE': v:msgpack_types.nil, '_VAL': ["foo"]},
    980      ]}] ]]):gsub('\n', '')
    981      )
    982    end)
    983 
    984    it('works with global mark items', function()
    985      sd2strings_eq({
    986        'Global mark with timestamp ' .. epoch .. ':',
    987        '  # Unexpected type: array instead of map',
    988        '  = [1, 2, 3]',
    989      }, { { type = 7, timestamp = 0, data = { 1, 2, 3 } } })
    990      sd2strings_eq(
    991        {
    992          'Global mark with timestamp ' .. epoch .. ':',
    993          '  % Key  Description  Value',
    994          '  # Required key missing: n',
    995          '  # Required key missing: f',
    996          '  + l    line number  1',
    997          '  + c    column       0',
    998        },
    999        ([[ [{'type': 7, 'timestamp': 0, 'data': {
   1000      }}] ]]):gsub('\n', '')
   1001      )
   1002      sd2strings_eq(
   1003        {
   1004          'Global mark with timestamp ' .. epoch .. ':',
   1005          '  % Key  Description  Value',
   1006          '  # Expected integer',
   1007          '  + n    name         "foo"',
   1008          '  # Required key missing: f',
   1009          '  + l    line number  1',
   1010          '  + c    column       0',
   1011        },
   1012        ([[ [{'type': 7, 'timestamp': 0, 'data': {
   1013        'n': 'foo',
   1014      }}] ]]):gsub('\n', '')
   1015      )
   1016      sd2strings_eq(
   1017        {
   1018          'Global mark with timestamp ' .. epoch .. ':',
   1019          '  % Key  Description  Value',
   1020          '  # Required key missing: n',
   1021          '  + f    file name    "foo"',
   1022          '  + l    line number  1',
   1023          '  + c    column       0',
   1024        },
   1025        ([[ [{'type': 7, 'timestamp': 0, 'data': {
   1026        'f': 'foo',
   1027      }}] ]]):gsub('\n', '')
   1028      )
   1029      sd2strings_eq(
   1030        {
   1031          'Global mark with timestamp ' .. epoch .. ':',
   1032          '  % Key  Description  Value',
   1033          '  # Value is negative',
   1034          '  + n    name         -10',
   1035          '  # Expected no NUL bytes',
   1036          '  + f    file name    "\\0"',
   1037          '  + l    line number  1',
   1038          '  + c    column       0',
   1039        },
   1040        ([[ [{'type': 7, 'timestamp': 0, 'data': {
   1041        'n': -10,
   1042        'f': {'_TYPE': v:msgpack_types.string, '_VAL': ["\n"]},
   1043      }}] ]]):gsub('\n', '')
   1044      )
   1045      sd2strings_eq(
   1046        {
   1047          'Global mark with timestamp ' .. epoch .. ':',
   1048          '  % Key  Description  Value',
   1049          "  + n    name         '\\20'",
   1050          '  + f    file name    "foo"',
   1051          '  # Value is negative',
   1052          '  + l    line number  -10',
   1053          '  # Value is negative',
   1054          '  + c    column       -10',
   1055        },
   1056        ([[ [{'type': 7, 'timestamp': 0, 'data': {
   1057        'n': 20,
   1058        'f': 'foo',
   1059        'l': -10,
   1060        'c': -10,
   1061      }}] ]]):gsub('\n', '')
   1062      )
   1063      sd2strings_eq(
   1064        {
   1065          'Global mark with timestamp ' .. epoch .. ':',
   1066          '  % Key  Description  Value',
   1067          '  + n    name         128',
   1068          '  + f    file name    "foo"',
   1069          '  + l    line number  1',
   1070          '  + c    column       0',
   1071        },
   1072        ([[ [{'type': 7, 'timestamp': 0, 'data': {
   1073        'n': 128,
   1074        'f': 'foo',
   1075      }}] ]]):gsub('\n', '')
   1076      )
   1077      sd2strings_eq(
   1078        {
   1079          'Global mark with timestamp ' .. epoch .. ':',
   1080          '  % Key  Description  Value',
   1081          "  + n    name         '\\20'",
   1082          '  + f    file name    "foo"',
   1083          '  # Expected integer',
   1084          '  + l    line number  "FOO"',
   1085          '  # Expected integer',
   1086          '  + c    column       "foo"',
   1087          '  + mX                10',
   1088        },
   1089        ([[ [{'type': 7, 'timestamp': 0, 'data': {
   1090        'n': 20,
   1091        'f': 'foo',
   1092        'l': 'FOO',
   1093        'c': 'foo',
   1094        'mX': 10,
   1095      }}] ]]):gsub('\n', '')
   1096      )
   1097      sd2strings_eq(
   1098        {
   1099          'Global mark with timestamp ' .. epoch .. ':',
   1100          '  % Key________  Description  Value',
   1101          "  + n            name         'A'",
   1102          '  + f            file name    "foo"',
   1103          '  + l            line number  2',
   1104          '  + c            column       200',
   1105          '  + mX                        10',
   1106          '  + mYYYYYYYYYY               10',
   1107        },
   1108        ([[ [{'type': 7, 'timestamp': 0, 'data': {
   1109        'n': char2nr('A'),
   1110        'f': 'foo',
   1111        'l': 2,
   1112        'c': 200,
   1113        'mX': 10,
   1114        'mYYYYYYYYYY': 10,
   1115      }}] ]]):gsub('\n', '')
   1116      )
   1117    end)
   1118 
   1119    it('works with jump items', function()
   1120      sd2strings_eq({
   1121        'Jump with timestamp ' .. epoch .. ':',
   1122        '  # Unexpected type: array instead of map',
   1123        '  = [1, 2, 3]',
   1124      }, { { type = 8, timestamp = 0, data = { 1, 2, 3 } } })
   1125      sd2strings_eq(
   1126        {
   1127          'Jump with timestamp ' .. epoch .. ':',
   1128          '  % Key  Description  Value',
   1129          '  # Required key missing: f',
   1130          '  + l    line number  1',
   1131          '  + c    column       0',
   1132        },
   1133        ([[ [{'type': 8, 'timestamp': 0, 'data': {
   1134      }}] ]]):gsub('\n', '')
   1135      )
   1136      sd2strings_eq(
   1137        {
   1138          'Jump with timestamp ' .. epoch .. ':',
   1139          '  % Key  Description  Value',
   1140          '  # Required key missing: f',
   1141          '  + l    line number  1',
   1142          '  + c    column       0',
   1143          '  # Expected integer',
   1144          '  + n    name         "foo"',
   1145        },
   1146        ([[ [{'type': 8, 'timestamp': 0, 'data': {
   1147        'n': 'foo',
   1148      }}] ]]):gsub('\n', '')
   1149      )
   1150      sd2strings_eq(
   1151        {
   1152          'Jump with timestamp ' .. epoch .. ':',
   1153          '  % Key  Description  Value',
   1154          '  + f    file name    "foo"',
   1155          '  + l    line number  1',
   1156          '  + c    column       0',
   1157        },
   1158        ([[ [{'type': 8, 'timestamp': 0, 'data': {
   1159        'f': 'foo',
   1160      }}] ]]):gsub('\n', '')
   1161      )
   1162      sd2strings_eq(
   1163        {
   1164          'Jump with timestamp ' .. epoch .. ':',
   1165          '  % Key  Description  Value',
   1166          '  # Expected no NUL bytes',
   1167          '  + f    file name    "\\0"',
   1168          '  + l    line number  1',
   1169          '  + c    column       0',
   1170          '  # Value is negative',
   1171          '  + n    name         -10',
   1172        },
   1173        ([[ [{'type': 8, 'timestamp': 0, 'data': {
   1174        'n': -10,
   1175        'f': {'_TYPE': v:msgpack_types.string, '_VAL': ["\n"]},
   1176      }}] ]]):gsub('\n', '')
   1177      )
   1178      sd2strings_eq(
   1179        {
   1180          'Jump with timestamp ' .. epoch .. ':',
   1181          '  % Key  Description  Value',
   1182          '  + f    file name    "foo"',
   1183          '  # Value is negative',
   1184          '  + l    line number  -10',
   1185          '  # Value is negative',
   1186          '  + c    column       -10',
   1187        },
   1188        ([[ [{'type': 8, 'timestamp': 0, 'data': {
   1189        'f': 'foo',
   1190        'l': -10,
   1191        'c': -10,
   1192      }}] ]]):gsub('\n', '')
   1193      )
   1194      sd2strings_eq(
   1195        {
   1196          'Jump with timestamp ' .. epoch .. ':',
   1197          '  % Key  Description  Value',
   1198          '  + f    file name    "foo"',
   1199          '  # Expected integer',
   1200          '  + l    line number  "FOO"',
   1201          '  # Expected integer',
   1202          '  + c    column       "foo"',
   1203          '  + mX                10',
   1204        },
   1205        ([[ [{'type': 8, 'timestamp': 0, 'data': {
   1206        'f': 'foo',
   1207        'l': 'FOO',
   1208        'c': 'foo',
   1209        'mX': 10,
   1210      }}] ]]):gsub('\n', '')
   1211      )
   1212      sd2strings_eq(
   1213        {
   1214          'Jump with timestamp ' .. epoch .. ':',
   1215          '  % Key________  Description  Value',
   1216          '  + f            file name    "foo"',
   1217          '  + l            line number  2',
   1218          '  + c            column       200',
   1219          '  + mX                        10',
   1220          '  + mYYYYYYYYYY               10',
   1221          "  + n            name         ' '",
   1222        },
   1223        ([[ [{'type': 8, 'timestamp': 0, 'data': {
   1224        'n': 0x20,
   1225        'f': 'foo',
   1226        'l': 2,
   1227        'c': 200,
   1228        'mX': 10,
   1229        'mYYYYYYYYYY': 10,
   1230      }}] ]]):gsub('\n', '')
   1231      )
   1232    end)
   1233 
   1234    it('works with buffer list items', function()
   1235      sd2strings_eq({
   1236        'Buffer list with timestamp ' .. epoch .. ':',
   1237        '  # Unexpected type: map instead of array',
   1238        '  = {"a": [10]}',
   1239      }, { { type = 9, timestamp = 0, data = { a = { 10 } } } })
   1240      sd2strings_eq({
   1241        'Buffer list with timestamp ' .. epoch .. ':',
   1242        '  # Expected array of maps',
   1243        '  = [[], []]',
   1244      }, { { type = 9, timestamp = 0, data = { {}, {} } } })
   1245      sd2strings_eq({
   1246        'Buffer list with timestamp ' .. epoch .. ':',
   1247        '  # Expected array of maps',
   1248        '  = [{"a": 10}, []]',
   1249      }, { { type = 9, timestamp = 0, data = { { a = 10 }, {} } } })
   1250      sd2strings_eq({
   1251        'Buffer list with timestamp ' .. epoch .. ':',
   1252        '  % Key  Description  Value',
   1253        '  # Required key missing: f',
   1254        '  + l    line number  1',
   1255        '  + c    column       0',
   1256        '  + a                 10',
   1257      }, { { type = 9, timestamp = 0, data = { { a = 10 } } } })
   1258      sd2strings_eq({
   1259        'Buffer list with timestamp ' .. epoch .. ':',
   1260        '  % Key  Description  Value',
   1261        '  # Required key missing: f',
   1262        '  # Expected integer',
   1263        '  + l    line number  "10"',
   1264        '  # Expected integer',
   1265        '  + c    column       "10"',
   1266        '  + a                 10',
   1267      }, { { type = 9, timestamp = 0, data = { { l = '10', c = '10', a = 10 } } } })
   1268      sd2strings_eq({
   1269        'Buffer list with timestamp ' .. epoch .. ':',
   1270        '  % Key  Description  Value',
   1271        '  # Required key missing: f',
   1272        '  + l    line number  10',
   1273        '  + c    column       10',
   1274        '  + a                 10',
   1275      }, { { type = 9, timestamp = 0, data = { { l = 10, c = 10, a = 10 } } } })
   1276      sd2strings_eq({
   1277        'Buffer list with timestamp ' .. epoch .. ':',
   1278        '  % Key  Description  Value',
   1279        '  # Required key missing: f',
   1280        '  # Value is negative',
   1281        '  + l    line number  -10',
   1282        '  # Value is negative',
   1283        '  + c    column       -10',
   1284      }, { { type = 9, timestamp = 0, data = { { l = -10, c = -10 } } } })
   1285      sd2strings_eq({
   1286        'Buffer list with timestamp ' .. epoch .. ':',
   1287        '  % Key  Description  Value',
   1288        '  + f    file name    "abc"',
   1289        '  + l    line number  1',
   1290        '  + c    column       0',
   1291      }, { { type = 9, timestamp = 0, data = { { f = 'abc' } } } })
   1292      sd2strings_eq({
   1293        'Buffer list with timestamp ' .. epoch .. ':',
   1294        '  % Key  Description  Value',
   1295        '  # Expected binary string',
   1296        '  + f    file name    10',
   1297        '  + l    line number  1',
   1298        '  + c    column       0',
   1299        '',
   1300        '  % Key  Description  Value',
   1301        '  # Expected binary string',
   1302        '  + f    file name    20',
   1303        '  + l    line number  1',
   1304        '  + c    column       0',
   1305      }, { { type = 9, timestamp = 0, data = { { f = 10 }, { f = 20 } } } })
   1306      sd2strings_eq(
   1307        {
   1308          'Buffer list with timestamp ' .. epoch .. ':',
   1309          '  % Key  Description  Value',
   1310          '  # Expected binary string',
   1311          '  + f    file name    10',
   1312          '  + l    line number  1',
   1313          '  + c    column       0',
   1314          '',
   1315          '  % Key  Description  Value',
   1316          '  # Expected no NUL bytes',
   1317          '  + f    file name    "\\0"',
   1318          '  + l    line number  1',
   1319          '  + c    column       0',
   1320        },
   1321        ([[ [{'type': 9, 'timestamp': 0, 'data': [
   1322        {'f': 10},
   1323        {'f': {'_TYPE': v:msgpack_types.string, '_VAL': ["\n"]}},
   1324      ]}] ]]):gsub('\n', '')
   1325      )
   1326    end)
   1327 
   1328    it('works with local mark items', function()
   1329      sd2strings_eq({
   1330        'Local mark with timestamp ' .. epoch .. ':',
   1331        '  # Unexpected type: array instead of map',
   1332        '  = [1, 2, 3]',
   1333      }, { { type = 10, timestamp = 0, data = { 1, 2, 3 } } })
   1334      sd2strings_eq(
   1335        {
   1336          'Local mark with timestamp ' .. epoch .. ':',
   1337          '  % Key  Description  Value',
   1338          '  # Required key missing: f',
   1339          "  + n    name         '\"'",
   1340          '  + l    line number  1',
   1341          '  + c    column       0',
   1342        },
   1343        ([[ [{'type': 10, 'timestamp': 0, 'data': {
   1344      }}] ]]):gsub('\n', '')
   1345      )
   1346      sd2strings_eq(
   1347        {
   1348          'Local mark with timestamp ' .. epoch .. ':',
   1349          '  % Key  Description  Value',
   1350          '  # Required key missing: f',
   1351          '  # Expected integer',
   1352          '  + n    name         "foo"',
   1353          '  + l    line number  1',
   1354          '  + c    column       0',
   1355        },
   1356        ([[ [{'type': 10, 'timestamp': 0, 'data': {
   1357        'n': 'foo',
   1358      }}] ]]):gsub('\n', '')
   1359      )
   1360      sd2strings_eq(
   1361        {
   1362          'Local mark with timestamp ' .. epoch .. ':',
   1363          '  % Key  Description  Value',
   1364          '  + f    file name    "foo"',
   1365          "  + n    name         '\"'",
   1366          '  + l    line number  1',
   1367          '  + c    column       0',
   1368        },
   1369        ([[ [{'type': 10, 'timestamp': 0, 'data': {
   1370        'f': 'foo',
   1371      }}] ]]):gsub('\n', '')
   1372      )
   1373      sd2strings_eq(
   1374        {
   1375          'Local mark with timestamp ' .. epoch .. ':',
   1376          '  % Key  Description  Value',
   1377          '  # Expected no NUL bytes',
   1378          '  + f    file name    "\\0"',
   1379          '  # Value is negative',
   1380          '  + n    name         -10',
   1381          '  + l    line number  1',
   1382          '  + c    column       0',
   1383        },
   1384        ([[ [{'type': 10, 'timestamp': 0, 'data': {
   1385        'n': -10,
   1386        'f': {'_TYPE': v:msgpack_types.string, '_VAL': ["\n"]},
   1387      }}] ]]):gsub('\n', '')
   1388      )
   1389      sd2strings_eq(
   1390        {
   1391          'Local mark with timestamp ' .. epoch .. ':',
   1392          '  % Key  Description  Value',
   1393          '  + f    file name    "foo"',
   1394          "  + n    name         '\\20'",
   1395          '  # Value is negative',
   1396          '  + l    line number  -10',
   1397          '  # Value is negative',
   1398          '  + c    column       -10',
   1399        },
   1400        ([[ [{'type': 10, 'timestamp': 0, 'data': {
   1401        'n': 20,
   1402        'f': 'foo',
   1403        'l': -10,
   1404        'c': -10,
   1405      }}] ]]):gsub('\n', '')
   1406      )
   1407      sd2strings_eq(
   1408        {
   1409          'Local mark with timestamp ' .. epoch .. ':',
   1410          '  % Key  Description  Value',
   1411          '  + f    file name    "foo"',
   1412          "  + n    name         '\\20'",
   1413          '  # Expected integer',
   1414          '  + l    line number  "FOO"',
   1415          '  # Expected integer',
   1416          '  + c    column       "foo"',
   1417          '  + mX                10',
   1418        },
   1419        ([[ [{'type': 10, 'timestamp': 0, 'data': {
   1420        'n': 20,
   1421        'f': 'foo',
   1422        'l': 'FOO',
   1423        'c': 'foo',
   1424        'mX': 10,
   1425      }}] ]]):gsub('\n', '')
   1426      )
   1427      sd2strings_eq(
   1428        {
   1429          'Local mark with timestamp ' .. epoch .. ':',
   1430          '  % Key________  Description  Value',
   1431          '  + f            file name    "foo"',
   1432          "  + n            name         'a'",
   1433          '  + l            line number  2',
   1434          '  + c            column       200',
   1435          '  + mX                        10',
   1436          '  + mYYYYYYYYYY               10',
   1437        },
   1438        ([[ [{'type': 10, 'timestamp': 0, 'data': {
   1439        'n': char2nr('a'),
   1440        'f': 'foo',
   1441        'l': 2,
   1442        'c': 200,
   1443        'mX': 10,
   1444        'mYYYYYYYYYY': 10,
   1445      }}] ]]):gsub('\n', '')
   1446      )
   1447    end)
   1448 
   1449    it('works with change items', function()
   1450      sd2strings_eq({
   1451        'Change with timestamp ' .. epoch .. ':',
   1452        '  # Unexpected type: array instead of map',
   1453        '  = [1, 2, 3]',
   1454      }, { { type = 11, timestamp = 0, data = { 1, 2, 3 } } })
   1455      sd2strings_eq(
   1456        {
   1457          'Change with timestamp ' .. epoch .. ':',
   1458          '  % Key  Description  Value',
   1459          '  # Required key missing: f',
   1460          '  + l    line number  1',
   1461          '  + c    column       0',
   1462        },
   1463        ([[ [{'type': 11, 'timestamp': 0, 'data': {
   1464      }}] ]]):gsub('\n', '')
   1465      )
   1466      sd2strings_eq(
   1467        {
   1468          'Change with timestamp ' .. epoch .. ':',
   1469          '  % Key  Description  Value',
   1470          '  # Required key missing: f',
   1471          '  + l    line number  1',
   1472          '  + c    column       0',
   1473          '  # Expected integer',
   1474          '  + n    name         "foo"',
   1475        },
   1476        ([[ [{'type': 11, 'timestamp': 0, 'data': {
   1477        'n': 'foo',
   1478      }}] ]]):gsub('\n', '')
   1479      )
   1480      sd2strings_eq(
   1481        {
   1482          'Change with timestamp ' .. epoch .. ':',
   1483          '  % Key  Description  Value',
   1484          '  + f    file name    "foo"',
   1485          '  + l    line number  1',
   1486          '  + c    column       0',
   1487        },
   1488        ([[ [{'type': 11, 'timestamp': 0, 'data': {
   1489        'f': 'foo',
   1490      }}] ]]):gsub('\n', '')
   1491      )
   1492      sd2strings_eq(
   1493        {
   1494          'Change with timestamp ' .. epoch .. ':',
   1495          '  % Key  Description  Value',
   1496          '  # Expected no NUL bytes',
   1497          '  + f    file name    "\\0"',
   1498          '  + l    line number  1',
   1499          '  + c    column       0',
   1500          '  # Value is negative',
   1501          '  + n    name         -10',
   1502        },
   1503        ([[ [{'type': 11, 'timestamp': 0, 'data': {
   1504        'n': -10,
   1505        'f': {'_TYPE': v:msgpack_types.string, '_VAL': ["\n"]},
   1506      }}] ]]):gsub('\n', '')
   1507      )
   1508      sd2strings_eq(
   1509        {
   1510          'Change with timestamp ' .. epoch .. ':',
   1511          '  % Key  Description  Value',
   1512          '  + f    file name    "foo"',
   1513          '  # Value is negative',
   1514          '  + l    line number  -10',
   1515          '  # Value is negative',
   1516          '  + c    column       -10',
   1517        },
   1518        ([[ [{'type': 11, 'timestamp': 0, 'data': {
   1519        'f': 'foo',
   1520        'l': -10,
   1521        'c': -10,
   1522      }}] ]]):gsub('\n', '')
   1523      )
   1524      sd2strings_eq(
   1525        {
   1526          'Change with timestamp ' .. epoch .. ':',
   1527          '  % Key  Description  Value',
   1528          '  + f    file name    "foo"',
   1529          '  # Expected integer',
   1530          '  + l    line number  "FOO"',
   1531          '  # Expected integer',
   1532          '  + c    column       "foo"',
   1533          '  + mX                10',
   1534        },
   1535        ([[ [{'type': 11, 'timestamp': 0, 'data': {
   1536        'f': 'foo',
   1537        'l': 'FOO',
   1538        'c': 'foo',
   1539        'mX': 10,
   1540      }}] ]]):gsub('\n', '')
   1541      )
   1542      sd2strings_eq(
   1543        {
   1544          'Change with timestamp ' .. epoch .. ':',
   1545          '  % Key________  Description  Value',
   1546          '  + f            file name    "foo"',
   1547          '  + l            line number  2',
   1548          '  + c            column       200',
   1549          '  + mX                        10',
   1550          '  + mYYYYYYYYYY               10',
   1551          "  + n            name         ' '",
   1552        },
   1553        ([[ [{'type': 11, 'timestamp': 0, 'data': {
   1554        'n': 0x20,
   1555        'f': 'foo',
   1556        'l': 2,
   1557        'c': 200,
   1558        'mX': 10,
   1559        'mYYYYYYYYYY': 10,
   1560      }}] ]]):gsub('\n', '')
   1561      )
   1562    end)
   1563  end)
   1564 
   1565  describe('function shada#get_strings', function()
   1566    it('works', function()
   1567      eq({
   1568        'Header with timestamp ' .. epoch .. ':',
   1569        '  % Key  Value',
   1570      }, nvim_eval('shada#get_strings(msgpackdump([1, 0, 0, {}]))'))
   1571    end)
   1572  end)
   1573 
   1574  describe('function shada#strings_to_sd', function()
   1575    local strings2sd_eq = function(expected, input)
   1576      api.nvim_set_var('__input', input)
   1577      nvim_command(
   1578        'let g:__actual = map(shada#strings_to_sd(g:__input), '
   1579          .. '"filter(v:val, \\"v:key[0] isnot# \'_\' '
   1580          .. '&& v:key isnot# \'length\'\\")")'
   1581      )
   1582      -- print()
   1583      if type(expected) == 'table' then
   1584        api.nvim_set_var('__expected', expected)
   1585        nvim_command('let g:__expected = ModifyVal(g:__expected)')
   1586        expected = 'g:__expected'
   1587        -- print(nvim_eval('msgpack#string(g:__expected)'))
   1588      end
   1589      -- print(nvim_eval('msgpack#string(g:__actual)'))
   1590      eq(1, nvim_eval(('msgpack#equal(%s, g:__actual)'):format(expected)))
   1591      if type(expected) == 'table' then
   1592        nvim_command('unlet g:__expected')
   1593      end
   1594      nvim_command('unlet g:__input')
   1595      nvim_command('unlet g:__actual')
   1596    end
   1597 
   1598    it('works with multiple items', function()
   1599      strings2sd_eq({
   1600        {
   1601          type = 11,
   1602          timestamp = 0,
   1603          data = {
   1604            f = 'foo',
   1605            l = 2,
   1606            c = 200,
   1607            mX = 10,
   1608            mYYYYYYYYYY = 10,
   1609            n = (' '):byte(),
   1610          },
   1611        },
   1612        {
   1613          type = 1,
   1614          timestamp = 0,
   1615          data = {
   1616            c = 'abc',
   1617            f = { '!string', { 'abc\ndef' } },
   1618            l = -10,
   1619            n = -64,
   1620            rc = '10',
   1621            rt = 10,
   1622            sc = { '!nil', 0 },
   1623            sm = 'TRUE',
   1624            so = 'TRUE',
   1625            sp = { '!string', { 'abc' } },
   1626          },
   1627        },
   1628      }, {
   1629        'Change with timestamp ' .. epoch .. ':',
   1630        '  % Key________  Description  Value',
   1631        '  + f            file name    "foo"',
   1632        '  + l            line number  2',
   1633        '  + c            column       200',
   1634        '  + mX                        10',
   1635        '  + mYYYYYYYYYY               10',
   1636        "  + n            name         ' '",
   1637        'Header with timestamp ' .. epoch .. ':',
   1638        '  % Key  Description____  Value',
   1639        '  # Expected integer',
   1640        '  + c    column           "abc"',
   1641        '  # Expected no NUL bytes',
   1642        '  + f    file name        "abc\\0def"',
   1643        '  # Value is negative',
   1644        '  + l    line number      -10',
   1645        '  # Value is negative',
   1646        '  + n    name             -64',
   1647        '  # Expected array value',
   1648        '  + rc   contents         "10"',
   1649        '  # Unexpected enum value: expected one of '
   1650          .. '0 (CHARACTERWISE), 1 (LINEWISE), 2 (BLOCKWISE)',
   1651        '  + rt   type             10',
   1652        '  # Expected boolean',
   1653        '  + sc   smartcase value  NIL',
   1654        '  # Expected boolean',
   1655        '  + sm   magic value      "TRUE"',
   1656        '  # Expected integer',
   1657        '  + so   offset value     "TRUE"',
   1658        '  # Expected binary string',
   1659        '  + sp   pattern          ="abc"',
   1660      })
   1661    end)
   1662 
   1663    it('works with empty list', function()
   1664      strings2sd_eq({}, {})
   1665    end)
   1666 
   1667    it('works with header items', function()
   1668      strings2sd_eq({ { type = 1, timestamp = 0, data = {
   1669        generator = 'test',
   1670      } } }, {
   1671        'Header with timestamp ' .. epoch .. ':',
   1672        '  % Key______  Value',
   1673        '  + generator  "test"',
   1674      })
   1675      strings2sd_eq(
   1676        { { type = 1, timestamp = 0, data = {
   1677          1,
   1678          2,
   1679          3,
   1680        } } },
   1681        {
   1682          'Header with timestamp ' .. epoch .. ':',
   1683          '  # Unexpected type: array instead of map',
   1684          '  = [1, 2, 3]',
   1685        }
   1686      )
   1687      strings2sd_eq({
   1688        {
   1689          type = 1,
   1690          timestamp = 0,
   1691          data = {
   1692            a = 1,
   1693            b = 2,
   1694            c = 3,
   1695            d = 4,
   1696          },
   1697        },
   1698      }, {
   1699        'Header with timestamp ' .. epoch .. ':',
   1700        '  % Key  Description  Value',
   1701        '  + a                 1',
   1702        '  + b                 2',
   1703        '  + c    column       3',
   1704        '  + d                 4',
   1705      })
   1706      strings2sd_eq({
   1707        {
   1708          type = 1,
   1709          timestamp = 0,
   1710          data = {
   1711            c = 'abc',
   1712            f = { '!string', { 'abc\ndef' } },
   1713            l = -10,
   1714            n = -64,
   1715            rc = '10',
   1716            rt = 10,
   1717            sc = { '!nil', 0 },
   1718            sm = 'TRUE',
   1719            so = 'TRUE',
   1720            sp = { '!string', { 'abc' } },
   1721          },
   1722        },
   1723      }, {
   1724        'Header with timestamp ' .. epoch .. ':',
   1725        '  % Key  Description____  Value',
   1726        '  # Expected integer',
   1727        '  + c    column           "abc"',
   1728        '  # Expected no NUL bytes',
   1729        '  + f    file name        "abc\\0def"',
   1730        '  # Value is negative',
   1731        '  + l    line number      -10',
   1732        '  # Value is negative',
   1733        '  + n    name             -64',
   1734        '  # Expected array value',
   1735        '  + rc   contents         "10"',
   1736        '  # Unexpected enum value: expected one of '
   1737          .. '0 (CHARACTERWISE), 1 (LINEWISE), 2 (BLOCKWISE)',
   1738        '  + rt   type             10',
   1739        '  # Expected boolean',
   1740        '  + sc   smartcase value  NIL',
   1741        '  # Expected boolean',
   1742        '  + sm   magic value      "TRUE"',
   1743        '  # Expected integer',
   1744        '  + so   offset value     "TRUE"',
   1745        '  # Expected binary string',
   1746        '  + sp   pattern          ="abc"',
   1747      })
   1748    end)
   1749 
   1750    it('works with search pattern items', function()
   1751      strings2sd_eq(
   1752        { { type = 2, timestamp = 0, data = {
   1753          1,
   1754          2,
   1755          3,
   1756        } } },
   1757        {
   1758          'Search pattern with timestamp ' .. epoch .. ':',
   1759          '  # Unexpected type: array instead of map',
   1760          '  = [1, 2, 3]',
   1761        }
   1762      )
   1763      strings2sd_eq({ { type = 2, timestamp = 0, data = {
   1764        sp = 'abc',
   1765      } } }, {
   1766        'Search pattern with timestamp ' .. epoch .. ':',
   1767        '  % Key  Description________  Value',
   1768        '  + sp   pattern              "abc"',
   1769        '  + sh   v:hlsearch value     FALSE',
   1770        '  + ss   is :s pattern        FALSE',
   1771        '  + sm   magic value          TRUE',
   1772        '  + sc   smartcase value      FALSE',
   1773        '  + sl   has line offset      FALSE',
   1774        '  + se   place cursor at end  FALSE',
   1775        '  + so   offset value         0',
   1776        '  + su   is last used         TRUE',
   1777      })
   1778      strings2sd_eq({
   1779        {
   1780          type = 2,
   1781          timestamp = 0,
   1782          data = {
   1783            sp = 'abc',
   1784            sX = { '!nil', 0 },
   1785            sY = { '!nil', 0 },
   1786            sZ = { '!nil', 0 },
   1787          },
   1788        },
   1789      }, {
   1790        'Search pattern with timestamp ' .. epoch .. ':',
   1791        '  % Key  Description________  Value',
   1792        '  + sp   pattern              "abc"',
   1793        '  + sh   v:hlsearch value     FALSE',
   1794        '  + ss   is :s pattern        FALSE',
   1795        '  + sm   magic value          TRUE',
   1796        '  + sc   smartcase value      FALSE',
   1797        '  + sl   has line offset      FALSE',
   1798        '  + se   place cursor at end  FALSE',
   1799        '  + so   offset value         0',
   1800        '  + su   is last used         TRUE',
   1801        '  + sX                        NIL',
   1802        '  + sY                        NIL',
   1803        '  + sZ                        NIL',
   1804      })
   1805      strings2sd_eq({ { type = 2, timestamp = 0, data = { '!map', {} } } }, {
   1806        'Search pattern with timestamp ' .. epoch .. ':',
   1807        '  % Key  Description________  Value',
   1808        '  # Required key missing: sp',
   1809        '  + sh   v:hlsearch value     FALSE',
   1810        '  + ss   is :s pattern        FALSE',
   1811        '  + sm   magic value          TRUE',
   1812        '  + sc   smartcase value      FALSE',
   1813        '  + sl   has line offset      FALSE',
   1814        '  + se   place cursor at end  FALSE',
   1815        '  + so   offset value         0',
   1816        '  + su   is last used         TRUE',
   1817      })
   1818      strings2sd_eq({
   1819        {
   1820          type = 2,
   1821          timestamp = 0,
   1822          data = {
   1823            sp = '',
   1824            sh = { '!boolean', 1 },
   1825            ss = { '!boolean', 1 },
   1826            sc = { '!boolean', 1 },
   1827            sl = { '!boolean', 1 },
   1828            se = { '!boolean', 1 },
   1829            sm = { '!boolean', 0 },
   1830            su = { '!boolean', 0 },
   1831            so = -10,
   1832          },
   1833        },
   1834      }, {
   1835        'Search pattern with timestamp ' .. epoch .. ':',
   1836        '  % Key  Description________  Value',
   1837        '  + sp   pattern              ""',
   1838        '  + sh   v:hlsearch value     TRUE',
   1839        '  + ss   is :s pattern        TRUE',
   1840        '  + sm   magic value          FALSE',
   1841        '  + sc   smartcase value      TRUE',
   1842        '  + sl   has line offset      TRUE',
   1843        '  + se   place cursor at end  TRUE',
   1844        '  + so   offset value         -10',
   1845        '  + su   is last used         FALSE',
   1846      })
   1847      strings2sd_eq({
   1848        {
   1849          type = 2,
   1850          timestamp = 0,
   1851          data = {
   1852            sp = 0,
   1853            sh = 0,
   1854            ss = 0,
   1855            sc = 0,
   1856            sl = 0,
   1857            se = 0,
   1858            sm = 0,
   1859            su = 0,
   1860            so = '',
   1861          },
   1862        },
   1863      }, {
   1864        'Search pattern with timestamp ' .. epoch .. ':',
   1865        '  % Key  Description________  Value',
   1866        '  # Expected binary string',
   1867        '  + sp   pattern              0',
   1868        '  # Expected boolean',
   1869        '  + sh   v:hlsearch value     0',
   1870        '  # Expected boolean',
   1871        '  + ss   is :s pattern        0',
   1872        '  # Expected boolean',
   1873        '  + sm   magic value          0',
   1874        '  # Expected boolean',
   1875        '  + sc   smartcase value      0',
   1876        '  # Expected boolean',
   1877        '  + sl   has line offset      0',
   1878        '  # Expected boolean',
   1879        '  + se   place cursor at end  0',
   1880        '  # Expected integer',
   1881        '  + so   offset value         ""',
   1882        '  # Expected boolean',
   1883        '  + su   is last used         0',
   1884      })
   1885    end)
   1886 
   1887    it('works with replacement string items', function()
   1888      strings2sd_eq({ { type = 3, timestamp = 0, data = {
   1889        a = { 10 },
   1890      } } }, {
   1891        'Replacement string with timestamp ' .. epoch .. ':',
   1892        '  # Unexpected type: map instead of array',
   1893        '  = {"a": [10]}',
   1894      })
   1895      strings2sd_eq({ { type = 3, timestamp = 0, data = {} } }, {
   1896        'Replacement string with timestamp ' .. epoch .. ':',
   1897        '  @ Description__________  Value',
   1898        '  # Expected more elements in list',
   1899      })
   1900      strings2sd_eq({ { type = 3, timestamp = 0, data = {
   1901        0,
   1902      } } }, {
   1903        'Replacement string with timestamp ' .. epoch .. ':',
   1904        '  @ Description__________  Value',
   1905        '  # Expected binary string',
   1906        '  - :s replacement string  0',
   1907      })
   1908      strings2sd_eq(
   1909        { { type = 3, timestamp = 0, data = {
   1910          'abc\ndef',
   1911          0,
   1912        } } },
   1913        {
   1914          'Replacement string with timestamp ' .. epoch .. ':',
   1915          '  @ Description__________  Value',
   1916          '  - :s replacement string  "abc\\ndef"',
   1917          '  -                        0',
   1918        }
   1919      )
   1920      strings2sd_eq({ { type = 3, timestamp = 0, data = {
   1921        'abc\ndef',
   1922      } } }, {
   1923        'Replacement string with timestamp ' .. epoch .. ':',
   1924        '  @ Description__________  Value',
   1925        '  - :s replacement string  "abc\\ndef"',
   1926      })
   1927    end)
   1928 
   1929    it('works with history entry items', function()
   1930      strings2sd_eq({ { type = 4, timestamp = 0, data = {
   1931        a = { 10 },
   1932      } } }, {
   1933        'History entry with timestamp ' .. epoch .. ':',
   1934        '  # Unexpected type: map instead of array',
   1935        '  = {"a": [10]}',
   1936      })
   1937      strings2sd_eq({ { type = 4, timestamp = 0, data = {} } }, {
   1938        'History entry with timestamp ' .. epoch .. ':',
   1939        '  @ Description_  Value',
   1940        '  # Expected more elements in list',
   1941      })
   1942      strings2sd_eq({ { type = 4, timestamp = 0, data = {
   1943        '',
   1944      } } }, {
   1945        'History entry with timestamp ' .. epoch .. ':',
   1946        '  @ Description_  Value',
   1947        '  # Expected integer',
   1948        '  - history type  ""',
   1949        '  # Expected more elements in list',
   1950      })
   1951      strings2sd_eq({ { type = 4, timestamp = 0, data = {
   1952        5,
   1953        '',
   1954      } } }, {
   1955        'History entry with timestamp ' .. epoch .. ':',
   1956        '  @ Description_  Value',
   1957        '  # Unexpected enum value: expected one of 0 (CMD), 1 (SEARCH), '
   1958          .. '2 (EXPR), 3 (INPUT), 4 (DEBUG)',
   1959        '  - history type  5',
   1960        '  - contents      ""',
   1961      })
   1962      strings2sd_eq(
   1963        { { type = 4, timestamp = 0, data = {
   1964          5,
   1965          '',
   1966          32,
   1967        } } },
   1968        {
   1969          'History entry with timestamp ' .. epoch .. ':',
   1970          '  @ Description_  Value',
   1971          '  # Unexpected enum value: expected one of 0 (CMD), 1 (SEARCH), '
   1972            .. '2 (EXPR), 3 (INPUT), 4 (DEBUG)',
   1973          '  - history type  5',
   1974          '  - contents      ""',
   1975          '  -               32',
   1976        }
   1977      )
   1978      strings2sd_eq(
   1979        { { type = 4, timestamp = 0, data = {
   1980          0,
   1981          '',
   1982          32,
   1983        } } },
   1984        {
   1985          'History entry with timestamp ' .. epoch .. ':',
   1986          '  @ Description_  Value',
   1987          '  - history type  CMD',
   1988          '  - contents      ""',
   1989          '  -               32',
   1990        }
   1991      )
   1992      strings2sd_eq(
   1993        { { type = 4, timestamp = 0, data = {
   1994          1,
   1995          '',
   1996          32,
   1997        } } },
   1998        {
   1999          'History entry with timestamp ' .. epoch .. ':',
   2000          '  @ Description_  Value',
   2001          '  - history type  SEARCH',
   2002          '  - contents      ""',
   2003          "  - separator     ' '",
   2004        }
   2005      )
   2006      strings2sd_eq({ { type = 4, timestamp = 0, data = {
   2007        1,
   2008        '',
   2009      } } }, {
   2010        'History entry with timestamp ' .. epoch .. ':',
   2011        '  @ Description_  Value',
   2012        '  - history type  SEARCH',
   2013        '  - contents      ""',
   2014        '  # Expected more elements in list',
   2015      })
   2016      strings2sd_eq({ { type = 4, timestamp = 0, data = {
   2017        2,
   2018        '',
   2019      } } }, {
   2020        'History entry with timestamp ' .. epoch .. ':',
   2021        '  @ Description_  Value',
   2022        '  - history type  EXPR',
   2023        '  - contents      ""',
   2024      })
   2025      strings2sd_eq({ { type = 4, timestamp = 0, data = {
   2026        3,
   2027        '',
   2028      } } }, {
   2029        'History entry with timestamp ' .. epoch .. ':',
   2030        '  @ Description_  Value',
   2031        '  - history type  INPUT',
   2032        '  - contents      ""',
   2033      })
   2034      strings2sd_eq({ { type = 4, timestamp = 0, data = {
   2035        4,
   2036        '',
   2037      } } }, {
   2038        'History entry with timestamp ' .. epoch .. ':',
   2039        '  @ Description_  Value',
   2040        '  - history type  DEBUG',
   2041        '  - contents      ""',
   2042      })
   2043    end)
   2044 
   2045    it('works with register items', function()
   2046      strings2sd_eq(
   2047        { { type = 5, timestamp = 0, data = {
   2048          1,
   2049          2,
   2050          3,
   2051        } } },
   2052        {
   2053          'Register with timestamp ' .. epoch .. ':',
   2054          '  # Unexpected type: array instead of map',
   2055          '  = [1, 2, 3]',
   2056        }
   2057      )
   2058      strings2sd_eq({ { type = 5, timestamp = 0, data = { '!map', {} } } }, {
   2059        'Register with timestamp ' .. epoch .. ':',
   2060        '  % Key  Description  Value',
   2061        '  # Required key missing: n',
   2062        '  # Required key missing: rc',
   2063        '  + rw   block width  0',
   2064        '  + rt   type         CHARACTERWISE',
   2065      })
   2066      strings2sd_eq({ { type = 5, timestamp = 0, data = {
   2067        n = (' '):byte(),
   2068      } } }, {
   2069        'Register with timestamp ' .. epoch .. ':',
   2070        '  % Key  Description  Value',
   2071        "  + n    name         ' '",
   2072        '  # Required key missing: rc',
   2073        '  + rw   block width  0',
   2074        '  + rt   type         CHARACTERWISE',
   2075      })
   2076      strings2sd_eq({
   2077        {
   2078          type = 5,
   2079          timestamp = 0,
   2080          data = {
   2081            n = (' '):byte(),
   2082            rc = { 'abc', 'def' },
   2083          },
   2084        },
   2085      }, {
   2086        'Register with timestamp ' .. epoch .. ':',
   2087        '  % Key  Description  Value',
   2088        "  + n    name         ' '",
   2089        '  + rc   contents     ["abc", "def"]',
   2090        '  + rw   block width  0',
   2091        '  + rt   type         CHARACTERWISE',
   2092      })
   2093      strings2sd_eq({
   2094        {
   2095          type = 5,
   2096          timestamp = 0,
   2097          data = {
   2098            n = (' '):byte(),
   2099            rc = { 'abcdefghijklmnopqrstuvwxyz', 'abcdefghijklmnopqrstuvwxyz' },
   2100          },
   2101        },
   2102      }, {
   2103        'Register with timestamp ' .. epoch .. ':',
   2104        '  % Key  Description  Value',
   2105        "  + n    name         ' '",
   2106        '  + rc   contents     @',
   2107        '  | - "abcdefghijklmnopqrstuvwxyz"',
   2108        '  | - "abcdefghijklmnopqrstuvwxyz"',
   2109        '  + rw   block width  0',
   2110        '  + rt   type         CHARACTERWISE',
   2111      })
   2112      strings2sd_eq({
   2113        {
   2114          type = 5,
   2115          timestamp = 0,
   2116          data = {
   2117            n = (' '):byte(),
   2118            rc = { 'abcdefghijklmnopqrstuvwxyz', 'abcdefghijklmnopqrstuvwxyz' },
   2119            rw = 5,
   2120            rt = 1,
   2121          },
   2122        },
   2123      }, {
   2124        'Register with timestamp ' .. epoch .. ':',
   2125        '  % Key  Description  Value',
   2126        "  + n    name         ' '",
   2127        '  + rc   contents     @',
   2128        '  | - "abcdefghijklmnopqrstuvwxyz"',
   2129        '  | - "abcdefghijklmnopqrstuvwxyz"',
   2130        '  + rw   block width  5',
   2131        '  + rt   type         LINEWISE',
   2132      })
   2133      strings2sd_eq({
   2134        {
   2135          type = 5,
   2136          timestamp = 0,
   2137          data = {
   2138            n = (' '):byte(),
   2139            rc = { 'abcdefghijklmnopqrstuvwxyz', 'abcdefghijklmnopqrstuvwxyz' },
   2140            rw = 5,
   2141            rt = 2,
   2142          },
   2143        },
   2144      }, {
   2145        'Register with timestamp ' .. epoch .. ':',
   2146        '  % Key  Description  Value',
   2147        "  + n    name         ' '",
   2148        '  + rc   contents     @',
   2149        '  | - "abcdefghijklmnopqrstuvwxyz"',
   2150        '  | - "abcdefghijklmnopqrstuvwxyz"',
   2151        '  + rw   block width  5',
   2152        '  + rt   type         BLOCKWISE',
   2153      })
   2154      strings2sd_eq({
   2155        {
   2156          type = 5,
   2157          timestamp = 0,
   2158          data = {
   2159            n = (' '):byte(),
   2160            rc = 0,
   2161            rw = -1,
   2162            rt = 10,
   2163          },
   2164        },
   2165      }, {
   2166        'Register with timestamp ' .. epoch .. ':',
   2167        '  % Key  Description  Value',
   2168        "  + n    name         ' '",
   2169        '  # Expected array value',
   2170        '  + rc   contents     0',
   2171        '  # Value is negative',
   2172        '  + rw   block width  -1',
   2173        '  # Unexpected enum value: expected one of 0 (CHARACTERWISE), '
   2174          .. '1 (LINEWISE), 2 (BLOCKWISE)',
   2175        '  + rt   type         10',
   2176      })
   2177    end)
   2178 
   2179    it('works with variable items', function()
   2180      strings2sd_eq({ { type = 6, timestamp = 0, data = {
   2181        a = { 10 },
   2182      } } }, {
   2183        'Variable with timestamp ' .. epoch .. ':',
   2184        '  # Unexpected type: map instead of array',
   2185        '  = {"a": [10]}',
   2186      })
   2187      strings2sd_eq({ { type = 6, timestamp = 0, data = {} } }, {
   2188        'Variable with timestamp ' .. epoch .. ':',
   2189        '  @ Description  Value',
   2190        '  # Expected more elements in list',
   2191      })
   2192      strings2sd_eq({ { type = 6, timestamp = 0, data = {
   2193        'foo',
   2194      } } }, {
   2195        'Variable with timestamp ' .. epoch .. ':',
   2196        '  @ Description  Value',
   2197        '  - name         "foo"',
   2198        '  # Expected more elements in list',
   2199      })
   2200      strings2sd_eq({
   2201        {
   2202          type = 6,
   2203          timestamp = 0,
   2204          data = {
   2205            'foo',
   2206            { '!nil', 0 },
   2207          },
   2208        },
   2209      }, {
   2210        'Variable with timestamp ' .. epoch .. ':',
   2211        '  @ Description  Value',
   2212        '  - name         "foo"',
   2213        '  - value        NIL',
   2214      })
   2215      strings2sd_eq({
   2216        {
   2217          type = 6,
   2218          timestamp = 0,
   2219          data = {
   2220            'foo',
   2221            { '!nil', 0 },
   2222            { '!nil', 0 },
   2223          },
   2224        },
   2225      }, {
   2226        'Variable with timestamp ' .. epoch .. ':',
   2227        '  @ Description  Value',
   2228        '  - name         "foo"',
   2229        '  - value        NIL',
   2230        '  -              NIL',
   2231      })
   2232    end)
   2233 
   2234    it('works with global mark items', function()
   2235      strings2sd_eq(
   2236        { { type = 7, timestamp = 0, data = {
   2237          1,
   2238          2,
   2239          3,
   2240        } } },
   2241        {
   2242          'Global mark with timestamp ' .. epoch .. ':',
   2243          '  # Unexpected type: array instead of map',
   2244          '  = [1, 2, 3]',
   2245        }
   2246      )
   2247      strings2sd_eq({
   2248        {
   2249          type = 7,
   2250          timestamp = 0,
   2251          data = {
   2252            n = ('A'):byte(),
   2253            f = 'foo',
   2254            l = 2,
   2255            c = 200,
   2256            mX = 10,
   2257            mYYYYYYYYYY = 10,
   2258          },
   2259        },
   2260      }, {
   2261        'Global mark with timestamp ' .. epoch .. ':',
   2262        '  % Key________  Description  Value',
   2263        "  + n            name         'A'",
   2264        '  + f            file name    "foo"',
   2265        '  + l            line number  2',
   2266        '  + c            column       200',
   2267        '  + mX                        10',
   2268        '  + mYYYYYYYYYY               10',
   2269      })
   2270    end)
   2271 
   2272    it('works with jump items', function()
   2273      strings2sd_eq(
   2274        { { type = 8, timestamp = 0, data = {
   2275          1,
   2276          2,
   2277          3,
   2278        } } },
   2279        {
   2280          'Jump with timestamp ' .. epoch .. ':',
   2281          '  # Unexpected type: array instead of map',
   2282          '  = [1, 2, 3]',
   2283        }
   2284      )
   2285      strings2sd_eq({
   2286        {
   2287          type = 8,
   2288          timestamp = 0,
   2289          data = {
   2290            n = ('A'):byte(),
   2291            f = 'foo',
   2292            l = 2,
   2293            c = 200,
   2294            mX = 10,
   2295            mYYYYYYYYYY = 10,
   2296          },
   2297        },
   2298      }, {
   2299        'Jump with timestamp ' .. epoch .. ':',
   2300        '  % Key________  Description  Value',
   2301        "  + n            name         'A'",
   2302        '  + f            file name    "foo"',
   2303        '  + l            line number  2',
   2304        '  + c            column       200',
   2305        '  + mX                        10',
   2306        '  + mYYYYYYYYYY               10',
   2307      })
   2308    end)
   2309 
   2310    it('works with buffer list items', function()
   2311      strings2sd_eq({ { type = 9, timestamp = 0, data = {
   2312        a = { 10 },
   2313      } } }, {
   2314        'Buffer list with timestamp ' .. epoch .. ':',
   2315        '  # Unexpected type: map instead of array',
   2316        '  = {"a": [10]}',
   2317      })
   2318      strings2sd_eq(
   2319        { { type = 9, timestamp = 0, data = {
   2320          { a = 10 },
   2321          {},
   2322        } } },
   2323        {
   2324          'Buffer list with timestamp ' .. epoch .. ':',
   2325          '  # Expected array of maps',
   2326          '  = [{"a": 10}, []]',
   2327        }
   2328      )
   2329      strings2sd_eq({ { type = 9, timestamp = 0, data = {
   2330        { a = 10 },
   2331      } } }, {
   2332        'Buffer list with timestamp ' .. epoch .. ':',
   2333        '  % Key  Description  Value',
   2334        '  # Required key missing: f',
   2335        '  + l    line number  1',
   2336        '  + c    column       0',
   2337        '  + a                 10',
   2338      })
   2339      strings2sd_eq({
   2340        {
   2341          type = 9,
   2342          timestamp = 0,
   2343          data = {
   2344            { l = '10', c = '10', a = 10 },
   2345          },
   2346        },
   2347      }, {
   2348        'Buffer list with timestamp ' .. epoch .. ':',
   2349        '  % Key  Description  Value',
   2350        '  # Required key missing: f',
   2351        '  # Expected integer',
   2352        '  + l    line number  "10"',
   2353        '  # Expected integer',
   2354        '  + c    column       "10"',
   2355        '  + a                 10',
   2356      })
   2357      strings2sd_eq(
   2358        { { type = 9, timestamp = 0, data = {
   2359          { l = 10, c = 10, a = 10 },
   2360        } } },
   2361        {
   2362          'Buffer list with timestamp ' .. epoch .. ':',
   2363          '  % Key  Description  Value',
   2364          '  # Required key missing: f',
   2365          '  + l    line number  10',
   2366          '  + c    column       10',
   2367          '  + a                 10',
   2368        }
   2369      )
   2370      strings2sd_eq(
   2371        { { type = 9, timestamp = 0, data = {
   2372          { l = -10, c = -10 },
   2373        } } },
   2374        {
   2375          'Buffer list with timestamp ' .. epoch .. ':',
   2376          '  % Key  Description  Value',
   2377          '  # Required key missing: f',
   2378          '  # Value is negative',
   2379          '  + l    line number  -10',
   2380          '  # Value is negative',
   2381          '  + c    column       -10',
   2382        }
   2383      )
   2384      strings2sd_eq({ { type = 9, timestamp = 0, data = {
   2385        { f = 'abc' },
   2386      } } }, {
   2387        'Buffer list with timestamp ' .. epoch .. ':',
   2388        '  % Key  Description  Value',
   2389        '  + f    file name    "abc"',
   2390        '  + l    line number  1',
   2391        '  + c    column       0',
   2392      })
   2393      strings2sd_eq({
   2394        {
   2395          type = 9,
   2396          timestamp = 0,
   2397          data = {
   2398            { f = 10 },
   2399            { f = 20 },
   2400          },
   2401        },
   2402      }, {
   2403        'Buffer list with timestamp ' .. epoch .. ':',
   2404        '  % Key  Description  Value',
   2405        '  # Expected binary string',
   2406        "  + f    file name    '\\10'",
   2407        '  + l    line number  1',
   2408        '  + c    column       0',
   2409        '',
   2410        '  % Key  Description  Value',
   2411        '  # Expected binary string',
   2412        "  + f    file name    '\\20'",
   2413        '  + l    line number  1',
   2414        '  + c    column       0',
   2415      })
   2416      strings2sd_eq({
   2417        {
   2418          type = 9,
   2419          timestamp = 0,
   2420          data = {
   2421            { f = 10 },
   2422            { f = { '!string', { '\n' } } },
   2423          },
   2424        },
   2425      }, {
   2426        'Buffer list with timestamp ' .. epoch .. ':',
   2427        '  % Key  Description  Value',
   2428        '  # Expected binary string',
   2429        "  + f    file name    '\\10'",
   2430        '  + l    line number  1',
   2431        '  + c    column       0',
   2432        '',
   2433        '  % Key  Description  Value',
   2434        '  # Expected no NUL bytes',
   2435        '  + f    file name    "\\0"',
   2436        '  + l    line number  1',
   2437        '  + c    column       0',
   2438      })
   2439    end)
   2440 
   2441    it('works with local mark items', function()
   2442      strings2sd_eq(
   2443        { { type = 10, timestamp = 0, data = {
   2444          1,
   2445          2,
   2446          3,
   2447        } } },
   2448        {
   2449          'Local mark with timestamp ' .. epoch .. ':',
   2450          '  # Unexpected type: array instead of map',
   2451          '  = [1, 2, 3]',
   2452        }
   2453      )
   2454      strings2sd_eq({
   2455        {
   2456          type = 10,
   2457          timestamp = 0,
   2458          data = {
   2459            n = ('A'):byte(),
   2460            f = 'foo',
   2461            l = 2,
   2462            c = 200,
   2463            mX = 10,
   2464            mYYYYYYYYYY = 10,
   2465          },
   2466        },
   2467      }, {
   2468        'Local mark with timestamp ' .. epoch .. ':',
   2469        '  % Key________  Description  Value',
   2470        "  + n            name         'A'",
   2471        '  + f            file name    "foo"',
   2472        '  + l            line number  2',
   2473        '  + c            column       200',
   2474        '  + mX                        10',
   2475        '  + mYYYYYYYYYY               10',
   2476      })
   2477    end)
   2478 
   2479    it('works with change items', function()
   2480      strings2sd_eq(
   2481        { { type = 11, timestamp = 0, data = {
   2482          1,
   2483          2,
   2484          3,
   2485        } } },
   2486        {
   2487          'Change with timestamp ' .. epoch .. ':',
   2488          '  # Unexpected type: array instead of map',
   2489          '  = [1, 2, 3]',
   2490        }
   2491      )
   2492      strings2sd_eq({
   2493        {
   2494          type = 11,
   2495          timestamp = 0,
   2496          data = {
   2497            n = ('A'):byte(),
   2498            f = 'foo',
   2499            l = 2,
   2500            c = 200,
   2501            mX = 10,
   2502            mYYYYYYYYYY = 10,
   2503          },
   2504        },
   2505      }, {
   2506        'Change with timestamp ' .. epoch .. ':',
   2507        '  % Key________  Description  Value',
   2508        "  + n            name         'A'",
   2509        '  + f            file name    "foo"',
   2510        '  + l            line number  2',
   2511        '  + c            column       200',
   2512        '  + mX                        10',
   2513        '  + mYYYYYYYYYY               10',
   2514      })
   2515    end)
   2516  end)
   2517 
   2518  describe('function shada#get_binstrings', function()
   2519    local getbstrings_eq = function(expected, input)
   2520      local result = fn['shada#get_binstrings'](input)
   2521      for i, s in ipairs(result) do
   2522        result[i] = s:gsub('\n', '\0')
   2523      end
   2524      local mpack_result = table.concat(result, '\n')
   2525      return mpack_eq(expected, mpack_result)
   2526    end
   2527 
   2528    it('works', function()
   2529      local version = api.nvim_get_vvar('version')
   2530      getbstrings_eq({
   2531        {
   2532          timestamp = 'current',
   2533          type = 1,
   2534          value = {
   2535            generator = 'shada.vim',
   2536            version = version,
   2537          },
   2538        },
   2539      }, {})
   2540      getbstrings_eq({
   2541        {
   2542          timestamp = 'current',
   2543          type = 1,
   2544          value = {
   2545            generator = 'shada.vim',
   2546            version = version,
   2547          },
   2548        },
   2549        { timestamp = 0, type = 1, value = { generator = 'test' } },
   2550      }, {
   2551        'Header with timestamp ' .. epoch .. ':',
   2552        '  % Key______  Value',
   2553        '  + generator  "test"',
   2554      })
   2555      api.nvim_set_var('shada#add_own_header', 1)
   2556      getbstrings_eq({
   2557        {
   2558          timestamp = 'current',
   2559          type = 1,
   2560          value = {
   2561            generator = 'shada.vim',
   2562            version = version,
   2563          },
   2564        },
   2565      }, {})
   2566      getbstrings_eq({
   2567        {
   2568          timestamp = 'current',
   2569          type = 1,
   2570          value = {
   2571            generator = 'shada.vim',
   2572            version = version,
   2573          },
   2574        },
   2575        { timestamp = 0, type = 1, value = { generator = 'test' } },
   2576      }, {
   2577        'Header with timestamp ' .. epoch .. ':',
   2578        '  % Key______  Value',
   2579        '  + generator  "test"',
   2580      })
   2581      api.nvim_set_var('shada#add_own_header', 0)
   2582      getbstrings_eq({}, {})
   2583      getbstrings_eq({ { timestamp = 0, type = 1, value = { generator = 'test' } } }, {
   2584        'Header with timestamp ' .. epoch .. ':',
   2585        '  % Key______  Value',
   2586        '  + generator  "test"',
   2587      })
   2588      api.nvim_set_var('shada#keep_old_header', 0)
   2589      getbstrings_eq({}, {
   2590        'Header with timestamp ' .. epoch .. ':',
   2591        '  % Key______  Value',
   2592        '  + generator  "test"',
   2593      })
   2594      getbstrings_eq({
   2595        { type = 3, timestamp = 0, value = { 'abc\ndef' } },
   2596        { type = 3, timestamp = 0, value = { 'abc\ndef' } },
   2597        { type = 3, timestamp = 0, value = { 'abc\ndef' } },
   2598      }, {
   2599        'Replacement string with timestamp ' .. epoch .. ':',
   2600        '  @ Description__________  Value',
   2601        '  - :s replacement string  "abc\\ndef"',
   2602        'Replacement string with timestamp ' .. epoch .. ':',
   2603        '  @ Description__________  Value',
   2604        '  - :s replacement string  "abc\\ndef"',
   2605        'Replacement string with timestamp ' .. epoch .. ':',
   2606        '  @ Description__________  Value',
   2607        '  - :s replacement string  "abc\\ndef"',
   2608      })
   2609    end)
   2610  end)
   2611 end)
   2612 
   2613 describe('plugin/shada.vim', function()
   2614  local epoch = os.date('%Y-%m-%dT%H:%M:%S', 0)
   2615  local eol = t.is_os('win') and '\r\n' or '\n'
   2616  before_each(function()
   2617    -- Note: reset() is called explicitly in each test.
   2618    os.remove(fname)
   2619    os.remove(fname .. '.tst')
   2620    os.remove(fname_tmp)
   2621  end)
   2622 
   2623  teardown(function()
   2624    os.remove(fname)
   2625    os.remove(fname .. '.tst')
   2626    os.remove(fname_tmp)
   2627  end)
   2628 
   2629  local shada_eq = function(expected, fname_)
   2630    local mpack_result = read_file(fname_)
   2631    mpack_eq(expected, mpack_result)
   2632  end
   2633 
   2634  it('event BufReadCmd', function()
   2635    reset()
   2636    wshada('\004\000\009\147\000\196\002ab\196\001a')
   2637    wshada_tmp('\004\000\009\147\000\196\002ab\196\001b')
   2638 
   2639    local bufread_commands = api.nvim_get_autocmds({ group = 'nvim.shada', event = 'BufReadCmd' })
   2640    eq(2, #bufread_commands--[[, vim.inspect(bufread_commands) ]])
   2641 
   2642    -- Need to set nohidden so that the buffer containing 'fname' is not unloaded
   2643    -- after loading 'fname_tmp', otherwise the '++opt not supported' test below
   2644    -- won't work since the BufReadCmd autocmd won't be triggered.
   2645    nvim_command('set nohidden')
   2646 
   2647    nvim_command('edit ' .. fname)
   2648    eq({
   2649      'History entry with timestamp ' .. epoch .. ':',
   2650      '  @ Description_  Value',
   2651      '  - history type  CMD',
   2652      '  - contents      "ab"',
   2653      '  -               "a"',
   2654    }, nvim_eval('getline(1, "$")'))
   2655    eq(false, api.nvim_get_option_value('modified', {}))
   2656    eq('shada', api.nvim_get_option_value('filetype', {}))
   2657    nvim_command('edit ' .. fname_tmp)
   2658    eq({
   2659      'History entry with timestamp ' .. epoch .. ':',
   2660      '  @ Description_  Value',
   2661      '  - history type  CMD',
   2662      '  - contents      "ab"',
   2663      '  -               "b"',
   2664    }, nvim_eval('getline(1, "$")'))
   2665    eq(false, api.nvim_get_option_value('modified', {}))
   2666    eq('shada', api.nvim_get_option_value('filetype', {}))
   2667    t.matches(
   2668      '++opt not supported',
   2669      t.pcall_err(function()
   2670        nvim_command('edit ++enc=latin1 ' .. fname)
   2671      end)
   2672    )
   2673    neq({
   2674      'History entry with timestamp ' .. epoch .. ':',
   2675      '  @ Description_  Value',
   2676      '  - history type  CMD',
   2677      '  - contents      "ab"',
   2678      '  -               "a"',
   2679    }, nvim_eval('getline(1, "$")'))
   2680    neq(true, api.nvim_get_option_value('modified', {}))
   2681  end)
   2682 
   2683  it('event FileReadCmd', function()
   2684    reset()
   2685    wshada('\004\000\009\147\000\196\002ab\196\001a')
   2686    wshada_tmp('\004\000\009\147\000\196\002ab\196\001b')
   2687    nvim_command('$read ' .. fname)
   2688    eq({
   2689      '',
   2690      'History entry with timestamp ' .. epoch .. ':',
   2691      '  @ Description_  Value',
   2692      '  - history type  CMD',
   2693      '  - contents      "ab"',
   2694      '  -               "a"',
   2695    }, nvim_eval('getline(1, "$")'))
   2696    eq(true, api.nvim_get_option_value('modified', {}))
   2697    neq('shada', api.nvim_get_option_value('filetype', {}))
   2698    nvim_command('1,$read ' .. fname_tmp)
   2699    eq({
   2700      '',
   2701      'History entry with timestamp ' .. epoch .. ':',
   2702      '  @ Description_  Value',
   2703      '  - history type  CMD',
   2704      '  - contents      "ab"',
   2705      '  -               "a"',
   2706      'History entry with timestamp ' .. epoch .. ':',
   2707      '  @ Description_  Value',
   2708      '  - history type  CMD',
   2709      '  - contents      "ab"',
   2710      '  -               "b"',
   2711    }, nvim_eval('getline(1, "$")'))
   2712    eq(true, api.nvim_get_option_value('modified', {}))
   2713    neq('shada', api.nvim_get_option_value('filetype', {}))
   2714    api.nvim_set_option_value('modified', false, {})
   2715    t.matches(
   2716      '++opt not supported',
   2717      t.pcall_err(function()
   2718        nvim_command('$read ++enc=latin1 ' .. fname)
   2719      end)
   2720    )
   2721    eq({
   2722      '',
   2723      'History entry with timestamp ' .. epoch .. ':',
   2724      '  @ Description_  Value',
   2725      '  - history type  CMD',
   2726      '  - contents      "ab"',
   2727      '  -               "a"',
   2728      'History entry with timestamp ' .. epoch .. ':',
   2729      '  @ Description_  Value',
   2730      '  - history type  CMD',
   2731      '  - contents      "ab"',
   2732      '  -               "b"',
   2733    }, nvim_eval('getline(1, "$")'))
   2734    neq(true, api.nvim_get_option_value('modified', {}))
   2735  end)
   2736 
   2737  it('event BufWriteCmd', function()
   2738    reset()
   2739    api.nvim_set_var('shada#add_own_header', 0)
   2740    api.nvim_buf_set_lines(0, 0, 1, true, {
   2741      'Jump with timestamp ' .. epoch .. ':',
   2742      '  % Key________  Description  Value',
   2743      "  + n            name         'A'",
   2744      '  + f            file name    ["foo"]',
   2745      '  + l            line number  2',
   2746      '  + c            column       -200',
   2747      'Jump with timestamp ' .. epoch .. ':',
   2748      '  % Key________  Description  Value',
   2749      "  + n            name         'A'",
   2750      '  + f            file name    ["foo"]',
   2751      '  + l            line number  2',
   2752      '  + c            column       -200',
   2753    })
   2754    nvim_command('w ' .. fname .. '.tst')
   2755    nvim_command('w ' .. fname)
   2756    nvim_command('w ' .. fname_tmp)
   2757    t.matches(
   2758      '++opt not supported',
   2759      t.pcall_err(function()
   2760        nvim_command('w! ++enc=latin1 ' .. fname)
   2761      end)
   2762    )
   2763    eq(table.concat({
   2764      'Jump with timestamp ' .. epoch .. ':',
   2765      '  % Key________  Description  Value',
   2766      "  + n            name         'A'",
   2767      '  + f            file name    ["foo"]',
   2768      '  + l            line number  2',
   2769      '  + c            column       -200',
   2770      'Jump with timestamp ' .. epoch .. ':',
   2771      '  % Key________  Description  Value',
   2772      "  + n            name         'A'",
   2773      '  + f            file name    ["foo"]',
   2774      '  + l            line number  2',
   2775      '  + c            column       -200',
   2776    }, eol) .. eol, read_file(fname .. '.tst'))
   2777    shada_eq({
   2778      {
   2779        timestamp = 0,
   2780        type = 8,
   2781        value = { c = -200, f = { 'foo' }, l = 2, n = ('A'):byte() },
   2782      },
   2783      {
   2784        timestamp = 0,
   2785        type = 8,
   2786        value = { c = -200, f = { 'foo' }, l = 2, n = ('A'):byte() },
   2787      },
   2788    }, fname)
   2789    shada_eq({
   2790      {
   2791        timestamp = 0,
   2792        type = 8,
   2793        value = { c = -200, f = { 'foo' }, l = 2, n = ('A'):byte() },
   2794      },
   2795      {
   2796        timestamp = 0,
   2797        type = 8,
   2798        value = { c = -200, f = { 'foo' }, l = 2, n = ('A'):byte() },
   2799      },
   2800    }, fname_tmp)
   2801  end)
   2802 
   2803  it('event FileWriteCmd', function()
   2804    reset()
   2805    api.nvim_set_var('shada#add_own_header', 0)
   2806    api.nvim_buf_set_lines(0, 0, 1, true, {
   2807      'Jump with timestamp ' .. epoch .. ':',
   2808      '  % Key________  Description  Value',
   2809      "  + n            name         'A'",
   2810      '  + f            file name    ["foo"]',
   2811      '  + l            line number  2',
   2812      '  + c            column       -200',
   2813      'Jump with timestamp ' .. epoch .. ':',
   2814      '  % Key________  Description  Value',
   2815      "  + n            name         'A'",
   2816      '  + f            file name    ["foo"]',
   2817      '  + l            line number  2',
   2818      '  + c            column       -200',
   2819    })
   2820    nvim_command('1,3w ' .. fname .. '.tst')
   2821    nvim_command('1,3w ' .. fname)
   2822    nvim_command('1,3w ' .. fname_tmp)
   2823    t.matches(
   2824      '++opt not supported',
   2825      t.pcall_err(function()
   2826        nvim_command('1,3w! ++enc=latin1 ' .. fname)
   2827      end)
   2828    )
   2829    eq(table.concat({
   2830      'Jump with timestamp ' .. epoch .. ':',
   2831      '  % Key________  Description  Value',
   2832      "  + n            name         'A'",
   2833    }, eol) .. eol, read_file(fname .. '.tst'))
   2834    shada_eq(
   2835      { {
   2836        timestamp = 0,
   2837        type = 8,
   2838        value = { n = ('A'):byte() },
   2839      } },
   2840      fname
   2841    )
   2842    shada_eq(
   2843      { {
   2844        timestamp = 0,
   2845        type = 8,
   2846        value = { n = ('A'):byte() },
   2847      } },
   2848      fname_tmp
   2849    )
   2850  end)
   2851 
   2852  it('event FileAppendCmd', function()
   2853    reset()
   2854    api.nvim_set_var('shada#add_own_header', 0)
   2855    api.nvim_buf_set_lines(0, 0, 1, true, {
   2856      'Jump with timestamp ' .. epoch .. ':',
   2857      '  % Key________  Description  Value',
   2858      "  + n            name         'A'",
   2859      '  + f            file name    ["foo"]',
   2860      '  + l            line number  2',
   2861      '  + c            column       -200',
   2862      'Jump with timestamp ' .. epoch .. ':',
   2863      '  % Key________  Description  Value',
   2864      "  + n            name         'A'",
   2865      '  + f            file name    ["foo"]',
   2866      '  + l            line number  2',
   2867      '  + c            column       -200',
   2868    })
   2869    fn.writefile({ '' }, fname .. '.tst', 'b')
   2870    fn.writefile({ '' }, fname, 'b')
   2871    fn.writefile({ '' }, fname_tmp, 'b')
   2872    nvim_command('1,3w >> ' .. fname .. '.tst')
   2873    nvim_command('1,3w >> ' .. fname)
   2874    nvim_command('1,3w >> ' .. fname_tmp)
   2875    nvim_command('w >> ' .. fname .. '.tst')
   2876    nvim_command('w >> ' .. fname)
   2877    nvim_command('w >> ' .. fname_tmp)
   2878    t.matches(
   2879      '++opt not supported',
   2880      t.pcall_err(function()
   2881        nvim_command('1,3w! ++enc=latin1 >> ' .. fname)
   2882      end)
   2883    )
   2884    eq(table.concat({
   2885      'Jump with timestamp ' .. epoch .. ':',
   2886      '  % Key________  Description  Value',
   2887      "  + n            name         'A'",
   2888      'Jump with timestamp ' .. epoch .. ':',
   2889      '  % Key________  Description  Value',
   2890      "  + n            name         'A'",
   2891      '  + f            file name    ["foo"]',
   2892      '  + l            line number  2',
   2893      '  + c            column       -200',
   2894      'Jump with timestamp ' .. epoch .. ':',
   2895      '  % Key________  Description  Value',
   2896      "  + n            name         'A'",
   2897      '  + f            file name    ["foo"]',
   2898      '  + l            line number  2',
   2899      '  + c            column       -200',
   2900    }, eol) .. eol, read_file(fname .. '.tst'))
   2901    shada_eq({
   2902      {
   2903        timestamp = 0,
   2904        type = 8,
   2905        value = { n = ('A'):byte() },
   2906      },
   2907      {
   2908        timestamp = 0,
   2909        type = 8,
   2910        value = { c = -200, f = { 'foo' }, l = 2, n = ('A'):byte() },
   2911      },
   2912      {
   2913        timestamp = 0,
   2914        type = 8,
   2915        value = { c = -200, f = { 'foo' }, l = 2, n = ('A'):byte() },
   2916      },
   2917    }, fname)
   2918    shada_eq({
   2919      {
   2920        timestamp = 0,
   2921        type = 8,
   2922        value = { n = ('A'):byte() },
   2923      },
   2924      {
   2925        timestamp = 0,
   2926        type = 8,
   2927        value = { c = -200, f = { 'foo' }, l = 2, n = ('A'):byte() },
   2928      },
   2929      {
   2930        timestamp = 0,
   2931        type = 8,
   2932        value = { c = -200, f = { 'foo' }, l = 2, n = ('A'):byte() },
   2933      },
   2934    }, fname_tmp)
   2935  end)
   2936 
   2937  it('event SourceCmd', function()
   2938    reset(fname)
   2939    finally(function()
   2940      nvim_command('set shadafile=NONE') -- Avoid writing shada file on exit
   2941    end)
   2942    wshada('\004\000\006\146\000\196\002ab')
   2943    wshada_tmp('\004\001\006\146\000\196\002bc')
   2944    eq(0, exc_exec('source ' .. fname))
   2945    eq(0, exc_exec('source ' .. fname_tmp))
   2946    eq('bc', fn.histget(':', -1))
   2947    eq('ab', fn.histget(':', -2))
   2948  end)
   2949 end)
   2950 
   2951 describe('ftplugin/shada.vim', function()
   2952  local epoch = os.date('%Y-%m-%dT%H:%M:%S', 0)
   2953  before_each(reset)
   2954 
   2955  it('sets indentexpr correctly', function()
   2956    nvim_command('filetype plugin indent on')
   2957    nvim_command('setlocal filetype=shada')
   2958    fn.setline(1, {
   2959      'Jump with timestamp ' .. epoch .. ':',
   2960      '% Key________  Description  Value',
   2961      "+ n            name         'A'",
   2962      '+ f            file name    "foo"',
   2963      '+ l            line number  2',
   2964      '+ c            column       200',
   2965      '+ mX                        10',
   2966      '+ mYYYYYYYYYY               10',
   2967      'Register with timestamp ' .. epoch .. ':',
   2968      '% Key  Description  Value',
   2969      "+ n    name         ' '",
   2970      '+ rc   contents     @',
   2971      '| - "abcdefghijklmnopqrstuvwxyz"',
   2972      '| - "abcdefghijklmnopqrstuvwxyz"',
   2973      '+ rw   block width  0',
   2974      '+ rt   type         CHARACTERWISE',
   2975      'Replacement string with timestamp ' .. epoch .. ':',
   2976      '    @ Description__________  Value',
   2977      '    - :s replacement string  "abc\\ndef"',
   2978      '   Buffer list with timestamp ' .. epoch .. ':',
   2979      '    # Expected array of maps',
   2980      '= [{"a": 10}, []]',
   2981      '    Buffer list with timestamp ' .. epoch .. ':',
   2982      '   % Key  Description  Value',
   2983      '  # Expected binary string',
   2984      '+ f    file name    10',
   2985      ' + l    line number  1',
   2986      '   + c    column       0',
   2987      '',
   2988      ' % Key  Description  Value',
   2989      '    # Expected binary string',
   2990      ' + f    file name    20',
   2991      '+ l    line number  1',
   2992      '    + c    column       0',
   2993    })
   2994    nvim_command('normal! gg=G')
   2995    eq({
   2996      'Jump with timestamp ' .. epoch .. ':',
   2997      '  % Key________  Description  Value',
   2998      "  + n            name         'A'",
   2999      '  + f            file name    "foo"',
   3000      '  + l            line number  2',
   3001      '  + c            column       200',
   3002      '  + mX                        10',
   3003      '  + mYYYYYYYYYY               10',
   3004      'Register with timestamp ' .. epoch .. ':',
   3005      '  % Key  Description  Value',
   3006      "  + n    name         ' '",
   3007      '  + rc   contents     @',
   3008      '  | - "abcdefghijklmnopqrstuvwxyz"',
   3009      '  | - "abcdefghijklmnopqrstuvwxyz"',
   3010      '  + rw   block width  0',
   3011      '  + rt   type         CHARACTERWISE',
   3012      'Replacement string with timestamp ' .. epoch .. ':',
   3013      '  @ Description__________  Value',
   3014      '  - :s replacement string  "abc\\ndef"',
   3015      'Buffer list with timestamp ' .. epoch .. ':',
   3016      '  # Expected array of maps',
   3017      '  = [{"a": 10}, []]',
   3018      'Buffer list with timestamp ' .. epoch .. ':',
   3019      '  % Key  Description  Value',
   3020      '  # Expected binary string',
   3021      '  + f    file name    10',
   3022      '  + l    line number  1',
   3023      '  + c    column       0',
   3024      '',
   3025      '  % Key  Description  Value',
   3026      '  # Expected binary string',
   3027      '  + f    file name    20',
   3028      '  + l    line number  1',
   3029      '  + c    column       0',
   3030    }, fn.getline(1, fn.line('$')))
   3031  end)
   3032 
   3033  it('sets options correctly', function()
   3034    nvim_command('filetype plugin indent on')
   3035    nvim_command('setlocal filetype=shada')
   3036    eq(true, api.nvim_get_option_value('expandtab', {}))
   3037    eq(2, api.nvim_get_option_value('tabstop', {}))
   3038    eq(2, api.nvim_get_option_value('softtabstop', {}))
   3039    eq(2, api.nvim_get_option_value('shiftwidth', {}))
   3040  end)
   3041 
   3042  it('sets indentkeys correctly', function()
   3043    nvim_command('filetype plugin indent on')
   3044    nvim_command('setlocal filetype=shada')
   3045    fn.setline(1, '  Replacement with timestamp ' .. epoch)
   3046    nvim_feed('ggA:\027')
   3047    eq('Replacement with timestamp ' .. epoch .. ':', api.nvim_buf_get_lines(0, 0, 1, true)[1])
   3048    nvim_feed('o-\027')
   3049    eq({ '  -' }, api.nvim_buf_get_lines(0, 1, 2, true))
   3050    nvim_feed('ggO+\027')
   3051    eq({ '+' }, api.nvim_buf_get_lines(0, 0, 1, true))
   3052    nvim_feed('GO*\027')
   3053    eq({ '  *' }, api.nvim_buf_get_lines(0, 2, 3, true))
   3054    nvim_feed('ggO  /\027')
   3055    eq({ '  /' }, api.nvim_buf_get_lines(0, 0, 1, true))
   3056    nvim_feed('ggOx\027')
   3057    eq({ 'x' }, api.nvim_buf_get_lines(0, 0, 1, true))
   3058  end)
   3059 end)
   3060 
   3061 describe('syntax/shada.vim', function()
   3062  local epoch = os.date('!%Y-%m-%dT%H:%M:%S', 0)
   3063  before_each(reset)
   3064 
   3065  it('works', function()
   3066    nvim_command('syntax on')
   3067    nvim_command('setlocal syntax=shada')
   3068    nvim_command('set laststatus&')
   3069    local screen = Screen.new(60, 37)
   3070    screen:set_default_attr_ids {
   3071      [1] = { bold = true, foreground = Screen.colors.Brown },
   3072      [2] = { foreground = tonumber('0x6a0dad') },
   3073      [3] = { foreground = Screen.colors.Fuchsia },
   3074      [4] = { foreground = Screen.colors.Blue1 },
   3075      [5] = { bold = true, foreground = Screen.colors.SeaGreen4 },
   3076      [6] = { foreground = Screen.colors.SlateBlue },
   3077      [7] = { bold = true, reverse = true },
   3078      [8] = { bold = true, foreground = Screen.colors.Blue },
   3079    }
   3080 
   3081    api.nvim_buf_set_lines(0, 0, 1, true, {
   3082      'Header with timestamp ' .. epoch .. ':',
   3083      '  % Key  Value',
   3084      '  + t    "test"',
   3085      'Jump with timestamp ' .. epoch .. ':',
   3086      '  % Key________  Description  Value',
   3087      "  + n            name         'A'",
   3088      '  + f            file name    ["foo"]',
   3089      '  + l            line number  2',
   3090      '  + c            column       -200',
   3091      'Register with timestamp ' .. epoch .. ':',
   3092      '  % Key  Description  Value',
   3093      '  + rc   contents     @',
   3094      '  | - {"abcdefghijklmnopqrstuvwxyz": 1.0}',
   3095      '  + rt   type         CHARACTERWISE',
   3096      '  + rt   type         LINEWISE',
   3097      '  + rt   type         BLOCKWISE',
   3098      'Replacement string with timestamp ' .. epoch .. ':',
   3099      '  @ Description__________  Value',
   3100      '  - :s replacement string  CMD',
   3101      '  - :s replacement string  SEARCH',
   3102      '  - :s replacement string  EXPR',
   3103      '  - :s replacement string  INPUT',
   3104      '  - :s replacement string  DEBUG',
   3105      'Buffer list with timestamp ' .. epoch .. ':',
   3106      '  # Expected array of maps',
   3107      '  = [{"a": +(10)"ac\\0df\\ngi\\"tt\\.", TRUE: FALSE}, [NIL, +(-10)""]]',
   3108      'Buffer list with timestamp ' .. epoch .. ':',
   3109      '  % Key  Description  Value',
   3110      '',
   3111      '  % Key  Description  Value',
   3112      'Header with timestamp ' .. epoch .. ':',
   3113      '  % Key  Description________  Value',
   3114      '  + se   place cursor at end  TRUE',
   3115    })
   3116    screen:expect {
   3117      grid = [=[
   3118      {1:^Header} with timestamp 1970{1:-}01{1:-}01{1:T}00{1::}00{1::}00:                  |
   3119      {2:  % Key  Value}                                              |
   3120       {1: +} {3:t  }  {1:"}{3:test}{1:"}                                             |
   3121      {1:Jump} with timestamp 1970{1:-}01{1:-}01{1:T}00{1::}00{1::}00:                    |
   3122      {2:  % Key________  Description  Value}                         |
   3123       {1: +} {3:n          }  {4:name       }  {3:'A'}                           |
   3124       {1: +} {3:f          }  {4:file name  }  {1:["}{3:foo}{1:"]}                       |
   3125       {1: +} {3:l          }  {4:line number}  {3:2}                             |
   3126       {1: +} {3:c          }  {4:column     }  {3:-200}                          |
   3127      {1:Register} with timestamp 1970{1:-}01{1:-}01{1:T}00{1::}00{1::}00:                |
   3128      {2:  % Key  Description  Value}                                 |
   3129       {1: +} {3:rc }  {4:contents   }  {1:@}                                     |
   3130       {1: | -} {1:{"}{3:abcdefghijklmnopqrstuvwxyz}{1:":} {3:1.0}{1:}}                   |
   3131       {1: +} {3:rt }  {4:type       }  {1:CHARACTERWISE}                         |
   3132       {1: +} {3:rt }  {4:type       }  {1:LINEWISE}                              |
   3133       {1: +} {3:rt }  {4:type       }  {1:BLOCKWISE}                             |
   3134      {1:Replacement string} with timestamp 1970{1:-}01{1:-}01{1:T}00{1::}00{1::}00:      |
   3135      {2:  @ Description__________  Value}                            |
   3136       {1: -} {4::s replacement string}  {1:CMD}                              |
   3137       {1: -} {4::s replacement string}  {1:SEARCH}                           |
   3138       {1: -} {4::s replacement string}  {1:EXPR}                             |
   3139       {1: -} {4::s replacement string}  {1:INPUT}                            |
   3140       {1: -} {4::s replacement string}  {1:DEBUG}                            |
   3141      {1:Buffer list} with timestamp 1970{1:-}01{1:-}01{1:T}00{1::}00{1::}00:             |
   3142      {4:  # Expected array of maps}                                  |
   3143        = {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}|
   3144      {5:0}{1:)""]]}                                                      |
   3145      {1:Buffer list} with timestamp 1970{1:-}01{1:-}01{1:T}00{1::}00{1::}00:             |
   3146      {2:  % Key  Description  Value}                                 |
   3147                                                                  |
   3148      {2:  % Key  Description  Value}                                 |
   3149      {1:Header} with timestamp 1970{1:-}01{1:-}01{1:T}00{1::}00{1::}00:                  |
   3150      {2:  % Key  Description________  Value}                         |
   3151       {1: +} {3:se }  {4:place cursor at end}  {1:TRUE}                          |
   3152      {8:~                                                           }|
   3153      {7:[No Name] [+]                                               }|
   3154                                                                  |
   3155    ]=],
   3156    }
   3157 
   3158    nvim_command([[
   3159      function GetSyntax()
   3160        let lines = []
   3161        for l in range(1, line('$'))
   3162          let columns = []
   3163          let line = getline(l)
   3164          for c in range(1, col([l, '$']) - 1)
   3165            let synstack = map(synstack(l, c), 'synIDattr(v:val, "name")')
   3166            if !empty(columns) && columns[-1][0] ==# synstack
   3167              let columns[-1][1] .= line[c - 1]
   3168            else
   3169              call add(columns, [ synstack, line[c - 1] ])
   3170            endif
   3171          endfor
   3172          call add(lines, columns)
   3173        endfor
   3174        return lines
   3175      endfunction
   3176    ]])
   3177    local hname = function(s)
   3178      return { { 'ShaDaEntryHeader', 'ShaDaEntryName' }, s }
   3179    end
   3180    local h = function(s)
   3181      return { { 'ShaDaEntryHeader' }, s }
   3182    end
   3183    local htsnum = function(s)
   3184      return {
   3185        { 'ShaDaEntryHeader', 'ShaDaEntryTimestamp', 'ShaDaEntryTimestampNumber' },
   3186        s,
   3187      }
   3188    end
   3189    local synhtssep = function(s)
   3190      return { { 'ShaDaEntryHeader', 'ShaDaEntryTimestamp' }, s }
   3191    end
   3192    local synepoch = {
   3193      year = htsnum(os.date('!%Y', 0)),
   3194      month = htsnum(os.date('!%m', 0)),
   3195      day = htsnum(os.date('!%d', 0)),
   3196      hour = htsnum(os.date('!%H', 0)),
   3197      minute = htsnum(os.date('!%M', 0)),
   3198      second = htsnum(os.date('!%S', 0)),
   3199    }
   3200    local msh = function(s)
   3201      return {
   3202        { 'ShaDaEntryMapShort', 'ShaDaEntryMapHeader' },
   3203        s,
   3204      }
   3205    end
   3206    local mlh = function(s)
   3207      return { { 'ShaDaEntryMapLong', 'ShaDaEntryMapHeader' }, s }
   3208    end
   3209    local ah = function(s)
   3210      return { { 'ShaDaEntryArray', 'ShaDaEntryArrayHeader' }, s }
   3211    end
   3212    -- luacheck: ignore
   3213    local mses = function(s)
   3214      return {
   3215        {
   3216          'ShaDaEntryMapShort',
   3217          'ShaDaEntryMapShortEntryStart',
   3218        },
   3219        s,
   3220      }
   3221    end
   3222    local mles = function(s)
   3223      return {
   3224        { 'ShaDaEntryMapLong', 'ShaDaEntryMapLongEntryStart' },
   3225        s,
   3226      }
   3227    end
   3228    local act = fn.GetSyntax()
   3229    local ms = function(syn)
   3230      return {
   3231        { 'ShaDaEntryMap' .. syn, 'ShaDaEntryMap' .. syn .. 'EntryStart' },
   3232        '  + ',
   3233      }
   3234    end
   3235    local as = function()
   3236      return { { 'ShaDaEntryArray', 'ShaDaEntryArrayEntryStart' }, '  - ' }
   3237    end
   3238    local ad = function(s)
   3239      return {
   3240        { 'ShaDaEntryArray', 'ShaDaEntryArrayDescription' },
   3241        s,
   3242      }
   3243    end
   3244    local mbas = function(syn)
   3245      return {
   3246        { 'ShaDaEntryMap' .. syn, 'ShaDaEntryMapBinArrayStart' },
   3247        '  | - ',
   3248      }
   3249    end
   3250    local msk = function(s)
   3251      return {
   3252        { 'ShaDaEntryMapShort', 'ShaDaEntryMapShortKey' },
   3253        s,
   3254      }
   3255    end
   3256    local mlk = function(s)
   3257      return {
   3258        { 'ShaDaEntryMapLong', 'ShaDaEntryMapLongKey' },
   3259        s,
   3260      }
   3261    end
   3262    local mld = function(s)
   3263      return {
   3264        { 'ShaDaEntryMapLong', 'ShaDaEntryMapLongDescription' },
   3265        s,
   3266      }
   3267    end
   3268    local c = function(s)
   3269      return { { 'ShaDaComment' }, s }
   3270    end
   3271    local exp = {
   3272      {
   3273        hname('Header'),
   3274        h(' with timestamp '),
   3275        synepoch.year,
   3276        synhtssep('-'),
   3277        synepoch.month,
   3278        synhtssep('-'),
   3279        synepoch.day,
   3280        synhtssep('T'),
   3281        synepoch.hour,
   3282        synhtssep(':'),
   3283        synepoch.minute,
   3284        synhtssep(':'),
   3285        synepoch.second,
   3286        h(':'),
   3287      },
   3288      {
   3289        msh('  % Key  Value'),
   3290      },
   3291      {
   3292        ms('Short'),
   3293        msk('t    '),
   3294        {
   3295          { 'ShaDaEntryMapShort', 'ShaDaMsgpackBinaryString', 'ShaDaMsgpackStringQuotes' },
   3296          '"',
   3297        },
   3298        { { 'ShaDaEntryMapShort', 'ShaDaMsgpackBinaryString' }, 'test' },
   3299        { { 'ShaDaEntryMapShort', 'ShaDaMsgpackStringQuotes' }, '"' },
   3300      },
   3301      {
   3302        hname('Jump'),
   3303        h(' with timestamp '),
   3304        synepoch.year,
   3305        synhtssep('-'),
   3306        synepoch.month,
   3307        synhtssep('-'),
   3308        synepoch.day,
   3309        synhtssep('T'),
   3310        synepoch.hour,
   3311        synhtssep(':'),
   3312        synepoch.minute,
   3313        synhtssep(':'),
   3314        synepoch.second,
   3315        h(':'),
   3316      },
   3317      {
   3318        mlh('  % Key________  Description  Value'),
   3319      },
   3320      {
   3321        ms('Long'),
   3322        mlk('n            '),
   3323        mld('name         '),
   3324        { { 'ShaDaEntryMapLong', 'ShaDaMsgpackCharacter' }, "'A'" },
   3325      },
   3326      {
   3327        ms('Long'),
   3328        mlk('f            '),
   3329        mld('file name    '),
   3330        { { 'ShaDaEntryMapLong', 'ShaDaMsgpackArray', 'ShaDaMsgpackArrayBraces' }, '[' },
   3331        {
   3332          {
   3333            'ShaDaEntryMapLong',
   3334            'ShaDaMsgpackArray',
   3335            'ShaDaMsgpackBinaryString',
   3336            'ShaDaMsgpackStringQuotes',
   3337          },
   3338          '"',
   3339        },
   3340        { { 'ShaDaEntryMapLong', 'ShaDaMsgpackArray', 'ShaDaMsgpackBinaryString' }, 'foo' },
   3341        { { 'ShaDaEntryMapLong', 'ShaDaMsgpackArray', 'ShaDaMsgpackStringQuotes' }, '"' },
   3342        { { 'ShaDaEntryMapLong', 'ShaDaMsgpackArrayBraces' }, ']' },
   3343      },
   3344      {
   3345        ms('Long'),
   3346        mlk('l            '),
   3347        mld('line number  '),
   3348        { { 'ShaDaEntryMapLong', 'ShaDaMsgpackInteger' }, '2' },
   3349      },
   3350      {
   3351        ms('Long'),
   3352        mlk('c            '),
   3353        mld('column       '),
   3354        { { 'ShaDaEntryMapLong', 'ShaDaMsgpackInteger' }, '-200' },
   3355      },
   3356      {
   3357        hname('Register'),
   3358        h(' with timestamp '),
   3359        synepoch.year,
   3360        synhtssep('-'),
   3361        synepoch.month,
   3362        synhtssep('-'),
   3363        synepoch.day,
   3364        synhtssep('T'),
   3365        synepoch.hour,
   3366        synhtssep(':'),
   3367        synepoch.minute,
   3368        synhtssep(':'),
   3369        synepoch.second,
   3370        h(':'),
   3371      },
   3372      {
   3373        mlh('  % Key  Description  Value'),
   3374      },
   3375      {
   3376        ms('Long'),
   3377        mlk('rc   '),
   3378        mld('contents     '),
   3379        { { 'ShaDaEntryMapLong', 'ShaDaMsgpackMultilineArray' }, '@' },
   3380      },
   3381      {
   3382        mbas('Long'),
   3383        { { 'ShaDaEntryMapLong', 'ShaDaMsgpackMap', 'ShaDaMsgpackMapBraces' }, '{' },
   3384        {
   3385          {
   3386            'ShaDaEntryMapLong',
   3387            'ShaDaMsgpackMap',
   3388            'ShaDaMsgpackBinaryString',
   3389            'ShaDaMsgpackStringQuotes',
   3390          },
   3391          '"',
   3392        },
   3393        {
   3394          { 'ShaDaEntryMapLong', 'ShaDaMsgpackMap', 'ShaDaMsgpackBinaryString' },
   3395          'abcdefghijklmnopqrstuvwxyz',
   3396        },
   3397        { { 'ShaDaEntryMapLong', 'ShaDaMsgpackMap', 'ShaDaMsgpackStringQuotes' }, '"' },
   3398        { { 'ShaDaEntryMapLong', 'ShaDaMsgpackMap', 'ShaDaMsgpackColon' }, ':' },
   3399        { { 'ShaDaEntryMapLong', 'ShaDaMsgpackMap' }, ' ' },
   3400        { { 'ShaDaEntryMapLong', 'ShaDaMsgpackMap', 'ShaDaMsgpackFloat' }, '1.0' },
   3401        { { 'ShaDaEntryMapLong', 'ShaDaMsgpackMapBraces' }, '}' },
   3402      },
   3403      {
   3404        ms('Long'),
   3405        mlk('rt   '),
   3406        mld('type         '),
   3407        { { 'ShaDaEntryMapLong', 'ShaDaMsgpackShaDaKeyword' }, 'CHARACTERWISE' },
   3408      },
   3409      {
   3410        ms('Long'),
   3411        mlk('rt   '),
   3412        mld('type         '),
   3413        { { 'ShaDaEntryMapLong', 'ShaDaMsgpackShaDaKeyword' }, 'LINEWISE' },
   3414      },
   3415      {
   3416        ms('Long'),
   3417        mlk('rt   '),
   3418        mld('type         '),
   3419        { { 'ShaDaEntryMapLong', 'ShaDaMsgpackShaDaKeyword' }, 'BLOCKWISE' },
   3420      },
   3421      {
   3422        hname('Replacement string'),
   3423        h(' with timestamp '),
   3424        synepoch.year,
   3425        synhtssep('-'),
   3426        synepoch.month,
   3427        synhtssep('-'),
   3428        synepoch.day,
   3429        synhtssep('T'),
   3430        synepoch.hour,
   3431        synhtssep(':'),
   3432        synepoch.minute,
   3433        synhtssep(':'),
   3434        synepoch.second,
   3435        h(':'),
   3436      },
   3437      {
   3438        ah('  @ Description__________  Value'),
   3439      },
   3440      {
   3441        as(),
   3442        ad(':s replacement string  '),
   3443        { { 'ShaDaEntryArray', 'ShaDaMsgpackShaDaKeyword' }, 'CMD' },
   3444      },
   3445      {
   3446        as(),
   3447        ad(':s replacement string  '),
   3448        { { 'ShaDaEntryArray', 'ShaDaMsgpackShaDaKeyword' }, 'SEARCH' },
   3449      },
   3450      {
   3451        as(),
   3452        ad(':s replacement string  '),
   3453        { { 'ShaDaEntryArray', 'ShaDaMsgpackShaDaKeyword' }, 'EXPR' },
   3454      },
   3455      {
   3456        as(),
   3457        ad(':s replacement string  '),
   3458        { { 'ShaDaEntryArray', 'ShaDaMsgpackShaDaKeyword' }, 'INPUT' },
   3459      },
   3460      {
   3461        as(),
   3462        ad(':s replacement string  '),
   3463        { { 'ShaDaEntryArray', 'ShaDaMsgpackShaDaKeyword' }, 'DEBUG' },
   3464      },
   3465      {
   3466        hname('Buffer list'),
   3467        h(' with timestamp '),
   3468        synepoch.year,
   3469        synhtssep('-'),
   3470        synepoch.month,
   3471        synhtssep('-'),
   3472        synepoch.day,
   3473        synhtssep('T'),
   3474        synepoch.hour,
   3475        synhtssep(':'),
   3476        synepoch.minute,
   3477        synhtssep(':'),
   3478        synepoch.second,
   3479        h(':'),
   3480      },
   3481      {
   3482        c('  # Expected array of maps'),
   3483      },
   3484      {
   3485        { { 'ShaDaEntryRawMsgpack' }, '  = ' },
   3486        { { 'ShaDaMsgpackArray', 'ShaDaMsgpackArrayBraces' }, '[' },
   3487        { { 'ShaDaMsgpackArray', 'ShaDaMsgpackMap', 'ShaDaMsgpackMapBraces' }, '{' },
   3488        {
   3489          {
   3490            'ShaDaMsgpackArray',
   3491            'ShaDaMsgpackMap',
   3492            'ShaDaMsgpackBinaryString',
   3493            'ShaDaMsgpackStringQuotes',
   3494          },
   3495          '"',
   3496        },
   3497        { { 'ShaDaMsgpackArray', 'ShaDaMsgpackMap', 'ShaDaMsgpackBinaryString' }, 'a' },
   3498        { { 'ShaDaMsgpackArray', 'ShaDaMsgpackMap', 'ShaDaMsgpackStringQuotes' }, '"' },
   3499        { { 'ShaDaMsgpackArray', 'ShaDaMsgpackMap', 'ShaDaMsgpackColon' }, ':' },
   3500        { { 'ShaDaMsgpackArray', 'ShaDaMsgpackMap' }, ' ' },
   3501        { { 'ShaDaMsgpackArray', 'ShaDaMsgpackMap', 'ShaDaMsgpackExt' }, '+(' },
   3502        {
   3503          {
   3504            'ShaDaMsgpackArray',
   3505            'ShaDaMsgpackMap',
   3506            'ShaDaMsgpackExt',
   3507            'ShaDaMsgpackExtType',
   3508          },
   3509          '10',
   3510        },
   3511        { { 'ShaDaMsgpackArray', 'ShaDaMsgpackMap', 'ShaDaMsgpackExt' }, ')' },
   3512        {
   3513          {
   3514            'ShaDaMsgpackArray',
   3515            'ShaDaMsgpackMap',
   3516            'ShaDaMsgpackBinaryString',
   3517            'ShaDaMsgpackStringQuotes',
   3518          },
   3519          '"',
   3520        },
   3521        { { 'ShaDaMsgpackArray', 'ShaDaMsgpackMap', 'ShaDaMsgpackBinaryString' }, 'ac' },
   3522        {
   3523          {
   3524            'ShaDaMsgpackArray',
   3525            'ShaDaMsgpackMap',
   3526            'ShaDaMsgpackBinaryString',
   3527            'ShaDaMsgpackBinaryStringEscape',
   3528          },
   3529          '\\0',
   3530        },
   3531        { { 'ShaDaMsgpackArray', 'ShaDaMsgpackMap', 'ShaDaMsgpackBinaryString' }, 'df' },
   3532        {
   3533          {
   3534            'ShaDaMsgpackArray',
   3535            'ShaDaMsgpackMap',
   3536            'ShaDaMsgpackBinaryString',
   3537            'ShaDaMsgpackBinaryStringEscape',
   3538          },
   3539          '\\n',
   3540        },
   3541        { { 'ShaDaMsgpackArray', 'ShaDaMsgpackMap', 'ShaDaMsgpackBinaryString' }, 'gi' },
   3542        {
   3543          {
   3544            'ShaDaMsgpackArray',
   3545            'ShaDaMsgpackMap',
   3546            'ShaDaMsgpackBinaryString',
   3547            'ShaDaMsgpackBinaryStringEscape',
   3548          },
   3549          '\\"',
   3550        },
   3551        { { 'ShaDaMsgpackArray', 'ShaDaMsgpackMap', 'ShaDaMsgpackBinaryString' }, 'tt\\.' },
   3552        { { 'ShaDaMsgpackArray', 'ShaDaMsgpackMap', 'ShaDaMsgpackStringQuotes' }, '"' },
   3553        { { 'ShaDaMsgpackArray', 'ShaDaMsgpackMap', 'ShaDaMsgpackComma' }, ',' },
   3554        { { 'ShaDaMsgpackArray', 'ShaDaMsgpackMap' }, ' ' },
   3555        { { 'ShaDaMsgpackArray', 'ShaDaMsgpackMap', 'ShaDaMsgpackKeyword' }, 'TRUE' },
   3556        { { 'ShaDaMsgpackArray', 'ShaDaMsgpackMap', 'ShaDaMsgpackColon' }, ':' },
   3557        { { 'ShaDaMsgpackArray', 'ShaDaMsgpackMap' }, ' ' },
   3558        { { 'ShaDaMsgpackArray', 'ShaDaMsgpackMap', 'ShaDaMsgpackKeyword' }, 'FALSE' },
   3559        { { 'ShaDaMsgpackArray', 'ShaDaMsgpackMapBraces' }, '}' },
   3560        { { 'ShaDaMsgpackArray', 'ShaDaMsgpackComma' }, ',' },
   3561        { { 'ShaDaMsgpackArray' }, ' ' },
   3562        { { 'ShaDaMsgpackArray', 'ShaDaMsgpackArray', 'ShaDaMsgpackArrayBraces' }, '[' },
   3563        { { 'ShaDaMsgpackArray', 'ShaDaMsgpackArray', 'ShaDaMsgpackKeyword' }, 'NIL' },
   3564        { { 'ShaDaMsgpackArray', 'ShaDaMsgpackArray', 'ShaDaMsgpackComma' }, ',' },
   3565        { { 'ShaDaMsgpackArray', 'ShaDaMsgpackArray' }, ' ' },
   3566        { { 'ShaDaMsgpackArray', 'ShaDaMsgpackArray', 'ShaDaMsgpackExt' }, '+(' },
   3567        {
   3568          {
   3569            'ShaDaMsgpackArray',
   3570            'ShaDaMsgpackArray',
   3571            'ShaDaMsgpackExt',
   3572            'ShaDaMsgpackExtType',
   3573          },
   3574          '-10',
   3575        },
   3576        { { 'ShaDaMsgpackArray', 'ShaDaMsgpackArray', 'ShaDaMsgpackExt' }, ')' },
   3577        {
   3578          {
   3579            'ShaDaMsgpackArray',
   3580            'ShaDaMsgpackArray',
   3581            'ShaDaMsgpackBinaryString',
   3582            'ShaDaMsgpackStringQuotes',
   3583          },
   3584          '"',
   3585        },
   3586        { { 'ShaDaMsgpackArray', 'ShaDaMsgpackArray', 'ShaDaMsgpackStringQuotes' }, '"' },
   3587        { { 'ShaDaMsgpackArray', 'ShaDaMsgpackArrayBraces' }, ']' },
   3588        { { 'ShaDaMsgpackArrayBraces' }, ']' },
   3589      },
   3590      {
   3591        hname('Buffer list'),
   3592        h(' with timestamp '),
   3593        synepoch.year,
   3594        synhtssep('-'),
   3595        synepoch.month,
   3596        synhtssep('-'),
   3597        synepoch.day,
   3598        synhtssep('T'),
   3599        synepoch.hour,
   3600        synhtssep(':'),
   3601        synepoch.minute,
   3602        synhtssep(':'),
   3603        synepoch.second,
   3604        h(':'),
   3605      },
   3606      {
   3607        mlh('  % Key  Description  Value'),
   3608      },
   3609      {},
   3610      {
   3611        mlh('  % Key  Description  Value'),
   3612      },
   3613      {
   3614        hname('Header'),
   3615        h(' with timestamp '),
   3616        synepoch.year,
   3617        synhtssep('-'),
   3618        synepoch.month,
   3619        synhtssep('-'),
   3620        synepoch.day,
   3621        synhtssep('T'),
   3622        synepoch.hour,
   3623        synhtssep(':'),
   3624        synepoch.minute,
   3625        synhtssep(':'),
   3626        synepoch.second,
   3627        h(':'),
   3628      },
   3629      {
   3630        mlh('  % Key  Description________  Value'),
   3631      },
   3632      {
   3633        mles('  + '),
   3634        mlk('se   '),
   3635        mld('place cursor at end  '),
   3636        { { 'ShaDaEntryMapLong', 'ShaDaMsgpackKeyword' }, 'TRUE' },
   3637      },
   3638    }
   3639    eq(exp, act)
   3640  end)
   3641 end)