mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
Merge pull request #3270 from ZyX-I/shada-support
Add plugin for editing ShaDa files
This commit is contained in:
commit
321db59ca1
820
runtime/autoload/msgpack.vim
Normal file
820
runtime/autoload/msgpack.vim
Normal file
@ -0,0 +1,820 @@
|
||||
if exists('g:loaded_msgpack_autoload')
|
||||
finish
|
||||
endif
|
||||
let g:loaded_msgpack_autoload = 1
|
||||
|
||||
""
|
||||
" Check that given value is an integer. Respects |msgpack-special-dict|.
|
||||
function msgpack#is_int(v) abort
|
||||
return type(a:v) == type(0) || (
|
||||
\type(a:v) == type({}) && get(a:v, '_TYPE') is# v:msgpack_types.integer)
|
||||
endfunction
|
||||
|
||||
""
|
||||
" Check that given value is an unsigned integer. Respects
|
||||
" |msgpack-special-dict|.
|
||||
function msgpack#is_uint(v) abort
|
||||
return msgpack#is_int(a:v) && (type(a:v) == type(0)
|
||||
\? a:v >= 0
|
||||
\: a:v._VAL[0] > 0)
|
||||
endfunction
|
||||
|
||||
""
|
||||
" True if s:msgpack_init_python() function was already run.
|
||||
let s:msgpack_python_initialized = 0
|
||||
|
||||
""
|
||||
" Cached return of s:msgpack_init_python() used when
|
||||
" s:msgpack_python_initialized is true.
|
||||
let s:msgpack_python_type = 0
|
||||
|
||||
""
|
||||
" Create Python functions that are necessary for work. Also defines functions
|
||||
" s:msgpack_dict_strftime(format, timestamp) and s:msgpack_dict_strptime(format,
|
||||
" string).
|
||||
"
|
||||
" @return Zero in case no Python is available, empty string if Python-2 is
|
||||
" available and string `"3"` if Python-3 is available.
|
||||
function s:msgpack_init_python() abort
|
||||
if s:msgpack_python_initialized
|
||||
return s:msgpack_python_type
|
||||
endif
|
||||
let s:msgpack_python_initialized = 1
|
||||
for suf in ['', '3']
|
||||
try
|
||||
execute 'python' . suf
|
||||
\. "def shada_dict_strftime():\n"
|
||||
\. " import datetime\n"
|
||||
\. " import vim\n"
|
||||
\. " fmt = vim.eval('a:format')\n"
|
||||
\. " timestamp = vim.eval('a:timestamp')\n"
|
||||
\. " timestamp = [int(v) for v in timestamp['_VAL']]\n"
|
||||
\. " timestamp = timestamp[0] * (timestamp[1] << 62\n"
|
||||
\. " | timestamp[2] << 31\n"
|
||||
\. " | timestamp[3])\n"
|
||||
\. " time = datetime.datetime.fromtimestamp(timestamp)\n"
|
||||
\. " return time.strftime(fmt)\n"
|
||||
\. "def shada_dict_strptime():\n"
|
||||
\. " import datetime\n"
|
||||
\. " import vim\n"
|
||||
\. " fmt = vim.eval('a:format')\n"
|
||||
\. " timestr = vim.eval('a:string')\n"
|
||||
\. " timestamp = datetime.datetime.strptime(timestr, fmt)\n"
|
||||
\. " timestamp = int(timestamp.timestamp())\n"
|
||||
\. " if timestamp > 2 ** 31:\n"
|
||||
\. " tsabs = abs(timestamp)"
|
||||
\. " return ('{\"_TYPE\": v:msgpack_types.integer,'\n"
|
||||
\. " + '\"_VAL\": [{sign},{v1},{v2},{v3}]}').format(\n"
|
||||
\. " sign=1 if timestamp >= 0 else -1,\n"
|
||||
\. " v1=((tsabs >> 62) & 0x3),\n"
|
||||
\. " v2=((tsabs >> 31) & (2 ** 31 - 1)),\n"
|
||||
\. " v3=(tsabs & (2 ** 31 - 1)))\n"
|
||||
\. " else:\n"
|
||||
\. " return str(timestamp)\n"
|
||||
execute "function s:msgpack_dict_strftime(format, timestamp) abort\n"
|
||||
\. " return py" . suf . "eval('shada_dict_strftime()')\n"
|
||||
\. "endfunction\n"
|
||||
\. "function s:msgpack_dict_strptime(format, string)\n"
|
||||
\. " return eval(py" . suf . "eval('shada_dict_strptime()'))\n"
|
||||
\. "endfunction\n"
|
||||
let s:msgpack_python_type = suf
|
||||
return suf
|
||||
catch
|
||||
continue
|
||||
endtry
|
||||
endfor
|
||||
|
||||
""
|
||||
" strftime() function for |msgpack-special-dict| values.
|
||||
"
|
||||
" @param[in] format String according to which time should be formatted.
|
||||
" @param[in] timestamp Timestamp (seconds since epoch) to format.
|
||||
"
|
||||
" @return Formatted timestamp.
|
||||
"
|
||||
" @warning Without +python or +python3 this function does not work correctly.
|
||||
" The VimL code contains “reference” implementation which does not
|
||||
" really work because of precision loss.
|
||||
function s:msgpack_dict_strftime(format, timestamp)
|
||||
return msgpack#strftime(a:format, +msgpack#int_dict_to_str(a:timestamp))
|
||||
endfunction
|
||||
|
||||
""
|
||||
" Function that parses given string according to given format.
|
||||
"
|
||||
" @param[in] format String according to which string was formatted.
|
||||
" @param[in] string Time formatted according to format.
|
||||
"
|
||||
" @return Timestamp.
|
||||
"
|
||||
" @warning Without +python or +python3 this function is able to work only with
|
||||
" 31-bit (32-bit signed) timestamps that have format
|
||||
" `%Y-%m-%dT%H:%M:%S`.
|
||||
function s:msgpack_dict_strptime(format, string)
|
||||
let fmt = '%Y-%m-%dT%H:%M:%S'
|
||||
if a:format isnot# fmt
|
||||
throw 'notimplemented-format:Only ' . fmt . ' format is supported'
|
||||
endif
|
||||
let match = matchlist(a:string,
|
||||
\'\v\C^(\d+)\-(\d+)\-(\d+)T(\d+)\:(\d+)\:(\d+)$')
|
||||
if empty(match)
|
||||
throw 'invalid-string:Given string does not match format ' . a:format
|
||||
endif
|
||||
call map(match, 'str2nr(v:val, 10)')
|
||||
let [year, month, day, hour, minute, second] = match[1:6]
|
||||
" Bisection start and end:
|
||||
"
|
||||
" Start: 365 days in year, 28 days in month, -12 hours tz offset.
|
||||
let bisect_ts_start = (((((year - 1970) * 365
|
||||
\+ (month - 1) * 28
|
||||
\+ (day - 1)) * 24
|
||||
\+ hour - 12) * 60
|
||||
\+ minute) * 60
|
||||
\+ second)
|
||||
if bisect_ts_start < 0
|
||||
let bisect_ts_start = 0
|
||||
endif
|
||||
let start_string = strftime(fmt, bisect_ts_start)
|
||||
if start_string is# a:string
|
||||
return bisect_ts_start
|
||||
endif
|
||||
" End: 366 days in year, 31 day in month, +14 hours tz offset.
|
||||
let bisect_ts_end = (((((year - 1970) * 366
|
||||
\+ (month - 1) * 31
|
||||
\+ (day - 1)) * 24
|
||||
\+ hour + 14) * 60
|
||||
\+ minute) * 60
|
||||
\+ second)
|
||||
let end_string = strftime(fmt, bisect_ts_end)
|
||||
if end_string is# a:string
|
||||
return bisect_ts_end
|
||||
endif
|
||||
if start_string ># end_string
|
||||
throw 'internal-start-gt:Internal error: start > end'
|
||||
endif
|
||||
if start_string is# end_string
|
||||
throw printf('internal-start-eq:Internal error: '
|
||||
\. 'start(%u)==end(%u), but start(%s)!=string(%s)',
|
||||
\bisect_ts_start, bisect_ts_end,
|
||||
\string(start_string), string(a:string))
|
||||
endif
|
||||
if start_string ># a:string
|
||||
throw 'internal-start-string:Internal error: start > string'
|
||||
endif
|
||||
if end_string <# a:string
|
||||
throw 'internal-end-string:Internal error: end < string'
|
||||
endif
|
||||
while 1
|
||||
let bisect_ts_middle = (bisect_ts_start/2) + (bisect_ts_end/2)
|
||||
let middle_string = strftime(fmt, bisect_ts_middle)
|
||||
if a:string is# middle_string
|
||||
return bisect_ts_middle
|
||||
elseif a:string ># middle_string
|
||||
if bisect_ts_middle == bisect_ts_start
|
||||
let bisect_ts_start += 1
|
||||
else
|
||||
let bisect_ts_start = bisect_ts_middle
|
||||
endif
|
||||
else
|
||||
if bisect_ts_middle == bisect_ts_end
|
||||
let bisect_ts_end -= 1
|
||||
else
|
||||
let bisect_ts_end = bisect_ts_middle
|
||||
endif
|
||||
endif
|
||||
if bisect_ts_start >= bisect_ts_end
|
||||
throw 'not-found:Unable to find timestamp'
|
||||
endif
|
||||
endwhile
|
||||
endfunction
|
||||
|
||||
return 0
|
||||
endfunction
|
||||
|
||||
""
|
||||
" Wrapper for strftime() that respects |msgpack-special-dict|. May actually use
|
||||
" non-standard strftime() implementations for |msgpack-special-dict| values.
|
||||
"
|
||||
" @param[in] format Format string.
|
||||
" @param[in] timestamp Formatted timestamp.
|
||||
function msgpack#strftime(format, timestamp) abort
|
||||
if type(a:timestamp) == type({})
|
||||
call s:msgpack_init_python()
|
||||
return s:msgpack_dict_strftime(a:format, a:timestamp)
|
||||
else
|
||||
return strftime(a:format, a:timestamp)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
""
|
||||
" Parse string according to the format.
|
||||
"
|
||||
" Requires +python available. If it is not then only supported format is
|
||||
" `%Y-%m-%dT%H:%M:%S` because this is the format used by ShaDa plugin. Also in
|
||||
" this case bisection will be used (timestamps tried with strftime() up until
|
||||
" result matches the string) and only 31-bit (signed 32-bit: with negative
|
||||
" timestamps being useless this leaves 31 bits) timestamps will be supported.
|
||||
"
|
||||
" @param[in] format Time format.
|
||||
" @param[in] string Parsed time string. Must match given format.
|
||||
"
|
||||
" @return Timestamp. Possibly as |msgpack-special-dict|.
|
||||
function msgpack#strptime(format, string) abort
|
||||
call s:msgpack_init_python()
|
||||
return s:msgpack_dict_strptime(a:format, a:string)
|
||||
endfunction
|
||||
|
||||
let s:MSGPACK_HIGHEST_BIT = 1
|
||||
let s:MSGPACK_HIGHEST_BIT_NR = 0
|
||||
while s:MSGPACK_HIGHEST_BIT * 2 > 0
|
||||
let s:MSGPACK_HIGHEST_BIT = s:MSGPACK_HIGHEST_BIT * 2
|
||||
let s:MSGPACK_HIGHEST_BIT_NR += 1
|
||||
endwhile
|
||||
|
||||
""
|
||||
" Shift given number by given amount of bits
|
||||
function s:shift(n, s) abort
|
||||
if a:s == 0
|
||||
return a:n
|
||||
elseif a:s < 0
|
||||
let ret = a:n
|
||||
for _ in range(-a:s)
|
||||
let ret = ret / 2
|
||||
endfor
|
||||
return ret
|
||||
else
|
||||
let ret = a:n
|
||||
for i in range(a:s)
|
||||
let new_ret = ret * 2
|
||||
if new_ret < ret
|
||||
" Overflow: remove highest bit
|
||||
let ret = xor(s:MSGPACK_HIGHEST_BIT, ret) * 2
|
||||
endif
|
||||
let ret = new_ret
|
||||
endfor
|
||||
return ret
|
||||
endif
|
||||
endfunction
|
||||
|
||||
let s:msgpack_mask_cache = {
|
||||
\s:MSGPACK_HIGHEST_BIT_NR : s:MSGPACK_HIGHEST_BIT - 1}
|
||||
|
||||
""
|
||||
" Apply a mask where first m bits are ones and other are zeroes to a given
|
||||
" number
|
||||
function s:mask1(n, m) abort
|
||||
if a:m > s:MSGPACK_HIGHEST_BIT_NR + 1
|
||||
let m = s:MSGPACK_HIGHEST_BIT_NR + 1
|
||||
else
|
||||
let m = a:m
|
||||
endif
|
||||
if !has_key(s:msgpack_mask_cache, m)
|
||||
let p = 0
|
||||
for _ in range(m)
|
||||
let p = p * 2 + 1
|
||||
endfor
|
||||
let s:msgpack_mask_cache[m] = p
|
||||
endif
|
||||
return and(a:n, s:msgpack_mask_cache[m])
|
||||
endfunction
|
||||
|
||||
""
|
||||
" Convert |msgpack-special-dict| that represents integer value to a string. Uses
|
||||
" hexadecimal representation starting with 0x because it is the easiest to
|
||||
" convert to.
|
||||
function msgpack#int_dict_to_str(v) abort
|
||||
let v = a:v._VAL
|
||||
" 64-bit number:
|
||||
" 0000000001111111111222222222233333333334444444444555555555566666
|
||||
" 1234567890123456789012345678901234567890123456789012345678901234
|
||||
" Split in _VAL:
|
||||
" 0000000001111111111222222222233 3333333344444444445555555555666 66
|
||||
" 1234567890123456789012345678901 2345678901234567890123456789012 34
|
||||
" Split by hex digits:
|
||||
" 0000 0000 0111 1111 1112 2222 2222 2333 3333 3334 4444 4444 4555 5555 5556 6666
|
||||
" 1234 5678 9012 3456 7890 1234 5678 9012 3456 7890 1234 5678 9012 3456 7890 1234
|
||||
"
|
||||
" Total split:
|
||||
" _VAL[3] _VAL[2] _VAL[1]
|
||||
" ______________________________________ _______________________________________ __
|
||||
" 0000 0000 0111 1111 1112 2222 2222 233 3 3333 3334 4444 4444 4555 5555 5556 66 66
|
||||
" 1234 5678 9012 3456 7890 1234 5678 901 2 3456 7890 1234 5678 9012 3456 7890 12 34
|
||||
" ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^
|
||||
" g4 g3 g2 g1
|
||||
" ********************************** *** * ********************************** ** **
|
||||
" 1 2 3 4 5 6
|
||||
" 1: s:mask1(v[3], 28): first 28 bits of _VAL[3]
|
||||
" 2: s:shift(v[3], -28): last 3 bits of _VAL[3]
|
||||
" 3: s:mask1(v[2], 1): first bit of _VAL[2]
|
||||
" 4: s:mask1(s:shift(v[2], -1), 28): bits 2 .. 29 of _VAL[2]
|
||||
" 5: s:shift(v[2], -29): last 2 bits of _VAL[2]
|
||||
" 6: s:shift(v[1], 2): _VAL[1]
|
||||
let g4 = printf('%07x', s:mask1(v[3], 28))
|
||||
let g3 = printf('%01x', or(s:shift(v[3], -28), s:shift(s:mask1(v[2], 1), 3)))
|
||||
let g2 = printf('%07x', s:mask1(s:shift(v[2], -1), 28))
|
||||
let g1 = printf('%01x', or(s:shift(v[2], -29), s:shift(v[1], 2)))
|
||||
return ((v[0] < 0 ? '-' : '') . '0x' . g1 . g2 . g3 . g4)
|
||||
endfunction
|
||||
|
||||
""
|
||||
" True boolean value.
|
||||
let g:msgpack#true = {'_TYPE': v:msgpack_types.boolean, '_VAL': 1}
|
||||
lockvar! g:msgpack#true
|
||||
|
||||
""
|
||||
" False boolean value.
|
||||
let g:msgpack#false = {'_TYPE': v:msgpack_types.boolean, '_VAL': 0}
|
||||
lockvar! g:msgpack#false
|
||||
|
||||
""
|
||||
" NIL value.
|
||||
let g:msgpack#nil = {'_TYPE': v:msgpack_types.nil, '_VAL': 0}
|
||||
lockvar! g:msgpack#nil
|
||||
|
||||
""
|
||||
" Deduce type of |msgpack-special-dict|.
|
||||
"
|
||||
" @return zero if given dictionary is not special or name of the key in
|
||||
" v:msgpack_types dictionary.
|
||||
function msgpack#special_type(v) abort
|
||||
if type(a:v) != type({}) || !has_key(a:v, '_TYPE')
|
||||
return 0
|
||||
endif
|
||||
for [k, v] in items(v:msgpack_types)
|
||||
if a:v._TYPE is v
|
||||
return k
|
||||
endif
|
||||
endfor
|
||||
return 0
|
||||
endfunction
|
||||
|
||||
""
|
||||
" Mapping that maps type() output to type names.
|
||||
let s:MSGPACK_STANDARD_TYPES = {
|
||||
\type(0): 'integer',
|
||||
\type(0.0): 'float',
|
||||
\type(''): 'binary',
|
||||
\type([]): 'array',
|
||||
\type({}): 'map',
|
||||
\}
|
||||
|
||||
""
|
||||
" Deduce type of one of items returned by msgpackparse().
|
||||
"
|
||||
" @return Name of a key in v:msgpack_types.
|
||||
function msgpack#type(v) abort
|
||||
let special_type = msgpack#special_type(a:v)
|
||||
if special_type is 0
|
||||
return s:MSGPACK_STANDARD_TYPES[type(a:v)]
|
||||
endif
|
||||
return special_type
|
||||
endfunction
|
||||
|
||||
""
|
||||
" Dump nil value.
|
||||
function s:msgpack_dump_nil(v) abort
|
||||
return 'NIL'
|
||||
endfunction
|
||||
|
||||
""
|
||||
" Dump boolean value.
|
||||
function s:msgpack_dump_boolean(v) abort
|
||||
return a:v._VAL ? 'TRUE' : 'FALSE'
|
||||
endfunction
|
||||
|
||||
""
|
||||
" Dump integer msgpack value.
|
||||
function s:msgpack_dump_integer(v) abort
|
||||
if type(a:v) == type({})
|
||||
return msgpack#int_dict_to_str(a:v)
|
||||
else
|
||||
return string(a:v)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
""
|
||||
" Dump floating-point value.
|
||||
function s:msgpack_dump_float(v) abort
|
||||
return string(type(a:v) == type({}) ? a:v._VAL : a:v)
|
||||
endfunction
|
||||
|
||||
""
|
||||
" Dump |msgpack-special-dict| that represents a string. If any additional
|
||||
" parameter is given then it dumps binary string.
|
||||
function s:msgpack_dump_string(v, ...) abort
|
||||
let ret = [a:0 ? '"' : '="']
|
||||
for v in a:v._VAL
|
||||
call add(
|
||||
\ret,
|
||||
\substitute(
|
||||
\substitute(v, '["\\]', '\\\0', 'g'),
|
||||
\'\n', '\\0', 'g'))
|
||||
call add(ret, '\n')
|
||||
endfor
|
||||
let ret[-1] = '"'
|
||||
return join(ret, '')
|
||||
endfunction
|
||||
|
||||
""
|
||||
" Dump binary string.
|
||||
function s:msgpack_dump_binary(v) abort
|
||||
if type(a:v) == type({})
|
||||
return s:msgpack_dump_string(a:v, 1)
|
||||
else
|
||||
return s:msgpack_dump_string({'_VAL': split(a:v, "\n", 1)}, 1)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
""
|
||||
" Dump array value.
|
||||
function s:msgpack_dump_array(v) abort
|
||||
let val = type(a:v) == type({}) ? a:v._VAL : a:v
|
||||
return '[' . join(map(val[:], 'msgpack#string(v:val)'), ', ') . ']'
|
||||
endfunction
|
||||
|
||||
""
|
||||
" Dump dictionary value.
|
||||
function s:msgpack_dump_map(v) abort
|
||||
let ret = ['{']
|
||||
if msgpack#special_type(a:v) is 0
|
||||
for [k, v] in items(a:v)
|
||||
let ret += [s:msgpack_dump_string({'_VAL': split(k, "\n", 1)}),
|
||||
\': ',
|
||||
\msgpack#string(v),
|
||||
\', ']
|
||||
unlet v
|
||||
endfor
|
||||
if !empty(a:v)
|
||||
call remove(ret, -1)
|
||||
endif
|
||||
else
|
||||
for [k, v] in sort(copy(a:v._VAL))
|
||||
let ret += [msgpack#string(k),
|
||||
\': ',
|
||||
\msgpack#string(v),
|
||||
\', ']
|
||||
unlet k
|
||||
unlet v
|
||||
endfor
|
||||
if !empty(a:v._VAL)
|
||||
call remove(ret, -1)
|
||||
endif
|
||||
endif
|
||||
let ret += ['}']
|
||||
return join(ret, '')
|
||||
endfunction
|
||||
|
||||
""
|
||||
" Dump extension value.
|
||||
function s:msgpack_dump_ext(v) abort
|
||||
return printf('+(%i)%s', a:v._VAL[0],
|
||||
\s:msgpack_dump_string({'_VAL': a:v._VAL[1]}, 1))
|
||||
endfunction
|
||||
|
||||
""
|
||||
" Convert msgpack object to a string, like string() function does. Result of the
|
||||
" conversion may be passed to msgpack#eval().
|
||||
function msgpack#string(v) abort
|
||||
if type(a:v) == type({})
|
||||
let type = msgpack#special_type(a:v)
|
||||
if type is 0
|
||||
let type = 'map'
|
||||
endif
|
||||
else
|
||||
let type = get(s:MSGPACK_STANDARD_TYPES, type(a:v), 0)
|
||||
if type is 0
|
||||
throw printf('msgpack:invtype: Unable to convert value %s', string(a:v))
|
||||
endif
|
||||
endif
|
||||
return s:msgpack_dump_{type}(a:v)
|
||||
endfunction
|
||||
|
||||
""
|
||||
" Copy msgpack object like deepcopy() does, but leave types intact
|
||||
function msgpack#deepcopy(obj) abort
|
||||
if type(a:obj) == type([])
|
||||
return map(copy(a:obj), 'msgpack#deepcopy(v:val)')
|
||||
elseif type(a:obj) == type({})
|
||||
let special_type = msgpack#special_type(a:obj)
|
||||
if special_type is 0
|
||||
return map(copy(a:obj), 'msgpack#deepcopy(v:val)')
|
||||
else
|
||||
return {
|
||||
\'_TYPE': v:msgpack_types[special_type],
|
||||
\'_VAL': msgpack#deepcopy(a:obj._VAL)
|
||||
\}
|
||||
endif
|
||||
else
|
||||
return copy(a:obj)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
""
|
||||
" Convert an escaped character to needed value
|
||||
function s:msgpack_eval_str_sub(ch) abort
|
||||
if a:ch is# 'n'
|
||||
return '", "'
|
||||
elseif a:ch is# '0'
|
||||
return '\n'
|
||||
else
|
||||
return '\' . a:ch
|
||||
endif
|
||||
endfunction
|
||||
|
||||
let s:MSGPACK_SPECIAL_OBJECTS = {
|
||||
\'NIL': '{''_TYPE'': v:msgpack_types.nil, ''_VAL'': 0}',
|
||||
\'TRUE': '{''_TYPE'': v:msgpack_types.boolean, ''_VAL'': 1}',
|
||||
\'FALSE': '{''_TYPE'': v:msgpack_types.boolean, ''_VAL'': 0}',
|
||||
\'nan': '(-(1.0/0.0-1.0/0.0))',
|
||||
\'inf': '(1.0/0.0)',
|
||||
\}
|
||||
|
||||
""
|
||||
" Convert msgpack object dumped by msgpack#string() to a VimL object suitable
|
||||
" for msgpackdump().
|
||||
"
|
||||
" @param[in] s String to evaluate.
|
||||
" @param[in] special_objs Additional special objects, in the same format as
|
||||
" s:MSGPACK_SPECIAL_OBJECTS.
|
||||
"
|
||||
" @return Any value that msgpackparse() may return.
|
||||
function msgpack#eval(s, special_objs) abort
|
||||
let s = a:s
|
||||
let expr = []
|
||||
let context = []
|
||||
while !empty(s)
|
||||
let s = substitute(s, '^\s*', '', '')
|
||||
if s[0] =~# '\v^\h$'
|
||||
let name = matchstr(s, '\v\C^\w+')
|
||||
if has_key(s:MSGPACK_SPECIAL_OBJECTS, name)
|
||||
call add(expr, s:MSGPACK_SPECIAL_OBJECTS[name])
|
||||
elseif has_key(a:special_objs, name)
|
||||
call add(expr, a:special_objs[name])
|
||||
else
|
||||
throw 'name-unknown:Unknown name ' . name . ': ' . s
|
||||
endif
|
||||
let s = s[len(name):]
|
||||
elseif (s[0] is# '-' && s[1] =~# '\v^\d$') || s[0] =~# '\v^\d$'
|
||||
let sign = 1
|
||||
if s[0] is# '-'
|
||||
let s = s[1:]
|
||||
let sign = -1
|
||||
endif
|
||||
if s[0:1] is# '0x'
|
||||
" See comment in msgpack#int_dict_to_str().
|
||||
let s = s[2:]
|
||||
let hexnum = matchstr(s, '\v\C^\x+')
|
||||
if empty(hexnum)
|
||||
throw '0x-empty:Must have number after 0x: ' . s
|
||||
elseif len(hexnum) > 16
|
||||
throw '0x-long:Must have at most 16 hex digits: ' . s
|
||||
endif
|
||||
let s = s[len(hexnum):]
|
||||
let hexnum = repeat('0', 16 - len(hexnum)) . hexnum
|
||||
let g1 = str2nr(hexnum[0], 16)
|
||||
let g2 = str2nr(hexnum[1:7], 16)
|
||||
let g3 = str2nr(hexnum[8], 16)
|
||||
let g4 = str2nr(hexnum[9:15], 16)
|
||||
let v1 = s:shift(g1, -2)
|
||||
let v2 = or(or(s:shift(s:mask1(g1, 2), 29), s:shift(g2, 1)),
|
||||
\s:mask1(s:shift(g3, -3), 1))
|
||||
let v3 = or(s:shift(s:mask1(g3, 3), 28), g4)
|
||||
call add(expr, printf('{''_TYPE'': v:msgpack_types.integer, '.
|
||||
\'''_VAL'': [%i, %u, %u, %u]}',
|
||||
\sign, v1, v2, v3))
|
||||
else
|
||||
let num = matchstr(s, '\v\C^\d+')
|
||||
let s = s[len(num):]
|
||||
if sign == -1
|
||||
call add(expr, '-')
|
||||
endif
|
||||
call add(expr, num)
|
||||
if s[0] is# '.'
|
||||
let dec = matchstr(s, '\v\C^\.\d+%(e[+-]?\d+)?')
|
||||
if empty(dec)
|
||||
throw '0.-nodigits:Decimal dot must be followed by digit(s): ' . s
|
||||
endif
|
||||
let s = s[len(dec):]
|
||||
call add(expr, dec)
|
||||
endif
|
||||
endif
|
||||
elseif s =~# '-\?\%(inf\|nan\)'
|
||||
if s[0] is# '-'
|
||||
call add(expr, '-')
|
||||
let s = s[1:]
|
||||
endif
|
||||
call add(expr, s:MSGPACK_SPECIAL_OBJECTS[s[0:2]])
|
||||
let s = s[3:]
|
||||
elseif stridx('="+', s[0]) != -1
|
||||
let match = matchlist(s, '\v\C^(\=|\+\((\-?\d+)\)|)(\"%(\\.|[^\\"]+)*\")')
|
||||
if empty(match)
|
||||
throw '"-invalid:Invalid string: ' . s
|
||||
endif
|
||||
call add(expr, '{''_TYPE'': v:msgpack_types.')
|
||||
if empty(match[1])
|
||||
call add(expr, 'binary')
|
||||
elseif match[1] is# '='
|
||||
call add(expr, 'string')
|
||||
else
|
||||
call add(expr, 'ext')
|
||||
endif
|
||||
call add(expr, ', ''_VAL'': [')
|
||||
if match[1][0] is# '+'
|
||||
call add(expr, match[2] . ', [')
|
||||
endif
|
||||
call add(expr, substitute(match[3], '\v\C\\(.)',
|
||||
\'\=s:msgpack_eval_str_sub(submatch(1))', 'g'))
|
||||
if match[1][0] is# '+'
|
||||
call add(expr, ']')
|
||||
endif
|
||||
call add(expr, ']}')
|
||||
let s = s[len(match[0]):]
|
||||
elseif s[0] is# '{'
|
||||
call add(context, 'map')
|
||||
call add(expr, '{''_TYPE'': v:msgpack_types.map, ''_VAL'': [')
|
||||
call add(expr, '[')
|
||||
let s = s[1:]
|
||||
elseif s[0] is# '['
|
||||
call add(context, 'array')
|
||||
call add(expr, '[')
|
||||
let s = s[1:]
|
||||
elseif s[0] is# ':'
|
||||
call add(expr, ',')
|
||||
let s = s[1:]
|
||||
elseif s[0] is# ','
|
||||
if context[-1] is# 'array'
|
||||
call add(expr, ',')
|
||||
else
|
||||
call add(expr, '], [')
|
||||
endif
|
||||
let s = s[1:]
|
||||
elseif s[0] is# ']'
|
||||
call remove(context, -1)
|
||||
call add(expr, ']')
|
||||
let s = s[1:]
|
||||
elseif s[0] is# '}'
|
||||
call remove(context, -1)
|
||||
if expr[-1] is# "\x5B"
|
||||
call remove(expr, -1)
|
||||
else
|
||||
call add(expr, ']')
|
||||
endif
|
||||
call add(expr, ']}')
|
||||
let s = s[1:]
|
||||
elseif s[0] is# ''''
|
||||
let char = matchstr(s, '\m\C^''\zs.\ze''')
|
||||
if empty(char)
|
||||
throw 'char-invalid:Invalid integer character literal format: ' . s
|
||||
endif
|
||||
call add(expr, char2nr(char))
|
||||
let s = s[len(char) + 2:]
|
||||
else
|
||||
throw 'unknown:Invalid non-space character: ' . s
|
||||
endif
|
||||
endwhile
|
||||
if empty(expr)
|
||||
throw 'empty:Parsed string is empty'
|
||||
endif
|
||||
return eval(join(expr, ''))
|
||||
endfunction
|
||||
|
||||
""
|
||||
" Check whether two msgpack values are equal
|
||||
function msgpack#equal(a, b)
|
||||
let atype = msgpack#type(a:a)
|
||||
let btype = msgpack#type(a:b)
|
||||
if atype isnot# btype
|
||||
return 0
|
||||
endif
|
||||
let aspecial = msgpack#special_type(a:a)
|
||||
let bspecial = msgpack#special_type(a:b)
|
||||
if aspecial is# bspecial
|
||||
if aspecial is# 0
|
||||
if type(a:a) == type({})
|
||||
if len(a:a) != len(a:b)
|
||||
return 0
|
||||
endif
|
||||
if !empty(filter(keys(a:a), '!has_key(a:b, v:val)'))
|
||||
return 0
|
||||
endif
|
||||
for [k, v] in items(a:a)
|
||||
if !msgpack#equal(v, a:b[k])
|
||||
return 0
|
||||
endif
|
||||
unlet v
|
||||
endfor
|
||||
return 1
|
||||
elseif type(a:a) == type([])
|
||||
if len(a:a) != len(a:b)
|
||||
return 0
|
||||
endif
|
||||
let i = 0
|
||||
for asubval in a:a
|
||||
if !msgpack#equal(asubval, a:b[i])
|
||||
return 0
|
||||
endif
|
||||
let i += 1
|
||||
unlet asubval
|
||||
endfor
|
||||
return 1
|
||||
elseif type(a:a) == type(0.0)
|
||||
return (a:a == a:a ? a:a == a:b : string(a:a) ==# string(a:b))
|
||||
else
|
||||
return a:a ==# a:b
|
||||
endif
|
||||
elseif aspecial is# 'map' || aspecial is# 'array'
|
||||
if len(a:a._VAL) != len(a:b._VAL)
|
||||
return 0
|
||||
endif
|
||||
let alist = aspecial is# 'map' ? sort(copy(a:a._VAL)) : a:a._VAL
|
||||
let blist = bspecial is# 'map' ? sort(copy(a:b._VAL)) : a:b._VAL
|
||||
let i = 0
|
||||
for asubval in alist
|
||||
let bsubval = blist[i]
|
||||
if aspecial is# 'map'
|
||||
if !(msgpack#equal(asubval[0], bsubval[0])
|
||||
\&& msgpack#equal(asubval[1], bsubval[1]))
|
||||
return 0
|
||||
endif
|
||||
else
|
||||
if !msgpack#equal(asubval, bsubval)
|
||||
return 0
|
||||
endif
|
||||
endif
|
||||
let i += 1
|
||||
unlet asubval
|
||||
unlet bsubval
|
||||
endfor
|
||||
return 1
|
||||
elseif aspecial is# 'nil'
|
||||
return 1
|
||||
elseif aspecial is# 'float'
|
||||
return (a:a._VAL == a:a._VAL
|
||||
\? (a:a._VAL == a:b._VAL)
|
||||
\: (string(a:a._VAL) ==# string(a:b._VAL)))
|
||||
else
|
||||
return a:a._VAL ==# a:b._VAL
|
||||
endif
|
||||
else
|
||||
if atype is# 'array'
|
||||
let a = aspecial is 0 ? a:a : a:a._VAL
|
||||
let b = bspecial is 0 ? a:b : a:b._VAL
|
||||
return msgpack#equal(a, b)
|
||||
elseif atype is# 'binary'
|
||||
let a = (aspecial is 0 ? split(a:a, "\n", 1) : a:a._VAL)
|
||||
let b = (bspecial is 0 ? split(a:b, "\n", 1) : a:b._VAL)
|
||||
return a ==# b
|
||||
elseif atype is# 'map'
|
||||
if aspecial is 0
|
||||
let akeys = copy(a:a)
|
||||
if len(a:b._VAL) != len(akeys)
|
||||
return 0
|
||||
endif
|
||||
for [k, v] in a:b._VAL
|
||||
if msgpack#type(k) isnot# 'string'
|
||||
" Non-special mapping cannot have non-string keys
|
||||
return 0
|
||||
endif
|
||||
if (empty(k._VAL)
|
||||
\|| k._VAL ==# [""]
|
||||
\|| !empty(filter(copy(k._VAL), 'stridx(v:val, "\n") != -1')))
|
||||
" Non-special mapping cannot have zero byte in key or an empty key
|
||||
return 0
|
||||
endif
|
||||
let kstr = join(k._VAL, "\n")
|
||||
if !has_key(akeys, kstr)
|
||||
" Protects from both missing and duplicate keys
|
||||
return 0
|
||||
endif
|
||||
if !msgpack#equal(akeys[kstr], v)
|
||||
return 0
|
||||
endif
|
||||
call remove(akeys, kstr)
|
||||
unlet k
|
||||
unlet v
|
||||
endfor
|
||||
return 1
|
||||
else
|
||||
return msgpack#equal(a:b, a:a)
|
||||
endif
|
||||
elseif atype is# 'float'
|
||||
let a = aspecial is 0 ? a:a : a:a._VAL
|
||||
let b = bspecial is 0 ? a:b : a:b._VAL
|
||||
return (a == a ? a == b : string(a) ==# string(b))
|
||||
elseif atype is# 'integer'
|
||||
if aspecial is 0
|
||||
let sign = a:a >= 0 ? 1 : -1
|
||||
let a = sign * a:a
|
||||
let v1 = s:mask1(s:shift(a, -62), 2)
|
||||
let v2 = s:mask1(s:shift(a, -31), 31)
|
||||
let v3 = s:mask1(a, 31)
|
||||
return [sign, v1, v2, v3] == a:b._VAL
|
||||
else
|
||||
return msgpack#equal(a:b, a:a)
|
||||
endif
|
||||
else
|
||||
throw printf('internal-invalid-type: %s == %s, but special %s /= %s',
|
||||
\atype, btype, aspecial, bspecial)
|
||||
endif
|
||||
endif
|
||||
endfunction
|
694
runtime/autoload/shada.vim
Normal file
694
runtime/autoload/shada.vim
Normal file
@ -0,0 +1,694 @@
|
||||
if exists('g:loaded_shada_autoload')
|
||||
finish
|
||||
endif
|
||||
let g:loaded_shada_autoload = 1
|
||||
|
||||
""
|
||||
" If true keep the old header entry when editing existing ShaDa file.
|
||||
"
|
||||
" Old header entry will be kept only if it is listed in the opened file. To
|
||||
" remove old header entry despite of the setting just remove it from the
|
||||
" listing. Setting it to false makes plugin ignore all header entries. Defaults
|
||||
" to 1.
|
||||
let g:shada#keep_old_header = get(g:, 'shada#keep_old_header', 1)
|
||||
|
||||
""
|
||||
" If true then first entry will be plugin’s own header entry.
|
||||
let g:shada#add_own_header = get(g:, 'shada#add_own_header', 1)
|
||||
|
||||
""
|
||||
" Dictionary that maps ShaDa types to their names.
|
||||
let s:SHADA_ENTRY_NAMES = {
|
||||
\1: 'header',
|
||||
\2: 'search_pattern',
|
||||
\3: 'replacement_string',
|
||||
\4: 'history_entry',
|
||||
\5: 'register',
|
||||
\6: 'variable',
|
||||
\7: 'global_mark',
|
||||
\8: 'jump',
|
||||
\9: 'buffer_list',
|
||||
\10: 'local_mark',
|
||||
\11: 'change',
|
||||
\}
|
||||
|
||||
""
|
||||
" Dictionary that maps ShaDa names to corresponding types
|
||||
let s:SHADA_ENTRY_TYPES = {}
|
||||
call map(copy(s:SHADA_ENTRY_NAMES),
|
||||
\'extend(s:SHADA_ENTRY_TYPES, {v:val : +v:key})')
|
||||
|
||||
""
|
||||
" Map that maps entry names to lists of keys that can be used by this entry.
|
||||
" Only contains data for entries which are represented as mappings, except for
|
||||
" the header.
|
||||
let s:SHADA_MAP_ENTRIES = {
|
||||
\'search_pattern': ['sp', 'sh', 'ss', 'sm', 'sc', 'sl', 'se', 'so', 'su'],
|
||||
\'register': ['n', 'rc', 'rw', 'rt'],
|
||||
\'global_mark': ['n', 'f', 'l', 'c'],
|
||||
\'local_mark': ['f', 'n', 'l', 'c'],
|
||||
\'jump': ['f', 'l', 'c'],
|
||||
\'change': ['f', 'l', 'c'],
|
||||
\'header': [],
|
||||
\}
|
||||
|
||||
""
|
||||
" Like one of the values from s:SHADA_MAP_ENTRIES, but for a single buffer in
|
||||
" buffer list entry.
|
||||
let s:SHADA_BUFFER_LIST_KEYS = ['f', 'l', 'c']
|
||||
|
||||
""
|
||||
" List of possible history types. Maps integer values that represent history
|
||||
" types to human-readable names.
|
||||
let s:SHADA_HISTORY_TYPES = ['command', 'search', 'expression', 'input', 'debug']
|
||||
|
||||
""
|
||||
" Map that maps entry names to their descriptions. Only for entries which have
|
||||
" list as a data type. Description is a list of lists where each entry has item
|
||||
" description and item type.
|
||||
let s:SHADA_FIXED_ARRAY_ENTRIES = {
|
||||
\'replacement_string': [[':s replacement string', 'bin']],
|
||||
\'history_entry': [
|
||||
\['history type', 'histtype'],
|
||||
\['contents', 'bin'],
|
||||
\['separator', 'intchar'],
|
||||
\],
|
||||
\'variable': [['name', 'bin'], ['value', 'any']],
|
||||
\}
|
||||
|
||||
""
|
||||
" Dictionary that maps enum names to dictionary with enum values. Dictionary
|
||||
" with enum values maps enum human-readable names to corresponding values. Enums
|
||||
" are used as type names in s:SHADA_FIXED_ARRAY_ENTRIES and
|
||||
" s:SHADA_STANDARD_KEYS.
|
||||
let s:SHADA_ENUMS = {
|
||||
\'histtype': {
|
||||
\'CMD': 0,
|
||||
\'SEARCH': 1,
|
||||
\'EXPR': 2,
|
||||
\'INPUT': 3,
|
||||
\'DEBUG': 4,
|
||||
\},
|
||||
\'regtype': {
|
||||
\'CHARACTERWISE': 0,
|
||||
\'LINEWISE': 1,
|
||||
\'BLOCKWISE': 2,
|
||||
\}
|
||||
\}
|
||||
|
||||
""
|
||||
" Second argument to msgpack#eval.
|
||||
let s:SHADA_SPECIAL_OBJS = {}
|
||||
call map(values(s:SHADA_ENUMS),
|
||||
\'extend(s:SHADA_SPECIAL_OBJS, map(copy(v:val), "string(v:val)"))')
|
||||
|
||||
""
|
||||
" Like s:SHADA_ENUMS, but inner dictionary maps values to names and not names to
|
||||
" values.
|
||||
let s:SHADA_REV_ENUMS = map(copy(s:SHADA_ENUMS), '{}')
|
||||
call map(copy(s:SHADA_ENUMS),
|
||||
\'map(copy(v:val), '
|
||||
\. '"extend(s:SHADA_REV_ENUMS[" . string(v:key) . "], '
|
||||
\. '{v:val : v:key})")')
|
||||
|
||||
""
|
||||
" Maximum length of ShaDa entry name. Used to arrange entries to the table.
|
||||
let s:SHADA_MAX_ENTRY_LENGTH = max(
|
||||
\map(values(s:SHADA_ENTRY_NAMES), 'len(v:val)')
|
||||
\+ [len('unknown (0x)') + 16])
|
||||
|
||||
""
|
||||
" Object that marks required value.
|
||||
let s:SHADA_REQUIRED = []
|
||||
|
||||
""
|
||||
" Dictionary that maps default key names to their description. Description is
|
||||
" a list that contains human-readable hint, key type and default value.
|
||||
let s:SHADA_STANDARD_KEYS = {
|
||||
\'sm': ['magic value', 'boolean', g:msgpack#true],
|
||||
\'sc': ['smartcase value', 'boolean', g:msgpack#false],
|
||||
\'sl': ['has line offset', 'boolean', g:msgpack#false],
|
||||
\'se': ['place cursor at end', 'boolean', g:msgpack#false],
|
||||
\'so': ['offset value', 'integer', 0],
|
||||
\'su': ['is last used', 'boolean', g:msgpack#true],
|
||||
\'ss': ['is :s pattern', 'boolean', g:msgpack#false],
|
||||
\'sh': ['v:hlsearch value', 'boolean', g:msgpack#false],
|
||||
\'sp': ['pattern', 'bin', s:SHADA_REQUIRED],
|
||||
\'rt': ['type', 'regtype', s:SHADA_ENUMS.regtype.CHARACTERWISE],
|
||||
\'rw': ['block width', 'uint', 0],
|
||||
\'rc': ['contents', 'binarray', s:SHADA_REQUIRED],
|
||||
\'n': ['name', 'intchar', char2nr('"')],
|
||||
\'l': ['line number', 'uint', 1],
|
||||
\'c': ['column', 'uint', 0],
|
||||
\'f': ['file name', 'bin', s:SHADA_REQUIRED],
|
||||
\}
|
||||
|
||||
""
|
||||
" Set of entry types containing entries which require `n` key.
|
||||
let s:SHADA_REQUIRES_NAME = {'local_mark': 1, 'global_mark': 1, 'register': 1}
|
||||
|
||||
""
|
||||
" Maximum width of human-readable hint. Used to arrange data in table.
|
||||
let s:SHADA_MAX_HINT_WIDTH = max(map(values(s:SHADA_STANDARD_KEYS),
|
||||
\'len(v:val[0])'))
|
||||
|
||||
""
|
||||
" Default mark name for the cases when it makes sense (i.e. for local marks).
|
||||
let s:SHADA_DEFAULT_MARK_NAME = '"'
|
||||
|
||||
""
|
||||
" Mapping that maps timestamps represented using msgpack#string to strftime
|
||||
" output. Used by s:shada_strftime.
|
||||
let s:shada_strftime_cache = {}
|
||||
|
||||
""
|
||||
" Mapping that maps strftime output from s:shada_strftime to timestamps.
|
||||
let s:shada_strptime_cache = {}
|
||||
|
||||
""
|
||||
" Time format used for displaying ShaDa files.
|
||||
let s:SHADA_TIME_FORMAT = '%Y-%m-%dT%H:%M:%S'
|
||||
|
||||
""
|
||||
" Wrapper around msgpack#strftime that caches its output.
|
||||
"
|
||||
" Format is hardcoded to s:SHADA_TIME_FORMAT.
|
||||
function s:shada_strftime(timestamp) abort
|
||||
let key = msgpack#string(a:timestamp)
|
||||
if has_key(s:shada_strftime_cache, key)
|
||||
return s:shada_strftime_cache[key]
|
||||
endif
|
||||
let val = msgpack#strftime(s:SHADA_TIME_FORMAT, a:timestamp)
|
||||
let s:shada_strftime_cache[key] = val
|
||||
let s:shada_strptime_cache[val] = a:timestamp
|
||||
return val
|
||||
endfunction
|
||||
|
||||
""
|
||||
" Wrapper around msgpack#strftime that uses cache created by s:shada_strftime().
|
||||
"
|
||||
" Also caches its own results. Format is hardcoded to s:SHADA_TIME_FORMAT.
|
||||
function s:shada_strptime(string) abort
|
||||
if has_key(s:shada_strptime_cache, a:string)
|
||||
return s:shada_strptime_cache[a:string]
|
||||
endif
|
||||
let ts = msgpack#strptime(s:SHADA_TIME_FORMAT, a:string)
|
||||
let s:shada_strptime_cache[a:string] = ts
|
||||
return ts
|
||||
endfunction
|
||||
|
||||
""
|
||||
" Check whether given value matches given type.
|
||||
"
|
||||
" @return Zero if value matches, error message string if it does not.
|
||||
function s:shada_check_type(type, val) abort
|
||||
let type = msgpack#type(a:val)
|
||||
if type is# a:type
|
||||
return 0
|
||||
endif
|
||||
if has_key(s:SHADA_ENUMS, a:type)
|
||||
let msg = s:shada_check_type('uint', a:val)
|
||||
if msg isnot 0
|
||||
return msg
|
||||
endif
|
||||
if !has_key(s:SHADA_REV_ENUMS[a:type], a:val)
|
||||
let evals_msg = join(map(sort(items(s:SHADA_REV_ENUMS[a:type])),
|
||||
\'v:val[0] . " (" . v:val[1] . ")"'), ', ')
|
||||
return 'Unexpected enum value: expected one of ' . evals_msg
|
||||
endif
|
||||
return 0
|
||||
elseif a:type is# 'uint'
|
||||
if type isnot# 'integer'
|
||||
return 'Expected integer'
|
||||
endif
|
||||
if !(type(a:val) == type({}) ? a:val._VAL[0] == 1 : a:val >= 0)
|
||||
return 'Value is negative'
|
||||
endif
|
||||
return 0
|
||||
elseif a:type is# 'bin'
|
||||
" Binary string without zero bytes
|
||||
if type isnot# 'binary'
|
||||
return 'Expected binary string'
|
||||
elseif (type(a:val) == type({})
|
||||
\&& !empty(filter(copy(a:val._VAL), 'stridx(v:val, "\n") != -1')))
|
||||
return 'Expected no NUL bytes'
|
||||
endif
|
||||
return 0
|
||||
elseif a:type is# 'intchar'
|
||||
let msg = s:shada_check_type('uint', a:val)
|
||||
if msg isnot# 0
|
||||
return msg
|
||||
endif
|
||||
if a:val > 0 || a:val < 1
|
||||
endif
|
||||
return 0
|
||||
elseif a:type is# 'binarray'
|
||||
if type isnot# 'array'
|
||||
return 'Expected array value'
|
||||
elseif !empty(filter(copy(type(a:val) == type({}) ? a:val._VAL : a:val),
|
||||
\'msgpack#type(v:val) isnot# "binary"'))
|
||||
return 'Expected array of binary strings'
|
||||
else
|
||||
for element in (type(a:val) == type({}) ? a:val._VAL : a:val)
|
||||
if (type(element) == type({})
|
||||
\&& !empty(filter(copy(element._VAL), 'stridx(v:val, "\n") != -1')))
|
||||
return 'Expected no NUL bytes'
|
||||
endif
|
||||
unlet element
|
||||
endfor
|
||||
endif
|
||||
return 0
|
||||
elseif a:type is# 'boolean'
|
||||
return 'Expected boolean'
|
||||
elseif a:type is# 'integer'
|
||||
return 'Expected integer'
|
||||
elseif a:type is# 'any'
|
||||
return 0
|
||||
endif
|
||||
return 'Internal error: unknown type ' . a:type
|
||||
endfunction
|
||||
|
||||
""
|
||||
" Convert msgpack mapping object to a list of strings for
|
||||
" s:shada_convert_entry().
|
||||
"
|
||||
" @param[in] map Mapping to convert.
|
||||
" @param[in] default_keys List of keys which have default value in this
|
||||
" mapping.
|
||||
" @param[in] name Name of the converted entry.
|
||||
function s:shada_convert_map(map, default_keys, name) abort
|
||||
let ret = []
|
||||
let keys = copy(a:default_keys)
|
||||
call map(sort(keys(a:map)), 'index(keys, v:val) == -1 ? add(keys, v:val) : 0')
|
||||
let descriptions = map(copy(keys),
|
||||
\'get(s:SHADA_STANDARD_KEYS, v:val, ["", 0, 0])')
|
||||
let max_key_len = max(map(copy(keys), 'len(v:val)'))
|
||||
let max_desc_len = max(map(copy(descriptions),
|
||||
\'v:val[0] is 0 ? 0 : len(v:val[0])'))
|
||||
if max_key_len < len('Key')
|
||||
let max_key_len = len('Key')
|
||||
endif
|
||||
let key_header = 'Key' . repeat('_', max_key_len - len('Key'))
|
||||
if max_desc_len == 0
|
||||
call add(ret, printf(' %% %s %s', key_header, 'Value'))
|
||||
else
|
||||
if max_desc_len < len('Description')
|
||||
let max_desc_len = len('Description')
|
||||
endif
|
||||
let desc_header = ('Description'
|
||||
\. repeat('_', max_desc_len - len('Description')))
|
||||
call add(ret, printf(' %% %s %s %s', key_header, desc_header, 'Value'))
|
||||
endif
|
||||
let i = 0
|
||||
for key in keys
|
||||
let [description, type, default] = descriptions[i]
|
||||
if a:name isnot# 'local_mark' && key is# 'n'
|
||||
unlet default
|
||||
let default = s:SHADA_REQUIRED
|
||||
endif
|
||||
let value = get(a:map, key, default)
|
||||
if (key is# 'n' && !has_key(s:SHADA_REQUIRES_NAME, a:name)
|
||||
\&& value is# s:SHADA_REQUIRED)
|
||||
" Do nothing
|
||||
elseif value is s:SHADA_REQUIRED
|
||||
call add(ret, ' # Required key missing: ' . key)
|
||||
elseif max_desc_len == 0
|
||||
call add(ret, printf(' + %-*s %s',
|
||||
\max_key_len, key,
|
||||
\msgpack#string(value)))
|
||||
else
|
||||
if type isnot 0 && value isnot# default
|
||||
let msg = s:shada_check_type(type, value)
|
||||
if msg isnot 0
|
||||
call add(ret, ' # ' . msg)
|
||||
endif
|
||||
endif
|
||||
let strval = s:shada_string(type, value)
|
||||
if msgpack#type(value) is# 'array' && msg is 0
|
||||
let shift = 2 + 2 + max_key_len + 2 + max_desc_len + 2
|
||||
" Value: 1 2 3 4 5 6:
|
||||
" " + Key Description Value"
|
||||
" 1122333445555555555566
|
||||
if shift + strdisplaywidth(strval, shift) > 80
|
||||
let strval = '@'
|
||||
endif
|
||||
endif
|
||||
call add(ret, printf(' + %-*s %-*s %s',
|
||||
\max_key_len, key,
|
||||
\max_desc_len, description,
|
||||
\strval))
|
||||
if strval is '@'
|
||||
for v in value
|
||||
call add(ret, printf(' | - %s', msgpack#string(v)))
|
||||
unlet v
|
||||
endfor
|
||||
endif
|
||||
endif
|
||||
let i += 1
|
||||
unlet value
|
||||
unlet default
|
||||
endfor
|
||||
return ret
|
||||
endfunction
|
||||
|
||||
""
|
||||
" Wrapper around msgpack#string() which may return string from s:SHADA_REV_ENUMS
|
||||
function s:shada_string(type, v) abort
|
||||
if (has_key(s:SHADA_ENUMS, a:type) && type(a:v) == type(0)
|
||||
\&& has_key(s:SHADA_REV_ENUMS[a:type], a:v))
|
||||
return s:SHADA_REV_ENUMS[a:type][a:v]
|
||||
elseif (a:type is# 'intchar' && type(a:v) == type(0)
|
||||
\&& strtrans(nr2char(a:v)) is# nr2char(a:v))
|
||||
return "'" . nr2char(a:v) . "'"
|
||||
else
|
||||
return msgpack#string(a:v)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
""
|
||||
" Evaluate string obtained by s:shada_string().
|
||||
function s:shada_eval(s) abort
|
||||
return msgpack#eval(a:s, s:SHADA_SPECIAL_OBJS)
|
||||
endfunction
|
||||
|
||||
""
|
||||
" Convert one ShaDa entry to a list of strings suitable for setline().
|
||||
"
|
||||
" Returned format looks like this:
|
||||
"
|
||||
" TODO
|
||||
function s:shada_convert_entry(entry) abort
|
||||
if type(a:entry.type) == type({})
|
||||
" |msgpack-special-dict| may only be used if value does not fit into the
|
||||
" default integer type. All known entry types do fit, so it is definitely
|
||||
" unknown entry.
|
||||
let name = 'unknown_(' . msgpack#int_dict_to_str(a:entry.type) . ')'
|
||||
else
|
||||
let name = get(s:SHADA_ENTRY_NAMES, a:entry.type, 0)
|
||||
if name is 0
|
||||
let name = printf('unknown_(0x%x)', a:entry.type)
|
||||
endif
|
||||
endif
|
||||
let title = toupper(name[0]) . tr(name[1:], '_', ' ')
|
||||
let header = printf('%s with timestamp %s:', title,
|
||||
\s:shada_strftime(a:entry.timestamp))
|
||||
let ret = [header]
|
||||
if name[:8] is# 'unknown_(' && name[-1:] is# ')'
|
||||
call add(ret, ' = ' . msgpack#string(a:entry.data))
|
||||
elseif has_key(s:SHADA_FIXED_ARRAY_ENTRIES, name)
|
||||
if type(a:entry.data) != type([])
|
||||
call add(ret, printf(' # Unexpected type: %s instead of array',
|
||||
\msgpack#type(a:entry.data)))
|
||||
call add(ret, ' = ' . msgpack#string(a:entry.data))
|
||||
return ret
|
||||
endif
|
||||
let i = 0
|
||||
let max_desc_len = max(map(copy(s:SHADA_FIXED_ARRAY_ENTRIES[name]),
|
||||
\'len(v:val[0])'))
|
||||
if max_desc_len < len('Description')
|
||||
let max_desc_len = len('Description')
|
||||
endif
|
||||
let desc_header = ('Description'
|
||||
\. repeat('_', max_desc_len - len('Description')))
|
||||
call add(ret, printf(' @ %s %s', desc_header, 'Value'))
|
||||
for value in a:entry.data
|
||||
let [desc, type] = get(s:SHADA_FIXED_ARRAY_ENTRIES[name], i, ['', 0])
|
||||
if (i == 2 && name is# 'history_entry'
|
||||
\&& a:entry.data[0] isnot# s:SHADA_ENUMS.histtype.SEARCH)
|
||||
let [desc, type] = ['', 0]
|
||||
endif
|
||||
if type isnot 0
|
||||
let msg = s:shada_check_type(type, value)
|
||||
if msg isnot 0
|
||||
call add(ret, ' # ' . msg)
|
||||
endif
|
||||
endif
|
||||
call add(ret, printf(' - %-*s %s', max_desc_len, desc,
|
||||
\s:shada_string(type, value)))
|
||||
let i += 1
|
||||
unlet value
|
||||
endfor
|
||||
if (len(a:entry.data) < len(s:SHADA_FIXED_ARRAY_ENTRIES[name])
|
||||
\&& !(name is# 'history_entry'
|
||||
\&& len(a:entry.data) == 2
|
||||
\&& a:entry.data[0] isnot# s:SHADA_ENUMS.histtype.SEARCH))
|
||||
call add(ret, ' # Expected more elements in list')
|
||||
endif
|
||||
elseif has_key(s:SHADA_MAP_ENTRIES, name)
|
||||
if type(a:entry.data) != type({})
|
||||
call add(ret, printf(' # Unexpected type: %s instead of map',
|
||||
\msgpack#type(a:entry.data)))
|
||||
call add(ret, ' = ' . msgpack#string(a:entry.data))
|
||||
return ret
|
||||
endif
|
||||
if msgpack#special_type(a:entry.data) isnot 0
|
||||
call add(ret, ' # Entry is a special dict which is unexpected')
|
||||
call add(ret, ' = ' . msgpack#string(a:entry.data))
|
||||
return ret
|
||||
endif
|
||||
let ret += s:shada_convert_map(a:entry.data, s:SHADA_MAP_ENTRIES[name],
|
||||
\name)
|
||||
elseif name is# 'buffer_list'
|
||||
if type(a:entry.data) != type([])
|
||||
call add(ret, printf(' # Unexpected type: %s instead of array',
|
||||
\msgpack#type(a:entry.data)))
|
||||
call add(ret, ' = ' . msgpack#string(a:entry.data))
|
||||
return ret
|
||||
elseif !empty(filter(copy(a:entry.data),
|
||||
\'type(v:val) != type({}) '
|
||||
\. '|| msgpack#special_type(v:val) isnot 0'))
|
||||
call add(ret, ' # Expected array of maps')
|
||||
call add(ret, ' = ' . msgpack#string(a:entry.data))
|
||||
return ret
|
||||
endif
|
||||
for bufdef in a:entry.data
|
||||
if bufdef isnot a:entry.data[0]
|
||||
call add(ret, '')
|
||||
endif
|
||||
let ret += s:shada_convert_map(bufdef, s:SHADA_BUFFER_LIST_KEYS, name)
|
||||
endfor
|
||||
else
|
||||
throw 'internal-unknown-type:Internal error: unknown type name: ' . name
|
||||
endif
|
||||
return ret
|
||||
endfunction
|
||||
|
||||
""
|
||||
" Order of msgpack objects in one ShaDa entry. Each item in the list is name of
|
||||
" the key in dictionaries returned by shada#read().
|
||||
let s:SHADA_ENTRY_OBJECT_SEQUENCE = ['type', 'timestamp', 'length', 'data']
|
||||
|
||||
""
|
||||
" Convert list returned by msgpackparse() to a list of ShaDa objects
|
||||
"
|
||||
" @param[in] mpack List of VimL objects returned by msgpackparse().
|
||||
"
|
||||
" @return List of dictionaries with keys type, timestamp, length and data. Each
|
||||
" dictionary describes one ShaDa entry.
|
||||
function shada#mpack_to_sd(mpack) abort
|
||||
let ret = []
|
||||
let i = 0
|
||||
for element in a:mpack
|
||||
let key = s:SHADA_ENTRY_OBJECT_SEQUENCE[
|
||||
\i % len(s:SHADA_ENTRY_OBJECT_SEQUENCE)]
|
||||
if key is# 'type'
|
||||
call add(ret, {})
|
||||
endif
|
||||
let ret[-1][key] = element
|
||||
if key isnot# 'data'
|
||||
if !msgpack#is_uint(element)
|
||||
throw printf('not-uint:Entry %i has %s element '.
|
||||
\'which is not an unsigned integer',
|
||||
\len(ret), key)
|
||||
endif
|
||||
if key is# 'type' && msgpack#equal(element, 0)
|
||||
throw printf('zero-uint:Entry %i has %s element '.
|
||||
\'which is zero',
|
||||
\len(ret), key)
|
||||
endif
|
||||
endif
|
||||
let i += 1
|
||||
unlet element
|
||||
endfor
|
||||
return ret
|
||||
endfunction
|
||||
|
||||
""
|
||||
" Convert read ShaDa file to a list of lines suitable for setline()
|
||||
"
|
||||
" @param[in] shada List of ShaDa entries like returned by shada#mpack_to_sd().
|
||||
"
|
||||
" @return List of strings suitable for setline()-like functions.
|
||||
function shada#sd_to_strings(shada) abort
|
||||
let ret = []
|
||||
for entry in a:shada
|
||||
let ret += s:shada_convert_entry(entry)
|
||||
endfor
|
||||
return ret
|
||||
endfunction
|
||||
|
||||
""
|
||||
" Convert a readfile()-like list of strings to a list of lines suitable for
|
||||
" setline().
|
||||
"
|
||||
" @param[in] binstrings List of strings to convert.
|
||||
"
|
||||
" @return List of lines.
|
||||
function shada#get_strings(binstrings) abort
|
||||
return shada#sd_to_strings(shada#mpack_to_sd(msgpackparse(a:binstrings)))
|
||||
endfunction
|
||||
|
||||
""
|
||||
" Convert s:shada_convert_entry() output to original entry.
|
||||
function s:shada_convert_strings(strings) abort
|
||||
let strings = copy(a:strings)
|
||||
let match = matchlist(
|
||||
\strings[0],
|
||||
\'\v\C^(.{-})\m with timestamp \(\d\{4}-\d\d-\d\dT\d\d:\d\d:\d\d\):$')
|
||||
if empty(match)
|
||||
throw 'invalid-header:Header has invalid format: ' . strings[0]
|
||||
endif
|
||||
call remove(strings, 0)
|
||||
let title = match[1]
|
||||
let name = tolower(title[0]) . tr(title[1:], ' ', '_')
|
||||
let ret = {}
|
||||
let empty_default = g:msgpack#nil
|
||||
if name[:8] is# 'unknown_(' && name[-1:] is# ')'
|
||||
let ret.type = +name[9:-2]
|
||||
elseif has_key(s:SHADA_ENTRY_TYPES, name)
|
||||
let ret.type = s:SHADA_ENTRY_TYPES[name]
|
||||
if has_key(s:SHADA_MAP_ENTRIES, name)
|
||||
unlet empty_default
|
||||
let empty_default = {}
|
||||
elseif has_key(s:SHADA_FIXED_ARRAY_ENTRIES, name) || name is# 'buffer_list'
|
||||
unlet empty_default
|
||||
let empty_default = []
|
||||
endif
|
||||
else
|
||||
throw 'invalid-type:Unknown type ' . name
|
||||
endif
|
||||
let ret.timestamp = s:shada_strptime(match[2])
|
||||
if empty(strings)
|
||||
let ret.data = empty_default
|
||||
else
|
||||
while !empty(strings)
|
||||
if strings[0][2] is# '='
|
||||
let data = s:shada_eval(strings[0][4:])
|
||||
call remove(strings, 0)
|
||||
elseif strings[0][2] is# '%'
|
||||
if name is# 'buffer_list' && !has_key(ret, 'data')
|
||||
let ret.data = []
|
||||
endif
|
||||
let match = matchlist(
|
||||
\strings[0],
|
||||
\'\m\C^ % \(Key_*\)\( Description_*\)\? Value')
|
||||
if empty(match)
|
||||
throw 'invalid-map-header:Invalid mapping header: ' . strings[0]
|
||||
endif
|
||||
call remove(strings, 0)
|
||||
let key_len = len(match[1])
|
||||
let desc_skip_len = len(match[2])
|
||||
let data = {'_TYPE': v:msgpack_types.map, '_VAL': []}
|
||||
while !empty(strings) && strings[0][2] is# '+'
|
||||
let line = remove(strings, 0)[4:]
|
||||
let key = substitute(line[:key_len - 1], '\v\C\ *$', '', '')
|
||||
let strval = line[key_len + desc_skip_len + 2:]
|
||||
if strval is# '@'
|
||||
let val = []
|
||||
while !empty(strings) && strings[0][2] is# '|'
|
||||
if strings[0][4] isnot# '-'
|
||||
throw ('invalid-array:Expected hyphen-minus at column 5: '
|
||||
\. strings)
|
||||
endif
|
||||
call add(val, s:shada_eval(remove(strings, 0)[5:]))
|
||||
endwhile
|
||||
else
|
||||
let val = s:shada_eval(strval)
|
||||
endif
|
||||
if (has_key(s:SHADA_STANDARD_KEYS, key)
|
||||
\&& s:SHADA_STANDARD_KEYS[key][2] isnot# s:SHADA_REQUIRED
|
||||
\&& msgpack#equal(s:SHADA_STANDARD_KEYS[key][2], val))
|
||||
unlet val
|
||||
continue
|
||||
endif
|
||||
call add(data._VAL, [{'_TYPE': v:msgpack_types.string, '_VAL': [key]},
|
||||
\val])
|
||||
unlet val
|
||||
endwhile
|
||||
elseif strings[0][2] is# '@'
|
||||
let match = matchlist(
|
||||
\strings[0],
|
||||
\'\m\C^ @ \(Description_* \)\?Value')
|
||||
if empty(match)
|
||||
throw 'invalid-array-header:Invalid array header: ' . strings[0]
|
||||
endif
|
||||
call remove(strings, 0)
|
||||
let desc_skip_len = len(match[1])
|
||||
let data = []
|
||||
while !empty(strings) && strings[0][2] is# '-'
|
||||
let val = remove(strings, 0)[4 + desc_skip_len :]
|
||||
call add(data, s:shada_eval(val))
|
||||
endwhile
|
||||
else
|
||||
throw 'invalid-line:Unrecognized line: ' . strings[0]
|
||||
endif
|
||||
if !has_key(ret, 'data')
|
||||
let ret.data = data
|
||||
elseif type(ret.data) == type([])
|
||||
call add(ret.data, data)
|
||||
else
|
||||
let ret.data = [ret.data, data]
|
||||
endif
|
||||
unlet data
|
||||
endwhile
|
||||
endif
|
||||
let ret._data = msgpackdump([ret.data])
|
||||
let ret.length = len(ret._data) - 1
|
||||
for s in ret._data
|
||||
let ret.length += len(s)
|
||||
endfor
|
||||
return ret
|
||||
endfunction
|
||||
|
||||
""
|
||||
" Convert s:shada_sd_to_strings() output to a list of original entries.
|
||||
function shada#strings_to_sd(strings) abort
|
||||
let strings = filter(copy(a:strings), 'v:val !~# ''\v^\s*%(\#|$)''')
|
||||
let stringss = []
|
||||
for string in strings
|
||||
if string[0] isnot# ' '
|
||||
call add(stringss, [])
|
||||
endif
|
||||
call add(stringss[-1], string)
|
||||
endfor
|
||||
return map(copy(stringss), 's:shada_convert_strings(v:val)')
|
||||
endfunction
|
||||
|
||||
""
|
||||
" Convert a list of strings to list of strings suitable for writefile().
|
||||
function shada#get_binstrings(strings) abort
|
||||
let entries = shada#strings_to_sd(a:strings)
|
||||
if !g:shada#keep_old_header
|
||||
call filter(entries, 'v:val.type != ' . s:SHADA_ENTRY_TYPES.header)
|
||||
endif
|
||||
if g:shada#add_own_header
|
||||
let data = {'version': v:version, 'generator': 'shada.vim'}
|
||||
let dumped_data = msgpackdump([data])
|
||||
let length = len(dumped_data) - 1
|
||||
for s in dumped_data
|
||||
let length += len(s)
|
||||
endfor
|
||||
call insert(entries, {
|
||||
\'type': s:SHADA_ENTRY_TYPES.header,
|
||||
\'timestamp': localtime(),
|
||||
\'length': length,
|
||||
\'data': data,
|
||||
\'_data': dumped_data,
|
||||
\})
|
||||
endif
|
||||
let mpack = []
|
||||
for entry in entries
|
||||
let mpack += map(copy(s:SHADA_ENTRY_OBJECT_SEQUENCE), 'entry[v:val]')
|
||||
endfor
|
||||
return msgpackdump(mpack)
|
||||
endfunction
|
@ -557,6 +557,149 @@ Since the text for this plugin is rather long it has been put in a separate
|
||||
file: |pi_spec.txt|.
|
||||
|
||||
|
||||
SHADA *ft-shada*
|
||||
|
||||
Allows editing binary |shada-file|s in a nice way. Opened binary files are
|
||||
displayed in the following format: >
|
||||
|
||||
Type with timestamp YYYY-mm-ddTHH:MM:SS:
|
||||
% Key__ Description___ Value
|
||||
+ fooba foo bar baz fo {msgpack-value}
|
||||
+ abcde abc def ghi jk {msgpack-value}
|
||||
Other type with timestamp YYYY-mm-ddTHH:MM:SS:
|
||||
@ Description__ Value
|
||||
- foo bar baz t {msgpack-value}
|
||||
# Expected more elements in list
|
||||
Some other type with timestamp YYYY-mm-ddTHH:MM:SS:
|
||||
# Unexpected type: type instead of map
|
||||
= {msgpack-value}
|
||||
|
||||
Filetype plugin defines all |Cmd-event|s. Defined |SourceCmd| event makes
|
||||
"source file.shada" be equivalent to "|:rshada| file.shada". |BufWriteCmd|,
|
||||
|FileWriteCmd| and |FileAppendCmd| events are affected by the following
|
||||
settings:
|
||||
|
||||
*g:shada#keep_old_header* Boolean, if set to false all header entries
|
||||
are ignored when writing. Defaults to 1.
|
||||
*g:shada#add_own_header* Boolean, if set to true first written entry
|
||||
will always be header entry with two values in
|
||||
a map with attached data: |v:version| attached
|
||||
to "version" key and "shada.vim" attached to
|
||||
"generator" key. Defaults to 1.
|
||||
|
||||
Format description:
|
||||
|
||||
1. `#` starts a comment. Lines starting with space characters and then `#`
|
||||
are ignored. Plugin may only add comment lines to indicate some errors in
|
||||
ShaDa format. Lines containing no non-whitespace characters are also
|
||||
ignored.
|
||||
2. Each entry starts with line that has format "{type} with timestamp
|
||||
{timestamp}:". {timestamp} is |strftime()|-formatted string representing
|
||||
actual UNIX timestamp value. First strftime() argument is equal to
|
||||
`%Y-%m-%dT%H:%M:%S`. When writing this timestamp is parsed using
|
||||
|msgpack#strptime()|, with caching (it remembers which timestamp produced
|
||||
particular strftime() output and uses this value if you did not change
|
||||
timestamp). {type} is one of
|
||||
1 - Header
|
||||
2 - Search pattern
|
||||
3 - Replacement string
|
||||
4 - History entry
|
||||
5 - Register
|
||||
6 - Variable
|
||||
7 - Global mark
|
||||
8 - Jump
|
||||
9 - Buffer list
|
||||
10 - Local mark
|
||||
11 - Change
|
||||
* - Unknown (0x{type-hex})
|
||||
|
||||
Each type may be represented using Unknown entry: "Jump with timestamp ..."
|
||||
is the same as "Unknown (0x8) with timestamp ....".
|
||||
3. After header there is one of the following lines:
|
||||
1. " % Key__ Description__ Value": map header. After mapping header
|
||||
follows a table which may contain comments and lines consisting of
|
||||
" +", key, description and |{msgpack-value}|. Key is separated by at
|
||||
least two spaces with description, description is separated by at least
|
||||
two spaces with the value. Each key in the map must be at most as wide
|
||||
as "Key__" header: "Key" allows at most 3-byte keys, "Key__" allows at
|
||||
most 5-byte keys. If keys actually occupy less bytes then the rest is
|
||||
filled with spaces. Keys cannot be empty, end with spaces, contain two
|
||||
consequent spaces inside of them or contain multibyte characters (use
|
||||
"=" format if you need this). Descriptions have the same restrictions
|
||||
on width and contents, except that empty descriptions are allowed.
|
||||
Description column may be omitted.
|
||||
|
||||
When writing description is ignored. Keys with values |msgpack#equal|
|
||||
to default ones are ignored. Order of keys is preserved. All keys are
|
||||
treated as strings (not binary strings).
|
||||
|
||||
Note: specifically for buffer list entries it is allowed to have more
|
||||
then one map header. Each map header starts a new map entry inside
|
||||
buffer list because ShaDa buffer list entry is an array of maps. I.e. >
|
||||
|
||||
Buffer list with timestamp 1970-01-01T00:00:00:
|
||||
% Key Description Value
|
||||
+ f file name "/foo"
|
||||
+ l line number 1
|
||||
+ c column 10
|
||||
<
|
||||
is equivalent to >
|
||||
|
||||
Buffer list with timestamp 1970-01-01T00:00:00:
|
||||
= [{="f": "/foo", ="c": 10}]
|
||||
<
|
||||
and >
|
||||
|
||||
Buffer list with timestamp 1970-01-01T00:00:00:
|
||||
% Key Description Value
|
||||
+ f file name "/foo"
|
||||
|
||||
% Key Description Value
|
||||
+ f file name "/bar"
|
||||
<
|
||||
is equivalent to >
|
||||
|
||||
Buffer list with timestamp 1970-01-01T00:00:00:
|
||||
= [{="f": "/foo"}, {="f": "/bar"}]
|
||||
<
|
||||
Note 2: specifically for register entries special syntax for arrays was
|
||||
designed: >
|
||||
|
||||
Register with timestamp 1970-01-01T00:00:00:
|
||||
% Key Description Value
|
||||
+ rc contents @
|
||||
| - "line1"
|
||||
| - "line2"
|
||||
<
|
||||
This is equivalent to >
|
||||
|
||||
Register with timestamp 1970-01-01T00:00:00:
|
||||
% Key Description Value
|
||||
+ rc contents ["line1", "line2"]
|
||||
<
|
||||
Such syntax is automatically used if array representation appears to be
|
||||
too lengthy.
|
||||
2. " @ Description__ Value": array header. Same as map, but key is
|
||||
omitted and description cannot be omitted. Array entries start with
|
||||
" -". Example: >
|
||||
|
||||
History entry with timestamp 1970-01-01T00:00:00:
|
||||
@ Description_ Value
|
||||
- history type SEARCH
|
||||
- contents "foo"
|
||||
- separator '/'
|
||||
<
|
||||
is equivalent to >
|
||||
|
||||
History entry with timestamp 1970-01-01T00:00:00:
|
||||
= [SEARCH, "foo", '/']
|
||||
<
|
||||
Note: special array syntax for register entries is not recognized here.
|
||||
3. " = {msgpack-value}": raw values. |{msgpack-value}| in this case may
|
||||
have absolutely any type. Special array syntax for register entries is
|
||||
not recognized here as well.
|
||||
|
||||
|
||||
SQL *ft-sql*
|
||||
|
||||
Since the text for this plugin is rather long it has been put in a separate
|
||||
|
139
runtime/doc/pi_msgpack.txt
Normal file
139
runtime/doc/pi_msgpack.txt
Normal file
@ -0,0 +1,139 @@
|
||||
*pi_msgpack.txt* For NeoVim version 0.1.
|
||||
|
||||
Author: Nikolay Pavlov <kp-pav@yandex.ru>
|
||||
Copyright: (c) 2015 by Nikolay Pavlov
|
||||
|
||||
The Apache license applies to the files in this package, including
|
||||
runtime/autoload/msgpack.vim, runtime/doc/pi_msgpack.txt and
|
||||
test/functional/plugin/msgpack_spec.lua. Like anything else that's free,
|
||||
msgpack.vim and its associated files are provided *as is* and comes with no
|
||||
warranty of any kind, either expressed or implied. No guarantees of
|
||||
merchantability. No guarantees of suitability for any purpose. By using this
|
||||
plugin, you agree that in no event will the copyright holder be liable for any
|
||||
damages resulting from the use of this software. Use at your own risk!
|
||||
|
||||
==============================================================================
|
||||
1. Contents *msgpack.vim-contents*
|
||||
|
||||
1. Contents..............................: |msgpack.vim-contents|
|
||||
2. Msgpack.vim introduction..............: |msgpack.vim-intro|
|
||||
3. Msgpack.vim manual....................: |msgpack.vim-manual|
|
||||
Function arguments....................: |msgpack.vim-arguments|
|
||||
msgpack#is_int function...............: |msgpack#is_int()|
|
||||
msgpack#is_uint function..............: |msgpack#is_uint()|
|
||||
msgpack#strftime function.............: |msgpack#strftime()|
|
||||
msgpack#strptime function.............: |msgpack#strptime()|
|
||||
msgpack#int_dict_to_str function......: |msgpack#int_dict_to_str()|
|
||||
msgpack#special_type function.........: |msgpack#special_type()|
|
||||
msgpack#type function.................: |msgpack#type()|
|
||||
msgpack#deepcopy function.............: |msgpack#deepcopy()|
|
||||
msgpack#string function...............: |msgpack#string()|
|
||||
msgpack#eval function.................: |msgpack#eval()|
|
||||
msgpack#equal function................: |msgpack#equal()|
|
||||
|
||||
|
||||
==============================================================================
|
||||
2. Msgpack.vim introduction *msgpack.vim-intro*
|
||||
|
||||
This plugin contains utility functions to be used in conjunction with
|
||||
|msgpackdump()| and |msgpackparse()| functions.
|
||||
|
||||
==============================================================================
|
||||
3. Msgpack.vim manual *msgpack.vim-manual*
|
||||
|
||||
FUNCTION ARGUMENTS *msgpack.vim-arguments*
|
||||
|
||||
Disambiguation of arguments described below. Note: if e.g. function is listed
|
||||
as accepting |{msgpack-integer}| (or anything else) it means that function
|
||||
does not check whether argument matches its description.
|
||||
|
||||
*{msgpack-value}* Either |msgpack-special-dict| or a regular value, but
|
||||
not function reference.
|
||||
*{msgpack-integer}* Any value for which |msgpack#type| will return
|
||||
"integer".
|
||||
*{msgpack-special-int}* |msgpack-special-dict| representing integer.
|
||||
|
||||
msgpack#is_int({msgpack-value}) *msgpack#is_int()*
|
||||
Returns 1 if given {msgpack-value} is integer value, 0 otherwise.
|
||||
|
||||
msgpack#is_uint({msgpack-value}) *msgpack#is_uint()*
|
||||
Returns 1 if given {msgpack-value} is integer value greater or equal
|
||||
to zero, 0 otherwise.
|
||||
|
||||
*msgpack#strftime*
|
||||
msgpack#strftime({format}, {msgpack-integer}) *msgpack#strftime()*
|
||||
Same as |strftime()|, but second argument may be
|
||||
|msgpack-special-dict|. Requires |+python| or |+python3| to really
|
||||
work with |msgpack-special-dict|s.
|
||||
|
||||
*msgpack#strptime*
|
||||
msgpack#strptime({format}, {time}) *msgpack#strptime()*
|
||||
Reverse of |msgpack#strptime()|: for any time and format
|
||||
|msgpack#equal|( |msgpack#strptime|(format, |msgpack#strftime|(format,
|
||||
time)), time) be true. Requires |+python| or |+python3|, without it
|
||||
only supports non-|msgpack-special-dict| nonnegative times and format
|
||||
equal to `%Y-%m-%dT%H:%M:%S`.
|
||||
|
||||
msgpack#int_dict_to_str({msgpack-special-int}) *msgpack#int_dict_to_str()*
|
||||
Function which converts |msgpack-special-dict| integer value to
|
||||
a hexadecimal value like 0x1234567890ABCDEF (always returns exactly 16
|
||||
hexadecimal digits).
|
||||
|
||||
msgpack#special_type({msgpack-value}) *msgpack#special_type()*
|
||||
Returns zero if {msgpack-value} is not |msgpack-special-dict|. If it
|
||||
is it returns name of the key in |v:msgpack_types| which represents
|
||||
{msgpack-value} type.
|
||||
|
||||
msgpack#type({msgpack-value}) *msgpack#type()*
|
||||
Returns name of the key in |v:msgpack_types| that represents
|
||||
{msgpack-value} type. Never returns zero: this function returns
|
||||
msgpack type which will be dumped by |msgpackdump()| should it receive
|
||||
a list with singe {msgpack-value} as input.
|
||||
|
||||
msgpack#deepcopy({msgpack-value}) *msgpack#deepcopy()*
|
||||
Like |deepcopy()|, but works correctly with |msgpack-special-dict|
|
||||
values. Plain |deepcopy()| will destroy all types in
|
||||
|msgpack-special-dict| values because it will copy _TYPE key values,
|
||||
while they should be preserved.
|
||||
|
||||
msgpack#string({msgpack-value}) *msgpack#string()*
|
||||
Like |string()|, but saves information about msgpack types. Values
|
||||
dumped by msgpack#string may be read back by |msgpack#eval()|.
|
||||
Returns is the following:
|
||||
|
||||
- Dictionaries are dumped as "{key1: value1, key2: value2}". Note:
|
||||
msgpack allows any values in keys, so with some
|
||||
|msgpack-special-dict| values |msgpack#string()| may produce even
|
||||
"{{1: 2}: 3, [4]: 5}".
|
||||
- Lists are dumped as "[value1, value2]".
|
||||
- Strings are dumped as
|
||||
1. `"abc"`: binary string.
|
||||
2. `="abc"`: string.
|
||||
3. `+(10)"ext"`: extension strings (10 may be replaced with any
|
||||
8-bit signed integer).
|
||||
Inside strings the following escape sequences may be present: "\0"
|
||||
(represents NUL byte), "\n" (represents line feed) and "\""
|
||||
(represents double quote).
|
||||
- Floating-point and integer values are dumped using |string()| or
|
||||
|msgpack#int_dict_to_str()|.
|
||||
- Booleans are dumped as "TRUE" or "FALSE".
|
||||
- Nil values are dumped as "NIL".
|
||||
|
||||
msgpack#eval({string}, {dict}) *msgpack#eval()*
|
||||
Transforms string created by |msgpack#string()| into a value suitable
|
||||
for |msgpackdump()|. Second argument allows adding special values
|
||||
that start with head characters (|/\h|) and contain only word
|
||||
characters (|/\w|). Built-in special values are "TRUE", "FALSE",
|
||||
"NIL", "nan" and "inf" and they cannot be overridden. Map values are
|
||||
always evaluated to |msgpack-special-dict| values, as well as
|
||||
hexadecimal digits. When evaluating maps order of keys is preserved.
|
||||
|
||||
*msgpack#equal*
|
||||
msgpack#equal({msgpack-value}, {msgpack-value}) *msgpack#equal()*
|
||||
Returns 1 if given values are equal, 0 otherwise. When comparing
|
||||
msgpack map values order of keys is ignored. Comparing
|
||||
|msgpack-special-dict| with equivalent non-special-dict value
|
||||
evaluates to 1.
|
||||
|
||||
==============================================================================
|
||||
vim:tw=78:ts=8:ft=help:fdm=marker
|
18
runtime/ftplugin/shada.vim
Normal file
18
runtime/ftplugin/shada.vim
Normal file
@ -0,0 +1,18 @@
|
||||
if exists('b:did_ftplugin')
|
||||
finish
|
||||
endif
|
||||
|
||||
let b:did_ftplugin = 1
|
||||
|
||||
function! ShaDaIndent(lnum)
|
||||
if a:lnum == 1 || getline(a:lnum) =~# '\mwith timestamp.*:$'
|
||||
return 0
|
||||
else
|
||||
return shiftwidth()
|
||||
endif
|
||||
endfunction
|
||||
|
||||
setlocal expandtab tabstop=2 softtabstop=2 shiftwidth=2
|
||||
setlocal indentexpr=ShaDaIndent(v:lnum) indentkeys=<:>,o,O
|
||||
|
||||
let b:undo_ftplugin = 'setlocal et< ts< sts< sw< indentexpr< indentkeys<'
|
39
runtime/plugin/shada.vim
Normal file
39
runtime/plugin/shada.vim
Normal file
@ -0,0 +1,39 @@
|
||||
if exists('g:loaded_shada_plugin')
|
||||
finish
|
||||
endif
|
||||
let g:loaded_shada_plugin = 1
|
||||
|
||||
augroup ShaDaCommands
|
||||
autocmd!
|
||||
autocmd BufReadCmd *.shada,*.shada.tmp.[a-z]
|
||||
\ :if !empty(v:cmdarg)|throw '++opt not supported'|endif
|
||||
\ |call setline('.', shada#get_strings(readfile(expand('<afile>'),'b')))
|
||||
\ |setlocal filetype=shada
|
||||
autocmd FileReadCmd *.shada,*.shada.tmp.[a-z]
|
||||
\ :if !empty(v:cmdarg)|throw '++opt not supported'|endif
|
||||
\ |call append("'[", shada#get_strings(readfile(expand('<afile>'), 'b')))
|
||||
autocmd BufWriteCmd *.shada,*.shada.tmp.[a-z]
|
||||
\ :if !empty(v:cmdarg)|throw '++opt not supported'|endif
|
||||
\ |if writefile(shada#get_binstrings(getline(1, '$')),
|
||||
\expand('<afile>'), 'b') == 0
|
||||
\ | let &l:modified = (expand('<afile>') is# bufname(+expand('<abuf>'))
|
||||
\? 0
|
||||
\: stridx(&cpoptions, '+') != -1)
|
||||
\ |endif
|
||||
autocmd FileWriteCmd *.shada,*.shada.tmp.[a-z]
|
||||
\ :if !empty(v:cmdarg)|throw '++opt not supported'|endif
|
||||
\ |call writefile(
|
||||
\shada#get_binstrings(getline(min([line("'["), line("']")]),
|
||||
\max([line("'["), line("']")]))),
|
||||
\expand('<afile>'),
|
||||
\'b')
|
||||
autocmd FileAppendCmd *.shada,*.shada.tmp.[a-z]
|
||||
\ :if !empty(v:cmdarg)|throw '++opt not supported'|endif
|
||||
\ |call writefile(
|
||||
\shada#get_binstrings(getline(min([line("'["), line("']")]),
|
||||
\max([line("'["), line("']")]))),
|
||||
\expand('<afile>'),
|
||||
\'ab')
|
||||
autocmd SourceCmd *.shada,*.shada.tmp.[a-z]
|
||||
\ :execute 'rshada' fnameescape(expand('<afile>'))
|
||||
augroup END
|
125
runtime/syntax/shada.vim
Normal file
125
runtime/syntax/shada.vim
Normal file
@ -0,0 +1,125 @@
|
||||
if exists("b:current_syntax")
|
||||
finish
|
||||
endif
|
||||
|
||||
syntax match ShaDaEntryHeader
|
||||
\ '^\u.\{-} with timestamp \d\{4}-\d\d-\d\dT\d\d:\d\d:\d\d:$'
|
||||
syntax match ShaDaEntryName '^\u.\{-}\ze with' contained
|
||||
\ containedin=ShaDaEntryHeader
|
||||
syntax match ShaDaEntryTimestamp 'timestamp \zs\d\{4}-\d\d-\d\dT\d\d:\d\d:\d\d'
|
||||
\ contained containedin=ShaDaEntryHeader
|
||||
syntax match ShaDaEntryTimestampNumber '\d\+' contained
|
||||
\ containedin=ShaDaEntryTimestamp
|
||||
|
||||
syntax match ShaDaComment '^\s*#.*$'
|
||||
|
||||
syntax region ShaDaEntryMapLong start='^ % Key_* Description_* Value$'
|
||||
\ end='^ %\|^\S'me=s-1 contains=ShaDaComment,ShaDaEntryMapLongEntryStart
|
||||
syntax region ShaDaEntryMapShort start='^ % Key_* Value$'
|
||||
\ end='^ %\|^\S'me=s-1 contains=ShaDaComment,ShaDaEntryMapShortEntryStart
|
||||
syntax match ShaDaEntryMapHeader '^ % Key_* \(Description_* \)\?Value$'
|
||||
\ contained containedin=ShaDaEntryMapLong,ShaDaEntryMapShort
|
||||
syntax match ShaDaEntryMapLongEntryStart '^ + 'hs=e-2,he=e-1
|
||||
\ nextgroup=ShaDaEntryMapLongKey
|
||||
syntax match ShaDaEntryMapLongKey '\S\+ \+\ze\S'he=e-2 contained
|
||||
\ nextgroup=ShaDaEntryMapLongDescription
|
||||
syntax match ShaDaEntryMapLongDescription '.\{-} \ze\S'he=e-2 contained
|
||||
\ nextgroup=@ShaDaEntryMsgpackValue
|
||||
syntax match ShaDaEntryMapShortEntryStart '^ + 'hs=e-2,he=e-1 contained
|
||||
\ nextgroup=ShaDaEntryMapShortKey
|
||||
syntax match ShaDaEntryMapShortKey '\S\+ \+\ze\S'he=e-2 contained
|
||||
\ nextgroup=@ShaDaEntryMsgpackValue
|
||||
syntax match ShaDaEntryMapBinArrayStart '^ | - 'hs=e-4,he=e-1 contained
|
||||
\ containedin=ShaDaEntryMapLong,ShaDaEntryMapShort
|
||||
\ nextgroup=@ShaDaEntryMsgpackValue
|
||||
|
||||
syntax region ShaDaEntryArray start='^ @ Description_* Value$'
|
||||
\ end='^\S'me=s-1 keepend
|
||||
\ contains=ShaDaComment,ShaDaEntryArrayEntryStart,ShaDaEntryArrayHeader
|
||||
syntax match ShaDaEntryArrayHeader '^ @ Description_* Value$' contained
|
||||
syntax match ShaDaEntryArrayEntryStart '^ - 'hs=e-2,he=e-1
|
||||
\ nextgroup=ShaDaEntryArrayDescription
|
||||
syntax match ShaDaEntryArrayDescription '.\{-} \ze\S'he=e-2 contained
|
||||
\ nextgroup=@ShaDaEntryMsgpackValue
|
||||
|
||||
syntax match ShaDaEntryRawMsgpack '^ = ' nextgroup=@ShaDaEntryMsgpackValue
|
||||
|
||||
syntax cluster ShaDaEntryMsgpackValue
|
||||
\ add=ShaDaMsgpackKeyword,ShaDaMsgpackShaDaKeyword
|
||||
\ add=ShaDaMsgpackInteger,ShaDaMsgpackCharacter,ShaDaMsgpackFloat
|
||||
\ add=ShaDaMsgpackBinaryString,ShaDaMsgpackString,ShaDaMsgpackExt
|
||||
\ add=ShaDaMsgpackArray,ShaDaMsgpackMap
|
||||
\ add=ShaDaMsgpackMultilineArray
|
||||
syntax keyword ShaDaMsgpackKeyword contained NIL TRUE FALSE
|
||||
syntax keyword ShaDaMsgpackShaDaKeyword contained
|
||||
\ CMD SEARCH EXPR INPUT DEBUG
|
||||
\ CHARACTERWISE LINEWISE BLOCKWISE
|
||||
syntax region ShaDaMsgpackBinaryString matchgroup=ShaDaMsgpackStringQuotes
|
||||
\ start='"' skip='\\"' end='"' contained keepend
|
||||
syntax match ShaDaMsgpackBinaryStringEscape '\\[\\0n"]'
|
||||
\ contained containedin=ShaDaMsgpackBinaryString
|
||||
syntax match ShaDaMsgpackString '=' contained nextgroup=ShaDaMsgpackBinaryString
|
||||
syntax match ShaDaMsgpackExt '+(-\?\d\+)' contained
|
||||
\ nextgroup=ShaDaMsgpackBinaryString
|
||||
syntax match ShaDaMsgpackExtType '-\?\d\+' contained containedin=ShaDaMsgpackExt
|
||||
syntax match ShaDaMsgpackCharacter /'.'/ contained
|
||||
syntax match ShaDaMsgpackInteger '-\?\%(0x\x\{,16}\|\d\+\)' contained
|
||||
syntax match ShaDaMsgpackFloat '-\?\d\+\.\d\+\%(e[+-]\?\d\+\)\?' contained
|
||||
syntax region ShaDaMsgpackArray matchgroup=ShaDaMsgpackArrayBraces
|
||||
\ start='\[' end='\]' contained
|
||||
\ contains=@ShaDaEntryMsgpackValue,ShaDaMsgpackComma
|
||||
syntax region ShaDaMsgpackMap matchgroup=ShaDaMsgpackMapBraces
|
||||
\ start='{' end='}' contained
|
||||
\ contains=@ShaDaEntryMsgpackValue,ShaDaMsgpackComma,ShaDaMsgpackColon
|
||||
syntax match ShaDaMsgpackComma ',' contained
|
||||
syntax match ShaDaMsgpackColon ':' contained
|
||||
syntax match ShaDaMsgpackMultilineArray '@' contained
|
||||
|
||||
hi def link ShaDaComment Comment
|
||||
hi def link ShaDaEntryNumber Number
|
||||
hi def link ShaDaEntryTimestamp Operator
|
||||
hi def link ShaDaEntryName Keyword
|
||||
|
||||
hi def link ShaDaEntryMapHeader PreProc
|
||||
|
||||
hi def link ShaDaEntryMapEntryStart Label
|
||||
hi def link ShaDaEntryMapLongEntryStart ShaDaEntryMapEntryStart
|
||||
hi def link ShaDaEntryMapShortEntryStart ShaDaEntryMapEntryStart
|
||||
hi def link ShaDaEntryMapBinArrayStart ShaDaEntryMapEntryStart
|
||||
hi def link ShaDaEntryArrayEntryStart ShaDaEntryMapEntryStart
|
||||
|
||||
hi def link ShaDaEntryMapKey String
|
||||
hi def link ShaDaEntryMapLongKey ShaDaEntryMapKey
|
||||
hi def link ShaDaEntryMapShortKey ShaDaEntryMapKey
|
||||
|
||||
hi def link ShaDaEntryMapDescription Comment
|
||||
hi def link ShaDaEntryMapLongDescription ShaDaEntryMapDescription
|
||||
hi def link ShaDaEntryMapShortDescription ShaDaEntryMapDescription
|
||||
|
||||
hi def link ShaDaEntryArrayHeader PreProc
|
||||
|
||||
hi def link ShaDaEntryArrayDescription ShaDaEntryMapDescription
|
||||
|
||||
hi def link ShaDaMsgpackKeyword Keyword
|
||||
hi def link ShaDaMsgpackShaDaKeyword ShaDaMsgpackKeyword
|
||||
hi def link ShaDaMsgpackCharacter Character
|
||||
hi def link ShaDaMsgpackInteger Number
|
||||
hi def link ShaDaMsgpackFloat Float
|
||||
|
||||
hi def link ShaDaMsgpackBinaryString String
|
||||
hi def link ShaDaMsgpackBinaryStringEscape SpecialChar
|
||||
hi def link ShaDaMsgpackExtType Typedef
|
||||
|
||||
hi def link ShaDaMsgpackStringQuotes Operator
|
||||
hi def link ShaDaMsgpackString ShaDaMsgpackStringQuotes
|
||||
hi def link ShaDaMsgpackExt ShaDaMsgpackStringQuotes
|
||||
|
||||
hi def link ShaDaMsgpackMapBraces Operator
|
||||
hi def link ShaDaMsgpackArrayBraces ShaDaMsgpackMapBraces
|
||||
|
||||
hi def link ShaDaMsgpackComma Operator
|
||||
hi def link ShaDaMsgpackColon ShaDaMsgpackComma
|
||||
|
||||
hi def link ShaDaMsgpackMultilineArray Operator
|
||||
|
||||
let b:current_syntax = "shada"
|
41
test/functional/plugin/helpers.lua
Normal file
41
test/functional/plugin/helpers.lua
Normal file
@ -0,0 +1,41 @@
|
||||
local paths = require('test.config.paths')
|
||||
|
||||
local helpers = require('test.functional.helpers')
|
||||
local spawn, set_session, nvim_prog, merge_args =
|
||||
helpers.spawn, helpers.set_session, helpers.nvim_prog, helpers.merge_args
|
||||
|
||||
local additional_cmd = ''
|
||||
|
||||
local function nvim_argv(shada_file)
|
||||
local rtp_value = ('\'%s/runtime\''):format(
|
||||
paths.test_source_path:gsub('\'', '\'\''))
|
||||
local nvim_argv = {nvim_prog, '-u', 'NORC', '-i', shada_file or 'NONE', '-N',
|
||||
'--cmd', 'set shortmess+=I background=light noswapfile',
|
||||
'--cmd', 'let &runtimepath=' .. rtp_value,
|
||||
'--cmd', additional_cmd,
|
||||
'--embed'}
|
||||
if helpers.prepend_argv then
|
||||
return merge_args(helpers.prepend_argv, nvim_argv)
|
||||
else
|
||||
return nvim_argv
|
||||
end
|
||||
end
|
||||
|
||||
local session = nil
|
||||
|
||||
local reset = function(...)
|
||||
if session then
|
||||
session:exit(0)
|
||||
end
|
||||
session = spawn(nvim_argv(...))
|
||||
set_session(session)
|
||||
end
|
||||
|
||||
local set_additional_cmd = function(s)
|
||||
additional_cmd = s
|
||||
end
|
||||
|
||||
return {
|
||||
reset=reset,
|
||||
set_additional_cmd=set_additional_cmd,
|
||||
}
|
699
test/functional/plugin/msgpack_spec.lua
Normal file
699
test/functional/plugin/msgpack_spec.lua
Normal file
@ -0,0 +1,699 @@
|
||||
local helpers = require('test.functional.helpers')
|
||||
local eq, nvim_eval, nvim_command, exc_exec =
|
||||
helpers.eq, helpers.eval, helpers.command, helpers.exc_exec
|
||||
|
||||
local plugin_helpers = require('test.functional.plugin.helpers')
|
||||
local reset = plugin_helpers.reset
|
||||
|
||||
describe('In autoload/msgpack.vim', function()
|
||||
before_each(reset)
|
||||
|
||||
local sp = function(typ, val)
|
||||
return ('{"_TYPE": v:msgpack_types.%s, "_VAL": %s}'):format(typ, val)
|
||||
end
|
||||
local mapsp = function(...)
|
||||
local val = ''
|
||||
for i=1,(select('#', ...)/2) do
|
||||
val = ('%s[%s,%s],'):format(val, select(i * 2 - 1, ...),
|
||||
select(i * 2, ...))
|
||||
end
|
||||
return sp('map', '[' .. val .. ']')
|
||||
end
|
||||
|
||||
local nan = -(1.0/0.0-1.0/0.0)
|
||||
local minus_nan = 1.0/0.0-1.0/0.0
|
||||
local inf = 1.0/0.0
|
||||
local minus_inf = -(1.0/0.0)
|
||||
local has_minus_nan = tostring(nan) ~= tostring(minus_nan)
|
||||
|
||||
describe('function msgpack#equal', function()
|
||||
local msgpack_eq = function(expected, a, b)
|
||||
eq(expected, nvim_eval(('msgpack#equal(%s, %s)'):format(a, b)))
|
||||
if a ~= b then
|
||||
eq(expected, nvim_eval(('msgpack#equal(%s, %s)'):format(b, a)))
|
||||
end
|
||||
end
|
||||
it('compares raw integers correctly', function()
|
||||
msgpack_eq(1, '1', '1')
|
||||
msgpack_eq(0, '1', '0')
|
||||
end)
|
||||
it('compares integer specials correctly', function()
|
||||
msgpack_eq(1, sp('integer', '[-1, 1, 0, 0]'),
|
||||
sp('integer', '[-1, 1, 0, 0]'))
|
||||
msgpack_eq(0, sp('integer', '[-1, 1, 0, 0]'),
|
||||
sp('integer', '[ 1, 1, 0, 0]'))
|
||||
end)
|
||||
it('compares integer specials with raw integer correctly', function()
|
||||
msgpack_eq(1, sp('integer', '[-1, 0, 0, 1]'), '-1')
|
||||
msgpack_eq(0, sp('integer', '[-1, 0, 0, 1]'), '1')
|
||||
msgpack_eq(0, sp('integer', '[ 1, 0, 0, 1]'), '-1')
|
||||
msgpack_eq(1, sp('integer', '[ 1, 0, 0, 1]'), '1')
|
||||
end)
|
||||
it('compares integer with float correctly', function()
|
||||
msgpack_eq(0, '0', '0.0')
|
||||
end)
|
||||
it('compares raw binaries correctly', function()
|
||||
msgpack_eq(1, '"abc\\ndef"', '"abc\\ndef"')
|
||||
msgpack_eq(0, '"abc\\ndef"', '"abc\\nghi"')
|
||||
end)
|
||||
it('compares binary specials correctly', function()
|
||||
msgpack_eq(1, sp('binary', '["abc\\n", "def"]'),
|
||||
sp('binary', '["abc\\n", "def"]'))
|
||||
msgpack_eq(0, sp('binary', '["abc", "def"]'),
|
||||
sp('binary', '["abc\\n", "def"]'))
|
||||
end)
|
||||
it('compares binary specials with raw binaries correctly', function()
|
||||
msgpack_eq(1, sp('binary', '["abc", "def"]'), '"abc\\ndef"')
|
||||
msgpack_eq(0, sp('binary', '["abc", "def"]'), '"abcdef"')
|
||||
end)
|
||||
it('compares string specials correctly', function()
|
||||
msgpack_eq(1, sp('string', '["abc\\n", "def"]'),
|
||||
sp('string', '["abc\\n", "def"]'))
|
||||
msgpack_eq(0, sp('string', '["abc", "def"]'),
|
||||
sp('string', '["abc\\n", "def"]'))
|
||||
end)
|
||||
it('compares string specials with binary correctly', function()
|
||||
msgpack_eq(0, sp('string', '["abc\\n", "def"]'),
|
||||
sp('binary', '["abc\\n", "def"]'))
|
||||
msgpack_eq(0, sp('string', '["abc", "def"]'), '"abc\\ndef"')
|
||||
msgpack_eq(0, sp('binary', '["abc\\n", "def"]'),
|
||||
sp('string', '["abc\\n", "def"]'))
|
||||
msgpack_eq(0, '"abc\\ndef"', sp('string', '["abc", "def"]'))
|
||||
end)
|
||||
it('compares ext specials correctly', function()
|
||||
msgpack_eq(1, sp('ext', '[1, ["", "ac"]]'), sp('ext', '[1, ["", "ac"]]'))
|
||||
msgpack_eq(0, sp('ext', '[2, ["", "ac"]]'), sp('ext', '[1, ["", "ac"]]'))
|
||||
msgpack_eq(0, sp('ext', '[1, ["", "ac"]]'), sp('ext', '[1, ["", "abc"]]'))
|
||||
end)
|
||||
it('compares raw maps correctly', function()
|
||||
msgpack_eq(1, '{"a": 1, "b": 2}', '{"b": 2, "a": 1}')
|
||||
msgpack_eq(1, '{}', '{}')
|
||||
msgpack_eq(0, '{}', '{"a": 1}')
|
||||
msgpack_eq(0, '{"a": 2}', '{"a": 1}')
|
||||
msgpack_eq(0, '{"a": 1}', '{"b": 1}')
|
||||
msgpack_eq(0, '{"a": 1}', '{"a": 1, "b": 1}')
|
||||
msgpack_eq(0, '{"a": 1, "b": 1}', '{"b": 1}')
|
||||
end)
|
||||
it('compares map specials correctly', function()
|
||||
msgpack_eq(1, mapsp(), mapsp())
|
||||
msgpack_eq(1, mapsp(sp('binary', '[""]'), '""'),
|
||||
mapsp(sp('binary', '[""]'), '""'))
|
||||
msgpack_eq(1, mapsp(mapsp('1', '1'), mapsp('1', '1')),
|
||||
mapsp(mapsp('1', '1'), mapsp('1', '1')))
|
||||
msgpack_eq(0, mapsp(), mapsp('1', '1'))
|
||||
msgpack_eq(0, mapsp(sp('binary', '["a"]'), '""'),
|
||||
mapsp(sp('binary', '[""]'), '""'))
|
||||
msgpack_eq(0, mapsp(sp('binary', '[""]'), '"a"'),
|
||||
mapsp(sp('binary', '[""]'), '""'))
|
||||
msgpack_eq(0, mapsp(sp('binary', '["a"]'), '"a"'),
|
||||
mapsp(sp('binary', '[""]'), '""'))
|
||||
msgpack_eq(0, mapsp(mapsp('1', '1'), mapsp('1', '1')),
|
||||
mapsp(sp('binary', '[""]'), mapsp('1', '1')))
|
||||
msgpack_eq(0, mapsp(mapsp('1', '1'), mapsp('1', '1')),
|
||||
mapsp(mapsp('2', '1'), mapsp('1', '1')))
|
||||
msgpack_eq(0, mapsp(mapsp('1', '1'), mapsp('1', '1')),
|
||||
mapsp(mapsp('1', '2'), mapsp('1', '1')))
|
||||
msgpack_eq(0, mapsp(mapsp('1', '1'), mapsp('1', '1')),
|
||||
mapsp(mapsp('1', '1'), mapsp('2', '1')))
|
||||
msgpack_eq(0, mapsp(mapsp('1', '1'), mapsp('1', '1')),
|
||||
mapsp(mapsp('1', '1'), mapsp('1', '2')))
|
||||
msgpack_eq(1, mapsp(mapsp('2', '1'), mapsp('1', '1'),
|
||||
mapsp('1', '1'), mapsp('1', '1')),
|
||||
mapsp(mapsp('1', '1'), mapsp('1', '1'),
|
||||
mapsp('2', '1'), mapsp('1', '1')))
|
||||
end)
|
||||
it('compares map specials with raw maps correctly', function()
|
||||
msgpack_eq(1, mapsp(), '{}')
|
||||
msgpack_eq(1, mapsp(sp('string', '["1"]'), '1'), '{"1": 1}')
|
||||
msgpack_eq(1, mapsp(sp('string', '["1"]'), sp('integer', '[1, 0, 0, 1]')),
|
||||
'{"1": 1}')
|
||||
msgpack_eq(0, mapsp(sp('integer', '[1, 0, 0, 1]'), sp('string', '["1"]')),
|
||||
'{1: "1"}')
|
||||
msgpack_eq(0, mapsp('"1"', sp('integer', '[1, 0, 0, 1]')),
|
||||
'{"1": 1}')
|
||||
msgpack_eq(0,
|
||||
mapsp(sp('string', '["1"]'), '1', sp('string', '["2"]'), '2'),
|
||||
'{"1": 1}')
|
||||
msgpack_eq(0, mapsp(sp('string', '["1"]'), '1'), '{"1": 1, "2": 2}')
|
||||
end)
|
||||
it('compares raw arrays correctly', function()
|
||||
msgpack_eq(1, '[]', '[]')
|
||||
msgpack_eq(0, '[]', '[1]')
|
||||
msgpack_eq(1, '[1]', '[1]')
|
||||
msgpack_eq(1, '[[[1]]]', '[[[1]]]')
|
||||
msgpack_eq(0, '[[[2]]]', '[[[1]]]')
|
||||
end)
|
||||
it('compares array specials correctly', function()
|
||||
msgpack_eq(1, sp('array', '[]'), sp('array', '[]'))
|
||||
msgpack_eq(0, sp('array', '[]'), sp('array', '[1]'))
|
||||
msgpack_eq(1, sp('array', '[1]'), sp('array', '[1]'))
|
||||
msgpack_eq(1, sp('array', '[[[1]]]'), sp('array', '[[[1]]]'))
|
||||
msgpack_eq(0, sp('array', '[[[1]]]'), sp('array', '[[[2]]]'))
|
||||
end)
|
||||
it('compares array specials with raw arrays correctly', function()
|
||||
msgpack_eq(1, sp('array', '[]'), '[]')
|
||||
msgpack_eq(0, sp('array', '[]'), '[1]')
|
||||
msgpack_eq(1, sp('array', '[1]'), '[1]')
|
||||
msgpack_eq(1, sp('array', '[[[1]]]'), '[[[1]]]')
|
||||
msgpack_eq(0, sp('array', '[[[1]]]'), '[[[2]]]')
|
||||
end)
|
||||
it('compares raw floats correctly', function()
|
||||
msgpack_eq(1, '0.0', '0.0')
|
||||
msgpack_eq(1, '(1.0/0.0-1.0/0.0)', '(1.0/0.0-1.0/0.0)')
|
||||
if has_minus_nan then
|
||||
msgpack_eq(0, '(1.0/0.0-1.0/0.0)', '-(1.0/0.0-1.0/0.0)')
|
||||
end
|
||||
msgpack_eq(1, '-(1.0/0.0-1.0/0.0)', '-(1.0/0.0-1.0/0.0)')
|
||||
msgpack_eq(1, '1.0/0.0', '1.0/0.0')
|
||||
msgpack_eq(1, '-(1.0/0.0)', '-(1.0/0.0)')
|
||||
msgpack_eq(1, '0.0', '0.0')
|
||||
msgpack_eq(0, '0.0', '1.0')
|
||||
msgpack_eq(0, '0.0', '(1.0/0.0-1.0/0.0)')
|
||||
msgpack_eq(0, '0.0', '1.0/0.0')
|
||||
msgpack_eq(0, '0.0', '-(1.0/0.0)')
|
||||
msgpack_eq(0, '1.0/0.0', '-(1.0/0.0)')
|
||||
msgpack_eq(0, '(1.0/0.0-1.0/0.0)', '-(1.0/0.0)')
|
||||
msgpack_eq(0, '(1.0/0.0-1.0/0.0)', '1.0/0.0')
|
||||
end)
|
||||
it('compares float specials with raw floats correctly', function()
|
||||
msgpack_eq(1, sp('float', '0.0'), '0.0')
|
||||
msgpack_eq(1, sp('float', '(1.0/0.0-1.0/0.0)'), '(1.0/0.0-1.0/0.0)')
|
||||
if has_minus_nan then
|
||||
msgpack_eq(0, sp('float', '(1.0/0.0-1.0/0.0)'), '-(1.0/0.0-1.0/0.0)')
|
||||
msgpack_eq(0, sp('float', '-(1.0/0.0-1.0/0.0)'), '(1.0/0.0-1.0/0.0)')
|
||||
end
|
||||
msgpack_eq(1, sp('float', '-(1.0/0.0-1.0/0.0)'), '-(1.0/0.0-1.0/0.0)')
|
||||
msgpack_eq(1, sp('float', '1.0/0.0'), '1.0/0.0')
|
||||
msgpack_eq(1, sp('float', '-(1.0/0.0)'), '-(1.0/0.0)')
|
||||
msgpack_eq(1, sp('float', '0.0'), '0.0')
|
||||
msgpack_eq(0, sp('float', '0.0'), '1.0')
|
||||
msgpack_eq(0, sp('float', '0.0'), '(1.0/0.0-1.0/0.0)')
|
||||
msgpack_eq(0, sp('float', '0.0'), '1.0/0.0')
|
||||
msgpack_eq(0, sp('float', '0.0'), '-(1.0/0.0)')
|
||||
msgpack_eq(0, sp('float', '1.0/0.0'), '-(1.0/0.0)')
|
||||
msgpack_eq(0, sp('float', '(1.0/0.0-1.0/0.0)'), '-(1.0/0.0)')
|
||||
msgpack_eq(0, sp('float', '(1.0/0.0-1.0/0.0)'), '1.0/0.0')
|
||||
end)
|
||||
it('compares float specials correctly', function()
|
||||
msgpack_eq(1, sp('float', '0.0'), sp('float', '0.0'))
|
||||
msgpack_eq(1, sp('float', '(1.0/0.0-1.0/0.0)'),
|
||||
sp('float', '(1.0/0.0-1.0/0.0)'))
|
||||
msgpack_eq(1, sp('float', '1.0/0.0'), sp('float', '1.0/0.0'))
|
||||
msgpack_eq(1, sp('float', '-(1.0/0.0)'), sp('float', '-(1.0/0.0)'))
|
||||
msgpack_eq(1, sp('float', '0.0'), sp('float', '0.0'))
|
||||
msgpack_eq(0, sp('float', '0.0'), sp('float', '1.0'))
|
||||
msgpack_eq(0, sp('float', '0.0'), sp('float', '(1.0/0.0-1.0/0.0)'))
|
||||
msgpack_eq(0, sp('float', '0.0'), sp('float', '1.0/0.0'))
|
||||
msgpack_eq(0, sp('float', '0.0'), sp('float', '-(1.0/0.0)'))
|
||||
msgpack_eq(0, sp('float', '1.0/0.0'), sp('float', '-(1.0/0.0)'))
|
||||
msgpack_eq(0, sp('float', '(1.0/0.0-1.0/0.0)'), sp('float', '-(1.0/0.0)'))
|
||||
if has_minus_nan then
|
||||
msgpack_eq(0, sp('float', '(1.0/0.0-1.0/0.0)'),
|
||||
sp('float', '-(1.0/0.0-1.0/0.0)'))
|
||||
end
|
||||
msgpack_eq(1, sp('float', '-(1.0/0.0-1.0/0.0)'),
|
||||
sp('float', '-(1.0/0.0-1.0/0.0)'))
|
||||
msgpack_eq(0, sp('float', '(1.0/0.0-1.0/0.0)'), sp('float', '1.0/0.0'))
|
||||
end)
|
||||
it('compares boolean specials correctly', function()
|
||||
msgpack_eq(1, sp('boolean', '1'), sp('boolean', '1'))
|
||||
msgpack_eq(0, sp('boolean', '1'), sp('boolean', '0'))
|
||||
end)
|
||||
it('compares nil specials correctly', function()
|
||||
msgpack_eq(1, sp('nil', '1'), sp('nil', '0'))
|
||||
end)
|
||||
it('compares nil, boolean and integer values with each other correctly',
|
||||
function()
|
||||
msgpack_eq(0, sp('boolean', '1'), '1')
|
||||
msgpack_eq(0, sp('boolean', '1'), sp('nil', '0'))
|
||||
msgpack_eq(0, sp('boolean', '1'), sp('nil', '1'))
|
||||
msgpack_eq(0, sp('boolean', '0'), sp('nil', '0'))
|
||||
msgpack_eq(0, sp('boolean', '0'), '0')
|
||||
msgpack_eq(0, sp('boolean', '0'), sp('integer', '[1, 0, 0, 0]'))
|
||||
msgpack_eq(0, sp('boolean', '0'), sp('integer', '[1, 0, 0, 1]'))
|
||||
msgpack_eq(0, sp('boolean', '1'), sp('integer', '[1, 0, 0, 1]'))
|
||||
msgpack_eq(0, sp('nil', '0'), sp('integer', '[1, 0, 0, 0]'))
|
||||
msgpack_eq(0, sp('nil', '0'), '0')
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('function msgpack#is_int', function()
|
||||
it('works', function()
|
||||
eq(1, nvim_eval('msgpack#is_int(1)'))
|
||||
eq(1, nvim_eval('msgpack#is_int(-1)'))
|
||||
eq(1, nvim_eval(('msgpack#is_int(%s)'):format(
|
||||
sp('integer', '[1, 0, 0, 1]'))))
|
||||
eq(1, nvim_eval(('msgpack#is_int(%s)'):format(
|
||||
sp('integer', '[-1, 0, 0, 1]'))))
|
||||
eq(0, nvim_eval(('msgpack#is_int(%s)'):format(
|
||||
sp('float', '0.0'))))
|
||||
eq(0, nvim_eval(('msgpack#is_int(%s)'):format(
|
||||
sp('boolean', '0'))))
|
||||
eq(0, nvim_eval(('msgpack#is_int(%s)'):format(
|
||||
sp('nil', '0'))))
|
||||
eq(0, nvim_eval('msgpack#is_int("")'))
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('function msgpack#is_uint', function()
|
||||
it('works', function()
|
||||
eq(1, nvim_eval('msgpack#is_uint(1)'))
|
||||
eq(0, nvim_eval('msgpack#is_uint(-1)'))
|
||||
eq(1, nvim_eval(('msgpack#is_uint(%s)'):format(
|
||||
sp('integer', '[1, 0, 0, 1]'))))
|
||||
eq(0, nvim_eval(('msgpack#is_uint(%s)'):format(
|
||||
sp('integer', '[-1, 0, 0, 1]'))))
|
||||
eq(0, nvim_eval(('msgpack#is_uint(%s)'):format(
|
||||
sp('float', '0.0'))))
|
||||
eq(0, nvim_eval(('msgpack#is_uint(%s)'):format(
|
||||
sp('boolean', '0'))))
|
||||
eq(0, nvim_eval(('msgpack#is_uint(%s)'):format(
|
||||
sp('nil', '0'))))
|
||||
eq(0, nvim_eval('msgpack#is_uint("")'))
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('function msgpack#strftime', function()
|
||||
it('works', function()
|
||||
local epoch = os.date('%Y-%m-%dT%H:%M:%S', 0)
|
||||
eq(epoch, nvim_eval('msgpack#strftime("%Y-%m-%dT%H:%M:%S", 0)'))
|
||||
eq(epoch, nvim_eval(
|
||||
('msgpack#strftime("%%Y-%%m-%%dT%%H:%%M:%%S", %s)'):format(sp(
|
||||
'integer', '[1, 0, 0, 0]'))))
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('function msgpack#strptime', function()
|
||||
it('works', function()
|
||||
for _, v in ipairs({0, 10, 100000, 204, 1000000000}) do
|
||||
local time = os.date('%Y-%m-%dT%H:%M:%S', v)
|
||||
eq(v, nvim_eval('msgpack#strptime("%Y-%m-%dT%H:%M:%S", '
|
||||
.. '"' .. time .. '")'))
|
||||
end
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('function msgpack#type', function()
|
||||
local type_eq = function(expected, val)
|
||||
eq(expected, nvim_eval(('msgpack#type(%s)'):format(val)))
|
||||
end
|
||||
|
||||
it('works for special dictionaries', function()
|
||||
type_eq('string', sp('string', '[""]'))
|
||||
type_eq('binary', sp('binary', '[""]'))
|
||||
type_eq('ext', sp('ext', '[1, [""]]'))
|
||||
type_eq('array', sp('array', '[]'))
|
||||
type_eq('map', sp('map', '[]'))
|
||||
type_eq('integer', sp('integer', '[1, 0, 0, 0]'))
|
||||
type_eq('float', sp('float', '0.0'))
|
||||
type_eq('boolean', sp('boolean', '0'))
|
||||
type_eq('nil', sp('nil', '0'))
|
||||
end)
|
||||
|
||||
it('works for regular values', function()
|
||||
type_eq('binary', '""')
|
||||
type_eq('array', '[]')
|
||||
type_eq('map', '{}')
|
||||
type_eq('integer', '1')
|
||||
type_eq('float', '0.0')
|
||||
type_eq('float', '(1.0/0.0)')
|
||||
type_eq('float', '-(1.0/0.0)')
|
||||
type_eq('float', '(1.0/0.0-1.0/0.0)')
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('function msgpack#special_type', function()
|
||||
local sp_type_eq = function(expected, val)
|
||||
eq(expected, nvim_eval(('msgpack#special_type(%s)'):format(val)))
|
||||
end
|
||||
|
||||
it('works for special dictionaries', function()
|
||||
sp_type_eq('string', sp('string', '[""]'))
|
||||
sp_type_eq('binary', sp('binary', '[""]'))
|
||||
sp_type_eq('ext', sp('ext', '[1, [""]]'))
|
||||
sp_type_eq('array', sp('array', '[]'))
|
||||
sp_type_eq('map', sp('map', '[]'))
|
||||
sp_type_eq('integer', sp('integer', '[1, 0, 0, 0]'))
|
||||
sp_type_eq('float', sp('float', '0.0'))
|
||||
sp_type_eq('boolean', sp('boolean', '0'))
|
||||
sp_type_eq('nil', sp('nil', '0'))
|
||||
end)
|
||||
|
||||
it('works for regular values', function()
|
||||
sp_type_eq(0, '""')
|
||||
sp_type_eq(0, '[]')
|
||||
sp_type_eq(0, '{}')
|
||||
sp_type_eq(0, '1')
|
||||
sp_type_eq(0, '0.0')
|
||||
sp_type_eq(0, '(1.0/0.0)')
|
||||
sp_type_eq(0, '-(1.0/0.0)')
|
||||
sp_type_eq(0, '(1.0/0.0-1.0/0.0)')
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('function msgpack#string', function()
|
||||
local string_eq = function(expected, val)
|
||||
eq(expected, nvim_eval(('msgpack#string(%s)'):format(val)))
|
||||
end
|
||||
|
||||
it('works for special dictionaries', function()
|
||||
string_eq('=""', sp('string', '[""]'))
|
||||
string_eq('="\\n"', sp('string', '["", ""]'))
|
||||
string_eq('="ab\\0c\\nde"', sp('string', '["ab\\nc", "de"]'))
|
||||
string_eq('""', sp('binary', '[""]'))
|
||||
string_eq('"\\n"', sp('binary', '["", ""]'))
|
||||
string_eq('"ab\\0c\\nde"', sp('binary', '["ab\\nc", "de"]'))
|
||||
string_eq('+(2)""', sp('ext', '[2, [""]]'))
|
||||
string_eq('+(2)"\\n"', sp('ext', '[2, ["", ""]]'))
|
||||
string_eq('+(2)"ab\\0c\\nde"', sp('ext', '[2, ["ab\\nc", "de"]]'))
|
||||
string_eq('[]', sp('array', '[]'))
|
||||
string_eq('[[[[{}]]]]', sp('array', '[[[[{}]]]]'))
|
||||
string_eq('{}', sp('map', '[]'))
|
||||
string_eq('{2: 10}', sp('map', '[[2, 10]]'))
|
||||
string_eq('{{1: 1}: {1: 1}, {2: 1}: {1: 1}}',
|
||||
mapsp(mapsp('2', '1'), mapsp('1', '1'),
|
||||
mapsp('1', '1'), mapsp('1', '1')))
|
||||
string_eq('{{1: 1}: {1: 1}, {2: 1}: {1: 1}}',
|
||||
mapsp(mapsp('1', '1'), mapsp('1', '1'),
|
||||
mapsp('2', '1'), mapsp('1', '1')))
|
||||
string_eq('{[1, 2, {{1: 2}: 1}]: [1, 2, {{1: 2}: 1}]}',
|
||||
mapsp(('[1, 2, %s]'):format(mapsp(mapsp('1', '2'), '1')),
|
||||
('[1, 2, %s]'):format(mapsp(mapsp('1', '2'), '1'))))
|
||||
string_eq('0x0000000000000000', sp('integer', '[1, 0, 0, 0]'))
|
||||
string_eq('-0x0000000100000000', sp('integer', '[-1, 0, 2, 0]'))
|
||||
string_eq('0x123456789abcdef0',
|
||||
sp('integer', '[ 1, 0, 610839793, 448585456]'))
|
||||
string_eq('-0x123456789abcdef0',
|
||||
sp('integer', '[-1, 0, 610839793, 448585456]'))
|
||||
string_eq('0xf23456789abcdef0',
|
||||
sp('integer', '[ 1, 3, 1684581617, 448585456]'))
|
||||
string_eq('-0x723456789abcdef0',
|
||||
sp('integer', '[-1, 1, 1684581617, 448585456]'))
|
||||
string_eq('0.0', sp('float', '0.0'))
|
||||
string_eq('inf', sp('float', '(1.0/0.0)'))
|
||||
string_eq('-inf', sp('float', '-(1.0/0.0)'))
|
||||
if has_minus_nan then
|
||||
string_eq('-nan', sp('float', '(1.0/0.0-1.0/0.0)'))
|
||||
end
|
||||
string_eq('nan', sp('float', '-(1.0/0.0-1.0/0.0)'))
|
||||
string_eq('FALSE', sp('boolean', '0'))
|
||||
string_eq('TRUE', sp('boolean', '1'))
|
||||
string_eq('NIL', sp('nil', '0'))
|
||||
end)
|
||||
|
||||
it('works for regular values', function()
|
||||
string_eq('""', '""')
|
||||
string_eq('"\\n"', '"\\n"')
|
||||
string_eq('[]', '[]')
|
||||
string_eq('[[[{}]]]', '[[[{}]]]')
|
||||
string_eq('{}', '{}')
|
||||
string_eq('{="2": 10}', '{2: 10}')
|
||||
string_eq('{="2": [{}]}', '{2: [{}]}')
|
||||
string_eq('1', '1')
|
||||
string_eq('0.0', '0.0')
|
||||
string_eq('inf', '(1.0/0.0)')
|
||||
string_eq('-inf', '-(1.0/0.0)')
|
||||
if has_minus_nan then
|
||||
string_eq('-nan', '(1.0/0.0-1.0/0.0)')
|
||||
end
|
||||
string_eq('nan', '-(1.0/0.0-1.0/0.0)')
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('function msgpack#deepcopy', function()
|
||||
it('works for special dictionaries', function()
|
||||
nvim_command('let sparr = ' .. sp('array', '[[[]]]'))
|
||||
nvim_command('let spmap = ' .. mapsp('"abc"', '[[]]'))
|
||||
nvim_command('let spint = ' .. sp('integer', '[1, 0, 0, 0]'))
|
||||
nvim_command('let spflt = ' .. sp('float', '1.0'))
|
||||
nvim_command('let spext = ' .. sp('ext', '[2, ["abc", "def"]]'))
|
||||
nvim_command('let spstr = ' .. sp('string', '["abc", "def"]'))
|
||||
nvim_command('let spbin = ' .. sp('binary', '["abc", "def"]'))
|
||||
nvim_command('let spbln = ' .. sp('boolean', '0'))
|
||||
nvim_command('let spnil = ' .. sp('nil', '0'))
|
||||
|
||||
nvim_command('let sparr2 = msgpack#deepcopy(sparr)')
|
||||
nvim_command('let spmap2 = msgpack#deepcopy(spmap)')
|
||||
nvim_command('let spint2 = msgpack#deepcopy(spint)')
|
||||
nvim_command('let spflt2 = msgpack#deepcopy(spflt)')
|
||||
nvim_command('let spext2 = msgpack#deepcopy(spext)')
|
||||
nvim_command('let spstr2 = msgpack#deepcopy(spstr)')
|
||||
nvim_command('let spbin2 = msgpack#deepcopy(spbin)')
|
||||
nvim_command('let spbln2 = msgpack#deepcopy(spbln)')
|
||||
nvim_command('let spnil2 = msgpack#deepcopy(spnil)')
|
||||
|
||||
eq('array', nvim_eval('msgpack#type(sparr2)'))
|
||||
eq('map', nvim_eval('msgpack#type(spmap2)'))
|
||||
eq('integer', nvim_eval('msgpack#type(spint2)'))
|
||||
eq('float', nvim_eval('msgpack#type(spflt2)'))
|
||||
eq('ext', nvim_eval('msgpack#type(spext2)'))
|
||||
eq('string', nvim_eval('msgpack#type(spstr2)'))
|
||||
eq('binary', nvim_eval('msgpack#type(spbin2)'))
|
||||
eq('boolean', nvim_eval('msgpack#type(spbln2)'))
|
||||
eq('nil', nvim_eval('msgpack#type(spnil2)'))
|
||||
|
||||
nvim_command('call add(sparr._VAL, 0)')
|
||||
nvim_command('call add(sparr._VAL[0], 0)')
|
||||
nvim_command('call add(sparr._VAL[0][0], 0)')
|
||||
nvim_command('call add(spmap._VAL, [0, 0])')
|
||||
nvim_command('call add(spmap._VAL[0][1], 0)')
|
||||
nvim_command('call add(spmap._VAL[0][1][0], 0)')
|
||||
nvim_command('let spint._VAL[1] = 1')
|
||||
nvim_command('let spflt._VAL = 0.0')
|
||||
nvim_command('let spext._VAL[0] = 3')
|
||||
nvim_command('let spext._VAL[1][0] = "gh"')
|
||||
nvim_command('let spstr._VAL[0] = "gh"')
|
||||
nvim_command('let spbin._VAL[0] = "gh"')
|
||||
nvim_command('let spbln._VAL = 1')
|
||||
nvim_command('let spnil._VAL = 1')
|
||||
|
||||
eq({_TYPE={}, _VAL={{{}}}}, nvim_eval('sparr2'))
|
||||
eq({_TYPE={}, _VAL={{'abc', {{}}}}}, nvim_eval('spmap2'))
|
||||
eq({_TYPE={}, _VAL={1, 0, 0, 0}}, nvim_eval('spint2'))
|
||||
eq({_TYPE={}, _VAL=1.0}, nvim_eval('spflt2'))
|
||||
eq({_TYPE={}, _VAL={2, {'abc', 'def'}}}, nvim_eval('spext2'))
|
||||
eq({_TYPE={}, _VAL={'abc', 'def'}}, nvim_eval('spstr2'))
|
||||
eq({_TYPE={}, _VAL={'abc', 'def'}}, nvim_eval('spbin2'))
|
||||
eq({_TYPE={}, _VAL=0}, nvim_eval('spbln2'))
|
||||
eq({_TYPE={}, _VAL=0}, nvim_eval('spnil2'))
|
||||
|
||||
nvim_command('let sparr._TYPE = []')
|
||||
nvim_command('let spmap._TYPE = []')
|
||||
nvim_command('let spint._TYPE = []')
|
||||
nvim_command('let spflt._TYPE = []')
|
||||
nvim_command('let spext._TYPE = []')
|
||||
nvim_command('let spstr._TYPE = []')
|
||||
nvim_command('let spbin._TYPE = []')
|
||||
nvim_command('let spbln._TYPE = []')
|
||||
nvim_command('let spnil._TYPE = []')
|
||||
|
||||
eq('array', nvim_eval('msgpack#special_type(sparr2)'))
|
||||
eq('map', nvim_eval('msgpack#special_type(spmap2)'))
|
||||
eq('integer', nvim_eval('msgpack#special_type(spint2)'))
|
||||
eq('float', nvim_eval('msgpack#special_type(spflt2)'))
|
||||
eq('ext', nvim_eval('msgpack#special_type(spext2)'))
|
||||
eq('string', nvim_eval('msgpack#special_type(spstr2)'))
|
||||
eq('binary', nvim_eval('msgpack#special_type(spbin2)'))
|
||||
eq('boolean', nvim_eval('msgpack#special_type(spbln2)'))
|
||||
eq('nil', nvim_eval('msgpack#special_type(spnil2)'))
|
||||
end)
|
||||
|
||||
it('works for regular values', function()
|
||||
nvim_command('let arr = [[[]]]')
|
||||
nvim_command('let map = {1: {}}')
|
||||
nvim_command('let int = 1')
|
||||
nvim_command('let flt = 2.0')
|
||||
nvim_command('let bin = "abc"')
|
||||
|
||||
nvim_command('let arr2 = msgpack#deepcopy(arr)')
|
||||
nvim_command('let map2 = msgpack#deepcopy(map)')
|
||||
nvim_command('let int2 = msgpack#deepcopy(int)')
|
||||
nvim_command('let flt2 = msgpack#deepcopy(flt)')
|
||||
nvim_command('let bin2 = msgpack#deepcopy(bin)')
|
||||
|
||||
eq('array', nvim_eval('msgpack#type(arr2)'))
|
||||
eq('map', nvim_eval('msgpack#type(map2)'))
|
||||
eq('integer', nvim_eval('msgpack#type(int2)'))
|
||||
eq('float', nvim_eval('msgpack#type(flt2)'))
|
||||
eq('binary', nvim_eval('msgpack#type(bin2)'))
|
||||
|
||||
nvim_command('call add(arr, 0)')
|
||||
nvim_command('call add(arr[0], 0)')
|
||||
nvim_command('call add(arr[0][0], 0)')
|
||||
nvim_command('let map.a = 1')
|
||||
nvim_command('let map.1.a = 1')
|
||||
nvim_command('let int = 2')
|
||||
nvim_command('let flt = 3.0')
|
||||
nvim_command('let bin = ""')
|
||||
|
||||
eq({{{}}}, nvim_eval('arr2'))
|
||||
eq({['1']={}}, nvim_eval('map2'))
|
||||
eq(1, nvim_eval('int2'))
|
||||
eq(2.0, nvim_eval('flt2'))
|
||||
eq('abc', nvim_eval('bin2'))
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('function msgpack#eval', function()
|
||||
local eval_eq = function(expected_type, expected_val, str, ...)
|
||||
nvim_command(('let g:__val = msgpack#eval(\'%s\', %s)'):format(str:gsub(
|
||||
'\'', '\'\''), select(1, ...) or '{}'))
|
||||
eq(expected_type, nvim_eval('msgpack#type(g:__val)'))
|
||||
local expected_val_full = expected_val
|
||||
if (not (({float=true, integer=true})[expected_type]
|
||||
and type(expected_val) ~= 'table')
|
||||
and expected_type ~= 'array') then
|
||||
expected_val_full = {_TYPE={}, _VAL=expected_val_full}
|
||||
end
|
||||
if expected_val_full == expected_val_full then
|
||||
eq(expected_val_full, nvim_eval('g:__val'))
|
||||
else
|
||||
eq(tostring(expected_val_full), tostring(nvim_eval('g:__val')))
|
||||
end
|
||||
nvim_command('unlet g:__val')
|
||||
end
|
||||
|
||||
it('correctly loads binary strings', function()
|
||||
eval_eq('binary', {'abcdef'}, '"abcdef"')
|
||||
eval_eq('binary', {'abc', 'def'}, '"abc\\ndef"')
|
||||
eval_eq('binary', {'abc\ndef'}, '"abc\\0def"')
|
||||
eval_eq('binary', {'\nabc\ndef\n'}, '"\\0abc\\0def\\0"')
|
||||
eval_eq('binary', {'abc\n\n\ndef'}, '"abc\\0\\0\\0def"')
|
||||
eval_eq('binary', {'abc\n', '\ndef'}, '"abc\\0\\n\\0def"')
|
||||
eval_eq('binary', {'abc', '', '', 'def'}, '"abc\\n\\n\\ndef"')
|
||||
eval_eq('binary', {'abc', '', '', 'def', ''}, '"abc\\n\\n\\ndef\\n"')
|
||||
eval_eq('binary', {'', 'abc', '', '', 'def'}, '"\\nabc\\n\\n\\ndef"')
|
||||
eval_eq('binary', {''}, '""')
|
||||
eval_eq('binary', {'"'}, '"\\""')
|
||||
end)
|
||||
|
||||
it('correctly loads strings', function()
|
||||
eval_eq('string', {'abcdef'}, '="abcdef"')
|
||||
eval_eq('string', {'abc', 'def'}, '="abc\\ndef"')
|
||||
eval_eq('string', {'abc\ndef'}, '="abc\\0def"')
|
||||
eval_eq('string', {'\nabc\ndef\n'}, '="\\0abc\\0def\\0"')
|
||||
eval_eq('string', {'abc\n\n\ndef'}, '="abc\\0\\0\\0def"')
|
||||
eval_eq('string', {'abc\n', '\ndef'}, '="abc\\0\\n\\0def"')
|
||||
eval_eq('string', {'abc', '', '', 'def'}, '="abc\\n\\n\\ndef"')
|
||||
eval_eq('string', {'abc', '', '', 'def', ''}, '="abc\\n\\n\\ndef\\n"')
|
||||
eval_eq('string', {'', 'abc', '', '', 'def'}, '="\\nabc\\n\\n\\ndef"')
|
||||
eval_eq('string', {''}, '=""')
|
||||
eval_eq('string', {'"'}, '="\\""')
|
||||
end)
|
||||
|
||||
it('correctly loads ext values', function()
|
||||
eval_eq('ext', {0, {'abcdef'}}, '+(0)"abcdef"')
|
||||
eval_eq('ext', {0, {'abc', 'def'}}, '+(0)"abc\\ndef"')
|
||||
eval_eq('ext', {0, {'abc\ndef'}}, '+(0)"abc\\0def"')
|
||||
eval_eq('ext', {0, {'\nabc\ndef\n'}}, '+(0)"\\0abc\\0def\\0"')
|
||||
eval_eq('ext', {0, {'abc\n\n\ndef'}}, '+(0)"abc\\0\\0\\0def"')
|
||||
eval_eq('ext', {0, {'abc\n', '\ndef'}}, '+(0)"abc\\0\\n\\0def"')
|
||||
eval_eq('ext', {0, {'abc', '', '', 'def'}}, '+(0)"abc\\n\\n\\ndef"')
|
||||
eval_eq('ext', {0, {'abc', '', '', 'def', ''}},
|
||||
'+(0)"abc\\n\\n\\ndef\\n"')
|
||||
eval_eq('ext', {0, {'', 'abc', '', '', 'def'}},
|
||||
'+(0)"\\nabc\\n\\n\\ndef"')
|
||||
eval_eq('ext', {0, {''}}, '+(0)""')
|
||||
eval_eq('ext', {0, {'"'}}, '+(0)"\\""')
|
||||
|
||||
eval_eq('ext', {-1, {'abcdef'}}, '+(-1)"abcdef"')
|
||||
eval_eq('ext', {-1, {'abc', 'def'}}, '+(-1)"abc\\ndef"')
|
||||
eval_eq('ext', {-1, {'abc\ndef'}}, '+(-1)"abc\\0def"')
|
||||
eval_eq('ext', {-1, {'\nabc\ndef\n'}}, '+(-1)"\\0abc\\0def\\0"')
|
||||
eval_eq('ext', {-1, {'abc\n\n\ndef'}}, '+(-1)"abc\\0\\0\\0def"')
|
||||
eval_eq('ext', {-1, {'abc\n', '\ndef'}}, '+(-1)"abc\\0\\n\\0def"')
|
||||
eval_eq('ext', {-1, {'abc', '', '', 'def'}}, '+(-1)"abc\\n\\n\\ndef"')
|
||||
eval_eq('ext', {-1, {'abc', '', '', 'def', ''}},
|
||||
'+(-1)"abc\\n\\n\\ndef\\n"')
|
||||
eval_eq('ext', {-1, {'', 'abc', '', '', 'def'}},
|
||||
'+(-1)"\\nabc\\n\\n\\ndef"')
|
||||
eval_eq('ext', {-1, {''}}, '+(-1)""')
|
||||
eval_eq('ext', {-1, {'"'}}, '+(-1)"\\""')
|
||||
end)
|
||||
|
||||
it('correctly loads floats', function()
|
||||
eval_eq('float', inf, 'inf')
|
||||
eval_eq('float', minus_inf, '-inf')
|
||||
eval_eq('float', nan, 'nan')
|
||||
eval_eq('float', minus_nan, '-nan')
|
||||
eval_eq('float', 1.0e10, '1.0e10')
|
||||
eval_eq('float', 1.0e10, '1.0e+10')
|
||||
eval_eq('float', -1.0e10, '-1.0e+10')
|
||||
eval_eq('float', 1.0, '1.0')
|
||||
eval_eq('float', -1.0, '-1.0')
|
||||
eval_eq('float', 1.0e-10, '1.0e-10')
|
||||
eval_eq('float', -1.0e-10, '-1.0e-10')
|
||||
end)
|
||||
|
||||
it('correctly loads integers', function()
|
||||
eval_eq('integer', 10, '10')
|
||||
eval_eq('integer', -10, '-10')
|
||||
eval_eq('integer', { 1, 0, 610839793, 448585456}, ' 0x123456789ABCDEF0')
|
||||
eval_eq('integer', {-1, 0, 610839793, 448585456}, '-0x123456789ABCDEF0')
|
||||
eval_eq('integer', { 1, 3, 1684581617, 448585456}, ' 0xF23456789ABCDEF0')
|
||||
eval_eq('integer', {-1, 1, 1684581617, 448585456}, '-0x723456789ABCDEF0')
|
||||
eval_eq('integer', { 1, 0, 0, 0x100}, '0x100')
|
||||
eval_eq('integer', {-1, 0, 0, 0x100}, '-0x100')
|
||||
|
||||
eval_eq('integer', ('a'):byte(), '\'a\'')
|
||||
eval_eq('integer', 0xAB, '\'«\'')
|
||||
end)
|
||||
|
||||
it('correctly loads constants', function()
|
||||
eval_eq('boolean', 1, 'TRUE')
|
||||
eval_eq('boolean', 0, 'FALSE')
|
||||
eval_eq('nil', 0, 'NIL')
|
||||
eval_eq('nil', 0, 'NIL', '{"NIL": 1, "nan": 2, "T": 3}')
|
||||
eval_eq('float', nan, 'nan',
|
||||
'{"NIL": "1", "nan": "2", "T": "3"}')
|
||||
eval_eq('integer', 3, 'T', '{"NIL": "1", "nan": "2", "T": "3"}')
|
||||
eval_eq('integer', {1, 0, 0, 0}, 'T',
|
||||
('{"NIL": "1", "nan": "2", "T": \'%s\'}'):format(
|
||||
sp('integer', '[1, 0, 0, 0]')))
|
||||
end)
|
||||
|
||||
it('correctly loads maps', function()
|
||||
eval_eq('map', {}, '{}')
|
||||
eval_eq('map', {{{_TYPE={}, _VAL={{1, 2}}}, {_TYPE={}, _VAL={{3, 4}}}}},
|
||||
'{{1: 2}: {3: 4}}')
|
||||
eval_eq('map', {{{_TYPE={}, _VAL={{1, 2}}}, {_TYPE={}, _VAL={{3, 4}}}},
|
||||
{1, 2}},
|
||||
'{{1: 2}: {3: 4}, 1: 2}')
|
||||
end)
|
||||
|
||||
it('correctly loads arrays', function()
|
||||
eval_eq('array', {}, '[]')
|
||||
eval_eq('array', {1}, '[1]')
|
||||
eval_eq('array', {{_TYPE={}, _VAL=1}}, '[TRUE]')
|
||||
eval_eq('array', {{{_TYPE={}, _VAL={{1, 2}}}}, {_TYPE={}, _VAL={{3, 4}}}},
|
||||
'[[{1: 2}], {3: 4}]')
|
||||
end)
|
||||
|
||||
it('errors out when needed', function()
|
||||
eq('empty:Parsed string is empty',
|
||||
exc_exec('call msgpack#eval("", {})'))
|
||||
eq('unknown:Invalid non-space character: ^',
|
||||
exc_exec('call msgpack#eval("^", {})'))
|
||||
eq('char-invalid:Invalid integer character literal format: \'\'',
|
||||
exc_exec('call msgpack#eval("\'\'", {})'))
|
||||
eq('char-invalid:Invalid integer character literal format: \'ab\'',
|
||||
exc_exec('call msgpack#eval("\'ab\'", {})'))
|
||||
eq('char-invalid:Invalid integer character literal format: \'',
|
||||
exc_exec('call msgpack#eval("\'", {})'))
|
||||
eq('"-invalid:Invalid string: "',
|
||||
exc_exec('call msgpack#eval("\\"", {})'))
|
||||
eq('"-invalid:Invalid string: ="',
|
||||
exc_exec('call msgpack#eval("=\\"", {})'))
|
||||
eq('"-invalid:Invalid string: +(0)"',
|
||||
exc_exec('call msgpack#eval("+(0)\\"", {})'))
|
||||
eq('0.-nodigits:Decimal dot must be followed by digit(s): .e1',
|
||||
exc_exec('call msgpack#eval("0.e1", {})'))
|
||||
eq('0x-long:Must have at most 16 hex digits: FEDCBA98765432100',
|
||||
exc_exec('call msgpack#eval("0xFEDCBA98765432100", {})'))
|
||||
eq('0x-empty:Must have number after 0x: ',
|
||||
exc_exec('call msgpack#eval("0x", {})'))
|
||||
eq('name-unknown:Unknown name FOO: FOO',
|
||||
exc_exec('call msgpack#eval("FOO", {})'))
|
||||
end)
|
||||
end)
|
||||
end)
|
2833
test/functional/plugin/shada_spec.lua
Normal file
2833
test/functional/plugin/shada_spec.lua
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user