{Begin SubSec Undoing} {Title Undoing} {Text {Tag Undoing} {index *BEGIN* undoing} {index *PRIMARY* undoing} Note: This discussion only applies to undoing under the executive and break; the editors handles undoing itself in a slightly different fashion{note , as described in {PageRef Tag EditorAndPA}}. The {lisp UNDO} capability of the programmer's assistant is implemented by requiring that each operation that is to be undoable be responsible itself for saving on the history list enough information to enable reversal of its side effects. In other words, the assistant does not "know" when it is about to perform a destructive operation, i.e., it is not constantly checking or anticipating. Instead, it simply executes operations, and any undoable changes that occur are automatically saved on the history list by the responsible functions. The {pacom UNDO} command, which involves recovering the saved information and performing the corresponding inverses, works the same way, so that the user can {pacom UNDO} an {pacom UNDO}, and {pacom UNDO} that etc. At each point, until the user specifically requests an operation to be undone, the assistant does not know, or care, whether information has been saved to enable the undoing. Only when the user attempts to undo an operation does the assistant check to see whether any information has been saved. If none has been saved, and the user has specifically named the event he wants undone, the assistant types {index *PRIMARY* NOTHING SAVED (Printed by System)}{lisp NOTHING SAVED}. (When the user simply types {lisp UNDO}, the assistant searches for the last undoable event, ignoring events already undone as well as {pacom UNDO} operations themselves.) This implementation minimizes the overhead for undoing. Only those operations which actually make changes are affected, and the overhead is small: two or three cells of storage for saving the information, and an extra function call. However, even this small price may be too expensive if the operation is sufficiently primitive and repetitive, i.e., if the extra overhead may seriously degrade the overall performance of the program. Hence not every destructive operation in a program should necessarily be undoable; the programmer must be allowed to decide each case individually. {index *PRIMARY* / functions}Therefore for each primitive destructive function, Interlisp has defined an undoable version which always saves information. By convention, the name of the undoable version of a function is the function name, preceeded by "{lisp /}." For example, there is {fn RPLACA} and {fn /RPLACA}, {fn REMPROP} and {fn /REMPROP}, etc. The "slash" functions that are currently implemented can be found as the value of {var /FNS}.{index *PRIMARY* /FNS Var} The various system packages use the appropriate undoable functions. For example, {fn BREAK} uses {fn /PUTD} and {fn /REMPROP} so as to be undoable, and DWIM uses {fn /RPLACA} and {fn /RPLACD}, when it makes a correction.{foot The effects of the following functions are always undoable: {fn DEFINE}, {fn DEFINEQ}, {fn DEFC}{index DEFC Fn} (used to give a function a compiled code definition), {fn DEFLIST}, {fn LOAD}, {fn SAVEDEF}, {fn UNSAVEDEF}, {fn BREAK}, {fn UNBREAK}, {fn REBREAK}, {fn TRACE}, {fn BREAKIN}, {fn UNBREAKIN}, {fn CHANGENAME}, {fn EDITFNS}, {fn EDITF}, {fn EDITV}, {fn EDITP}, {fn EDITE}, {fn EDITL}, {fn ESUBST}, {fn ADVISE}, {fn UNADVISE}, {fn READVISE}, plus any changes caused by DWIM. }{comment endfootnote} Similarly, the user can simply use the corresponding {index / functions}{lisp /} function if he wants to make a destructive operation in his own program undoable. When the {lisp /} function is called, it will save the {pacom UNDO} information in the current event on the history list. {Tag SlashifyingTypedInFns} The programmer's assistant cannot know whether efficiency and overhead are serious considerations for the execution of an expression in a user {it program}, so the user must decide if he wants these operations undoable by explicitly calling {fn /MAPCONC}, etc. However, {it typed-in} expressions rarely involve iterations or lengthy computations {it directly}. Therefore, before evaluating the user input, the programmer's assistant substitutes the corresponding undoable function for any destructive function (see {index LISPX/ FN}{fn LISPX/}, {PageRef Fn LISPX/}). For example, if the user types {lisp (MAPCONC NASDIC {ellipsis})}, it is actually {lisp (/MAPCONC NASDIC {ellipsis})} that is evaluated. Obviously, with a more sophisticated analysis of both user input and user programs, the decision concerning which operations to make undoable could be better advised. However, we have found the configuration described here to be a very satisfactory one. The user pays a very small price for being able to undo what he types in, and if he wishes to protect himself from malfunctioning in his own programs, he can have his program explicitly call undoable functions. {note Section on TESTMODE deleted, per instructions from lmm (stored in Stuff.TESTMODE)} {Begin SubSec Undoing Out of Order} {Title Undoing Out of Order} {Text {Tag UndoingOutOfOrder} {index *PRIMARY* undoing out of order} {fn /RPLACA} operates undoably by saving (on the history list) the list cell that is to be changed and its original {fn CAR}. Undoing a {fn /RPLACA} simply restores the saved {fn CAR}. This implementation can produce unexpected results when multiple {fn /RPLACA}s are done on the same list cell, and then undone out of order. For example, if the user types {lisp (RPLACA FOO 1)}, followed by {lisp (RPLACA FOO 2)}, then undoes both events by undoing the most recent event first, then undoing the older event, {lisp FOO} will be restored to its state before either {fn RPLACA} operated. However if the user undoes the first event, {it then} the second event, {lisp (CAR FOO)} will be {lisp 1}, since this is what was in {fn CAR} of {lisp FOO} before {lisp (RPLACA FOO 2)} was executed. Similarly, if the user types {lisp (NCONC1 FOO 1)}, followed by {lisp (NCONC1 FOO 2)}, undoing just {lisp (NCONC1 FOO 1)} will remove both {lisp 1} and {lisp 2} from {lisp FOO}. The problem in both cases is that the two operations are not "independent." In general, operations are always independent if they affect different lists or different sublists of the same list. Undoing in reverse order of execution, or undoing independent operations, is always guaranteed to do the "right" thing. However, undoing dependent operations out of order may not always have the predicted effect. Property list operations, (i.e., {fn PUTPROP}, {fn ADDPROP} and {fn REMPROP}) are handled specially, so that operations that affect different properties on the same property list are always independent. For example, if the user types {lisp (PUTPROP 'FOO 'BAR 1)} then {lisp (PUTPROP 'FOO 'BAZ 2)}, then undoes the first event, the {lisp BAZ} property will remain, even though it may not have been on the property list of {lisp FOO} at the time the first event was executed. }{End SubSec Undoing Out of Order} {Begin SubSec SAVESET} {Title SAVESET} {Text Typed-in {fn SET}s are made undoable by substituting a call to {fn SAVESET}. {fn SETQ} is made undoable by substituting {fn SAVESETQ}{index SAVESETQ FN}, and {fn SETQQ} by {fn SAVESETQQ}{index SAVESETQQ FN}, both of which are implemented in terms of {fn SAVESET}. In addition to saving enough information on the history list to enable undoing, {fn SAVESET} operates in a manner analogous to {fn SAVEDEF} ({PageRef Fn SAVEDEF}) when it resets a top level value: when it changes a top level binding from a value other than {index NOBIND Litatom}{lisp NOBIND} to a new value that is not {fn EQUAL} to the old one, {fn SAVESET} saves the old value of the variable being set on the variable's property list under the property {index VALUE Prop}{prop VALUE}, and prints the message {lisp ({arg VARIABLE} RESET)}. The old value can be restored via the function {fn UNSET}, which also saves the current value (but does not print a message). Thus {index UNSET FN}{fn UNSET} can be used to flip back and forth between two values. {indexX {Name RESET} {Type (Printed by System)} {Text {lisp ({arg VARIABLE} RESET)}} } Of course, {index UNDO PACom}{pacom UNDO} can be used as long as the event containing this call to {index SAVESET FN}{fn SAVESET} is still active. Note however that the old value will remain on the property list, and therefore be recoverable via {index UNSET FN}{fn UNSET}, even after the original event has been forgotten. {index RPAQ FN}{fn RPAQ} and {index RPAQQ FN}{fn RPAQQ} are implemented via calls to {fn SAVESET}. Thus old values will be saved and messages printed for any variables that are reset as the result of loading a file. For top level variables, {index SAVESET FN}{fn SAVESET} also adds the variable to the appropriate spelling list, thereby noticing variables set in files via {fn RPAQ} or {fn RPAQQ}, as well as those set via type-in. {FnDef {FnName SAVESET} {FnArgs NAME VALUE TOPFLG FLG} {Text An undoable {fn SET}. {fn SAVESET} scans the stack looking for the last binding of {arg NAME}, sets {arg NAME} to {arg VALUE}, and returns {arg VALUE}. If the binding changed was a top level binding, {arg NAME} is added to the spelling list {index SPELLINGS3 Var}{var SPELLINGS3} (see {PageRef Var SPELLINGS3}). Furthermore, if the old value was not {index NOBIND Litatom}{lisp NOBIND}, and was also not {fn EQUAL} to the new value, {fn SAVESET} calls the file package to update the necessary file records. Then, if {index DFNFLG Var}{var DFNFLG} is not equal to {lisp T}, {fn SAVESET} prints {lisp ({arg NAME} RESET)}, and saves the old value on the property list of {arg NAME}, under the property {index VALUE Prop}{prop VALUE}. If {arg TOPFLG}={lisp T}, {index SAVESET FN}{fn SAVESET} operates as above except that it always uses {arg NAME}'s top-level value cell. When {arg TOPFLG} is {lisp T}, and {index DFNFLG Var}{var DFNFLG} is {index ALLPROP Litatom}{lisp ALLPROP} and the old value was not {index NOBIND Litatom}{lisp NOBIND}, {fn SAVESET} simply stores {arg VALUE} on the property list of {arg NAME} under the property {index VALUE Prop}{prop VALUE}, and returns {arg VALUE}. This option is used for loading files without disturbing the current value of variables (see {PageRef Var DFNFLG}). If {arg FLG}={lisp NOPRINT},{index NOPRINT Litatom} {fn SAVESET} saves the old value, but does not print the message. This option is used by {fn UNSET}. If {arg FLG}={lisp NOSAVE},{index NOSAVE Litatom} {fn SAVESET} does {it not} save the old value on the property list, nor does it add {arg NAME} to {index SPELLINGS3 Var}{var SPELLINGS3}. However, the call to {index SAVESET FN}{fn SAVESET} is still undoable. This option is used by {fn /SET}. If {arg FLG}={lisp NOSTACKUNDO},{index NOSTACKUNDO Litatom} {fn SAVESET} is undoable only if the binding being changed is a top-level binding, i.e. this says when resetting a variable that has been rebound, don't bother to make it undoable. {note synonyms for FLG? : (check these, thay may have other meanings) NOUNDO <-> NOSTACKUNDO NOPROPSAVE <-> NOSAVE} }} {FnDef {FnName UNSET} {FnArgs NAME} {Text If {arg NAME} does not contain a property {index VALUE Prop}{prop VALUE}, {fn UNSET} generates an error.{index NO VALUE SAVED: Error} Otherwise {fn UNSET} calls {fn SAVESET} with {arg NAME}, the property value, {arg TOPFLG}={lisp T}, and {arg FLG}={lisp NOPRINT}. }} }{End SubSec SAVESET} {Begin SubSec UNDONLSETQ and RESETUNDO} {Title UNDONLSETQ and RESETUNDO} {Text The function {fn UNDONLSETQ} provides a limited form of backtracking: if an error occurs under the {fn UNDONLSETQ}, all undoable side effects executed under the {fn UNDONLSETQ} are undone. {fn RESETUNDO}, used in conjunction with {fn RESETLST} and {fn RESETSAVE} ({PageRef Fn RESETSAVE}), provides a more general undo capability where the user can specify that the side effects be undone after the specified computation finishes, is aborted by an error, or by a control-D. {FnDef {FnName UNDONLSETQ} {FnArgs UNDOFORM {anonarg}} {Type NLAMBDA} {Text An nlambda function similar to {index NLSETQ FN}{fn NLSETQ} ({PageRef Fn NLSETQ}). {fn UNDONLSETQ} evaluates {arg UNDOFORM}, and if no error occurs during the evaluation, returns {lisp (LIST (EVAL {arg UNDOFORM}))} and passes the undo information from {arg UNDOFORM} (if any) upwards. If an error does occur, the {fn UNDONLSETQ} returns {lisp NIL}, and any undoable changes made during the evaluation of {arg UNDOFORM} are undone. Any undo information is stored directly on the history event (if {index LISPXHIST Var}{var LISPXHIST} is not {lisp NIL}), so that if the user control-D's out of the {fn UNDONLSETQ}, the event is still undoable. {fn UNDONLSETQ} will operate correctly if {index #UNDOSAVES Var}{var #UNDOSAVES} is or has been exceeded for this event, or is exceeded while under the scope of the {fn UNDONLSETQ}. Note: Caution must be exercised in using coroutines or other non-standard means of exiting while under an {fn UNDONLSETQ}. See discussion in {PageRef Fn RESETLST}. }} {FnDef {FnName RESETUNDO} {FnArgs X STOPFLG} {Text For use in conjunction with {fn RESETLST} ({PageRef Fn RESETLST}). {lisp (RESETUNDO)} initializes the saving of undo information and returns a value which when given back to {fn RESETUNDO} undoes the intervening side effects. For example, {lisp (RESETLST (RESETSAVE (RESETUNDO)) . {arg FORMS})} will undo the side effects of {arg FORMS} on normal exit, or if an error occurs or a control-D is typed. If {arg STOPFLG}={lisp T}, {fn RESETUNDO} stops accumulating undo information it is saving on {arg X}. Note that this has no bearing on the saving of undo information on higher {fn RESETUNDO}'s, or on being able to undo the entire event. For example, {lispcode (RESETLST (SETQ FOO (RESETUNDO)) (RESETSAVE NIL (LIST 'RESETUNDO FOO)) (ADVISE {ellipsis}) (RESETUNDO FOO T) . {arg FORMS})} would cause the advice to be undone, but {it not} any of the side effects in {arg FORMS}. }} }{End SubSec UNDONLSETQ and RESETUNDO} }{End SubSec Undoing} {Begin SubSec Format and Use of the History List} {Title Format and Use of the History List} {Text {Tag HistoryListFormat} {index *BEGIN* history list} {index *BEGIN* format and use of history list} The system currently uses three history lists, {index *PRIMARY* LISPXHISTORY Var}{var LISPXHISTORY} for the top-level Interlisp executive, {index EDITHISTORY Var}{var EDITHISTORY} for the editors, and {index *PRIMARY* ARCHIVELST Var}{var ARCHIVELST} for archiving events (see {PageRef PACom ARCHIVE}). All history lists have the same format, use the same functions, {index HISTORYSAVE FN}{fn HISTORYSAVE}, for recording events, and use the same set of functions for implementing commands that refer to the history list, e.g., {fn HISTORYFIND}, {fn PRINTHISTORY}, {fn UNDOSAVE}, etc. Each history list is a list of the form {lisp ({arg L} {arg EVENT#} {arg SIZE} {arg MOD})}, where {arg L} is the list of events with the most recent event first, {arg EVENT#} is the event number for the most recent event on {arg L}, {arg SIZE} is the size of the time-slice (below), i.e., the maximum length of {arg L}, and {arg MOD} is the highest possible event number. {var LISPXHISTORY} and {var EDITHISTORY} are both initialized to {lisp (NIL 0 100 100)}. Setting {var LISPXHISTORY} or {var EDITHISTORY} to {lisp NIL} disables all history features, so {var LISPXHISTORY} and {var EDITHISTORY} act like flags as well as repositories of events. {Tag TimeSlice} {index *PRIMARY* time-slice of history list} Each history list has a maximum length, called its "time-slice." As new events occur, existing events are aged, and the oldest events are "forgotten." For efficiency, the storage used to represent the forgotten event is reused in the representation of the new event, so the history list is actually a ring buffer. The time-slice of a history list can be changed with the function {index CHANGESLICE FN}{fn CHANGESLICE}, {PageRef Fn CHANGESLICE}. Larger time-slices enable longer "memory spans," but tie up correspondingly greater amounts of storage. Since the user seldom needs really "ancient history," and a facility is provided for saving and remembering selected events (see {pacom NAME} and {pacom RETRIEVE}, {PageRef PACom NAME}), a relatively small time-slice such as 30 events is more than adequate, although some users prefer to set the time-slice as large as 100 events. {Tag EventNumbers} {index *PRIMARY* event number} If {var PROMPT#FLG} ({PageRef Var PROMPT#FLG}) is set to {lisp T}, an "event number" will be printed before each prompt. More recent events have higher numbers. When the event number of the current event is 100, the next event will be given number 1. If the time-slice is greater than 100, the "roll-over" occurs at the next highest hundred, so that at no time will two events ever have the same event number. For example, if the time-slice is 150, event number 1 will follow event number 200. Each individual event on {arg L} is a list of the form {lisp ({arg INPUT} {arg ID} {arg VALUE} . {arg PROPS})}. {arg ID} is the prompt character for this event, e.g., {lisp _}, {lisp :}, {lisp *}, etc. {arg VALUE} is the value of the event, and is initialized to {index bell (in history event)}bell.{foot On {index EDITHISTORY Var}{var EDITHISTORY}, this field is used to save the side effects of each command. See {PageRef Tag EditorAndPA}. }{comment endfootnote} {arg PROPS} is a property list used to associate other information with the event (described below). {arg INPUT} is the input sequence for the event. Normally, this is just the input that the user typed-in. For an {fn APPLY} format input, this is a list consisting of two expressions; for an {fn EVAL} format input, this is a list of just one expression; for an input entered as list of atoms, {arg INPUT} is simply that list. For example, {Begin Table format of INPUT in event} {COLUMN} {COLUMN} {First User Input} {Next {arg INPUT} is:} {First {lisp PLUS[1 1]}} {Next {lisp (PLUS (1 1))}} {First {lisp (PLUS 1 1)}} {Next {lisp ((PLUS 1 1))}} {First {lisp PLUS 1 1{cr}}} {Next {lisp (PLUS 1 1)}} {End Table format of INPUT in event} {Tag PseudoCarriageReturn} If the user types in a programmer's assistant command that "unreads" and reexecutes other events ({pacom REDO}, {pacom USE},{index USE PACom}, etc.), {arg INPUT} contains a "sequence" of the inputs from the redone events. Specifically, the {arg INPUT} fields from the specified events are concatenated into a single list, seperated by special markers called "pseudo-carriage returns,"{index *PRIMARY* pseudo-carriage return} which print out as the string {lisp ""}.{foot The value of the variable {var HISTSTR0}{index *PRIMARY* HISTSTR0 Var} is used to represent a pseudo-carriage return. This is initially the string {lisp ""}.{index *PRIMARY* "" (in history commands)} Note that the functions that recognize pseudo-carriage returns compare them to {var HISTSTR0} using {fn EQ}, so this marker will never be confused with a string that was typed in by the user. }{comment endfootnote} When the result of this concatenation is "reread," the pseudo-carriage-returns are treated by {index LISPXREAD FN}{fn LISPXREAD} and {index READLINE FN}{fn READLINE} exactly as real carriage returns, i.e., they serve to distinguish between {fn APPLY} and {fn EVAL} formats on inputs to {index LISPX FN}{fn LISPX}, and to delimit line commands to the editor. The same convention is used for representing multiple inputs when a {pacom USE} command involves sequential substitutions. For example, if the user types {lisp GETD(FOO)} and then {lisp USE FIE FUM FOR FOO}, the input sequence that will be constructed is {lisp (GETD (FIE) "" GETD (FUM))}, which is the result of substituting {lisp FIE} for {lisp FOO} in {lisp (GETD (FOO))} concatenated with the result of substituting {lisp FUM} for {lisp FOO} in {lisp (GETD (FOO))}. Note that once a multiple input has been entered as the input portion of a new event, that event can be treated exactly the same as one resulting from type-in. In other words, no special checks have to be made when {it referencing} an event, to see if it is simple or multiple. This implementation permits an event specification to refer to a single simple event, or to several events, or to a single event originally constructed from several events (which may themselves have been multiple input events, etc.) without having to treat each case separately. {pacom REDO},{index REDO PACom} {pacom RETRY},{index RETRY PACom} {pacom USE},{index USE PACom} {pacom ...},{index ... PACom} and {index FIX PACom}{pacom FIX} commands, i.e., those commands that reexecute previous events, are not stored as inputs, because the input portion for these events are the expressions to be "reread". The history commands {index UNDO PACom}{pacom UNDO}, {index NAME PACom}{pacom NAME}, {index RETRIEVE PACom}{pacom RETRIEVE}, {index BEFORE PACom}{pacom BEFORE}, and {index AFTER PACom}{pacom AFTER} {it are} recorded as inputs, and {pacom ??}{index ?? PACom} prints them exactly as they were typed. {arg PROPS} is a property list of the form {lisp ({arg PROPERTY{sub 1}} {arg VALUE{sub 1}} {arg PROPERTY{sub 2}} {arg VALUE{sub 2}} {ellipsis})}, that can be used to associate arbitrary information with a particular event. Currently, the following properties are used by the programmer's assistant: {Begin LabeledList event properties} {Name {lisp SIDE}{index *PRIMARY* SIDE (History List Property)}} {Text A list of the side effects of the event. See {fn UNDOSAVE}, {PageRef Fn UNDOSAVE}. {note should info from UNDOSAVE about SIDE=NOSAVE and #UNDOSAVES be moved here??} } {Name {lisp *PRINT*}{index *PRIMARY* *PRINT* (History List Property)}} {Text Used by the {pacom ??} command when special formatting is required, for example, when printing events corresponding to the break commands {breakcom OK}, {breakcom GO}, {breakcom EVAL}, and {breakcom ?=}. } {Name {lisp USE-ARGS}{index *PRIMARY* USE-ARGS (History List Property)}} {Name {lisp ...ARGS}{index *PRIMARY* ...ARGS (History List Property)}} {Text The {lisp USE-ARGS} and {lisp ...ARGS} properties are used to save the arguments and expression for the corresponding history command. } {Name {lisp *ERROR*}{index *PRIMARY* *ERROR* (History List Property)}} {Name {lisp *CONTEXT*}{index *PRIMARY* *CONTEXT* (History List Property)}} {Text {lisp *ERROR*} and {lisp *CONTEXT*} are used to save information when errors occur for subsequent use by the {pacom $} command. Whenever an error occurs, the offender is automatically saved on that event's entry in the history list, under the {lisp *ERROR*} property. {note *CONTEXT* ??} } {Name {lisp *LISPXPRINT*}{index *PRIMARY* *LISPXPRINT* (History List Property)}} {Text Used to record calls to {index LISPXPRINT FN}{fn LISPXPRINT}, {fn LISPXPRIN1}, etc. (see {PageRef Fn LISPXPRINT}). } {Name {lisp *ARCHIVE*}{index *PRIMARY* *ARCHIVE* (History List Property)}} {Text The property {lisp *ARCHIVE*} on an event causes the event to be automatically archived when it "falls off the end" of the history list (see {PageRef PACom ARCHIVE}). {note does it just have to be on PROPS, or does it have to have value T??} } {Name {lisp *GROUP*}{index *PRIMARY* *GROUP* (History List Property)}} {Name {lisp *HISTORY*}{index *PRIMARY* *HISTORY* (History List Property)}} {Text The {lisp *HISTORY*} and {lisp *GROUP*} properties are used for commands that reexecute previous events, i.e., {pacom REDO}, {pacom RETRY}, {pacom USE}, {pacom ...}, and {pacom FIX}. The value of the {lisp *HISTORY*} property is the history command that the user actually typed, e.g., {lisp REDO FROM F}. This is used by the {pacom ??} command when printing the event. The value of the {lisp *GROUP*} property is a structure containing the side effects, etc. for the individual inputs being reexecuted. This structure is described below. } {End LabeledList event properties} {note where should the following discussion go?? near HISTORYSAVE ??} When {fn LISPX} is given an input, it calls {fn HISTORYSAVE} ({PageRef Fn HISTORYSAVE}) to record the input in a new event.{foot The commands {pacom ??}, {pacom FORGET}, {pacom TYPE-AHEAD}, {pacom $BUFS}, and {pacom ARCHIVE} are executed immediately, and are not recorded on the history list. }{comment endfootnote} Normally, {fn HISTORYSAVE} creates and returns a new event. {index LISPX FN}{fn LISPX} binds the variable {var LISPXHIST}{index *PRIMARY* LISPXHIST Var} to the value of {index HISTORYSAVE FN}{fn HISTORYSAVE}, so that when the operation has completed, {fn LISPX} knows where to store the value. Note that by the time it completes, the operation may no longer correspond to the most recent event on the history list. For example, all inputs typed to a lower break will appear later on the history list. After binding {var LISPXHIST}, {fn LISPX} executes the input, stores its value in the value field of the {var LISPXHIST} event, prints the value, and returns. {Tag EventGroup} When the input is a {pacom REDO}, {pacom RETRY}, {pacom USE}, {pacom ...}, or {pacom FIX} command, the procedure is similar, except that the event is also given a {lisp *GROUP*} property, initially {lisp NIL}, and a {lisp *HISTORY*} property, and {fn LISPX} simply unreads the input and returns. When the input is "reread", it is {fn HISTORYSAVE}, not {fn LISPX}, that notices this fact, and finds the event from which the input originally came.{foot If {index HISTORYSAVE FN}{fn HISTORYSAVE} cannot find the event, for example if a user program unreads the input directly, and not via a history command, {fn HISTORYSAVE} proceeds as though the input were typed. }{comment endfootnote} {fn HISTORYSAVE} then adds a new {lisp ({arg INPUT} {arg ID} {arg VALUE} . {arg PROPS})} entry to the {lisp *GROUP*} property for this event, and returns this entry as the "new event." {fn LISPX} then proceeds exactly as when its input was typed directly, i.e., it binds {var LISPXHIST} to the value of {fn HISTORYSAVE}, executes the input, stores the value in {fn CADDR} of {var LISPXHIST}, prints the value, and returns. In fact, {index LISPX FN}{fn LISPX} never notices whether it is working on freshly typed input, or input that was reread. Similarly, {index UNDOSAVE FN}{fn UNDOSAVE} will store undo information on {index LISPXHIST Var}{var LISPXHIST} the same as always, and does not know or care that {var LISPXHIST} is not the entire event, but one of the elements of the {lisp *GROUP*} property. Thus when the event is finished, its entry will look like: {lispcode ({arg INPUT} {arg ID} {arg VALUE} *HISTORY* {arg COMMAND} *GROUP* (({arg INPUT{sub 1}} {arg ID{sub 1}} {arg VALUE{sub 1}} SIDE {arg SIDE{sub 1}}) ({arg INPUT{sub 2}} {arg ID{sub 2}} {arg VALUE{sub 2}} SIDE {arg SIDE{sub 2}}) {ellipsis}))} In this case, the value field of the event with the {lisp *GROUP*} property is not being used; {index VALUEOF FN}{fn VALUEOF} instead returns a list of the values from the {prop *GROUP*} property. Similarly, {pacom UNDO} operates by collecting the {index SIDE Prop}{prop SIDE} properties from each of the elements of the {index *GROUP* Prop}{prop *GROUP*} property, and then undoing them in reverse order. This implementation removes the burden from the function calling {index HISTORYSAVE FN}{fn HISTORYSAVE} of distinguishing between new input and reexecution of input whose history entry has already been set up. {index *END* format and use of history list} {index *END* history list} }{End SubSec Format and Use of the History List}