mirror of
https://github.com/vim/vim.git
synced 2025-07-26 11:04:33 -04:00
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.
This commit is contained in:
parent
defa067c54
commit
5f32ece459
493
src/ex_getln.c
493
src/ex_getln.c
@ -60,21 +60,11 @@ static int extra_char = NUL; /* extra character to display when redrawing
|
||||
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 *xp, regmatch_T *regmatch, int *num_file,
|
||||
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,479 +6616,37 @@ 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;
|
||||
}
|
||||
|
||||
histentry_T *
|
||||
get_histentry(int hist_type)
|
||||
{
|
||||
return history[hist_type];
|
||||
}
|
||||
|
||||
/*
|
||||
* 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)
|
||||
set_histentry(int hist_type, histentry_T *entry)
|
||||
{
|
||||
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;
|
||||
}
|
||||
history[hist_type] = entry;
|
||||
}
|
||||
|
||||
if (!overwrite)
|
||||
int *
|
||||
get_hisidx(int hist_type)
|
||||
{
|
||||
/* 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 &hisidx[hist_type];
|
||||
}
|
||||
|
||||
/*
|
||||
* Concatenate history lines from viminfo after the lines typed in this Vim.
|
||||
*/
|
||||
static void
|
||||
concat_history(int type)
|
||||
int *
|
||||
get_hisnum(int hist_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;
|
||||
return &hisnum[hist_type];
|
||||
}
|
||||
#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
|
||||
|
@ -34,7 +34,10 @@ void set_cmd_context(expand_T *xp, char_u *str, int len, int col, int use_ccline
|
||||
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 : */
|
||||
|
@ -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
|
||||
|
@ -777,6 +777,8 @@ static char *(features[]) =
|
||||
|
||||
static int included_patches[] =
|
||||
{ /* Add new patch number below this line */
|
||||
/**/
|
||||
1728,
|
||||
/**/
|
||||
1727,
|
||||
/**/
|
||||
|
487
src/viminfo.c
487
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)
|
||||
{
|
||||
|
Loading…
x
Reference in New Issue
Block a user