diff options
author | Bram Moolenaar <Bram@vim.org> | 2019-07-21 21:51:59 +0200 |
---|---|---|
committer | Bram Moolenaar <Bram@vim.org> | 2019-07-21 21:51:59 +0200 |
commit | 5f32ece459d1f310b1b48b72e07dcd77d3261a76 (patch) | |
tree | 32f770f03d408817c017596ae1836f2e29ebde32 /src/viminfo.c | |
parent | defa067c54874dd987121dd7252c62755e0aebfa (diff) | |
download | vim-git-5f32ece459d1f310b1b48b72e07dcd77d3261a76.tar.gz |
patch 8.1.1728: wrong place for command line history viminfo supportv8.1.1728
Problem: Wrong place for command line history viminfo support.
Solution: Move it to viminfo.c.
Diffstat (limited to 'src/viminfo.c')
-rw-r--r-- | src/viminfo.c | 487 |
1 files changed, 487 insertions, 0 deletions
diff --git a/src/viminfo.c b/src/viminfo.c index 3d3a410f9..d85ad119b 100644 --- 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) { |