shada.vim (23564B)
1 if exists('g:loaded_shada_autoload') 2 finish 3 endif 4 let 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. 13 let 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. 17 let g:shada#add_own_header = get(g:, 'shada#add_own_header', 1) 18 19 "" 20 " Dictionary that maps ShaDa types to their names. 21 let 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 37 let s:SHADA_ENTRY_TYPES = {} 38 call 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. 45 let 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. 59 let 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. 64 let 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. 70 let 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. 85 let 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. 102 let s:SHADA_SPECIAL_OBJS = {} 103 call 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. 109 let s:SHADA_REV_ENUMS = map(copy(s:SHADA_ENUMS), '{}') 110 call 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. 117 let 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. 123 let 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. 128 let 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. 151 let 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. 155 let 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). 160 let 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. 165 let s:shada_strftime_cache = {} 166 167 "" 168 " Mapping that maps strftime output from s:shada_strftime to timestamps. 169 let s:shada_strptime_cache = {} 170 171 "" 172 " Time format used for displaying ShaDa files. 173 let 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. 179 function 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 188 endfunction 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. 194 function 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 201 endfunction 202 203 "" 204 " Check whether given value matches given type. 205 " 206 " @return Zero if value matches, error message string if it does not. 207 function 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# 'string' 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# "string"')) 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 270 endfunction 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. 280 function 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 353 endfunction 354 355 "" 356 " Wrapper around msgpack#string() which may return string from s:SHADA_REV_ENUMS 357 function 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 372 endfunction 373 374 "" 375 " Evaluate string obtained by s:shada_string(). 376 function s:shada_eval(s) abort 377 return msgpack#eval(a:s, s:SHADA_SPECIAL_OBJS) 378 endfunction 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 386 function 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 480 endfunction 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(). 485 let 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 Vimscript objects returned by msgpackparse(). 491 " 492 " @return List of dictionaries with keys type, timestamp, length and data. Each 493 " dictionary describes one ShaDa entry. 494 function 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 520 endfunction 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. 528 function 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 534 endfunction 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. 543 function shada#get_strings(binstrings) abort 544 return shada#sd_to_strings(shada#mpack_to_sd(msgpackparse(a:binstrings))) 545 endfunction 546 547 "" 548 " Convert s:shada_convert_entry() output to original entry. 549 function 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 657 endfunction 658 659 "" 660 " Convert s:shada_sd_to_strings() output to a list of original entries. 661 function 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)') 671 endfunction 672 673 "" 674 " Convert a list of strings to list of strings suitable for writefile(). 675 function 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) 700 endfunction