mirror of
https://github.com/vim/vim.git
synced 2025-09-17 02:48:10 -04:00
Add file save counter to undo information. Add undotree() function.
This commit is contained in:
parent
d69980f9dd
commit
a800b42975
@ -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.
|
||||
|
@ -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*
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
19
src/buffer.c
19
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);
|
||||
|
87
src/eval.c
87
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));
|
||||
@ -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);
|
||||
|
@ -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 */
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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, ...)
|
||||
|
@ -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
|
||||
|
@ -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));
|
||||
|
@ -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 : */
|
||||
|
@ -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
|
||||
|
162
src/undo.c
162
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
|
||||
|
@ -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 */
|
||||
|
Loading…
x
Reference in New Issue
Block a user