GET ".ECCE-HDR" /* This chapter supports the UNDO feature which allows the user to backtrack by either 'n' commands or the maximum in the log trail. */ MANIFEST { stack_csz = 1000 // size of circular buffer used for log. del_type = 1 cha_type = 2 ins_type = 3 pos_type = 4 } STATIC { stack_base = 0 // position of length word of last logged item. stack_top = 0 // position of next free cell. } /* The circular buffer is structured as follows: Length, logged item, type... Length, item, type ^ ^ stack_base stack_top If there is insufficient room in the buffer for a new item then a whole log-item is removed using 'loose_items'. */ LET next_pos(p) = (p = stack_csz -> 0, p+1) AND prev_pos(p) = (p = 0 -> stack_csz, p-1) AND wr_item(value) BE {1 undo_stack!stack_top := value stack_top := next_pos(stack_top) }1 AND free_space() = stack_base <= stack_top -> stack_csz - stack_top + stack_base, stack_base - stack_top AND rd_item() = VALOF {1 IF stack_base = stack_top RESULTIS errorvalue // buffer empty stack_top := prev_pos(stack_top) RESULTIS undo_stack!stack_top }1 AND loose_items() BE {1 LET len = undo_stack!stack_base stack_base := (stack_base+len) REM stack_csz }1 AND make_room(s) = VALOF {1 s +:= 1 // include length word. IF free_space() < s THEN { IF s > stack_csz THEN // too large to log { stack_top := stack_base // empty stack RESULTIS FALSE } loose_items() REPEATUNTIL free_space() >= s } wr_item(s) // write length word. RESULTIS TRUE }1 LET trace(format, p1, p2, p3, p4, p5, p6) BE {1 LET o = output() selectoutput(outstream) writef(format, p1, p2, p3, p4, p5, p6) newline() selectoutput(o) }1 /* The structure of the log-items are as follows: DEL_type, number, first grab, value 1, value 2 ... value n CHA_type, grab, original value INS_type, grab POS_type, line, cursor, match_len, max_grab */ LET reset_trail() BE stack_top := stack_base LET log_del(start, n) BE {1 // trace("log_del called with start = %n, n = %n", start, n) UNLESS make_room(n+3) RETURN FOR i=start+n TO start BY -1 DO wr_item(fetch_grab(i)) wr_item(start) wr_item(n) wr_item(del_type) }1 AND log_cha(line, grab) BE {1 // trace("log_cha called with grab = %n", grab) UNLESS make_room(3) RETURN wr_item(grab) wr_item(line) wr_item(cha_type) }1 AND log_ins(grab) BE {1 // trace("log_ins called with grab = %n", grab) UNLESS make_room(2) RETURN wr_item(grab) wr_item(ins_type) }1 AND log_pos(line, cursor, ml) BE {1 // trace("log_pos called with line = %n, cursor = %n, ml = %n", line, cursor, ml) UNLESS make_room(5) RETURN wr_item(fetch_max_grab()) wr_item(ml) wr_item(cursor) wr_item(line) wr_item(pos_type) }1 // Now for the restore part of the code. LET restore_del() BE {1 // trace("restore_del called with no parameters") { LET n = rd_item() LET grab = rd_item() // trace("Number: %n, start grab: %n*N", n, grab) copy_lines(grab, grab+n, last_line-grab+1) // make space for deleted lines last_line +:= n FOR i=grab TO grab+n DO store_grab(i, rd_item()) // restore old values of pointers }1 AND restore_cha() BE store_grab(rd_item(), rd_item()) AND del_ins() BE {1 // trace("del_ins called with no parameters") { LET grab = rd_item() w_space := errorvalue // prevent 'copy_lines' from logging. copy_lines(grab+1, grab, last_line-grab) last_line -:= 1 w_space := 0 }1 AND restore_pos() BE {1 // trace("restore_pos called with no parameters") cur_line, cursor, ml := rd_item(), rd_item(), rd_item() restore_max_grab(rd_item()) }1 AND undo_changes(n) = VALOF {1 LET type = rd_item() SWITCHON type INTO { CASE del_type: restore_del(); ENDCASE CASE cha_type: restore_cha(); ENDCASE CASE ins_type: del_ins(); ENDCASE CASE pos_type: restore_pos() n -:= 1 IF n = 0 THEN { rd_item() // remove length word RESULTIS TRUE } DEFAULT: RESULTIS FALSE // no more items logged } rd_item() // remove length word. }1 REPEAT