]> granicus.if.org Git - vim/commitdiff
Added ":earlier 1f" and ":later 1f".
authorBram Moolenaar <Bram@vim.org>
Sun, 27 Jun 2010 03:18:54 +0000 (05:18 +0200)
committerBram Moolenaar <Bram@vim.org>
Sun, 27 Jun 2010 03:18:54 +0000 (05:18 +0200)
14 files changed:
runtime/doc/eval.txt
runtime/doc/undo.txt
runtime/doc/usr_02.txt
runtime/doc/usr_32.txt
runtime/doc/usr_toc.txt
src/eval.c
src/ex_docmd.c
src/fileio.c
src/normal.c
src/proto/undo.pro
src/structs.h
src/testdir/test61.in
src/testdir/test61.ok
src/undo.c

index c19a34569f0b977d93d110a6a223b19ca63c6b07..5d6a205001f4b7045459e5fe4e0352318690b1bd 100644 (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|.
index 1ed2ecd05a17871422a8428de95e9d151def721a..d1433d5bc21ea6cc8358d167e05b47decefcc1ad 100644 (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
index 2e49781aba5e284d924ab99778350562af9e92f7..6cd4b4f620d8cc58e28a69642eaa2e47218700ba 100644 (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
index 02669b00d053e4697eb6205fc987b4583ef6c373..e6cfd4d6823d87e0c6bcd87783985a9b91add556 100644 (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
index 63242e3621f9276213748dfaf1302838bed9238e..0e3460f92a5707e5f9d92e04ef82757480778832 100644 (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 ~
index 105ba025fbe618af195a83bfd75df5325c6cb57a..296efda463ef187bc322871a534d2d395c06c3c6 100644 (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)
index 42c39934943a3d622fcf774f6671e8e9e5486e92..d165305e052b78879250694f794017084c6f76c7 100644 (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);
 }
 
 /*
index 967f49ea43f9a6954f0d708a798125de85864026..cdbd01a125b345c8bda6eba78481d9f7d761fb2f 100644 (file)
@@ -4879,6 +4879,7 @@ restore_backup:
     {
        unchanged(buf, TRUE);
        u_unchanged(buf);
+       u_update_save_nr(buf);
     }
 
     /*
index e161d455d96673fa6c6fb73d63407f50ea83b972..9ad32f7d8654f286f552b83892229120b272859f 100644 (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:
index daac3776e9467c3a332b3adeda88ada623271345..40d166a5441d11fee712dd7b378e9cdf70c55726 100644 (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));
index 36b08707eda2b846cee5a64d3f12ca75ff48162b..a95e1ba38f7cc47294d288cec0bffdad41bf924b 100644 (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
index 7ce72e627f719300298e30ba60aac4a7f62e4809..f9a15746160fb9e7e006c266b1283fe4812d6e16 100644 (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 @@ obbbb\eu:.w >>test.out
 obbbb\e:set ul=100
 :undojoin
 occcc\eu:.w >>test.out
+:e! Xtest
+ione one one\e:set ul=100
+:w!
+otwo\e:set ul=100
+otwo\e:set ul=100
+:w
+othree\e: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---\e:0put e
+ggO---\e:0put d
+ggO---\e:0put c
+ggO---\e:0put b
+ggO---\e:0put a
+ggO---\e:w >>test.out
 :qa!
 ENDTEST
 
index 020dd5383b9b2bfe13450668fba1a84e1260646d..6e25e3bfd76b48fe8fa2852e9ba15d71dcd20e5a 100644 (file)
 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
index ddd31117fe8586d8746f61643bbbad501f63463d..33ca01e0341397b7bf91003af29ca47a7be40995 100644 (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;