To: vim_dev@googlegroups.com Subject: Patch 8.1.1728 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.1.1728 Problem: Wrong place for command line history viminfo support. Solution: Move it to viminfo.c. Files: src/ex_getln.c, src/proto/ex_getln.pro, src/viminfo.c, src/structs.h *** ../vim-8.1.1727/src/ex_getln.c 2019-07-16 21:38:48.101430996 +0200 --- src/ex_getln.c 2019-07-21 21:50:54.391963984 +0200 *************** *** 60,80 **** 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 --- 60,70 ---- *************** *** 116,124 **** 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); --- 106,111 ---- *************** *** 5873,5879 **** /* * Translate a history character to the associated type number. */ ! static int hist_char2type(int c) { if (c == ':') --- 5860,5866 ---- /* * Translate a history character to the associated type number. */ ! int hist_char2type(int c) { if (c == ':') *************** *** 6010,6016 **** } } ! static void clear_hist_entry(histentry_T *hisptr) { hisptr->hisnum = 0; --- 5997,6003 ---- } } ! void clear_hist_entry(histentry_T *hisptr) { hisptr->hisnum = 0; *************** *** 6023,6029 **** * 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 in_history( int type, char_u *str, --- 6010,6016 ---- * Check if command line 'str' is already in history. * If 'move_to_front' is TRUE, matching entry is moved to end of history. */ ! int in_history( int type, char_u *str, *************** *** 6629,7107 **** #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) { ! 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. - */ 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; ! ! 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; } #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 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; - } - } - - /* - * 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 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; - } - } - #endif /* FEAT_VIMINFO */ - #if defined(FEAT_CMDWIN) || defined(PROTO) /* * Open a window on the current command line and history. Allow editing in --- 6616,6652 ---- #endif #if (defined(FEAT_VIMINFO) && defined(FEAT_CMDHIST)) || defined(PROTO) ! int ! get_hislen(void) { ! return hislen; } ! histentry_T * ! get_histentry(int hist_type) { ! return history[hist_type]; } void ! set_histentry(int hist_type, histentry_T *entry) { ! history[hist_type] = entry; } ! int * ! get_hisidx(int hist_type) { ! return &hisidx[hist_type]; } ! int * ! get_hisnum(int hist_type) { ! return &hisnum[hist_type]; } #endif #if defined(FEAT_CMDWIN) || defined(PROTO) /* * Open a window on the current command line and history. Allow editing in *** ../vim-8.1.1727/src/proto/ex_getln.pro 2019-07-04 20:26:17.798397726 +0200 --- src/proto/ex_getln.pro 2019-07-21 21:36:44.449022096 +0200 *************** *** 34,40 **** --- 34,43 ---- 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,58 **** 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); char_u *script_get(exarg_T *eap, char_u *cmd); /* vim: set ft=c : */ --- 52,61 ---- int get_cmdline_type(void); int get_list_range(char_u **str, int *num1, int *num2); void ex_history(exarg_T *eap); ! 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 : */ *** ../vim-8.1.1727/src/viminfo.c 2019-07-21 19:25:16.654609424 +0200 --- src/viminfo.c 2019-07-21 21:42:26.870931629 +0200 *************** *** 169,174 **** --- 169,661 ---- 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) { *** ../vim-8.1.1727/src/structs.h 2019-07-21 19:25:16.654609424 +0200 --- src/structs.h 2019-07-21 19:31:33.645009873 +0200 *************** *** 1115,1120 **** --- 1115,1131 ---- 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 *** ../vim-8.1.1727/src/version.c 2019-07-21 19:25:16.654609424 +0200 --- src/version.c 2019-07-21 21:43:16.222637934 +0200 *************** *** 779,780 **** --- 779,782 ---- { /* Add new patch number below this line */ + /**/ + 1728, /**/ -- A year spent in artificial intelligence is enough to make one believe in God. /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\ \\\ an exciting new programming language -- http://www.Zimbu.org /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///