diff options
author | Bram Moolenaar <Bram@vim.org> | 2010-05-23 23:34:36 +0200 |
---|---|---|
committer | Bram Moolenaar <Bram@vim.org> | 2010-05-23 23:34:36 +0200 |
commit | 55debbe38429b81c0ce6e8400aef36812eb151d7 (patch) | |
tree | 992320729b697015fb4b99e9f8645cffe2eeddd6 /src | |
parent | c39125d7c45d17566665c06358501073ea9c4141 (diff) |
Included patch for persistent undo. Lots of changes and added test.
Diffstat (limited to 'src')
-rw-r--r-- | src/buffer.c | 5 | ||||
-rw-r--r-- | src/eval.c | 3 | ||||
-rw-r--r-- | src/ex_cmds.h | 4 | ||||
-rw-r--r-- | src/ex_docmd.c | 30 | ||||
-rw-r--r-- | src/feature.h | 8 | ||||
-rw-r--r-- | src/fileio.c | 71 | ||||
-rw-r--r-- | src/memline.c | 37 | ||||
-rw-r--r-- | src/option.c | 28 | ||||
-rw-r--r-- | src/option.h | 2 | ||||
-rw-r--r-- | src/os_mac.h | 2 | ||||
-rw-r--r-- | src/proto/memline.pro | 1 | ||||
-rw-r--r-- | src/proto/sha256.pro | 3 | ||||
-rw-r--r-- | src/proto/spell.pro | 5 | ||||
-rw-r--r-- | src/proto/undo.pro | 3 | ||||
-rw-r--r-- | src/sha256.c | 27 | ||||
-rw-r--r-- | src/spell.c | 15 | ||||
-rw-r--r-- | src/structs.h | 9 | ||||
-rw-r--r-- | src/testdir/Makefile | 2 | ||||
-rw-r--r-- | src/testdir/test61.in | 47 | ||||
-rw-r--r-- | src/testdir/test61.ok | 9 | ||||
-rw-r--r-- | src/undo.c | 803 | ||||
-rw-r--r-- | src/version.c | 5 | ||||
-rw-r--r-- | src/vim.h | 3 |
23 files changed, 1066 insertions, 56 deletions
diff --git a/src/buffer.c b/src/buffer.c index 531e4b9a29..5ecb638727 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -61,8 +61,9 @@ static void buf_delete_signs __ARGS((buf_T *buf)); #endif /* - * Open current buffer, that is: open the memfile and read the file into memory - * return FAIL for failure, OK otherwise + * Open current buffer, that is: open the memfile and read the file into + * memory. + * Return FAIL for failure, OK otherwise. */ int open_buffer(read_stdin, eap) diff --git a/src/eval.c b/src/eval.c index 446df8ed3f..3c238fcbec 100644 --- a/src/eval.c +++ b/src/eval.c @@ -11869,6 +11869,9 @@ f_has(argvars, rettv) "perl", #endif #endif +#ifdef FEAT_PERSISTENT_UNDO + "persistent_undo", +#endif #ifdef FEAT_PYTHON #ifndef DYNAMIC_PYTHON "python", diff --git a/src/ex_cmds.h b/src/ex_cmds.h index 1ef885acdd..f41e0f449d 100644 --- a/src/ex_cmds.h +++ b/src/ex_cmds.h @@ -773,6 +773,8 @@ EX(CMD_rubydo, "rubydo", ex_rubydo, RANGE|DFLALL|EXTRA|NEEDARG|CMDWIN), EX(CMD_rubyfile, "rubyfile", ex_rubyfile, RANGE|FILE1|NEEDARG|CMDWIN), +EX(CMD_rundo, "rundo", ex_rundo, + NEEDARG|EXTRA|XFILE), EX(CMD_rviminfo, "rviminfo", ex_viminfo, BANG|FILE1|TRLBAR|CMDWIN), EX(CMD_substitute, "substitute", do_sub, @@ -1061,6 +1063,8 @@ EX(CMD_wqall, "wqall", do_wqall, BANG|FILE1|ARGOPT|DFLALL|TRLBAR), EX(CMD_wsverb, "wsverb", ex_wsverb, EXTRA|NOTADR|NEEDARG), +EX(CMD_wundo, "wundo", ex_wundo, + BANG|NEEDARG|EXTRA|XFILE), EX(CMD_wviminfo, "wviminfo", ex_viminfo, BANG|FILE1|TRLBAR|CMDWIN), EX(CMD_xit, "xit", ex_exit, diff --git a/src/ex_docmd.c b/src/ex_docmd.c index ff39040cf7..4097f1d8f2 100644 --- a/src/ex_docmd.c +++ b/src/ex_docmd.c @@ -243,6 +243,10 @@ static void ex_popup __ARGS((exarg_T *eap)); # define ex_spellinfo ex_ni # define ex_spellrepall ex_ni #endif +#ifndef FEAT_PERSISTENT_UNDO +# define ex_rundo ex_ni +# define ex_wundo ex_ni +#endif #ifndef FEAT_MZSCHEME # define ex_mzscheme ex_script_ni # define ex_mzfile ex_ni @@ -298,6 +302,10 @@ static void ex_join __ARGS((exarg_T *eap)); static void ex_at __ARGS((exarg_T *eap)); static void ex_bang __ARGS((exarg_T *eap)); 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)); +#endif static void ex_redo __ARGS((exarg_T *eap)); static void ex_later __ARGS((exarg_T *eap)); static void ex_redir __ARGS((exarg_T *eap)); @@ -8452,6 +8460,28 @@ ex_undo(eap) u_undo(1); } +#ifdef FEAT_PERSISTENT_UNDO + void +ex_wundo(eap) + exarg_T *eap; +{ + char_u hash[UNDO_HASH_SIZE]; + + u_compute_hash(hash); + u_write_undo(eap->arg, eap->forceit, curbuf, hash); +} + + void +ex_rundo(eap) + exarg_T *eap; +{ + char_u hash[UNDO_HASH_SIZE]; + + u_compute_hash(hash); + u_read_undo(eap->arg, hash); +} +#endif + /* * ":redo". */ diff --git a/src/feature.h b/src/feature.h index 6b8f600ec0..61fe3345bb 100644 --- a/src/feature.h +++ b/src/feature.h @@ -1275,3 +1275,11 @@ || defined(FEAT_BIG) # define FEAT_AUTOCHDIR #endif + +/* + * +persistent_undo 'undofile', 'undodir' options, :wundo and :rundo, and + * implementation. + */ +#ifdef FEAT_NORMAL +# define FEAT_PERSISTENT_UNDO +#endif diff --git a/src/fileio.c b/src/fileio.c index 7a697ee020..b7c86af7ef 100644 --- a/src/fileio.c +++ b/src/fileio.c @@ -253,6 +253,10 @@ readfile(fname, sfname, from, lines_to_skip, lines_to_read, eap, flags) char_u *cryptkey = NULL; int did_ask_for_key = FALSE; #endif +#ifdef FEAT_PERSISTENT_UNDO + context_sha256_T sha_ctx; + int read_undo_file = FALSE; +#endif int split = 0; /* number of split lines */ #define UNKNOWN 0x0fffffff /* file size is unknown */ linenr_T linecnt; @@ -1178,6 +1182,12 @@ retry: #ifdef FEAT_MBYTE conv_restlen = 0; #endif +#ifdef FEAT_PERSISTENT_UNDO + read_undo_file = (newfile && curbuf->b_ffname != NULL && curbuf->b_p_udf + && !filtering && !read_stdin && !read_buffer); + if (read_undo_file) + sha256_start(&sha_ctx); +#endif } while (!error && !got_int) @@ -2133,6 +2143,10 @@ rewind_retry: error = TRUE; break; } +#ifdef FEAT_PERSISTENT_UNDO + if (read_undo_file) + sha256_update(&sha_ctx, line_start, len); +#endif ++lnum; if (--read_count == 0) { @@ -2197,6 +2211,10 @@ rewind_retry: error = TRUE; break; } +#ifdef FEAT_PERSISTENT_UNDO + if (read_undo_file) + sha256_update(&sha_ctx, line_start, len); +#endif ++lnum; if (--read_count == 0) { @@ -2237,11 +2255,17 @@ failed: if (set_options) curbuf->b_p_eol = FALSE; *ptr = NUL; - if (ml_append(lnum, line_start, - (colnr_T)(ptr - line_start + 1), newfile) == FAIL) + len = (colnr_T)(ptr - line_start + 1); + if (ml_append(lnum, line_start, len, newfile) == FAIL) error = TRUE; else + { +#ifdef FEAT_PERSISTENT_UNDO + if (read_undo_file) + sha256_update(&sha_ctx, line_start, len); +#endif read_no_eol_lnum = ++lnum; + } } if (set_options) @@ -2555,6 +2579,19 @@ failed: */ write_no_eol_lnum = read_no_eol_lnum; +#ifdef FEAT_PERSISTENT_UNDO + /* + * When opening a new file locate undo info and read it. + */ + if (read_undo_file) + { + char_u hash[UNDO_HASH_SIZE]; + + sha256_finish(&sha_ctx, hash); + u_read_undo(NULL, hash); + } +#endif + #ifdef FEAT_AUTOCMD if (!read_stdin && !read_buffer) { @@ -3038,6 +3075,10 @@ buf_write(buf, fname, sfname, start, end, eap, append, forceit, vim_acl_T acl = NULL; /* ACL copied from original file to backup or new file */ #endif +#ifdef FEAT_PERSISTENT_UNDO + int write_undo_file = FALSE; + context_sha256_T sha_ctx; +#endif if (fname == NULL || *fname == NUL) /* safety check */ return FAIL; @@ -4344,6 +4385,14 @@ restore_backup: write_info.bw_start_lnum = start; #endif +#ifdef FEAT_PERSISTENT_UNDO + write_undo_file = (buf->b_p_udf && overwriting && !append + && !filtering && reset_changed); + if (write_undo_file) + /* Prepare for computing the hash value of the text. */ + sha256_start(&sha_ctx); +#endif + write_info.bw_len = bufsize; #ifdef HAS_BW_FLAGS write_info.bw_flags = wb_flags; @@ -4358,6 +4407,10 @@ restore_backup: * Keep it fast! */ ptr = ml_get_buf(buf, lnum, FALSE) - 1; +#ifdef FEAT_PERSISTENT_UNDO + if (write_undo_file) + sha256_update(&sha_ctx, ptr + 1, STRLEN(ptr + 1) + 1); +#endif while ((c = *++ptr) != NUL) { if (c == NL) @@ -4886,6 +4939,20 @@ nofail: } msg_scroll = msg_save; +#ifdef FEAT_PERSISTENT_UNDO + /* + * When writing the whole file and 'undofile' is set, also write the undo + * file. + */ + if (retval == OK && write_undo_file) + { + char_u hash[UNDO_HASH_SIZE]; + + sha256_finish(&sha_ctx, hash); + u_write_undo(NULL, FALSE, buf, hash); + } +#endif + #ifdef FEAT_AUTOCMD #ifdef FEAT_EVAL if (!should_abort(retval)) diff --git a/src/memline.c b/src/memline.c index b3c1727335..c3fdba3137 100644 --- a/src/memline.c +++ b/src/memline.c @@ -245,9 +245,6 @@ static char_u *make_percent_swname __ARGS((char_u *dir, char_u *name)); #ifdef FEAT_BYTEOFF static void ml_updatechunk __ARGS((buf_T *buf, long line, long len, int updtype)); #endif -#ifdef HAVE_READLINK -static int resolve_symlink __ARGS((char_u *fname, char_u *buf)); -#endif /* * Open a new memline for "buf". @@ -3559,7 +3556,7 @@ ml_lineadd(buf, count) } } -#ifdef HAVE_READLINK +#if defined(HAVE_READLINK) || defined(PROTO) /* * Resolve a symlink in the last component of a file name. * Note that f_resolve() does it for every part of the path, we don't do that @@ -3567,7 +3564,7 @@ ml_lineadd(buf, count) * If it worked returns OK and the resolved link in "buf[MAXPATHL]". * Otherwise returns FAIL. */ - static int + int resolve_symlink(fname, buf) char_u *fname; char_u *buf; @@ -3862,7 +3859,7 @@ do_swapexists(buf, fname) * Returns the name in allocated memory or NULL. * * Note: If BASENAMELEN is not correct, you will get error messages for - * not being able to open the swapfile + * not being able to open the swap or undo file * Note: May trigger SwapExists autocmd, pointers may change! */ static char_u * @@ -3886,29 +3883,29 @@ findswapname(buf, dirp, old_fname) # define CREATE_DUMMY_FILE FILE *dummyfd = NULL; -/* - * If we start editing a new file, e.g. "test.doc", which resides on an MSDOS - * compatible filesystem, it is possible that the file "test.doc.swp" which we - * create will be exactly the same file. To avoid this problem we temporarily - * create "test.doc". - * Don't do this when the check below for a 8.3 file name is used. - */ + /* + * If we start editing a new file, e.g. "test.doc", which resides on an + * MSDOS compatible filesystem, it is possible that the file + * "test.doc.swp" which we create will be exactly the same file. To avoid + * this problem we temporarily create "test.doc". Don't do this when the + * check below for a 8.3 file name is used. + */ if (!(buf->b_p_sn || buf->b_shortname) && buf->b_fname != NULL && mch_getperm(buf->b_fname) < 0) dummyfd = mch_fopen((char *)buf->b_fname, "w"); #endif -/* - * Isolate a directory name from *dirp and put it in dir_name. - * First allocate some memory to put the directory name in. - */ + /* + * Isolate a directory name from *dirp and put it in dir_name. + * First allocate some memory to put the directory name in. + */ dir_name = alloc((unsigned)STRLEN(*dirp) + 1); if (dir_name != NULL) (void)copy_option_part(dirp, dir_name, 31000, ","); -/* - * we try different names until we find one that does not exist yet - */ + /* + * we try different names until we find one that does not exist yet + */ if (dir_name == NULL) /* out of memory */ fname = NULL; else diff --git a/src/option.c b/src/option.c index 6c0618ab37..a4af18adfe 100644 --- a/src/option.c +++ b/src/option.c @@ -177,6 +177,9 @@ #define PV_TS OPT_BUF(BV_TS) #define PV_TW OPT_BUF(BV_TW) #define PV_TX OPT_BUF(BV_TX) +#ifdef FEAT_PERSISTENT_UNDO +# define PV_UDF OPT_BUF(BV_UDF) +#endif #define PV_WM OPT_BUF(BV_WM) /* @@ -362,6 +365,9 @@ static char_u *p_spl; static long p_ts; static long p_tw; static int p_tx; +#ifdef FEAT_PERSISTENT_UNDO +static int p_udf; +#endif static long p_wm; #ifdef FEAT_KEYMAP static char_u *p_keymap; @@ -2586,6 +2592,22 @@ static struct vimoption {"ttytype", "tty", P_STRING|P_EXPAND|P_NODEFAULT|P_NO_MKRC|P_VI_DEF|P_RALL, (char_u *)&T_NAME, PV_NONE, {(char_u *)"", (char_u *)0L} SCRIPTID_INIT}, + {"undodir", "udir", P_STRING|P_EXPAND|P_COMMA|P_NODUP|P_SECURE|P_VI_DEF, +#ifdef FEAT_PERSISTENT_UNDO + (char_u *)&p_udir, PV_NONE, + {(char_u *)".", (char_u *)0L} +#else + (char_u *)NULL, PV_NONE, + {(char_u *)0L, (char_u *)0L} +#endif + SCRIPTID_INIT}, + {"undofile", "udf", P_BOOL|P_VI_DEF|P_VIM, +#ifdef FEAT_PERSISTENT_UNDO + (char_u *)&p_udf, PV_UDF, +#else + (char_u *)NULL, PV_NONE, +#endif + {(char_u *)FALSE, (char_u *)0L} SCRIPTID_INIT}, {"undolevels", "ul", P_NUM|P_VI_DEF, (char_u *)&p_ul, PV_NONE, { @@ -9404,6 +9426,9 @@ get_varp(p) case PV_TS: return (char_u *)&(curbuf->b_p_ts); case PV_TW: return (char_u *)&(curbuf->b_p_tw); case PV_TX: return (char_u *)&(curbuf->b_p_tx); +#ifdef FEAT_PERSISTENT_UNDO + case PV_UDF: return (char_u *)&(curbuf->b_p_udf); +#endif case PV_WM: return (char_u *)&(curbuf->b_p_wm); #ifdef FEAT_KEYMAP case PV_KMAP: return (char_u *)&(curbuf->b_p_keymap); @@ -9774,6 +9799,9 @@ buf_copy_options(buf, flags) #if defined(FEAT_BEVAL) && defined(FEAT_EVAL) buf->b_p_bexpr = empty_option; #endif +#ifdef FEAT_PERSISTENT_UNDO + buf->b_p_udf = p_udf; +#endif /* * Don't copy the options set by ex_help(), use the saved values, diff --git a/src/option.h b/src/option.h index b49c0c47fc..f0ebc8de7a 100644 --- a/src/option.h +++ b/src/option.h @@ -815,6 +815,7 @@ static char *(p_ttym_values[]) = {"xterm", "xterm2", "dec", "netterm", "jsbterm" # define TTYM_JSBTERM 0x10 # define TTYM_PTERM 0x20 #endif +EXTERN char_u *p_udir; /* 'undodir' */ EXTERN long p_ul; /* 'undolevels' */ EXTERN long p_uc; /* 'updatecount' */ EXTERN long p_ut; /* 'updatetime' */ @@ -1004,6 +1005,7 @@ enum , BV_TS , BV_TW , BV_TX + , BV_UDF , BV_WM , BV_COUNT /* must be the last one */ }; diff --git a/src/os_mac.h b/src/os_mac.h index 314ec32482..f1ed96541f 100644 --- a/src/os_mac.h +++ b/src/os_mac.h @@ -204,7 +204,7 @@ #endif #ifndef DFLT_VDIR -# define DFLT_VDIR "$VIM/vimfiles/view" /* default for 'viewdir' */ +# define DFLT_VDIR "$VIM/vimfiles/view" /* default for 'viewdir' */ #endif #define DFLT_ERRORFILE "errors.err" diff --git a/src/proto/memline.pro b/src/proto/memline.pro index de75a7dcf6..4d50671298 100644 --- a/src/proto/memline.pro +++ b/src/proto/memline.pro @@ -25,6 +25,7 @@ int ml_delete __ARGS((linenr_T lnum, int message)); void ml_setmarked __ARGS((linenr_T lnum)); linenr_T ml_firstmarked __ARGS((void)); void ml_clearmarked __ARGS((void)); +int resolve_symlink __ARGS((char_u *fname, char_u *buf)); char_u *makeswapname __ARGS((char_u *fname, char_u *ffname, buf_T *buf, char_u *dir_name)); char_u *get_file_in_dir __ARGS((char_u *fname, char_u *dname)); void ml_setflags __ARGS((buf_T *buf)); diff --git a/src/proto/sha256.pro b/src/proto/sha256.pro index a6d6be74af..e9a3d3cdd8 100644 --- a/src/proto/sha256.pro +++ b/src/proto/sha256.pro @@ -1,4 +1,7 @@ /* sha256.c */ +void sha256_start __ARGS((context_sha256_T *ctx)); +void sha256_update __ARGS((context_sha256_T *ctx, char_u *input, uint32_t length)); +void sha256_finish __ARGS((context_sha256_T *ctx, char_u digest[32])); char_u *sha256_key __ARGS((char_u *buf)); int sha256_self_test __ARGS((void)); void sha2_seed __ARGS((char_u header[], int header_len)); diff --git a/src/proto/spell.pro b/src/proto/spell.pro index f497dc61c8..8ab544dbf0 100644 --- a/src/proto/spell.pro +++ b/src/proto/spell.pro @@ -2,11 +2,14 @@ int spell_check __ARGS((win_T *wp, char_u *ptr, hlf_T *attrp, int *capcol, int docount)); int spell_move_to __ARGS((win_T *wp, int dir, int allwords, int curline, hlf_T *attrp)); void spell_cat_line __ARGS((char_u *buf, char_u *line, int maxlen)); +int get2c __ARGS((FILE *fd)); +int get3c __ARGS((FILE *fd)); +int get4c __ARGS((FILE *fd)); char_u *did_set_spelllang __ARGS((buf_T *buf)); void spell_free_all __ARGS((void)); void spell_reload __ARGS((void)); int spell_check_msm __ARGS((void)); -void put_bytes __ARGS((FILE *fd, long_u nr, int len)); +int put_bytes __ARGS((FILE *fd, long_u nr, int len)); void ex_mkspell __ARGS((exarg_T *eap)); void ex_spell __ARGS((exarg_T *eap)); void spell_add_word __ARGS((char_u *word, int len, int bad, int idx, int undo)); diff --git a/src/proto/undo.pro b/src/proto/undo.pro index e712f03ae1..e5f28bec41 100644 --- a/src/proto/undo.pro +++ b/src/proto/undo.pro @@ -5,6 +5,9 @@ int u_savesub __ARGS((linenr_T lnum)); int u_inssub __ARGS((linenr_T lnum)); int u_savedel __ARGS((linenr_T lnum, long nlines)); int undo_allowed __ARGS((void)); +void u_compute_hash __ARGS((char_u *hash)); +void u_read_undo __ARGS((char_u *name, char_u *hash)); +void u_write_undo __ARGS((char_u *name, int forceit, buf_T *buf, char_u *hash)); void u_undo __ARGS((int count)); void u_redo __ARGS((int count)); void undo_time __ARGS((long step, int sec, int absolute)); diff --git a/src/sha256.c b/src/sha256.c index 9372d5f00f..048ce75fe1 100644 --- a/src/sha256.c +++ b/src/sha256.c @@ -20,18 +20,9 @@ #include "vim.h" -#ifdef FEAT_CRYPT +#if defined(FEAT_CRYPT) || defined(FEAT_PERSISTENT_UNDO) -typedef struct { - UINT32_T total[2]; - UINT32_T state[8]; - char_u buffer[64]; -} context_sha256_T; - -static void sha256_starts __ARGS((context_sha256_T *ctx)); static void sha256_process __ARGS((context_sha256_T *ctx, char_u data[64])); -static void sha256_update __ARGS((context_sha256_T *ctx, char_u *input, UINT32_T length)); -static void sha256_finish __ARGS((context_sha256_T *ctx, char_u digest[32])); static char_u *sha256_bytes __ARGS((char_u *buf, int buflen)); static unsigned int get_some_time __ARGS((void)); @@ -52,8 +43,8 @@ static unsigned int get_some_time __ARGS((void)); (b)[(i) + 3] = (char_u)((n) ); \ } - static void -sha256_starts(ctx) + void +sha256_start(ctx) context_sha256_T *ctx; { ctx->total[0] = 0; @@ -203,7 +194,7 @@ sha256_process(ctx, data) ctx->state[7] += H; } - static void + void sha256_update(ctx, input, length) context_sha256_T *ctx; char_u *input; @@ -250,7 +241,7 @@ static char_u sha256_padding[64] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - static void + void sha256_finish(ctx, digest) context_sha256_T *ctx; char_u digest[32]; @@ -296,7 +287,7 @@ sha256_bytes(buf, buflen) sha256_self_test(); - sha256_starts(&ctx); + sha256_start(&ctx); sha256_update(&ctx, buf, buflen); sha256_finish(&ctx, sha256sum); for (j = 0; j < 32; j++) @@ -368,7 +359,7 @@ sha256_self_test() } else { - sha256_starts(&ctx); + sha256_start(&ctx); memset(buf, 'a', 1000); for (j = 0; j < 1000; j++) sha256_update(&ctx, (char_u *)buf, 1000); @@ -416,7 +407,7 @@ sha2_seed(header, header_len) for (i = 0; i < (int)sizeof(random_data) - 1; i++) random_data[i] = (char_u)((get_some_time() ^ rand()) & 0xff); - sha256_starts(&ctx); + sha256_start(&ctx); sha256_update(&ctx, (char_u *)random_data, sizeof(random_data)); sha256_finish(&ctx, sha256sum); @@ -424,4 +415,4 @@ sha2_seed(header, header_len) header[i] = sha256sum[i % sizeof(sha256sum)]; } -#endif /* FEAT_CRYPT */ +#endif /* FEAT_CRYPT || FEAT_PERSISTENT_UNDO */ diff --git a/src/spell.c b/src/spell.c index 47b86ade83..1c3fd0f651 100644 --- a/src/spell.c +++ b/src/spell.c @@ -854,9 +854,6 @@ static char_u *spell_enc __ARGS((void)); static void int_wordlist_spl __ARGS((char_u *fname)); static void spell_load_cb __ARGS((char_u *fname, void *cookie)); static slang_T *spell_load_file __ARGS((char_u *fname, char_u *lang, slang_T *old_lp, int silent)); -static int get2c __ARGS((FILE *fd)); -static int get3c __ARGS((FILE *fd)); -static int get4c __ARGS((FILE *fd)); static time_t get8c __ARGS((FILE *fd)); static char_u *read_cnt_string __ARGS((FILE *fd, int cnt_bytes, int *lenp)); static char_u *read_string __ARGS((FILE *fd, int cnt)); @@ -2988,7 +2985,7 @@ endOK: /* * Read 2 bytes from "fd" and turn them into an int, MSB first. */ - static int + int get2c(fd) FILE *fd; { @@ -3002,7 +2999,7 @@ get2c(fd) /* * Read 3 bytes from "fd" and turn them into an int, MSB first. */ - static int + int get3c(fd) FILE *fd; { @@ -3017,7 +3014,7 @@ get3c(fd) /* * Read 4 bytes from "fd" and turn them into an int, MSB first. */ - static int + int get4c(fd) FILE *fd; { @@ -8018,7 +8015,7 @@ node_equal(n1, n2) /* * Write a number to file "fd", MSB first, in "len" bytes. */ - void + int put_bytes(fd, nr, len) FILE *fd; long_u nr; @@ -8027,7 +8024,9 @@ put_bytes(fd, nr, len) int i; for (i = len - 1; i >= 0; --i) - putc((int)(nr >> (i * 8)), fd); + if (putc((int)(nr >> (i * 8)), fd) == EOF) + return FAIL; + return OK; } #ifdef _MSC_VER diff --git a/src/structs.h b/src/structs.h index a34fa664aa..a7632b8517 100644 --- a/src/structs.h +++ b/src/structs.h @@ -1465,6 +1465,9 @@ struct file_buffer char_u *b_p_dict; /* 'dictionary' local value */ char_u *b_p_tsr; /* 'thesaurus' local value */ #endif +#ifdef FEAT_PERSISTENT_UNDO + int b_p_udf; /* 'undofile' */ +#endif /* end of buffer options */ @@ -2392,3 +2395,9 @@ typedef struct #define CPT_KIND 2 /* "kind" */ #define CPT_INFO 3 /* "info" */ #define CPT_COUNT 4 /* Number of entries */ + +typedef struct { + UINT32_T total[2]; + UINT32_T state[8]; + char_u buffer[64]; +} context_sha256_T; diff --git a/src/testdir/Makefile b/src/testdir/Makefile index 6a8e85f6bd..53e085469e 100644 --- a/src/testdir/Makefile +++ b/src/testdir/Makefile @@ -69,7 +69,7 @@ test1.out: test1.in fi \ else echo $* NO OUTPUT >>test.log; \ fi" - #-rm -rf X* test.ok viminfo + -rm -rf X* test.ok viminfo test49.out: test49.vim diff --git a/src/testdir/test61.in b/src/testdir/test61.in index 7ce72e627f..909d1230c2 100644 --- a/src/testdir/test61.in +++ b/src/testdir/test61.in @@ -50,6 +50,53 @@ obbbbu:.w >>test.out obbbb:set ul=100 :undojoin occccu:.w >>test.out +:" +:" Test 'undofile': first a simple one-line change. +:set nocp ul=100 undofile +:e! Xtestfile +ggdGithis is one line:set ul=100 +:s/one/ONE/ +:set ul=100 +:w +:bwipe! +:e Xtestfile +u:.w >>test.out +:" +:" Test 'undofile', change in original file fails check +:set noundofile +:e! Xtestfile +:s/line/Line/ +:w +:set undofile +:bwipe! +:e Xtestfile +u:.w >>test.out +:" +:" Test 'undofile', add 10 lines, delete 6 lines, undo 3 +:set undofile +ggdGione +two +three +four +five +six +seven +eight +nine +ten:set ul=100 +3Gdd:set ul=100 +dd:set ul=100 +dd:set ul=100 +dd:set ul=100 +dd:set ul=100 +dd:set ul=100 +:w +:bwipe! +:e Xtestfile +uuu:w >>test.out +:" +:" Rename the undo file so that it gets cleaned up. +:call rename(".Xtestfile.un~", "Xtestundo") :qa! ENDTEST diff --git a/src/testdir/test61.ok b/src/testdir/test61.ok index 020dd5383b..ea88c07c36 100644 --- a/src/testdir/test61.ok +++ b/src/testdir/test61.ok @@ -22,3 +22,12 @@ 123456abc aaaa aaaa +this is one line +this is ONE Line +one +two +six +seven +eight +nine +ten diff --git a/src/undo.c b/src/undo.c index 4035466148..b4945d12e1 100644 --- a/src/undo.c +++ b/src/undo.c @@ -99,6 +99,14 @@ static void u_freeheader __ARGS((buf_T *buf, u_header_T *uhp, u_header_T **uhpp) static void u_freebranch __ARGS((buf_T *buf, u_header_T *uhp, u_header_T **uhpp)); static void u_freeentries __ARGS((buf_T *buf, u_header_T *uhp, u_header_T **uhpp)); static void u_freeentry __ARGS((u_entry_T *, long)); +#ifdef FEAT_PERSISTENT_UNDO +static void unserialize_pos __ARGS((pos_T *pos, FILE *fp)); +static void unserialize_visualinfo __ARGS((visualinfo_T *info, FILE *fp)); +static char_u *u_get_undo_file_name __ARGS((char_u *, int reading)); +static int serialize_uep __ARGS((u_entry_T *uep, FILE *fp)); +static void serialize_pos __ARGS((pos_T pos, FILE *fp)); +static void serialize_visualinfo __ARGS((visualinfo_T info, FILE *fp)); +#endif #ifdef U_USE_MALLOC # define U_FREE_LINE(ptr) vim_free(ptr) @@ -119,6 +127,8 @@ static long u_newcount, u_oldcount; */ static int undo_undoes = FALSE; +static int lastmark = 0; + #ifdef U_DEBUG /* * Check the undo structures for being valid. Print a warning when something @@ -652,6 +662,795 @@ nomem: return FAIL; } +#ifdef FEAT_PERSISTENT_UNDO + +# define UF_START_MAGIC 0xfeac /* magic at start of undofile */ +# define UF_HEADER_MAGIC 0x5fd0 /* magic at start of header */ +# define UF_END_MAGIC 0xe7aa /* magic after last header */ +# define UF_VERSION 1 /* 2-byte undofile version number */ + +/* + * Compute the hash for the current buffer text into hash[UNDO_HASH_SIZE]. + */ + void +u_compute_hash(hash) + char_u *hash; +{ + context_sha256_T ctx; + linenr_T lnum; + char_u *p; + + sha256_start(&ctx); + for (lnum = 1; lnum < curbuf->b_ml.ml_line_count; ++lnum) + { + p = ml_get(lnum); + sha256_update(&ctx, p, STRLEN(p) + 1); + } + sha256_finish(&ctx, hash); +} + +/* + * Unserialize the pos_T at the current position in fp. + */ + static void +unserialize_pos(pos, fp) + pos_T *pos; + FILE *fp; +{ + pos->lnum = get4c(fp); + pos->col = get4c(fp); +#ifdef FEAT_VIRTUALEDIT + pos->coladd = get4c(fp); +#else + (void)get4c(fp); +#endif +} + +/* + * Unserialize the visualinfo_T at the current position in fp. + */ + static void +unserialize_visualinfo(info, fp) + visualinfo_T *info; + FILE *fp; +{ + unserialize_pos(&info->vi_start, fp); + unserialize_pos(&info->vi_end, fp); + info->vi_mode = get4c(fp); + info->vi_curswant = get4c(fp); +} + +/* + * Return an allocated string of the full path of the target undofile. + * When "reading" is TRUE find the file to read, go over all directories in + * 'undodir'. + * When "reading" is FALSE use the first name where the directory exists. + */ + static char_u * +u_get_undo_file_name(buf_ffname, reading) + char_u *buf_ffname; + int reading; +{ + char_u *dirp; + char_u dir_name[IOSIZE + 1]; + char_u *munged_name = NULL; + char_u *undo_file_name = NULL; + int dir_len; + char_u *p; + struct stat st; + char_u *ffname = buf_ffname; +#ifdef HAVE_READLINK + char_u fname_buf[MAXPATHL]; +#endif + + if (ffname == NULL) + return NULL; + +#ifdef HAVE_READLINK + /* Expand symlink in the file name, so that we put the undo file with the + * actual file instead of with the symlink. */ + if (resolve_symlink(ffname, fname_buf) == OK) + ffname = fname_buf; +#endif + + /* Loop over 'undodir'. When reading find the first file that exists. + * When not reading use the first directory that exists or ".". */ + dirp = p_udir; + while (*dirp != NUL) + { + dir_len = copy_option_part(&dirp, dir_name, IOSIZE, ","); + if (dir_len == 1 && dir_name[0] == '.') + { + /* Use same directory as the ffname, + * "dir/name" -> "dir/.name.un~" */ + undo_file_name = vim_strnsave(ffname, STRLEN(ffname) + 5); + if (undo_file_name == NULL) + break; + p = gettail(undo_file_name); + mch_memmove(p + 1, p, STRLEN(p) + 1); + *p = '.'; + STRCAT(p, ".un~"); + } + else + { + dir_name[dir_len] = NUL; + if (mch_isdir(dir_name)) + { + if (munged_name == NULL) + { + munged_name = vim_strsave(ffname); + if (munged_name == NULL) + return NULL; + for (p = munged_name; *p != NUL; mb_ptr_adv(p)) + if (vim_ispathsep(*p)) + *p = '%'; + } + undo_file_name = concat_fnames(dir_name, munged_name, |