123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701 |
- 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', 'sb', 'sm', 'sc', 'sl', 'se', 'so',
- \ 'su'],
- \'register': ['n', 'rc', 'rw', 'rt', 'ru'],
- \'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],
- \'sb': ['search backward', 'boolean', g:msgpack#false],
- \'rt': ['type', 'regtype', s:SHADA_ENUMS.regtype.CHARACTERWISE],
- \'rw': ['block width', 'uint', 0],
- \'rc': ['contents', 'binarray', s:SHADA_REQUIRED],
- \'ru': ['is_unnamed', 'boolean', g:msgpack#false],
- \'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# 'string'
- 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
- 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# "string"'))
- 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]
- " Restricting a:v to be <= 127 is not necessary, but intchar constants are
- " normally expected to be either ASCII printable characters or NUL.
- elseif a:type is# 'intchar' && type(a:v) == type(0) && a:v >= 0 && a:v <= 127
- if a:v > 0 && strtrans(nr2char(a:v)) is# nr2char(a:v)
- return "'" . nr2char(a:v) . "'"
- else
- return "'\\" . a:v . "'"
- endif
- 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 Vimscript 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
|