diff options
author | Josh Rahm <joshuarahm@gmail.com> | 2023-11-30 20:35:25 +0000 |
---|---|---|
committer | Josh Rahm <joshuarahm@gmail.com> | 2023-11-30 20:35:25 +0000 |
commit | 1b7b916b7631ddf73c38e3a0070d64e4636cb2f3 (patch) | |
tree | cd08258054db80bb9a11b1061bb091c70b76926a /src/nvim/undo.c | |
parent | eaa89c11d0f8aefbb512de769c6c82f61a8baca3 (diff) | |
parent | 4a8bf24ac690004aedf5540fa440e788459e5e34 (diff) | |
download | rneovim-aucmd_textputpost.tar.gz rneovim-aucmd_textputpost.tar.bz2 rneovim-aucmd_textputpost.zip |
Merge remote-tracking branch 'upstream/master' into aucmd_textputpostaucmd_textputpost
Diffstat (limited to 'src/nvim/undo.c')
-rw-r--r-- | src/nvim/undo.c | 295 |
1 files changed, 177 insertions, 118 deletions
diff --git a/src/nvim/undo.c b/src/nvim/undo.c index 0f12c00f15..93a973c33d 100644 --- a/src/nvim/undo.c +++ b/src/nvim/undo.c @@ -1,6 +1,3 @@ -// This is an open source non-commercial project. Dear PVS-Studio, please check -// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com - // undo.c: multi level undo facility // The saved lines are stored in a list of lists (one for each buffer): @@ -80,12 +77,13 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <sys/types.h> #include <time.h> #include <uv.h> #include "auto/config.h" #include "klib/kvec.h" -#include "nvim/ascii.h" +#include "nvim/ascii_defs.h" #include "nvim/autocmd.h" #include "nvim/buffer.h" #include "nvim/buffer_updates.h" @@ -93,39 +91,41 @@ #include "nvim/cursor.h" #include "nvim/drawscreen.h" #include "nvim/edit.h" +#include "nvim/eval/funcs.h" #include "nvim/eval/typval.h" -#include "nvim/eval/typval_defs.h" #include "nvim/ex_cmds_defs.h" +#include "nvim/ex_docmd.h" #include "nvim/ex_getln.h" #include "nvim/extmark.h" #include "nvim/fileio.h" #include "nvim/fold.h" +#include "nvim/func_attr.h" #include "nvim/garray.h" #include "nvim/getchar.h" #include "nvim/gettext.h" #include "nvim/globals.h" -#include "nvim/highlight_defs.h" -#include "nvim/macros.h" +#include "nvim/highlight.h" +#include "nvim/macros_defs.h" #include "nvim/mark.h" #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/message.h" #include "nvim/option.h" -#include "nvim/os/fs_defs.h" +#include "nvim/option_vars.h" +#include "nvim/os/fs.h" #include "nvim/os/input.h" -#include "nvim/os/os.h" #include "nvim/os/os_defs.h" #include "nvim/os/time.h" #include "nvim/path.h" -#include "nvim/pos.h" -#include "nvim/screen.h" +#include "nvim/pos_defs.h" #include "nvim/sha256.h" +#include "nvim/spell.h" #include "nvim/state.h" #include "nvim/strings.h" -#include "nvim/types.h" +#include "nvim/types_defs.h" #include "nvim/undo.h" #include "nvim/undo_defs.h" -#include "nvim/vim.h" +#include "nvim/vim_defs.h" /// Structure passed around between undofile functions. typedef struct { @@ -137,8 +137,15 @@ typedef struct { # include "undo.c.generated.h" #endif +static const char e_undo_list_corrupt[] + = N_("E439: Undo list corrupt"); +static const char e_undo_line_missing[] + = N_("E440: Undo line missing"); +static const char e_write_error_in_undo_file_str[] + = N_("E829: Write error in undo file: %s"); + // used in undo_end() to report number of added and deleted lines -static long u_newcount, u_oldcount; +static int u_newcount, u_oldcount; // When 'u' flag included in 'cpoptions', we behave like vi. Need to remember // the action that "u" should do. @@ -174,12 +181,12 @@ static void u_check_tree(u_header_T *uhp, u_header_T *exp_uh_next, u_header_T *e // Check pointers back are correct. if (uhp->uh_next.ptr != exp_uh_next) { emsg("uh_next wrong"); - smsg("expected: 0x%x, actual: 0x%x", + smsg(0, "expected: 0x%x, actual: 0x%x", exp_uh_next, uhp->uh_next.ptr); } if (uhp->uh_alt_prev.ptr != exp_uh_alt_prev) { emsg("uh_alt_prev wrong"); - smsg("expected: 0x%x, actual: 0x%x", + smsg(0, "expected: 0x%x, actual: 0x%x", exp_uh_alt_prev, uhp->uh_alt_prev.ptr); } @@ -216,7 +223,7 @@ static void u_check(int newhead_may_be_NULL) } if (header_count != curbuf->b_u_numhead) { emsg("b_u_numhead invalid"); - smsg("expected: %" PRId64 ", actual: %" PRId64, + smsg(0, "expected: %" PRId64 ", actual: %" PRId64, (int64_t)header_count, (int64_t)curbuf->b_u_numhead); } } @@ -241,15 +248,20 @@ int u_save_cursor(void) /// Returns FAIL when lines could not be saved, OK otherwise. int u_save(linenr_T top, linenr_T bot) { - if (top >= bot || bot > (curbuf->b_ml.ml_line_count + 1)) { + return u_save_buf(curbuf, top, bot); +} + +int u_save_buf(buf_T *buf, linenr_T top, linenr_T bot) +{ + if (top >= bot || bot > (buf->b_ml.ml_line_count + 1)) { return FAIL; // rely on caller to do error messages } if (top + 2 == bot) { - u_saveline((linenr_T)(top + 1)); + u_saveline(buf, top + 1); } - return u_savecommon(curbuf, top, bot, (linenr_T)0, false); + return u_savecommon(buf, top, bot, 0, false); } /// Save the line "lnum" (used by ":s" and "~" command). @@ -275,9 +287,9 @@ int u_inssub(linenr_T lnum) /// becomes empty. /// Careful: may trigger autocommands that reload the buffer. /// Returns FAIL when lines could not be saved, OK otherwise. -int u_savedel(linenr_T lnum, long nlines) +int u_savedel(linenr_T lnum, linenr_T nlines) { - return u_savecommon(curbuf, lnum - 1, lnum + (linenr_T)nlines, + return u_savecommon(curbuf, lnum - 1, lnum + nlines, nlines == curbuf->b_ml.ml_line_count ? 2 : lnum, false); } @@ -301,7 +313,7 @@ bool undo_allowed(buf_T *buf) // Don't allow changes in the buffer while editing the cmdline. The // caller of getcmdline() may get confused. - if (textlock != 0) { + if (textlock != 0 || expr_map_locked()) { emsg(_(e_textlock)); return false; } @@ -310,7 +322,7 @@ bool undo_allowed(buf_T *buf) } /// Get the 'undolevels' value for the current buffer. -static long get_undolevel(buf_T *buf) +static OptInt get_undolevel(buf_T *buf) { if (buf->b_p_ul == NO_LOCAL_UNDOLEVEL) { return p_ul; @@ -364,7 +376,7 @@ int u_savecommon(buf_T *buf, linenr_T top, linenr_T bot, linenr_T newbot, int re u_entry_T *uep; u_entry_T *prev_uep; - long size = bot - top - 1; + linenr_T size = bot - top - 1; // If curbuf->b_u_synced == true make a new header. if (buf->b_u_synced) { @@ -493,7 +505,7 @@ int u_savecommon(buf_T *buf, linenr_T top, linenr_T bot, linenr_T newbot, int re if (size == 1) { uep = u_get_headentry(buf); prev_uep = NULL; - for (long i = 0; i < 10; i++) { + for (int i = 0; i < 10; i++) { if (uep == NULL) { break; } @@ -575,7 +587,7 @@ int u_savecommon(buf_T *buf, linenr_T top, linenr_T bot, linenr_T newbot, int re if (size > 0) { uep->ue_array = xmalloc(sizeof(char *) * (size_t)size); linenr_T lnum; - long i; + int i; for (i = 0, lnum = top + 1; i < size; i++) { fast_breakcheck(); if (got_int) { @@ -624,7 +636,7 @@ int u_savecommon(buf_T *buf, linenr_T top, linenr_T bot, linenr_T newbot, int re // extra fields for uhp #define UHP_SAVE_NR 1 -static char e_not_open[] = N_("E828: Cannot open undo file for writing: %s"); +static const char e_not_open[] = N_("E828: Cannot open undo file for writing: %s"); /// Compute the hash for a buffer text into hash[UNDO_HASH_SIZE]. /// @@ -636,7 +648,7 @@ void u_compute_hash(buf_T *buf, uint8_t *hash) context_sha256_T ctx; sha256_start(&ctx); for (linenr_T lnum = 1; lnum <= buf->b_ml.ml_line_count; lnum++) { - char *p = ml_get_buf(buf, lnum, false); + char *p = ml_get_buf(buf, lnum); sha256_update(&ctx, (uint8_t *)p, strlen(p) + 1); } sha256_finish(&ctx, hash); @@ -705,7 +717,7 @@ char *u_get_undo_file_name(const char *const buf_ffname, const bool reading) // Last directory in the list does not exist, create it. int ret; char *failed_dir; - if ((ret = os_mkdir_recurse(dir_name, 0755, &failed_dir)) != 0) { + if ((ret = os_mkdir_recurse(dir_name, 0755, &failed_dir, NULL)) != 0) { semsg(_("E5003: Unable to create directory \"%s\" for undo file: %s"), failed_dir, os_strerror(ret)); xfree(failed_dir); @@ -904,7 +916,7 @@ static u_header_T *unserialize_uhp(bufinfo_T *bi, const char *file_name) uhp->uh_time = undo_read_time(bi); // Unserialize optional fields. - for (;;) { + while (true) { int len = undo_read_byte(bi); if (len == EOF) { @@ -1074,7 +1086,7 @@ static u_entry_T *unserialize_uep(bufinfo_T *bi, bool *error, const char *file_n char **array = NULL; if (uep->ue_size > 0) { - if ((size_t)uep->ue_size < SIZE_MAX / sizeof(char *)) { // -V547 + if ((size_t)uep->ue_size < SIZE_MAX / sizeof(char *)) { array = xmalloc(sizeof(char *) * (size_t)uep->ue_size); memset(array, 0, sizeof(char *) * (size_t)uep->ue_size); } @@ -1165,7 +1177,7 @@ void u_write_undo(const char *const name, const bool forceit, buf_T *const buf, if (file_name == NULL) { if (p_verbose > 0) { verbose_enter(); - smsg("%s", _("Cannot write undo file in any directory in 'undodir'")); + smsg(0, "%s", _("Cannot write undo file in any directory in 'undodir'")); verbose_leave(); } return; @@ -1179,7 +1191,7 @@ void u_write_undo(const char *const name, const bool forceit, buf_T *const buf, // allow the user to access the undo file. int perm = 0600; if (buf->b_ffname != NULL) { - perm = os_getperm((const char *)buf->b_ffname); + perm = os_getperm(buf->b_ffname); if (perm < 0) { perm = 0600; } @@ -1201,7 +1213,7 @@ void u_write_undo(const char *const name, const bool forceit, buf_T *const buf, if (name == NULL) { verbose_enter(); } - smsg(_("Will not overwrite with undo file, cannot read: %s"), + smsg(0, _("Will not overwrite with undo file, cannot read: %s"), file_name); if (name == NULL) { verbose_leave(); @@ -1218,7 +1230,7 @@ void u_write_undo(const char *const name, const bool forceit, buf_T *const buf, if (name == NULL) { verbose_enter(); } - smsg(_("Will not overwrite, this is not an undo file: %s"), + smsg(0, _("Will not overwrite, this is not an undo file: %s"), file_name); if (name == NULL) { verbose_leave(); @@ -1248,7 +1260,7 @@ void u_write_undo(const char *const name, const bool forceit, buf_T *const buf, (void)os_setperm(file_name, perm); if (p_verbose > 0) { verbose_enter(); - smsg(_("Writing undo file: %s"), file_name); + smsg(0, _("Writing undo file: %s"), file_name); verbose_leave(); } @@ -1333,20 +1345,22 @@ void u_write_undo(const char *const name, const bool forceit, buf_T *const buf, } #endif + if (p_fs && fflush(fp) == 0 && os_fsync(fd) != 0) { + write_ok = false; + } + write_error: fclose(fp); if (!write_ok) { - semsg(_("E829: write error in undo file: %s"), file_name); + semsg(_(e_write_error_in_undo_file_str), file_name); } -#ifdef HAVE_ACL if (buf->b_ffname != NULL) { // For systems that support ACL: get the ACL from the original file. vim_acl_T acl = os_get_acl(buf->b_ffname); os_set_acl(file_name, acl); os_free_acl(acl); } -#endif theend: if (file_name != name) { @@ -1383,7 +1397,7 @@ void u_read_undo(char *name, const uint8_t *hash, const char *orig_name FUNC_ATT && file_info_undo.stat.st_uid != getuid()) { if (p_verbose > 0) { verbose_enter(); - smsg(_("Not reading undo file, owner differs: %s"), + smsg(0, _("Not reading undo file, owner differs: %s"), file_name); verbose_leave(); } @@ -1396,7 +1410,7 @@ void u_read_undo(char *name, const uint8_t *hash, const char *orig_name FUNC_ATT if (p_verbose > 0) { verbose_enter(); - smsg(_("Reading undo file: %s"), file_name); + smsg(0, _("Reading undo file: %s"), file_name); verbose_leave(); } @@ -1472,8 +1486,8 @@ void u_read_undo(char *name, const uint8_t *hash, const char *orig_name FUNC_ATT time_t seq_time = undo_read_time(&bi); // Optional header fields. - long last_save_nr = 0; - for (;;) { + int last_save_nr = 0; + while (true) { int len = undo_read_byte(&bi); if (len == 0 || len == EOF) { @@ -1498,12 +1512,12 @@ void u_read_undo(char *name, const uint8_t *hash, const char *orig_name FUNC_ATT // sequence numbers of the headers. // When there are no headers uhp_table is NULL. if (num_head > 0) { - if ((size_t)num_head < SIZE_MAX / sizeof(*uhp_table)) { // -V547 + if ((size_t)num_head < SIZE_MAX / sizeof(*uhp_table)) { uhp_table = xmalloc((size_t)num_head * sizeof(*uhp_table)); } } - long num_read_uhps = 0; + int num_read_uhps = 0; int c; while ((c = undo_read_2c(&bi)) == UF_HEADER_MAGIC) { @@ -1632,14 +1646,14 @@ void u_read_undo(char *name, const uint8_t *hash, const char *orig_name FUNC_ATT #endif if (name != NULL) { - smsg(_("Finished reading undo file %s"), file_name); + smsg(0, _("Finished reading undo file %s"), file_name); } goto theend; error: xfree(line_ptr); if (uhp_table != NULL) { - for (long i = 0; i < num_read_uhps; i++) { + for (int i = 0; i < num_read_uhps; i++) { if (uhp_table[i] != NULL) { u_free_uhp(uhp_table[i]); } @@ -1811,8 +1825,8 @@ bool u_undo_and_forget(int count, bool do_buf_event) if (curbuf->b_u_curhead) { to_forget->uh_alt_next.ptr = NULL; curbuf->b_u_curhead->uh_alt_prev.ptr = to_forget->uh_alt_prev.ptr; - curbuf->b_u_seq_cur = curbuf->b_u_curhead->uh_next.ptr ? - curbuf->b_u_curhead->uh_next.ptr->uh_seq : 0; + curbuf->b_u_seq_cur = curbuf->b_u_curhead->uh_next.ptr + ? curbuf->b_u_curhead->uh_next.ptr->uh_seq : 0; } else if (curbuf->b_u_newhead) { curbuf->b_u_seq_cur = curbuf->b_u_newhead->uh_seq; } @@ -1867,7 +1881,7 @@ static void u_doit(int startcount, bool quiet, bool do_buf_event) curbuf->b_u_curhead = curbuf->b_u_oldhead; beep_flush(); if (count == startcount - 1) { - msg(_("Already at oldest change")); + msg(_("Already at oldest change"), 0); return; } break; @@ -1878,7 +1892,7 @@ static void u_doit(int startcount, bool quiet, bool do_buf_event) if (curbuf->b_u_curhead == NULL || get_undolevel(curbuf) <= 0) { beep_flush(); // nothing to redo if (count == startcount - 1) { - msg(_("Already at newest change")); + msg(_("Already at newest change"), 0); return; } break; @@ -1904,7 +1918,7 @@ static void u_doit(int startcount, bool quiet, bool do_buf_event) // 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(long step, bool sec, bool file, bool absolute) +void undo_time(int step, bool sec, bool file, bool absolute) { if (text_locked()) { text_locked_msg(); @@ -1922,8 +1936,8 @@ void undo_time(long step, bool sec, bool file, bool absolute) u_oldcount = -1; } - long target; - long closest; + int target; + int closest; u_header_T *uhp = NULL; bool dosec = sec; bool dofile = file; @@ -1937,7 +1951,7 @@ void undo_time(long step, bool sec, bool file, bool absolute) closest = -1; } else { if (dosec) { - target = (long)(curbuf->b_u_time_cur) + step; + target = (int)curbuf->b_u_time_cur + step; } else if (dofile) { if (step < 0) { // Going back to a previous write. If there were changes after @@ -1982,7 +1996,7 @@ void undo_time(long step, bool sec, bool file, bool absolute) closest = -1; } else { if (dosec) { - closest = (long)(os_time() + 1); + closest = (int)(os_time() + 1); } else if (dofile) { closest = curbuf->b_u_save_nr_last + 2; } else { @@ -1993,8 +2007,8 @@ void undo_time(long step, bool sec, bool file, bool absolute) } } } - long closest_start = closest; - long closest_seq = curbuf->b_u_seq_cur; + int closest_start = closest; + int closest_seq = curbuf->b_u_seq_cur; int mark; int nomark = 0; // shut up compiler @@ -2026,9 +2040,9 @@ void undo_time(long step, bool sec, bool file, bool absolute) while (uhp != NULL) { uhp->uh_walk = mark; - long val = dosec ? (long)(uhp->uh_time) : - dofile ? uhp->uh_save_nr - : uhp->uh_seq; + int val = dosec ? (int)(uhp->uh_time) + : dofile ? uhp->uh_save_nr + : uhp->uh_seq; if (round == 1 && !(dofile && val == 0)) { // Remember the header that is closest to the target. @@ -2036,7 +2050,7 @@ void undo_time(long step, bool sec, bool file, bool absolute) // "b_u_seq_cur"). When the timestamp is equal find the // highest/lowest sequence number. if ((step < 0 ? uhp->uh_seq <= curbuf->b_u_seq_cur - : uhp->uh_seq > curbuf->b_u_seq_cur) + : uhp->uh_seq > curbuf->b_u_seq_cur) && ((dosec && val == closest) ? (step < 0 ? uhp->uh_seq < closest_seq @@ -2102,9 +2116,9 @@ void undo_time(long step, bool sec, bool file, bool absolute) if (closest == closest_start) { if (step < 0) { - msg(_("Already at oldest change")); + msg(_("Already at oldest change"), 0); } else { - msg(_("Already at newest change")); + msg(_("Already at newest change"), 0); } return; } @@ -2277,12 +2291,12 @@ static void u_undoredo(int undo, bool do_buf_event) || bot > curbuf->b_ml.ml_line_count + 1) { unblock_autocmds(); iemsg(_("E438: u_undo: line numbers wrong")); - changed(); // don't want UNCHANGED now + changed(curbuf); // don't want UNCHANGED now return; } linenr_T oldsize = bot - top - 1; // number of lines before undo - linenr_T newsize = (linenr_T)uep->ue_size; // number of lines after undo + linenr_T newsize = uep->ue_size; // number of lines after undo if (top < newlnum) { // If the saved cursor is somewhere in this undo block, move it to @@ -2296,7 +2310,7 @@ static void u_undoredo(int undo, bool do_buf_event) // Use the first line that actually changed. Avoids that // undoing auto-formatting puts the cursor in the previous // line. - long i; + int i; for (i = 0; i < newsize && i < oldsize; i++) { if (strcmp(uep->ue_array[i], ml_get(top + 1 + (linenr_T)i)) != 0) { break; @@ -2318,7 +2332,7 @@ static void u_undoredo(int undo, bool do_buf_event) if (oldsize > 0) { newarray = xmalloc(sizeof(char *) * (size_t)oldsize); // delete backwards, it goes faster in most cases - long i; + int i; linenr_T lnum; for (lnum = bot - 1, i = oldsize; --i >= 0; lnum--) { // what can we do when we run out of memory? @@ -2336,15 +2350,15 @@ static void u_undoredo(int undo, bool do_buf_event) // insert the lines in u_array between top and bot if (newsize) { - long i; + int i; linenr_T lnum; for (lnum = top, i = 0; i < newsize; i++, lnum++) { // If the file is empty, there is an empty line 1 that we // should get rid of, by replacing it with the new line if (empty_buffer && lnum == 0) { - ml_replace((linenr_T)1, uep->ue_array[i], true); + ml_replace(1, uep->ue_array[i], true); } else { - ml_append(lnum, uep->ue_array[i], (colnr_T)0, false); + ml_append(lnum, uep->ue_array[i], 0, false); } xfree(uep->ue_array[i]); } @@ -2362,7 +2376,13 @@ static void u_undoredo(int undo, bool do_buf_event) } } - changed_lines(top + 1, 0, bot, newsize - oldsize, do_buf_event); + changed_lines(curbuf, top + 1, 0, bot, newsize - oldsize, do_buf_event); + // When text has been changed, possibly the start of the next line + // may have SpellCap that should be removed or it needs to be + // displayed. Schedule the next line for redrawing just in case. + if (spell_check_window(curwin) && bot <= curbuf->b_ml.ml_line_count) { + redrawWinline(curwin, bot); + } // Set the '[ mark. if (top + 1 < curbuf->b_op_start.lnum) { @@ -2398,13 +2418,13 @@ static void u_undoredo(int undo, bool do_buf_event) // Adjust Extmarks ExtmarkUndoObject undo_info; if (undo) { - for (long i = (int)kv_size(curhead->uh_extmark) - 1; i > -1; i--) { + for (int i = (int)kv_size(curhead->uh_extmark) - 1; i > -1; i--) { undo_info = kv_A(curhead->uh_extmark, i); extmark_apply_undo(undo_info, undo); } // redo } else { - for (long i = 0; i < (int)kv_size(curhead->uh_extmark); i++) { + for (int i = 0; i < (int)kv_size(curhead->uh_extmark); i++) { undo_info = kv_A(curhead->uh_extmark, i); extmark_apply_undo(undo_info, undo); } @@ -2422,7 +2442,7 @@ static void u_undoredo(int undo, bool do_buf_event) curbuf->b_ml.ml_flags |= ML_EMPTY; } if (old_flags & UH_CHANGED) { - changed(); + changed(curbuf); } else { unchanged(curbuf, false, true); } @@ -2435,7 +2455,7 @@ static void u_undoredo(int undo, bool do_buf_event) } // restore marks from before undo/redo - for (long i = 0; i < NMARKS; i++) { + for (int i = 0; i < NMARKS; i++) { if (curhead->uh_namedm[i].mark.lnum != 0) { free_fmark(curbuf->b_namedm[i]); curbuf->b_namedm[i] = curhead->uh_namedm[i]; @@ -2462,7 +2482,7 @@ static void u_undoredo(int undo, bool do_buf_event) if (curhead->uh_cursor.lnum == curwin->w_cursor.lnum) { curwin->w_cursor.col = curhead->uh_cursor.col; if (virtual_active() && curhead->uh_cursor_vcol >= 0) { - coladvance((colnr_T)curhead->uh_cursor_vcol); + coladvance(curhead->uh_cursor_vcol); } else { curwin->w_cursor.coladd = 0; } @@ -2486,8 +2506,8 @@ static void u_undoredo(int undo, bool do_buf_event) if (undo) { // We are below the previous undo. However, to make ":earlier 1s" // work we compute this as being just above the just undone change. - curbuf->b_u_seq_cur = curhead->uh_next.ptr ? - curhead->uh_next.ptr->uh_seq : 0; + curbuf->b_u_seq_cur = curhead->uh_next.ptr + ? curhead->uh_next.ptr->uh_seq : 0; } // Remember where we are for ":earlier 1f" and ":later 1f". @@ -2589,7 +2609,7 @@ static void u_undo_end(bool did_undo, bool absolute, bool quiet) u_oldcount < 0 ? (int64_t)-u_oldcount : (int64_t)u_oldcount, _(msgstr), did_undo ? _("before") : _("after"), - uhp == NULL ? (int64_t)0L : (int64_t)uhp->uh_seq, + uhp == NULL ? 0 : (int64_t)uhp->uh_seq, msgbuf); } @@ -2600,7 +2620,7 @@ void undo_fmt_time(char *buf, size_t buflen, time_t tt) struct tm curtime; os_localtime_r(&tt, &curtime); size_t n; - if (time(NULL) - tt < (60L * 60L * 12L)) { + if (time(NULL) - tt < (60 * 60 * 12)) { // within 12 hours n = strftime(buf, buflen, "%H:%M:%S", &curtime); } else { @@ -2654,13 +2674,13 @@ void ex_undolist(exarg_T *eap) while (uhp != NULL) { if (uhp->uh_prev.ptr == NULL && uhp->uh_walk != nomark && uhp->uh_walk != mark) { - vim_snprintf(IObuff, IOSIZE, "%6ld %7d ", uhp->uh_seq, changes); + vim_snprintf(IObuff, IOSIZE, "%6d %7d ", uhp->uh_seq, changes); undo_fmt_time(IObuff + strlen(IObuff), IOSIZE - strlen(IObuff), uhp->uh_time); if (uhp->uh_save_nr > 0) { while (strlen(IObuff) < 33) { - STRCAT(IObuff, " "); + xstrlcat(IObuff, " ", IOSIZE); } - vim_snprintf_add(IObuff, IOSIZE, " %3ld", uhp->uh_save_nr); + vim_snprintf_add(IObuff, IOSIZE, " %3d", uhp->uh_save_nr); } GA_APPEND(char *, &ga, xstrdup(IObuff)); } @@ -2697,7 +2717,7 @@ void ex_undolist(exarg_T *eap) } if (GA_EMPTY(&ga)) { - msg(_("Nothing to undo")); + msg(_("Nothing to undo"), 0); } else { sort_strings(ga.ga_data, ga.ga_len); @@ -2762,7 +2782,7 @@ void u_find_first_changed(void) linenr_T lnum; for (lnum = 1; lnum < curbuf->b_ml.ml_line_count && lnum <= uep->ue_size; lnum++) { - if (strcmp(ml_get_buf(curbuf, lnum, false), uep->ue_array[lnum - 1]) != 0) { + if (strcmp(ml_get_buf(curbuf, lnum), uep->ue_array[lnum - 1]) != 0) { clearpos(&(uhp->uh_cursor)); uhp->uh_cursor.lnum = lnum; return; @@ -2807,7 +2827,7 @@ static void u_unch_branch(u_header_T *uhp) static u_entry_T *u_get_headentry(buf_T *buf) { if (buf->b_u_newhead == NULL || buf->b_u_newhead->uh_entry == NULL) { - iemsg(_("E439: undo list corrupt")); + iemsg(_(e_undo_list_corrupt)); return NULL; } return buf->b_u_newhead->uh_entry; @@ -2828,9 +2848,9 @@ static void u_getbot(buf_T *buf) // inserted (0 - deleted) since calling u_save. This is equal to the // old line count subtracted from the current line count. linenr_T extra = buf->b_ml.ml_line_count - uep->ue_lcount; - uep->ue_bot = uep->ue_top + (linenr_T)uep->ue_size + 1 + extra; + uep->ue_bot = uep->ue_top + uep->ue_size + 1 + extra; if (uep->ue_bot < 1 || uep->ue_bot > buf->b_ml.ml_line_count) { - iemsg(_("E440: undo line missing")); + iemsg(_(e_undo_line_missing)); uep->ue_bot = uep->ue_top + 1; // assume all lines deleted, will // get all the old lines back // without deleting the current @@ -2912,8 +2932,6 @@ static void u_freebranch(buf_T *buf, u_header_T *uhp, u_header_T **uhpp) /// @param uhpp if not NULL reset when freeing this header static void u_freeentries(buf_T *buf, u_header_T *uhp, u_header_T **uhpp) { - u_entry_T *uep, *nuep; - // Check for pointers to the header that become invalid now. if (buf->b_u_curhead == uhp) { buf->b_u_curhead = NULL; @@ -2925,7 +2943,8 @@ static void u_freeentries(buf_T *buf, u_header_T *uhp, u_header_T **uhpp) *uhpp = NULL; } - for (uep = uhp->uh_entry; uep != NULL; uep = nuep) { + u_entry_T *nuep; + for (u_entry_T *uep = uhp->uh_entry; uep != NULL; uep = nuep) { nuep = uep->ue_next; u_freeentry(uep, uep->ue_size); } @@ -2940,7 +2959,7 @@ static void u_freeentries(buf_T *buf, u_header_T *uhp, u_header_T **uhpp) } /// free entry 'uep' and 'n' lines in uep->ue_array[] -static void u_freeentry(u_entry_T *uep, long n) +static void u_freeentry(u_entry_T *uep, int n) { while (n > 0) { xfree(uep->ue_array[--n]); @@ -2962,35 +2981,35 @@ void u_clearall(buf_T *buf) buf->b_u_line_lnum = 0; } -/// save the line "lnum" for the "U" command -void u_saveline(linenr_T lnum) +/// Save the line "lnum" for the "U" command. +void u_saveline(buf_T *buf, linenr_T lnum) { - if (lnum == curbuf->b_u_line_lnum) { // line is already saved + if (lnum == buf->b_u_line_lnum) { // line is already saved return; } - if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count) { // should never happen + if (lnum < 1 || lnum > buf->b_ml.ml_line_count) { // should never happen return; } - u_clearline(); - curbuf->b_u_line_lnum = lnum; - if (curwin->w_cursor.lnum == lnum) { - curbuf->b_u_line_colnr = curwin->w_cursor.col; + u_clearline(buf); + buf->b_u_line_lnum = lnum; + if (curwin->w_buffer == buf && curwin->w_cursor.lnum == lnum) { + buf->b_u_line_colnr = curwin->w_cursor.col; } else { - curbuf->b_u_line_colnr = 0; + buf->b_u_line_colnr = 0; } - curbuf->b_u_line_ptr = u_save_line(lnum); + buf->b_u_line_ptr = u_save_line_buf(buf, lnum); } /// clear the line saved for the "U" command /// (this is used externally for crossing a line while in insert mode) -void u_clearline(void) +void u_clearline(buf_T *buf) { - if (curbuf->b_u_line_ptr == NULL) { + if (buf->b_u_line_ptr == NULL) { return; } - XFREE_CLEAR(curbuf->b_u_line_ptr); - curbuf->b_u_line_lnum = 0; + XFREE_CLEAR(buf->b_u_line_ptr); + buf->b_u_line_lnum = 0; } /// Implementation of the "U" command. @@ -3007,7 +3026,7 @@ void u_undoline(void) // first save the line for the 'u' command if (u_savecommon(curbuf, curbuf->b_u_line_lnum - 1, - curbuf->b_u_line_lnum + 1, (linenr_T)0, false) == FAIL) { + curbuf->b_u_line_lnum + 1, 0, false) == FAIL) { return; } @@ -3056,7 +3075,7 @@ static char *u_save_line(linenr_T lnum) /// @param buf buffer to copy from static char *u_save_line_buf(buf_T *buf, linenr_T lnum) { - return xstrdup(ml_get_buf(buf, lnum, false)); + return xstrdup(ml_get_buf(buf, lnum)); } /// Check if the 'modified' flag is set, or 'ff' has changed (only need to @@ -3102,7 +3121,7 @@ bool curbufIsChanged(void) /// @param[in] first_uhp Undo blocks list to start with. /// /// @return [allocated] List with a representation of undo blocks. -list_T *u_eval_tree(const u_header_T *const first_uhp) +static list_T *u_eval_tree(buf_T *const buf, const u_header_T *const first_uhp) FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_RET { list_T *const list = tv_list_alloc(kListLenMayKnow); @@ -3111,10 +3130,10 @@ list_T *u_eval_tree(const u_header_T *const first_uhp) dict_T *const dict = tv_dict_alloc(); tv_dict_add_nr(dict, S_LEN("seq"), (varnumber_T)uhp->uh_seq); tv_dict_add_nr(dict, S_LEN("time"), (varnumber_T)uhp->uh_time); - if (uhp == curbuf->b_u_newhead) { + if (uhp == buf->b_u_newhead) { tv_dict_add_nr(dict, S_LEN("newhead"), 1); } - if (uhp == curbuf->b_u_curhead) { + if (uhp == buf->b_u_curhead) { tv_dict_add_nr(dict, S_LEN("curhead"), 1); } if (uhp->uh_save_nr > 0) { @@ -3123,7 +3142,7 @@ list_T *u_eval_tree(const u_header_T *const first_uhp) if (uhp->uh_alt_next.ptr != NULL) { // Recursive call to add alternate undo tree. - tv_dict_add_list(dict, S_LEN("alt"), u_eval_tree(uhp->uh_alt_next.ptr)); + tv_dict_add_list(dict, S_LEN("alt"), u_eval_tree(buf, uhp->uh_alt_next.ptr)); } tv_list_append_dict(list, dict); @@ -3132,6 +3151,48 @@ list_T *u_eval_tree(const u_header_T *const first_uhp) return list; } +/// "undofile(name)" function +void f_undofile(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + rettv->v_type = VAR_STRING; + const char *const fname = tv_get_string(&argvars[0]); + + if (*fname == NUL) { + // If there is no file name there will be no undo file. + rettv->vval.v_string = NULL; + } else { + char *ffname = FullName_save(fname, true); + + if (ffname != NULL) { + rettv->vval.v_string = u_get_undo_file_name(ffname, false); + } + xfree(ffname); + } +} + +/// "undotree(expr)" function +void f_undotree(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + tv_dict_alloc_ret(rettv); + + typval_T *const tv = &argvars[0]; + buf_T *const buf = tv->v_type == VAR_UNKNOWN ? curbuf : get_buf_arg(tv); + if (buf == NULL) { + return; + } + + dict_T *dict = rettv->vval.v_dict; + + tv_dict_add_nr(dict, S_LEN("synced"), (varnumber_T)buf->b_u_synced); + tv_dict_add_nr(dict, S_LEN("seq_last"), (varnumber_T)buf->b_u_seq_last); + tv_dict_add_nr(dict, S_LEN("save_last"), (varnumber_T)buf->b_u_save_nr_last); + tv_dict_add_nr(dict, S_LEN("seq_cur"), (varnumber_T)buf->b_u_seq_cur); + tv_dict_add_nr(dict, S_LEN("time_cur"), (varnumber_T)buf->b_u_time_cur); + tv_dict_add_nr(dict, S_LEN("save_cur"), (varnumber_T)buf->b_u_save_nr_cur); + + tv_dict_add_list(dict, S_LEN("entries"), u_eval_tree(buf, buf->b_u_oldhead)); +} + // Given the buffer, Return the undo header. If none is set, set one first. // NULL will be returned if e.g undolevels = -1 (undo disabled) u_header_T *u_force_get_undo_header(buf_T *buf) @@ -3144,15 +3205,13 @@ u_header_T *u_force_get_undo_header(buf_T *buf) } // Create the first undo header for the buffer if (!uhp) { - // Undo is normally invoked in change code, which already has swapped - // curbuf. // Args are tricky: this means replace empty range by empty range.. - u_savecommon(curbuf, 0, 1, 1, true); + u_savecommon(buf, 0, 1, 1, true); uhp = buf->b_u_curhead; if (!uhp) { uhp = buf->b_u_newhead; - if (get_undolevel(curbuf) > 0 && !uhp) { + if (get_undolevel(buf) > 0 && !uhp) { abort(); } } |