Mercurial > vim
comparison src/viminfo.c @ 17460:e43f0c0c491c v8.1.1728
patch 8.1.1728: wrong place for command line history viminfo support
commit https://github.com/vim/vim/commit/5f32ece459d1f310b1b48b72e07dcd77d3261a76
Author: Bram Moolenaar <Bram@vim.org>
Date: Sun Jul 21 21:51:59 2019 +0200
patch 8.1.1728: wrong place for command line history viminfo support
Problem: Wrong place for command line history viminfo support.
Solution: Move it to viminfo.c.
author | Bram Moolenaar <Bram@vim.org> |
---|---|
date | Sun, 21 Jul 2019 22:00:05 +0200 |
parents | cfdef48743ed |
children | 3e708b5c0509 |
comparison
equal
deleted
inserted
replaced
17459:02dc4260ddbb | 17460:e43f0c0c491c |
---|---|
166 buf->b_last_cursor.col); | 166 buf->b_last_cursor.col); |
167 viminfo_writestring(fp, line); | 167 viminfo_writestring(fp, line); |
168 } | 168 } |
169 vim_free(line); | 169 vim_free(line); |
170 } | 170 } |
171 | |
172 #if defined(FEAT_CMDHIST) || defined(PROTO) | |
173 /* | |
174 * Buffers for history read from a viminfo file. Only valid while reading. | |
175 */ | |
176 static histentry_T *viminfo_history[HIST_COUNT] = | |
177 {NULL, NULL, NULL, NULL, NULL}; | |
178 static int viminfo_hisidx[HIST_COUNT] = {0, 0, 0, 0, 0}; | |
179 static int viminfo_hislen[HIST_COUNT] = {0, 0, 0, 0, 0}; | |
180 static int viminfo_add_at_front = FALSE; | |
181 | |
182 /* | |
183 * Translate a history type number to the associated character. | |
184 */ | |
185 static int | |
186 hist_type2char( | |
187 int type, | |
188 int use_question) // use '?' instead of '/' | |
189 { | |
190 if (type == HIST_CMD) | |
191 return ':'; | |
192 if (type == HIST_SEARCH) | |
193 { | |
194 if (use_question) | |
195 return '?'; | |
196 else | |
197 return '/'; | |
198 } | |
199 if (type == HIST_EXPR) | |
200 return '='; | |
201 return '@'; | |
202 } | |
203 | |
204 /* | |
205 * Prepare for reading the history from the viminfo file. | |
206 * This allocates history arrays to store the read history lines. | |
207 */ | |
208 static void | |
209 prepare_viminfo_history(int asklen, int writing) | |
210 { | |
211 int i; | |
212 int num; | |
213 int type; | |
214 int len; | |
215 int hislen = get_hislen(); | |
216 | |
217 init_history(); | |
218 viminfo_add_at_front = (asklen != 0 && !writing); | |
219 if (asklen > hislen) | |
220 asklen = hislen; | |
221 | |
222 for (type = 0; type < HIST_COUNT; ++type) | |
223 { | |
224 histentry_T *histentry = get_histentry(type); | |
225 | |
226 // Count the number of empty spaces in the history list. Entries read | |
227 // from viminfo previously are also considered empty. If there are | |
228 // more spaces available than we request, then fill them up. | |
229 for (i = 0, num = 0; i < hislen; i++) | |
230 if (histentry[i].hisstr == NULL || histentry[i].viminfo) | |
231 num++; | |
232 len = asklen; | |
233 if (num > len) | |
234 len = num; | |
235 if (len <= 0) | |
236 viminfo_history[type] = NULL; | |
237 else | |
238 viminfo_history[type] = LALLOC_MULT(histentry_T, len); | |
239 if (viminfo_history[type] == NULL) | |
240 len = 0; | |
241 viminfo_hislen[type] = len; | |
242 viminfo_hisidx[type] = 0; | |
243 } | |
244 } | |
245 | |
246 /* | |
247 * Accept a line from the viminfo, store it in the history array when it's | |
248 * new. | |
249 */ | |
250 static int | |
251 read_viminfo_history(vir_T *virp, int writing) | |
252 { | |
253 int type; | |
254 long_u len; | |
255 char_u *val; | |
256 char_u *p; | |
257 | |
258 type = hist_char2type(virp->vir_line[0]); | |
259 if (viminfo_hisidx[type] < viminfo_hislen[type]) | |
260 { | |
261 val = viminfo_readstring(virp, 1, TRUE); | |
262 if (val != NULL && *val != NUL) | |
263 { | |
264 int sep = (*val == ' ' ? NUL : *val); | |
265 | |
266 if (!in_history(type, val + (type == HIST_SEARCH), | |
267 viminfo_add_at_front, sep, writing)) | |
268 { | |
269 // Need to re-allocate to append the separator byte. | |
270 len = STRLEN(val); | |
271 p = alloc(len + 2); | |
272 if (p != NULL) | |
273 { | |
274 if (type == HIST_SEARCH) | |
275 { | |
276 // Search entry: Move the separator from the first | |
277 // column to after the NUL. | |
278 mch_memmove(p, val + 1, (size_t)len); | |
279 p[len] = sep; | |
280 } | |
281 else | |
282 { | |
283 // Not a search entry: No separator in the viminfo | |
284 // file, add a NUL separator. | |
285 mch_memmove(p, val, (size_t)len + 1); | |
286 p[len + 1] = NUL; | |
287 } | |
288 viminfo_history[type][viminfo_hisidx[type]].hisstr = p; | |
289 viminfo_history[type][viminfo_hisidx[type]].time_set = 0; | |
290 viminfo_history[type][viminfo_hisidx[type]].viminfo = TRUE; | |
291 viminfo_history[type][viminfo_hisidx[type]].hisnum = 0; | |
292 viminfo_hisidx[type]++; | |
293 } | |
294 } | |
295 } | |
296 vim_free(val); | |
297 } | |
298 return viminfo_readline(virp); | |
299 } | |
300 | |
301 /* | |
302 * Accept a new style history line from the viminfo, store it in the history | |
303 * array when it's new. | |
304 */ | |
305 static void | |
306 handle_viminfo_history( | |
307 garray_T *values, | |
308 int writing) | |
309 { | |
310 int type; | |
311 long_u len; | |
312 char_u *val; | |
313 char_u *p; | |
314 bval_T *vp = (bval_T *)values->ga_data; | |
315 | |
316 // Check the format: | |
317 // |{bartype},{histtype},{timestamp},{separator},"text" | |
318 if (values->ga_len < 4 | |
319 || vp[0].bv_type != BVAL_NR | |
320 || vp[1].bv_type != BVAL_NR | |
321 || (vp[2].bv_type != BVAL_NR && vp[2].bv_type != BVAL_EMPTY) | |
322 || vp[3].bv_type != BVAL_STRING) | |
323 return; | |
324 | |
325 type = vp[0].bv_nr; | |
326 if (type >= HIST_COUNT) | |
327 return; | |
328 if (viminfo_hisidx[type] < viminfo_hislen[type]) | |
329 { | |
330 val = vp[3].bv_string; | |
331 if (val != NULL && *val != NUL) | |
332 { | |
333 int sep = type == HIST_SEARCH && vp[2].bv_type == BVAL_NR | |
334 ? vp[2].bv_nr : NUL; | |
335 int idx; | |
336 int overwrite = FALSE; | |
337 | |
338 if (!in_history(type, val, viminfo_add_at_front, sep, writing)) | |
339 { | |
340 // If lines were written by an older Vim we need to avoid | |
341 // getting duplicates. See if the entry already exists. | |
342 for (idx = 0; idx < viminfo_hisidx[type]; ++idx) | |
343 { | |
344 p = viminfo_history[type][idx].hisstr; | |
345 if (STRCMP(val, p) == 0 | |
346 && (type != HIST_SEARCH || sep == p[STRLEN(p) + 1])) | |
347 { | |
348 overwrite = TRUE; | |
349 break; | |
350 } | |
351 } | |
352 | |
353 if (!overwrite) | |
354 { | |
355 // Need to re-allocate to append the separator byte. | |
356 len = vp[3].bv_len; | |
357 p = alloc(len + 2); | |
358 } | |
359 else | |
360 len = 0; // for picky compilers | |
361 if (p != NULL) | |
362 { | |
363 viminfo_history[type][idx].time_set = vp[1].bv_nr; | |
364 if (!overwrite) | |
365 { | |
366 mch_memmove(p, val, (size_t)len + 1); | |
367 // Put the separator after the NUL. | |
368 p[len + 1] = sep; | |
369 viminfo_history[type][idx].hisstr = p; | |
370 viminfo_history[type][idx].hisnum = 0; | |
371 viminfo_history[type][idx].viminfo = TRUE; | |
372 viminfo_hisidx[type]++; | |
373 } | |
374 } | |
375 } | |
376 } | |
377 } | |
378 } | |
379 | |
380 /* | |
381 * Concatenate history lines from viminfo after the lines typed in this Vim. | |
382 */ | |
383 static void | |
384 concat_history(int type) | |
385 { | |
386 int idx; | |
387 int i; | |
388 int hislen = get_hislen(); | |
389 histentry_T *histentry = get_histentry(type); | |
390 int *hisidx = get_hisidx(type); | |
391 int *hisnum = get_hisnum(type); | |
392 | |
393 idx = *hisidx + viminfo_hisidx[type]; | |
394 if (idx >= hislen) | |
395 idx -= hislen; | |
396 else if (idx < 0) | |
397 idx = hislen - 1; | |
398 if (viminfo_add_at_front) | |
399 *hisidx = idx; | |
400 else | |
401 { | |
402 if (*hisidx == -1) | |
403 *hisidx = hislen - 1; | |
404 do | |
405 { | |
406 if (histentry[idx].hisstr != NULL || histentry[idx].viminfo) | |
407 break; | |
408 if (++idx == hislen) | |
409 idx = 0; | |
410 } while (idx != *hisidx); | |
411 if (idx != *hisidx && --idx < 0) | |
412 idx = hislen - 1; | |
413 } | |
414 for (i = 0; i < viminfo_hisidx[type]; i++) | |
415 { | |
416 vim_free(histentry[idx].hisstr); | |
417 histentry[idx].hisstr = viminfo_history[type][i].hisstr; | |
418 histentry[idx].viminfo = TRUE; | |
419 histentry[idx].time_set = viminfo_history[type][i].time_set; | |
420 if (--idx < 0) | |
421 idx = hislen - 1; | |
422 } | |
423 idx += 1; | |
424 idx %= hislen; | |
425 for (i = 0; i < viminfo_hisidx[type]; i++) | |
426 { | |
427 histentry[idx++].hisnum = ++*hisnum; | |
428 idx %= hislen; | |
429 } | |
430 } | |
431 | |
432 static int | |
433 sort_hist(const void *s1, const void *s2) | |
434 { | |
435 histentry_T *p1 = *(histentry_T **)s1; | |
436 histentry_T *p2 = *(histentry_T **)s2; | |
437 | |
438 if (p1->time_set < p2->time_set) return -1; | |
439 if (p1->time_set > p2->time_set) return 1; | |
440 return 0; | |
441 } | |
442 | |
443 /* | |
444 * Merge history lines from viminfo and lines typed in this Vim based on the | |
445 * timestamp; | |
446 */ | |
447 static void | |
448 merge_history(int type) | |
449 { | |
450 int max_len; | |
451 histentry_T **tot_hist; | |
452 histentry_T *new_hist; | |
453 int i; | |
454 int len; | |
455 int hislen = get_hislen(); | |
456 histentry_T *histentry = get_histentry(type); | |
457 int *hisidx = get_hisidx(type); | |
458 int *hisnum = get_hisnum(type); | |
459 | |
460 // Make one long list with all entries. | |
461 max_len = hislen + viminfo_hisidx[type]; | |
462 tot_hist = ALLOC_MULT(histentry_T *, max_len); | |
463 new_hist = ALLOC_MULT(histentry_T, hislen ); | |
464 if (tot_hist == NULL || new_hist == NULL) | |
465 { | |
466 vim_free(tot_hist); | |
467 vim_free(new_hist); | |
468 return; | |
469 } | |
470 for (i = 0; i < viminfo_hisidx[type]; i++) | |
471 tot_hist[i] = &viminfo_history[type][i]; | |
472 len = i; | |
473 for (i = 0; i < hislen; i++) | |
474 if (histentry[i].hisstr != NULL) | |
475 tot_hist[len++] = &histentry[i]; | |
476 | |
477 // Sort the list on timestamp. | |
478 qsort((void *)tot_hist, (size_t)len, sizeof(histentry_T *), sort_hist); | |
479 | |
480 // Keep the newest ones. | |
481 for (i = 0; i < hislen; i++) | |
482 { | |
483 if (i < len) | |
484 { | |
485 new_hist[i] = *tot_hist[i]; | |
486 tot_hist[i]->hisstr = NULL; | |
487 if (new_hist[i].hisnum == 0) | |
488 new_hist[i].hisnum = ++*hisnum; | |
489 } | |
490 else | |
491 clear_hist_entry(&new_hist[i]); | |
492 } | |
493 *hisidx = (i < len ? i : len) - 1; | |
494 | |
495 // Free what is not kept. | |
496 for (i = 0; i < viminfo_hisidx[type]; i++) | |
497 vim_free(viminfo_history[type][i].hisstr); | |
498 for (i = 0; i < hislen; i++) | |
499 vim_free(histentry[i].hisstr); | |
500 vim_free(histentry); | |
501 set_histentry(type, new_hist); | |
502 vim_free(tot_hist); | |
503 } | |
504 | |
505 /* | |
506 * Finish reading history lines from viminfo. Not used when writing viminfo. | |
507 */ | |
508 static void | |
509 finish_viminfo_history(vir_T *virp) | |
510 { | |
511 int type; | |
512 int merge = virp->vir_version >= VIMINFO_VERSION_WITH_HISTORY; | |
513 | |
514 for (type = 0; type < HIST_COUNT; ++type) | |
515 { | |
516 if (get_histentry(type) == NULL) | |
517 continue; | |
518 | |
519 if (merge) | |
520 merge_history(type); | |
521 else | |
522 concat_history(type); | |
523 | |
524 VIM_CLEAR(viminfo_history[type]); | |
525 viminfo_hisidx[type] = 0; | |
526 } | |
527 } | |
528 | |
529 /* | |
530 * Write history to viminfo file in "fp". | |
531 * When "merge" is TRUE merge history lines with a previously read viminfo | |
532 * file, data is in viminfo_history[]. | |
533 * When "merge" is FALSE just write all history lines. Used for ":wviminfo!". | |
534 */ | |
535 static void | |
536 write_viminfo_history(FILE *fp, int merge) | |
537 { | |
538 int i; | |
539 int type; | |
540 int num_saved; | |
541 int round; | |
542 int hislen; | |
543 | |
544 init_history(); | |
545 hislen = get_hislen(); | |
546 if (hislen == 0) | |
547 return; | |
548 for (type = 0; type < HIST_COUNT; ++type) | |
549 { | |
550 histentry_T *histentry = get_histentry(type); | |
551 int *hisidx = get_hisidx(type); | |
552 | |
553 num_saved = get_viminfo_parameter(hist_type2char(type, FALSE)); | |
554 if (num_saved == 0) | |
555 continue; | |
556 if (num_saved < 0) // Use default | |
557 num_saved = hislen; | |
558 fprintf(fp, _("\n# %s History (newest to oldest):\n"), | |
559 type == HIST_CMD ? _("Command Line") : | |
560 type == HIST_SEARCH ? _("Search String") : | |
561 type == HIST_EXPR ? _("Expression") : | |
562 type == HIST_INPUT ? _("Input Line") : | |
563 _("Debug Line")); | |
564 if (num_saved > hislen) | |
565 num_saved = hislen; | |
566 | |
567 /* | |
568 * Merge typed and viminfo history: | |
569 * round 1: history of typed commands. | |
570 * round 2: history from recently read viminfo. | |
571 */ | |
572 for (round = 1; round <= 2; ++round) | |
573 { | |
574 if (round == 1) | |
575 // start at newest entry, somewhere in the list | |
576 i = *hisidx; | |
577 else if (viminfo_hisidx[type] > 0) | |
578 // start at newest entry, first in the list | |
579 i = 0; | |
580 else | |
581 // empty list | |
582 i = -1; | |
583 if (i >= 0) | |
584 while (num_saved > 0 | |
585 && !(round == 2 && i >= viminfo_hisidx[type])) | |
586 { | |
587 char_u *p; | |
588 time_t timestamp; | |
589 int c = NUL; | |
590 | |
591 if (round == 1) | |
592 { | |
593 p = histentry[i].hisstr; | |
594 timestamp = histentry[i].time_set; | |
595 } | |
596 else | |
597 { | |
598 p = viminfo_history[type] == NULL ? NULL | |
599 : viminfo_history[type][i].hisstr; | |
600 timestamp = viminfo_history[type] == NULL ? 0 | |
601 : viminfo_history[type][i].time_set; | |
602 } | |
603 | |
604 if (p != NULL && (round == 2 | |
605 || !merge | |
606 || !histentry[i].viminfo)) | |
607 { | |
608 --num_saved; | |
609 fputc(hist_type2char(type, TRUE), fp); | |
610 // For the search history: put the separator in the | |
611 // second column; use a space if there isn't one. | |
612 if (type == HIST_SEARCH) | |
613 { | |
614 c = p[STRLEN(p) + 1]; | |
615 putc(c == NUL ? ' ' : c, fp); | |
616 } | |
617 viminfo_writestring(fp, p); | |
618 | |
619 { | |
620 char cbuf[NUMBUFLEN]; | |
621 | |
622 // New style history with a bar line. Format: | |
623 // |{bartype},{histtype},{timestamp},{separator},"text" | |
624 if (c == NUL) | |
625 cbuf[0] = NUL; | |
626 else | |
627 sprintf(cbuf, "%d", c); | |
628 fprintf(fp, "|%d,%d,%ld,%s,", BARTYPE_HISTORY, | |
629 type, (long)timestamp, cbuf); | |
630 barline_writestring(fp, p, LSIZE - 20); | |
631 putc('\n', fp); | |
632 } | |
633 } | |
634 if (round == 1) | |
635 { | |
636 // Decrement index, loop around and stop when back at | |
637 // the start. | |
638 if (--i < 0) | |
639 i = hislen - 1; | |
640 if (i == *hisidx) | |
641 break; | |
642 } | |
643 else | |
644 { | |
645 // Increment index. Stop at the end in the while. | |
646 ++i; | |
647 } | |
648 } | |
649 } | |
650 for (i = 0; i < viminfo_hisidx[type]; ++i) | |
651 if (viminfo_history[type] != NULL) | |
652 vim_free(viminfo_history[type][i].hisstr); | |
653 VIM_CLEAR(viminfo_history[type]); | |
654 viminfo_hisidx[type] = 0; | |
655 } | |
656 } | |
657 #endif // FEAT_VIMINFO | |
171 | 658 |
172 static void | 659 static void |
173 write_viminfo_barlines(vir_T *virp, FILE *fp_out) | 660 write_viminfo_barlines(vir_T *virp, FILE *fp_out) |
174 { | 661 { |
175 int i; | 662 int i; |