# HG changeset patch # User Christian Brabandt # Date 1465240507 -7200 # Node ID 636cfa97200eb09f1a8cf489c2f0596b1f65deb7 # Parent a744b63c4ed085748ebf4db209c6cf94ea43c71a commit https://github.com/vim/vim/commit/45d2eeaad66939348893b9254171067b0457cd9d Author: Bram Moolenaar Date: Mon Jun 6 21:07:52 2016 +0200 patch 7.4.1903 Problem: When writing viminfo merging current history with history in viminfo may drop recent history entries. Solution: Add new format for viminfo lines, use it for history entries. Use a timestamp for ordering the entries. Add test_settime(). Add the viminfo version. Does not do merging on timestamp yet. diff --git a/src/eval.c b/src/eval.c --- a/src/eval.c +++ b/src/eval.c @@ -820,6 +820,7 @@ static void f_test_null_job(typval_T *ar static void f_test_null_list(typval_T *argvars, typval_T *rettv); static void f_test_null_partial(typval_T *argvars, typval_T *rettv); static void f_test_null_string(typval_T *argvars, typval_T *rettv); +static void f_test_settime(typval_T *argvars, typval_T *rettv); #ifdef FEAT_FLOAT static void f_tan(typval_T *argvars, typval_T *rettv); static void f_tanh(typval_T *argvars, typval_T *rettv); @@ -8809,13 +8810,14 @@ static struct fst #ifdef FEAT_JOB_CHANNEL {"test_null_channel", 0, 0, f_test_null_channel}, #endif - {"test_null_dict", 0, 0, f_test_null_dict}, + {"test_null_dict", 0, 0, f_test_null_dict}, #ifdef FEAT_JOB_CHANNEL - {"test_null_job", 0, 0, f_test_null_job}, -#endif - {"test_null_list", 0, 0, f_test_null_list}, + {"test_null_job", 0, 0, f_test_null_job}, +#endif + {"test_null_list", 0, 0, f_test_null_list}, {"test_null_partial", 0, 0, f_test_null_partial}, {"test_null_string", 0, 0, f_test_null_string}, + {"test_settime", 1, 1, f_test_settime}, #ifdef FEAT_TIMERS {"timer_start", 2, 3, f_timer_start}, {"timer_stop", 1, 1, f_timer_stop}, @@ -20849,7 +20851,7 @@ f_test_garbagecollect_now(typval_T *argv #ifdef FEAT_JOB_CHANNEL static void -f_test_null_channel(typval_T *argvars UNUSED, typval_T *rettv UNUSED) +f_test_null_channel(typval_T *argvars UNUSED, typval_T *rettv) { rettv->v_type = VAR_CHANNEL; rettv->vval.v_channel = NULL; @@ -20857,7 +20859,7 @@ f_test_null_channel(typval_T *argvars UN #endif static void -f_test_null_dict(typval_T *argvars UNUSED, typval_T *rettv UNUSED) +f_test_null_dict(typval_T *argvars UNUSED, typval_T *rettv) { rettv->v_type = VAR_DICT; rettv->vval.v_dict = NULL; @@ -20865,7 +20867,7 @@ f_test_null_dict(typval_T *argvars UNUSE #ifdef FEAT_JOB_CHANNEL static void -f_test_null_job(typval_T *argvars UNUSED, typval_T *rettv UNUSED) +f_test_null_job(typval_T *argvars UNUSED, typval_T *rettv) { rettv->v_type = VAR_JOB; rettv->vval.v_job = NULL; @@ -20873,26 +20875,32 @@ f_test_null_job(typval_T *argvars UNUSED #endif static void -f_test_null_list(typval_T *argvars UNUSED, typval_T *rettv UNUSED) +f_test_null_list(typval_T *argvars UNUSED, typval_T *rettv) { rettv->v_type = VAR_LIST; rettv->vval.v_list = NULL; } static void -f_test_null_partial(typval_T *argvars UNUSED, typval_T *rettv UNUSED) +f_test_null_partial(typval_T *argvars UNUSED, typval_T *rettv) { rettv->v_type = VAR_PARTIAL; rettv->vval.v_partial = NULL; } static void -f_test_null_string(typval_T *argvars UNUSED, typval_T *rettv UNUSED) +f_test_null_string(typval_T *argvars UNUSED, typval_T *rettv) { rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; } + static void +f_test_settime(typval_T *argvars, typval_T *rettv UNUSED) +{ + time_for_testing = (time_t)get_tv_number(&argvars[0]); +} + #if defined(FEAT_JOB_CHANNEL) || defined(FEAT_TIMERS) || defined(PROTO) /* * Get a callback from "arg". It can be a Funcref or a function name. diff --git a/src/ex_cmds.c b/src/ex_cmds.c --- a/src/ex_cmds.c +++ b/src/ex_cmds.c @@ -1750,9 +1750,14 @@ append_redir( #if defined(FEAT_VIMINFO) || defined(PROTO) static int no_viminfo(void); +static int read_viminfo_barline(vir_T *virp, int got_encoding, int writing); +static void write_viminfo_version(FILE *fp_out); static void write_viminfo_barlines(vir_T *virp, FILE *fp_out); static int viminfo_errcnt; +#define VIMINFO_VERSION 2 +#define VIMINFO_VERSION_WITH_HISTORY 2 + static int no_viminfo(void) { @@ -2156,6 +2161,7 @@ do_viminfo(FILE *fp_in, FILE *fp_out, in vir.vir_conv.vc_type = CONV_NONE; #endif ga_init2(&vir.vir_barlines, (int)sizeof(char_u *), 100); + vir.vir_version = -1; if (fp_in != NULL) { @@ -2177,6 +2183,7 @@ do_viminfo(FILE *fp_in, FILE *fp_out, in fprintf(fp_out, _("# This viminfo file was generated by Vim %s.\n"), VIM_VERSION_MEDIUM); fputs(_("# You may edit it if you're careful!\n\n"), fp_out); + write_viminfo_version(fp_out); #ifdef FEAT_MBYTE fputs(_("# Value of 'encoding' when this file was written\n"), fp_out); fprintf(fp_out, "*encoding=%s\n\n", p_enc); @@ -2220,6 +2227,7 @@ read_viminfo_up_to_marks( { int eof; buf_T *buf; + int got_encoding = FALSE; #ifdef FEAT_CMDHIST prepare_viminfo_history(forceit ? 9999 : 0, writing); @@ -2240,12 +2248,11 @@ read_viminfo_up_to_marks( case '#': eof = viminfo_readline(virp); break; - case '|': /* copy line (for future use) */ - if (writing) - ga_add_string(&virp->vir_barlines, virp->vir_line); - eof = viminfo_readline(virp); + case '|': + eof = read_viminfo_barline(virp, got_encoding, writing); break; case '*': /* "*encoding=value" */ + got_encoding = TRUE; eof = viminfo_encoding(virp); break; case '!': /* global variable */ @@ -2274,10 +2281,13 @@ read_viminfo_up_to_marks( case '=': case '@': #ifdef FEAT_CMDHIST - eof = read_viminfo_history(virp, writing); -#else - eof = viminfo_readline(virp); -#endif + /* When history is in bar lines skip the old style history + * lines. */ + if (virp->vir_version < VIMINFO_VERSION_WITH_HISTORY) + eof = read_viminfo_history(virp, writing); + else +#endif + eof = viminfo_readline(virp); break; case '-': case '\'': @@ -2347,8 +2357,8 @@ viminfo_readline(vir_T *virp) } /* - * check string read from viminfo file - * remove '\n' at the end of the line + * Check string read from viminfo file. + * Remove '\n' at the end of the line. * - replace CTRL-V CTRL-V with CTRL-V * - replace CTRL-V 'n' with '\n' * @@ -2463,6 +2473,283 @@ viminfo_writestring(FILE *fd, char_u *p) putc('\n', fd); } +/* + * Write a string in quotes that barline_parse() can read back. + * Breaks the line in less than LSIZE pieces when needed. + * Returns remaining characters in the line. + */ + int +barline_writestring(FILE *fd, char_u *s, int remaining_start) +{ + char_u *p; + int remaining = remaining_start; + int len = 2; + + /* Count the number of characters produced, including quotes. */ + for (p = s; *p != NUL; ++p) + { + if (*p == NL) + len += 2; + else if (*p == '"' || *p == '\\') + len += 2; + else + ++len; + } + if (len > remaining) + { + fprintf(fd, ">%d\n|<", len); + remaining = LSIZE - 20; + } + + putc('"', fd); + for (p = s; *p != NUL; ++p) + { + if (*p == NL) + { + putc('\\', fd); + putc('n', fd); + --remaining; + } + else if (*p == '"' || *p == '\\') + { + putc('\\', fd); + putc(*p, fd); + --remaining; + } + else + putc(*p, fd); + --remaining; + + if (remaining < 3) + { + putc('\n', fd); + putc('|', fd); + putc('<', fd); + /* Leave enough space for another continuation. */ + remaining = LSIZE - 20; + } + } + putc('"', fd); + return remaining; +} + +/* + * Parse a viminfo line starting with '|'. + * Put each decoded value in "values" and return the number of values found. + */ + static int +barline_parse(vir_T *virp, char_u *text, bval_T *values) +{ + char_u *p = text; + char_u *nextp = NULL; + char_u *buf = NULL;; + int count = 0; + int i; + int allocated = FALSE; + + while (*p == ',') + { + if (count == BVAL_MAX) + { + EMSG2(e_intern2, "barline_parse()"); + break; + } + ++p; + + if (*p == '>') + { + /* Need to read a continuation line. Need to put strings in + * allocated memory, because virp->vir_line is overwritten. */ + if (!allocated) + { + for (i = 0; i < count; ++i) + if (values[i].bv_type == BVAL_STRING) + { + values[i].bv_string = vim_strnsave( + values[i].bv_string, values[i].bv_len); + values[i].bv_allocated = TRUE; + } + allocated = TRUE; + } + + if (vim_isdigit(p[1])) + { + int len; + int todo; + int n; + + /* String value was split into lines that are each shorter + * than LSIZE: + * |{bartype},>{length of "{text}{text2}"} + * |<"{text1} + * |<{text2}",{value} + */ + ++p; + len = getdigits(&p); + buf = alloc(len + 1); + p = buf; + for (todo = len; todo > 0; todo -= n) + { + if (viminfo_readline(virp) || virp->vir_line[0] != '|' + || virp->vir_line[1] != '<') + /* file was truncated or garbled */ + return 0; + /* Get length of text, excluding |< and NL chars. */ + n = STRLEN(virp->vir_line); + while (n > 0 && (virp->vir_line[n - 1] == NL + || virp->vir_line[n - 1] == CAR)) + --n; + n -= 2; + if (n > todo) + { + /* more values follow after the string */ + nextp = virp->vir_line + 2 + todo; + n = todo; + } + mch_memmove(p, virp->vir_line + 2, n); + p += n; + } + *p = NUL; + p = buf; + } + else + { + /* Line ending in ">" continues in the next line: + * |{bartype},{lots of values},> + * |<{value},{value} + */ + if (viminfo_readline(virp) || virp->vir_line[0] != '|' + || virp->vir_line[1] != '<') + /* file was truncated or garbled */ + return 0; + p = virp->vir_line + 2; + } + } + + if (isdigit(*p)) + { + values[count].bv_type = BVAL_NR; + values[count].bv_nr = getdigits(&p); + ++count; + } + else if (*p == '"') + { + int len = 0; + char_u *s = p; + + /* Unescape special characters in-place. */ + ++p; + while (*p != '"') + { + if (*p == NL || *p == NUL) + return count; /* syntax error, drop the value */ + if (*p == '\\') + { + ++p; + if (*p == 'n') + s[len++] = '\n'; + else + s[len++] = *p; + ++p; + } + else + s[len++] = *p++; + } + s[len] = NUL; + + if (s != buf && allocated) + s = vim_strsave(s); + values[count].bv_string = s; + values[count].bv_type = BVAL_STRING; + values[count].bv_len = len; + values[count].bv_allocated = allocated; + ++count; + if (nextp != NULL) + { + /* values following a long string */ + p = nextp; + nextp = NULL; + } + } + else if (*p == ',') + { + values[count].bv_type = BVAL_EMPTY; + ++count; + } + else + break; + } + + return count; +} + + static int +read_viminfo_barline(vir_T *virp, int got_encoding, int writing) +{ + char_u *p = virp->vir_line + 1; + int bartype; + bval_T values[BVAL_MAX]; + int count = 0; + int i; + + /* The format is: |{bartype},{value},... + * For a very long string: + * |{bartype},>{length of "{text}{text2}"} + * |<{text1} + * |<{text2},{value} + * For a long line not using a string + * |{bartype},{lots of values},> + * |<{value},{value} + */ + if (*p == '<') + { + /* Continuation line of an unrecognized item. */ + if (writing) + ga_add_string(&virp->vir_barlines, virp->vir_line); + } + else + { + bartype = getdigits(&p); + switch (bartype) + { + case BARTYPE_VERSION: + /* Only use the version when it comes before the encoding. + * If it comes later it was copied by a Vim version that + * doesn't understand the version. */ + if (!got_encoding) + { + count = barline_parse(virp, p, values); + if (count > 0 && values[0].bv_type == BVAL_NR) + virp->vir_version = values[0].bv_nr; + } + break; + + case BARTYPE_HISTORY: + count = barline_parse(virp, p, values); + handle_viminfo_history(values, count, writing); + break; + + default: + /* copy unrecognized line (for future use) */ + if (writing) + ga_add_string(&virp->vir_barlines, virp->vir_line); + } + } + + for (i = 0; i < count; ++i) + if (values[i].bv_type == BVAL_STRING && values[i].bv_allocated) + vim_free(values[i].bv_string); + + return viminfo_readline(virp); +} + + static void +write_viminfo_version(FILE *fp_out) +{ + fprintf(fp_out, "# Viminfo version\n|%d,%d\n\n", + BARTYPE_VERSION, VIMINFO_VERSION); +} + static void write_viminfo_barlines(vir_T *virp, FILE *fp_out) { diff --git a/src/ex_getln.c b/src/ex_getln.c --- a/src/ex_getln.c +++ b/src/ex_getln.c @@ -58,6 +58,7 @@ 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}; @@ -5407,6 +5408,20 @@ static char *(history_names[]) = NULL }; +/* + * Return the current time in seconds. Calls time(), unless test_settime() + * was used. + */ + static time_t +vim_time(void) +{ +#ifdef FEAT_EVAL + return time_for_testing == 0 ? time(NULL) : time_for_testing; +#else + return time(NULL); +#endif +} + #if defined(FEAT_CMDL_COMPL) || defined(PROTO) /* * Function given to ExpandGeneric() to obtain the possible first @@ -5576,6 +5591,7 @@ in_history( history[type][i].hisnum = ++hisnum[type]; history[type][i].viminfo = FALSE; history[type][i].hisstr = str; + history[type][i].time_set = vim_time(); return TRUE; } return FALSE; @@ -5663,6 +5679,7 @@ add_to_history( hisptr->hisnum = ++hisnum[histype]; hisptr->viminfo = FALSE; + hisptr->time_set = vim_time(); if (histype == HIST_SEARCH && in_map) last_maptick = maptick; } @@ -6131,9 +6148,10 @@ ex_history(exarg_T *eap) /* * Buffers for history read from a viminfo file. Only valid while reading. */ -static char_u **viminfo_history[HIST_COUNT] = {NULL, NULL, NULL, NULL}; -static int viminfo_hisidx[HIST_COUNT] = {0, 0, 0, 0}; -static int viminfo_hislen[HIST_COUNT] = {0, 0, 0, 0}; +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; static int hist_type2char(int type, int use_question); @@ -6191,8 +6209,8 @@ prepare_viminfo_history(int asklen, int if (len <= 0) viminfo_history[type] = NULL; else - viminfo_history[type] = - (char_u **)lalloc((long_u)(len * sizeof(char_u *)), FALSE); + viminfo_history[type] = (histentry_T *)lalloc( + (long_u)(len * sizeof(histentry_T)), FALSE); if (viminfo_history[type] == NULL) len = 0; viminfo_hislen[type] = len; @@ -6242,7 +6260,9 @@ read_viminfo_history(vir_T *virp, int wr mch_memmove(p, val, (size_t)len + 1); p[len + 1] = NUL; } - viminfo_history[type][viminfo_hisidx[type]++] = p; + viminfo_history[type][viminfo_hisidx[type]].hisstr = p; + viminfo_history[type][viminfo_hisidx[type]].time_set = 0; + viminfo_hisidx[type]++; } } } @@ -6252,6 +6272,81 @@ read_viminfo_history(vir_T *virp, int wr } /* + * Accept a new style history line from the viminfo, store it in the history + * array when it's new. + */ + void +handle_viminfo_history( + bval_T *values, + int count, + int writing) +{ + int type; + long_u len; + char_u *val; + char_u *p; + + /* Check the format: + * |{bartype},{histtype},{timestamp},{separator},"text" */ + if (count < 4 + || values[0].bv_type != BVAL_NR + || values[1].bv_type != BVAL_NR + || (values[2].bv_type != BVAL_NR && values[2].bv_type != BVAL_EMPTY) + || values[3].bv_type != BVAL_STRING) + return; + + type = values[0].bv_nr; + if (type >= HIST_COUNT) + return; + if (viminfo_hisidx[type] < viminfo_hislen[type]) + { + val = values[3].bv_string; + if (val != NULL && *val != NUL) + { + int sep = type == HIST_SEARCH && values[2].bv_type == BVAL_NR + ? values[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 = values[3].bv_len; + p = lalloc(len + 2, TRUE); + } + if (p != NULL) + { + viminfo_history[type][idx].time_set = values[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_hisidx[type]++; + } + } + } + } + } +} + +/* * Finish reading history lines from viminfo. Not used when writing viminfo. */ void @@ -6290,8 +6385,9 @@ finish_viminfo_history(void) for (i = 0; i < viminfo_hisidx[type]; i++) { vim_free(history[type][idx].hisstr); - history[type][idx].hisstr = viminfo_history[type][i]; + 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; } @@ -6315,15 +6411,11 @@ finish_viminfo_history(void) * When "merge" is FALSE just write all history lines. Used for ":wviminfo!". */ void -write_viminfo_history( - FILE *fp, - int merge) +write_viminfo_history(FILE *fp, int merge) { int i; int type; int num_saved; - char_u *p; - int c; int round; init_history(); @@ -6339,8 +6431,9 @@ write_viminfo_history( fprintf(fp, _("\n# %s History (newest to oldest):\n"), type == HIST_CMD ? _("Command Line") : type == HIST_SEARCH ? _("Search String") : - type == HIST_EXPR ? _("Expression") : - _("Input Line")); + type == HIST_EXPR ? _("Expression") : + type == HIST_INPUT ? _("Input Line") : + _("Debug Line")); if (num_saved > hislen) num_saved = hislen; @@ -6364,9 +6457,23 @@ write_viminfo_history( while (num_saved > 0 && !(round == 2 && i >= viminfo_hisidx[type])) { - p = round == 1 ? history[type][i].hisstr - : viminfo_history[type] == NULL ? NULL - : viminfo_history[type][i]; + 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)) @@ -6381,6 +6488,21 @@ write_viminfo_history( 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) { @@ -6400,7 +6522,7 @@ write_viminfo_history( } for (i = 0; i < viminfo_hisidx[type]; ++i) if (viminfo_history[type] != NULL) - vim_free(viminfo_history[type][i]); + vim_free(viminfo_history[type][i].hisstr); vim_free(viminfo_history[type]); viminfo_history[type] = NULL; viminfo_hisidx[type] = 0; diff --git a/src/globals.h b/src/globals.h --- a/src/globals.h +++ b/src/globals.h @@ -1639,6 +1639,10 @@ EXTERN int in_free_unref_items INIT(= F EXTERN int did_add_timer INIT(= FALSE); #endif +#ifdef FEAT_EVAL +EXTERN time_t time_for_testing INIT(= 0); +#endif + /* * Optional Farsi support. Include it here, so EXTERN and INIT are defined. */ diff --git a/src/proto/ex_cmds.pro b/src/proto/ex_cmds.pro --- a/src/proto/ex_cmds.pro +++ b/src/proto/ex_cmds.pro @@ -16,6 +16,7 @@ void write_viminfo(char_u *file, int for int viminfo_readline(vir_T *virp); char_u *viminfo_readstring(vir_T *virp, int off, int convert); void viminfo_writestring(FILE *fd, char_u *p); +int barline_writestring(FILE *fd, char_u *s, int remaining_start); void do_fixdel(exarg_T *eap); void print_line_no_prefix(linenr_T lnum, int use_number, int list); void print_line(linenr_T lnum, int use_number, int list); 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 @@ -37,19 +37,20 @@ void init_history(void); 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); -char_u *get_cmdline_str(void); -int get_cmdline_pos(void); -int set_cmdline_pos(int pos); -int get_cmdline_type(void); char_u *get_history_entry(int histype, int idx); int clr_history(int histype); int del_history_entry(int histype, char_u *str); int del_history_idx(int histype, int idx); void remove_key_from_history(void); +char_u *get_cmdline_str(void); +int get_cmdline_pos(void); +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(bval_T *values, int count, int writing); void finish_viminfo_history(void); void write_viminfo_history(FILE *fp, int merge); void cmd_pchar(int c, int offset); diff --git a/src/structs.h b/src/structs.h --- a/src/structs.h +++ b/src/structs.h @@ -1014,6 +1014,7 @@ typedef struct #ifdef FEAT_MBYTE vimconv_T vir_conv; /* encoding conversion */ #endif + int vir_version; /* viminfo version detected or -1 */ garray_T vir_barlines; /* lines starting with | */ } vir_T; diff --git a/src/testdir/test_viminfo.vim b/src/testdir/test_viminfo.vim --- a/src/testdir/test_viminfo.vim +++ b/src/testdir/test_viminfo.vim @@ -1,6 +1,7 @@ " Test for reading and writing .viminfo function Test_read_and_write() + call histdel(':') let lines = [ \ '# comment line', \ '*encoding=utf-8', @@ -18,14 +19,16 @@ function Test_read_and_write() for line in lines if line[0] == '|' if done == 0 + call assert_equal('|1,2', line) + elseif done == 1 call assert_equal('|copied as-is', line) - elseif done == 1 + elseif done == 2 call assert_equal('|and one more', line) endif let done += 1 endif endfor - call assert_equal(2, done) + call assert_equal(3, done) call delete('Xviminfo') endfunc @@ -48,3 +51,68 @@ func Test_global_vars() call delete('Xviminfo') set viminfo-=! endfunc + +func Test_cmdline_history() + call histdel(':') + call test_settime(11) + call histadd(':', "echo 'one'") + call test_settime(12) + " split into two lines + let long800 = repeat(" 'eight'", 100) + call histadd(':', "echo " . long800) + call test_settime(13) + " split into three lines + let long1400 = repeat(" 'fourteeeeen'", 100) + call histadd(':', "echo " . long1400) + wviminfo Xviminfo + let lines = readfile('Xviminfo') + let done_colon = 0 + let done_bar = 0 + let lnum = 0 + while lnum < len(lines) + let line = lines[lnum] | let lnum += 1 + if line[0] == ':' + if done_colon == 0 + call assert_equal(":\x161408", line) + let line = lines[lnum] | let lnum += 1 + call assert_equal('1407", line) + let line = lines[lnum] | let lnum += 1 + call assert_equal('|<"echo ' . long1400[0:484], line) + let line = lines[lnum] | let lnum += 1 + call assert_equal('|<' . long1400[485:974], line) + let line = lines[lnum] | let lnum += 1 + call assert_equal('|<' . long1400[975:] . '"', line) + elseif done_bar == 1 + call assert_equal('|2,0,12,,>807', line) + let line = lines[lnum] | let lnum += 1 + call assert_equal('|<"echo ' . long800[0:484], line) + let line = lines[lnum] | let lnum += 1 + call assert_equal('|<' . long800[485:] . '"', line) + elseif done_bar == 2 + call assert_equal("|2,0,11,,\"echo 'one'\"", line) + endif + let done_bar += 1 + endif + endwhile + call assert_equal(3, done_colon) + call assert_equal(3, done_bar) + + call histdel(':') + rviminfo Xviminfo + call assert_equal("echo " . long1400, histget(':', -1)) + call assert_equal("echo " . long800, histget(':', -2)) + call assert_equal("echo 'one'", histget(':', -3)) + + call delete('Xviminfo') +endfunc diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1903, +/**/ 1902, /**/ 1901,