0
0
mirror of https://github.com/vim/vim.git synced 2025-07-04 23:07:33 -04:00

Added ":earlier 1f" and ":later 1f".

This commit is contained in:
Bram Moolenaar 2010-06-27 05:18:54 +02:00
parent a800b42975
commit 730cde924c
14 changed files with 216 additions and 40 deletions

View File

@ -5797,6 +5797,8 @@ undotree() *undotree()*
something readable.
"save_last" Number of the last file write. Zero when no
write yet.
"save_cur" Number of the current position in the undo
tree.
"synced" Non-zero when the last undo block was synced.
This happens when waiting from input from the
user. See |undo-blocks|.

View File

@ -145,6 +145,16 @@ g- Go to older text state. With a count repeat that many
:earlier {N}s Go to older text state about {N} seconds before.
:earlier {N}m Go to older text state about {N} minutes before.
:earlier {N}h Go to older text state about {N} hours before.
:earlier {N}d Go to older text state about {N} days before.
:earlier {N}f Go to older text state {N} file writes before.
When changes were made since the laste write
":earlier 1f" will revert the text to the state when
it was written. Otherwise it will go to the write
before that.
When at the state of the first file write, or when
the file was not written, ":earlier 1f" will go to
before the first change.
*g+*
g+ Go to newer text state. With a count repeat that many
@ -154,6 +164,11 @@ g+ Go to newer text state. With a count repeat that many
:later {N}s Go to newer text state about {N} seconds later.
:later {N}m Go to newer text state about {N} minutes later.
:later {N}h Go to newer text state about {N} hours later.
:later {N}d Go to newer text state about {N} days later.
:later {N}f Go to newer text state {N} file writes later.
When at the state of the last file write, ":later 1f"
will go to the newest text state.
Note that text states will become unreachable when undo information is cleared

View File

@ -302,7 +302,7 @@ edited. Typing this command twice cancels the preceding "U".
The "U" command is a change by itself, which the "u" command undoes and CTRL-R
redoes. This might be a bit confusing. Don't worry, with "u" and CTRL-R you
can go to any of the situations you had. More about that in section |32.1|.
can go to any of the situations you had. More about that in section |32.2|.
==============================================================================
*02.6* Other editing commands

View File

@ -9,16 +9,40 @@ Vim provides multi-level undo. If you undo a few changes and then make a new
change you create a branch in the undo tree. This text is about moving
through the branches.
|32.1| Numbering changes
|32.2| Jumping around the tree
|32.3| Time travelling
|32.1| Undo up to a file write
|32.2| Numbering changes
|32.3| Jumping around the tree
|32.4| Time travelling
Next chapter: |usr_40.txt| Make new commands
Previous chapter: |usr_31.txt| Exploiting the GUI
Table of contents: |usr_toc.txt|
==============================================================================
*32.1* Numbering changes
*32.1* Undo up to a file write
Sometimes you make several changes, and then discover you want to go back to
when you have last written the file. You can do that with this command: >
:earlier 1f
The "f" stands for "file" here.
You can repeat this command to go further back in the past. Or use a count
diferent from 1 to go back faster.
If you go back too far, go forward again with: >
:later 1f
Note that these commands really work in time sequence. This matters if you
made changes after undoing some changes. It's explained in the next section.
Also note that we are talking about text writes here. For writing the undo
information in a file see |undo-persistence|.
==============================================================================
*32.2* Numbering changes
In section |02.5| we only discussed one line of undo/redo. But it is also
possible to branch off. This happens when you undo a few changes and then
@ -66,7 +90,7 @@ it. But sometimes by the number of one of the changes below it, especially
when moving up in the tree, so that you know which change was just undone.
==============================================================================
*32.2* Jumping around the tree
*32.3* Jumping around the tree
So how do you get to "one two" now? You can use this command: >
@ -114,7 +138,7 @@ Using |:undo| is useful if you know what change you want to jump to. |g-| and
You can type a count before |g-| and |g+| to repeat them.
==============================================================================
*32.3* Time travelling
*32.4* Time travelling
When you have been working on text for a while the tree grows to become big.
Then you may want to go to the text of some minutes ago.
@ -133,10 +157,10 @@ seconds with this command: >
:earlier 10s
Depending on how much time you took for the changes you end up at a certain
position in the tree. The |:earlier| command argument can be "m" for minutes
and "h" for hours. To go all the way back use a big number: >
position in the tree. The |:earlier| command argument can be "m" for minutes,
"h" for hours and "d" for days. To go all the way back use a big number: >
:earlier 10h
:earlier 100d
To travel forward in time again use the |:later| command: >
@ -144,6 +168,11 @@ To travel forward in time again use the |:later| command: >
The arguments are "s", "m" and "h", just like with |:earlier|.
If you want even more details, or want to manipulate the information, you can
use the |undotree()| function. To see what it returns: >
:echo undotree()
==============================================================================
Next chapter: |usr_40.txt| Make new commands

