Mercurial > vim
diff src/ex_cmds.c @ 9240:636cfa97200e v7.4.1903
commit https://github.com/vim/vim/commit/45d2eeaad66939348893b9254171067b0457cd9d
Author: Bram Moolenaar <Bram@vim.org>
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.
author | Christian Brabandt <cb@256bit.org> |
---|---|
date | Mon, 06 Jun 2016 21:15:07 +0200 |
parents | 6e80397a592c |
children | 931bbf7b6ee3 |
line wrap: on
line diff
--- 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) {