1if exists('g:loaded_shada_autoload') 2 finish 3endif 4let g:loaded_shada_autoload = 1 5 6"" 7" If true keep the old header entry when editing existing ShaDa file. 8" 9" Old header entry will be kept only if it is listed in the opened file. To 10" remove old header entry despite of the setting just remove it from the 11" listing. Setting it to false makes plugin ignore all header entries. Defaults 12" to 1. 13let g:shada#keep_old_header = get(g:, 'shada#keep_old_header', 1) 14 15"" 16" If true then first entry will be plugin’s own header entry. 17let g:shada#add_own_header = get(g:, 'shada#add_own_header', 1) 18 19"" 20" Dictionary that maps ShaDa types to their names. 21let s:SHADA_ENTRY_NAMES = { 22 \1: 'header', 23 \2: 'search_pattern', 24 \3: 'replacement_string', 25 \4: 'history_entry', 26 \5: 'register', 27 \6: 'variable', 28 \7: 'global_mark', 29 \8: 'jump', 30 \9: 'buffer_list', 31 \10: 'local_mark', 32 \11: 'change', 33\} 34 35"" 36" Dictionary that maps ShaDa names to corresponding types 37let s:SHADA_ENTRY_TYPES = {} 38call map(copy(s:SHADA_ENTRY_NAMES), 39 \'extend(s:SHADA_ENTRY_TYPES, {v:val : +v:key})') 40 41"" 42" Map that maps entry names to lists of keys that can be used by this entry. 43" Only contains data for entries which are represented as mappings, except for 44" the header. 45let s:SHADA_MAP_ENTRIES = { 46 \'search_pattern': ['sp', 'sh', 'ss', 'sb', 'sm', 'sc', 'sl', 'se', 'so', 47 \ 'su'], 48 \'register': ['n', 'rc', 'rw', 'rt', 'ru'], 49 \'global_mark': ['n', 'f', 'l', 'c'], 50 \'local_mark': ['f', 'n', 'l', 'c'], 51 \'jump': ['f', 'l', 'c'], 52 \'change': ['f', 'l', 'c'], 53 \'header': [], 54\} 55 56"" 57" Like one of the values from s:SHADA_MAP_ENTRIES, but for a single buffer in 58" buffer list entry. 59let s:SHADA_BUFFER_LIST_KEYS = ['f', 'l', 'c'] 60 61"" 62" List of possible history types. Maps integer values that represent history 63" types to human-readable names. 64let s:SHADA_HISTORY_TYPES = ['command', 'search', 'expression', 'input', 'debug'] 65 66"" 67" Map that maps entry names to their descriptions. Only for entries which have 68" list as a data type. Description is a list of lists where each entry has item 69" description and item type. 70let s:SHADA_FIXED_ARRAY_ENTRIES = { 71 \'replacement_string': [[':s replacement string', 'bin']], 72 \'history_entry': [ 73 \['history type', 'histtype'], 74 \['contents', 'bin'], 75 \['separator', 'intchar'], 76 \], 77 \'variable': [['name', 'bin'], ['value', 'any']], 78\} 79 80"" 81" Dictionary that maps enum names to dictionary with enum values. Dictionary 82" with enum values maps enum human-readable names to corresponding values. Enums 83" are used as type names in s:SHADA_FIXED_ARRAY_ENTRIES and 84" s:SHADA_STANDARD_KEYS. 85let s:SHADA_ENUMS = { 86 \'histtype': { 87 \'CMD': 0, 88 \'SEARCH': 1, 89 \'EXPR': 2, 90 \'INPUT': 3, 91 \'DEBUG': 4, 92 \}, 93 \'regtype': { 94 \'CHARACTERWISE': 0, 95 \'LINEWISE': 1, 96 \'BLOCKWISE': 2, 97 \} 98\} 99 100"" 101" Second argument to msgpack#eval. 102let s:SHADA_SPECIAL_OBJS = {} 103call map(values(s:SHADA_ENUMS), 104 \'extend(s:SHADA_SPECIAL_OBJS, map(copy(v:val), "string(v:val)"))') 105 106"" 107" Like s:SHADA_ENUMS, but inner dictionary maps values to names and not names to 108" values. 109let s:SHADA_REV_ENUMS = map(copy(s:SHADA_ENUMS), '{}') 110call map(copy(s:SHADA_ENUMS), 111 \'map(copy(v:val), ' 112 \. '"extend(s:SHADA_REV_ENUMS[" . string(v:key) . "], ' 113 \. '{v:val : v:key})")') 114 115"" 116" Maximum length of ShaDa entry name. Used to arrange entries to the table. 117let s:SHADA_MAX_ENTRY_LENGTH = max( 118 \map(values(s:SHADA_ENTRY_NAMES), 'len(v:val)') 119 \+ [len('unknown (0x)') + 16]) 120 121"" 122" Object that marks required value. 123let s:SHADA_REQUIRED = [] 124 125"" 126" Dictionary that maps default key names to their description. Description is 127" a list that contains human-readable hint, key type and default value. 128let s:SHADA_STANDARD_KEYS = { 129 \'sm': ['magic value', 'boolean', g:msgpack#true], 130 \'sc': ['smartcase value', 'boolean', g:msgpack#false], 131 \'sl': ['has line offset', 'boolean', g:msgpack#false], 132 \'se': ['place cursor at end', 'boolean', g:msgpack#false], 133 \'so': ['offset value', 'integer', 0], 134 \'su': ['is last used', 'boolean', g:msgpack#true], 135 \'ss': ['is :s pattern', 'boolean', g:msgpack#false], 136 \'sh': ['v:hlsearch value', 'boolean', g:msgpack#false], 137 \'sp': ['pattern', 'bin', s:SHADA_REQUIRED], 138 \'sb': ['search backward', 'boolean', g:msgpack#false], 139 \'rt': ['type', 'regtype', s:SHADA_ENUMS.regtype.CHARACTERWISE], 140 \'rw': ['block width', 'uint', 0], 141 \'rc': ['contents', 'binarray', s:SHADA_REQUIRED], 142 \'ru': ['is_unnamed', 'boolean', g:msgpack#false], 143 \'n': ['name', 'intchar', char2nr('"')], 144 \'l': ['line number', 'uint', 1], 145 \'c': ['column', 'uint', 0], 146 \'f': ['file name', 'bin', s:SHADA_REQUIRED], 147\} 148 149"" 150" Set of entry types containing entries which require `n` key. 151let s:SHADA_REQUIRES_NAME = {'local_mark': 1, 'global_mark': 1, 'register': 1} 152 153"" 154" Maximum width of human-readable hint. Used to arrange data in table. 155let s:SHADA_MAX_HINT_WIDTH = max(map(values(s:SHADA_STANDARD_KEYS), 156 \'len(v:val[0])')) 157 158"" 159" Default mark name for the cases when it makes sense (i.e. for local marks). 160let s:SHADA_DEFAULT_MARK_NAME = '"' 161 162"" 163" Mapping that maps timestamps represented using msgpack#string to strftime 164" output. Used by s:shada_strftime. 165let s:shada_strftime_cache = {} 166 167"" 168" Mapping that maps strftime output from s:shada_strftime to timestamps. 169let s:shada_strptime_cache = {} 170 171"" 172" Time format used for displaying ShaDa files. 173let s:SHADA_TIME_FORMAT = '%Y-%m-%dT%H:%M:%S' 174 175"" 176" Wrapper around msgpack#strftime that caches its output. 177" 178" Format is hardcoded to s:SHADA_TIME_FORMAT. 179function s:shada_strftime(timestamp) abort 180 let key = msgpack#string(a:timestamp) 181 if has_key(s:shada_strftime_cache, key) 182 return s:shada_strftime_cache[key] 183 endif 184 let val = msgpack#strftime(s:SHADA_TIME_FORMAT, a:timestamp) 185 let s:shada_strftime_cache[key] = val 186 let s:shada_strptime_cache[val] = a:timestamp 187 return val 188endfunction 189 190"" 191" Wrapper around msgpack#strftime that uses cache created by s:shada_strftime(). 192" 193" Also caches its own results. Format is hardcoded to s:SHADA_TIME_FORMAT. 194function s:shada_strptime(string) abort 195 if has_key(s:shada_strptime_cache, a:string) 196 return s:shada_strptime_cache[a:string] 197 endif 198 let ts = msgpack#strptime(s:SHADA_TIME_FORMAT, a:string) 199 let s:shada_strptime_cache[a:string] = ts 200 return ts 201endfunction 202 203"" 204" Check whether given value matches given type. 205" 206" @return Zero if value matches, error message string if it does not. 207function s:shada_check_type(type, val) abort 208 let type = msgpack#type(a:val) 209 if type is# a:type 210 return 0 211 endif 212 if has_key(s:SHADA_ENUMS, a:type) 213 let msg = s:shada_check_type('uint', a:val) 214 if msg isnot 0 215 return msg 216 endif 217 if !has_key(s:SHADA_REV_ENUMS[a:type], a:val) 218 let evals_msg = join(map(sort(items(s:SHADA_REV_ENUMS[a:type])), 219 \'v:val[0] . " (" . v:val[1] . ")"'), ', ') 220 return 'Unexpected enum value: expected one of ' . evals_msg 221 endif 222 return 0 223 elseif a:type is# 'uint' 224 if type isnot# 'integer' 225 return 'Expected integer' 226 endif 227 if !(type(a:val) == type({}) ? a:val._VAL[0] == 1 : a:val >= 0) 228 return 'Value is negative' 229 endif 230 return 0 231 elseif a:type is# 'bin' 232 " Binary string without zero bytes 233 if type isnot# 'binary' 234 return 'Expected binary string' 235 elseif (type(a:val) == type({}) 236 \&& !empty(filter(copy(a:val._VAL), 'stridx(v:val, "\n") != -1'))) 237 return 'Expected no NUL bytes' 238 endif 239 return 0 240 elseif a:type is# 'intchar' 241 let msg = s:shada_check_type('uint', a:val) 242 if msg isnot# 0 243 return msg 244 endif 245 return 0 246 elseif a:type is# 'binarray' 247 if type isnot# 'array' 248 return 'Expected array value' 249 elseif !empty(filter(copy(type(a:val) == type({}) ? a:val._VAL : a:val), 250 \'msgpack#type(v:val) isnot# "binary"')) 251 return 'Expected array of binary strings' 252 else 253 for element in (type(a:val) == type({}) ? a:val._VAL : a:val) 254 if (type(element) == type({}) 255 \&& !empty(filter(copy(element._VAL), 'stridx(v:val, "\n") != -1'))) 256 return 'Expected no NUL bytes' 257 endif 258 unlet element 259 endfor 260 endif 261 return 0 262 elseif a:type is# 'boolean' 263 return 'Expected boolean' 264 elseif a:type is# 'integer' 265 return 'Expected integer' 266 elseif a:type is# 'any' 267 return 0 268 endif 269 return 'Internal error: unknown type ' . a:type 270endfunction 271 272"" 273" Convert msgpack mapping object to a list of strings for 274" s:shada_convert_entry(). 275" 276" @param[in] map Mapping to convert. 277" @param[in] default_keys List of keys which have default value in this 278" mapping. 279" @param[in] name Name of the converted entry. 280function s:shada_convert_map(map, default_keys, name) abort 281 let ret = [] 282 let keys = copy(a:default_keys) 283 call map(sort(keys(a:map)), 'index(keys, v:val) == -1 ? add(keys, v:val) : 0') 284 let descriptions = map(copy(keys), 285 \'get(s:SHADA_STANDARD_KEYS, v:val, ["", 0, 0])') 286 let max_key_len = max(map(copy(keys), 'len(v:val)')) 287 let max_desc_len = max(map(copy(descriptions), 288 \'v:val[0] is 0 ? 0 : len(v:val[0])')) 289 if max_key_len < len('Key') 290 let max_key_len = len('Key') 291 endif 292 let key_header = 'Key' . repeat('_', max_key_len - len('Key')) 293 if max_desc_len == 0 294 call add(ret, printf(' %% %s %s', key_header, 'Value')) 295 else 296 if max_desc_len < len('Description') 297 let max_desc_len = len('Description') 298 endif 299 let desc_header = ('Description' 300 \. repeat('_', max_desc_len - len('Description'))) 301 call add(ret, printf(' %% %s %s %s', key_header, desc_header, 'Value')) 302 endif 303 let i = 0 304 for key in keys 305 let [description, type, default] = descriptions[i] 306 if a:name isnot# 'local_mark' && key is# 'n' 307 unlet default 308 let default = s:SHADA_REQUIRED 309 endif 310 let value = get(a:map, key, default) 311 if (key is# 'n' && !has_key(s:SHADA_REQUIRES_NAME, a:name) 312 \&& value is# s:SHADA_REQUIRED) 313 " Do nothing 314 elseif value is s:SHADA_REQUIRED 315 call add(ret, ' # Required key missing: ' . key) 316 elseif max_desc_len == 0 317 call add(ret, printf(' + %-*s %s', 318 \max_key_len, key, 319 \msgpack#string(value))) 320 else 321 if type isnot 0 && value isnot# default 322 let msg = s:shada_check_type(type, value) 323 if msg isnot 0 324 call add(ret, ' # ' . msg) 325 endif 326 endif 327 let strval = s:shada_string(type, value) 328 if msgpack#type(value) is# 'array' && msg is 0 329 let shift = 2 + 2 + max_key_len + 2 + max_desc_len + 2 330 " Value: 1 2 3 4 5 6: 331 " " + Key Description Value" 332 " 1122333445555555555566 333 if shift + strdisplaywidth(strval, shift) > 80 334 let strval = '@' 335 endif 336 endif 337 call add(ret, printf(' + %-*s %-*s %s', 338 \max_key_len, key, 339 \max_desc_len, description, 340 \strval)) 341 if strval is '@' 342 for v in value 343 call add(ret, printf(' | - %s', msgpack#string(v))) 344 unlet v 345 endfor 346 endif 347 endif 348 let i += 1 349 unlet value 350 unlet default 351 endfor 352 return ret 353endfunction 354 355"" 356" Wrapper around msgpack#string() which may return string from s:SHADA_REV_ENUMS 357function s:shada_string(type, v) abort 358 if (has_key(s:SHADA_ENUMS, a:type) && type(a:v) == type(0) 359 \&& has_key(s:SHADA_REV_ENUMS[a:type], a:v)) 360 return s:SHADA_REV_ENUMS[a:type][a:v] 361 " Restricting a:v to be <= 127 is not necessary, but intchar constants are 362 " normally expected to be either ASCII printable characters or NUL. 363 elseif a:type is# 'intchar' && type(a:v) == type(0) && a:v >= 0 && a:v <= 127 364 if a:v > 0 && strtrans(nr2char(a:v)) is# nr2char(a:v) 365 return "'" . nr2char(a:v) . "'" 366 else 367 return "'\\" . a:v . "'" 368 endif 369 else 370 return msgpack#string(a:v) 371 endif 372endfunction 373 374"" 375" Evaluate string obtained by s:shada_string(). 376function s:shada_eval(s) abort 377 return msgpack#eval(a:s, s:SHADA_SPECIAL_OBJS) 378endfunction 379 380"" 381" Convert one ShaDa entry to a list of strings suitable for setline(). 382" 383" Returned format looks like this: 384" 385" TODO 386function s:shada_convert_entry(entry) abort 387 if type(a:entry.type) == type({}) 388 " |msgpack-special-dict| may only be used if value does not fit into the 389 " default integer type. All known entry types do fit, so it is definitely 390 " unknown entry. 391 let name = 'unknown_(' . msgpack#int_dict_to_str(a:entry.type) . ')' 392 else 393 let name = get(s:SHADA_ENTRY_NAMES, a:entry.type, 0) 394 if name is 0 395 let name = printf('unknown_(0x%x)', a:entry.type) 396 endif 397 endif 398 let title = toupper(name[0]) . tr(name[1:], '_', ' ') 399 let header = printf('%s with timestamp %s:', title, 400 \s:shada_strftime(a:entry.timestamp)) 401 let ret = [header] 402 if name[:8] is# 'unknown_(' && name[-1:] is# ')' 403 call add(ret, ' = ' . msgpack#string(a:entry.data)) 404 elseif has_key(s:SHADA_FIXED_ARRAY_ENTRIES, name) 405 if type(a:entry.data) != type([]) 406 call add(ret, printf(' # Unexpected type: %s instead of array', 407 \msgpack#type(a:entry.data))) 408 call add(ret, ' = ' . msgpack#string(a:entry.data)) 409 return ret 410 endif 411 let i = 0 412 let max_desc_len = max(map(copy(s:SHADA_FIXED_ARRAY_ENTRIES[name]), 413 \'len(v:val[0])')) 414 if max_desc_len < len('Description') 415 let max_desc_len = len('Description') 416 endif 417 let desc_header = ('Description' 418 \. repeat('_', max_desc_len - len('Description'))) 419 call add(ret, printf(' @ %s %s', desc_header, 'Value')) 420 for value in a:entry.data 421 let [desc, type] = get(s:SHADA_FIXED_ARRAY_ENTRIES[name], i, ['', 0]) 422 if (i == 2 && name is# 'history_entry' 423 \&& a:entry.data[0] isnot# s:SHADA_ENUMS.histtype.SEARCH) 424 let [desc, type] = ['', 0] 425 endif 426 if type isnot 0 427 let msg = s:shada_check_type(type, value) 428 if msg isnot 0 429 call add(ret, ' # ' . msg) 430 endif 431 endif 432 call add(ret, printf(' - %-*s %s', max_desc_len, desc, 433 \s:shada_string(type, value))) 434 let i += 1 435 unlet value 436 endfor 437 if (len(a:entry.data) < len(s:SHADA_FIXED_ARRAY_ENTRIES[name]) 438 \&& !(name is# 'history_entry' 439 \&& len(a:entry.data) == 2 440 \&& a:entry.data[0] isnot# s:SHADA_ENUMS.histtype.SEARCH)) 441 call add(ret, ' # Expected more elements in list') 442 endif 443 elseif has_key(s:SHADA_MAP_ENTRIES, name) 444 if type(a:entry.data) != type({}) 445 call add(ret, printf(' # Unexpected type: %s instead of map', 446 \msgpack#type(a:entry.data))) 447 call add(ret, ' = ' . msgpack#string(a:entry.data)) 448 return ret 449 endif 450 if msgpack#special_type(a:entry.data) isnot 0 451 call add(ret, ' # Entry is a special dict which is unexpected') 452 call add(ret, ' = ' . msgpack#string(a:entry.data)) 453 return ret 454 endif 455 let ret += s:shada_convert_map(a:entry.data, s:SHADA_MAP_ENTRIES[name], 456 \name) 457 elseif name is# 'buffer_list' 458 if type(a:entry.data) != type([]) 459 call add(ret, printf(' # Unexpected type: %s instead of array', 460 \msgpack#type(a:entry.data))) 461 call add(ret, ' = ' . msgpack#string(a:entry.data)) 462 return ret 463 elseif !empty(filter(copy(a:entry.data), 464 \'type(v:val) != type({}) ' 465 \. '|| msgpack#special_type(v:val) isnot 0')) 466 call add(ret, ' # Expected array of maps') 467 call add(ret, ' = ' . msgpack#string(a:entry.data)) 468 return ret 469 endif 470 for bufdef in a:entry.data 471 if bufdef isnot a:entry.data[0] 472 call add(ret, '') 473 endif 474 let ret += s:shada_convert_map(bufdef, s:SHADA_BUFFER_LIST_KEYS, name) 475 endfor 476 else 477 throw 'internal-unknown-type:Internal error: unknown type name: ' . name 478 endif 479 return ret 480endfunction 481 482"" 483" Order of msgpack objects in one ShaDa entry. Each item in the list is name of 484" the key in dictionaries returned by shada#read(). 485let s:SHADA_ENTRY_OBJECT_SEQUENCE = ['type', 'timestamp', 'length', 'data'] 486 487"" 488" Convert list returned by msgpackparse() to a list of ShaDa objects 489" 490" @param[in] mpack List of VimL objects returned by msgpackparse(). 491" 492" @return List of dictionaries with keys type, timestamp, length and data. Each 493" dictionary describes one ShaDa entry. 494function shada#mpack_to_sd(mpack) abort 495 let ret = [] 496 let i = 0 497 for element in a:mpack 498 let key = s:SHADA_ENTRY_OBJECT_SEQUENCE[ 499 \i % len(s:SHADA_ENTRY_OBJECT_SEQUENCE)] 500 if key is# 'type' 501 call add(ret, {}) 502 endif 503 let ret[-1][key] = element 504 if key isnot# 'data' 505 if !msgpack#is_uint(element) 506 throw printf('not-uint:Entry %i has %s element '. 507 \'which is not an unsigned integer', 508 \len(ret), key) 509 endif 510 if key is# 'type' && msgpack#equal(element, 0) 511 throw printf('zero-uint:Entry %i has %s element '. 512 \'which is zero', 513 \len(ret), key) 514 endif 515 endif 516 let i += 1 517 unlet element 518 endfor 519 return ret 520endfunction 521 522"" 523" Convert read ShaDa file to a list of lines suitable for setline() 524" 525" @param[in] shada List of ShaDa entries like returned by shada#mpack_to_sd(). 526" 527" @return List of strings suitable for setline()-like functions. 528function shada#sd_to_strings(shada) abort 529 let ret = [] 530 for entry in a:shada 531 let ret += s:shada_convert_entry(entry) 532 endfor 533 return ret 534endfunction 535 536"" 537" Convert a readfile()-like list of strings to a list of lines suitable for 538" setline(). 539" 540" @param[in] binstrings List of strings to convert. 541" 542" @return List of lines. 543function shada#get_strings(binstrings) abort 544 return shada#sd_to_strings(shada#mpack_to_sd(msgpackparse(a:binstrings))) 545endfunction 546 547"" 548" Convert s:shada_convert_entry() output to original entry. 549function s:shada_convert_strings(strings) abort 550 let strings = copy(a:strings) 551 let match = matchlist( 552 \strings[0], 553 \'\v\C^(.{-})\m with timestamp \(\d\{4}-\d\d-\d\dT\d\d:\d\d:\d\d\):$') 554 if empty(match) 555 throw 'invalid-header:Header has invalid format: ' . strings[0] 556 endif 557 call remove(strings, 0) 558 let title = match[1] 559 let name = tolower(title[0]) . tr(title[1:], ' ', '_') 560 let ret = {} 561 let empty_default = g:msgpack#nil 562 if name[:8] is# 'unknown_(' && name[-1:] is# ')' 563 let ret.type = +name[9:-2] 564 elseif has_key(s:SHADA_ENTRY_TYPES, name) 565 let ret.type = s:SHADA_ENTRY_TYPES[name] 566 if has_key(s:SHADA_MAP_ENTRIES, name) 567 unlet empty_default 568 let empty_default = {} 569 elseif has_key(s:SHADA_FIXED_ARRAY_ENTRIES, name) || name is# 'buffer_list' 570 unlet empty_default 571 let empty_default = [] 572 endif 573 else 574 throw 'invalid-type:Unknown type ' . name 575 endif 576 let ret.timestamp = s:shada_strptime(match[2]) 577 if empty(strings) 578 let ret.data = empty_default 579 else 580 while !empty(strings) 581 if strings[0][2] is# '=' 582 let data = s:shada_eval(strings[0][4:]) 583 call remove(strings, 0) 584 elseif strings[0][2] is# '%' 585 if name is# 'buffer_list' && !has_key(ret, 'data') 586 let ret.data = [] 587 endif 588 let match = matchlist( 589 \strings[0], 590 \'\m\C^ % \(Key_*\)\( Description_*\)\? Value') 591 if empty(match) 592 throw 'invalid-map-header:Invalid mapping header: ' . strings[0] 593 endif 594 call remove(strings, 0) 595 let key_len = len(match[1]) 596 let desc_skip_len = len(match[2]) 597 let data = {'_TYPE': v:msgpack_types.map, '_VAL': []} 598 while !empty(strings) && strings[0][2] is# '+' 599 let line = remove(strings, 0)[4:] 600 let key = substitute(line[:key_len - 1], '\v\C\ *$', '', '') 601 let strval = line[key_len + desc_skip_len + 2:] 602 if strval is# '@' 603 let val = [] 604 while !empty(strings) && strings[0][2] is# '|' 605 if strings[0][4] isnot# '-' 606 throw ('invalid-array:Expected hyphen-minus at column 5: ' 607 \. strings) 608 endif 609 call add(val, s:shada_eval(remove(strings, 0)[5:])) 610 endwhile 611 else 612 let val = s:shada_eval(strval) 613 endif 614 if (has_key(s:SHADA_STANDARD_KEYS, key) 615 \&& s:SHADA_STANDARD_KEYS[key][2] isnot# s:SHADA_REQUIRED 616 \&& msgpack#equal(s:SHADA_STANDARD_KEYS[key][2], val)) 617 unlet val 618 continue 619 endif 620 call add(data._VAL, [{'_TYPE': v:msgpack_types.string, '_VAL': [key]}, 621 \val]) 622 unlet val 623 endwhile 624 elseif strings[0][2] is# '@' 625 let match = matchlist( 626 \strings[0], 627 \'\m\C^ @ \(Description_* \)\?Value') 628 if empty(match) 629 throw 'invalid-array-header:Invalid array header: ' . strings[0] 630 endif 631 call remove(strings, 0) 632 let desc_skip_len = len(match[1]) 633 let data = [] 634 while !empty(strings) && strings[0][2] is# '-' 635 let val = remove(strings, 0)[4 + desc_skip_len :] 636 call add(data, s:shada_eval(val)) 637 endwhile 638 else 639 throw 'invalid-line:Unrecognized line: ' . strings[0] 640 endif 641 if !has_key(ret, 'data') 642 let ret.data = data 643 elseif type(ret.data) == type([]) 644 call add(ret.data, data) 645 else 646 let ret.data = [ret.data, data] 647 endif 648 unlet data 649 endwhile 650 endif 651 let ret._data = msgpackdump([ret.data]) 652 let ret.length = len(ret._data) - 1 653 for s in ret._data 654 let ret.length += len(s) 655 endfor 656 return ret 657endfunction 658 659"" 660" Convert s:shada_sd_to_strings() output to a list of original entries. 661function shada#strings_to_sd(strings) abort 662 let strings = filter(copy(a:strings), 'v:val !~# ''\v^\s*%(\#|$)''') 663 let stringss = [] 664 for string in strings 665 if string[0] isnot# ' ' 666 call add(stringss, []) 667 endif 668 call add(stringss[-1], string) 669 endfor 670 return map(copy(stringss), 's:shada_convert_strings(v:val)') 671endfunction 672 673"" 674" Convert a list of strings to list of strings suitable for writefile(). 675function shada#get_binstrings(strings) abort 676 let entries = shada#strings_to_sd(a:strings) 677 if !g:shada#keep_old_header 678 call filter(entries, 'v:val.type != ' . s:SHADA_ENTRY_TYPES.header) 679 endif 680 if g:shada#add_own_header 681 let data = {'version': v:version, 'generator': 'shada.vim'} 682 let dumped_data = msgpackdump([data]) 683 let length = len(dumped_data) - 1 684 for s in dumped_data 685 let length += len(s) 686 endfor 687 call insert(entries, { 688 \'type': s:SHADA_ENTRY_TYPES.header, 689 \'timestamp': localtime(), 690 \'length': length, 691 \'data': data, 692 \'_data': dumped_data, 693 \}) 694 endif 695 let mpack = [] 696 for entry in entries 697 let mpack += map(copy(s:SHADA_ENTRY_OBJECT_SEQUENCE), 'entry[v:val]') 698 endfor 699 return msgpackdump(mpack) 700endfunction 701