Bram,

On Fr, 07 Mär 2014, Christian Brabandt wrote:
[new :undorecover command]

Attached is a new patch. It works by restoring each line to the last 
known state, which means, the recovered buffer might be inconsistent, 
since you basically end up with a mix of undo states.

Secondly, on any undofile that was written with a non-patched Vim, it 
will probably only restore the buffer partly. From the undofile of my 
.vimrc (currently containing 402 lines with many lines empty or 
commented out) I could recover 50 lines (with about 450 empty lines in 
between).

Therefore this patch makes Vim store one complete buffer content in the 
undofile, so recovery will be more useful.

Bram wrote:
> > Also, this is difficult to test with something under src/testdir.  It's
> > probably more suited for a unittest like memfile_test.c does.

Not sure, how about to do that currently, so I am skipping this part for 
now.

Best,
Christian

-- 
-- 
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 @@
 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 @@
 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
@@ -18704,6 +18704,7 @@
 	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
@@ -3614,6 +3614,7 @@
 	    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
@@ -1007,6 +1007,8 @@
 			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 @@
 #endif
 #ifndef FEAT_PERSISTENT_UNDO
 # define ex_rundo		ex_ni
+# define ex_undorecover		ex_ni
 # define ex_wundo		ex_ni
 #endif
 #ifndef FEAT_LUA
@@ -325,6 +326,7 @@
 #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 @@
     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
@@ -2596,7 +2596,7 @@
 	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
@@ -7681,7 +7681,7 @@
 			&& !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 @@
 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
@@ -1466,6 +1466,7 @@
     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
@@ -110,6 +110,8 @@
 static void serialize_visualinfo __ARGS((visualinfo_T *info, FILE *fp));
 static void unserialize_visualinfo __ARGS((visualinfo_T *info, FILE *fp));
 static void put_header_ptr __ARGS((FILE	*fp, u_header_T *uhp));
+static void u_undorecover __ARGS((long size));
+static void u_undorecover_entry __ARGS((u_header_T *uhp, linenr_T **line));
 #endif
 
 #define U_ALLOC_LINE(size) lalloc((long_u)(size), FALSE)
@@ -716,6 +718,7 @@
 
 /* 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
@@ -970,6 +973,9 @@
     putc(4, fp);
     putc(UF_LAST_SAVE_NR, fp);
     put_bytes(fp, (long_u)buf->b_u_save_nr_last, 4);
+    putc(4, fp);
+    putc(UF_SAVE_WHOLE_BUF, fp);
+    put_bytes(fp, (long_u)buf->b_u_save_buf, 4);
 
     putc(0, fp);  /* end marker */
 
@@ -1293,6 +1299,71 @@
     put_bytes(fp, (long_u)(uhp != NULL ? uhp->uh_seq : 0), 4);
 }
 
+/* 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.
@@ -1428,6 +1499,17 @@
 	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)
@@ -1573,10 +1655,11 @@
  * "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;
@@ -1589,6 +1672,7 @@
     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;
@@ -1694,8 +1778,8 @@
 	goto error;
     }
     line_count = (linenr_T)get4c(fp);
-    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)
 	{
@@ -1746,6 +1830,9 @@
 	    case UF_LAST_SAVE_NR:
 		last_save_nr = get4c(fp);
 		break;
+	    case UF_SAVE_WHOLE_BUF:
+		last_save_buf = get4c(fp);
+		break;
 	    default:
 		/* field not supported, skip */
 		while (--len >= 0)
@@ -1877,6 +1964,7 @@
     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);
@@ -1889,6 +1977,31 @@
     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 */
+	curbuf->b_u_line_colnr = (colnr_T)0;
+	curbuf->b_u_seq_last = 0;
+	curbuf->b_u_seq_cur = 0;
+	curbuf->b_u_save_nr_last = 0;
+	curbuf->b_u_save_nr_cur = 0;
+	curbuf->b_u_save_buf = 0;
+	update_screen(NOT_VALID);
+	MSG(_("Undorecovery completed."));
+    }
+
     if (name != NULL)
 	smsg((char_u *)_("Finished reading undo file %s"), file_name);
     goto theend;
@@ -3103,6 +3216,9 @@
 						 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);
 }
 

Raspunde prin e-mail lui