View File

@ -273,9 +273,10 @@ Subjects that can be read independently.
|31.5| Various
|usr_32.txt| The undo tree
|32.1| Numbering changes
|32.2| Jumping around the tree
|32.3| Time travelling
|32.1| Undo up to a file write
|32.2| Numbering changes
|32.3| Jumping around the tree
|32.4| Time travelling
==============================================================================
Tuning Vim ~

View File

@ -17735,12 +17735,13 @@ f_undotree(argvars, rettv)
dict_T *dict = rettv->vval.v_dict;
list_T *list;
dict_add_nr_str(dict, "synced", (long)curbuf->b_u_synced, NULL);
dict_add_nr_str(dict, "seq_last", curbuf->b_u_seq_last, NULL);
dict_add_nr_str(dict, "save_last",
(long)curbuf->b_u_save_nr_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);
dict_add_nr_str(dict, "save_cur", (long)curbuf->b_u_save_nr_cur, NULL);
list = list_alloc();
if (list != NULL)

View File

@ -8461,7 +8461,7 @@ ex_undo(eap)
exarg_T *eap UNUSED;
{
if (eap->addr_count == 1) /* :undo 123 */
undo_time(eap->line2, FALSE, TRUE);
undo_time(eap->line2, FALSE, FALSE, TRUE);
else
u_undo(1);
}
@ -8507,6 +8507,7 @@ ex_later(eap)
{
long count = 0;
int sec = FALSE;
int file = FALSE;
char_u *p = eap->arg;
if (*p == NUL)
@ -8519,13 +8520,16 @@ ex_later(eap)
case 's': ++p; sec = TRUE; break;
case 'm': ++p; sec = TRUE; count *= 60; break;
case 'h': ++p; sec = TRUE; count *= 60 * 60; break;
case 'd': ++p; sec = TRUE; count *= 24 * 60 * 60; break;
case 'f': ++p; file = TRUE; break;
}
}
if (*p != NUL)
EMSG2(_(e_invarg2), eap->arg);
else
undo_time(eap->cmdidx == CMD_earlier ? -count : count, sec, FALSE);
undo_time(eap->cmdidx == CMD_earlier ? -count : count,
sec, file, FALSE);
}
/*

View File

@ -4879,6 +4879,7 @@ restore_backup:
{
unchanged(buf, TRUE);
u_unchanged(buf);
u_update_save_nr(buf);
}
/*

View File

@ -8294,7 +8294,7 @@ nv_g_cmd(cap)
case '-': /* "g+" and "g-": undo or redo along the timeline */
if (!checkclearopq(oap))
undo_time(cap->nchar == '-' ? -cap->count1 : cap->count1,
FALSE, FALSE);
FALSE, FALSE, FALSE);
break;
default:

View File

@ -12,11 +12,12 @@ void u_write_undo __ARGS((char_u *name, int forceit, buf_T *buf, char_u *hash));
void u_read_undo __ARGS((char_u *name, char_u *hash, char_u *orig_name));
void u_undo __ARGS((int count));
void u_redo __ARGS((int count));
void undo_time __ARGS((long step, int sec, int absolute));
void undo_time __ARGS((long step, int sec, int file, int absolute));
void u_sync __ARGS((int force));
void ex_undolist __ARGS((exarg_T *eap));
void ex_undojoin __ARGS((exarg_T *eap));
void u_unchanged __ARGS((buf_T *buf));
void u_update_save_nr __ARGS((buf_T *buf));
void u_clearall __ARGS((buf_T *buf));
void u_saveline __ARGS((linenr_T lnum));
void u_clearline __ARGS((void));

