# HG changeset patch # User Bram Moolenaar # Date 1563739205 -7200 # Node ID e43f0c0c491c82ed097eceb45fabb5ae8552cc85 # Parent 02dc4260ddbb3d39ae7044e0d9cf1739d4f8a89b patch 8.1.1728: wrong place for command line history viminfo support commit https://github.com/vim/vim/commit/5f32ece459d1f310b1b48b72e07dcd77d3261a76 Author: Bram Moolenaar 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. diff --git a/src/ex_getln.c b/src/ex_getln.c --- a/src/ex_getln.c +++ b/src/ex_getln.c @@ -60,21 +60,11 @@ static int extra_char = NUL; /* extra c static int extra_char_shift; #ifdef FEAT_CMDHIST -typedef struct hist_entry -{ - int hisnum; /* identifying number */ - int viminfo; /* when TRUE hisstr comes from viminfo */ - char_u *hisstr; /* actual entry, separator char after the NUL */ - time_t time_set; /* when it was typed, zero if unknown */ -} histentry_T; - static histentry_T *(history[HIST_COUNT]) = {NULL, NULL, NULL, NULL, NULL}; static int hisidx[HIST_COUNT] = {-1, -1, -1, -1, -1}; /* lastused entry */ static int hisnum[HIST_COUNT] = {0, 0, 0, 0, 0}; /* identifying (unique) number of newest history entry */ static int hislen = 0; /* actual length of history tables */ - -static int hist_char2type(int c); #endif #ifdef FEAT_RIGHTLEFT @@ -116,9 +106,6 @@ static int ExpandUserDefined(expand_T *x static int ExpandUserList(expand_T *xp, int *num_file, char_u ***file); # endif #endif -#ifdef FEAT_CMDHIST -static void clear_hist_entry(histentry_T *hisptr); -#endif #ifdef FEAT_CMDWIN static int open_cmdwin(void); @@ -5873,7 +5860,7 @@ globpath( /* * Translate a history character to the associated type number. */ - static int + int hist_char2type(int c) { if (c == ':') @@ -6010,7 +5997,7 @@ init_history(void) } } - static void + void clear_hist_entry(histentry_T *hisptr) { hisptr->hisnum = 0; @@ -6023,7 +6010,7 @@ clear_hist_entry(histentry_T *hisptr) * Check if command line 'str' is already in history. * If 'move_to_front' is TRUE, matching entry is moved to end of history. */ - static int + int in_history( int type, char_u *str, @@ -6629,478 +6616,36 @@ ex_history(exarg_T *eap) #endif #if (defined(FEAT_VIMINFO) && defined(FEAT_CMDHIST)) || defined(PROTO) -/* - * Buffers for history read from a viminfo file. Only valid while reading. - */ -static histentry_T *viminfo_history[HIST_COUNT] = - {NULL, NULL, NULL, NULL, NULL}; -static int viminfo_hisidx[HIST_COUNT] = {0, 0, 0, 0, 0}; -static int viminfo_hislen[HIST_COUNT] = {0, 0, 0, 0, 0}; -static int viminfo_add_at_front = FALSE; - -/* - * Translate a history type number to the associated character. - */ - static int -hist_type2char( - int type, - int use_question) /* use '?' instead of '/' */ -{ - if (type == HIST_CMD) - return ':'; - if (type == HIST_SEARCH) - { - if (use_question) - return '?'; - else - return '/'; - } - if (type == HIST_EXPR) - return '='; - return '@'; -} - -/* - * Prepare for reading the history from the viminfo file. - * This allocates history arrays to store the read history lines. - */ - void -prepare_viminfo_history(int asklen, int writing) -{ - int i; - int num; - int type; - int len; - - init_history(); - viminfo_add_at_front = (asklen != 0 && !writing); - if (asklen > hislen) - asklen = hislen; - - for (type = 0; type < HIST_COUNT; ++type) - { - /* Count the number of empty spaces in the history list. Entries read - * from viminfo previously are also considered empty. If there are - * more spaces available than we request, then fill them up. */ - for (i = 0, num = 0; i < hislen; i++) - if (history[type][i].hisstr == NULL || history[type][i].viminfo) - num++; - len = asklen; - if (num > len) - len = num; - if (len <= 0) - viminfo_history[type] = NULL; - else - viminfo_history[type] = LALLOC_MULT(histentry_T, len); - if (viminfo_history[type] == NULL) - len = 0; - viminfo_hislen[type] = len; - viminfo_hisidx[type] = 0; - } -} - -/* - * Accept a line from the viminfo, store it in the history array when it's - * new. - */ int -read_viminfo_history(vir_T *virp, int writing) +get_hislen(void) { - int type; - long_u len; - char_u *val; - char_u *p; - - type = hist_char2type(virp->vir_line[0]); - if (viminfo_hisidx[type] < viminfo_hislen[type]) - { - val = viminfo_readstring(virp, 1, TRUE); - if (val != NULL && *val != NUL) - { - int sep = (*val == ' ' ? NUL : *val); - - if (!in_history(type, val + (type == HIST_SEARCH), - viminfo_add_at_front, sep, writing)) - { - /* Need to re-allocate to append the separator byte. */ - len = STRLEN(val); - p = alloc(len + 2); - if (p != NULL) - { - if (type == HIST_SEARCH) - { - /* Search entry: Move the separator from the first - * column to after the NUL. */ - mch_memmove(p, val + 1, (size_t)len); - p[len] = sep; - } - else - { - /* Not a search entry: No separator in the viminfo - * file, add a NUL separator. */ - mch_memmove(p, val, (size_t)len + 1); - p[len + 1] = NUL; - } - viminfo_history[type][viminfo_hisidx[type]].hisstr = p; - viminfo_history[type][viminfo_hisidx[type]].time_set = 0; - viminfo_history[type][viminfo_hisidx[type]].viminfo = TRUE; - viminfo_history[type][viminfo_hisidx[type]].hisnum = 0; - viminfo_hisidx[type]++; - } - } - } - vim_free(val); - } - return viminfo_readline(virp); + return hislen; } -/* - * Accept a new style history line from the viminfo, store it in the history - * array when it's new. - */ - void -handle_viminfo_history( - garray_T *values, - int writing) + histentry_T * +get_histentry(int hist_type) { - int type; - long_u len; - char_u *val; - char_u *p; - bval_T *vp = (bval_T *)values->ga_data; - - /* Check the format: - * |{bartype},{histtype},{timestamp},{separator},"text" */ - if (values->ga_len < 4 - || vp[0].bv_type != BVAL_NR - || vp[1].bv_type != BVAL_NR - || (vp[2].bv_type != BVAL_NR && vp[2].bv_type != BVAL_EMPTY) - || vp[3].bv_type != BVAL_STRING) - return; - - type = vp[0].bv_nr; - if (type >= HIST_COUNT) - return; - if (viminfo_hisidx[type] < viminfo_hislen[type]) - { - val = vp[3].bv_string; - if (val != NULL && *val != NUL) - { - int sep = type == HIST_SEARCH && vp[2].bv_type == BVAL_NR - ? vp[2].bv_nr : NUL; - int idx; - int overwrite = FALSE; - - if (!in_history(type, val, viminfo_add_at_front, sep, writing)) - { - /* If lines were written by an older Vim we need to avoid - * getting duplicates. See if the entry already exists. */ - for (idx = 0; idx < viminfo_hisidx[type]; ++idx) - { - p = viminfo_history[type][idx].hisstr; - if (STRCMP(val, p) == 0 - && (type != HIST_SEARCH || sep == p[STRLEN(p) + 1])) - { - overwrite = TRUE; - break; - } - } - - if (!overwrite) - { - /* Need to re-allocate to append the separator byte. */ - len = vp[3].bv_len; - p = alloc(len + 2); - } - else - len = 0; /* for picky compilers */ - if (p != NULL) - { - viminfo_history[type][idx].time_set = vp[1].bv_nr; - if (!overwrite) - { - mch_memmove(p, val, (size_t)len + 1); - /* Put the separator after the NUL. */ - p[len + 1] = sep; - viminfo_history[type][idx].hisstr = p; - viminfo_history[type][idx].hisnum = 0; - viminfo_history[type][idx].viminfo = TRUE; - viminfo_hisidx[type]++; - } - } - } - } - } + return history[hist_type]; } -/* - * Concatenate history lines from viminfo after the lines typed in this Vim. - */ - static void -concat_history(int type) + void +set_histentry(int hist_type, histentry_T *entry) { - int idx; - int i; - - idx = hisidx[type] + viminfo_hisidx[type]; - if (idx >= hislen) - idx -= hislen; - else if (idx < 0) - idx = hislen - 1; - if (viminfo_add_at_front) - hisidx[type] = idx; - else - { - if (hisidx[type] == -1) - hisidx[type] = hislen - 1; - do - { - if (history[type][idx].hisstr != NULL - || history[type][idx].viminfo) - break; - if (++idx == hislen) - idx = 0; - } while (idx != hisidx[type]); - if (idx != hisidx[type] && --idx < 0) - idx = hislen - 1; - } - for (i = 0; i < viminfo_hisidx[type]; i++) - { - vim_free(history[type][idx].hisstr); - history[type][idx].hisstr = viminfo_history[type][i].hisstr; - history[type][idx].viminfo = TRUE; - history[type][idx].time_set = viminfo_history[type][i].time_set; - if (--idx < 0) - idx = hislen - 1; - } - idx += 1; - idx %= hislen; - for (i = 0; i < viminfo_hisidx[type]; i++) - { - history[type][idx++].hisnum = ++hisnum[type]; - idx %= hislen; - } -} - -#if defined(FEAT_CMDL_COMPL) || defined(PROTO) - static int -sort_hist(const void *s1, const void *s2) -{ - histentry_T *p1 = *(histentry_T **)s1; - histentry_T *p2 = *(histentry_T **)s2; - - if (p1->time_set < p2->time_set) return -1; - if (p1->time_set > p2->time_set) return 1; - return 0; + history[hist_type] = entry; } -#endif - -/* - * Merge history lines from viminfo and lines typed in this Vim based on the - * timestamp; - */ - static void -merge_history(int type) -{ - int max_len; - histentry_T **tot_hist; - histentry_T *new_hist; - int i; - int len; - - /* Make one long list with all entries. */ - max_len = hislen + viminfo_hisidx[type]; - tot_hist = ALLOC_MULT(histentry_T *, max_len); - new_hist = ALLOC_MULT(histentry_T, hislen ); - if (tot_hist == NULL || new_hist == NULL) - { - vim_free(tot_hist); - vim_free(new_hist); - return; - } - for (i = 0; i < viminfo_hisidx[type]; i++) - tot_hist[i] = &viminfo_history[type][i]; - len = i; - for (i = 0; i < hislen; i++) - if (history[type][i].hisstr != NULL) - tot_hist[len++] = &history[type][i]; - - /* Sort the list on timestamp. */ - qsort((void *)tot_hist, (size_t)len, sizeof(histentry_T *), sort_hist); - - /* Keep the newest ones. */ - for (i = 0; i < hislen; i++) - { - if (i < len) - { - new_hist[i] = *tot_hist[i]; - tot_hist[i]->hisstr = NULL; - if (new_hist[i].hisnum == 0) - new_hist[i].hisnum = ++hisnum[type]; - } - else - clear_hist_entry(&new_hist[i]); - } - hisidx[type] = (i < len ? i : len) - 1; - - /* Free what is not kept. */ - for (i = 0; i < viminfo_hisidx[type]; i++) - vim_free(viminfo_history[type][i].hisstr); - for (i = 0; i < hislen; i++) - vim_free(history[type][i].hisstr); - vim_free(history[type]); - history[type] = new_hist; - vim_free(tot_hist); -} - -/* - * Finish reading history lines from viminfo. Not used when writing viminfo. - */ - void -finish_viminfo_history(vir_T *virp) + + int * +get_hisidx(int hist_type) { - int type; - int merge = virp->vir_version >= VIMINFO_VERSION_WITH_HISTORY; - - for (type = 0; type < HIST_COUNT; ++type) - { - if (history[type] == NULL) - continue; - - if (merge) - merge_history(type); - else - concat_history(type); - - VIM_CLEAR(viminfo_history[type]); - viminfo_hisidx[type] = 0; - } + return &hisidx[hist_type]; } -/* - * Write history to viminfo file in "fp". - * When "merge" is TRUE merge history lines with a previously read viminfo - * file, data is in viminfo_history[]. - * When "merge" is FALSE just write all history lines. Used for ":wviminfo!". - */ - void -write_viminfo_history(FILE *fp, int merge) + int * +get_hisnum(int hist_type) { - int i; - int type; - int num_saved; - int round; - - init_history(); - if (hislen == 0) - return; - for (type = 0; type < HIST_COUNT; ++type) - { - num_saved = get_viminfo_parameter(hist_type2char(type, FALSE)); - if (num_saved == 0) - continue; - if (num_saved < 0) /* Use default */ - num_saved = hislen; - fprintf(fp, _("\n# %s History (newest to oldest):\n"), - type == HIST_CMD ? _("Command Line") : - type == HIST_SEARCH ? _("Search String") : - type == HIST_EXPR ? _("Expression") : - type == HIST_INPUT ? _("Input Line") : - _("Debug Line")); - if (num_saved > hislen) - num_saved = hislen; - - /* - * Merge typed and viminfo history: - * round 1: history of typed commands. - * round 2: history from recently read viminfo. - */ - for (round = 1; round <= 2; ++round) - { - if (round == 1) - /* start at newest entry, somewhere in the list */ - i = hisidx[type]; - else if (viminfo_hisidx[type] > 0) - /* start at newest entry, first in the list */ - i = 0; - else - /* empty list */ - i = -1; - if (i >= 0) - while (num_saved > 0 - && !(round == 2 && i >= viminfo_hisidx[type])) - { - char_u *p; - time_t timestamp; - int c = NUL; - - if (round == 1) - { - p = history[type][i].hisstr; - timestamp = history[type][i].time_set; - } - else - { - p = viminfo_history[type] == NULL ? NULL - : viminfo_history[type][i].hisstr; - timestamp = viminfo_history[type] == NULL ? 0 - : viminfo_history[type][i].time_set; - } - - if (p != NULL && (round == 2 - || !merge - || !history[type][i].viminfo)) - { - --num_saved; - fputc(hist_type2char(type, TRUE), fp); - /* For the search history: put the separator in the - * second column; use a space if there isn't one. */ - if (type == HIST_SEARCH) - { - c = p[STRLEN(p) + 1]; - putc(c == NUL ? ' ' : c, fp); - } - viminfo_writestring(fp, p); - - { - char cbuf[NUMBUFLEN]; - - /* New style history with a bar line. Format: - * |{bartype},{histtype},{timestamp},{separator},"text" */ - if (c == NUL) - cbuf[0] = NUL; - else - sprintf(cbuf, "%d", c); - fprintf(fp, "|%d,%d,%ld,%s,", BARTYPE_HISTORY, - type, (long)timestamp, cbuf); - barline_writestring(fp, p, LSIZE - 20); - putc('\n', fp); - } - } - if (round == 1) - { - /* Decrement index, loop around and stop when back at - * the start. */ - if (--i < 0) - i = hislen - 1; - if (i == hisidx[type]) - break; - } - else - { - /* Increment index. Stop at the end in the while. */ - ++i; - } - } - } - for (i = 0; i < viminfo_hisidx[type]; ++i) - if (viminfo_history[type] != NULL) - vim_free(viminfo_history[type][i].hisstr); - VIM_CLEAR(viminfo_history[type]); - viminfo_hisidx[type] = 0; - } + return &hisnum[hist_type]; } -#endif /* FEAT_VIMINFO */ +#endif #if defined(FEAT_CMDWIN) || defined(PROTO) /* diff --git a/src/proto/ex_getln.pro b/src/proto/ex_getln.pro --- a/src/proto/ex_getln.pro +++ b/src/proto/ex_getln.pro @@ -34,7 +34,10 @@ void set_cmd_context(expand_T *xp, char_ int expand_cmdline(expand_T *xp, char_u *str, int col, int *matchcount, char_u ***matches); int ExpandGeneric(expand_T *xp, regmatch_T *regmatch, int *num_file, char_u ***file, char_u *((*func)(expand_T *, int)), int escaped); void globpath(char_u *path, char_u *file, garray_T *ga, int expand_options); +int hist_char2type(int c); void init_history(void); +void clear_hist_entry(histentry_T *hisptr); +int in_history(int type, char_u *str, int move_to_front, int sep, int writing); int get_histtype(char_u *name); void add_to_history(int histype, char_u *new_entry, int in_map, int sep); int get_history_idx(int histype); @@ -49,10 +52,10 @@ int set_cmdline_pos(int pos); int get_cmdline_type(void); int get_list_range(char_u **str, int *num1, int *num2); void ex_history(exarg_T *eap); -void prepare_viminfo_history(int asklen, int writing); -int read_viminfo_history(vir_T *virp, int writing); -void handle_viminfo_history(garray_T *values, int writing); -void finish_viminfo_history(vir_T *virp); -void write_viminfo_history(FILE *fp, int merge); +int get_hislen(void); +histentry_T *get_histentry(int hist_type); +void set_histentry(int hist_type, histentry_T *entry); +int *get_hisidx(int hist_type); +int *get_hisnum(int hist_type); char_u *script_get(exarg_T *eap, char_u *cmd); /* vim: set ft=c : */ diff --git a/src/structs.h b/src/structs.h --- a/src/structs.h +++ b/src/structs.h @@ -1115,6 +1115,17 @@ typedef struct garray_T vir_barlines; // lines starting with | } vir_T; +/* + * Structure used for the command line history. + */ +typedef struct hist_entry +{ + int hisnum; /* identifying number */ + int viminfo; /* when TRUE hisstr comes from viminfo */ + char_u *hisstr; /* actual entry, separator char after the NUL */ + time_t time_set; /* when it was typed, zero if unknown */ +} histentry_T; + #define CONV_NONE 0 #define CONV_TO_UTF8 1 #define CONV_9_TO_UTF8 2 diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -778,6 +778,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1728, +/**/ 1727, /**/ 1726, diff --git a/src/viminfo.c b/src/viminfo.c --- a/src/viminfo.c +++ b/src/viminfo.c @@ -169,6 +169,493 @@ write_viminfo_bufferlist(FILE *fp) vim_free(line); } +#if defined(FEAT_CMDHIST) || defined(PROTO) +/* + * Buffers for history read from a viminfo file. Only valid while reading. + */ +static histentry_T *viminfo_history[HIST_COUNT] = + {NULL, NULL, NULL, NULL, NULL}; +static int viminfo_hisidx[HIST_COUNT] = {0, 0, 0, 0, 0}; +static int viminfo_hislen[HIST_COUNT] = {0, 0, 0, 0, 0}; +static int viminfo_add_at_front = FALSE; + +/* + * Translate a history type number to the associated character. + */ + static int +hist_type2char( + int type, + int use_question) // use '?' instead of '/' +{ + if (type == HIST_CMD) + return ':'; + if (type == HIST_SEARCH) + { + if (use_question) + return '?'; + else + return '/'; + } + if (type == HIST_EXPR) + return '='; + return '@'; +} + +/* + * Prepare for reading the history from the viminfo file. + * This allocates history arrays to store the read history lines. + */ + static void +prepare_viminfo_history(int asklen, int writing) +{ + int i; + int num; + int type; + int len; + int hislen = get_hislen(); + + init_history(); + viminfo_add_at_front = (asklen != 0 && !writing); + if (asklen > hislen) + asklen = hislen; + + for (type = 0; type < HIST_COUNT; ++type) + { + histentry_T *histentry = get_histentry(type); + + // Count the number of empty spaces in the history list. Entries read + // from viminfo previously are also considered empty. If there are + // more spaces available than we request, then fill them up. + for (i = 0, num = 0; i < hislen; i++) + if (histentry[i].hisstr == NULL || histentry[i].viminfo) + num++; + len = asklen; + if (num > len) + len = num; + if (len <= 0) + viminfo_history[type] = NULL; + else + viminfo_history[type] = LALLOC_MULT(histentry_T, len); + if (viminfo_history[type] == NULL) + len = 0; + viminfo_hislen[type] = len; + viminfo_hisidx[type] = 0; + } +} + +/* + * Accept a line from the viminfo, store it in the history array when it's + * new. + */ + static int +read_viminfo_history(vir_T *virp, int writing) +{ + int type; + long_u len; + char_u *val; + char_u *p; + + type = hist_char2type(virp->vir_line[0]); + if (viminfo_hisidx[type] < viminfo_hislen[type]) + { + val = viminfo_readstring(virp, 1, TRUE); + if (val != NULL && *val != NUL) + { + int sep = (*val == ' ' ? NUL : *val); + + if (!in_history(type, val + (type == HIST_SEARCH), + viminfo_add_at_front, sep, writing)) + { + // Need to re-allocate to append the separator byte. + len = STRLEN(val); + p = alloc(len + 2); + if (p != NULL) + { + if (type == HIST_SEARCH) + { + // Search entry: Move the separator from the first + // column to after the NUL. + mch_memmove(p, val + 1, (size_t)len); + p[len] = sep; + } + else + { + // Not a search entry: No separator in the viminfo + // file, add a NUL separator. + mch_memmove(p, val, (size_t)len + 1); + p[len + 1] = NUL; + } + viminfo_history[type][viminfo_hisidx[type]].hisstr = p; + viminfo_history[type][viminfo_hisidx[type]].time_set = 0; + viminfo_history[type][viminfo_hisidx[type]].viminfo = TRUE; + viminfo_history[type][viminfo_hisidx[type]].hisnum = 0; + viminfo_hisidx[type]++; + } + } + } + vim_free(val); + } + return viminfo_readline(virp); +} + +/* + * Accept a new style history line from the viminfo, store it in the history + * array when it's new. + */ + static void +handle_viminfo_history( + garray_T *values, + int writing) +{ + int type; + long_u len; + char_u *val; + char_u *p; + bval_T *vp = (bval_T *)values->ga_data; + + // Check the format: + // |{bartype},{histtype},{timestamp},{separator},"text" + if (values->ga_len < 4 + || vp[0].bv_type != BVAL_NR + || vp[1].bv_type != BVAL_NR + || (vp[2].bv_type != BVAL_NR && vp[2].bv_type != BVAL_EMPTY) + || vp[3].bv_type != BVAL_STRING) + return; + + type = vp[0].bv_nr; + if (type >= HIST_COUNT) + return; + if (viminfo_hisidx[type] < viminfo_hislen[type]) + { + val = vp[3].bv_string; + if (val != NULL && *val != NUL) + { + int sep = type == HIST_SEARCH && vp[2].bv_type == BVAL_NR + ? vp[2].bv_nr : NUL; + int idx; + int overwrite = FALSE; + + if (!in_history(type, val, viminfo_add_at_front, sep, writing)) + { + // If lines were written by an older Vim we need to avoid + // getting duplicates. See if the entry already exists. + for (idx = 0; idx < viminfo_hisidx[type]; ++idx) + { + p = viminfo_history[type][idx].hisstr; + if (STRCMP(val, p) == 0 + && (type != HIST_SEARCH || sep == p[STRLEN(p) + 1])) + { + overwrite = TRUE; + break; + } + } + + if (!overwrite) + { + // Need to re-allocate to append the separator byte. + len = vp[3].bv_len; + p = alloc(len + 2); + } + else + len = 0; // for picky compilers + if (p != NULL) + { + viminfo_history[type][idx].time_set = vp[1].bv_nr; + if (!overwrite) + { + mch_memmove(p, val, (size_t)len + 1); + // Put the separator after the NUL. + p[len + 1] = sep; + viminfo_history[type][idx].hisstr = p; + viminfo_history[type][idx].hisnum = 0; + viminfo_history[type][idx].viminfo = TRUE; + viminfo_hisidx[type]++; + } + } + } + } + } +} + +/* + * Concatenate history lines from viminfo after the lines typed in this Vim. + */ + static void +concat_history(int type) +{ + int idx; + int i; + int hislen = get_hislen(); + histentry_T *histentry = get_histentry(type); + int *hisidx = get_hisidx(type); + int *hisnum = get_hisnum(type); + + idx = *hisidx + viminfo_hisidx[type]; + if (idx >= hislen) + idx -= hislen; + else if (idx < 0) + idx = hislen - 1; + if (viminfo_add_at_front) + *hisidx = idx; + else + { + if (*hisidx == -1) + *hisidx = hislen - 1; + do + { + if (histentry[idx].hisstr != NULL || histentry[idx].viminfo) + break; + if (++idx == hislen) + idx = 0; + } while (idx != *hisidx); + if (idx != *hisidx && --idx < 0) + idx = hislen - 1; + } + for (i = 0; i < viminfo_hisidx[type]; i++) + { + vim_free(histentry[idx].hisstr); + histentry[idx].hisstr = viminfo_history[type][i].hisstr; + histentry[idx].viminfo = TRUE; + histentry[idx].time_set = viminfo_history[type][i].time_set; + if (--idx < 0) + idx = hislen - 1; + } + idx += 1; + idx %= hislen; + for (i = 0; i < viminfo_hisidx[type]; i++) + { + histentry[idx++].hisnum = ++*hisnum; + idx %= hislen; + } +} + + static int +sort_hist(const void *s1, const void *s2) +{ + histentry_T *p1 = *(histentry_T **)s1; + histentry_T *p2 = *(histentry_T **)s2; + + if (p1->time_set < p2->time_set) return -1; + if (p1->time_set > p2->time_set) return 1; + return 0; +} + +/* + * Merge history lines from viminfo and lines typed in this Vim based on the + * timestamp; + */ + static void +merge_history(int type) +{ + int max_len; + histentry_T **tot_hist; + histentry_T *new_hist; + int i; + int len; + int hislen = get_hislen(); + histentry_T *histentry = get_histentry(type); + int *hisidx = get_hisidx(type); + int *hisnum = get_hisnum(type); + + // Make one long list with all entries. + max_len = hislen + viminfo_hisidx[type]; + tot_hist = ALLOC_MULT(histentry_T *, max_len); + new_hist = ALLOC_MULT(histentry_T, hislen ); + if (tot_hist == NULL || new_hist == NULL) + { + vim_free(tot_hist); + vim_free(new_hist); + return; + } + for (i = 0; i < viminfo_hisidx[type]; i++) + tot_hist[i] = &viminfo_history[type][i]; + len = i; + for (i = 0; i < hislen; i++) + if (histentry[i].hisstr != NULL) + tot_hist[len++] = &histentry[i]; + + // Sort the list on timestamp. + qsort((void *)tot_hist, (size_t)len, sizeof(histentry_T *), sort_hist); + + // Keep the newest ones. + for (i = 0; i < hislen; i++) + { + if (i < len) + { + new_hist[i] = *tot_hist[i]; + tot_hist[i]->hisstr = NULL; + if (new_hist[i].hisnum == 0) + new_hist[i].hisnum = ++*hisnum; + } + else + clear_hist_entry(&new_hist[i]); + } + *hisidx = (i < len ? i : len) - 1; + + // Free what is not kept. + for (i = 0; i < viminfo_hisidx[type]; i++) + vim_free(viminfo_history[type][i].hisstr); + for (i = 0; i < hislen; i++) + vim_free(histentry[i].hisstr); + vim_free(histentry); + set_histentry(type, new_hist); + vim_free(tot_hist); +} + +/* + * Finish reading history lines from viminfo. Not used when writing viminfo. + */ + static void +finish_viminfo_history(vir_T *virp) +{ + int type; + int merge = virp->vir_version >= VIMINFO_VERSION_WITH_HISTORY; + + for (type = 0; type < HIST_COUNT; ++type) + { + if (get_histentry(type) == NULL) + continue; + + if (merge) + merge_history(type); + else + concat_history(type); + + VIM_CLEAR(viminfo_history[type]); + viminfo_hisidx[type] = 0; + } +} + +/* + * Write history to viminfo file in "fp". + * When "merge" is TRUE merge history lines with a previously read viminfo + * file, data is in viminfo_history[]. + * When "merge" is FALSE just write all history lines. Used for ":wviminfo!". + */ + static void +write_viminfo_history(FILE *fp, int merge) +{ + int i; + int type; + int num_saved; + int round; + int hislen; + + init_history(); + hislen = get_hislen(); + if (hislen == 0) + return; + for (type = 0; type < HIST_COUNT; ++type) + { + histentry_T *histentry = get_histentry(type); + int *hisidx = get_hisidx(type); + + num_saved = get_viminfo_parameter(hist_type2char(type, FALSE)); + if (num_saved == 0) + continue; + if (num_saved < 0) // Use default + num_saved = hislen; + fprintf(fp, _("\n# %s History (newest to oldest):\n"), + type == HIST_CMD ? _("Command Line") : + type == HIST_SEARCH ? _("Search String") : + type == HIST_EXPR ? _("Expression") : + type == HIST_INPUT ? _("Input Line") : + _("Debug Line")); + if (num_saved > hislen) + num_saved = hislen; + + /* + * Merge typed and viminfo history: + * round 1: history of typed commands. + * round 2: history from recently read viminfo. + */ + for (round = 1; round <= 2; ++round) + { + if (round == 1) + // start at newest entry, somewhere in the list + i = *hisidx; + else if (viminfo_hisidx[type] > 0) + // start at newest entry, first in the list + i = 0; + else + // empty list + i = -1; + if (i >= 0) + while (num_saved > 0 + && !(round == 2 && i >= viminfo_hisidx[type])) + { + char_u *p; + time_t timestamp; + int c = NUL; + + if (round == 1) + { + p = histentry[i].hisstr; + timestamp = histentry[i].time_set; + } + else + { + p = viminfo_history[type] == NULL ? NULL + : viminfo_history[type][i].hisstr; + timestamp = viminfo_history[type] == NULL ? 0 + : viminfo_history[type][i].time_set; + } + + if (p != NULL && (round == 2 + || !merge + || !histentry[i].viminfo)) + { + --num_saved; + fputc(hist_type2char(type, TRUE), fp); + // For the search history: put the separator in the + // second column; use a space if there isn't one. + if (type == HIST_SEARCH) + { + c = p[STRLEN(p) + 1]; + putc(c == NUL ? ' ' : c, fp); + } + viminfo_writestring(fp, p); + + { + char cbuf[NUMBUFLEN]; + + // New style history with a bar line. Format: + // |{bartype},{histtype},{timestamp},{separator},"text" + if (c == NUL) + cbuf[0] = NUL; + else + sprintf(cbuf, "%d", c); + fprintf(fp, "|%d,%d,%ld,%s,", BARTYPE_HISTORY, + type, (long)timestamp, cbuf); + barline_writestring(fp, p, LSIZE - 20); + putc('\n', fp); + } + } + if (round == 1) + { + // Decrement index, loop around and stop when back at + // the start. + if (--i < 0) + i = hislen - 1; + if (i == *hisidx) + break; + } + else + { + // Increment index. Stop at the end in the while. + ++i; + } + } + } + for (i = 0; i < viminfo_hisidx[type]; ++i) + if (viminfo_history[type] != NULL) + vim_free(viminfo_history[type][i].hisstr); + VIM_CLEAR(viminfo_history[type]); + viminfo_hisidx[type] = 0; + } +} +#endif // FEAT_VIMINFO + static void write_viminfo_barlines(vir_T *virp, FILE *fp_out) {