The Interlisp teletype editor allows rapid, convenient modification of list structures. Most often it is used to edit function definitions, (often while the function itself is running) via the function {index EDITF Fn}{fn EDITF}, e.g., {lisp EDITF(FOO)}. However, the editor can also be used to edit the value of a variable, via {index EDITV Fn}{fn EDITV}, to edit a property list, via {index EDITP Fn}{fn EDITP}, or to edit an arbitrary expression, via {index EDITE Fn}{fn EDITE}. It is an important feature which allows good on-line interaction in the Interlisp system. In Interlisp-D, most editing is done using the display editor DEdit ({PageRef Tag DEdit}), which is an extended, display-oriented version of the teletype editor. The teletype editor is still available, as it offers a facility for doing complex modifications of program structure under program control. For example, {fn BREAKIN} ({PageRef Fn BREAKIN}) calls the teletype editor to insert a function break within the body of a function. By calling the function {fn EDITMODE} ({PageRef Fn EDITMODE}) it is possible to set the "default editor" ({lisp TELETYPE} or {lisp DISPLAY}) called by Masterscope, the break package, etc. This chapter begins with a lengthy introduction intended for the new user. The reference portion begins on {PageRef Tag EditorReference}. {Begin SubSec Introduction} {Title Introduction} {Text Let us introduce some of the basic editor commands, and give a flavor for the editor's language structure by guiding the reader through a hypothetical editing session. Suppose we are editing the following incorrect definition of {fn APPEND}: {lispcode [LAMBDA (X) Y (COND ((NUL X) Z) (T (CONS (CAR) (APPEND (CDR X Y]} We call the editor via the function {fn EDITF}: {lispcode _EDITF(APPEND) EDIT *} The editor responds by typing {lisp EDIT} followed by {Index * (Printed by Editor)}{lisp *}, which is the editor's prompt character.{index prompt character} This signifies that the editor is ready to accept commands. In the examples in this chapter, all lines beginning with {lisp *} were typed by the user, the rest by the editor. At any given moment, the editor's attention is centered on some substructure of the expression being edited. This substructure is called the {it current expression},{Index *PRIMARY* current expression (in Editor)} and it is what the user sees when he gives the editor the command {index P EditCom}{editcom P}, for print. Initially, the current expression is the top level one, i.e., the entire expression being edited. Thus: {lispcode *P (LAMBDA (X) Y (COND & &)) *} Note that the editor prints the current expression as though printlevel ({PageRef Fn PRINTLEVEL}) were set to {lisp (2 . 20)}, i.e., sublists of sublists are printed as {Index & (Printed by Editor)}{lisp &}, tails of long lists printed as {lisp --}{Index -- (Printed by Editor)}. The command {editcom ?}{Index ? EditCom} will print the current expression as though printlevel were 1000. {lispcode *? (LAMBDA (X) Y (COND ((NUL X) Z) (T (CONS (CAR) (APPEND (CDR X Y)))))) *} and the command {Index PP EditCom}{editcom PP} will prettyprint the current expression.{Index current expression (in Editor)} A positive integer is interpreted by the editor as a command to descend into the correspondingly numbered element of the current expression. Thus: {lispcode *2 *P (X) *} A negative integer has a similar effect, but counting begins from the end of the current expression and proceeds backward, i.e., {lisp -1} refers to the last element in the current expression, {lisp -2} the next to the last, etc. For either positive integer or negative integer, if there is no such element, an error occurs. "Editor errors"{index errors in Editor} are not the same as Interlisp function errors, i.e., they never cause breaks or even go through the error machinery but are direct calls to {fn ERROR!} indicating that a command is in some way faulty. What happens next depends on the context in which the command was being executed. For example, there are conditional commands which branch on errors. In most situations, though, an error will cause the editor to type the faulty command followed by a {lisp ?} and wait for more input. Note that typing {index control-E}control-E while a command is being executed aborts the command exactly as though it had caused an error. {it The current expression is never changed when a command causes an error.} Thus: {lispcode *P (X) *2 2 ? *1 *P X *} {Begin Note} Date: 19 June 1982 4:57 am PDT (Saturday) From: JonL.PA Subject: A few EDITF/P problems If you ^D quit while an RC was happening, you can't undo -- message is "Nothing Saved". Are there other areas where the editor is ^D-unsafe? Date: 19 JUN 1982 2224-PDT From: MASINTER.PA ^D during edit. This is complex. I think I explained this and that it might even be explained in the manual. The edit operations aren't put on the top-level history until the edit is exited. If you ^D, you can undo specific commands from within the editor but not the entire event. {End Note} {it A phrase of the form "the current expression is changed" or "the current expression becomes" refers to a shift in the editor's attention, not to a modification of the structure being edited. } When the user changes the current expression by descending into it, the old current expression is not lost. Instead, the editor actually operates by maintaining a {it chain}{index *PRIMARY* edit chain} of expressions leading to the current one. The current expression{Index current expression (in Editor)} is simply the last link in the chain. Descending adds the indicated subexpression onto the end of the chain, thereby making it be the current expression. The command {Index 0 EditCom}{editcom 0} is used to ascend the chain; it removes the last link of the chain, thereby making the {it previous} link be the current expression. Thus: {lispcode *P X *0 P (X) *0 -1 P (COND (& Z) (T &)) *} Note the use of several commands on a single line in the previous output. The editor operates in a line buffered mode, the same as {fn EVALQT}. Thus no command is actually seen by the editor, or executed, until the line is terminated, either by a carriage return, or a matching right parenthesis. The user can thus use control-A and control-Q for line-editing edit commands, the same as he does for inputs to the Interlisp executive. In our editing session, we will make the following corrections to {fn APPEND}: delete {lisp Y} from where it appears, add {lisp Y} to the end of the argument list, change {lisp NUL} to {lisp NULL}, change {lisp Z} to {lisp Y}, add {lisp X} after {lisp CAR}, and insert a right parenthesis following {lisp CDR X}. First we will delete {lisp Y}. By now we have forgotten where we are in the function definition, but we want to be at the "top" so we use the command {Index ^ EditCom}{editcom ^}, which ascends through the entire chain of expressions to the top level expression, which then becomes the current expression, i.e., {editcom ^} removes all links except the first one. {lispcode *^ P (LAMBDA (X) Y (COND & &)) *} Note that if we are already at the top, {editcom ^} has no effect, i.e., it is a no-op. However, {Index 0 EditCom}{editcom 0} would generate an error.{Index CAN'T - AT TOP (Printed by Editor)} In other words, {editcom ^} means "go to the top," while {Index 0 EditCom}{editcom 0} means "ascend one link." The basic structure modification commands in the editor are: {Def {Type EditCom} {Name N} {PrintName {lisp ({arg N})} ({arg N}{ge}1)} {text Deletes the corresponding element from the current expression. }} {Def {Type EditCom} {Name N} {PrintName {lisp ({arg N} {arg E{sub 1}} {ellipsis} {arg E{sub M}})} ({arg N}{ge}1)} {text Replaces the {arg N}th element in the current expression with {arg E{sub 1}} {ellipsis} {arg E{sub M}}. }} {Def {Type EditCom} {Name N} {Args E{sub 1} {ellipsis} E{sub M}} {PrintName {lisp (-{arg N} {arg E{sub 1}} {ellipsis} {arg E{sub M}})} ({arg N}{ge}1)} {text Inserts {arg E{sub 1}} {ellipsis} {arg E{sub M}} before the {arg N}th element in the current expression. }} Thus: {lispcode *P (LAMBDA (X) Y (COND & &)) *(3) *(2 (X Y)) *P (LAMBDA (X Y) (COND & &)) *} {it All structure modification done by the editor is destructive, i.e., the editor uses {fn RPLACA} and {fn RPLACD} to physically change the structure it was given. } Note that all three of the above commands perform their operation with respect to the {arg N}th element from the front of the current expression; the sign of {arg N} is used to specify whether the operation is replacement or insertion. Thus, there is no way to specify deletion or replacement of the {arg N}th element from the end of the current expression, or insertion before the {arg N}th element from the end without counting out that element's position from the front of the list. Similarly, because we cannot specify insertion after a particular element, we cannot attach something at the end of the current expression using the above commands. Instead, we use the command {editcom N} (for {fn NCONC}). Thus we could have performed the above changes instead by: {lispcode *P (LAMBDA (X) Y (COND & &)) *(3) *2 (N Y) *P (X Y) *^ P *(LAMBDA (X Y) (COND & &)) *} Now we are ready to change {lisp NUL} to {lisp NULL}. Rather than specify the sequence of descent commands necessary to reach {lisp NUL}, and then replace it with {lisp NULL}, e.g., {lisp 3 2 1 (1 NULL)}, we will use {index F EditCom}{editcom F}, the find command, to find {lisp NUL}: {lispcode *P (LAMBDA (X Y) (COND & &)) *F NUL *P (NUL X) *(1 NULL) *0 P ((NULL X) Z) *} Note that {index F EditCom}{editcom F} is special in that it corresponds to {it two} inputs. In other words, {editcom F} says to the editor, "treat your {it next} command as an expression to be searched for." The search is carried out in printout order in the current expression. If the target expression is not found there, {editcom F} automatically ascends and searches those portions of the higher expressions that would appear after (in a printout) the current expression. If the search is successful, the new current expression will be the structure where the expression was found,{foot If the search is for an atom, e.g., {lisp F NUL}, the current expression will be the structure containing the atom. }{comment endfootnote} and the chain{index edit chain} will be the same as one resulting from the appropriate sequence of ascent and descent commands. If the search is not successful, an error occurs, and neither the current expression nor the chain is changed:{foot {editcom F} is never a no-op, i.e., if successful, the current expression after the search will never be the same as the current expression before the search. Thus {lisp F {arg EXPR}} repeated without intervening commands that change the edit chain can be used to find successive instances of {arg EXPR}. }{comment endfootnote} {lispcode *P ((NULL X) Z) *F COND P COND ? *P *((NULL X) Z) *} Here the search failed to find a {fn COND} following the current expression, although of course a {fn COND} does appear earlier in the structure. This last example illustrates another facet of the error recovery mechanism: to avoid further confusion when an error occurs, all commands on the line {it beyond} the one which caused the error (and all commands that may have been typed ahead while the editor was computing) are forgotten. We could also have used the {Index R EditCom}{editcom R} command (for {lisp R}eplace) to change {lisp NUL} to {lisp NULL}. A command of the form {lisp (R {arg E{sub 1}} {arg E{sub 2}})} will replace all occurrences of {arg E{sub 1}} in the current expression by {arg E{sub 2}}. There must be at least one such occurrence or the {editcom R} command will generate an error. Let us use the {editcom R} command to change all {lisp Z}'s (even though there is only one) in {fn APPEND} to {lisp Y}: {lispcode *^ (R Z Y) *F Z Z ? *PP [LAMBDA (X Y) (COND ((NULL X) Y) (T (CONS (CAR) (APPEND (CDR X Y] *} The next task is to change {lisp (CAR)} to {lisp (CAR X)}. We could do this by {lisp (R (CAR) (CAR X))}, or by: {lispcode *F CAR *(N X) *P (CAR X) *} The expression we now want to change is the next expression after the current expression,{Index current expression (in Editor)} i.e., we are currently looking at {lisp (CAR X)} in {lisp (CONS (CAR X) (APPEND (CDR X Y)))}. We could get to the {fn APPEND} expression by typing {editcom 0} and then {lisp 3} or {lisp -1}, or we can use the command {index NX EditCom}{editcom NX}, which does both operations: {lispcode *P (CAR X) *NX P (APPEND (CDR X Y)) *} Finally, to change {lisp (APPEND (CDR X Y))} to {lisp (APPEND (CDR X) Y)}, we could perform {lisp (2 (CDR X) Y)}, or {lisp (2 (CDR X))} and {lisp (N Y)}, or {lisp 2} and {lisp (3)}, deleting the {lisp Y}, and then {lisp 0 (N Y)}. However, if {lisp Y} were a complex expression, we would not want to have to retype it. Instead, we could use a command which effectively inserts and/or removes left and right parentheses. There are six of these commands: {editcom BI} ("Both In"), {editcom BO} ("Both Out"), {editcom LI} ("Left In"), {editcom LO} ("Left Out"), {editcom RI} ("Right In"), and {editcom RO} ("Right Out"). Of course, we will always have the same number of left parentheses as right parentheses, because the parentheses are just a notational guide to structure that is provided by our print program. Herein lies one of the principal advantages of a LISP oriented editor over a text editor: unbalanced parentheses errors are not possible. Thus, {editcom LI}, {editcom LO}, {editcom RI}, and {editcom RO} actually do not insert or remove just one parenthesis, but this is very suggestive of what actually happens. In this case, we would like a right parenthesis to appear following {lisp X} in {lisp (CDR X Y)}. Therefore, we use the command {lisp (RI 2 2)}, which means insert a right parentheses after the second element in the second element (of the current expression): {lispcode *P (APPEND (CDR X Y)) *(RI 2 2) *P (APPEND (CDR X) Y) *} We have now finished our editing, and can exit from the editor, to test {fn APPEND}, or we could test it while still inside of the editor, by using the {Index E EditCom}{editcom E} command: {lispcode *E APPEND((A B) (C D E)) (A B C D E) *} The {editcom E} command causes the next input to be evaluated by Interlisp. If there is another input following it, as in the above example, the first will be applied (with {fn APPLY}) to the second. Otherwise, the input is evaluated (with {fn EVAL}). We prettyprint {fn APPEND}, and leave the editor. {lispcode *PP [LAMBDA (X Y) (COND ((NULL X) Y) (T (CONS (CAR X) (APPEND (CDR X) Y] *OK APPEND _} }{End SubSec Introduction} {Begin SubSec Commands for the New User} {Title Commands for the New User} {Text As mentioned earlier, the Interlisp manual is intended primarily as a reference manual, and the remainder of this chapter is organized and presented accordingly. While the commands introduced in the previous scenario constitute a complete set, i.e., the user could perform any and all editing operations using just those commands, there are many situations in which knowing the right command(s) can save the user considerable effort. We include here as part of the introduction a list of those commands which are not only frequently applicable but also easy to use. They are not presented in any particular order, and are all discussed in detail in the reference portion of the chapter. {Def {Type EditCom} {Name UNDO} {text {Index undoing (in Editor)}Undoes the last modification to the structure being edited, e.g., if the user deletes the wrong element, {editcom UNDO} will restore it. The availability of {editcom UNDO} should give the user confidence to experiment with any and all editing commands, no matter how complex, because he can always reverse the effect of the command. }} {Def {Type EditCom} {Name BK} {text Like {editcom NX}, except makes the expression immediately {it before} the current expression become current. }} {Def {Type EditCom} {Name BF} {text {lisp B}ackwards {lisp F}ind. Like {editcom F}, except searches backwards, i.e., in inverse print order. }} {Def {Type EditCom} {Name \} {text Restores the current expression{Index current expression (in Editor)} to the expression before the last "big jump", e.g., a find command, an {editcom ^}, or another {editcom \}. For example, if the user types {lisp F COND}, and then {lisp F CAR}, {editcom \} would take him back to the {lisp COND}. Another {editcom \} would take him back to the {lisp CAR}. }} {Def {Type EditCom} {Name \P} {text Like {editcom \} except it restores the edit chain{index edit chain} to its state as of the last print, either by {editcom P}, {editcom ?}, or {editcom PP}. If the edit chain has not been changed since the last print, {editcom \P} restores it to its state as of the printing before that one, i.e., two chains are always saved. }} Thus if the user types {editcom P} followed by {lisp 3 2 1 P}, {editcom \P} will take him back to the first {editcom P}, i.e., would be equivalent to {lisp 0 0 0}. Another {editcom \P} would then take him back to the second {editcom P}. Thus the user can use {editcom \P} to flip back and forth between two current expressions. The search expression given to the {editcom F} or {editcom BF} command need not be a literal expression. Instead, it can be a pattern. The symbol {lisp &}{Index *PRIMARY* & (in Edit Pattern)} can be used anywhere within this pattern to match with any single {it element} of a list, and {lisp --}{Index *PRIMARY* -- (in Edit Pattern)} can be used to match with any {it segment} of a list. Thus, in the incorrect definition of {fn APPEND} used earlier, {lisp F (NUL &)} could have been used to find {lisp (NUL X)}, and {lisp F (CDR --)} or {lisp F (CDR & &)}, but not {lisp F (CDR &)}, to find {lisp (CDR X Y)}. Note that {lisp &} and {lisp --} can be nested arbitrarily deeply in the pattern. For example, if there are many places where the variable {lisp X} is set, {lisp F SETQ} may not find the desired expression, nor may {lisp F (SETQ X &)}. It may be necessary to use {lisp F (SETQ X (LIST --))}. However, the usual technique in such a case is to pick out a unique atom which occurs prior to the desired expression, and perform two {editcom F} commands. This "homing in" process seems to be more convenient than ultra-precise specification of the pattern. {lisp $} (){Index *PRIMARY* $ () (in Edit Pattern)} is equivalent to {lisp --} at the character level, e.g., {lisp VER$} will match with {lisp VERYLONGATOM}, as will {lisp $ATOM}, {lisp $LONG$}, (but not {lisp $LONG)} and {lisp $V$N$M$}. {lisp $} can be nested inside of a pattern, e.g., {lisp F (SETQ VER$ (CONS --))}. If the search is successful, the editor will print {lisp =}{Index = (Printed by Editor)} followed by the atom which matched with the {lisp $}-atom, e.g., {lispcode *F (SETQ VER$ &) =VERYLONGATOM *} Frequently the user will want to replace the entire current expression, or insert something before it. In order to do this using a command of the form {lisp ({arg N} {arg E{sub 1}} {ellipsis} {arg E{sub M}})} or {lisp (-{arg N} {arg E{sub 1}} {ellipsis} {arg E{sub M}})}, the user must be {it above} the current expression. In other words, he would have to perform a {editcom 0} followed by a command with the appropriate number. However, if he has reached the current expression via an {editcom F} command, he may not know what that number is. In this case, the user would like a command whose effect would be to modify the edit chain{index edit chain} so that the current expression{Index current expression (in Editor)} became the first element in a new, higher current expression. Then he could perform the desired operation via {lisp (1 {arg E{sub 1}} {ellipsis} {arg E{sub M}})} or {lisp (-1 {arg E{sub 1}} {ellipsis} {arg E{sub M}})}. {Index UP EditCom}{editcom UP} is provided for this purpose. {Def {Type EditCom} {Name UP} {text After {lisp UP} operates, the old current expression is the first element of the new current expression. Note that if the current expression happens to be the first element in the next higher expression, then {editcom UP} is exactly the same as {editcom 0}. Otherwise, {editcom UP} modifies the edit chain{index edit chain} so that the new current expression is a proper tail ({PageRef Fn TAILP}) of the next higher expression: {lispcode *F APPEND P (APPEND (CDR X) Y) *UP P {ellipsis} (APPEND & Y)) *0 P (CONS (CAR X) (APPEND & Y)) *} The {ellipsis}{Index ... (Printed by Editor)} is used by the editor to indicate that the current expression is a {it tail} of the next higher expression as opposed to being an element (i.e., a member) of the next higher expression. Note: if the current expression{Index current expression (in Editor)} is {it already} a tail, {editcom UP} has no effect. }} {Def {Type EditCom} {Name B} {Args E{sub 1} {ellipsis} E{sub M}} {text Inserts {arg E{sub 1}} {ellipsis} {arg E{sub M}} before the current expression, i.e., does an {editcom UP} and then a {lisp (-1 {arg E{sub 1}} {ellipsis} {arg E{sub M}})}. }} {Def {Type EditCom} {Name A} {Args E{sub 1} {ellipsis} E{sub M}} {text Inserts {arg E{sub 1}} {ellipsis} {arg E{sub M}} after the current expression, i.e., does an {editcom UP} and then either a {lisp (-2 {arg E{sub 1}} {ellipsis} {arg E{sub M}})} or an {lisp (N {arg E{sub 1}} {ellipsis} {arg E{sub M}})}, if the current expression is the last one in the next higher expression. }} {Def {Type EditCom} {Name :} {Args E{sub 1} {ellipsis} E{sub M}} {text Replaces the current expression{Index current expression (in Editor)} by {arg E{sub 1}} {ellipsis} {arg E{sub M}}, i.e., does an {editcom UP} and then a {lisp (1 {arg E{sub 1}} {ellipsis} {arg E{sub M}})}. }} {Def {Type EditCom} {name DELETE} {text Deletes the current expression; equivalent to {lisp (:)}. }} Earlier, we introduced the {editcom RI} command in the {fn APPEND} example. The rest of the commands in this family: {editcom BI}, {editcom BO}, {editcom LI}, {editcom LO}, and {editcom RO}, perform similar functions and are useful in certain situations. In addition, the commands {editcom MBD} and {editcom XTR} can be used to combine the effects of several commands of the {lisp BI-BO} family. {editcom MBD} ({PageRef EditCom MBD}) is used to embed the current expression in a larger expression. For example, if the current expression is {lisp (PRINT {arg bigexpression})}, and the user wants to replace it by {lisp (COND (FLG (PRINT {arg bigexpression})))}, he could accomplish this by {lisp (LI 1)}, {lisp (-1 FLG)}, {lisp (LI 1)}, and {lisp (-1 COND)}, or by a single {editcom MBD} command. {editcom XTR} ({PageRef EditCom XTR}) is used to e{lisp XTR}act an expression from the current expression. For example, extracting the {lisp PRINT} expression from the above {lisp COND} could be accomplished by {lisp (1)}, {lisp (LO 1)}, {lisp (1)}, and {lisp (LO 1)} or by a single {editcom XTR} command. The new user is encouraged to include {editcom XTR} and {editcom MBD} in his repertoire as soon as he is familiar with the more basic commands. }{End SubSec Commands for the New User}