Mercurial > vim
changeset 2280:941ff1cd317a vim73
Add file save counter to undo information. Add undotree() function.
author | Bram Moolenaar <bram@vim.org> |
---|---|
date | Sun, 27 Jun 2010 01:15:55 +0200 |
parents | 420f001db3d2 |
children | e41433ea71df |
files | runtime/doc/eval.txt runtime/doc/tags runtime/doc/todo.txt runtime/doc/undo.txt src/buffer.c src/eval.c src/ex_cmds.c src/fileio.c src/message.c src/proto.h src/proto/eval.pro src/proto/undo.pro src/structs.h src/undo.c src/workshop.c |
diffstat | 15 files changed, 356 insertions(+), 64 deletions(-) [+] |
line wrap: on
line diff
--- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -1944,6 +1944,7 @@ tr( {src}, {fromstr}, {tostr}) String tr trunc( {expr} Float truncate Float {expr} type( {name}) Number type of variable {name} undofile( {name}) String undo file name for {name} +undotree() List undo file tree values( {dict}) List values in {dict} virtcol( {expr}) Number screen column of cursor or mark visualmode( [expr]) String last visual mode used @@ -2349,11 +2350,13 @@ confirm({msg} [, {choices} [, {default} choice this is 1. Note: confirm() is only supported when compiled with dialog support, see |+dialog_con| and |+dialog_gui|. + {msg} is displayed in a |dialog| with {choices} as the alternatives. When {choices} is missing or empty, "&OK" is used (and translated). {msg} is a String, use '\n' to include a newline. Only on some systems the string is wrapped when it doesn't fit. + {choices} is a String, with the individual choices separated by '\n', e.g. > confirm("Save changes?", "&Yes\n&No\n&Cancel") @@ -2363,15 +2366,18 @@ confirm({msg} [, {choices} [, {default} confirm("file has been modified", "&Save\nSave &All") < For the console, the first letter of each choice is used as the default shortcut key. + The optional {default} argument is the number of the choice that is made if the user hits <CR>. Use 1 to make the first choice the default one. Use 0 to not set a default. If {default} is omitted, 1 is used. - The optional {type} argument gives the type of dialog. This - is only used for the icon of the Win32 GUI. It can be one of - these values: "Error", "Question", "Info", "Warning" or - "Generic". Only the first character is relevant. When {type} - is omitted, "Generic" is used. + + The optional {type} argument gives the type of dialog. This + is only used for the icon of the GTK, Mac, Motif and Win32 + GUI. It can be one of these values: "Error", "Question", + "Info", "Warning" or "Generic". Only the first character is + relevant. When {type} is omitted, "Generic" is used. + If the user aborts the dialog by pressing <Esc>, CTRL-C, or another valid interrupt key, confirm() returns 0. @@ -5779,6 +5785,47 @@ undofile({name}) *undofile()* When compiled without the +persistent_undo option this always returns an empty string. +undotree() *undotree()* + Return the current state of the undo tree in a dictionary with + the following items: + "seq_last" The highest undo sequence number used. + "seq_cur" The sequence number of the current position in + the undo tree. This differs from "seq_last" + when some changes were undone. + "time_cur" Time last used for |:earlier| and related + commands. Use |strftime()| to convert to + something readable. + "save_last" Number of the last file write. Zero when no + write yet. + "synced" Non-zero when the last undo block was synced. + This happens when waiting from input from the + user. See |undo-blocks|. + "entries" A list of dictionaries with information about + undo blocks. + + The first item in the "entries" list is the oldest undo item. + Each List item is a Dictionary with these items: + "seq" Undo sequence number. Same as what appears in + |:undolist|. + "time" Timestamp when the change happened. Use + |strftime()| to convert to something readable. + "newhead" Only appears in the item that is the last one + that was added. This marks the last change + and where further changes will be added. + "curhead" Only appears in the item that is the last one + that was undone. This marks the current + position in the undo tree, the block that will + be used by a redo command. When nothing was + undone after the last change this item will + not appear anywhere. + "save" Only appears on the last block before a file + write. The number is the write count. The + first write has number 1, the last one the + "save_last" mentioned above. + "alt" Alternate entry. This is again a List of undo + blocks. Each item may again have an "alt" + item. + values({dict}) *values()* Return a |List| with all the values of {dict}. The |List| is in arbitrary order.
--- a/runtime/doc/tags +++ b/runtime/doc/tags @@ -7890,6 +7890,7 @@ undo-two-ways undo.txt /*undo-two-ways* undo.txt undo.txt /*undo.txt* undo_ftplugin usr_41.txt /*undo_ftplugin* undofile() eval.txt /*undofile()* +undotree() eval.txt /*undotree()* unicode mbyte.txt /*unicode* unix os_unix.txt /*unix* unlisted-buffer windows.txt /*unlisted-buffer*
--- a/runtime/doc/todo.txt +++ b/runtime/doc/todo.txt @@ -1089,14 +1089,11 @@ Vim 7.3: Use register_shell_extension()? Patch from Geoffrey Reilly, 2010 Jun 22 - Patch for conceal feature from Vince, 2010 June 16. - Needs some more testing. -- undofile: keep markers where the file was written/read, so that it's easy to - go back to a saved version of the file: ":earlier 1f" (f for file)? + Needs some more testing, better patch is coming. +- implement ":earlier 1f" (f for file)? Also add ":earlier 1d" (d for day). Something like changenr() to see the "file saved" marker? - Show "file saved" marker in :undolist -- Function to get undo tree: undotree(). List of lists. Each entry is a - dictionary: {'nr': 2, 'time': 1234, 'saved': 1} +- in August remove UF_VERSION_CRYPT_PREV and UF_VERSION_PREV. Patches to include: - Patch for Lisp support with ECL (Mikael Jansson, 2008 Oct 25) - Gvimext patch to support wide file names. (Szabolcs Horvat 2008 Sep 10)
--- a/runtime/doc/undo.txt +++ b/runtime/doc/undo.txt @@ -135,6 +135,7 @@ This is explained in the user manual: |u The "changes" column is the number of changes to this leaf from the root of the tree. The "time" column is the time this change was made. + For more details use the |undotree()| function. *g-* g- Go to older text state. With a count repeat that many
--- a/src/buffer.c +++ b/src/buffer.c @@ -2992,9 +2992,7 @@ fileinfo(fullname, shorthelp, dont_trunc (int)(IOSIZE - (p - buffer)), TRUE); } - len = STRLEN(buffer); - vim_snprintf((char *)buffer + len, IOSIZE - len, - "\"%s%s%s%s%s%s", + vim_snprintf_add((char *)buffer, IOSIZE, "\"%s%s%s%s%s%s", curbufIsChanged() ? (shortmess(SHM_MOD) ? " [+]" : _(" [Modified]")) : " ", (curbuf->b_flags & BF_NOTEDITED) @@ -3021,27 +3019,24 @@ fileinfo(fullname, shorthelp, dont_trunc else n = (int)(((long)curwin->w_cursor.lnum * 100L) / (long)curbuf->b_ml.ml_line_count); - len = STRLEN(buffer); if (curbuf->b_ml.ml_flags & ML_EMPTY) { - vim_snprintf((char *)buffer + len, IOSIZE - len, "%s", _(no_lines_msg)); + vim_snprintf_add((char *)buffer, IOSIZE, "%s", _(no_lines_msg)); } #ifdef FEAT_CMDL_INFO else if (p_ru) { /* Current line and column are already on the screen -- webb */ if (curbuf->b_ml.ml_line_count == 1) - vim_snprintf((char *)buffer + len, IOSIZE - len, - _("1 line --%d%%--"), n); + vim_snprintf_add((char *)buffer, IOSIZE, _("1 line --%d%%--"), n); else - vim_snprintf((char *)buffer + len, IOSIZE - len, - _("%ld lines --%d%%--"), + vim_snprintf_add((char *)buffer, IOSIZE, _("%ld lines --%d%%--"), (long)curbuf->b_ml.ml_line_count, n); } #endif else { - vim_snprintf((char *)buffer + len, IOSIZE - len, + vim_snprintf_add((char *)buffer, IOSIZE, _("line %ld of %ld --%d%%-- col "), (long)curwin->w_cursor.lnum, (long)curbuf->b_ml.ml_line_count, @@ -5043,7 +5038,6 @@ write_viminfo_bufferlist(fp) #endif char_u *line; int max_buffers; - size_t len; if (find_viminfo_parameter('%') == NULL) return; @@ -5079,8 +5073,7 @@ write_viminfo_bufferlist(fp) break; putc('%', fp); home_replace(NULL, buf->b_ffname, line, MAXPATHL, TRUE); - len = STRLEN(line); - vim_snprintf((char *)line + len, len - LINE_BUF_LEN, "\t%ld\t%d", + vim_snprintf_add((char *)line, LINE_BUF_LEN, "\t%ld\t%d", (long)buf->b_last_cursor.lnum, buf->b_last_cursor.col); viminfo_writestring(fp, line);
--- a/src/eval.c +++ b/src/eval.c @@ -445,6 +445,7 @@ static int free_unref_items __ARGS((int static void set_ref_in_ht __ARGS((hashtab_T *ht, int copyID)); static void set_ref_in_list __ARGS((list_T *l, int copyID)); static void set_ref_in_item __ARGS((typval_T *tv, int copyID)); +static int rettv_dict_alloc __ARGS((typval_T *rettv)); static void dict_unref __ARGS((dict_T *d)); static void dict_free __ARGS((dict_T *d, int recurse)); static dictitem_T *dictitem_copy __ARGS((dictitem_T *org)); @@ -731,6 +732,7 @@ static void f_trunc __ARGS((typval_T *ar #endif static void f_type __ARGS((typval_T *argvars, typval_T *rettv)); static void f_undofile __ARGS((typval_T *argvars, typval_T *rettv)); +static void f_undotree __ARGS((typval_T *argvars, typval_T *rettv)); static void f_values __ARGS((typval_T *argvars, typval_T *rettv)); static void f_virtcol __ARGS((typval_T *argvars, typval_T *rettv)); static void f_visualmode __ARGS((typval_T *argvars, typval_T *rettv)); @@ -6785,6 +6787,26 @@ dict_alloc() } /* + * Allocate an empty dict for a return value. + * Returns OK or FAIL. + */ + static int +rettv_dict_alloc(rettv) + typval_T *rettv; +{ + dict_T *d = dict_alloc(); + + if (d == NULL) + return FAIL; + + rettv->vval.v_dict = d; + rettv->v_type = VAR_DICT; + ++d->dv_refcount; + return OK; +} + + +/* * Unreference a Dictionary: decrement the reference count and free it when it * becomes zero. */ @@ -6979,7 +7001,7 @@ dict_copy(orig, deep, copyID) /* * Add item "item" to Dictionary "d". - * Returns FAIL when out of memory and when key already existed. + * Returns FAIL when out of memory and when key already exists. */ int dict_add(d, item) @@ -7026,6 +7048,32 @@ dict_add_nr_str(d, key, nr, str) } /* + * Add a list entry to dictionary "d". + * Returns FAIL when out of memory and when key already exists. + */ + int +dict_add_list(d, key, list) + dict_T *d; + char *key; + list_T *list; +{ + dictitem_T *item; + + item = dictitem_alloc((char_u *)key); + if (item == NULL) + return FAIL; + item->di_tv.v_lock = 0; + item->di_tv.v_type = VAR_LIST; + item->di_tv.vval.v_list = list; + if (dict_add(d, item) == FAIL) + { + dictitem_free(item); + return FAIL; + } + return OK; +} + +/* * Get the number of items in a Dictionary. */ static long @@ -7840,6 +7888,7 @@ static struct fst #endif {"type", 1, 1, f_type}, {"undofile", 1, 1, f_undofile}, + {"undotree", 0, 0, f_undotree}, {"values", 1, 1, f_values}, {"virtcol", 1, 1, f_virtcol}, {"visualmode", 0, 1, f_visualmode}, @@ -17674,6 +17723,35 @@ f_undofile(argvars, rettv) } /* + * "undotree()" function + */ + static void +f_undotree(argvars, rettv) + typval_T *argvars UNUSED; + typval_T *rettv; +{ + if (rettv_dict_alloc(rettv) == OK) + { + dict_T *dict = rettv->vval.v_dict; + list_T *list; + + dict_add_nr_str(dict, "seq_last", curbuf->b_u_seq_last, NULL); + dict_add_nr_str(dict, "seq_cur", curbuf->b_u_seq_cur, NULL); + dict_add_nr_str(dict, "time_cur", (long)curbuf->b_u_time_cur, NULL); + dict_add_nr_str(dict, "save_last", + (long)curbuf->b_u_last_save_nr, NULL); + dict_add_nr_str(dict, "synced", (long)curbuf->b_u_synced, NULL); + + list = list_alloc(); + if (list != NULL) + { + u_eval_tree(curbuf->b_u_oldhead, list); + dict_add_list(dict, "entries", list); + } + } +} + +/* * "values(dict)" function */ static void @@ -17892,12 +17970,9 @@ f_winsaveview(argvars, rettv) { dict_T *dict; - dict = dict_alloc(); - if (dict == NULL) - return; - rettv->v_type = VAR_DICT; - rettv->vval.v_dict = dict; - ++dict->dv_refcount; + if (rettv_dict_alloc(rettv) == FAIL) + return; + dict = rettv->vval.v_dict; dict_add_nr_str(dict, "lnum", (long)curwin->w_cursor.lnum, NULL); dict_add_nr_str(dict, "col", (long)curwin->w_cursor.col, NULL);
--- a/src/ex_cmds.c +++ b/src/ex_cmds.c @@ -5164,8 +5164,6 @@ outofmem: do_sub_msg(count_only) int count_only; /* used 'n' flag for ":s" */ { - int len = 0; - /* * Only report substitutions when: * - more than 'report' substitutions @@ -5177,23 +5175,19 @@ do_sub_msg(count_only) && messaging()) { if (got_int) - { STRCPY(msg_buf, _("(Interrupted) ")); - len = (int)STRLEN(msg_buf); - } if (sub_nsubs == 1) - vim_snprintf((char *)msg_buf + len, sizeof(msg_buf) - len, + vim_snprintf_add((char *)msg_buf, sizeof(msg_buf), "%s", count_only ? _("1 match") : _("1 substitution")); else - vim_snprintf((char *)msg_buf + len, sizeof(msg_buf) - len, + vim_snprintf_add((char *)msg_buf, sizeof(msg_buf), count_only ? _("%ld matches") : _("%ld substitutions"), sub_nsubs); - len = (int)STRLEN(msg_buf); if (sub_nlines == 1) - vim_snprintf((char *)msg_buf + len, sizeof(msg_buf) - len, + vim_snprintf_add((char *)msg_buf, sizeof(msg_buf), "%s", _(" on 1 line")); else - vim_snprintf((char *)msg_buf + len, sizeof(msg_buf) - len, + vim_snprintf_add((char *)msg_buf, sizeof(msg_buf), _(" on %ld lines"), (long)sub_nlines); if (msg(msg_buf)) /* save message to display it after redraw */
--- a/src/fileio.c +++ b/src/fileio.c @@ -4817,11 +4817,8 @@ restore_backup: STRCAT(IObuff, _(" CONVERSION ERROR")); c = TRUE; if (write_info.bw_conv_error_lnum != 0) - { - size_t l = STRLEN(IObuff); - vim_snprintf((char *)IObuff + l, IOSIZE - l, _(" in line %ld;"), + vim_snprintf_add((char *)IObuff, IOSIZE, _(" in line %ld;"), (long)write_info.bw_conv_error_lnum); - } } else if (notconverted) {
--- a/src/message.c +++ b/src/message.c @@ -3973,6 +3973,47 @@ tv_float(tvs, idxp) /* When generating prototypes all of this is skipped, cproto doesn't * understand this. */ #ifndef PROTO + +# ifdef HAVE_STDARG_H +/* Like vim_vsnprintf() but append to the string. */ + int +vim_snprintf_add(char *str, size_t str_m, char *fmt, ...) +{ + va_list ap; + int str_l; + size_t len = STRLEN(str); + size_t space; + + if (str_m <= len) + space = 0; + else + space = str_m - len; + va_start(ap, fmt); + str_l = vim_vsnprintf(str + len, space, fmt, ap, NULL); + va_end(ap); + return str_l; +} +# else +/* Like vim_vsnprintf() but append to the string. */ + int +vim_snprintf_add(str, str_m, fmt, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) + char *str; + size_t str_m; + char *fmt; + long a1, a2, a3, a4, a5, a6, a7, a8, a9, a10; +{ + size_t len = STRLEN(str); + size_t space; + + if (str_m <= len) + space = 0; + else + space = str_m - len; + return vim_vsnprintf(str + len, space, fmt, + a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); +} +# endif + # ifdef HAVE_STDARG_H int vim_snprintf(char *str, size_t str_m, char *fmt, ...)
--- a/src/proto.h +++ b/src/proto.h @@ -110,16 +110,25 @@ int _RTLENTRYF # endif smsg __ARGS((char_u *, ...)); + int # ifdef __BORLANDC__ _RTLENTRYF # endif smsg_attr __ARGS((int, char_u *, ...)); + +int +# ifdef __BORLANDC__ +_RTLENTRYF +# endif +vim_snprintf_add __ARGS((char *, size_t, char *, ...)); + int # ifdef __BORLANDC__ _RTLENTRYF # endif vim_snprintf __ARGS((char *, size_t, char *, ...)); + # if defined(HAVE_STDARG_H) int vim_vsnprintf(char *str, size_t str_m, char *fmt, va_list ap, typval_T *tvs); # endif
--- a/src/proto/eval.pro +++ b/src/proto/eval.pro @@ -56,6 +56,7 @@ dictitem_T *dictitem_alloc __ARGS((char_ void dictitem_free __ARGS((dictitem_T *item)); int dict_add __ARGS((dict_T *d, dictitem_T *item)); int dict_add_nr_str __ARGS((dict_T *d, char *key, long nr, char_u *str)); +int dict_add_list __ARGS((dict_T *d, char *key, list_T *list)); dictitem_T *dict_find __ARGS((dict_T *d, char_u *key, int len)); char_u *get_dict_string __ARGS((dict_T *d, char_u *key, int save)); long get_dict_number __ARGS((dict_T *d, char_u *key));
--- a/src/proto/undo.pro +++ b/src/proto/undo.pro @@ -24,4 +24,5 @@ void u_undoline __ARGS((void)); void u_blockfree __ARGS((buf_T *buf)); int bufIsChanged __ARGS((buf_T *buf)); int curbufIsChanged __ARGS((void)); +void u_eval_tree __ARGS((u_header_T *first_uhp, list_T *list)); /* vim: set ft=c : */
--- a/src/structs.h +++ b/src/structs.h @@ -327,6 +327,7 @@ struct u_header visualinfo_T uh_visual; /* Visual areas before undo/after redo */ #endif time_t uh_time; /* timestamp when the change was made */ + long_u uh_save_nr; /* counter for last time saved */ #ifdef U_DEBUG int uh_magic; /* magic number to check allocation */ #endif @@ -1371,7 +1372,8 @@ struct file_buffer int b_u_synced; /* entry lists are synced */ long b_u_seq_last; /* last used undo sequence number */ long b_u_seq_cur; /* hu_seq of header below which we are now */ - time_t b_u_seq_time; /* uh_time of header below which we are now */ + time_t b_u_time_cur; /* uh_time of header below which we are now */ + long_u b_u_last_save_nr; /* counter for last file write */ /* * variables for "U" command in undo.c
--- a/src/undo.c +++ b/src/undo.c @@ -106,7 +106,7 @@ static size_t fwrite_crypt __ARGS((buf_T static char_u *read_string_decrypt __ARGS((buf_T *buf UNUSED, FILE *fd, int len)); static int serialize_header __ARGS((FILE *fp, buf_T *buf, char_u *hash)); static int serialize_uhp __ARGS((FILE *fp, buf_T *buf, u_header_T *uhp)); -static u_header_T *unserialize_uhp __ARGS((FILE *fp, char_u *file_name)); +static u_header_T *unserialize_uhp __ARGS((FILE *fp, char_u *file_name, int new_version)); static int serialize_uep __ARGS((FILE *fp, buf_T *buf, u_entry_T *uep)); static u_entry_T *unserialize_uep __ARGS((FILE *fp, int *error, char_u *file_name)); static void serialize_pos __ARGS((pos_T pos, FILE *fp)); @@ -473,7 +473,8 @@ u_savecommon(top, bot, newbot) uhp->uh_seq = ++curbuf->b_u_seq_last; curbuf->b_u_seq_cur = uhp->uh_seq; uhp->uh_time = time(NULL); - curbuf->b_u_seq_time = uhp->uh_time + 1; + uhp->uh_save_nr = 0; + curbuf->b_u_time_cur = uhp->uh_time + 1; uhp->uh_walk = 0; uhp->uh_entry = NULL; @@ -671,8 +672,16 @@ nomem: # define UF_HEADER_END_MAGIC 0xe7aa /* magic after last header */ # define UF_ENTRY_MAGIC 0xf518 /* magic at start of entry */ # define UF_ENTRY_END_MAGIC 0x3581 /* magic after last entry */ -# define UF_VERSION 1 /* 2-byte undofile version number */ -# define UF_VERSION_CRYPT 0x8001 /* idem, encrypted */ +# define UF_VERSION_PREV 1 /* 2-byte undofile version number */ +# define UF_VERSION 2 /* 2-byte undofile version number */ +# define UF_VERSION_CRYPT_PREV 0x8001 /* idem, encrypted */ +# define UF_VERSION_CRYPT 0x8002 /* idem, encrypted */ + +/* extra fields for header */ +# define UF_LAST_SAVE_NR 1 + +/* extra fields for uhp */ +# define UHP_SAVE_NR 1 static char_u e_not_open[] = N_("E828: Cannot open undo file for writing: %s"); @@ -918,7 +927,14 @@ serialize_header(fp, buf, hash) put_bytes(fp, (long_u)buf->b_u_numhead, 4); put_bytes(fp, (long_u)buf->b_u_seq_last, 4); put_bytes(fp, (long_u)buf->b_u_seq_cur, 4); - put_time(fp, buf->b_u_seq_time); + put_time(fp, buf->b_u_time_cur); + + /* Optional fields. */ + putc(4, fp); + putc(UF_LAST_SAVE_NR, fp); + put_bytes(fp, (long_u)buf->b_u_last_save_nr, 4); + + putc(0, fp); /* end marker */ return OK; } @@ -962,6 +978,13 @@ serialize_uhp(fp, buf, uhp) #endif put_time(fp, uhp->uh_time); + /* Optional fields. */ + putc(4, fp); + putc(UHP_SAVE_NR, fp); + put_bytes(fp, (long_u)uhp->uh_save_nr, 4); + + putc(0, fp); /* end marker */ + /* Write all the entries. */ for (uep = uhp->uh_entry; uep != NULL; uep = uep->ue_next) { @@ -974,9 +997,10 @@ serialize_uhp(fp, buf, uhp) } static u_header_T * -unserialize_uhp(fp, file_name) +unserialize_uhp(fp, file_name, new_version) FILE *fp; char_u *file_name; + int new_version; { u_header_T *uhp; int i; @@ -1021,6 +1045,28 @@ unserialize_uhp(fp, file_name) #endif uhp->uh_time = get8ctime(fp); + /* Optional fields. */ + if (new_version) + for (;;) + { + int len = getc(fp); + int what; + + if (len == 0) + break; + what = getc(fp); + switch (what) + { + case UHP_SAVE_NR: + uhp->uh_save_nr = get4c(fp); + break; + default: + /* field not supported, skip */ + while (--len >= 0) + (void)getc(fp); + } + } + /* Unserialize the uep list. */ last_uep = NULL; while ((c = get2c(fp)) == UF_ENTRY_MAGIC) @@ -1398,6 +1444,17 @@ u_write_undo(name, forceit, buf, hash) /* Undo must be synced. */ u_sync(TRUE); + /* Increase the write count, store it in the last undo header, what would + * be used for "u". */ + ++buf->b_u_last_save_nr; + uhp = buf->b_u_curhead; + if (uhp != NULL) + uhp = uhp->uh_next.ptr; + else + uhp = buf->b_u_newhead; + if (uhp != NULL) + uhp->uh_save_nr = buf->b_u_last_save_nr; + /* * Write the header. */ @@ -1496,6 +1553,7 @@ u_read_undo(name, hash, orig_name) char_u *file_name; FILE *fp; long version, str_len; + int new_version; char_u *line_ptr = NULL; linenr_T line_lnum; colnr_T line_colnr; @@ -1503,6 +1561,7 @@ u_read_undo(name, hash, orig_name) int num_head = 0; long old_header_seq, new_header_seq, cur_header_seq; long seq_last, seq_cur; + long_u last_save_nr = 0; short old_idx = -1, new_idx = -1, cur_idx = -1; long num_read_uhps = 0; time_t seq_time; @@ -1575,7 +1634,7 @@ u_read_undo(name, hash, orig_name) goto error; } version = get2c(fp); - if (version == UF_VERSION_CRYPT) + if (version == UF_VERSION_CRYPT || version == UF_VERSION_CRYPT_PREV) { #ifdef FEAT_CRYPT if (*curbuf->b_p_key == NUL) @@ -1595,11 +1654,12 @@ u_read_undo(name, hash, orig_name) goto error; #endif } - else if (version != UF_VERSION) + else if (version != UF_VERSION && version != UF_VERSION_PREV) { EMSG2(_("E824: Incompatible undo file: %s"), file_name); goto error; } + new_version = (version == UF_VERSION || version == UF_VERSION_CRYPT); if (fread(read_hash, UNDO_HASH_SIZE, 1, fp) != 1) { @@ -1645,6 +1705,28 @@ u_read_undo(name, hash, orig_name) seq_cur = get4c(fp); seq_time = get8ctime(fp); + /* Optional header fields, not in previous version. */ + if (new_version) + for (;;) + { + int len = getc(fp); + int what; + + if (len == 0 || len == EOF) + break; + what = getc(fp); + switch (what) + { + case UF_LAST_SAVE_NR: + last_save_nr = get4c(fp); + break; + default: + /* field not supported, skip */ + while (--len >= 0) + (void)getc(fp); + } + } + /* uhp_table will store the freshly created undo headers we allocate * until we insert them into curbuf. The table remains sorted by the * sequence numbers of the headers. @@ -1665,7 +1747,7 @@ u_read_undo(name, hash, orig_name) goto error; } - uhp = unserialize_uhp(fp, file_name); + uhp = unserialize_uhp(fp, file_name, new_version); if (uhp == NULL) goto error; uhp_table[num_read_uhps++] = uhp; @@ -1766,7 +1848,8 @@ u_read_undo(name, hash, orig_name) curbuf->b_u_numhead = num_head; curbuf->b_u_seq_last = seq_last; curbuf->b_u_seq_cur = seq_cur; - curbuf->b_u_seq_time = seq_time; + curbuf->b_u_time_cur = seq_time; + curbuf->b_u_last_save_nr = last_save_nr; curbuf->b_u_synced = TRUE; vim_free(uhp_table); @@ -1962,7 +2045,7 @@ undo_time(step, sec, absolute) /* When doing computations with time_t subtract starttime, because * time_t converted to a long may result in a wrong number. */ if (sec) - target = (long)(curbuf->b_u_seq_time - starttime) + step; + target = (long)(curbuf->b_u_time_cur - starttime) + step; else target = curbuf->b_u_seq_cur + step; if (step < 0) @@ -2458,7 +2541,7 @@ u_undoredo(undo) /* The timestamp can be the same for multiple changes, just use the one of * the undone/redone change. */ - curbuf->b_u_seq_time = curhead->uh_time; + curbuf->b_u_time_cur = curhead->uh_time; #ifdef U_DEBUG u_check(FALSE); #endif @@ -2595,6 +2678,13 @@ ex_undolist(eap) uhp->uh_seq, changes); u_add_time(IObuff + STRLEN(IObuff), IOSIZE - STRLEN(IObuff), uhp->uh_time); + if (uhp->uh_save_nr > 0) + { + while (STRLEN(IObuff) < 32) + STRCAT(IObuff, " "); + vim_snprintf_add((char *)IObuff, IOSIZE, + " %3ld", uhp->uh_save_nr); + } ((char_u **)(ga.ga_data))[ga.ga_len++] = vim_strsave(IObuff); } @@ -2645,7 +2735,8 @@ ex_undolist(eap) sort_strings((char_u **)ga.ga_data, ga.ga_len); msg_start(); - msg_puts_attr((char_u *)_("number changes time"), hl_attr(HLF_T)); + msg_puts_attr((char_u *)_("number changes time saved"), + hl_attr(HLF_T)); for (i = 0; i < ga.ga_len && !got_int; ++i) { msg_putchar('\n'); @@ -3048,3 +3139,48 @@ curbufIsChanged() #endif (curbuf->b_changed || file_ff_differs(curbuf)); } + +#if defined(FEAT_EVAL) || defined(PROTO) +/* + * For undotree(): Append the list of undo blocks at "first_uhp" to "list". + * Recursive. + */ + void +u_eval_tree(first_uhp, list) + u_header_T *first_uhp; + list_T *list; +{ + u_header_T *uhp = first_uhp; + dict_T *dict; + + while (uhp != NULL) + { + dict = dict_alloc(); + if (dict == NULL) + return; + dict_add_nr_str(dict, "seq", uhp->uh_seq, NULL); + dict_add_nr_str(dict, "time", uhp->uh_time, NULL); + if (uhp == curbuf->b_u_newhead) + dict_add_nr_str(dict, "newhead", 1, NULL); + if (uhp == curbuf->b_u_curhead) + dict_add_nr_str(dict, "curhead", 1, NULL); + if (uhp->uh_save_nr > 0) + dict_add_nr_str(dict, "save", uhp->uh_save_nr, NULL); + + if (uhp->uh_alt_next.ptr != NULL) + { + list_T *alt_list = list_alloc(); + + if (alt_list != NULL) + { + /* Recursive call to add alternate undo tree. */ + u_eval_tree(uhp->uh_alt_next.ptr, alt_list); + dict_add_list(dict, "alt", alt_list); + } + } + + list_append_dict(list, dict); + uhp = uhp->uh_prev.ptr; + } +} +#endif
--- a/src/workshop.c +++ b/src/workshop.c @@ -820,7 +820,6 @@ workshop_toolbar_button( char namebuf[BUFSIZ]; static int tbid = 1; char_u *p; - int len; #ifdef WSDEBUG_TRACE if (WSDLEVEL(WS_TRACE_VERBOSE)) @@ -861,12 +860,10 @@ workshop_toolbar_button( if (file != NULL && *file != NUL) { p = vim_strsave_escaped((char_u *)file, (char_u *)" "); - len = STRLEN(cbuf); - vim_snprintf(cbuf + len, sizeof(cbuf) - len, "icon=%s ", p); + vim_snprintf_add(cbuf, sizeof(cbuf), "icon=%s ", p); vim_free(p); } - len = STRLEN(cbuf); - vim_snprintf(cbuf + len, sizeof(cbuf) - len,"1.%d %s :wsverb %s<CR>", + vim_snprintf_add(cbuf, sizeof(cbuf),"1.%d %s :wsverb %s<CR>", tbpri, namebuf, verb); /* Define the menu item */