summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--runtime/doc/eval.txt57
-rw-r--r--runtime/doc/tags1
-rw-r--r--runtime/doc/todo.txt9
-rw-r--r--runtime/doc/undo.txt1
-rw-r--r--src/buffer.c19
-rw-r--r--src/eval.c87
-rw-r--r--src/ex_cmds.c14
-rw-r--r--src/fileio.c5
-rw-r--r--src/message.c41
-rw-r--r--src/proto.h9
-rw-r--r--src/proto/eval.pro1
-rw-r--r--src/proto/undo.pro1
-rw-r--r--src/structs.h4
-rw-r--r--src/undo.c162
-rw-r--r--src/workshop.c7
15 files changed, 355 insertions, 63 deletions
diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt
index c57b8d4cd..c19a34569 100644
--- a/runtime/doc/eval.txt
+++ b/runtime/doc/eval.txt
@@ -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.
diff --git a/runtime/doc/tags b/runtime/doc/tags
index 936cb5e38..35fbdd9fe 100644
--- 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*
diff --git a/runtime/doc/todo.txt b/runtime/doc/todo.txt
index 5e4f3a0aa..a0675ec0d 100644
--- 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)
diff --git a/runtime/doc/undo.txt b/runtime/doc/undo.txt
index 1195a05a5..1ed2ecd05 100644
--- a/runtime/doc/undo.txt
+++ b/runtime/doc/undo.txt
@@ -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
diff --git a/src/buffer.c b/src/buffer.c
index a6878e6cd..a2ac3261c 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -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);
diff --git a/src/eval.c b/src/eval.c
index bf33cdddd..105ba025f 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -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));
@@ -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)
+ 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);
diff --git a/src/ex_cmds.c b/src/ex_cmds.c
index 53dc0adf3..e1d270ca5 100644
--- 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 */
diff --git a/src/fileio.c b/src/fileio.c
index b09946167..967f49ea4 100644
--- 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)
{
diff --git a/src/message.c b/src/message.c
index f234a9db2..c00846e0f 100644
--- 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, ...)
diff --git a/src/proto.h b/src/proto.h
index 331365615..01db6895f 100644
--- 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
diff --git a/src/proto/eval.pro b/src/proto/eval.pro
index 6d11d970d..5a85e4687 100644
--- a/src/proto/eval.pro
+++ b/src/proto/eval.pro
@@ -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));
diff --git a/src/proto/undo.pro b/src/proto/undo.pro
index baa98c4f8..daac3776e 100644
--- 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 : */
diff --git a/src/structs.h b/src/structs.h
index d792c7804..36b08707e 100644
--- 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
diff --git a/src/undo.c b/src/undo.c
index b17378153..ddd31117f 100644
--- a/src/undo.c
+++ b/src/undo.c
@@ -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
diff --git a/src/workshop.c b/src/workshop.c
index 334e6afff..c9c52954d 100644
--- 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 */