I've added some documentation to the patch I wrote for persistent
undo. It's been a while since I've posted about this, so I'll recap
the features:
1. New option 'undofile' causes Vim to automatically write out the
undo history for the current session to a file located in the directory
(ies) provided by the new 'undodir' option, and to automatically read
back this undo history when the same file is opened again.
2. New commands :wundo and :rundo allow you to manually write and read
undo files, respectively.
The attached diff was created against SVN version 1658. Once you patch
your source, you'll want to add 'set undofile' to your vimrc to enable
the functionality. Then make some edits to a file, quit Vim, and open
the file again - you will still be able to undo your edits.
I've been using this patch in my Vim for close to a year now, and its
a real productivity booster. Working on other machines without undo
persistence has become fairly difficult for me, as I'm so used to
being able to revert changes I've made to (for example) a LaTeX file
after exiting Vim, creating a PDF, and noticing a mistake.
Please try this patch! I think you all and the rest of the Vim
community will really like this functionality.
Thanks,
Jordan Lewis
I'm sorry to report that your patch does not build on Windows with
VS2008.
The following is the compiler output. It looks a bit like a missing
include file may be to blame.
C:\Documents and Settings\efalor\My Documents\devel\vim7\src>nmake -f Make_mvc.m
ak OLE=yes FEATURES=HUGE CPUNR=pentium4 DEBUG=no MAP=no CSCOPE=yes
Microsoft (R) Program Maintenance Utility Version 9.00.21022.08
Copyright (C) Microsoft Corporation. All rights reserved.
cl -c /W3 /nologo -I. -Iproto -DHAVE_PATHDEF -DWIN32 -DFEAT_CSCOPE
-DWINVER=0x0400 -D_WIN32_WINNT=0x0400 /Fo.\ObjCO/ /Ox /GL -DNDEBUG /arch:SS
E2 /Zl /MT -DFEAT_OLE -DDYNAMIC_ICONV -DDYNAMIC_GETTEXT -DFEAT_HUGE /Fd.\ObjCO/
/Zi .\ObjCO\pathdef.c
pathdef.c
cl -c /W3 /nologo -I. -Iproto -DHAVE_PATHDEF -DWIN32 -DFEAT_CSCOPE
-DWINVER=0x0400 -D_WIN32_WINNT=0x0400 /Fo.\ObjCO/ /Ox /GL -DNDEBUG /arch:SS
E2 /Zl /MT -DFEAT_OLE -DDYNAMIC_ICONV -DDYNAMIC_GETTEXT -DFEAT_HUGE /Fd.\ObjCO/
/Zi buffer.c charset.c diff.c digraph.c edit.c eval.c ex_cmds.c ex_cmds2.c ex_do
cmd.c ex_eval.c ex_getln.c fileio.c fold.c getchar.c hardcopy.c hashtab.c main.c
mark.c mbyte.c memfile.c memline.c menu.c message.c misc1.c misc2.c move.c norm
al.c ops.c option.c os_mswin.c os_win32.c popupmnu.c quickfix.c regexp.c screen.
c search.c spell.c syntax.c tag.c term.c ui.c undo.c window.c if_cscope.c
buffer.c
charset.c
diff.c
digraph.c
edit.c
eval.c
ex_cmds.c
ex_cmds2.c
ex_docmd.c
ex_eval.c
ex_getln.c
fileio.c
fold.c
getchar.c
hardcopy.c
hashtab.c
main.c
mark.c
mbyte.c
memfile.c
Compiling...
memline.c
menu.c
message.c
misc1.c
misc2.c
move.c
normal.c
ops.c
option.c
os_mswin.c
os_win32.c
popupmnu.c
quickfix.c
regexp.c
screen.c
search.c
spell.c
syntax.c
tag.c
term.c
Compiling...
ui.c
undo.c
undo.c(759) : error C2275: 'FILE' : illegal use of this type as an expression
c:\Program Files\Microsoft Visual Studio 9.0\VC\INCLUDE\stdio.h(69) : se
e declaration of 'FILE'
undo.c(759) : error C2065: 'fp' : undeclared identifier
undo.c(760) : error C2065: 'fp' : undeclared identifier
undo.c(760) : warning C4047: '==' : 'int' differs in levels of indirection from
'void *'
undo.c(767) : error C2143: syntax error : missing ';' before 'type'
undo.c(768) : error C2275: 'char_u' : illegal use of this type as an expression
c:\documents and settings\efalor\my documents\devel\vim7\src\vim.h(368)
: see declaration of 'char_u'
undo.c(768) : error C2065: 'line_ptr' : undeclared identifier
undo.c(769) : error C2275: 'linenr_T' : illegal use of this type as an expressio
n
c:\documents and settings\efalor\my documents\devel\vim7\src\vim.h(1471)
: see declaration of 'linenr_T'
undo.c(769) : error C2146: syntax error : missing ';' before identifier 'line_ln
um'
undo.c(769) : error C2065: 'line_lnum' : undeclared identifier
undo.c(770) : error C2275: 'colnr_T' : illegal use of this type as an expression
c:\documents and settings\efalor\my documents\devel\vim7\src\vim.h(1472)
: see declaration of 'colnr_T'
undo.c(770) : error C2146: syntax error : missing ';' before identifier 'line_co
lnr'
undo.c(770) : error C2065: 'line_colnr' : undeclared identifier
undo.c(771) : error C2275: 'linenr_T' : illegal use of this type as an expressio
n
c:\documents and settings\efalor\my documents\devel\vim7\src\vim.h(1471)
: see declaration of 'linenr_T'
undo.c(771) : error C2146: syntax error : missing ';' before identifier 'line_co
unt'
undo.c(771) : error C2065: 'line_count' : undeclared identifier
undo.c(772) : error C2143: syntax error : missing ';' before 'type'
undo.c(773) : error C2143: syntax error : missing ';' before 'type'
undo.c(774) : error C2143: syntax error : missing ';' before 'type'
undo.c(775) : error C2143: syntax error : missing ';' before 'type'
undo.c(776) : error C2275: 'time_t' : illegal use of this type as an expression
c:\Program Files\Microsoft Visual Studio 9.0\VC\INCLUDE\crtdefs.h(570) :
see declaration of 'time_t'
undo.c(776) : error C2146: syntax error : missing ';' before identifier 'seq_tim
e'
undo.c(776) : error C2065: 'seq_time' : undeclared identifier
undo.c(777) : error C2143: syntax error : missing ';' before 'type'
undo.c(778) : error C2143: syntax error : missing ';' before 'type'
undo.c(779) : error C2143: syntax error : missing ';' before 'type'
undo.c(780) : error C2275: 'char_u' : illegal use of this type as an expression
c:\documents and settings\efalor\my documents\devel\vim7\src\vim.h(368)
: see declaration of 'char_u'
undo.c(780) : error C2065: 'array' : undeclared identifier
undo.c(780) : error C2100: illegal indirection
undo.c(781) : error C2275: 'char_u' : illegal use of this type as an expression
c:\documents and settings\efalor\my documents\devel\vim7\src\vim.h(368)
: see declaration of 'char_u'
undo.c(781) : error C2065: 'line' : undeclared identifier
undo.c(782) : error C2275: 'u_entry_T' : illegal use of this type as an expressi
on
c:\documents and settings\efalor\my documents\devel\vim7\src\structs.h(2
71) : see declaration of 'u_entry_T'
undo.c(782) : error C2065: 'uep' : undeclared identifier
undo.c(782) : error C2065: 'last_uep' : undeclared identifier
undo.c(782) : error C2100: illegal indirection
undo.c(782) : error C2065: 'nuep' : undeclared identifier
undo.c(782) : error C2100: illegal indirection
undo.c(783) : error C2275: 'u_header_T' : illegal use of this type as an express
ion
c:\documents and settings\efalor\my documents\devel\vim7\src\structs.h(2
72) : see declaration of 'u_header_T'
undo.c(783) : error C2065: 'uhp' : undeclared identifier
undo.c(784) : error C2275: 'u_header_T' : illegal use of this type as an express
ion
c:\documents and settings\efalor\my documents\devel\vim7\src\structs.h(2
72) : see declaration of 'u_header_T'
undo.c(784) : error C2065: 'uhp_table' : undeclared identifier
undo.c(784) : error C2100: illegal indirection
undo.c(787) : error C2065: 'magic' : undeclared identifier
undo.c(787) : error C2065: 'fp' : undeclared identifier
undo.c(787) : warning C4047: 'function' : 'FILE *' differs in levels of indirect
ion from 'int'
undo.c(787) : warning C4024: 'get2c' : different types for formal and actual par
ameter 1
undo.c(788) : error C2065: 'magic' : undeclared identifier
undo.c(793) : error C2065: 'version' : undeclared identifier
undo.c(793) : error C2065: 'fp' : undeclared identifier
undo.c(793) : warning C4047: 'function' : 'FILE *' differs in levels of indirect
ion from 'int'
undo.c(793) : warning C4024: 'get2c' : different types for formal and actual par
ameter 1
undo.c(794) : error C2065: 'version' : undeclared identifier
undo.c(801) : error C2065: 'file_mtime' : undeclared identifier
undo.c(801) : error C2065: 'fp' : undeclared identifier
undo.c(801) : warning C4047: 'function' : 'FILE *' differs in levels of indirect
ion from 'int'
undo.c(801) : warning C4024: 'get4c' : different types for formal and actual par
ameter 1
undo.c(802) : error C2065: 'file_mtime' : undeclared identifier
undo.c(812) : error C2065: 'line_count' : undeclared identifier
undo.c(812) : error C2065: 'fp' : undeclared identifier
undo.c(812) : warning C4047: 'function' : 'FILE *' differs in levels of indirect
ion from 'int'
undo.c(812) : warning C4024: 'get4c' : different types for formal and actual par
ameter 1
undo.c(813) : error C2065: 'line_count' : undeclared identifier
undo.c(824) : error C2065: 'str_len' : undeclared identifier
undo.c(824) : error C2065: 'fp' : undeclared identifier
undo.c(824) : warning C4047: 'function' : 'FILE *' differs in levels of indirect
ion from 'int'
undo.c(824) : warning C4024: 'get4c' : different types for formal and actual par
ameter 1
undo.c(825) : error C2065: 'str_len' : undeclared identifier
undo.c(827) : error C2065: 'str_len' : undeclared identifier
undo.c(828) : error C2065: 'line_ptr' : undeclared identifier
undo.c(828) : error C2065: 'str_len' : undeclared identifier
undo.c(828) : warning C4047: '=' : 'int' differs in levels of indirection from '
char_u *'
undo.c(828) : warning C4047: '==' : 'int' differs in levels of indirection from
'void *'
undo.c(833) : error C2065: 'i' : undeclared identifier
undo.c(833) : error C2065: 'i' : undeclared identifier
undo.c(833) : error C2065: 'str_len' : undeclared identifier
undo.c(833) : error C2065: 'i' : undeclared identifier
undo.c(834) : error C2065: 'line_ptr' : undeclared identifier
undo.c(834) : error C2065: 'i' : undeclared identifier
undo.c(834) : error C2109: subscript requires array or pointer type
undo.c(834) : error C2065: 'fp' : undeclared identifier
undo.c(834) : warning C4047: 'function' : 'FILE *' differs in levels of indirect
ion from 'int'
undo.c(834) : warning C4024: 'getc' : different types for formal and actual para
meter 1
undo.c(836) : error C2065: 'line_ptr' : undeclared identifier
undo.c(836) : error C2065: 'i' : undeclared identifier
undo.c(836) : error C2109: subscript requires array or pointer type
undo.c(838) : error C2065: 'line_lnum' : undeclared identifier
undo.c(838) : error C2065: 'fp' : undeclared identifier
undo.c(838) : warning C4047: 'function' : 'FILE *' differs in levels of indirect
ion from 'int'
undo.c(838) : warning C4024: 'get4c' : different types for formal and actual par
ameter 1
undo.c(839) : error C2065: 'line_colnr' : undeclared identifier
undo.c(839) : error C2065: 'fp' : undeclared identifier
undo.c(839) : warning C4047: 'function' : 'FILE *' differs in levels of indirect
ion from 'int'
undo.c(839) : warning C4024: 'get4c' : different types for formal and actual par
ameter 1
undo.c(842) : error C2065: 'old_header_seq' : undeclared identifier
undo.c(842) : error C2065: 'fp' : undeclared identifier
undo.c(842) : warning C4047: 'function' : 'FILE *' differs in levels of indirect
ion from 'int'
undo.c(842) : warning C4024: 'get4c' : different types for formal and actual par
ameter 1
undo.c(843) : error C2065: 'new_header_seq' : undeclared identifier
undo.c(843) : error C2065: 'fp' : undeclared identifier
undo.c(843) : warning C4047: 'function' : 'FILE *' differs in levels of indirect
ion from 'int'
undo.c(843) : warning C4024: 'get4c' : different types for formal and actual par
ameter 1
undo.c(844) : error C2065: 'cur_header_seq' : undeclared identifier
undo.c(844) : error C2065: 'fp' : undeclared identifier
undo.c(844) : warning C4047: 'function' : 'FILE *' differs in levels of indirect
ion from 'int'
undo.c(844) : warning C4024: 'get4c' : different types for formal and actual par
ameter 1
undo.c(845) : error C2065: 'num_head' : undeclared identifier
undo.c(845) : error C2065: 'fp' : undeclared identifier
undo.c(845) : warning C4047: 'function' : 'FILE *' differs in levels of indirect
ion from 'int'
undo.c(845) : warning C4024: 'get4c' : different types for formal and actual par
ameter 1
undo.c(846) : error C2065: 'seq_last' : undeclared identifier
undo.c(846) : error C2065: 'fp' : undeclared identifier
undo.c(846) : warning C4047: 'function' : 'FILE *' differs in levels of indirect
ion from 'int'
undo.c(846) : warning C4024: 'get4c' : different types for formal and actual par
ameter 1
undo.c(847) : error C2065: 'seq_cur' : undeclared identifier
undo.c(847) : error C2065: 'fp' : undeclared identifier
undo.c(847) : warning C4047: 'function' : 'FILE *' differs in levels of indirect
ion from 'int'
undo.c(847) : warning C4024: 'get4c' : different types for formal and actual par
ameter 1
undo.c(848) : error C2065: 'seq_time' : undeclared identifier
undo.c(848) : error C2065: 'fp' : undeclared identifier
undo.c(848) : warning C4047: 'function' : 'FILE *' differs in levels of indirect
ion from 'int'
undo.c(848) : warning C4024: 'get4c' : different types for formal and actual par
ameter 1
undo.c(852) : error C2065: 'uhp_table' : undeclared identifier
undo.c(852) : error C2065: 'num_head' : undeclared identifier
undo.c(852) : warning C4047: '=' : 'int' differs in levels of indirection from '
u_header_T **'
undo.c(853) : error C2065: 'uhp_table' : undeclared identifier
undo.c(853) : warning C4047: '==' : 'int' differs in levels of indirection from
'void *'
undo.c(858) : error C2065: 'uhp_table' : undeclared identifier
undo.c(858) : warning C4022: 'memset' : pointer mismatch for actual parameter 1
undo.c(858) : error C2065: 'num_head' : undeclared identifier
undo.c(860) : error C2065: 'c' : undeclared identifier
undo.c(860) : error C2065: 'fp' : undeclared identifier
undo.c(860) : warning C4047: 'function' : 'FILE *' differs in levels of indirect
ion from 'int'
undo.c(860) : warning C4024: 'getc' : different types for formal and actual para
meter 1
undo.c(861) : error C2065: 'c' : undeclared identifier
undo.c(861) : error C2065: 'c' : undeclared identifier
undo.c(862) : error C2065: 'c' : undeclared identifier
undo.c(862) : error C2065: 'fp' : undeclared identifier
undo.c(862) : warning C4047: 'function' : 'FILE *' differs in levels of indirect
ion from 'int'
undo.c(862) : warning C4024: 'ungetc' : different types for formal and actual pa
rameter 2
undo.c(864) : error C2065: 'found_first_uep' : undeclared identifier
undo.c(865) : error C2065: 'uhp' : undeclared identifier
undo.c(865) : fatal error C1003: error count exceeds 100; stopping compilation
window.c
if_cscope.c
NMAKE : fatal error U1077: '"c:\Program Files\Microsoft Visual Studio 9.0\VC\BIN
\cl.EXE"' : return code '0x2'
Stop.
--
Erik Falor
Registered Linux User #445632 http://counter.li.org
Hi Jordan
Thanks for your work!
The persistent undo feature would be very nice in my opinion.
And I see that persistent undo is quite high on the feature voting
page:
http://www.vim.org/sponsor/vote_results.php
Some remarks about your patch:
- code compiles fine with huge version of Vim but fails to
compile with tiny version of Vim:
$ ./configure --with-features=tiny
$ make
...
objects/undo.o: In function `unserialize_pos':
/tmp/vim7/src/undo.c:667: undefined reference to `get4c'
/tmp/vim7/src/undo.c:668: undefined reference to `get4c'
etc, etc...
- It would be best to have the persistent undo code
within something like #ifdef FEAT_PERSISTENT_UNDO
- compilation warning:
undo.c:1073: warning: unused variable ‘cur_seq’
- typo in undo.c:946: strutures -> structures
- Memory leak: variable munged_name allocated at undo.c:703
is never freed:
702 name_len = STRLEN(ffname);
703 munged_name = alloc(name_len + 1);
704 mch_memmove(munged_name, ffname, name_len + 1);
- Using the same coding convention of Vim may increase your chance
of having the patch merged in the main tree. For example:
if (...) {
...
}
... should be...
if (...)
{
...
}
- serialize_uep(...) has FILE *fp as first argument whereas
functions serialize_pos(...) & serialize_visualinfo(...) have
FILE *fp as last argument. Consistent would be better.
- According to Valgrind memory checker, line undo.c:1033
writes an uninitialized value uep->ue_lcount in
serialize_uep(...):
put_bytes(fp, (long_u) uep->ue_lcount, 4);
I will check further when I have time, maybe next weekend.
Cheers
-- Dominique
Thanks for the excellent comments, Dominique. I've incorporated most
of your feedback into the patch, and I have a few further questions.
> objects/undo.o: In function `unserialize_pos':
> /tmp/vim7/src/undo.c:667: undefined reference to `get4c'
> /tmp/vim7/src/undo.c:668: undefined reference to `get4c'
> etc, etc...
This family of functions (get[2,3,4,8]c) currently lives in spell.c,
but they are generic enough that I was able to easily reuse them.
Should they be moved to misc.c or misc2.c? This would remove the
dependency of persistent undo on the spelling feature.
> - It would be best to have the persistent undo code
> within something like #ifdef FEAT_PERSISTENT_UNDO
I've wrapped all of the persistent undo with an #ifdef like you
recommended, and set FEAT_PERSISTENT_UNDO to appear in Vims of size
normal or above. Is that a reasonable setting, or should I change it
to big or huge?
I've attached an updated patch that should address your remarks with
the exception of the uninitialized value problem that Valgrind turned
up. I'm still working on the solution to that.
This updated patch should now also build and run on Windows, thanks to
Erik Falor's helpful advice.
- Jordan
I have been using the Persistent Undo patch for several months now and I
have a general comment regarding it's usability. Jordan and I had a
private conversation about this a while back, and he requested I post
something to the list so as to provide a broader audience for feedback.
Note that I have set the 'undofile' option in my .vimrc because I don't
want to have to remember to use :wundo .
Often times I need to make a _temporary_ change to a file; so I make the
change and save it without exiting. After doing whatever I need to with
the altered file I generally want to quickly revert back to the
"original" file (i.e., back to the file contents as they were prior to
the *current* editing session). In the past, I would do something like
"1000u" and then save it and exit. But with undofile set, if an
undofile exits and is applicable, the saved edits will be undone as
well. Of course, this is by design. But I would prefer an easy way to
undo everything up to the point of reading the undofile while keeping
"set undofile" specified in my .vimrc .
Perhaps one way of doing this would be if instead of the single
'undofile' option we had 'writeundofile' and 'readundofile'? That way I
could set my .vimrc to always write out the undo file, but only read it
in when I used :rundo . Although, this is not backward compatible with
the current patch.
Another possibility would be for 'undofile' to be a string variable with
possible choices being something like "read, write, both". Where "both"
is the default for backward compatibility.
Regards,
--
Mun
On Di, 17 Nov 2009, Mun wrote:
> I have been using the Persistent Undo patch for several months now and I
> have a general comment regarding it's usability. Jordan and I had a
> private conversation about this a while back, and he requested I post
> something to the list so as to provide a broader audience for feedback.
>
> Note that I have set the 'undofile' option in my .vimrc because I don't
> want to have to remember to use :wundo .
>
> Often times I need to make a _temporary_ change to a file; so I make the
> change and save it without exiting. After doing whatever I need to with
> the altered file I generally want to quickly revert back to the
> "original" file (i.e., back to the file contents as they were prior to
> the *current* editing session). In the past, I would do something like
> "1000u" and then save it and exit. But with undofile set, if an
> undofile exits and is applicable, the saved edits will be undone as
> well. Of course, this is by design. But I would prefer an easy way to
> undo everything up to the point of reading the undofile while keeping
> "set undofile" specified in my .vimrc .
Wouldn't that be a perfect use case for undo_branches? With the plugin
undo_tagsน you can tag a certain state and revert to that branch
whenever you like.
น)http://www.vim.org/scripts/script.php?script_id=1997
regards,
Christian
--
:wq
What about checking the modified option? (very pseudo cody ...):
Undo one step, undo more steps until the buffer is not modified -- "not
modified" is assumed to be the case when starting to edit a buffer.
undo
while &modified && CanUndo()
undo
endwhile
(or wrap :undo in a try-block which :breaks on error)
Or check this script: undo_tags
http://vim.sf.net/scripts/script.php?script_id=1997
Or try to use an autocommand:
:au BufRead * let b:undo_start_nr = changenr()
:com! Revert exec "undo" b:undo_start_nr
This is untested, and I don't know what will work together with the
patch. But I'm sure there will be a way without the need to add more
Vim options.
--
Andy