summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2010-06-27 05:18:54 +0200
committerBram Moolenaar <Bram@vim.org>2010-06-27 05:18:54 +0200
commit730cde924cea50977bdbfa5b977180bfaa188a27 (patch)
treec0570ec54157923527d3da2d10c31782738d532d /src
parenta800b42975f7a62282cb90d8c61ef3cff2fe810a (diff)
downloadvim-git-730cde924cea50977bdbfa5b977180bfaa188a27.tar.gz
Added ":earlier 1f" and ":later 1f".
Diffstat (limited to 'src')
-rw-r--r--src/eval.c7
-rw-r--r--src/ex_docmd.c8
-rw-r--r--src/fileio.c1
-rw-r--r--src/normal.c2
-rw-r--r--src/proto/undo.pro3
-rw-r--r--src/structs.h6
-rw-r--r--src/testdir/test61.in30
-rw-r--r--src/testdir/test61.ok19
-rw-r--r--src/undo.c107
9 files changed, 156 insertions, 27 deletions
diff --git a/src/eval.c b/src/eval.c
index 105ba025f..296efda46 100644
--- 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
index 42c399349..d165305e0 100644
--- 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
index 967f49ea4..cdbd01a12 100644
--- 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
index e161d455d..9ad32f7d8 100644
--- 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
index daac3776e..40d166a54 100644
--- a/src/proto/undo.pro
+++ b/src/proto/undo.pro
@@ -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));
diff --git a/src/structs.h b/src/structs.h
index 36b08707e..a95e1ba38 100644
--- 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
index 7ce72e627..f9a157461 100644
--- 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
index 020dd5383..6e25e3bfd 100644
--- a/src/testdir/test61.ok
+++ b/src/testdir/test61.ok
@@ -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
diff --git a/src/undo.c b/src/undo.c
index ddd31117f..33ca01e03 100644
--- a/src/undo.c
+++ b/src/undo.c
@@ -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;