To: vim_dev@googlegroups.com Subject: Patch 7.4.1903 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ 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. Files: src/eval.c, src/ex_getln.c, src/ex_cmds.c, src/structs.h, src/globals.h, src/proto/ex_cmds.pro, src/proto/ex_getln.pro, src/testdir/test_viminfo.vim *** ../vim-7.4.1902/src/eval.c 2016-06-04 18:49:15.382070039 +0200 --- src/eval.c 2016-06-06 20:06:23.415629375 +0200 *************** *** 820,825 **** --- 820,826 ---- 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,8821 **** #ifdef FEAT_JOB_CHANNEL {"test_null_channel", 0, 0, f_test_null_channel}, #endif ! {"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_partial", 0, 0, f_test_null_partial}, {"test_null_string", 0, 0, f_test_null_string}, #ifdef FEAT_TIMERS {"timer_start", 2, 3, f_timer_start}, {"timer_stop", 1, 1, f_timer_stop}, --- 8810,8823 ---- #ifdef FEAT_JOB_CHANNEL {"test_null_channel", 0, 0, f_test_null_channel}, #endif ! {"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_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,20855 **** #ifdef FEAT_JOB_CHANNEL static void ! f_test_null_channel(typval_T *argvars UNUSED, typval_T *rettv UNUSED) { rettv->v_type = VAR_CHANNEL; rettv->vval.v_channel = NULL; --- 20851,20857 ---- #ifdef FEAT_JOB_CHANNEL static void ! f_test_null_channel(typval_T *argvars UNUSED, typval_T *rettv) { rettv->v_type = VAR_CHANNEL; rettv->vval.v_channel = NULL; *************** *** 20857,20863 **** #endif static void ! f_test_null_dict(typval_T *argvars UNUSED, typval_T *rettv UNUSED) { rettv->v_type = VAR_DICT; rettv->vval.v_dict = NULL; --- 20859,20865 ---- #endif static void ! f_test_null_dict(typval_T *argvars UNUSED, typval_T *rettv) { rettv->v_type = VAR_DICT; rettv->vval.v_dict = NULL; *************** *** 20865,20871 **** #ifdef FEAT_JOB_CHANNEL static void ! f_test_null_job(typval_T *argvars UNUSED, typval_T *rettv UNUSED) { rettv->v_type = VAR_JOB; rettv->vval.v_job = NULL; --- 20867,20873 ---- #ifdef FEAT_JOB_CHANNEL static void ! f_test_null_job(typval_T *argvars UNUSED, typval_T *rettv) { rettv->v_type = VAR_JOB; rettv->vval.v_job = NULL; *************** *** 20873,20898 **** #endif static void ! f_test_null_list(typval_T *argvars UNUSED, typval_T *rettv UNUSED) { rettv->v_type = VAR_LIST; rettv->vval.v_list = NULL; } static void ! f_test_null_partial(typval_T *argvars UNUSED, typval_T *rettv UNUSED) { rettv->v_type = VAR_PARTIAL; rettv->vval.v_partial = NULL; } static void ! f_test_null_string(typval_T *argvars UNUSED, typval_T *rettv UNUSED) { rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; } #if defined(FEAT_JOB_CHANNEL) || defined(FEAT_TIMERS) || defined(PROTO) /* * Get a callback from "arg". It can be a Funcref or a function name. --- 20875,20906 ---- #endif static void ! 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) { rettv->v_type = VAR_PARTIAL; rettv->vval.v_partial = NULL; } static void ! 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. *** ../vim-7.4.1902/src/ex_getln.c 2016-05-07 18:36:44.244210666 +0200 --- src/ex_getln.c 2016-06-06 21:03:36.871582145 +0200 *************** *** 58,63 **** --- 58,64 ---- 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,5412 **** --- 5408,5427 ---- 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,5581 **** --- 5591,5597 ---- 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,5668 **** --- 5679,5685 ---- hisptr->hisnum = ++hisnum[histype]; hisptr->viminfo = FALSE; + hisptr->time_set = vim_time(); if (histype == HIST_SEARCH && in_map) last_maptick = maptick; } *************** *** 6131,6139 **** /* * 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 int viminfo_add_at_front = FALSE; static int hist_type2char(int type, int use_question); --- 6148,6157 ---- /* * 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; static int hist_type2char(int type, int use_question); *************** *** 6191,6198 **** if (len <= 0) viminfo_history[type] = NULL; else ! viminfo_history[type] = ! (char_u **)lalloc((long_u)(len * sizeof(char_u *)), FALSE); if (viminfo_history[type] == NULL) len = 0; viminfo_hislen[type] = len; --- 6209,6216 ---- if (len <= 0) viminfo_history[type] = NULL; else ! 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,6248 **** mch_memmove(p, val, (size_t)len + 1); p[len + 1] = NUL; } ! viminfo_history[type][viminfo_hisidx[type]++] = p; } } } --- 6260,6268 ---- 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_hisidx[type]++; } } } *************** *** 6252,6257 **** --- 6272,6352 ---- } /* + * 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,6297 **** 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].viminfo = TRUE; if (--idx < 0) idx = hislen - 1; } --- 6385,6393 ---- 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; } *************** *** 6315,6329 **** * 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; - char_u *p; - int c; int round; init_history(); --- 6411,6421 ---- * 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(); *************** *** 6339,6346 **** 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")); if (num_saved > hislen) num_saved = hislen; --- 6431,6439 ---- 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; *************** *** 6364,6372 **** 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]; if (p != NULL && (round == 2 || !merge || !history[type][i].viminfo)) --- 6457,6479 ---- 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)) *************** *** 6381,6386 **** --- 6488,6508 ---- 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,6406 **** } for (i = 0; i < viminfo_hisidx[type]; ++i) if (viminfo_history[type] != NULL) ! vim_free(viminfo_history[type][i]); vim_free(viminfo_history[type]); viminfo_history[type] = NULL; viminfo_hisidx[type] = 0; --- 6522,6528 ---- } for (i = 0; i < viminfo_hisidx[type]; ++i) if (viminfo_history[type] != NULL) ! vim_free(viminfo_history[type][i].hisstr); vim_free(viminfo_history[type]); viminfo_history[type] = NULL; viminfo_hisidx[type] = 0; *** ../vim-7.4.1902/src/ex_cmds.c 2016-06-04 22:31:23.969886694 +0200 --- src/ex_cmds.c 2016-06-06 21:06:24.771579836 +0200 *************** *** 1750,1758 **** --- 1750,1763 ---- #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,2161 **** --- 2161,2167 ---- 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,2182 **** --- 2183,2189 ---- 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,2225 **** --- 2227,2233 ---- { int eof; buf_T *buf; + int got_encoding = FALSE; #ifdef FEAT_CMDHIST prepare_viminfo_history(forceit ? 9999 : 0, writing); *************** *** 2240,2251 **** 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); break; case '*': /* "*encoding=value" */ eof = viminfo_encoding(virp); break; case '!': /* global variable */ --- 2248,2258 ---- case '#': eof = viminfo_readline(virp); break; ! 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,2283 **** case '=': case '@': #ifdef FEAT_CMDHIST ! eof = read_viminfo_history(virp, writing); ! #else ! eof = viminfo_readline(virp); #endif break; case '-': case '\'': --- 2281,2293 ---- case '=': case '@': #ifdef FEAT_CMDHIST ! /* 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,2354 **** } /* ! * 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' * --- 2357,2364 ---- } /* ! * 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,2468 **** --- 2473,2755 ---- 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) { *** ../vim-7.4.1902/src/structs.h 2016-06-04 17:17:07.042146086 +0200 --- src/structs.h 2016-06-05 17:28:47.096947955 +0200 *************** *** 1014,1019 **** --- 1014,1020 ---- #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; *** ../vim-7.4.1902/src/globals.h 2016-06-02 14:29:59.136661030 +0200 --- src/globals.h 2016-06-06 19:49:45.187643107 +0200 *************** *** 1639,1644 **** --- 1639,1648 ---- 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. */ *** ../vim-7.4.1902/src/proto/ex_cmds.pro 2016-01-19 13:21:55.837334377 +0100 --- src/proto/ex_cmds.pro 2016-06-05 19:16:00.536859458 +0200 *************** *** 16,21 **** --- 16,22 ---- 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); *** ../vim-7.4.1902/src/proto/ex_getln.pro 2016-01-19 13:21:55.837334377 +0100 --- src/proto/ex_getln.pro 2016-06-05 23:10:08.664666215 +0200 *************** *** 37,55 **** 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); 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 finish_viminfo_history(void); void write_viminfo_history(FILE *fp, int merge); void cmd_pchar(int c, int offset); --- 37,56 ---- 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_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); *** ../vim-7.4.1902/src/testdir/test_viminfo.vim 2016-03-30 20:50:41.905696041 +0200 --- src/testdir/test_viminfo.vim 2016-06-06 20:50:52.899592654 +0200 *************** *** 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,31 **** for line in lines if line[0] == '|' if done == 0 ! call assert_equal('|copied as-is', line) elseif done == 1 call assert_equal('|and one more', line) endif let done += 1 endif endfor ! call assert_equal(2, done) call delete('Xviminfo') endfunc --- 19,34 ---- 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 == 2 call assert_equal('|and one more', line) endif let done += 1 endif endfor ! call assert_equal(3, done) call delete('Xviminfo') endfunc *************** *** 48,50 **** --- 51,118 ---- 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 *** ../vim-7.4.1902/src/version.c 2016-06-05 16:10:49.537012299 +0200 --- src/version.c 2016-06-06 21:02:25.207583131 +0200 *************** *** 755,756 **** --- 755,758 ---- { /* Add new patch number below this line */ + /**/ + 1903, /**/ -- "I simultaneously try to keep my head in the clouds and my feet on the ground. Sometimes it's a stretch, though." -- Larry Wall /// 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 ///