1: /* Support routines for the undo facility. 2: Copyright (C) 1985 Fen Labalme and Richard M. Stallman. 3: 4: This file is part of GNU Emacs. 5: 6: GNU Emacs is distributed in the hope that it will be useful, 7: but WITHOUT ANY WARRANTY. No author or distributor 8: accepts responsibility to anyone for the consequences of using it 9: or for whether it serves any particular purpose or works at all, 10: unless he says so in writing. Refer to the GNU Emacs General Public 11: License for full details. 12: 13: Everyone is granted permission to copy, modify and redistribute 14: GNU Emacs, but only under the conditions described in the 15: GNU Emacs General Public License. A copy of this license is 16: supposed to have been given to you along with GNU Emacs so you 17: can know your rights and responsibilities. It should be in a 18: file named COPYING. Among other things, the copyright notice 19: and this notice must be preserved on all copies. */ 20: 21: 22: #include "config.h" 23: #include "lisp.h" 24: #include "undo.h" 25: #include "commands.h" 26: #include "buffer.h" 27: 28: /* Access undo records of current buffer */ 29: /* These assume that `u' points to the buffer's undodata */ 30: #define UndoRQ (u->undorecs) 31: #define UndoCQ (u->undochars) 32: #define FillRQ (u->nextrec) 33: #define FillCQ (u->nextchar) 34: 35: /* Record last undo record made, and what buffer made in */ 36: static struct UndoRec *LastUndoRec; 37: static struct buffer *LastUndoBuf; 38: 39: /* Record progress of undoing */ 40: static NUndone; 41: static NCharsLeft; 42: static LastUndoneC; 43: static LastUndone; 44: static struct buffer *LastUndoneBuf; 45: 46: Lisp_Object Fundo_boundary (); 47: 48: make_undo_records (b) 49: struct buffer *b; 50: { 51: register struct UndoData *u; 52: b->undodata = u = (struct UndoData *) xmalloc (sizeof (struct UndoData)); 53: u->undorecs 54: = (struct UndoRec *) xmalloc (sizeof (struct UndoRec) * InitNUndoR); 55: u->undochars = (char *) xmalloc (InitNUndoC); 56: u->undorecs[InitNUndoR - 1].kind = Unundoable; 57: u->nextrec = 0; 58: u->nextchar = 0; 59: u->num_undorecs = InitNUndoR; 60: u->num_undochars = InitNUndoC; 61: } 62: 63: free_undo_records (b) 64: struct buffer *b; 65: { 66: register struct UndoData *u = b->undodata; 67: free (u->undorecs); 68: free (u->undochars); 69: free (u); 70: } 71: 72: struct UndoRec * 73: NewUndo (kind, pos, len) 74: enum Ukinds kind; 75: { 76: register struct UndoData *u = bf_cur->undodata; 77: register struct UndoRec *p = &UndoRQ[FillRQ]; 78: register struct UndoRec *np; 79: 80: FillRQ++; 81: if (FillRQ >= NUndoR) 82: FillRQ = 0; 83: else if (FillRQ >= u->num_undorecs) 84: { 85: np = (struct UndoRec *) xrealloc (UndoRQ, NUndoR * sizeof *p); 86: if (np) 87: { 88: UndoRQ = np; 89: p = &UndoRQ[FillRQ-1]; 90: u->num_undorecs = NUndoR; 91: np[NUndoR - 1].kind = Unundoable; 92: } 93: else 94: FillRQ = 0; 95: } 96: UndoRQ[FillRQ].kind = Unundoable; 97: p -> kind = kind; 98: p -> pos = pos; 99: p -> len = len; 100: LastUndoRec = p; 101: LastUndoBuf = bf_cur; 102: if (kind != Uboundary) 103: LastUndone = -1; 104: return p; 105: } 106: 107: RecordInsert (pos, n) 108: { 109: register struct UndoRec *p = LastUndoRec; 110: if (!bf_cur->undodata) 111: return; 112: if (LastUndoBuf != bf_cur) 113: { 114: Fundo_boundary (); 115: p = 0; 116: } 117: 118: if (bf_modified <= bf_cur->save_modified) 119: NewUndo (Uunmod, pos, 0); 120: 121: if (p && p -> kind == Udelete && p -> pos + p -> len == pos) 122: p -> len += n; 123: else 124: NewUndo (Udelete, pos, n); 125: } 126: 127: RecordDelete (pos, n) 128: int pos, n; 129: { 130: register struct UndoRec *p = LastUndoRec; 131: 132: if (!bf_cur->undodata) 133: return; 134: if (LastUndoBuf != bf_cur) 135: { 136: Fundo_boundary (); 137: p = 0; 138: } 139: 140: if (bf_modified <= bf_cur->save_modified) 141: NewUndo (Uunmod, pos, 0); 142: 143: if (p && p->kind == Uinsert && p->pos == pos) 144: p->len += n; 145: else 146: NewUndo (Uinsert, pos, n); 147: 148: record_chars (pos, n); 149: } 150: 151: record_chars (pos, n) 152: register int pos, n; 153: { 154: if (pos < bf_s1 + 1 && pos + n > bf_s1 + 1) 155: { 156: record_block (&CharAt (pos), bf_s1 + 1 - pos); 157: n -= bf_s1 + 1 - pos; 158: pos = bf_s1 + 1; 159: } 160: 161: record_block (&CharAt (pos), n); 162: } 163: 164: record_block (p, n) 165: register char *p; 166: register int n; 167: { 168: register char *cp; 169: register struct UndoData *u = bf_cur->undodata; 170: register int i; 171: 172: NCharsLeft -= n; 173: cp = &UndoCQ[FillCQ]; 174: 175: while (n > 0) 176: { 177: i = u->num_undochars - FillCQ; 178: if (i > n) i = n; 179: if (i > 0) 180: { 181: bcopy (p, cp, i); 182: p += i; 183: cp += i; 184: FillCQ += i; 185: n -= i; 186: } 187: 188: if (FillCQ >= NUndoC) 189: { 190: FillCQ = 0; 191: cp = UndoCQ; 192: } 193: else 194: { 195: cp = (char *) xrealloc (UndoCQ, NUndoC); 196: UndoCQ = cp; 197: cp += FillCQ; 198: NCharsLeft += NUndoC - u->num_undochars; 199: u->num_undochars = NUndoC; 200: } 201: } 202: } 203: 204: RecordChange (pos, n) 205: int pos, n; 206: { 207: register struct UndoRec *p = LastUndoRec; 208: if (!bf_cur->undodata) 209: return; 210: if (LastUndoBuf != bf_cur) 211: { 212: Fundo_boundary (); 213: p = 0; 214: } 215: 216: if (bf_modified <= bf_cur->save_modified) 217: NewUndo (Uunmod, pos, 0); 218: 219: if (p && p -> kind == Uchange && p -> pos + p -> len == pos) 220: p -> len += n; 221: else 222: NewUndo (Uchange, pos, n); 223: 224: record_chars (pos, n); 225: } 226: 227: RecordChange1 (pos, bufp, n) 228: int pos; 229: char *bufp; 230: int n; 231: { 232: register struct UndoRec *p = LastUndoRec; 233: if (!bf_cur->undodata) 234: return; 235: if (LastUndoBuf != bf_cur) 236: { 237: Fundo_boundary (); 238: p = 0; 239: } 240: if (p && p -> kind == Uchange && p -> pos + p -> len == pos) 241: p -> len += n; 242: else 243: NewUndo (Uchange, pos, n); 244: 245: record_block (bufp, n); 246: } 247: 248: DoneIsDone () 249: { 250: register struct UndoData *u = bf_cur->undodata; 251: register struct UndoRec *p; 252: 253: if (!u) 254: return 0; 255: 256: p = &UndoRQ[(FillRQ + u->num_undorecs - 1) % u->num_undorecs]; 257: if (p->kind != Unundoable) 258: NewUndo (Unundoable, point, 0); 259: return 0; 260: } 261: 262: DEFUN ("undo-boundary", Fundo_boundary, Sundo_boundary, 0, 0, 0, 263: "Mark a boundary between units of undo.\n\ 264: An undo command will stop at this point,\n\ 265: but another undo command will undo to the previous boundary.") 266: () 267: { 268: register struct UndoData *u = bf_cur->undodata; 269: register struct UndoRec *p; 270: 271: if (!u) 272: return Qnil; 273: 274: p = &UndoRQ[(FillRQ + u->num_undorecs - 1) % u->num_undorecs]; 275: if (p->kind != Uboundary) 276: NewUndo (Uboundary, point, 0); 277: return Qnil; 278: } 279: 280: DEFUN ("undo-more", Fundo_more, Sundo_more, 1, 1, 0, 281: "Undo back N undo-boundaries beyond what was already undone recently.\n\ 282: Call undo-start to get ready to undo recent changes,\n\ 283: then call undo-more one or more times to undo them.") 284: (pfxarg) 285: Lisp_Object pfxarg; 286: { 287: register struct UndoData *u = bf_cur->undodata; 288: register int n = 0; 289: register int chars; 290: register int i = LastUndone; 291: register int arg = XINT (pfxarg); 292: register int len, pos; 293: char tembuf[NUndoC]; 294: 295: if (!u) 296: return Qnil; 297: 298: if (LastUndoneBuf != bf_cur || 299: i == -1) 300: error ("Cannot undo more: changes have been made since the last undo"); 301: 302: while (1) 303: { 304: while (UndoRQ[i = (!i ? u->num_undorecs-1 : i-1)].kind != Uboundary) 305: { 306: if (((UndoRQ[i].kind == Uinsert || UndoRQ[i].kind == Uchange) 307: && (NCharsLeft -= UndoRQ[i].len) < 0) 308: || UndoRQ[i].kind == Unundoable || NUndone >= u->num_undorecs) 309: error ("No further undo information available"); 310: NUndone++; 311: n++; 312: } 313: NUndone++; 314: n++; 315: if (--arg <= 0) 316: break; 317: } 318: 319: i = LastUndone; 320: chars = LastUndoneC; 321: while (--n >= 0) 322: { 323: if (!i) 324: i = u->num_undorecs; 325: i--; 326: 327: len = UndoRQ[i].len; 328: pos = UndoRQ[i].pos; 329: #ifdef SWITCH_ENUM_BUG 330: switch ((int) UndoRQ[i].kind) 331: #else 332: switch (UndoRQ[i].kind) 333: #endif 334: { 335: case Uboundary: 336: break; 337: 338: case Udelete: 339: if (pos < FirstCharacter 340: || pos + len > NumCharacters + 1) 341: error ("Changes to be undone are outside visible portion of buffer"); 342: SetPoint (pos); 343: del_range (point, point + len); 344: break; 345: 346: case Uchange: 347: if (pos < FirstCharacter 348: || pos + len > NumCharacters + 1) 349: error ("Changes to be undone are outside visible portion of buffer"); 350: SetPoint (pos); 351: if (len > NUndoC) 352: /* Should have already said "No more undo info available" */ 353: abort (); 354: save_undone_chars (pos, len, tembuf); 355: chars -= len; 356: if (chars < 0) 357: { 358: replace_chars (point - chars, len + chars, UndoCQ); 359: replace_chars (point, - chars, UndoCQ + chars + u->num_undochars); 360: chars += u->num_undochars; 361: } 362: else 363: replace_chars (point, len, UndoCQ + chars); 364: RecordChange1 (point, tembuf, len); 365: break; 366: 367: case Uinsert: 368: if (pos < FirstCharacter 369: || pos > NumCharacters + 1) 370: error ("Changes to be undone are outside visible portion of buffer"); 371: SetPoint (pos); 372: chars -= len; 373: if (chars < 0) 374: { 375: InsCStr (UndoCQ + chars + u->num_undochars, - chars); 376: InsCStr (UndoCQ, len + chars); 377: chars += u->num_undochars; 378: } 379: else 380: InsCStr (UndoCQ + chars, len); 381: SetPoint (pos); 382: break; 383: 384: case Uunmod: 385: #ifdef CLASH_DETECTION 386: Funlock_buffer (); 387: #endif /* CLASH_DETECTION */ 388: bf_cur->save_modified = bf_modified; 389: RedoModes++; 390: break; 391: 392: default: 393: error ("Something rotten in undo"); 394: return Qnil; 395: } 396: } 397: LastUndone = i; 398: LastUndoneC = chars; 399: return Qnil; 400: } 401: 402: replace_chars (pos, n, string) 403: register int pos, n; 404: register unsigned char *string; 405: { 406: modify_region (pos, pos + n); 407: while (--n >= 0) 408: { 409: CharAt (pos) = *string++; 410: pos++; 411: } 412: } 413: 414: save_undone_chars (pos, n, p) 415: register int pos, n; 416: register char *p; 417: { 418: if (pos < bf_s1 + 1 && pos + n > bf_s1 + 1) 419: { 420: bcopy (&CharAt (pos), p, bf_s1 + 1 - pos); 421: p += bf_s1 + 1 - pos; 422: n -= bf_s1 + 1 - pos; 423: pos = bf_s1 + 1; 424: } 425: 426: bcopy (&CharAt (pos), p, n); 427: } 428: 429: DEFUN ("undo-start", Fundo_start, Sundo_start, 0, 0, 0, 430: "Move undo-pointer to front of undo records.\n\ 431: The next call to undo-more will undo the most recently made change.") 432: () 433: { 434: register struct UndoData *u = bf_cur->undodata; 435: 436: if (!u) 437: error ("Undo information not kept for this buffer"); 438: LastUndoneBuf = bf_cur; 439: NCharsLeft = u->num_undochars; 440: NUndone = 0; 441: LastUndone = FillRQ; 442: LastUndoneC = FillCQ; 443: return Qnil; 444: } 445: 446: syms_of_undo () 447: { 448: defsubr (&Sundo_start); 449: defsubr (&Sundo_boundary); 450: defsubr (&Sundo_more); 451: }