comparison src/undo.c @ 753:ac005a544e24

updated for version 7.0223
author vimboss
date Mon, 13 Mar 2006 22:15:53 +0000
parents f08390485cd3
children d591d4ceeaee
comparison
equal deleted inserted replaced
752:7a6368b8edf6 753:ac005a544e24
39 * 39 *
40 * Each u_entry list contains the information for one undo or redo. 40 * Each u_entry list contains the information for one undo or redo.
41 * curbuf->b_u_curhead points to the header of the last undo (the next redo), 41 * curbuf->b_u_curhead points to the header of the last undo (the next redo),
42 * or is NULL if nothing has been undone. 42 * or is NULL if nothing has been undone.
43 * 43 *
44 * For keeping alternate undo/redo branches the uh_alt field is used. Thus at
45 * each point in the list a branch may appear for an alternate to redo. The
46 * uh_seq field is numbered sequentially to be able to find a newer or older
47 * branch.
48 *
44 * All data is allocated with U_ALLOC_LINE(), it will be freed as soon as the 49 * All data is allocated with U_ALLOC_LINE(), it will be freed as soon as the
45 * buffer is unloaded. 50 * buffer is unloaded.
46 */ 51 */
47 52
48 #include "vim.h" 53 #include "vim.h"
55 static int undo_allowed __ARGS((void)); 60 static int undo_allowed __ARGS((void));
56 static int u_savecommon __ARGS((linenr_T, linenr_T, linenr_T)); 61 static int u_savecommon __ARGS((linenr_T, linenr_T, linenr_T));
57 static void u_doit __ARGS((int count)); 62 static void u_doit __ARGS((int count));
58 static void u_undoredo __ARGS((void)); 63 static void u_undoredo __ARGS((void));
59 static void u_undo_end __ARGS((void)); 64 static void u_undo_end __ARGS((void));
60 static void u_freelist __ARGS((buf_T *buf, struct u_header *)); 65 static void u_freelist __ARGS((buf_T *buf, u_header_T *uhp, u_header_T **uhpp));
66 static void u_freebranch __ARGS((buf_T *buf, u_header_T *uhp, u_header_T **uhpp));
67 static void u_freeentries __ARGS((buf_T *buf, u_header_T *uhp, u_header_T **uhpp));
61 static void u_freeentry __ARGS((u_entry_T *, long)); 68 static void u_freeentry __ARGS((u_entry_T *, long));
62 69
63 #ifdef U_USE_MALLOC 70 #ifdef U_USE_MALLOC
64 # define U_FREE_LINE(ptr) vim_free(ptr) 71 # define U_FREE_LINE(ptr) vim_free(ptr)
65 # define U_ALLOC_LINE(size) lalloc((long_u)((size) + 1), FALSE) 72 # define U_ALLOC_LINE(size) lalloc((long_u)((size) + 1), FALSE)
194 static int 201 static int
195 u_savecommon(top, bot, newbot) 202 u_savecommon(top, bot, newbot)
196 linenr_T top, bot; 203 linenr_T top, bot;
197 linenr_T newbot; 204 linenr_T newbot;
198 { 205 {
199 linenr_T lnum; 206 linenr_T lnum;
200 long i; 207 long i;
201 struct u_header *uhp; 208 u_header_T *uhp;
202 u_entry_T *uep; 209 u_header_T *old_curhead;
203 u_entry_T *prev_uep; 210 u_entry_T *uep;
204 long size; 211 u_entry_T *prev_uep;
212 long size;
205 213
206 /* When making changes is not allowed return FAIL. It's a crude way to 214 /* When making changes is not allowed return FAIL. It's a crude way to
207 * make all change commands fail. */ 215 * make all change commands fail. */
208 if (!undo_allowed()) 216 if (!undo_allowed())
209 return FAIL; 217 return FAIL;
248 #ifdef FEAT_JUMPLIST 256 #ifdef FEAT_JUMPLIST
249 /* Need to create new entry in b_changelist. */ 257 /* Need to create new entry in b_changelist. */
250 curbuf->b_new_change = TRUE; 258 curbuf->b_new_change = TRUE;
251 #endif 259 #endif
252 260
261 if (p_ul >= 0)
262 {
263 /*
264 * Make a new header entry. Do this first so that we don't mess
265 * up the undo info when out of memory.
266 */
267 uhp = (u_header_T *)U_ALLOC_LINE((unsigned)sizeof(u_header_T));
268 if (uhp == NULL)
269 goto nomem;
270 }
271
253 /* 272 /*
254 * if we undid more than we redid, free the entry lists before and 273 * If we undid more than we redid, remove the entry lists before and
255 * including curbuf->b_u_curhead 274 * including curbuf->b_u_curhead to the alternate branch.
256 */ 275 */
257 while (curbuf->b_u_curhead != NULL) 276 old_curhead = curbuf->b_u_curhead;
258 u_freelist(curbuf, curbuf->b_u_newhead); 277 if (old_curhead != NULL)
278 {
279 curbuf->b_u_newhead = old_curhead->uh_next;
280 curbuf->b_u_curhead = NULL;
281 }
259 282
260 /* 283 /*
261 * free headers to keep the size right 284 * free headers to keep the size right
262 */ 285 */
263 while (curbuf->b_u_numhead > p_ul && curbuf->b_u_oldhead != NULL) 286 while (curbuf->b_u_numhead > p_ul && curbuf->b_u_oldhead != NULL)
264 u_freelist(curbuf, curbuf->b_u_oldhead); 287 {
288 u_header_T *upfree = curbuf->b_u_oldhead;
289
290 /* If there is no branch only free one header. */
291 if (upfree->uh_alt_next == NULL)
292 u_freelist(curbuf, upfree, &old_curhead);
293 else
294 {
295 /* Free the oldest alternate branch as a whole. */
296 while (upfree->uh_alt_next != NULL)
297 upfree = upfree->uh_alt_next;
298 u_freebranch(curbuf, upfree, &old_curhead);
299 }
300 }
265 301
266 if (p_ul < 0) /* no undo at all */ 302 if (p_ul < 0) /* no undo at all */
267 { 303 {
304 if (old_curhead != NULL)
305 u_freebranch(curbuf, old_curhead, NULL);
268 curbuf->b_u_synced = FALSE; 306 curbuf->b_u_synced = FALSE;
269 return OK; 307 return OK;
270 } 308 }
271 309
272 /*
273 * make a new header entry
274 */
275 uhp = (struct u_header *)U_ALLOC_LINE((unsigned)
276 sizeof(struct u_header));
277 if (uhp == NULL)
278 goto nomem;
279 uhp->uh_prev = NULL; 310 uhp->uh_prev = NULL;
280 uhp->uh_next = curbuf->b_u_newhead; 311 uhp->uh_next = curbuf->b_u_newhead;
312 uhp->uh_alt_next = old_curhead;
313 if (old_curhead != NULL)
314 {
315 old_curhead->uh_alt_prev = uhp;
316 if (curbuf->b_u_oldhead == old_curhead)
317 curbuf->b_u_oldhead = uhp;
318 }
319 uhp->uh_alt_prev = NULL;
320 uhp->uh_seq = curbuf->b_u_seq_last++;
321 curbuf->b_u_seq_cur = curbuf->b_u_seq_last;
281 if (curbuf->b_u_newhead != NULL) 322 if (curbuf->b_u_newhead != NULL)
282 curbuf->b_u_newhead->uh_prev = uhp; 323 curbuf->b_u_newhead->uh_prev = uhp;
324 uhp->uh_walk = 0;
283 uhp->uh_entry = NULL; 325 uhp->uh_entry = NULL;
284 uhp->uh_getbot_entry = NULL; 326 uhp->uh_getbot_entry = NULL;
285 uhp->uh_cursor = curwin->w_cursor; /* save cursor pos. for undo */ 327 uhp->uh_cursor = curwin->w_cursor; /* save cursor pos. for undo */
286 #ifdef FEAT_VIRTUALEDIT 328 #ifdef FEAT_VIRTUALEDIT
287 if (virtual_active() && curwin->w_cursor.coladd > 0) 329 if (virtual_active() && curwin->w_cursor.coladd > 0)
288 uhp->uh_cursor_vcol = getviscol(); 330 uhp->uh_cursor_vcol = getviscol();
289 else 331 else
290 uhp->uh_cursor_vcol = -1; 332 uhp->uh_cursor_vcol = -1;
291 #endif 333 #endif
334 uhp->uh_time = time(NULL);
292 335
293 /* save changed and buffer empty flag for undo */ 336 /* save changed and buffer empty flag for undo */
294 uhp->uh_flags = (curbuf->b_changed ? UH_CHANGED : 0) + 337 uhp->uh_flags = (curbuf->b_changed ? UH_CHANGED : 0) +
295 ((curbuf->b_ml.ml_flags & ML_EMPTY) ? UH_EMPTYBUF : 0); 338 ((curbuf->b_ml.ml_flags & ML_EMPTY) ? UH_EMPTYBUF : 0);
296 339
530 beep_flush(); 573 beep_flush();
531 break; 574 break;
532 } 575 }
533 576
534 u_undoredo(); 577 u_undoredo();
578 curbuf->b_u_seq_cur = curbuf->b_u_curhead->uh_seq;
535 } 579 }
536 else 580 else
537 { 581 {
538 if (curbuf->b_u_curhead == NULL || p_ul <= 0) 582 if (curbuf->b_u_curhead == NULL || p_ul <= 0)
539 { 583 {
540 beep_flush(); /* nothing to redo */ 584 beep_flush(); /* nothing to redo */
541 break; 585 break;
542 } 586 }
543 587
544 u_undoredo(); 588 u_undoredo();
545 /* advance for next redo */ 589 curbuf->b_u_seq_cur = curbuf->b_u_curhead->uh_seq + 1;
590
591 /* Advance for next redo. Set "newhead" when at the end of the
592 * redoable changes. */
593 if (curbuf->b_u_curhead->uh_prev == NULL)
594 curbuf->b_u_newhead = curbuf->b_u_curhead;
546 curbuf->b_u_curhead = curbuf->b_u_curhead->uh_prev; 595 curbuf->b_u_curhead = curbuf->b_u_curhead->uh_prev;
547 } 596 }
548 } 597 }
598 u_undo_end();
599 }
600
601 static int lastmark = 0;
602
603 /*
604 * Undo or redo over the timeline.
605 * When "step" is negative go back in time, otherwise goes forward in time.
606 */
607 void
608 undo_time(step)
609 int step;
610 {
611 long target;
612 long closest;
613 u_header_T *uhp;
614 u_header_T *last;
615 int mark;
616 int nomark;
617 int round;
618
619 u_newcount = 0;
620 u_oldcount = 0;
549 if (curbuf->b_ml.ml_flags & ML_EMPTY) 621 if (curbuf->b_ml.ml_flags & ML_EMPTY)
550 --u_newcount; 622 u_oldcount = -1;
623
624 /* "target" is the node below which we want to be. When going forward
625 * the current one also counts, thus do one less. */
626 if (step < 0)
627 {
628 target = curbuf->b_u_seq_cur + step;
629 closest = -1;
630 }
631 else
632 {
633 target = curbuf->b_u_seq_cur + step - 1;
634 closest = curbuf->b_u_seq_last + 1;
635 }
636
637 /* May do this twice:
638 * 1. Search for "target", update "closest" to the best match found.
639 * 2. If "target" not found search for "closest". */
640 for (round = 1; round <= 2; ++round)
641 {
642 /* Find the path from the current state to where we want to go. The
643 * desired state can be anywhere in the undo tree, need to go all over
644 * it. We put "nomark" in uh_walk where we have been without success,
645 * "mark" where it could possibly be. */
646 mark = ++lastmark;
647 nomark = ++lastmark;
648
649 if (curbuf->b_u_curhead == NULL) /* at leaf of the tree */
650 uhp = curbuf->b_u_newhead;
651 else
652 uhp = curbuf->b_u_curhead;
653
654 while (uhp != NULL)
655 {
656 uhp->uh_walk = mark;
657 if (uhp->uh_seq == target) /* found it! */
658 break;
659
660 if (round == 1 && (step < 0
661 ? (uhp->uh_seq < target && uhp->uh_seq > closest)
662 : (uhp->uh_seq > target && uhp->uh_seq < closest)))
663 closest = uhp->uh_seq;
664
665 /* go down in the tree if we haven't been there */
666 if (uhp->uh_prev != NULL && uhp->uh_prev->uh_walk != nomark
667 && uhp->uh_prev->uh_walk != mark)
668 uhp = uhp->uh_prev;
669
670 /* go to alternate branch if we haven't been there */
671 else if (uhp->uh_alt_next != NULL
672 && uhp->uh_alt_next->uh_walk != nomark
673 && uhp->uh_alt_next->uh_walk != mark)
674 uhp = uhp->uh_alt_next;
675
676 /* go up in the tree if we haven't been there and we are at the
677 * start of alternate branches */
678 else if (uhp->uh_next != NULL && uhp->uh_alt_prev == NULL
679 && uhp->uh_next->uh_walk != nomark
680 && uhp->uh_next->uh_walk != mark)
681 uhp = uhp->uh_next;
682
683 else
684 {
685 /* need to backtrack; mark this node as useless */
686 uhp->uh_walk = nomark;
687 if (uhp->uh_alt_prev != NULL)
688 uhp = uhp->uh_alt_prev;
689 else
690 uhp = uhp->uh_next;
691 }
692 }
693
694 if (uhp != NULL) /* found it */
695 break;
696 if (step < 0 && closest == -1)
697 {
698 MSG(_("Already at oldest change"));
699 return;
700 }
701 if (step > 0 && closest == curbuf->b_u_seq_last + 1)
702 {
703 MSG(_("Already at newest change"));
704 return;
705 }
706
707 target = closest;
708 }
709
710 /* If we found it: Follow the path to go to where we want to be. */
711 if (uhp != NULL)
712 {
713 /*
714 * First go up the tree as much as needed.
715 */
716 for (;;)
717 {
718 uhp = curbuf->b_u_curhead;
719 if (uhp == NULL)
720 uhp = curbuf->b_u_newhead;
721 else
722 {
723 while (uhp->uh_alt_prev != NULL)
724 {
725 uhp->uh_walk = nomark;
726 uhp = uhp->uh_alt_prev;
727 }
728 uhp = uhp->uh_next;
729 }
730 if (uhp == NULL || uhp->uh_walk != mark)
731 break;
732 curbuf->b_u_curhead = uhp;
733 u_undoredo();
734 uhp->uh_walk = nomark; /* don't go back down here */
735 curbuf->b_u_seq_cur = uhp->uh_seq;
736 }
737
738 /*
739 * And now go down the tree, branching off where needed.
740 */
741 uhp = curbuf->b_u_curhead;
742 for (;;)
743 {
744 /* Find the last branch with a mark, that's the one. */
745 last = uhp;
746 while (last->uh_alt_next != NULL
747 && last->uh_alt_next->uh_walk == mark)
748 last = last->uh_alt_next;
749 if (last != uhp)
750 {
751 /* Make the used branch the first entry in the list of
752 * alternatives to make "u" and CTRL-R take this branch. */
753 if (last->uh_alt_next != NULL)
754 last->uh_alt_next->uh_alt_prev = last->uh_alt_prev;
755 last->uh_alt_prev->uh_alt_next = last->uh_alt_next;
756 last->uh_alt_prev = NULL;
757 last->uh_alt_next = uhp;
758 uhp->uh_alt_prev = last;
759
760 uhp = last;
761 }
762
763 if (uhp->uh_walk != mark)
764 break; /* must have reached the target */
765
766 curbuf->b_u_curhead = uhp;
767 u_undoredo();
768 curbuf->b_u_seq_cur = uhp->uh_seq + 1;
769
770 /* Advance "curhead" to below the header we last used. If it
771 * becomes NULL then we need to set "newhead" to this leaf. */
772 if (uhp->uh_prev == NULL)
773 curbuf->b_u_newhead = uhp;
774 curbuf->b_u_curhead = uhp->uh_prev;
775
776 if (uhp->uh_seq == target) /* found it! */
777 break;
778
779 uhp = uhp->uh_prev;
780 if (uhp == NULL || uhp->uh_walk != mark)
781 {
782 EMSG2(_(e_intern2), "undo_time()");
783 break;
784 }
785 }
786 }
551 u_undo_end(); 787 u_undo_end();
552 } 788 }
553 789
554 /* 790 /*
555 * u_undoredo: common code for undo and redo 791 * u_undoredo: common code for undo and redo
806 * in some cases, but it's better than nothing). 1042 * in some cases, but it's better than nothing).
807 */ 1043 */
808 static void 1044 static void
809 u_undo_end() 1045 u_undo_end()
810 { 1046 {
811 if ((u_oldcount -= u_newcount) != 0) 1047 long sec;
812 msgmore(-u_oldcount); 1048 char *msg;
813 else if (u_newcount > p_report) 1049
814 {
815 if (u_newcount == 1)
816 MSG(_("1 change"));
817 else
818 smsg((char_u *)_("%ld changes"), u_newcount);
819 }
820 #ifdef FEAT_FOLDING 1050 #ifdef FEAT_FOLDING
821 if ((fdo_flags & FDO_UNDO) && KeyTyped) 1051 if ((fdo_flags & FDO_UNDO) && KeyTyped)
822 foldOpenCursor(); 1052 foldOpenCursor();
823 #endif 1053 #endif
1054
1055 if (global_busy /* no messages now, wait until global is finished */
1056 || !messaging()) /* 'lazyredraw' set, don't do messages now */
1057 return;
1058
1059 if (curbuf->b_ml.ml_flags & ML_EMPTY)
1060 --u_newcount;
1061
1062 u_oldcount -= u_newcount;
1063 if (u_oldcount == -1)
1064 msg = N_("more line");
1065 else if (u_oldcount < 0)
1066 msg = N_("more lines");
1067 else if (u_oldcount == 1)
1068 msg = N_("line less");
1069 else if (u_oldcount > 1)
1070 msg = N_("fewer lines");
1071 else
1072 {
1073 u_oldcount = u_newcount;
1074 if (u_newcount == 1)
1075 msg = N_("change");
1076 else
1077 msg = N_("changes");
1078 }
1079
1080 if (curbuf->b_u_curhead == 0)
1081 sec = 0;
1082 else
1083 sec = time(NULL) - curbuf->b_u_curhead->uh_time;
1084
1085 smsg((char_u *)_("%ld %s; %ld seconds ago"), u_oldcount, _(msg), sec);
824 } 1086 }
825 1087
826 /* 1088 /*
827 * u_sync: stop adding to the current entry list 1089 * u_sync: stop adding to the current entry list
828 */ 1090 */
872 */ 1134 */
873 void 1135 void
874 u_unchanged(buf) 1136 u_unchanged(buf)
875 buf_T *buf; 1137 buf_T *buf;
876 { 1138 {
877 struct u_header *uh; 1139 u_header_T *uh;
878 1140
879 for (uh = buf->b_u_newhead; uh; uh = uh->uh_next) 1141 for (uh = buf->b_u_newhead; uh; uh = uh->uh_next)
880 uh->uh_flags |= UH_CHANGED; 1142 uh->uh_flags |= UH_CHANGED;
881 buf->b_did_warn = FALSE; 1143 buf->b_did_warn = FALSE;
882 } 1144 }
934 1196
935 curbuf->b_u_synced = TRUE; 1197 curbuf->b_u_synced = TRUE;
936 } 1198 }
937 1199
938 /* 1200 /*
939 * u_freelist: free one entry list and adjust the pointers 1201 * Free one header and its entry list and adjust the pointers.
940 */ 1202 */
941 static void 1203 static void
942 u_freelist(buf, uhp) 1204 u_freelist(buf, uhp, uhpp)
943 buf_T *buf; 1205 buf_T *buf;
944 struct u_header *uhp; 1206 u_header_T *uhp;
945 { 1207 u_header_T **uhpp; /* if not NULL reset when freeing this header */
946 u_entry_T *uep, *nuep; 1208 {
947 1209 /* When there is an alternate redo list free that branch completely,
948 for (uep = uhp->uh_entry; uep != NULL; uep = nuep) 1210 * because we can never go there. */
949 { 1211 if (uhp->uh_alt_next != NULL)
950 nuep = uep->ue_next; 1212 u_freebranch(buf, uhp->uh_alt_next, uhpp);
951 u_freeentry(uep, uep->ue_size); 1213
952 } 1214 if (uhp->uh_alt_prev != NULL)
953 1215 uhp->uh_alt_prev->uh_alt_next = NULL;
954 if (buf->b_u_curhead == uhp) 1216
955 buf->b_u_curhead = NULL; 1217 /* Update the links in the list to remove the header. */
956
957 if (uhp->uh_next == NULL) 1218 if (uhp->uh_next == NULL)
958 buf->b_u_oldhead = uhp->uh_prev; 1219 buf->b_u_oldhead = uhp->uh_prev;
959 else 1220 else
960 uhp->uh_next->uh_prev = uhp->uh_prev; 1221 uhp->uh_next->uh_prev = uhp->uh_prev;
961 1222
962 if (uhp->uh_prev == NULL) 1223 if (uhp->uh_prev == NULL)
963 buf->b_u_newhead = uhp->uh_next; 1224 buf->b_u_newhead = uhp->uh_next;
964 else 1225 else
965 uhp->uh_prev->uh_next = uhp->uh_next; 1226 uhp->uh_prev->uh_next = uhp->uh_next;
1227
1228 u_freeentries(buf, uhp, uhpp);
1229 }
1230
1231 /*
1232 * Free an alternate branch and all following alternate branches.
1233 */
1234 static void
1235 u_freebranch(buf, uhp, uhpp)
1236 buf_T *buf;
1237 u_header_T *uhp;
1238 u_header_T **uhpp; /* if not NULL reset when freeing this header */
1239 {
1240 u_header_T *tofree, *next;
1241
1242 if (uhp->uh_alt_prev != NULL)
1243 uhp->uh_alt_prev->uh_alt_next = NULL;
1244
1245 next = uhp;
1246 while (next != NULL)
1247 {
1248 tofree = next;
1249 if (tofree->uh_alt_next != NULL)
1250 u_freebranch(buf, tofree->uh_alt_next, uhpp); /* recursive */
1251 next = tofree->uh_prev;
1252 u_freeentries(buf, tofree, uhpp);
1253 }
1254 }
1255
1256 /*
1257 * Free all the undo entries for one header and the header itself.
1258 * This means that "uhp" is invalid when returning.
1259 */
1260 static void
1261 u_freeentries(buf, uhp, uhpp)
1262 buf_T *buf;
1263 u_header_T *uhp;
1264 u_header_T **uhpp; /* if not NULL reset when freeing this header */
1265 {
1266 u_entry_T *uep, *nuep;
1267
1268 /* Check for pointers to the header that become invalid now. */
1269 if (buf->b_u_curhead == uhp)
1270 buf->b_u_curhead = NULL;
1271 if (uhpp != NULL && uhp == *uhpp)
1272 *uhpp = NULL;
1273
1274 for (uep = uhp->uh_entry; uep != NULL; uep = nuep)
1275 {
1276 nuep = uep->ue_next;
1277 u_freeentry(uep, uep->ue_size);
1278 }
966 1279
967 U_FREE_LINE((char_u *)uhp); 1280 U_FREE_LINE((char_u *)uhp);
968 --buf->b_u_numhead; 1281 --buf->b_u_numhead;
969 } 1282 }
970 1283
1099 */ 1412 */
1100 void 1413 void
1101 u_blockfree(buf) 1414 u_blockfree(buf)
1102 buf_T *buf; 1415 buf_T *buf;
1103 { 1416 {
1104 while (buf->b_u_newhead != NULL) 1417 while (buf->b_u_oldhead != NULL)
1105 u_freelist(buf, buf->b_u_newhead); 1418 u_freelist(buf, buf->b_u_oldhead, NULL);
1106 U_FREE_LINE(buf->b_u_line_ptr); 1419 U_FREE_LINE(buf->b_u_line_ptr);
1107 } 1420 }
1108 1421
1109 #else 1422 #else
1110 /* 1423 /*