]> granicus.if.org Git - vim/commitdiff
Add file save counter to undo information. Add undotree() function.
authorBram Moolenaar <Bram@vim.org>
Sat, 26 Jun 2010 23:15:55 +0000 (01:15 +0200)
committerBram Moolenaar <Bram@vim.org>
Sat, 26 Jun 2010 23:15:55 +0000 (01:15 +0200)
15 files changed:
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

index c57b8d4cd8d2d4458b84ac5a914d4ad657f1560d..c19a34569f0b977d93d110a6a223b19ca63c6b07 100644 (file)
@@ -1944,6 +1944,7 @@ tr( {src}, {fromstr}, {tostr})    String  translate chars of {src} in {fromstr}
 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} [, {type}]]])
                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} [, {type}]]])
                        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.
index 936cb5e386d0c4c8b53399b2087869277ca1dde5..35fbdd9fe11c8e8073a4d61a0556da850b005f9e 100644 (file)
@@ -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*
index 5e4f3a0aa7ebd242dc9b7eb26ec465a252c248d7..a0675ec0de53eb35257677fe98db6c5fdf6b9326 100644 (file)
@@ -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)
index 1195a05a56627e7b0326334e0aaadf72a5f4a0d8..1ed2ecd05a17871422a8428de95e9d151def721a 100644 (file)
@@ -135,6 +135,7 @@ This is explained in the user manual: |usr_32.txt|.
                        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
index a6878e6cdc7a26fc1d64a70e1d634c85392032da..a2ac3261cb52214194dbfda57caa3a74e23532a6 100644 (file)
@@ -2992,9 +2992,7 @@ fileinfo(fullname, shorthelp, dont_truncate)
                                          (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_truncate)
     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);
index bf33cdddd0ecc27854cd112e9abdbf6122501400..105ba025fbe618af195a83bfd75df5325c6cb57a 100644 (file)
@@ -445,6 +445,7 @@ static int free_unref_items __ARGS((int copyID));
 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 *argvars, typval_T *rettv));
 #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));
@@ -6784,6 +6786,26 @@ dict_alloc()
     return d;
 }
 
+/*
+ * 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)
@@ -7025,6 +7047,32 @@ dict_add_nr_str(d, key, nr, str)
     return OK;
 }
 
+/*
+ * 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.
  */
@@ -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},
@@ -17673,6 +17722,35 @@ f_undofile(argvars, rettv)
 #endif
 }
 
+/*
+ * "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
  */
@@ -17892,12 +17970,9 @@ f_winsaveview(argvars, rettv)
 {
     dict_T     *dict;
 
-    dict = dict_alloc();
-    if (dict == NULL)
+    if (rettv_dict_alloc(rettv) == FAIL)
        return;
-    rettv->v_type = VAR_DICT;
-    rettv->vval.v_dict = dict;
-    ++dict->dv_refcount;
+    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);
index 53dc0adf38f134a776cdfd834fe7d9eab97480a3..e1d270ca52c1af0a4137238614965098c17299b3 100644 (file)
@@ -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 */
index b0994616758395c5a5c510b5aeddaa0d8760c12a..967f49ea43f9a6954f0d708a798125de85864026 100644 (file)
@@ -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)
        {
index f234a9db2c999484509e5e78524ae61f90ba5d17..c00846e0ff19d977aa5fb04fbd3397fc927329a0 100644 (file)
@@ -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, ...)
index 33136561509afca091c55a431592e3e4db9962f3..01db6895f4b679ae196bade3c0cec9e0e0a1fb15 100644 (file)
@@ -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
index 6d11d970d7a88ec30cb08a0595e5094c977c4497..5a85e4687e8e4d6a7d50f7c7c9caf880998776ab 100644 (file)
@@ -56,6 +56,7 @@ dictitem_T *dictitem_alloc __ARGS((char_u *key));
 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));
index baa98c4f879cbe1bc8e5a027fc3ffbd02d889642..daac3776e9467c3a332b3adeda88ada623271345 100644 (file)
@@ -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 : */
index d792c7804e9868015a22a2cbfaffb9ebd9301d7e..36b08707eda2b846cee5a64d3f12ca75ff48162b 100644 (file)
@@ -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
index b1737815354a4ccdd234244d5c3bae978a5b946a..ddd31117fe8586d8746f61643bbbad501f63463d 100644 (file)
@@ -106,7 +106,7 @@ static size_t fwrite_crypt __ARGS((buf_T *buf UNUSED, char_u *ptr, size_t len, F
 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
index 334e6afff8d6e2ea739a6df9040c8845258e0bd9..c9c52954dbea9534bcb85021cdbc0507ff0e41c2 100644 (file)
@@ -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 */