# HG changeset patch # User Bram Moolenaar # Date 1277608734 -7200 # Node ID e41433ea71dfea5ab19657c8c534de057da9bfde # Parent 941ff1cd317ade6870378eda09a0478d84241218 Added ":earlier 1f" and ":later 1f". diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -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|. diff --git a/runtime/doc/undo.txt b/runtime/doc/undo.txt --- a/runtime/doc/undo.txt +++ b/runtime/doc/undo.txt @@ -145,6 +145,16 @@ g- Go to older text state. With a cou :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 cou :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 diff --git a/runtime/doc/usr_02.txt b/runtime/doc/usr_02.txt --- a/runtime/doc/usr_02.txt +++ b/runtime/doc/usr_02.txt @@ -302,7 +302,7 @@ edited. Typing this command twice cance 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 diff --git a/runtime/doc/usr_32.txt b/runtime/doc/usr_32.txt --- a/runtime/doc/usr_32.txt +++ b/runtime/doc/usr_32.txt @@ -9,16 +9,40 @@ Vim provides multi-level undo. If you u 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 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 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 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 diff --git a/runtime/doc/usr_toc.txt b/runtime/doc/usr_toc.txt --- a/runtime/doc/usr_toc.txt +++ b/runtime/doc/usr_toc.txt @@ -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 ~ diff --git a/src/eval.c b/src/eval.c --- a/src/eval.c +++ b/src/eval.c @@ -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) diff --git a/src/ex_docmd.c b/src/ex_docmd.c --- a/src/ex_docmd.c +++ b/src/ex_docmd.c @@ -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); } /* diff --git a/src/fileio.c b/src/fileio.c --- a/src/fileio.c +++ b/src/fileio.c @@ -4879,6 +4879,7 @@ restore_backup: { unchanged(buf, TRUE); u_unchanged(buf); + u_update_save_nr(buf); } /* diff --git a/src/normal.c b/src/normal.c --- a/src/normal.c +++ b/src/normal.c @@ -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: diff --git a/src/proto/undo.pro b/src/proto/undo.pro --- a/src/proto/undo.pro +++ b/src/proto/undo.pro @@ -12,11 +12,12 @@ void u_write_undo __ARGS((char_u *name, 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)); diff --git a/src/structs.h b/src/structs.h --- a/src/structs.h +++ b/src/structs.h @@ -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 diff --git a/src/testdir/test61.in b/src/testdir/test61.in --- a/src/testdir/test61.in +++ b/src/testdir/test61.in @@ -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 diff --git a/src/testdir/test61.ok b/src/testdir/test61.ok --- a/src/testdir/test61.ok +++ b/src/testdir/test61.ok @@ -22,3 +22,22 @@ 123456 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 diff --git a/src/undo.c b/src/undo.c --- a/src/undo.c +++ b/src/undo.c @@ -119,6 +119,7 @@ static void put_header_ptr __ARGS((FILE #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;