diff options
Diffstat (limited to 'src/nvim/undo.c')
-rw-r--r-- | src/nvim/undo.c | 588 |
1 files changed, 340 insertions, 248 deletions
diff --git a/src/nvim/undo.c b/src/nvim/undo.c index fc5d6acaa4..df0507ed41 100644 --- a/src/nvim/undo.c +++ b/src/nvim/undo.c @@ -1,3 +1,6 @@ +// 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 * @@ -76,17 +79,20 @@ #include <inttypes.h> #include <limits.h> #include <stdbool.h> +#include <stdint.h> #include <string.h> #include <fcntl.h> #include "nvim/vim.h" #include "nvim/ascii.h" #include "nvim/undo.h" +#include "nvim/macros.h" #include "nvim/cursor.h" #include "nvim/edit.h" #include "nvim/eval.h" #include "nvim/fileio.h" #include "nvim/fold.h" +#include "nvim/buffer_updates.h" #include "nvim/mark.h" #include "nvim/memline.h" #include "nvim/message.h" @@ -99,6 +105,7 @@ #include "nvim/quickfix.h" #include "nvim/screen.h" #include "nvim/sha256.h" +#include "nvim/state.h" #include "nvim/strings.h" #include "nvim/types.h" #include "nvim/os/os.h" @@ -115,7 +122,7 @@ static long u_newcount, u_oldcount; * When 'u' flag included in 'cpoptions', we behave like vi. Need to remember * the action that "u" should do. */ -static int undo_undoes = FALSE; +static bool undo_undoes = false; static int lastmark = 0; @@ -304,23 +311,19 @@ bool undo_allowed(void) return true; } -/* - * Get the undolevle value for the current buffer. - */ +/// Get the 'undolevels' value for the current buffer. static long get_undolevel(void) { - if (curbuf->terminal) { - return -1; - } - if (curbuf->b_p_ul == NO_LOCAL_UNDOLEVEL) + if (curbuf->b_p_ul == NO_LOCAL_UNDOLEVEL) { return p_ul; + } return curbuf->b_p_ul; } static inline void zero_fmark_additional_data(fmark_T *fmarks) { for (size_t i = 0; i < NMARKS; i++) { - dict_unref(fmarks[i].additional_data); + tv_dict_unref(fmarks[i].additional_data); fmarks[i].additional_data = NULL; } } @@ -588,7 +591,7 @@ int u_savecommon(linenr_T top, linenr_T bot, linenr_T newbot, int reload) uep->ue_next = curbuf->b_u_newhead->uh_entry; curbuf->b_u_newhead->uh_entry = uep; curbuf->b_u_synced = false; - undo_undoes = FALSE; + undo_undoes = false; #ifdef U_DEBUG u_check(FALSE); @@ -690,7 +693,7 @@ char *u_get_undo_file_name(const char *const buf_ffname, const bool reading) int ret; char *failed_dir; if ((ret = os_mkdir_recurse(dir_name, 0755, &failed_dir)) != 0) { - EMSG3(_("E926: Unable to create directory \"%s\" for undo file: %s"), + EMSG3(_("E5003: Unable to create directory \"%s\" for undo file: %s"), failed_dir, os_strerror(ret)); xfree(failed_dir); } else { @@ -700,7 +703,7 @@ char *u_get_undo_file_name(const char *const buf_ffname, const bool reading) if (has_directory) { if (munged_name == NULL) { munged_name = xstrdup(ffname); - for (char *p = munged_name; *p != NUL; mb_ptr_adv(p)) { + for (char *p = munged_name; *p != NUL; MB_PTR_ADV(p)) { if (vim_ispathsep(*p)) { *p = '%'; } @@ -888,7 +891,7 @@ static u_header_T *unserialize_uhp(bufinfo_T *bi, for (;; ) { int len = undo_read_byte(bi); - if (len == 0) { + if (len == 0 || len == EOF) { break; } int what = undo_read_byte(bi); @@ -968,12 +971,12 @@ static u_entry_T *unserialize_uep(bufinfo_T * bi, bool *error, uep->ue_lcount = undo_read_4c(bi); uep->ue_size = undo_read_4c(bi); - char_u **array; + char_u **array = NULL; if (uep->ue_size > 0) { - array = xmalloc(sizeof(char_u *) * (size_t)uep->ue_size); - memset(array, 0, sizeof(char_u *) * (size_t)uep->ue_size); - } else { - array = NULL; + if ((size_t)uep->ue_size < SIZE_MAX / sizeof(char_u *)) { // -V547 + array = xmalloc(sizeof(char_u *) * (size_t)uep->ue_size); + memset(array, 0, sizeof(char_u *) * (size_t)uep->ue_size); + } } uep->ue_array = array; @@ -1083,7 +1086,7 @@ void u_write_undo(const char *const name, const bool forceit, buf_T *const buf, */ perm = 0600; if (buf->b_ffname != NULL) { - perm = os_getperm(buf->b_ffname); + perm = os_getperm((const char *)buf->b_ffname); if (perm < 0) { perm = 0600; } @@ -1142,7 +1145,7 @@ void u_write_undo(const char *const name, const bool forceit, buf_T *const buf, EMSG2(_(e_not_open), file_name); goto theend; } - (void)os_setperm((char_u *)file_name, perm); + (void)os_setperm(file_name, perm); if (p_verbose > 0) { verbose_enter(); smsg(_("Writing undo file: %s"), file_name); @@ -1167,7 +1170,7 @@ void u_write_undo(const char *const name, const bool forceit, buf_T *const buf, && os_fileinfo(file_name, &file_info_new) && file_info_old.stat.st_gid != file_info_new.stat.st_gid && os_fchown(fd, (uv_uid_t)-1, (uv_gid_t)file_info_old.stat.st_gid)) { - os_setperm((char_u *)file_name, (perm & 0707) | ((perm & 07) << 3)); + os_setperm(file_name, (perm & 0707) | ((perm & 07) << 3)); } # ifdef HAVE_SELINUX if (buf->b_ffname != NULL) @@ -1402,7 +1405,9 @@ void u_read_undo(char *name, char_u *hash, char_u *orig_name) // sequence numbers of the headers. // When there are no headers uhp_table is NULL. if (num_head > 0) { - uhp_table = xmalloc((size_t)num_head * sizeof(u_header_T *)); + if ((size_t)num_head < SIZE_MAX / sizeof(*uhp_table)) { // -V547 + uhp_table = xmalloc((size_t)num_head * sizeof(*uhp_table)); + } } long num_read_uhps = 0; @@ -1630,7 +1635,13 @@ static time_t undo_read_time(bufinfo_T *bi) static bool undo_read(bufinfo_T *bi, uint8_t *buffer, size_t size) FUNC_ATTR_NONNULL_ARG(1) { - return fread(buffer, size, 1, bi->bi_fp) == 1; + const bool retval = fread(buffer, size, 1, bi->bi_fp) == 1; + if (!retval) { + // Error may be checked for only later. Fill with zeros, + // so that the reader won't use garbage. + memset(buffer, 0, size); + } + return retval; } /// Reads a string of length "len" from "bi->bi_fd" and appends a zero to it. @@ -1664,11 +1675,12 @@ void u_undo(int count) count = 1; } - if (vim_strchr(p_cpo, CPO_UNDO) == NULL) - undo_undoes = TRUE; - else + if (vim_strchr(p_cpo, CPO_UNDO) == NULL) { + undo_undoes = true; + } else { undo_undoes = !undo_undoes; - u_doit(count); + } + u_doit(count, false, true); } /* @@ -1677,15 +1689,64 @@ void u_undo(int count) */ void u_redo(int count) { - if (vim_strchr(p_cpo, CPO_UNDO) == NULL) - undo_undoes = FALSE; - u_doit(count); + if (vim_strchr(p_cpo, CPO_UNDO) == NULL) { + undo_undoes = false; + } + + u_doit(count, false, true); } -/* - * Undo or redo, depending on 'undo_undoes', 'count' times. - */ -static void u_doit(int startcount) +/// Undo and remove the branch from the undo tree. +/// Also moves the cursor (as a "normal" undo would). +bool u_undo_and_forget(int count) +{ + if (curbuf->b_u_synced == false) { + u_sync(true); + count = 1; + } + undo_undoes = true; + u_doit(count, true, + // Don't send nvim_buf_lines_event for u_undo_and_forget(). + false); + + if (curbuf->b_u_curhead == NULL) { + // nothing was undone. + return false; + } + + // Delete the current redo header + // set the redo header to the next alternative branch (if any) + // otherwise we will be in the leaf state + u_header_T *to_forget = curbuf->b_u_curhead; + curbuf->b_u_newhead = to_forget->uh_next.ptr; + curbuf->b_u_curhead = to_forget->uh_alt_next.ptr; + 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; + } else if (curbuf->b_u_newhead) { + curbuf->b_u_seq_cur = curbuf->b_u_newhead->uh_seq; + } + if (to_forget->uh_alt_prev.ptr) { + to_forget->uh_alt_prev.ptr->uh_alt_next.ptr = curbuf->b_u_curhead; + } + if (curbuf->b_u_newhead) { + curbuf->b_u_newhead->uh_prev.ptr = curbuf->b_u_curhead; + } + if (curbuf->b_u_seq_last == to_forget->uh_seq) { + curbuf->b_u_seq_last--; + } + u_freebranch(curbuf, to_forget, NULL); + return true; +} + +/// Undo or redo, depending on `undo_undoes`, `count` times. +/// +/// @param startcount How often to undo or redo +/// @param quiet If `true`, don't show messages +/// @param do_buf_event If `true`, send the changedtick with the buffer updates +static void u_doit(int startcount, bool quiet, bool do_buf_event) { int count = startcount; @@ -1721,7 +1782,7 @@ static void u_doit(int startcount) break; } - u_undoredo(TRUE); + u_undoredo(true, do_buf_event); } else { if (curbuf->b_u_curhead == NULL || get_undolevel() <= 0) { beep_flush(); /* nothing to redo */ @@ -1732,7 +1793,7 @@ static void u_doit(int startcount) break; } - u_undoredo(FALSE); + u_undoredo(false, do_buf_event); /* Advance for next redo. Set "newhead" when at the end of the * redoable changes. */ @@ -1741,34 +1802,32 @@ static void u_doit(int startcount) curbuf->b_u_curhead = curbuf->b_u_curhead->uh_prev.ptr; } } - u_undo_end(undo_undoes, FALSE); + u_undo_end(undo_undoes, false, quiet); } -/* - * Undo or redo over the timeline. - * When "step" is negative go back in time, otherwise goes forward in time. - * When "sec" is FALSE make "step" steps, when "sec" is TRUE use "step" as - * seconds. - * 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, int sec, int file, int absolute) +// Undo or redo over the timeline. +// When "step" is negative go back in time, otherwise goes forward in time. +// When "sec" is false make "step" steps, when "sec" is true use "step" as +// seconds. +// 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) { long target; long closest; long closest_start; long closest_seq = 0; long val; - u_header_T *uhp; + u_header_T *uhp = NULL; u_header_T *last; int mark; int nomark; int round; - int dosec = sec; - int dofile = file; - int above = FALSE; - int did_undo = TRUE; + bool dosec = sec; + bool dofile = file; + bool above = false; + bool did_undo = true; /* First make sure the current undoable change is synced. */ if (curbuf->b_u_synced == false) @@ -1782,20 +1841,12 @@ void undo_time(long step, int sec, int file, int absolute) /* "target" is the node below which we want to be. * Init "closest" to a value we can't reach. */ if (absolute) { - if (step == 0) { - // target 0 does not exist, got to 1 and above it. - target = 1; - above = true; - } else { - target = step; - } + target = step; closest = -1; } else { - /* When doing computations with time_t subtract starttime, because - * time_t converted to a long may result in a wrong number. */ - if (dosec) - target = (long)(curbuf->b_u_time_cur - starttime) + step; - else if (dofile) { + if (dosec) { + target = (long)(curbuf->b_u_time_cur) + step; + } else if (dofile) { if (step < 0) { /* Going back to a previous write. If there were changes after * the last write, count that as moving one file-write, so @@ -1815,7 +1866,7 @@ void undo_time(long step, int sec, int file, int absolute) if (target <= 0) /* Go to before first write: before the oldest change. Use * the sequence number for that. */ - dofile = FALSE; + dofile = false; } else { /* Moving forward to a newer write. */ target = curbuf->b_u_save_nr_cur + step; @@ -1823,7 +1874,7 @@ void undo_time(long step, int sec, int file, int absolute) /* Go to after last write: after the latest change. Use * the sequence number for that. */ target = curbuf->b_u_seq_last + 1; - dofile = FALSE; + dofile = false; } } } else @@ -1833,19 +1884,27 @@ void undo_time(long step, int sec, int file, int absolute) target = 0; closest = -1; } else { - if (dosec) - closest = (long)(time(NULL) - starttime + 1); - else if (dofile) + if (dosec) { + closest = (long)(os_time() + 1); + } else if (dofile) { closest = curbuf->b_u_save_nr_last + 2; - else + } else { closest = curbuf->b_u_seq_last + 2; - if (target >= closest) + } + if (target >= closest) { target = closest - 1; + } } } closest_start = closest; closest_seq = curbuf->b_u_seq_cur; + // When "target" is 0; Back to origin. + if (target == 0) { + mark = lastmark; // avoid that GCC complains + goto target_zero; + } + /* * May do this twice: * 1. Search for "target", update "closest" to the best match found. @@ -1869,12 +1928,13 @@ void undo_time(long step, int sec, int file, int absolute) while (uhp != NULL) { uhp->uh_walk = mark; - if (dosec) - val = (long)(uhp->uh_time - starttime); - else if (dofile) + if (dosec) { + val = (long)(uhp->uh_time); + } else if (dofile) { val = uhp->uh_save_nr; - else + } else { val = uhp->uh_seq; + } if (round == 1 && !(dofile && val == 0)) { /* Remember the header that is closest to the target. @@ -1954,17 +2014,17 @@ void undo_time(long step, int sec, int file, int absolute) } target = closest_seq; - dosec = FALSE; - dofile = FALSE; - if (step < 0) - above = TRUE; /* stop above the header */ + dosec = false; + dofile = false; + if (step < 0) { + above = true; // stop above the header + } } - /* If we found it: Follow the path to go to where we want to be. */ - if (uhp != NULL) { - /* - * First go up the tree as much as needed. - */ +target_zero: + // If we found it: Follow the path to go to where we want to be. + if (uhp != NULL || target == 0) { + // First go up the tree as much as needed. while (!got_int) { /* Do the change warning now, for the same reason as above. */ change_warning(0); @@ -1974,99 +2034,112 @@ void undo_time(long step, int sec, int file, int absolute) uhp = curbuf->b_u_newhead; else uhp = uhp->uh_next.ptr; - if (uhp == NULL || uhp->uh_walk != mark - || (uhp->uh_seq == target && !above)) + if (uhp == NULL + || (target > 0 && uhp->uh_walk != mark) + || (uhp->uh_seq == target && !above)) { break; + } curbuf->b_u_curhead = uhp; - u_undoredo(TRUE); - uhp->uh_walk = nomark; /* don't go back down here */ + u_undoredo(true, true); + if (target > 0) { + uhp->uh_walk = nomark; // don't go back down here + } } - /* - * And now go down the tree (redo), branching off where needed. - */ - while (!got_int) { - /* Do the change warning now, for the same reason as above. */ - change_warning(0); + // When back to origin, redo is not needed. + if (target > 0) { + // And now go down the tree (redo), branching off where needed. + while (!got_int) { + // Do the change warning now, for the same reason as above. + change_warning(0); - uhp = curbuf->b_u_curhead; - if (uhp == NULL) - break; - - /* Go back to the first branch with a mark. */ - while (uhp->uh_alt_prev.ptr != NULL - && uhp->uh_alt_prev.ptr->uh_walk == mark) - uhp = uhp->uh_alt_prev.ptr; + uhp = curbuf->b_u_curhead; + if (uhp == NULL) { + break; + } - /* Find the last branch with a mark, that's the one. */ - last = uhp; - while (last->uh_alt_next.ptr != NULL - && last->uh_alt_next.ptr->uh_walk == mark) - last = last->uh_alt_next.ptr; - if (last != uhp) { - /* Make the used branch the first entry in the list of - * alternatives to make "u" and CTRL-R take this branch. */ - while (uhp->uh_alt_prev.ptr != NULL) + // Go back to the first branch with a mark. + while (uhp->uh_alt_prev.ptr != NULL + && uhp->uh_alt_prev.ptr->uh_walk == mark) { uhp = uhp->uh_alt_prev.ptr; - if (last->uh_alt_next.ptr != NULL) - last->uh_alt_next.ptr->uh_alt_prev.ptr = - last->uh_alt_prev.ptr; - last->uh_alt_prev.ptr->uh_alt_next.ptr = last->uh_alt_next.ptr; - last->uh_alt_prev.ptr = NULL; - last->uh_alt_next.ptr = uhp; - uhp->uh_alt_prev.ptr = last; - - if (curbuf->b_u_oldhead == uhp) - curbuf->b_u_oldhead = last; - uhp = last; - if (uhp->uh_next.ptr != NULL) - uhp->uh_next.ptr->uh_prev.ptr = uhp; - } - curbuf->b_u_curhead = uhp; + } - if (uhp->uh_walk != mark) - break; /* must have reached the target */ + // Find the last branch with a mark, that's the one. + last = uhp; + while (last->uh_alt_next.ptr != NULL + && last->uh_alt_next.ptr->uh_walk == mark) { + last = last->uh_alt_next.ptr; + } + if (last != uhp) { + // Make the used branch the first entry in the list of + // alternatives to make "u" and CTRL-R take this branch. + while (uhp->uh_alt_prev.ptr != NULL) { + uhp = uhp->uh_alt_prev.ptr; + } + if (last->uh_alt_next.ptr != NULL) { + last->uh_alt_next.ptr->uh_alt_prev.ptr = last->uh_alt_prev.ptr; + } + last->uh_alt_prev.ptr->uh_alt_next.ptr = last->uh_alt_next.ptr; + last->uh_alt_prev.ptr = NULL; + last->uh_alt_next.ptr = uhp; + uhp->uh_alt_prev.ptr = last; - /* Stop when going backwards in time and didn't find the exact - * header we were looking for. */ - if (uhp->uh_seq == target && above) { - curbuf->b_u_seq_cur = target - 1; - break; - } + if (curbuf->b_u_oldhead == uhp) { + curbuf->b_u_oldhead = last; + } + uhp = last; + if (uhp->uh_next.ptr != NULL) { + uhp->uh_next.ptr->uh_prev.ptr = uhp; + } + } + curbuf->b_u_curhead = uhp; - u_undoredo(FALSE); + if (uhp->uh_walk != mark) { + break; // must have reached the target + } - /* Advance "curhead" to below the header we last used. If it - * becomes NULL then we need to set "newhead" to this leaf. */ - if (uhp->uh_prev.ptr == NULL) - curbuf->b_u_newhead = uhp; - curbuf->b_u_curhead = uhp->uh_prev.ptr; - did_undo = FALSE; + // Stop when going backwards in time and didn't find the exact + // header we were looking for. + if (uhp->uh_seq == target && above) { + curbuf->b_u_seq_cur = target - 1; + break; + } - if (uhp->uh_seq == target) /* found it! */ - break; + u_undoredo(false, true); - uhp = uhp->uh_prev.ptr; - if (uhp == NULL || uhp->uh_walk != mark) { - /* Need to redo more but can't find it... */ - EMSG2(_(e_intern2), "undo_time()"); - break; + // Advance "curhead" to below the header we last used. If it + // becomes NULL then we need to set "newhead" to this leaf. + if (uhp->uh_prev.ptr == NULL) { + curbuf->b_u_newhead = uhp; + } + curbuf->b_u_curhead = uhp->uh_prev.ptr; + did_undo = false; + + if (uhp->uh_seq == target) { // found it! + break; + } + + uhp = uhp->uh_prev.ptr; + if (uhp == NULL || uhp->uh_walk != mark) { + // Need to redo more but can't find it... + internal_error("undo_time()"); + break; + } } } } - u_undo_end(did_undo, absolute); + u_undo_end(did_undo, absolute, false); } -/* - * u_undoredo: common code for undo and redo - * - * The lines in the file are replaced by the lines in the entry list at - * curbuf->b_u_curhead. The replaced lines in the file are saved in the entry - * list for the next undo/redo. - * - * When "undo" is TRUE we go up in the tree, when FALSE we go down. - */ -static void u_undoredo(int undo) +/// u_undoredo: common code for undo and redo +/// +/// The lines in the file are replaced by the lines in the entry list at +/// curbuf->b_u_curhead. The replaced lines in the file are saved in the entry +/// list for the next undo/redo. +/// +/// @param undo If `true`, go up the tree. Down if `false`. +/// @param do_buf_event If `true`, send buffer updates. +static void u_undoredo(int undo, bool do_buf_event) { char_u **newarray = NULL; linenr_T oldsize; @@ -2115,8 +2188,8 @@ static void u_undoredo(int undo) if (top > curbuf->b_ml.ml_line_count || top >= bot || bot > curbuf->b_ml.ml_line_count + 1) { unblock_autocmds(); - EMSG(_("E438: u_undo: line numbers wrong")); - changed(); /* don't want UNCHANGED now */ + IEMSG(_("E438: u_undo: line numbers wrong")); + changed(); // don't want UNCHANGED now return; } @@ -2173,10 +2246,11 @@ static void u_undoredo(int undo) * 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); - else + if (empty_buffer && lnum == 0) { + ml_replace((linenr_T)1, uep->ue_array[i], true); + } else { ml_append(lnum, uep->ue_array[i], (colnr_T)0, FALSE); + } xfree(uep->ue_array[i]); } xfree((char_u *)uep->ue_array); @@ -2185,14 +2259,16 @@ static void u_undoredo(int undo) /* adjust marks */ if (oldsize != newsize) { mark_adjust(top + 1, top + oldsize, (long)MAXLNUM, - (long)newsize - (long)oldsize); - if (curbuf->b_op_start.lnum > top + oldsize) + (long)newsize - (long)oldsize, false); + if (curbuf->b_op_start.lnum > top + oldsize) { curbuf->b_op_start.lnum += newsize - oldsize; - if (curbuf->b_op_end.lnum > top + oldsize) + } + if (curbuf->b_op_end.lnum > top + oldsize) { curbuf->b_op_end.lnum += newsize - oldsize; + } } - changed_lines(top + 1, 0, bot, newsize - oldsize); + changed_lines(top + 1, 0, bot, newsize - oldsize, do_buf_event); /* set '[ and '] mark */ if (top + 1 < curbuf->b_op_start.lnum) @@ -2218,12 +2294,21 @@ static void u_undoredo(int undo) curhead->uh_entry = newlist; curhead->uh_flags = new_flags; - if ((old_flags & UH_EMPTYBUF) && bufempty()) + if ((old_flags & UH_EMPTYBUF) && BUFEMPTY()) { curbuf->b_ml.ml_flags |= ML_EMPTY; - if (old_flags & UH_CHANGED) + } + if (old_flags & UH_CHANGED) { changed(); - else + } else { unchanged(curbuf, FALSE); + } + + // because the calls to changed()/unchanged() above will bump changedtick + // again, we need to send a nvim_buf_lines_event with just the new value of + // b:changedtick + if (do_buf_event && kv_size(curbuf->update_channels)) { + buf_updates_changedtick(curbuf); + } /* * restore marks from before undo/redo @@ -2278,7 +2363,8 @@ static void u_undoredo(int undo) 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; + 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". */ if (curhead->uh_save_nr != 0) { @@ -2298,16 +2384,13 @@ static void u_undoredo(int undo) #endif } -/* - * If we deleted or added lines, report the number of less/more lines. - * Otherwise, report the number of changes (this may be incorrect - * in some cases, but it's better than nothing). - */ -static void -u_undo_end ( - int did_undo, /* just did an undo */ - int absolute /* used ":undo N" */ -) +/// If we deleted or added lines, report the number of less/more lines. +/// Otherwise, report the number of changes (this may be incorrect +/// in some cases, but it's better than nothing). +static void u_undo_end( + bool did_undo, ///< just did an undo + bool absolute, ///< used ":undo N" + bool quiet) { char *msgstr; u_header_T *uhp; @@ -2316,9 +2399,11 @@ u_undo_end ( if ((fdo_flags & FDO_UNDO) && KeyTyped) foldOpenCursor(); - if (global_busy /* no messages now, wait until global is finished */ - || !messaging()) /* 'lazyredraw' set, don't do messages now */ + if (quiet + || global_busy // no messages until global is finished + || !messaging()) { // 'lazyredraw' set, don't do messages now return; + } if (curbuf->b_ml.ml_flags & ML_EMPTY) --u_newcount; @@ -2344,13 +2429,15 @@ u_undo_end ( /* For ":undo N" we prefer a "after #N" message. */ if (absolute && curbuf->b_u_curhead->uh_next.ptr != NULL) { uhp = curbuf->b_u_curhead->uh_next.ptr; - did_undo = FALSE; - } else if (did_undo) + did_undo = false; + } else if (did_undo) { uhp = curbuf->b_u_curhead; - else + } else { uhp = curbuf->b_u_curhead->uh_next.ptr; - } else + } + } else { uhp = curbuf->b_u_newhead; + } if (uhp == NULL) *msgbuf = NUL; @@ -2376,9 +2463,9 @@ u_undo_end ( /* * u_sync: stop adding to the current entry list */ -void -u_sync ( - int force /* Also sync when no_u_sync is set. */ +void +u_sync( + int force // Also sync when no_u_sync is set. ) { /* Skip it when already synced or syncing is disabled. */ @@ -2416,8 +2503,8 @@ 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((char *)IObuff, IOSIZE, "%6ld %7ld ", - uhp->uh_seq, changes); + vim_snprintf((char *)IObuff, IOSIZE, "%6ld %7d ", + uhp->uh_seq, changes); u_add_time(IObuff + STRLEN(IObuff), IOSIZE - STRLEN(IObuff), uhp->uh_time); if (uhp->uh_save_nr > 0) { @@ -2468,13 +2555,14 @@ void ex_undolist(exarg_T *eap) sort_strings((char_u **)ga.ga_data, ga.ga_len); msg_start(); - msg_puts_attr((char_u *)_("number changes when saved"), - hl_attr(HLF_T)); - for (int i = 0; i < ga.ga_len && !got_int; ++i) { + msg_puts_attr(_("number changes when saved"), + HL_ATTR(HLF_T)); + for (int i = 0; i < ga.ga_len && !got_int; i++) { msg_putchar('\n'); - if (got_int) + if (got_int) { break; - msg_puts(((char_u **)ga.ga_data)[i]); + } + msg_puts(((const char **)ga.ga_data)[i]); } msg_end(); @@ -2507,20 +2595,20 @@ static void u_add_time(char_u *buf, size_t buflen, time_t tt) */ void ex_undojoin(exarg_T *eap) { - if (curbuf->b_u_newhead == NULL) - return; /* nothing changed before */ + if (curbuf->b_u_newhead == NULL) { + return; // nothing changed before + } if (curbuf->b_u_curhead != NULL) { EMSG(_("E790: undojoin is not allowed after undo")); return; } - if (!curbuf->b_u_synced) - return; /* already unsynced */ - if (get_undolevel() < 0) - return; /* no entries, nothing to do */ - else { - /* Go back to the last entry */ - curbuf->b_u_curhead = curbuf->b_u_newhead; - curbuf->b_u_synced = false; /* no entries, nothing to do */ + if (!curbuf->b_u_synced) { + return; // already unsynced + } + if (get_undolevel() < 0) { + return; // no entries, nothing to do + } else { + curbuf->b_u_synced = false; // Append next change to last entry } } @@ -2604,7 +2692,7 @@ static void u_unch_branch(u_header_T *uhp) static u_entry_T *u_get_headentry(void) { if (curbuf->b_u_newhead == NULL || curbuf->b_u_newhead->uh_entry == NULL) { - EMSG(_("E439: undo list corrupt")); + IEMSG(_("E439: undo list corrupt")); return NULL; } return curbuf->b_u_newhead->uh_entry; @@ -2633,11 +2721,11 @@ static void u_getbot(void) extra = curbuf->b_ml.ml_line_count - uep->ue_lcount; uep->ue_bot = uep->ue_top + uep->ue_size + 1 + extra; if (uep->ue_bot < 1 || uep->ue_bot > curbuf->b_ml.ml_line_count) { - EMSG(_("E440: 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 - * ones */ + IEMSG(_("E440: 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 + // ones } curbuf->b_u_newhead->uh_getbot_entry = NULL; @@ -2649,11 +2737,11 @@ static void u_getbot(void) /* * Free one header "uhp" and its entry list and adjust the pointers. */ -static void -u_freeheader ( +static void +u_freeheader( buf_T *buf, u_header_T *uhp, - u_header_T **uhpp /* if not NULL reset when freeing this header */ + u_header_T **uhpp // if not NULL reset when freeing this header ) { u_header_T *uhap; @@ -2685,11 +2773,11 @@ u_freeheader ( /* * Free an alternate branch and any following alternate branches. */ -static void -u_freebranch ( +static void +u_freebranch( buf_T *buf, u_header_T *uhp, - u_header_T **uhpp /* if not NULL reset when freeing this header */ + u_header_T **uhpp // if not NULL reset when freeing this header ) { u_header_T *tofree, *next; @@ -2719,11 +2807,11 @@ u_freebranch ( * Free all the undo entries for one header and the header itself. * This means that "uhp" is invalid when returning. */ -static void -u_freeentries ( +static void +u_freeentries( buf_T *buf, u_header_T *uhp, - u_header_T **uhpp /* if not NULL reset when freeing this header */ + u_header_T **uhpp // if not NULL reset when freeing this header ) { u_entry_T *uep, *nuep; @@ -2830,7 +2918,7 @@ void u_undoline(void) curbuf->b_u_line_lnum + 1, (linenr_T)0, FALSE) == FAIL) return; oldp = u_save_line(curbuf->b_u_line_lnum); - ml_replace(curbuf->b_u_line_lnum, curbuf->b_u_line_ptr, TRUE); + ml_replace(curbuf->b_u_line_lnum, curbuf->b_u_line_ptr, true); changed_bytes(curbuf->b_u_line_lnum, 0); xfree(curbuf->b_u_line_ptr); curbuf->b_u_line_ptr = oldp; @@ -2890,35 +2978,39 @@ bool curbufIsChanged(void) && (curbuf->b_changed || file_ff_differs(curbuf, true))); } -/* - * For undotree(): Append the list of undo blocks at "first_uhp" to "list". - * Recursive. - */ -void u_eval_tree(u_header_T *first_uhp, list_T *list) +/// Append the list of undo blocks to a newly allocated list +/// +/// For use in undotree(). Recursive. +/// +/// @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) + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_RET { - u_header_T *uhp = first_uhp; - dict_T *dict; + list_T *const list = tv_list_alloc(kListLenMayKnow); - while (uhp != NULL) { - dict = dict_alloc(); - dict_add_nr_str(dict, "seq", uhp->uh_seq, NULL); - dict_add_nr_str(dict, "time", (long)uhp->uh_time, NULL); - if (uhp == curbuf->b_u_newhead) - dict_add_nr_str(dict, "newhead", 1, NULL); - if (uhp == curbuf->b_u_curhead) - dict_add_nr_str(dict, "curhead", 1, NULL); - if (uhp->uh_save_nr > 0) - dict_add_nr_str(dict, "save", uhp->uh_save_nr, NULL); + for (const u_header_T *uhp = first_uhp; uhp != NULL; uhp = uhp->uh_prev.ptr) { + 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) { + tv_dict_add_nr(dict, S_LEN("newhead"), 1); + } + if (uhp == curbuf->b_u_curhead) { + tv_dict_add_nr(dict, S_LEN("curhead"), 1); + } + if (uhp->uh_save_nr > 0) { + tv_dict_add_nr(dict, S_LEN("save"), (varnumber_T)uhp->uh_save_nr); + } if (uhp->uh_alt_next.ptr != NULL) { - list_T *alt_list = list_alloc(); - - /* Recursive call to add alternate undo tree. */ - u_eval_tree(uhp->uh_alt_next.ptr, alt_list); - dict_add_list(dict, "alt", alt_list); + // Recursive call to add alternate undo tree. + tv_dict_add_list(dict, S_LEN("alt"), u_eval_tree(uhp->uh_alt_next.ptr)); } - list_append_dict(list, dict); - uhp = uhp->uh_prev.ptr; + tv_list_append_dict(list, dict); } + + return list; } |