{Begin SubSec Programmer's Assistant Functions} {Title Programmer's Assistant Functions} {Text {note some serious thought should be given to flushing the documentation of some of these functions. This section gives the user more info about the low-level mud of the p.a. than he/she really needs. If the user is going to muck around with this stuff, the interface should be revamped extensively!!} {FnDef {Name LISPX} {Args LISPXX LISPXID LISPXXMACROS LISPXXUSERFN LISPXFLG} {Text {fn LISPX} is the primary function of the programmer's assistant. {fn LISPX} takes one user input, saves it on the history list, evaluates it, saves its value, and prints and returns it. {fn LISPX} also interpretes p.a. commands, {var LISPXMACROS}, {var LISPXHISTORYMACROS}, and {var LISPXUSERFN}. If {arg LISPXX} is a list, it is interpreted as the input expression. Otherwise, {fn LISPX} calls {index READLINE FN}{fn READLINE}, and uses {arg LISPXX} plus the value of {fn READLINE} as the input for the event. If {arg LISPXX} is a list {fn CAR} of which is {lisp LAMBDA} or {lisp NLAMBDA}, {index LISPX FN}{fn LISPX} calls {index LISPXREAD FN}{fn LISPXREAD} to obtain the arguments. {arg LISPXID} is the prompt character to print before accepting user input. A user can call {fn LISPX} specifying any prompt character as {arg LISPXID} except for {lisp *}, since in certain cases {fn LISPX} must use the value of {arg LISPXID} to tell whether or not it was called from the editor. If {arg LISPXXMACROS} is not {lisp NIL}, it is used as the list of {fn LISPX} macros, otherwise the top level value of the variable {index LISPXMACROS Var}{var LISPXMACROS} is used. {note how about LISPXHISTORYMACROS ??} If {arg LISPXXUSERFN} is not {lisp NIL}, it is used as the {index LISPXUSERFN Var}{var LISPXUSERFN}. In this case, it is not necessary to both set and define {var LISPXUSERFN} as described on {PageRef Var LISPXUSERFN}. {arg LISPXFLG} is used by the {editcom E} command in the editor (see {PageRef Tag EditorAndPA}). Note that the history is {it not} one of the arguments to {index LISPX FN}{fn LISPX}, i.e., the editor must bind (reset) {index LISPXHISTORY Var}{var LISPXHISTORY} to {index EDITHISTORY Var}{var EDITHISTORY} before calling {fn LISPX} to carry out a history command. {fn LISPX} will continue to operate as an {fn EVAL}/{fn APPLY} function if {var LISPXHISTORY} is {lisp NIL}. Only those functions and commands that involve the history list will be affected. {fn LISPX} performs spelling corrections using {index LISPXCOMS Var}{var LISPXCOMS}, a list of its commands, as a spelling list{index spelling lists}{index spelling correction} whenever it is given an unbound atom or undefined function, before attempting to evaluate the input. {fn LISPX} is responsible for rebinding {index HELPCLOCK Var}{var HELPCLOCK}, used by {fn BREAKCHECK} ({PageRef Fn BREAKCHECK}) for computing the amount of time spent in a computation, in order to determine whether to go into a break if and when an error occurs. }} {note In Interlisp-10, the overhead for a call to {index LISPX FN}{fn LISPX} is approximately 17 milliseconds, of which 12 milliseconds are spent in maintaining the spelling lists. In other words, in Interlisp, the user pays 17 more milliseconds for each {fn EVAL} or {fn APPLY} input over a conventional LISP executive, in order to enable the features described in this chapter.} {FnDef {FnName USEREXEC} {FnArgs LISPXID LISPXXMACROS LISPXXUSERFN} {Text Repeatedly calls {index LISPX FN}{fn LISPX} under errorset protection specifying {arg LISPXXMACROS} and {arg LISPXXUSERFN}, and using {arg LISPXID} (or {lisp ←} if {arg LISPXID}={lisp NIL}) as a prompt character. {fn USEREXEC} is exited via the command {pacom OK},{index OK PAcom} or else with a {fn RETFROM}. }} {FnDef {FnName LISPXEVAL} {FnArgs LISPXFORM LISPXID} {Text Evaluates {arg LISPXFORM} (using {fn EVAL}) the same as though it were typed in to {index LISPX FN}{fn LISPX}, i.e., the event is recorded, and the evaluation is made undoable by substituting the slash functions for the corresponding destructive functions (see {PageRef Tag SlashifyingTypedInFns}). {index LISPXEVAL FN}{fn LISPXEVAL} returns the value of the form, but does not print it. }} When {fn LISPX} recieves an "input," it may come from the user typing it in, or it may be an input that has been "unread." {fn LISPX} handles these two cases by getting inputs with {fn LISPXREAD} and {fn READLINE}, described below. These functions use the variable {var READBUF}{index *PRIMARY* READBUF Var} to store the expressions that have been unread. When {var READBUF} is not {lisp NIL}, {fn READLINE} and {fn LISPXREAD} "read" expressions from {var READBUF} until {var READBUF} is {lisp NIL}, or until they read a pseudo-carriage return (see {PageRef Tag PseudoCarriageReturn}). Both functions return a list of the expressions that have been "read." (The pseudo-carriage return is not included in the list.) When {var READBUF} is {lisp NIL}, both {fn LISPXREAD} and {fn READLINE} actually obtain their input by performing {lisp (APPLY* LISPXREADFN {arg FILE})}, where {index *PRIMARY* LISPXREADFN Var}{var LISPXREADFN} is initially set to {lisp READ}. The user can make {fn LISPX}, the editor, break, etc. do their reading via a different input function by simply setting {var LISPXREADFN} to the name of that function (or an appropriate {lisp LAMBDA} expression). Note: The user should only add expressions to {var READBUF} using the function {fn LISPXUNREAD} ({PageRef Fn LISPXUNREAD}), which knows about the format of {var READBUF}. {FnDef {FnName READLINE} {FnArgs RDTBL {anonarg} {anonarg}} {Text Reads a line from the {index terminal}terminal, returning it as a list. If {lisp (READP T)} is {lisp NIL}, {fn READLINE} returns {lisp NIL}. Otherwise it reads expressions by performing {lisp (APPLY* LISPXREADFN T)} ({var LISPXREADFN}{index LISPXREADFN Var} is initially set to {fn READ}) until it encounters either: {Begin UnLabeledList until it encounters} {Item an {lisp EOL} (typed by the user) that is not preceded by any spaces, e.g., {lisp A B C{CRsymbol}} and {fn READLINE} returns {lisp (A B C)} } {Item a list terminating in a "{lisp ]}",{index ] (use in input)} in which case the list is included in the value of {fn READLINE}, e.g., {lisp A B (C D]} and {fn READLINE} returns {lisp (A B (C D))}. } {Item an unmatched right parentheses or right square bracket, which is not included in the value of {index READLINE FN}{fn READLINE}, e.g., {lisp A B C]} and {fn READLINE} returns {lisp (A B C)}. } {End UnLabeledList until it encounters} {note the various mentions to [, ] etc should be replaced by references to the syntax classes that they belong to in the terminal read table. rmk} In the case that one or more spaces precede a carriage-return,{index carriage-return} or a list is terminated with a "{lisp )}", {fn READLINE} will type "{lisp ...}" and continue reading on the next line, e.g.,{index *PRIMARY* ... (printed following a carriage-return)} {lispcode A B C{CRsymbol} ...(D E F) ...(X Y Z]} and {index READLINE FN}{fn READLINE} returns {lisp (A B C (D E F) (X Y Z))}. If the user types another carriage-return after the "{lisp ...}", the line will terminate, e.g., {lispcode A B C{CRsymbol} ...{CRsymbol}} and {fn READLINE} returns {lisp (A B C)}. Note that carriage-return, i.e., the {lisp EOL} character, can be redefined with {fn SETSYNTAX} ({PageRef Fn SETSYNTAX}). {fn READLINE} actually checks for the {lisp EOL} character, whatever that may be. The same is true for right parenthesis and right bracket. When {fn READLINE} is called from {fn LISPX}, it operates differently in two respects: {Begin NumberedList READLINE operates differently in two respects} {Item If the line consists of a single {lisp )} or {lisp ]}, {fn READLINE} returns {lisp (NIL)} instead of {lisp NIL}, i.e., the {lisp )} or {lisp ]} {it is} included in the line. This permits the user to type {lisp FOO)} or {lisp FOO]}, meaning call the function {lisp FOO} with no arguments, as opposed to {lisp FOO{CRsymbol}} ({lisp FOO}<carriage-return>), meaning evaluate the variable {lisp FOO}. } {Item If the first expression on the line is a list that is not preceded by any spaces, the list terminates the line regardless of whether or not it is terminated by {lisp ]}. This permits the user to type {lisp EDITF(FOO)} as a single input. } {End NumberedList READLINE operates differently in two respects} Note that if any spaces are inserted between the atom and the left parentheses or bracket, {index READLINE FN}{fn READLINE} will assume that the list does not terminate the line. This is to enable the user to type a line command such as {lisp USE (FOO) FOR FOO}. Therefore, if the user accidentially puts an extra space between a function and its arguments, he will have to complete the input with another carriage return, e.g., {lispcode ←EDITF (FOO) ...{CRsymbol} EDIT *} {note READLINE doc needs a LOT of work!!} }} {FnDef {FnName LISPXREAD} {FnArgs FILE RDTBL} {Text A generalized {fn READ}. If {index READBUF Var}{var READBUF}={lisp NIL}, {fn LISPXREAD} performs {lisp (APPLY* LISPXREADFN {arg FILE})}, which it returns as its value. If {var READBUF} is not {lisp NIL}, {fn LISPXREAD} "reads" and returns the next expression on {var READBUF}. Note: If the user types {index control-U}control-U during the call to {fn READ}, {fn LISPXREAD} calls the editor and returns the edited value. {fn LISPXREAD} also sets {index REREADFLG Var}{var REREADFLG} to {lisp NIL} when it reads via {fn READ}, and sets {var REREADFLG} to the value of {index READBUF Var}{var READBUF} when rereading. {note should this mention REREADFLG ??} }} {FnDef {FnName LISPXREADP} {FnArgs FLG} {Text A generalized {fn READP}. If {arg FLG}={lisp T}, {fn LISPXREADP} returns {lisp T} if there is any input waiting to be "read", in the manner of {fn LISPXREAD}. If {arg FLG}={lisp NIL}, {fn LISPXREADP} returns {lisp T} only if there is any input waiting to be "read" {it on this line.}{note ???} In both cases, leading spaces are ignored, i.e., skipped over with {fn READC}, so that if only spaces have been typed, {fn LISPXREADP} will return {lisp NIL}. }} {FnDef {FnName LISPXUNREAD} {FnArgs LST {anonarg}} {Text Unreads {arg LST}, a list of expressions.{index unreading} }} {FnDef {FnName PROMPTCHAR} {FnArgs ID FLG HISTORY} {Text Called by {fn LISPX} to print the prompt character{index prompt character} {arg ID} before each input. {fn PROMPTCHAR} will not print anything when the next input will be "reread", i.e., when {index READBUF Var}{var READBUF} is not {lisp NIL}. {fn PROMPTCHAR} will not print when {lisp (READP)}={lisp T}, unless {arg FLG} is {lisp T}. The editor calls {fn PROMPTCHAR} with {arg FLG}={lisp NIL} so that extra {lisp *}'s are not printed when the user types several commands on one line. However, {fn EVALQT} calls {fn PROMPTCHAR} with {arg FLG}={lisp T}, since it always wants the {lisp ←} printed (except when "rereading"). If {index PROMPT#FLG Var}{var PROMPT#FLG} ({PageRef Var PROMPT#FLG}) is {lisp T} and {arg HISTORY} is not {lisp NIL}, {fn PROMPTCHAR} prints the current event number (of {arg HISTORY}) before printing {arg ID}. The value of {index PROMPTCHARFORMS Var}{var PROMPTCHARFORMS} ({PageRef Var PROMPTCHARFORMS}) is a list of expressions that are evaluated by {fn PROMPTCHAR} before, and if, it does any printing. }} {FnDef {FnName HISTORYSAVE} {FnArgs HISTORY ID INPUT1 INPUT2 INPUT3 PROPS} {Text Records one event on {arg HISTORY}. If {arg INPUT1} is not {lisp NIL}, the input is of the form {lisp ({arg INPUT{sub 1}} {arg INPUT{sub 2}} . {arg INPUT{sub 3}})}. If {arg INPUT{sub 1}} is {lisp NIL}, and {arg INPUT{sub 2}} is not {lisp NIL}, the input is of the form {lisp ({arg INPUT{sub 2}} . {arg INPUT{sub 3}})}. Otherwise, the input is just {arg INPUT{sub 3}}. {fn HISTORYSAVE} creates a new event with the corresponding input, {arg ID}, value field initialized to {index bell (in history event)}bell, and {arg PROPS}. If the {arg HISTORY} has reached its full size, the last event is removed and cannibalized. The value of {fn HISTORYSAVE} is the new event. However, if {index REREADFLG Var}{var REREADFLG} is not {lisp NIL}, and the most recent event on the history list contains the history command that produced this input, {fn HISTORYSAVE} does not create a new event, but simply adds an {lisp ({arg INPUT} {arg ID} bell . {arg PROPS})} entry to the {lisp *GROUP*} property for that event and returns that entry. See discussion on {PageRef Tag EventGroup}. {var HISTORYSAVEFORMS} ({PageRef Var HISTORYSAVEFORMS}) is a list of expressions that are evaluated under errorset protection each time {fn HISTORYSAVE} creates a new event. }} {FnDef {FnName LISPXSTOREVALUE} {FnArgs EVENT VALUE} {Text Used by {fn LISPX} for storing the value of an event. Can be advised by user to watch for particular values or perform other monitoring functions. }} {FnDef {FnName LISPXFIND} {FnArgs HISTORY LINE TYPE BACKUP {anonarg}} {Text {arg LINE} is an event specification, {arg TYPE} specifies the format of the value to be returned by {fn LISPXFIND}, and can be either {lisp ENTRY}, {lisp ENTRIES}, {lisp COPY}, {lisp COPIES}, {lisp INPUT}, or {lisp REDO}.{note what is the meaning of these types??} {fn LISPXFIND} parses {arg LINE}, and uses {index HISTORYFIND FN}{fn HISTORYFIND} to find the corresponding events. {fn LISPXFIND} then assembles and returns the appropriate structure. {fn LISPXFIND} incorporates the following special features: {Begin NumberedList LISPXFIND incorporates the following special features} {Item if {arg BACKUP}={lisp T}, {fn LISPXFIND} interprets {arg LINE} in the context of the history list {it before} the current event was added. This feature is used, for example, by {fn VALUEOF}, so that {lisp (VALUEOF -1)} will not refer to the {fn VALUEOF} event itself. } {Item if {arg LINE}={lisp NIL} and the last event is an {pacom UNDO}, the next to the last event is taken. This permits the user to type {pacom UNDO} followed by {pacom REDO} or {pacom USE}. } {Item {fn LISPXFIND} recognizes {index @@ (in event specification)}{lisp @@}, and substitutes {index ARCHIVELST Var}{var ARCHIVELST} for {arg HISTORY} (see {PageRef PACom ARCHIVE}). } {Item {index LISPXFIND FN}{fn LISPXFIND} recognizes {index @ (in event specification)}{lisp @}, and retrieves the corresponding event(s) from the property list of the atom following {lisp @} (see {PageRef PACom NAME}). } {End NumberedList LISPXFIND incorporates the following special features} }} {FnDef {FnName HISTORYFIND} {FnArgs LST INDEX MOD EVENTADDRESS {anonarg}} {Text Searches {arg LST} and returns the tails of {arg LST} beginning with the event corresponding to {arg EVENTADDRESS}. {arg LST}, {arg INDEX}, and {arg MOD} are the first three elements of a "history list" structure (see {PageRef Tag HistoryListFormat}). {arg EVENTADDRESS} is an event address (see {PageRef Tag EventAddress}) e.g., {lisp (43)}, {lisp (-1)}, {lisp (FOO FIE)}, {lisp (LOAD ← FOO)}, etc. If {fn HISTORYFIND} cannot find {arg EVENTADDRESS}, it generates an error. }} {FnDef {FnName HISTORYMATCH} {FnArgs INPUT PAT EVENT} {Text Used by {fn HISTORYFIND} for "matching" when {arg EVENTADDRESS} specifies a pattern. Matches {arg PAT} against {arg INPUT}, the input portion of the history event {arg EVENT}, as matching is defined on {PageRef Tag EditPattern}. Initially defined as {lisp (EDITFINDP {arg INPUT} {arg PAT} T)}, but can be advised or redefined by the user. }} {FnDef {FnName ENTRY#} {FnArgs HIST X} {Text {arg HIST} is a history list (see {PageRef Tag HistoryListFormat}). {arg X} is {fn EQ} to one of the events on {arg HIST}. {fn ENTRY#} returns the {index event number}event number for {arg X}. }} {FnDef {FnName UNDOSAVE} {FnArgs UNDOFORM HISTENTRY} {Text {fn UNDOSAVE} adds the "undo information" {arg UNDOFORM} to the {lisp SIDE} property of the history event {arg HISTENTRY}. If there is no {lisp SIDE} property, one is created. If the value of the {lisp SIDE} property is {lisp NOSAVE},{index NOSAVE Litatom} the information is not saved. {arg HISTENTRY} specifies an event. If {arg HISTENTRY}={lisp NIL}, the value of {var LISPXHIST} is used. If both {arg HISTENTRY} and {var LISPXHIST} are {lisp NIL}, {fn UNDOSAVE} is a no-op. Note that {arg HISTENTRY} (or {var LISPXHIST}) can either be a "real" event, or an event within the {lisp *GROUP*} property of another event (see {PageRef Tag EventGroup}). The form of {arg UNDOFORM} is {lisp ({arg FN} . {arg ARGS})}.{foot In the special case of {fn /RPLNODE}{index /RPLNODE FN} and {fn /RPLNODE2},{index /RPLNODE2 FN} the format of {arg UNDOFORM} is {lisp ({arg X} {arg OLDCAR} . {arg OLDCDR})}. When {arg UNDOFORM} is undone, this form is recognized and handled specially. This implementation saves space.{note this is a total kludge!!} }{comment endfootnote} Undoing is done by performing {lisp (APPLY (CAR {arg UNDOFORM}) (CDR {arg UNDOFORM}))}. For example, if the definition of {lisp FOO} is {arg DEF}, {lisp (/PUTD FOO {arg NEWDEF})} will cause a call to {fn UNDOSAVE} with {arg UNDOFORM}={lisp (/PUTD FOO {arg DEF})}. {fn CAR} of the {index SIDE (History List Property)}{lisp SIDE} property of an event is a count of the number of {arg UNDOFORM}s saved for this event. Each call to {fn UNDOSAVE} increments this count. If this count is set to -1, then it is never incremented, and any number of {arg UNDOFORM}s can be saved. If this count is a positive number, {fn UNDOSAVE} restricts the number of {arg UNDOFORM}s saved to the value of {var #UNDOSAVES}, described below. {fn LOAD}{index LOAD FN} initializes the count to -1, so that regardless of the value of {index #UNDOSAVES Var}{var #UNDOSAVES}, no message will be printed, and the {fn LOAD} will be undoable. }} {VarDef {Name #UNDOSAVES} {Text {index #UNDOSAVES Var}The value of {var #UNDOSAVES} is the maximum number of {arg UNDOFORM}s to be saved for a single event. When the count of {arg UNDOFORM}s reaches this number, {index UNDOSAVE FN}{fn UNDOSAVE} prints the message {lisp CONTINUE SAVING?},{index *PRIMARY* CONTINUE SAVING? (Printed by System)} asking the user if he wants to continue saving. If the user answers {lisp NO} or defaults, {fn UNDOSAVE} discards the previously saved information for this event, and makes {index NOSAVE FN}{lisp NOSAVE} be the value of the property {index SIDE (History List Property)}{lisp SIDE}, which disables any further saving for this event. If the user answers {lisp YES}, {fn UNDOSAVE} changes the count to -1, which is then never incremented, and continues saving. The purpose of this feature is to avoid tying up large quantities of storage for operations that will never need to be undone. If {var #UNDOSAVES} is negative, then when the count reaches -{lisp #UNDOSAVES}, {fn UNDOSAVE} simply stops saving without printing any messages or interacting with the user. {var #UNDOSAVES}={lisp NIL} is equivalent to {var #UNDOSAVES}=infinity. {var #UNDOSAVES} is initially {lisp NIL}. }} {FnDef {FnName NEW/FN} {FnArgs FN} {Text {fn NEW/FN} performs the necessary housekeeping operations to make {arg FN} be translated to the undoable version {lisp /{arg FN}} when typed-in.{index / functions} For example, {fn RADIX} can be made undoable when typed-in by performing: {lispcode ← (DEFINEQ (/RADIX (X) (UNDOSAVE (LIST '/RADIX (RADIX X)) (/RADIX) ← (NEW/FN 'RADIX)} }} {FnDef {FnName LISPX/} {FnArgs X FN VARS} {Text {fn LISPX/} performs the substitution of {lisp /} functions{index / functions} for destructive functions that are typed-in. If {arg FN} is not {lisp NIL}, it is the name of a function, and {arg X} is its argument list. If {arg FN} is {lisp NIL}, {arg X} is a form. In both cases, {fn LISPX/} returns {arg X} with the appropriate substitutions. {arg VARS} is a list of bound variables (optional). {index LISPX/ FN}{fn LISPX/} incorporates information about the syntax and semantics of Interlisp expressions. For example, it does not bother to make undoable operations involving variables bound in {arg X}. It does not perform substitution inside of expressions {fn CAR} of which is an nlambda function (unless {fn CAR} of the form has the property {prop INFO}{index INFO Prop} value {lisp EVAL}, see {PageRef Prop INFO}). For example, {lisp (BREAK PUTD)} typed to {fn LISPX}, will break on {fn PUTD}, not {fn /PUTD}. Similarly, substitution {it should} be performed in the arguments for functions like {fn MAPC}, {fn RPTQ}, etc., since these contain expressions that will be evaluated or applied. For example, if the user types {lisp (MAPC '(FOO1 FOO2 FOO3) 'PUTD)} the {fn PUTD} must be replaced by {fn /PUTD}. }} {FnDef {FnName UNDOLISPX} {FnArgs LINE} {Text {arg LINE} is an event specification. {fn UNDOLISPX} is the function that executes {index UNDO PACom}{pacom UNDO} commands by calling {fn UNDOLISPX1} on the appropriate entry(s). }} {FnDef {FnName UNDOLISPX1} {FnArgs EVENT FLG {anonarg}} {Text Undoes one event. {fn UNDOLISPX1} returns {lisp NIL} if there is nothing to be undone. If the event is already undone, {fn UNDOLISPX1} prints {lisp ALREADY UNDONE}{index ALREADY UNDONE (Printed by System)} and returns {lisp T}. Otherwise, {fn UNDOLISPX1} undoes the event, prints a message, e.g., {lisp SETQ UNDONE},{index UNDONE (Printed by System)} and returns {lisp T}. If {arg FLG}={lisp T} and the event is already undone, or is an undo command, {fn UNDOLISPX1} takes no action and returns {lisp NIL}. {fn UNDOLISPX} uses this option to search for the last event to undo. Thus when {arg LINE}={lisp NIL}, {fn UNDOLISPX} simply searches history until it finds an event for which {fn UNDOLISPX1} returns {lisp T}. Undoing an event consists of mapping down ({fn CDR} of) the property value for {index SIDE (History List Property)}{lisp SIDE}, and for each element, applying {fn CAR} to {fn CDR}, and then marking the event undone by attaching (with {fn /ATTACH}) a {lisp NIL} to the front of its {prop SIDE} property. Note that the undoing of each element on the {lisp SIDE} property will usually cause undosaves to be added to the {it current} {index LISPXHIST Var}{var LISPXHIST}, thereby enabling the effects of {fn UNDOLISPX1} to be undone. }} {FnDef {FnName PRINTHISTORY} {FnArgs HISTORY LINE SKIPFN NOVALUES FILE} {Text {arg LINE} is an event specification. {fn PRINTHISTORY} prints the events on {arg HISTORY} specified by {arg LINE}, e.g., {lisp (-1 THRU -10)}. Printing is performed via the function {fn SHOWPRIN2}{index SHOWPRIN2 FN}, so that if the value of {var SYSPRETTYFLG}={lisp T},{index SYSPRETTYFLG Var} events will be prettyprinted. {arg SKIPFN} is an (optional) functional argument that is applied to each event before printing. If it returns non-{lisp NIL}, the event is skipped, i.e., not printed. If {arg NOVALUES}={lisp T}, or {arg NOVALUES} applied to the corresponding event is true, the value is not printed. For example, {arg NOVALUES} is {lisp T} when printing events on {index EDITHISTORY Var}{var EDITHISTORY}. For example, the following {lisp LISPXMACRO} will define {lisp ??'} as a command for printing the history list while skipping all "large events" and not printing any values. {lispcode (??' (PRINTHISTORY LISPXHISTORY LISPXLINE (FUNCTION (LAMBDA (X) (IGREATERP (COUNT (CAR X)) 5))) T T))} }} }{End SubSec Programmer's Assistant Functions} {note lets leave this out and see if anyone notices -- it is such a crock} {Begin SubSec The Editor and the Programmer's Assistant} {Title The Editor and the Programmer's Assistant} {Text {Tag EditorAndPA} {index programmer's assistant and the editor} As mentioned earlier, all of the remarks concerning "the programmer's assistant" apply equally well to user interactions with {fn EVALQT}, {fn BREAK} or the editor. The differences between the editor's implementation of these features and that of {fn LISPX} are mostly obvious or inconsequential. However, for completeness, this section discusses the editor's implementation of the programmer's assistant. The editor uses {index PROMPTCHAR FN}{fn PROMPTCHAR} to print its prompt character, and {index LISPXREAD FN}{fn LISPXREAD}, {index LISPXREADP FN}{fn LISPXREADP}, and {index READLINE FN}{fn READLINE} for obtaining inputs. When the editor is given an input, it calls {index HISTORYSAVE FN}{fn HISTORYSAVE} to record the input in a new event on its history list, {index *PRIMARY* EDITHISTORY Var}{var EDITHISTORY}.{foot Except that the atomic commands {editcom OK}, {editcom STOP}, {editcom SAVE}, {editcom P}, {editcom ?}, {editcom PP} and {editcom E} are not recorded. In addition, number commands are grouped together in a single event. For example, {lisp 3 3 -1} is considered as one command for changing position. }{comment endfootnote} {var EDITHISTORY} follows the same conventions and format as {var LISPXHISTORY}. However, since edit commands have no value, the editor uses the value field for saving side effects, rather than storing them under the property {index SIDE (History List Property)}{lisp SIDE}. The editor recognizes and processes the four commands {index DO EditCom}{editcom DO}, {index !E EditCom}{editcom !E}, {index !F EditCom}{editcom !F}, and {index !N EditCom}{editcom !N} which refer to previous events on {var EDITHISTORY}. The editor also processes {pacom UNDO}{index UNDO EditCom}{index UNDO PACom} itself, as described below. All other history commands{foot as indicated by their appearance on {index HISTORYCOMS Var}{var HISTORYCOMS}, a list of the history commands. {index EDITDEFAULT FN}{fn EDITDEFAULT} interrogates {var HISTORYCOMS} before attempting spelling correction. (All of the commands on {var HISTORYCOMS} are also on {var EDITCOMSA} and {var EDITCOMSL} so that they can be corrected if misspelled in the editor.) Thus if the user defines a {lisp LISPXMACRO} and wishes it to operate in the editor as well, he need simply add it to {var HISTORYCOMS}. For example, {pacom RETRIEVE} is implemented as a {lisp LISPXMACRO} and works equally well in {fn LISPX} and the editor. }{comment endfootnote} are simply given to {index LISPX FN}{fn LISPX} for execution, after first binding (resetting) {index LISPXHISTORY Var}{var LISPXHISTORY} to {index EDITHISTORY Var}{var EDITHISTORY}. The editor also calls {fn LISPX} when given an {index E EditCom}{editcom E} command ({PageRef EditCom E}). In this case, the editor uses the fifth argument to {index LISPX FN}{fn LISPX}, {arg LISPXFLG}, to specify that any history commands are to be executed by a recursive call to {fn LISPX}, rather than by unreading. For example, if the user types {lisp E REDO} in the editor, he wants the last event on {index LISPXHISTORY Var}{var LISPXHISTORY} processed as {fn LISPX} input, and not to be unread and processed by the editor. The major implementation difference between the editor and {fn LISPX} occurs in undoing.{index undoing}{index undoing (in Editor)} {var EDITHISTORY} is a list of only the last {arg N} commands, where {arg N} is the value of the time-slice. However the editor provides for undoing {it all} changes made in a single editing session, even if that session consisted of more than {arg N} edit commands. Therefore, the editor saves undo information independently of the {var EDITHISTORY} on a list called {index UNDOLST Var}{var UNDOLST}, (although it also stores each entry on {var UNDOLST} in the field of the corresponding event on {var EDITHISTORY}.) Thus, the commands {editcom UNDO}, {editcom !UNDO}, and {editcom UNBLOCK}, are not dependent on {var EDITHISTORY}, and in fact will work if {var EDITHISTORY}={lisp NIL}, or even in a system which does not contain {fn LISPX} at all. For example, {editcom UNDO} specifies undoing the last command on {var UNDOLST}, even if that event no longer appears on {var EDITHISTORY}. The only interaction between {editcom UNDO} and the history list occurs when the user types {editcom UNDO} followed by an event specification. In this case, the editor calls {index LISPXFIND FN}{fn LISPXFIND} to find the event, and then undoes the corresponding entry on {index UNDOLST Var}{var UNDOLST}. Thus the user can only undo a {it specified} command within the scope of the {index EDITHISTORY Var}{var EDITHISTORY}. (Note that this is also the only way {editcom UNDO} commands themselves can be undone, that is, by using the history feature, to specify the corresponding event, e.g., {lisp UNDO UNDO}.) The implementation of the actual undoing is similar to the way it is done in {fn LISPX}: each command that makes a change in the structure being edited does so via a function that records the change on a variable. After the command has completed, this variable contains a list of all the pointers that have been changed and their original contents. Undoing that command simply involves mapping down that list and restoring the pointers. {Begin Note} Date: 2 JUL 1978 2228-PDT From: TEITELMAN dontsavehistorycoms is a list of comands not saved by edit history. initially SAVE P ? PP PP* and E. compacthistorycoms is a lit of commands to be compacted and is initially NIL. numbers ae automatically on compacthistorycoms. in Interlisp-D, dontsavehistorycoms initially (SAVE P ? PP PP* E ; EF EV EP) compacthistorycoms initially (2P NXP BKP -1P) {End Note} }{End SubSec The Editor and the Programmer's Assistant}