View File

@ -327,7 +327,8 @@ 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 */
long uh_save_nr; /* set when the file was saved after the
changes in this block */
#ifdef U_DEBUG
int uh_magic; /* magic number to check allocation */
#endif
@ -1371,9 +1372,10 @@ struct file_buffer
int b_u_numhead; /* current number of headers */
int b_u_synced; /* entry lists are synced */
long b_u_seq_last; /* last used undo sequence number */
long b_u_save_nr_last; /* counter for last file write */
long b_u_seq_cur; /* hu_seq 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 */
long b_u_save_nr_cur; /* file write nr after which we are now */
/*
* variables for "U" command in undo.c

View File

@ -1,6 +1,7 @@
Tests for undo tree.
Since this script is sourced we need to explicitly break changes up in
undo-able pieces. Do that by setting 'undolevels'.
Also tests :earlier and :later.
STARTTEST
:" Delete three characters and undo
@ -50,6 +51,35 @@ obbbbu:.w >>test.out
obbbb:set ul=100
:undojoin
occccu:.w >>test.out
:e! Xtest
ione one one:set ul=100
:w!
otwo:set ul=100
otwo:set ul=100
:w
othree:earlier 1f
:" expect "one one one\ntwo\ntwo"
:%yank a
:earlier 1f
:" expect "one one one"
:%yank b
:earlier 1f
:" expect empty line
:%yank c
:later 1f
:" expect "one one one"
:%yank d
:later 1f
:" expect "one one one\ntwo\ntwo"
:%yank e
:later 1f
:" expect "one one one\ntwo\ntwo\nthree"
ggO---:0put e
ggO---:0put d
ggO---:0put c
ggO---:0put b
ggO---:0put a
ggO---:w >>test.out
:qa!
ENDTEST

View File

@ -22,3 +22,22 @@
123456abc
aaaa
aaaa
---
one one one
two
two
---
one one one
---
---
one one one
---
one one one
two
two
---
one one one
two
two
three

View File

@ -119,6 +119,7 @@ static void put_header_ptr __ARGS((FILE *fp, u_header_T *uhp));
#define U_ALLOC_LINE(size) lalloc((long_u)(size), FALSE)
static char_u *u_save_line __ARGS((linenr_T));
/* used in undo_end() to report number of added and deleted lines */
static long u_newcount, u_oldcount;
/*
@ -932,7 +933,7 @@ serialize_header(fp, buf, hash)
/* Optional fields. */
putc(4, fp);
putc(UF_LAST_SAVE_NR, fp);
put_bytes(fp, (long_u)buf->b_u_last_save_nr, 4);
put_bytes(fp, (long_u)buf->b_u_save_nr_last, 4);
putc(0, fp); /* end marker */
@ -1444,17 +1445,6 @@ 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.
*/
@ -1849,7 +1839,7 @@ u_read_undo(name, hash, orig_name)
curbuf->b_u_seq_last = seq_last;
curbuf->b_u_seq_cur = seq_cur;
curbuf->b_u_time_cur = seq_time;
curbuf->b_u_last_save_nr = last_save_nr;
curbuf->b_u_save_nr_last = last_save_nr;
curbuf->b_u_synced = TRUE;
vim_free(uhp_table);
@ -2001,13 +1991,15 @@ u_doit(startcount)
* When "step" is negative go back in time, otherwise goes forward in time.
* When "sec" is FALSE make "step" steps, when "sec" is TRUE use "step" as
* seconds.
* When "file" is TRUE use "step" as a number of file writes.
* When "absolute" is TRUE use "step" as the sequence number to jump to.
* "sec" must be FALSE then.
*/
void
undo_time(step, sec, absolute)
undo_time(step, sec, file, absolute)
long step;
int sec;
int file;
int absolute;
{
long target;
@ -2021,6 +2013,7 @@ undo_time(step, sec, absolute)
int nomark;
int round;
int dosec = sec;
int dofile = file;
int above = FALSE;
int did_undo = TRUE;
@ -2044,8 +2037,45 @@ 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)
if (dosec)
target = (long)(curbuf->b_u_time_cur - starttime) + step;
else if (dofile)
{
if (step < 0)
{
/* Going back to a previous write. If there were changes after
* the last write, count that as moving one file-write, so
* that ":earlier 1f" undoes all changes since the last save. */
uhp = curbuf->b_u_curhead;
if (uhp != NULL)
uhp = uhp->uh_next.ptr;
else
uhp = curbuf->b_u_newhead;
if (uhp != NULL && uhp->uh_save_nr != 0)
/* "uh_save_nr" was set in the last block, that means
* there were no changes since the last write */
target = curbuf->b_u_save_nr_cur + step;
else
/* count the changes since the last write as one step */
target = curbuf->b_u_save_nr_cur + step + 1;
if (target <= 0)
/* Go to before first write: before the oldest change. Use
* the sequence number for that. */
dofile = FALSE;
}
else
{
/* Moving forward to a newer write. */
target = curbuf->b_u_save_nr_cur + step;
if (target > curbuf->b_u_save_nr_last)
{
/* Go to after last write: after the latest change. Use
* the sequence number for that. */
target = curbuf->b_u_seq_last + 1;
dofile = FALSE;
}
}
}
else
target = curbuf->b_u_seq_cur + step;
if (step < 0)
@ -2056,8 +2086,10 @@ undo_time(step, sec, absolute)
}
else
{
if (sec)
if (dosec)
closest = (long)(time(NULL) - starttime + 1);
else if (dofile)
closest = curbuf->b_u_save_nr_last + 2;
else
closest = curbuf->b_u_seq_last + 2;
if (target >= closest)
@ -2092,9 +2124,14 @@ undo_time(step, sec, absolute)
while (uhp != NULL)
{
uhp->uh_walk = mark;
val = (long)(dosec ? (uhp->uh_time - starttime) : uhp->uh_seq);
if (dosec)
val = (long)(uhp->uh_time - starttime);
else if (dofile)
val = uhp->uh_save_nr;
else
val = uhp->uh_seq;
if (round == 1)
if (round == 1 && !(dofile && val == 0))
{
/* Remember the header that is closest to the target.
* It must be at least in the right direction (checked with
@ -2123,7 +2160,10 @@ undo_time(step, sec, absolute)
/* Quit searching when we found a match. But when searching for a
* time we need to continue looking for the best uh_seq. */
if (target == val && !dosec)
{
target = uhp->uh_seq;
break;
}
/* go down in the tree if we haven't been there */
if (uhp->uh_prev.ptr != NULL && uhp->uh_prev.ptr->uh_walk != nomark
@ -2179,6 +2219,7 @@ undo_time(step, sec, absolute)
target = closest_seq;
dosec = FALSE;
dofile = FALSE;
if (step < 0)
above = TRUE; /* stop above the header */
}
@ -2539,6 +2580,15 @@ u_undoredo(undo)
* work we compute this as being just above the just undone change. */
--curbuf->b_u_seq_cur;
/* Remember where we are for ":earlier 1f" and ":later 1f". */
if (curhead->uh_save_nr != 0)
{
if (undo)
curbuf->b_u_save_nr_cur = curhead->uh_save_nr - 1;
else
curbuf->b_u_save_nr_cur = curhead->uh_save_nr;
}
/* The timestamp can be the same for multiple changes, just use the one of
* the undone/redone change. */
curbuf->b_u_time_cur = curhead->uh_time;
@ -2811,6 +2861,27 @@ u_unchanged(buf)
buf->b_did_warn = FALSE;
}
/*
* Increase the write count, store it in the last undo header, what would be
* used for "u".
*/
void
u_update_save_nr(buf)
buf_T *buf;
{
u_header_T *uhp;
++buf->b_u_save_nr_last;
buf->b_u_save_nr_cur = buf->b_u_save_nr_last;
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_save_nr_last;
}
static void
u_unch_branch(uhp)
u_header_T *uhp;