/* vi:set ts=8 sts=4 sw=4: * * VIM - Vi IMproved by Bram Moolenaar * * Do ":help uganda" in Vim to read copying and usage conditions. * Do ":help credits" in Vim to see a list of people who contributed. * See README.txt for an overview of the Vim source code. */ /* * undo.c: multi level undo facility * * The saved lines are stored in a list of lists (one for each buffer): * * b_u_oldhead------------------------------------------------+ * | * V * +--------------+ +--------------+ +--------------+ * b_u_newhead--->| u_header | | u_header | | u_header | * | uh_next------>| uh_next------>| uh_next---->NULL * NULL<--------uh_prev |<---------uh_prev |<---------uh_prev | * | uh_entry | | uh_entry | | uh_entry | * +--------|-----+ +--------|-----+ +--------|-----+ * | | | * V V V * +--------------+ +--------------+ +--------------+ * | u_entry | | u_entry | | u_entry | * | ue_next | | ue_next | | ue_next | * +--------|-----+ +--------|-----+ +--------|-----+ * | | | * V V V * +--------------+ NULL NULL * | u_entry | * | ue_next | * +--------|-----+ * | * V * etc. * * Each u_entry list contains the information for one undo or redo. * curbuf->b_u_curhead points to the header of the last undo (the next redo), * or is NULL if nothing has been undone (end of the branch). * * For keeping alternate undo/redo branches the uh_alt field is used. Thus at * each point in the list a branch may appear for an alternate to redo. The * uh_seq field is numbered sequentially to be able to find a newer or older * branch. * * +---------------+ +---------------+ * b_u_oldhead --->| u_header | | u_header | * | uh_alt_next ---->| uh_alt_next ----> NULL * NULL <----- uh_alt_prev |<------ uh_alt_prev | * | uh_prev | | uh_prev | * +-----|---------+ +-----|---------+ * | | * V V * +---------------+ +---------------+ * | u_header | | u_header | * | uh_alt_next | | uh_alt_next | * b_u_newhead --->| uh_alt_prev | | uh_alt_prev | * | uh_prev | | uh_prev | * +-----|---------+ +-----|---------+ * | | * V V * NULL +---------------+ +---------------+ * | u_header | | u_header | * | uh_alt_next ---->| uh_alt_next | * | uh_alt_prev |<------ uh_alt_prev | * | uh_prev | | uh_prev | * +-----|---------+ +-----|---------+ * | | * etc. etc. * * * All data is allocated with U_ALLOC_LINE(), it will be freed as soon as the * buffer is unloaded. */ #include "vim.h" /* See below: use malloc()/free() for memory management. */#define U_USE_MALLOC 1 static void u_unch_branch __ARGS((u_header_T *uhp));static u_entry_T *u_get_headentry __ARGS((void));static void u_getbot __ARGS((void));static int u_savecommon __ARGS((linenr_T, linenr_T, linenr_T));static void u_doit __ARGS((int count));static void u_undoredo __ARGS((int undo));static void u_undo_end __ARGS((int did_undo, int absolute));static void u_add_time __ARGS((char_u *buf, size_t buflen, time_t tt));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 U_USE_MALLOC# define U_FREE_LINE(ptr) vim_free(ptr)# define U_ALLOC_LINE(size) lalloc((long_u)((size) + 1), FALSE)#elsestatic void u_free_line __ARGS((char_u *ptr, int keep));static char_u *u_alloc_line __ARGS((unsigned size));# define U_FREE_LINE(ptr) u_free_line((ptr), FALSE)# define U_ALLOC_LINE(size) u_alloc_line(size)#endifstatic char_u *u_save_line __ARGS((linenr_T)); 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; /* * Save the current line for both the "u" and "U" command. * Returns OK or FAIL. */ intu_save_cursor(){ return (u_save((linenr_T)(curwin->w_cursor.lnum - 1), (linenr_T)(curwin->w_cursor.lnum + 1)));} /* * Save the lines between "top" and "bot" for both the "u" and "U" command. * "top" may be 0 and bot may be curbuf->b_ml.ml_line_count + 1. * Returns FAIL when lines could not be saved, OK otherwise. */ intu_save(top, bot) linenr_T top, bot;{ if (undo_off) return OK; if (top > curbuf->b_ml.ml_line_count || top >= bot || bot > curbuf->b_ml.ml_line_count + 1) return FALSE; /* rely on caller to do error messages */ if (top + 2 == bot) u_saveline((linenr_T)(top + 1)); return (u_savecommon(top, bot, (linenr_T)0));} /* * save the line "lnum" (used by ":s" and "~" command) * The line is replaced, so the new bottom line is lnum + 1. */ intu_savesub(lnum) linenr_T lnum;{ if (undo_off) return OK; return (u_savecommon(lnum - 1, lnum + 1, lnum + 1));} /* * a new line is inserted before line "lnum" (used by :s command) * The line is inserted, so the new bottom line is lnum + 1. */ intu_inssub(lnum) linenr_T lnum;{ if (undo_off) return OK; return (u_savecommon(lnum - 1, lnum, lnum + 1));} /* * save the lines "lnum" - "lnum" + nlines (used by delete command) * The lines are deleted, so the new bottom line is lnum, unless the buffer * becomes empty. */ intu_savedel(lnum, nlines) linenr_T lnum; long nlines;{ if (undo_off) return OK; return (u_savecommon(lnum - 1, lnum + nlines, nlines == curbuf->b_ml.ml_line_count ? 2 : lnum));} /* * Return TRUE when undo is allowed. Otherwise give an error message and * return FALSE. */ intundo_allowed(){ /* Don't allow changes when 'modifiable' is off. */ if (!curbuf->b_p_ma) { EMSG(_(e_modifiable)); return FALSE; } #ifdef HAVE_SANDBOX /* In the sandbox it's not allowed to change the text. */ if (sandbox != 0) { EMSG(_(e_sandbox)); return FALSE; }#endif /* Don't allow changes in the buffer while editing the cmdline. The * caller of getcmdline() may get confused. */ if (textlock != 0) { EMSG(_(e_secure)); return FALSE; } return TRUE;} static intu_savecommon(top, bot, newbot) linenr_T top, bot; linenr_T newbot;{ linenr_T lnum; long i; u_header_T *uhp; u_header_T *old_curhead; u_entry_T *uep; u_entry_T *prev_uep; long size; /* When making changes is not allowed return FAIL. It's a crude way to * make all change commands fail. */ if (!undo_allowed()) return FAIL; #ifdef FEAT_NETBEANS_INTG /* * Netbeans defines areas that cannot be modified. Bail out here when * trying to change text in a guarded area. */ if (usingNetbeans) { if (netbeans_is_guarded(top, bot)) { EMSG(_(e_guarded)); return FAIL; } if (curbuf->b_p_ro) { EMSG(_(e_nbreadonly)); return FAIL; } }#endif #ifdef FEAT_AUTOCMD /* * Saving text for undo means we are going to make a change. Give a * warning for a read-only file before making the change, so that the * FileChangedRO event can replace the buffer with a read-write version * (e.g., obtained from a source control system). */ change_warning(0);#endif size = bot - top - 1; /* * if curbuf->b_u_synced == TRUE make a new header */ if (curbuf->b_u_synced) {#ifdef FEAT_JUMPLIST /* Need to create new entry in b_changelist. */ curbuf->b_new_change = TRUE;#endif if (p_ul >= 0) { /* * Make a new header entry. Do this first so that we don't mess * up the undo info when out of memory. */ uhp = (u_header_T *)U_ALLOC_LINE((unsigned)sizeof(u_header_T)); if (uhp == NULL) goto nomem; } else uhp = NULL; /* * If we undid more than we redid, move the entry lists before and * including curbuf->b_u_curhead to an alternate branch. */ old_curhead = curbuf->b_u_curhead; if (old_curhead != NULL) { curbuf->b_u_newhead = old_curhead->uh_next; curbuf->b_u_curhead = NULL; } /* * free headers to keep the size right */ while (curbuf->b_u_numhead > p_ul && curbuf->b_u_oldhead != NULL) { u_header_T *uhfree = curbuf->b_u_oldhead; /* If there is no branch only free one header. */ if (uhfree->uh_alt_next == NULL) u_freeheader(curbuf, uhfree, &old_curhead); else { /* Free the oldest alternate branch as a whole. */ while (uhfree->uh_alt_next != NULL) uhfree = uhfree->uh_alt_next; u_freebranch(curbuf, uhfree, &old_curhead); } } if (uhp == NULL) /* no undo at all */ { if (old_curhead != NULL) u_freebranch(curbuf, old_curhead, NULL); curbuf->b_u_synced = FALSE; return OK; } uhp->uh_prev = NULL; uhp->uh_next = curbuf->b_u_newhead; uhp->uh_alt_next = old_curhead; if (old_curhead != NULL) { uhp->uh_alt_prev = old_curhead->uh_alt_prev; if (uhp->uh_alt_prev != NULL) uhp->uh_alt_prev->uh_alt_next = uhp; old_curhead->uh_alt_prev = uhp; if (curbuf->b_u_oldhead == old_curhead) curbuf->b_u_oldhead = uhp; } else uhp->uh_alt_prev = NULL; if (curbuf->b_u_newhead != NULL) curbuf->b_u_newhead->uh_prev = uhp; uhp->uh_seq = ++curbuf->b_u_seq_last; curbuf->b_u_seq_cur = uhp->uh_seq; uhp->uh_time = time(NULL); curbuf->b_u_seq_time = uhp->uh_time + 1; uhp->uh_walk = 0; uhp->uh_entry = NULL; uhp->uh_getbot_entry = NULL; uhp->uh_cursor = curwin->w_cursor; /* save cursor pos. for undo */#ifdef FEAT_VIRTUALEDIT if (virtual_active() && curwin->w_cursor.coladd > 0) uhp->uh_cursor_vcol = getviscol(); else uhp->uh_cursor_vcol = -1;#endif /* save changed and buffer empty flag for undo */ uhp->uh_flags = (curbuf->b_changed ? UH_CHANGED : 0) + ((curbuf->b_ml.ml_flags & ML_EMPTY) ? UH_EMPTYBUF : 0); /* save named marks and Visual marks for undo */ mch_memmove(uhp->uh_namedm, curbuf->b_namedm, sizeof(pos_T) * NMARKS);#ifdef FEAT_VISUAL uhp->uh_visual = curbuf->b_visual;#endif curbuf->b_u_newhead = uhp; if (curbuf->b_u_oldhead == NULL) curbuf->b_u_oldhead = uhp; ++curbuf->b_u_numhead; } else { if (p_ul < 0) /* no undo at all */ return OK; /* * When saving a single line, and it has been saved just before, it * doesn't make sense saving it again. Saves a lot of memory when * making lots of changes inside the same line. * This is only possible if the previous change didn't increase or * decrease the number of lines. * Check the ten last changes. More doesn't make sense and takes too * long. */ if (size == 1) { uep = u_get_headentry(); prev_uep = NULL; for (i = 0; i < 10; ++i) { if (uep == NULL) break; /* If lines have been inserted/deleted we give up. * Also when the line was included in a multi-line save. */ if ((curbuf->b_u_newhead->uh_getbot_entry != uep ? (uep->ue_top + uep->ue_size + 1 != (uep->ue_bot == 0 ? curbuf->b_ml.ml_line_count + 1 : uep->ue_bot)) : uep->ue_lcount != curbuf->b_ml.ml_line_count) || (uep->ue_size > 1 && top >= uep->ue_top && top + 2 <= uep->ue_top + uep->ue_size + 1)) break; /* If it's the same line we can skip saving it again. */ if (uep->ue_size == 1 && uep->ue_top == top) { if (i > 0) { /* It's not the last entry: get ue_bot for the last * entry now. Following deleted/inserted lines go to * the re-used entry. */ u_getbot(); curbuf->b_u_synced = FALSE; /* Move the found entry to become the last entry. The * order of undo/redo doesn't matter for the entries * we move it over, since they don't change the line * count and don't include this line. It does matter * for the found entry if the line count is changed by * the executed command. */ prev_uep->ue_next = uep->ue_next; uep->ue_next = curbuf->b_u_newhead->uh_entry; curbuf->b_u_newhead->uh_entry = uep; } /* The executed command may change the line count. */ if (newbot != 0) uep->ue_bot = newbot; else if (bot > curbuf->b_ml.ml_line_count) uep->ue_bot = 0; else { uep->ue_lcount = curbuf->b_ml.ml_line_count; curbuf->b_u_newhead->uh_getbot_entry = uep; } return OK; } prev_uep = uep; uep = uep->ue_next; } } /* find line number for ue_bot for previous u_save() */ u_getbot(); } #if !defined(UNIX) && !defined(DJGPP) && !defined(WIN32) && !defined(__EMX__) /* * With Amiga and MSDOS 16 bit we can't handle big undo's, because * then u_alloc_line would have to allocate a block larger than 32K */ if (size >= 8000) goto nomem;#endif /* * add lines in front of entry list */ uep = (u_entry_T *)U_ALLOC_LINE((unsigned)sizeof(u_entry_T)); if (uep == NULL) goto nomem; uep->ue_size = size; uep->ue_top = top; if (newbot != 0) uep->ue_bot = newbot; /* * Use 0 for ue_bot if bot is below last line. * Otherwise we have to compute ue_bot later. */ else if (bot > curbuf->b_ml.ml_line_count) uep->ue_bot = 0; else { uep->ue_lcount = curbuf->b_ml.ml_line_count; curbuf->b_u_newhead->uh_getbot_entry = uep; } if (size > 0) { if ((uep->ue_array = (char_u **)U_ALLOC_LINE(Lines 1–500 of 2150. Load more
/* vi:set ts=8 sts=4 sw=4: * * VIM - Vi IMproved by Bram Moolenaar * * Do ":help uganda" in Vim to read copying and usage conditions. * Do ":help credits" in Vim to see a list of people who contributed. * See README.txt for an overview of the Vim source code. */ /* * undo.c: multi level undo facility * * The saved lines are stored in a list of lists (one for each buffer): * * b_u_oldhead------------------------------------------------+ * | * V * +--------------+ +--------------+ +--------------+ * b_u_newhead--->| u_header | | u_header | | u_header | * | uh_next------>| uh_next------>| uh_next---->NULL * NULL<--------uh_prev |<---------uh_prev |<---------uh_prev | * | uh_entry | | uh_entry | | uh_entry | * +--------|-----+ +--------|-----+ +--------|-----+ * | | | * V V V * +--------------+ +--------------+ +--------------+ * | u_entry | | u_entry | | u_entry | * | ue_next | | ue_next | | ue_next | * +--------|-----+ +--------|-----+ +--------|-----+ * | | | * V V V * +--------------+ NULL NULL * | u_entry | * | ue_next | * +--------|-----+ * | * V * etc. * * Each u_entry list contains the information for one undo or redo. * curbuf->b_u_curhead points to the header of the last undo (the next redo), * or is NULL if nothing has been undone (end of the branch). * * For keeping alternate undo/redo branches the uh_alt field is used. Thus at * each point in the list a branch may appear for an alternate to redo. The * uh_seq field is numbered sequentially to be able to find a newer or older * branch. * * +---------------+ +---------------+ * b_u_oldhead --->| u_header | | u_header | * | uh_alt_next ---->| uh_alt_next ----> NULL * NULL <----- uh_alt_prev |<------ uh_alt_prev | * | uh_prev | | uh_prev | * +-----|---------+ +-----|---------+ * | | * V V * +---------------+ +---------------+ * | u_header | | u_header | * | uh_alt_next | | uh_alt_next | * b_u_newhead --->| uh_alt_prev | | uh_alt_prev | * | uh_prev | | uh_prev | * +-----|---------+ +-----|---------+ * | | * V V * NULL +---------------+ +---------------+ * | u_header | | u_header | * | uh_alt_next ---->| uh_alt_next | * | uh_alt_prev |<------ uh_alt_prev | * | uh_prev | | uh_prev | * +-----|---------+ +-----|---------+ * | | * etc. etc. * * * All data is allocated with U_ALLOC_LINE(), it will be freed as soon as the * buffer is unloaded. */ #include "vim.h" /* See below: use malloc()/free() for memory management. */ #define U_USE_MALLOC 1 static void u_unch_branch __ARGS((u_header_T *uhp)); static u_entry_T *u_get_headentry __ARGS((void)); static void u_getbot __ARGS((void)); static int u_savecommon __ARGS((linenr_T, linenr_T, linenr_T)); static void u_doit __ARGS((int count)); static void u_undoredo __ARGS((int undo)); static void u_undo_end __ARGS((int did_undo, int absolute)); static void u_add_time __ARGS((char_u *buf, size_t buflen, time_t tt)); 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 U_USE_MALLOC # define U_FREE_LINE(ptr) vim_free(ptr) # define U_ALLOC_LINE(size) lalloc((long_u)((size) + 1), FALSE) #else static void u_free_line __ARGS((char_u *ptr, int keep)); static char_u *u_alloc_line __ARGS((unsigned size)); # define U_FREE_LINE(ptr) u_free_line((ptr), FALSE) # define U_ALLOC_LINE(size) u_alloc_line(size) #endif static char_u *u_save_line __ARGS((linenr_T)); 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; /* * Save the current line for both the "u" and "U" command. * Returns OK or FAIL. */ int u_save_cursor() { return (u_save((linenr_T)(curwin->w_cursor.lnum - 1), (linenr_T)(curwin->w_cursor.lnum + 1))); } /* * Save the lines between "top" and "bot" for both the "u" and "U" command. * "top" may be 0 and bot may be curbuf->b_ml.ml_line_count + 1. * Returns FAIL when lines could not be saved, OK otherwise. */ int u_save(top, bot) linenr_T top, bot; { if (undo_off) return OK; if (top > curbuf->b_ml.ml_line_count || top >= bot || bot > curbuf->b_ml.ml_line_count + 1) return FALSE; /* rely on caller to do error messages */ if (top + 2 == bot) u_saveline((linenr_T)(top + 1)); return (u_savecommon(top, bot, (linenr_T)0)); } /* * save the line "lnum" (used by ":s" and "~" command) * The line is replaced, so the new bottom line is lnum + 1. */ int u_savesub(lnum) linenr_T lnum; { if (undo_off) return OK; return (u_savecommon(lnum - 1, lnum + 1, lnum + 1)); } /* * a new line is inserted before line "lnum" (used by :s command) * The line is inserted, so the new bottom line is lnum + 1. */ int u_inssub(lnum) linenr_T lnum; { if (undo_off) return OK; return (u_savecommon(lnum - 1, lnum, lnum + 1)); } /* * save the lines "lnum" - "lnum" + nlines (used by delete command) * The lines are deleted, so the new bottom line is lnum, unless the buffer * becomes empty. */ int u_savedel(lnum, nlines) linenr_T lnum; long nlines; { if (undo_off) return OK; return (u_savecommon(lnum - 1, lnum + nlines, nlines == curbuf->b_ml.ml_line_count ? 2 : lnum)); } /* * Return TRUE when undo is allowed. Otherwise give an error message and * return FALSE. */ int undo_allowed() { /* Don't allow changes when 'modifiable' is off. */ if (!curbuf->b_p_ma) { EMSG(_(e_modifiable)); return FALSE; } #ifdef HAVE_SANDBOX /* In the sandbox it's not allowed to change the text. */ if (sandbox != 0) { EMSG(_(e_sandbox)); return FALSE; } #endif /* Don't allow changes in the buffer while editing the cmdline. The * caller of getcmdline() may get confused. */ if (textlock != 0) { EMSG(_(e_secure)); return FALSE; } return TRUE; } static int u_savecommon(top, bot, newbot) linenr_T top, bot; linenr_T newbot; { linenr_T lnum; long i; u_header_T *uhp; u_header_T *old_curhead; u_entry_T *uep; u_entry_T *prev_uep; long size; /* When making changes is not allowed return FAIL. It's a crude way to * make all change commands fail. */ if (!undo_allowed()) return FAIL; #ifdef FEAT_NETBEANS_INTG /* * Netbeans defines areas that cannot be modified. Bail out here when * trying to change text in a guarded area. */ if (usingNetbeans) { if (netbeans_is_guarded(top, bot)) { EMSG(_(e_guarded)); return FAIL; } if (curbuf->b_p_ro) { EMSG(_(e_nbreadonly)); return FAIL; } } #endif #ifdef FEAT_AUTOCMD /* * Saving text for undo means we are going to make a change. Give a * warning for a read-only file before making the change, so that the * FileChangedRO event can replace the buffer with a read-write version * (e.g., obtained from a source control system). */ change_warning(0); #endif size = bot - top - 1; /* * if curbuf->b_u_synced == TRUE make a new header */ if (curbuf->b_u_synced) { #ifdef FEAT_JUMPLIST /* Need to create new entry in b_changelist. */ curbuf->b_new_change = TRUE; #endif if (p_ul >= 0) { /* * Make a new header entry. Do this first so that we don't mess * up the undo info when out of memory. */ uhp = (u_header_T *)U_ALLOC_LINE((unsigned)sizeof(u_header_T)); if (uhp == NULL) goto nomem; } else uhp = NULL; /* * If we undid more than we redid, move the entry lists before and * including curbuf->b_u_curhead to an alternate branch. */ old_curhead = curbuf->b_u_curhead; if (old_curhead != NULL) { curbuf->b_u_newhead = old_curhead->uh_next; curbuf->b_u_curhead = NULL; } /* * free headers to keep the size right */ while (curbuf->b_u_numhead > p_ul && curbuf->b_u_oldhead != NULL) { u_header_T *uhfree = curbuf->b_u_oldhead; /* If there is no branch only free one header. */ if (uhfree->uh_alt_next == NULL) u_freeheader(curbuf, uhfree, &old_curhead); else { /* Free the oldest alternate branch as a whole. */ while (uhfree->uh_alt_next != NULL) uhfree = uhfree->uh_alt_next; u_freebranch(curbuf, uhfree, &old_curhead); } } if (uhp == NULL) /* no undo at all */ { if (old_curhead != NULL) u_freebranch(curbuf, old_curhead, NULL); curbuf->b_u_synced = FALSE; return OK; } uhp->uh_prev = NULL; uhp->uh_next = curbuf->b_u_newhead; uhp->uh_alt_next = old_curhead; if (old_curhead != NULL) { uhp->uh_alt_prev = old_curhead->uh_alt_prev; if (uhp->uh_alt_prev != NULL) uhp->uh_alt_prev->uh_alt_next = uhp; old_curhead->uh_alt_prev = uhp; if (curbuf->b_u_oldhead == old_curhead) curbuf->b_u_oldhead = uhp; } else uhp->uh_alt_prev = NULL; if (curbuf->b_u_newhead != NULL) curbuf->b_u_newhead->uh_prev = uhp; uhp->uh_seq = ++curbuf->b_u_seq_last; curbuf->b_u_seq_cur = uhp->uh_seq; uhp->uh_time = time(NULL); curbuf->b_u_seq_time = uhp->uh_time + 1; uhp->uh_walk = 0; uhp->uh_entry = NULL; uhp->uh_getbot_entry = NULL; uhp->uh_cursor = curwin->w_cursor; /* save cursor pos. for undo */ #ifdef FEAT_VIRTUALEDIT if (virtual_active() && curwin->w_cursor.coladd > 0) uhp->uh_cursor_vcol = getviscol(); else uhp->uh_cursor_vcol = -1; #endif /* save changed and buffer empty flag for undo */ uhp->uh_flags = (curbuf->b_changed ? UH_CHANGED : 0) + ((curbuf->b_ml.ml_flags & ML_EMPTY) ? UH_EMPTYBUF : 0); /* save named marks and Visual marks for undo */ mch_memmove(uhp->uh_namedm, curbuf->b_namedm, sizeof(pos_T) * NMARKS); #ifdef FEAT_VISUAL uhp->uh_visual = curbuf->b_visual; #endif curbuf->b_u_newhead = uhp; if (curbuf->b_u_oldhead == NULL) curbuf->b_u_oldhead = uhp; ++curbuf->b_u_numhead; } else { if (p_ul < 0) /* no undo at all */ return OK; /* * When saving a single line, and it has been saved just before, it * doesn't make sense saving it again. Saves a lot of memory when * making lots of changes inside the same line. * This is only possible if the previous change didn't increase or * decrease the number of lines. * Check the ten last changes. More doesn't make sense and takes too * long. */ if (size == 1) { uep = u_get_headentry(); prev_uep = NULL; for (i = 0; i < 10; ++i) { if (uep == NULL) break; /* If lines have been inserted/deleted we give up. * Also when the line was included in a multi-line save. */ if ((curbuf->b_u_newhead->uh_getbot_entry != uep ? (uep->ue_top + uep->ue_size + 1 != (uep->ue_bot == 0 ? curbuf->b_ml.ml_line_count + 1 : uep->ue_bot)) : uep->ue_lcount != curbuf->b_ml.ml_line_count) || (uep->ue_size > 1 && top >= uep->ue_top && top + 2 <= uep->ue_top + uep->ue_size + 1)) break; /* If it's the same line we can skip saving it again. */ if (uep->ue_size == 1 && uep->ue_top == top) { if (i > 0) { /* It's not the last entry: get ue_bot for the last * entry now. Following deleted/inserted lines go to * the re-used entry. */ u_getbot(); curbuf->b_u_synced = FALSE; /* Move the found entry to become the last entry. The * order of undo/redo doesn't matter for the entries * we move it over, since they don't change the line * count and don't include this line. It does matter * for the found entry if the line count is changed by * the executed command. */ prev_uep->ue_next = uep->ue_next; uep->ue_next = curbuf->b_u_newhead->uh_entry; curbuf->b_u_newhead->uh_entry = uep; } /* The executed command may change the line count. */ if (newbot != 0) uep->ue_bot = newbot; else if (bot > curbuf->b_ml.ml_line_count) uep->ue_bot = 0; else { uep->ue_lcount = curbuf->b_ml.ml_line_count; curbuf->b_u_newhead->uh_getbot_entry = uep; } return OK; } prev_uep = uep; uep = uep->ue_next; } } /* find line number for ue_bot for previous u_save() */ u_getbot(); } #if !defined(UNIX) && !defined(DJGPP) && !defined(WIN32) && !defined(__EMX__) /* * With Amiga and MSDOS 16 bit we can't handle big undo's, because * then u_alloc_line would have to allocate a block larger than 32K */ if (size >= 8000) goto nomem; #endif /* * add lines in front of entry list */ uep = (u_entry_T *)U_ALLOC_LINE((unsigned)sizeof(u_entry_T)); if (uep == NULL) goto nomem; uep->ue_size = size; uep->ue_top = top; if (newbot != 0) uep->ue_bot = newbot; /* * Use 0 for ue_bot if bot is below last line. * Otherwise we have to compute ue_bot later. */ else if (bot > curbuf->b_ml.ml_line_count) uep->ue_bot = 0; else { uep->ue_lcount = curbuf->b_ml.ml_line_count; curbuf->b_u_newhead->uh_getbot_entry = uep; } if (size > 0) { if ((uep->ue_array = (char_u **)U_ALLOC_LINE(