Hi,
Here is another update, that allows to 'undo' the undorecover command
and should work better in case of errors.
Best,
Christian
--
Ich mache mir immer Vorwürfe, daß meine Malerei nicht wert ist, was
sie kostet.
-- Vincent Willem van Gogh
--
--
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php
---
You received this message because you are subscribed to the Google Groups
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
For more options, visit https://groups.google.com/d/optout.
diff --git a/runtime/doc/undo.txt b/runtime/doc/undo.txt
--- a/runtime/doc/undo.txt
+++ b/runtime/doc/undo.txt
@@ -14,6 +14,7 @@ 3. Undo blocks |undo-blocks|
4. Undo branches |undo-branches|
5. Undo persistence |undo-persistence|
6. Remarks about undo |undo-remarks|
+7. Undo recovery |undo-recovery|
==============================================================================
1. Undo and redo commands *undo-commands*
@@ -403,4 +404,25 @@ if it is not what you want do 'u.'. Thi
first put, and repeat the put command for the second register. Repeat the
'u.' until you got what you want.
+==============================================================================
+7. Undo recovery *undo-recovery*
+
+:undorecovery {file}
+
+ Read undo history from {file} and try to recover the buffer
+ contents. In case you have lost your buffer, you can try to
+ recover from the undofile.
+
+ This works by checking the undo history and for each line
+ restore it to the last known state.
+
+ If you have previously stored the complete buffer in the undo
+ tree (e.g. by means of the 'undoreload' setting) all lines can
+ be recovered (but possibly not into a consistent state), else
+ you possibly end up with a file containing a lot of white
+ space (e.g. because those empty lines were not stored in the
+ undo tree).
+
+ Note: In any case you need to check each line carefully.
+
vim:tw=78:ts=8:ft=help:norl:
diff --git a/src/eval.c b/src/eval.c
--- a/src/eval.c
+++ b/src/eval.c
@@ -19317,6 +19317,7 @@ f_undotree(argvars, rettv)
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_cur", (long)curbuf->b_u_save_nr_cur, NULL);
+ dict_add_nr_str(dict, "whole_buf", (long)curbuf->b_u_save_buf, NULL);
list = list_alloc();
if (list != NULL)
diff --git a/src/ex_cmds.c b/src/ex_cmds.c
--- a/src/ex_cmds.c
+++ b/src/ex_cmds.c
@@ -3743,6 +3743,7 @@ do_ecmd(fnum, ffname, sfname, eap, newln
if (u_savecommon(0, curbuf->b_ml.ml_line_count + 1, 0, TRUE)
== FAIL)
goto theend;
+ curbuf->b_u_save_buf = curbuf->b_u_seq_cur;
u_unchanged(curbuf);
buf_freeall(curbuf, BFA_KEEP_UNDO);
diff --git a/src/ex_cmds.h b/src/ex_cmds.h
--- a/src/ex_cmds.h
+++ b/src/ex_cmds.h
@@ -1009,6 +1009,8 @@ EX(CMD_undojoin, "undojoin", ex_undojoin
TRLBAR|CMDWIN),
EX(CMD_undolist, "undolist", ex_undolist,
TRLBAR|CMDWIN),
+EX(CMD_undorecover, "undorecover", ex_undorecover,
+ NEEDARG|FILE1),
EX(CMD_unabbreviate, "unabbreviate", ex_abbreviate,
EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN),
EX(CMD_unhide, "unhide", ex_buffer_all,
diff --git a/src/ex_docmd.c b/src/ex_docmd.c
--- a/src/ex_docmd.c
+++ b/src/ex_docmd.c
@@ -254,6 +254,7 @@ static void ex_popup __ARGS((exarg_T *ea
#endif
#ifndef FEAT_PERSISTENT_UNDO
# define ex_rundo ex_ni
+# define ex_undorecover ex_ni
# define ex_wundo ex_ni
#endif
#ifndef FEAT_LUA
@@ -324,6 +325,7 @@ static void ex_undo __ARGS((exarg_T *eap
#ifdef FEAT_PERSISTENT_UNDO
static void ex_wundo __ARGS((exarg_T *eap));
static void ex_rundo __ARGS((exarg_T *eap));
+static void ex_undorecover __ARGS((exarg_T *eap));
#endif
static void ex_redo __ARGS((exarg_T *eap));
static void ex_later __ARGS((exarg_T *eap));
@@ -8808,7 +8810,17 @@ ex_rundo(eap)
char_u hash[UNDO_HASH_SIZE];
u_compute_hash(hash);
- u_read_undo(eap->arg, hash, NULL);
+ u_read_undo(eap->arg, hash, NULL, FALSE);
+}
+
+ static void
+ex_undorecover(eap)
+ exarg_T *eap;
+{
+ if (!(curbuf->b_ml.ml_flags & ML_EMPTY))
+ EMSG(_("E__: undorecovery: buffer not empty!"));
+ else
+ u_read_undo(eap->arg, NULL, NULL, TRUE);
}
#endif
diff --git a/src/fileio.c b/src/fileio.c
--- a/src/fileio.c
+++ b/src/fileio.c
@@ -2634,7 +2634,7 @@ failed:
char_u hash[UNDO_HASH_SIZE];
sha256_finish(&sha_ctx, hash);
- u_read_undo(NULL, hash, fname);
+ u_read_undo(NULL, hash, fname, FALSE);
}
#endif
diff --git a/src/option.c b/src/option.c
--- a/src/option.c
+++ b/src/option.c
@@ -7805,7 +7805,7 @@ set_bool_option(opt_idx, varp, value, op
&& !curbufIsChanged() && curbuf->b_ml.ml_mfp != NULL)
{
u_compute_hash(hash);
- u_read_undo(NULL, hash, curbuf->b_fname);
+ u_read_undo(NULL, hash, curbuf->b_fname, FALSE);
}
}
curbuf = save_curbuf;
diff --git a/src/proto/undo.pro b/src/proto/undo.pro
--- a/src/proto/undo.pro
+++ b/src/proto/undo.pro
@@ -9,7 +9,7 @@ int u_savecommon __ARGS((linenr_T top, l
void u_compute_hash __ARGS((char_u *hash));
char_u *u_get_undo_file_name __ARGS((char_u *buf_ffname, int reading));
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_read_undo __ARGS((char_u *name, char_u *hash, char_u *orig_name, int force));
void u_undo __ARGS((int count));
void u_redo __ARGS((int count));
void undo_time __ARGS((long step, int sec, int file, int absolute));
diff --git a/src/structs.h b/src/structs.h
--- a/src/structs.h
+++ b/src/structs.h
@@ -1489,6 +1489,7 @@ struct file_buffer
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_save_buf; /* counter for when buffer was completly saved in undo tree */
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 b_u_save_nr_cur; /* file write nr after which we are now */
diff --git a/src/undo.c b/src/undo.c
--- a/src/undo.c
+++ b/src/undo.c
@@ -120,6 +120,8 @@ static int undo_flush __ARGS((bufinfo_T
static int fwrite_crypt __ARGS((bufinfo_T *bi, char_u *ptr, size_t len));
static int undo_write_bytes __ARGS((bufinfo_T *bi, long_u nr, int len));
static void put_header_ptr __ARGS((bufinfo_T *bi, u_header_T *uhp));
+static void u_undorecover __ARGS((long size));
+static void u_undorecover_entry __ARGS((u_header_T *uhp, linenr_T **line));
static int undo_read_4c __ARGS((bufinfo_T *bi));
static int undo_read_2c __ARGS((bufinfo_T *bi));
static int undo_read_byte __ARGS((bufinfo_T *bi));
@@ -739,6 +741,7 @@ nomem:
/* extra fields for header */
# define UF_LAST_SAVE_NR 1
+# define UF_SAVE_WHOLE_BUF 2
/* extra fields for uhp */
# define UHP_SAVE_NR 1
@@ -1245,6 +1248,9 @@ serialize_header(bi, hash)
undo_write_bytes(bi, 4, 1);
undo_write_bytes(bi, UF_LAST_SAVE_NR, 1);
undo_write_bytes(bi, (long_u)buf->b_u_save_nr_last, 4);
+ undo_write_bytes(bi, 4, 1);
+ undo_write_bytes(bi, UF_SAVE_WHOLE_BUF, 1);
+ undo_write_bytes(bi, (long_u)buf->b_u_save_buf, 4);
undo_write_bytes(bi, 0, 1); /* end marker */
@@ -1540,6 +1546,71 @@ unserialize_visualinfo(bi, info)
info->vi_curswant = undo_read_4c(bi);
}
+/* Try to recover from the undotree. This works by running through all u_entry
+ * entries and recovering the line to the last known state of the line */
+ static void
+u_undorecover(size)
+ long size;
+{
+ u_header_T *uhp;
+ linenr_T *lines = NULL;
+
+ uhp = curbuf->b_u_oldhead;
+ if (uhp != NULL)
+ {
+ lines = (linenr_T *)lalloc_clear((unsigned)(size * sizeof(linenr_T)), FALSE);
+ if (lines == NULL)
+ {
+ EMSG(_("EXXX: Undorecovery not possible!"));
+ return;
+ }
+ u_undorecover_entry(uhp, &lines);
+
+ vim_free(lines);
+ }
+}
+
+ static void
+u_undorecover_entry(uhp, l_array)
+ u_header_T *uhp;
+ linenr_T **l_array; /* array holding the last sequence number for each line */
+{
+ u_entry_T *uep;
+ linenr_T i;
+
+ if (uhp == NULL)
+ return;
+ uep = uhp->uh_entry;
+ while (uep != NULL)
+ {
+ for (i = 0; i < uep->ue_size; i++)
+ {
+ /* safety check: add empty lines */
+ if (i + uep->ue_top >= curbuf->b_ml.ml_line_count)
+ {
+ /* adding empty lines at the end of the buffer,
+ * prevents partly recovered lines to move downwards */
+ while (curbuf->b_ml.ml_line_count <= i + uep->ue_top)
+ ml_append(curbuf->b_ml.ml_line_count, (char_u *)"", 0, TRUE);
+ }
+
+ if ((*l_array)[uep->ue_top + i] < uhp->uh_seq
+ && *uep->ue_array[i] != NUL)
+ {
+ ml_replace((long)uep->ue_top + i + 1, uep->ue_array[i], TRUE);
+ (*l_array)[uep->ue_top + i] = uhp->uh_seq;
+ }
+ }
+ uep = uep->ue_next;
+ }
+
+ /* Check next alt tree */
+ u_undorecover_entry(uhp->uh_alt_next.ptr, l_array);
+
+ /* Check next branch */
+ u_undorecover_entry(uhp->uh_prev.ptr, l_array);
+}
+
/*
* Write the undo tree in an undo file.
* When "name" is not NULL, use it as the name of the undo file.
@@ -1675,6 +1746,17 @@ u_write_undo(name, forceit, buf, hash)
goto theend;
}
+ if (buf->b_u_save_buf == 0 && (p_ur < 0
+ || buf->b_ml.ml_line_count <= p_ur))
+ {
+ /* store buffer content in the undo header in the current change */
+ if (buf->b_u_curhead == NULL && buf->b_u_newhead != NULL)
+ buf->b_u_curhead = buf->b_u_newhead;
+ buf->b_u_synced = FALSE;
+ u_savecommon(0, buf->b_ml.ml_line_count + 1, 0, TRUE);
+ buf->b_u_save_buf = buf->b_u_seq_cur;
+ }
+
fd = mch_open((char *)file_name,
O_CREAT|O_EXTRA|O_WRONLY|O_EXCL|O_NOFOLLOW, perm);
if (fd < 0)
@@ -1824,10 +1906,11 @@ theend:
* "hash[UNDO_HASH_SIZE]" must be the hash value of the buffer text.
*/
void
-u_read_undo(name, hash, orig_name)
+u_read_undo(name, hash, orig_name, recover)
char_u *name;
char_u *hash;
char_u *orig_name;
+ int recover; /* :undorecover */
{
char_u *file_name;
FILE *fp;
@@ -1840,6 +1923,7 @@ u_read_undo(name, hash, orig_name)
long old_header_seq, new_header_seq, cur_header_seq;
long seq_last, seq_cur;
long last_save_nr = 0;
+ long last_save_buf = 0;
short old_idx = -1, new_idx = -1, cur_idx = -1;
long num_read_uhps = 0;
time_t seq_time;
@@ -1857,6 +1941,11 @@ u_read_undo(name, hash, orig_name)
struct stat st_undo;
#endif
bufinfo_T bi;
+ /* structures needed for undorecovery */
+ u_header_T *uh_newhead = NULL;
+ u_entry_T *uep_dummy = NULL;
+ char_u **newarray = NULL;
+
vim_memset(&bi, 0, sizeof(bi));
if (name == NULL)
@@ -1958,8 +2047,8 @@ u_read_undo(name, hash, orig_name)
goto error;
}
line_count = (linenr_T)undo_read_4c(&bi);
- if (memcmp(hash, read_hash, UNDO_HASH_SIZE) != 0
- || line_count != curbuf->b_ml.ml_line_count)
+ if (!recover && (memcmp(hash, read_hash, UNDO_HASH_SIZE) != 0
+ || line_count != curbuf->b_ml.ml_line_count))
{
if (p_verbose > 0 || name != NULL)
{
@@ -2010,6 +2099,9 @@ u_read_undo(name, hash, orig_name)
case UF_LAST_SAVE_NR:
last_save_nr = undo_read_4c(&bi);
break;
+ case UF_SAVE_WHOLE_BUF:
+ last_save_buf = undo_read_4c(&bi);
+ break;
default:
/* field not supported, skip */
while (--len >= 0)
@@ -2141,9 +2233,11 @@ u_read_undo(name, hash, orig_name)
curbuf->b_u_time_cur = seq_time;
curbuf->b_u_save_nr_last = last_save_nr;
curbuf->b_u_save_nr_cur = last_save_nr;
+ curbuf->b_u_save_buf = last_save_buf;
curbuf->b_u_synced = TRUE;
vim_free(uhp_table);
+ uhp_table = NULL;
#ifdef U_DEBUG
for (i = 0; i < num_head; ++i)
@@ -2153,6 +2247,61 @@ u_read_undo(name, hash, orig_name)
u_check(TRUE);
#endif
+ if (recover)
+ {
+ if (!undo_allowed())
+ {
+ EMSG(_("EXXX: Undorecovery not allowed!"));
+ goto theend;
+ }
+
+ u_undorecover(line_count); /* adds many lines */
+#ifdef U_DEBUG
+ u_check(TRUE);
+#endif
+ appended_lines(0, curbuf->b_ml.ml_line_count);
+ u_blockfree(curbuf); /* free the memory allocated for undo */
+ u_clearall(curbuf); /* reset all undo information */
+ line_ptr = NULL;
+ curbuf->b_u_line_colnr = (colnr_T)0;
+ curbuf->b_u_save_nr_last = 0;
+ curbuf->b_u_save_nr_cur = 0;
+ curbuf->b_u_save_buf = 0;
+ curbuf->b_u_seq_last = 1;
+ curbuf->b_u_seq_cur = 1;
+ curbuf->b_u_numhead = 1;
+ /* Make a dummy entry for the first empty buffer,
+ * so that the recovery is undoable */
+ uh_newhead = (u_header_T *)U_ALLOC_LINE(sizeof(u_header_T));
+ if (uh_newhead == NULL)
+ goto error;
+ vim_memset(uh_newhead, 0, sizeof(u_header_T));
+ uep_dummy = (u_entry_T *)U_ALLOC_LINE(sizeof(u_entry_T));
+ if (uep_dummy == NULL)
+ goto error;
+ vim_memset(uep_dummy, 0, sizeof(u_entry_T));
+ newarray = (char_u **)U_ALLOC_LINE(sizeof(char_u *));
+ if (newarray == NULL)
+ goto error;
+#ifdef U_DEBUG
+ uh_newhead->uh_magic = UH_MAGIC;
+ uep_dummy->ue_magic = UE_MAGIC;
+#endif
+ uep_dummy->ue_size = 1;
+ uep_dummy->ue_array = newarray;
+ uep_dummy->ue_array[0] = vim_strsave((char_u *)"");
+ uh_newhead->uh_entry = uep_dummy;
+ uh_newhead->uh_seq = 1;
+ uh_newhead->uh_time = time(NULL);
+ uh_newhead->uh_cursor = curwin->w_cursor;
+ curbuf->b_u_newhead = curbuf->b_u_oldhead = uh_newhead;
+#ifdef U_DEBUG
+ u_check(TRUE);
+#endif
+ update_screen(NOT_VALID);
+ MSG(_("Undorecovery completed."));
+ }
+
if (name != NULL)
smsg((char_u *)_("Finished reading undo file %s"), file_name);
goto theend;
@@ -2166,6 +2315,13 @@ error:
u_free_uhp(uhp_table[i]);
vim_free(uhp_table);
}
+ if (recover)
+ {
+ if (uh_newhead != NULL)
+ u_free_uhp(uh_newhead);
+ vim_free(uh_newhead);
+ vim_free(newarray);
+ }
theend:
#ifdef FEAT_CRYPT
@@ -3362,6 +3518,9 @@ u_freeheader(buf, uhp, uhpp)
uhap = uhap->uh_alt_next.ptr)
uhap->uh_next.ptr = uhp->uh_next.ptr;
+ if (buf->b_u_save_buf == uhp->uh_seq)
+ buf->b_u_save_buf = 0;
+
u_freeentries(buf, uhp, uhpp);
}