{Begin SubSec Local Attention-Changing Commands} {Title Local Attention-Changing Commands} {Text {Tag EditorReference} {Index *BEGIN* current expression (in Editor)} {index *BEGIN* edit chain} This section describes commands that change the current expression (i.e., change the edit chain) thereby "shifting the editor's attention." These commands depend only on the {it structure} of the edit chain, as compared to the search commands (presented later), which search the contents of the structure. {index *PRIMARY* UP EditCom} {Def {Type EditCom} {name UP} {text {editcom UP} modifies the edit chain so that the old current expression (i.e., the one at the time {editcom UP} was called) is the first element in the new current expression. If the current expression is the first element in the next higher expression {editcom UP} simply does a {editcom 0}. Otherwise {editcom UP} adds the corresponding tail to the edit chain. If a {editcom P} command would cause the editor to type {lisp {ellipsis}} {Index ... (Printed by Editor)} before typing the current expression, ie., the current expression is a tail of the next higher expression, {editcom UP} has no effect. For Example: {lispcode *PP (COND ((NULL X) (RETURN Y))) *1 P COND *UP P (COND (& &)) *-1 P ((NULL X) (RETURN Y)) *UP P ... ((NULL X) (RETURN Y)) *UP P ... ((NULL X) (RETURN Y))) *F NULL P (NULL X) *UP P ((NULL X) (RETURN Y)) *UP P ... ((NULL X) (RETURN Y)))} }} The execution of {editcom UP} is straightforward, except in those cases where the current expression appears more than once in the next higher expression. For example, if the current expression is {lisp (A NIL B NIL C NIL)} and the user performs {lisp 4} followed by {editcom UP}, the current expression should then be {lisp ... NIL C NIL)}. {editcom UP} can determine which tail is the correct one because the commands that descend save the last tail on an internal editor variable, {index *PRIMARY* LASTAIL Var}{var LASTAIL}. Thus after the {lisp 4} command is executed, {var LASTAIL} is {lisp (NIL C NIL)}. When {editcom UP} is called, it first determines if the current expression is a tail of the next higher expression. If it is, {index UP EditCom}{editcom UP} is finished. Otherwise, {editcom UP} computes {lisp (MEMB {arg CURRENT-EXPRESSION} {arg NEXT-HIGHER-EXPRESSION})} to obtain a tail beginning with the current expression.{foot The current expression should {it always} be either a tail or an element of the next higher expression. If it is neither, for example the user has directly (and incorrectly) manipulated the edit chain, {editcom UP} generates an error. }{comment endfootnote} If there are no other instances of the current expression in the next higher expression, this tail is the correct one. Otherwise {editcom UP} uses {var LASTAIL} to select the correct tail.{foot Occasionally the user can get the edit chain into a state where {index LASTAIL Var}{var LASTAIL} cannot resolve the ambiguity, for example if there were two non-atomic structures in the same expression that were {fn EQ}, and the user descended more than one level into one of them and then tried to come back out using {editcom UP}. In this case, {editcom UP} prints {lisp LOCATION UNCERTAIN}{Index LOCATION UNCERTAIN (Printed by Editor)} and generates an error. Of course, we could have solved this problem completely in our implementation by saving at each descent {it both} elements and tails. However, this would be a costly solution to a situation that arises infrequently, and when it does, has no detrimental effects. The {var LASTAIL} solution is cheap and resolves 99% of the ambiguities. }{comment endfootnote} {Def {Type EditCom} {Name N} {PrintName {arg N} ({arg N}{ge}1)} {text Adds the {arg N}th element of the current expression to the front of the edit chain, thereby making it be the new current expression. Sets {var LASTAIL} for use by {editcom UP}. Generates an error if the current expression is not a list that contains at least {arg N} elements. }} {Def {Type EditCom} {name N} {PrintName {lisp -{arg N} ({arg N}{ge}1)}} {text Adds the {arg N}th element from the end of the current expression to the front of the edit chain, thereby making it be the new current expression. Sets {index LASTAIL Var}{var LASTAIL} for use by {editcom UP}. Generates an error if the current expression is not a list that contains at least {arg N} elements. }} {Def {Type EditCom} {name 0} {text Sets the edit chain to {fn CDR} of the edit chain, thereby making the next higher expression be the new current expression. Generates an error if there is no higher expression, i.e., {fn CDR} of edit chain is {lisp NIL}.{Index *PRIMARY* CAN'T - AT TOP (Printed by Editor)} }} Note that {editcom 0} usually corresponds to going back to the next higher left parenthesis, but not always. For example: {lispcode *P (A B C D E F B) *3 UP P ... C D E F G) *3 UP P ... E F G) *0 P ... C D E F G)} If the intention is to go back to the next higher left parenthesis, regardless of any intervening tails, the command {editcom !0} can be used. {Def {Type EditCom} {name !0} {text Does repeated {editcom 0}'s until it reaches a point where the current expression is {it not} a tail of the next higher expression, i.e., always goes back to the next higher left parenthesis. }} {Def {Type EditCom} {name ↑} {text Sets the edit chain to {fn LAST} of edit chain, thereby making the top level expression be the current expression. Never generates an error. }} {index *PRIMARY* NX EditCom} {Def {Type EditCom} {name NX} {text Effectively does an {editcom UP} followed by a {lisp 2}, thereby making the current expression be the next expression. Generates an error if the current expression is the last one in a list. (However, {editcom !NX} described below will handle this case.) }} {index *PRIMARY* BK EditCom} {Def {Type EditCom} {name BK} {text Makes the current expression be the previous expression in the next higher expression. Generates an error if the current expression is the first expression in a list. For example, {lispcode *PP (COND ((NULL X) (RETURN Y))) *F RETURN P (RETURN Y) *BK P (NULL X)} }} Both {editcom NX} and {editcom BK} operate by performing a {editcom !0} followed by an appropriate number, i.e., there won't be an extra tail above the new current expression, as there would be if {editcom NX} operated by performing an {editcom UP} followed by a {lisp 2}. {Def {Type EditCom} {name NX} {Args N} {text ({arg N} {ge} 1) Equivalent to {arg N} {editcom NX} commands, except if an error occurs, the edit chain is not changed. }} {Def {Type EditCom} {name BK} {Args N} {text ({arg N} {ge} 1) Equivalent to {arg N} {lisp BK} commands, except if an error occurs, the edit chain is not changed. }} Note: {lisp (NX -{arg N})} is equivalent to {lisp (BK {arg N})}, and vice versa. {Def {Type EditCom} {name !NX} {text Makes the current expression be the next expression at a higher level, i.e., goes through any number of right parentheses to get to the next expression. For example: {lispcode *PP (PROG ((L L) (UF L)) LP (COND ((NULL (SETQ L (CDR L))) (ERROR!)) ([NULL (CDR (FMEMB (CAR L) (CADR L] (GO LP))) (EDITCOM (QUOTE NX)) (SETQ UNFIND UF) (RETURN L)) *F CDR P (CDR L) *NX NX ? *!NX P (ERROR!) *!NX P ((NULL &) (GO LP)) *!NX P (EDITCOM (QUOTE NX)) *} }} {editcom !NX} operates by doing {editcom 0}'s until it reaches a stage where the current expression is {it not} the last expression in the next higher expression, and then does a {editcom NX}. Thus {editcom !NX}{Index !NX EditCom} always goes through at least one unmatched right parenthesis, and the new current expression is always on a different level, i.e., {editcom !NX} and {editcom NX} always produce different results. For example using the previous current expression: {lispcode *F CAR P (CAR L) *!NX P (GO LP) *\P P (CAR L) *NX P (CADR L) *} {Def {Type EditCom} {Name NTH} {Args N} {text ({arg N} {ne} 0) Equivalent to {arg N} followed by {editcom UP}, i.e., causes the list starting with the {arg N}th element of the current expression (or {arg N}th from the end if {arg N} < 0) to become the current expression. Causes an error if current expression does not have at least {arg N} elements. {lisp (NTH 1)} is a no-op, as is {lisp (NTH -{arg L})} where {arg L} is the length of the current expression. }} {Def {Type EditCom} {name line-feed} {PrintName line-feed} {text Moves to the "next" expression and prints it, i.e. performs a {editcom NX} if possible, otherwise performs a {editcom !NX}. (The latter case is indcated by first printing "{lisp >}".) }} {Def {Type EditCom} {name control-X} {PrintName control-X} {text Control-X{foot Control-A in Interlisp on TOPS-20.{IndexX {Name Control-A} {Type EditCom} {Text control-A (TOPS-20)} } }{comment endfootnote} moves to the "previous" thing and then prints it, i.e. performs a {editcom BK} if possible, otherwise a {editcom !0} followed by a {editcom BK}. }} {Def {Type EditCom} {name control-Z} {PrintName control-Z} {text Control-Z{foot Control-L in Interlisp on TOPS-20.{IndexX {Name Control-L} {Type EditCom} {Text control-L (TOPS-20)} } }{comment endfootnote} moves to the last expression and prints it, i.e. does {lisp -1} followed by {editcom P}. }} Line-feed, control-X, and control-Z are implemented as {it immediate} read macros; as soon as they are read, they abort the current printout. They thus provide a convenient way of moving around in the editor. In order to facilitate using different control characters for those macros, the function {index SETTERMCHARS Fn}{fn SETTERMCHARS} is provided (see {PageRef Fn SETTERMCHARS}). {Index *END* current expression (in Editor)} {index *END* edit chain} }{End SubSec Local Attention-Changing Commands} {begin subsec Commands That Search} {title Commands That Search} {Text {index *BEGIN* edit commands that search} {index *BEGIN* pattern match (in Editor)} {index *PRIMARY* pattern match (in Editor)} All of the editor commands that search use the same pattern matching routine (the function {fn EDIT4E}, {PageRef Fn EDIT4E}). We will therefore begin our discussion of searching by describing the pattern match mechanism. A pattern {arg PAT} matches with {arg X} if any of the following conditions are true: {Tag EditPattern} {Begin LabeledList EditPattern} {Indent 10percent} {Label (1)} {text If {arg PAT} is {fn EQ} to {arg X}. } {Label (2)} {text If {arg PAT} is {lisp &}.{index & (in Edit Pattern)} } {Label (3)} {text If {arg PAT} is a number and {fn EQP} to {arg X}. } {Label (4)} {text If {arg PAT} is a string and {lisp (STREQUAL {arg PAT} {arg X})} is true. } {Label (5)} {text If {lisp (CAR {arg PAT})} is the atom {index *ANY* (in Edit Pattern)}{lisp *ANY*}, {lisp (CDR {arg PAT})} is a list of patterns, and one of the patterns on {lisp (CDR {arg PAT})} matches {arg X}. } {Label (6)} {text If {arg PAT} is a literal atom or string containing one or more {lisp $}s (<esc>s),{index $ (<esc>) (in Edit Pattern)} each {lisp $} can match an indefinite number (including 0) of contiguous characters in the atom or string {arg X}, e.g., {lisp VER$} matches both {lisp VERYLONGATOM} and {lisp "VERYLONGSTRING"} as do {lisp $LONG$} (but not {lisp $LONG)}, and {lisp $V$L$T$}. Note: the atom {lisp $} (<esc>) matches only with itself. } {Label (7)} {text If {arg PAT} is a literal atom or string ending in {it two} <esc>s,{index $$ (two <esc>s) (in Edit Pattern)} {arg PAT} matches with the atom or string {arg X} if it is "close" to {arg PAT}, in the sense used by the spelling corrector ({PageRef Tag SpellingCorrector}). E.g. {lisp CONSS$$} matches with {lisp CONS}, {lisp CNONC$$} with {lisp NCONC} or {lisp NCONC1}. } {UnIndent The pattern matching routine always types a message of the form {lisp ={arg MATCHING-ITEM}} to inform the user of the object matched by a pattern of the above two types, unless {index EDITQUIETFLG Var}{var EDITQUIETFLG}={lisp T}. For example, if {lisp VER$} matches {lisp VERYLONGATOM}, the editor would print {lisp =VERYLONGATOM}. } {Label (8)} {text If {lisp (CAR {arg PAT})} is the atom {lisp --},{index -- (in Edit Pattern)} {arg PAT} matches {arg X} if {lisp (CDR {arg PAT})} matches with some tail of {arg X}. For example, {lisp (A -- (&))} will match with {lisp (A B C (D))}, but not {lisp (A B C D)}, or {lisp (A B C (D) E)}. However, note that {lisp (A -- (&) --)} will match with {lisp (A B C (D) E)}. In other words, {lisp --} can match any interior segment of a list. If {lisp (CDR {arg PAT})}= {lisp NIL}, i.e., {arg PAT}={lisp (--)}, then it matches any tail of a list. Therefore, {lisp (A --)} matches {lisp (A)}, {lisp (A B C)} and {lisp (A . B)}. } {Label (9)} {text If {lisp (CAR {arg PAT})} is the atom {lisp ==},{index == (in Edit Pattern)} {arg PAT} matches {arg X} if and only if {lisp (CDR {arg PAT})} is {fn EQ} to {arg X}. This pattern is for use by programs that call the editor as a subroutine, since any non-atomic expression in a command {it typed} in by the user obviously cannot be {fn EQ} to already existing structure. } {Label (10)} {text If {lisp (CADR {arg PAT})} is the atom {lisp ..} (two periods),{index .. (in Edit Pattern)} {arg PAT} matches {arg X} if {lisp (CAR {arg PAT})} matches {lisp (CAR {arg X})} and {lisp (CDDR {arg PAT})} is contained in {arg X}, as described on {PageRef EditCom ..}. } {Label (11)} {text Otherwise if {arg X} is a list, {arg PAT} matches {arg X} if {lisp (CAR {arg PAT})} matches {lisp (CAR {arg X})}, and {lisp (CDR {arg PAT})} matches {lisp (CDR {arg X})}. } {End LabeledList EditPattern} {Begin Note} Date: 14 NOV 1979 1546-PST From: TEITELMAN Subject: @ IN EDIT PATTERN now installed. shall i reload tonight? Date: 21 NOV 1979 2154-PST From: TEITELMAN To: KAPLAN by the way, is sufficient to use (& .. FOO) as a pattern, you dont have to do (*any* FOO (& .. FOO)). {End Note} When the editor is searching, the pattern matching routine is called to match with {it elements} in the structure, unless the pattern begins with {lisp ...} (three periods),{index ... (in Edit Pattern)} in which case {fn CDR} of the pattern is matched against proper tails in the structure. Thus, {lispcode *P (A B C (B C)) *F (B --) *P (B C) *0 F (... B --) *P ... B C (B C))} Matching is also attempted with atomic tails (except for {lisp NIL}). Thus, {lispcode *P (A (B . C)) *F C *P ... . C)} Although the current expression is the atom {lisp C} after the final command, it is printed as {lisp ... . C)} to alert the user to the fact that {lisp C} is a {it tail}, not an element. Note that the pattern {lisp C} will match with either instance of {lisp C} in {lisp (A C (B . C))}, whereas {lisp (... . C)} will match only the second {lisp C}. The pattern {lisp NIL} will only match with {lisp NIL} as an element, i.e., it will not match in {lisp (A B)}, even though {fn CDDR} of {lisp (A B)} is {lisp NIL}. However, {lisp (... . NIL)} (or equivalently {lisp (...)}) may be used to specify a {lisp NIL} {it tail}, e.g., {lisp (... . NIL)} will match with {fn CDR} of the third subexpression of {lisp ((A . B) (C . D) (E))}. {index *END* pattern match (in Editor)} {Begin subsec Search Algorithm} {title Search Algorithm} {Text {Tag EditorSearchAlgorithm} {index *BEGIN* search algorithm (in Editor)} {index *BEGIN* edit chain} {index *BEGIN* current expression (in Editor)} Searching begins with the current expression and proceeds in print order. Searching usually means find the next instance of this pattern, and consequently a match is not attempted that would leave the edit chain unchanged. At each step, the pattern is matched against the next element in the expression currently being searched, unless the pattern begins with {index ... (in Edit Pattern)}{lisp ...} (three periods) in which case it is matched against the next tail of the expression. If the match is not successful, the search operation is recursive first in the {fn CAR} direction, and then in the {fn CDR} direction, i.e., if the element under examination is a list, the search descends into that list before attempting to match with other elements (or tails) at the same level. Note: A find command of the form {lisp (F {arg PATTERN} NIL)} will only attempts matches at the top level of the current expression, i.e., it does not descend into elements, or ascend to higher expressions. However, at no point is the total recursive depth of the search (sum of number of {fn CAR}s and {fn CDR}s descended into) allowed to exceed the value of the variable {index *PRIMARY* MAXLEVEL Var}{var MAXLEVEL}. At that point, the search of that element or tail is abandoned, exactly as though the element or tail had been completely searched without finding a match, and the search continues with the element or tail for which the recursive depth is below {var MAXLEVEL}. This feature is designed to enable the user to search circular list structures (by setting {var MAXLEVEL} small), as well as protecting him from accidentally encountering a circular list structure in the course of normal editing. {var MAXLEVEL} can also be set to {lisp NIL}, which is equivalent to infinity. {var MAXLEVEL} is initially set to 300. If a successful match is not found in the current expression, the search automatically ascends to the next higher expression,{note strange footnote here (doesn't make any sense) See footnote 21.} and continues searching there on the next expression after the expression it just finished searching. If there is none, it ascends again, etc. This process continues until the entire edit chain has been searched, at which point the search fails, and an error is generated. If the search fails (or is aborted by control-E), the edit chain is not changed (nor are any {fn CONS}es performed). If the search is successful, i.e., an expression is found that the pattern matches, the edit chain is set to the value it would have had had the user reached that expression via a sequence of integer commands. If the expression that matched was a list, it will be the final link in the edit chain, i.e., the new current expression. If the expression that matched is not a list, e.g., is an atom, the current expression will be the tail beginning with that atom, unless the atom is a tail, e.g., {lisp B} in {lisp (A . B)}. In this case, the current expression will be {lisp B}, but will print as {lisp ... . B)}. In other words, the search effectively does an {lisp UP}.{foot Unless {index UPFINDFLG Var}{var UPFINDFLG}={lisp NIL} (initially set to {lisp T}). For discussion, see "Form Oriented Editing", {PageRef Tag FormOrientedEditing}. }{comment endfootnote} {index *END* search algorithm (in Editor)} }{End subsec Search Algorithm} {Begin subsec Search Commands} {title Search Commands} {Text All of the commands below set {index LASTAIL Var}{var LASTAIL} for use by {index UP EditCom}{lisp UP}, set {index UNFIND Var}{var UNFIND} for use by {editcom \} ({PageRef EditCom \}), and do not change the edit chain or perform any {fn CONS}es if they are unsuccessful or aborted. {index *PRIMARY* F EditCom} {Def {Type EditCom} {Name F {arg PATTERN}} {text Actually two commands: the {editcom F} informs the editor that the {it next} command is to be interpreted as a pattern. This is the most common and useful form of the find command. If successful, the edit chain always changes, i.e., {editcom F {arg PATTERN}} means find the next instance of {arg PATTERN}. If {lisp (MEMB {arg PATTERN} {arg CURRENT-EXPRESSION})} is true, {editcom F} does not proceed with a full recursive search. If the value of the {fn MEMB} is {lisp NIL}, {editcom F} invokes the search algorithm described on {PageRef Tag EditorSearchAlgorithm}. }} Note that if the current expression is {lisp (PROG NIL LP (COND (-- (GO LP1))) {ellipsis} LP1 {ellipsis})}, then {lisp F LP1} will find the {lisp PROG} label, not the {lisp LP1} inside of the {lisp GO} expression, even though the latter appears first (in print order) in the current expression. Note that typing {lisp 1} (making the atom {lisp PROG} be the current expression) followed by {lisp F LP1} {it would} find the first {lisp LP1}. {Def {Type EditCom} {Name F} {Args PATTERN} {PrintName {lisp F {arg PATTERN} N}} {text Same as {editcom F {arg PATTERN}}, i.e., {lisp F}inds the {lisp N}ext instance of {arg PATTERN}, except that the {fn MEMB} check of {editcom F {arg PATTERN}} is not performed. }} {Def {Type EditCom} {name F} {Args PATTERN} {PrintName {lisp F {arg PATTERN} T}} {text Similar to {editcom F {arg PATTERN}}, except that it may succeed without changing the edit chain, and it does not perform the {fn MEMB} check. For example, if the current expression is {lisp (COND {ellipsis})}, {lisp F COND} will look for the next {lisp COND}, but {lisp (F COND T)} will "stay here". }} {Def {Type EditCom} {name F} {Args PATTERN N} {text ({arg N} {ge} 1) Finds the {arg N}th place that {arg PATTERN} matches. Equivalent to {lisp (F {arg PATTERN} T)} followed by {lisp (F {arg PATTERN} N)} repeated {arg N}-1 times. Each time {arg PATTERN} successfully matches, {arg N} is decremented by 1, and the search continues, until {arg N} reaches 0. Note that {arg PATTERN} does not have to match with {arg N} identical expressions; it just has to match {arg N} times. Thus if the current expression is {lisp (FOO1 FOO2 FOO3)}, {lisp (F FOO$ 3)} will find {lisp FOO3}. If {arg PATTERN} does not match successfully {arg N} times, an error is generated and the edit chain is unchanged (even if {arg PATTERN} matched {arg N}-1 times). }} {Def {Type EditCom} {name F} {Args PATTERN} } {Def {Type EditCom} {name F} {Args PATTERN} {PrintName {lisp F {arg PATTERN} NIL}} {text Similar to {editcom F {arg PATTERN}}, except that it only matches with elements at the top level of the current expression, i.e., the search will not descend into the current expression, nor will it go outside of the current expression. May succeed without changing the edit chain. }} For example, if the current expression is {lisp (PROG NIL (SETQ X (COND & &)) (COND &) ...)}, the command {lisp F COND} will find the {lisp COND} inside the {lisp SETQ}, whereas {lisp (F (COND --))} will find the top level {lisp COND}, i.e., the second one. {Def {Type EditCom} {name FS} {Args PATTERN{sub 1} {ellipsis} PATTERN{sub N}} {text Equivalent to {lisp F {arg PATTERN{sub 1}}} followed by {lisp F {arg PATTERN{sub 2}}} {ellipsis} followed by {lisp F {arg PATTERN{sub N}}}, so that if {lisp F {arg PATTERN{sub M}}} fails, the edit chain is left at the place {arg PATTERN{sub M-1}} matched. }} {Def {Type EditCom} {name F=} {Args EXPRESSION X} {text Equivalent to {lisp (F (== . {arg EXPRESSION}) {arg X})}, i.e., searches for a structure {fn EQ} to {arg EXPRESSION} (see {PageRef Tag EditPattern}). }} {Def {Type EditCom} {name ORF} {Args PATTERN{sub 1} {ellipsis} PATTERN{sub N}} {text Equivalent to {lisp (F (*ANY*{arg PATTERN{sub 1}} {ellipsis} {arg PATTERN{sub N}}) N)}, i.e., searches for an expression that is matched by either {arg PATTERN{sub 1}}, {arg PATTERN{sub 2}}, {ellipsis} or {arg PATTERN{sub N}} (see {PageRef Tag EditPattern}). }} {index *PRIMARY* BF EditCom} {Def {Type EditCom} {Name BF} {Args PATTERN} {Noparens} {text "Backwards Find". Searches in reverse print order, beginning with the expression immediately before the current expression (unless the current expression is the top level expression, in which case {editcom BF} searches the entire expression, in reverse order). {editcom BF} uses the same pattern match routine as {editcom F}, and {index MAXLEVEL Var}{var MAXLEVEL} and {index UPFINDFLG Var}{var UPFINDFLG} have the same effect, but the searching begins at the {it end} of each list, and descends into each element before attempting to match that element. If unsuccessful, the search continues with the next previous element, etc., until the front of the list is reached, at which point {editcom BF} ascends and backs up, etc. }} For example, if the current expression is {lisp (PROG NIL (SETQ X (SETQ Y (LIST Z))) (COND ((SETQ W --) --)) --)}, the command {lisp F LIST} followed by {lisp BF SETQ} will leave the current expression as {lisp (SETQ Y (LIST Z))}, as will {lisp F COND} followed by {lisp BF SETQ}. {Def {Type EditCom} {name BF} {Args PATTERN} {PrintName {lisp BF {arg PATTERN} T}} {text Similar to {lisp BF {arg PATTERN}}, except that the search always includes the current expression, i.e., starts at the end of current expression and works backward, then ascends and backs up, etc. }} Thus in the previous example, where {lisp F COND} followed by {lisp BF SETQ} found {lisp (SETQ Y (LIST Z))}, {lisp F COND} followed by {lisp (BF SETQ T)} would find the {lisp (SETQ W --)} expression. {Def {Type EditCom} {name BF} {Args PATTERN} } {Def {Type EditCom} {name BF} {Args PATTERN} {PrintName {lisp BF {arg PATTERN} NIL}} {text Same as {lisp BF} {arg PATTERN}. }} {Def {Type EditCom} {name GO} {Args LABEL} {text Makes the current expression be the first thing after the {fn PROG} label {arg LABEL}, i.e. goes where an executed {fn GO} would go. }} }{End subsec Search Commands} {Begin subsec Location Specification} {title Location Specification} {Text {Tag EditorLocationSpecification} {index *PRIMARY* location specification (in Editor)} Many of the more sophisticated commands described later in this chapter use a more general method of specifying position called a {term location specification}. A {term location specification} is a list of edit commands that are executed in the normal fashion with two exceptions. First, all commands not recognized by the editor are interpreted as though they had been preceded by {lisp F}; normally such commands would cause errors. For example, the location specification {lisp (COND 2 3)} specifies the 3rd element in the first clause of the next {lisp COND}.{foot Note that the user could always write {lisp F COND} followed by 2 and 3 for {lisp (COND 2 3)} if he were not sure whether or not {lisp COND} was the name of an atomic command. }{comment endfootnote} Secondly, if an error occurs while evaluating one of the commands in the location specification,{index location specification (in Editor)} and the edit chain had been changed, i.e., was not the same as it was at the beginning of that execution of the location specification, the location operation will continue. In other words, the location operation keeps going unless it reaches a state where it detects that it is "looping", at which point it gives up. Thus, if {lisp (COND 2 3)} is being located, and the first clause of the next {lisp COND} contained only two elements, the execution of the command 3 would cause an error. The search would then continue by looking for the next {lisp COND}. However, if a point were reached where there were no further {lisp COND}s, then the first command, {lisp COND}, would cause the error; the edit chain would not have been changed, and so the entire location operation would fail, and cause an error. The {editcom IF} command ({PageRef EditCom IF}) in conjunction with the {index ## FN}{fn ##} function ({PageRef Fn ##}) provide a way of using arbitrary predicates applied to elements in the current expression. {editcom IF} and {fn ##} will be described in detail later in the chapter, along with examples illustrating their use in location specifications. Throughout this chapter, the meta-symbol {lisp @}{index @ (location specification) (in Editor)} is used to denote a location specification.{index location specification (in Editor)} Thus {lisp @} is a list of commands interpreted as described above. {lisp @} can also be atomic, in which case it is interpreted as {lisp (LIST @)}. {Def {Type EditCom} {name LC} {PrintName {lisp (LC . @)}} {text Provides a way of explicitly invoking the location operation, e.g., {lisp (LC COND 2 3)} will perform the the search described above. }} {Def {Type EditCom} {name LCL} {PrintName {lisp (LCL . @)}} {text Same as {editcom LC} except the search is confined to the current expression, i.e., the edit chain is rebound during the search so that it looks as though the editor were called on just the current expression. For example, to find a {lisp COND} containing a {lisp RETURN}, one might use the location specification {lisp (COND (LCL RETURN) \)} where the {editcom \} would reverse the effects of the {editcom LCL} command, and make the final current expression be the {lisp COND}. }} {Def {Type EditCom} {Name 2ND} {PrintName {lisp (2ND . @)}} {text Same as {lisp (LC . @)} followed by another {lisp (LC . @)} except that if the first succeeds and second fails, no change is made to the edit chain. }} {Def {Type EditCom} {Name 3ND} {PrintName {lisp (3ND . @)}} {text Similar to {editcom 2ND}. }} {index *PRIMARY* ← EditCom} {Def {Type EditCom} {name ←} {Args PATTERN} {text Ascends the edit chain looking for a link which matches {arg PATTERN}. In other words, it keeps doing {editcom 0}'s until it gets to a specified point. If {arg PATTERN} is atomic, it is matched with the first element of each link, otherwise with the entire link. If no match is found, an error is generated, and the edit chain is unchanged. Note: If {arg PATTERN} is of the form {lisp (IF {arg EXPRESSION})}, {arg EXPRESSION} is evaluated at each link, and if its value is {lisp NIL}, or the evaluation causes an error, the ascent continues. See {PageRef EditCom IF}. }} For example: {lispcode *PP [PROG NIL (COND [(NULL (SETQ L (CDR L))) (COND (FLG (RETURN L] ([NULL (CDR (FMEMB (CAR L) (CADR L]] *F CADR *(← COND) *P (COND (& &) (& &)) *} Note that this command differs from {editcom BF} in that it does not search {it inside} of each link, it simply ascends. Thus in the above example, {lisp F CADR} followed by {lisp BF COND} would find {lisp (COND (FLG (RETURN L)))}, not the higher {lisp COND}. {index *PRIMARY* BELOW EditCom} {Def {Type EditCom} {name BELOW} {Args COM X} {text Ascends the edit chain looking for a link specified by {arg COM}, and stops {arg X} links below that (only links that are elements are counted, not tails). In other words {editcom BELOW} keeps doing {editcom 0}'s until it gets to a specified point, and then backs off {arg X} {editcom 0}'s. Note that {arg X} is evaluated, so one can type {lisp (BELOW {arg COM} (IPLUS X Y))}. }} {Def {Type EditCom} {name BELOW} {Args COM} {text Same as {lisp (BELOW {arg COM} 1)}. }} For example, {lisp (BELOW COND)} will cause the {fn COND} {it clause} containing the current expression to become the new current expression. Thus if the current expression is as shown above, {lisp F CADR} followed by {lisp (BELOW COND)} will make the new expression be {lisp ([NULL (CDR (FMEMB (CAR L) (CADR L] (GO LP))}, and is therefore equivalent to {lisp 0 0 0 0}. The {editcom BELOW} command is useful for locating a substructure by specifying something it contains. For example, suppose the user is editing a list of lists, and wants to find a sublist that contains a {lisp FOO} (at any depth). He simply executes {lisp F FOO (BELOW \)}. {index *PRIMARY* NEX EditCom} {Def {Type EditCom} {Name NEX} {Args COM} {text Same as {lisp (BELOW {arg COM})} followed by {editcom NX}. }} For example, if the user is deep inside of a {fn SELECTQ} clause, he can advance to the next clause with {lisp (NEX SELECTQ)}. {Def {Type EditCom} {Name NEX} {text Same as {lisp (NEX ←)}. }} The atomic form of {editcom NEX} is useful if the user will be performing repeated executions of {lisp (NEX {arg COM})}. By simply {editcom MARK}ing (see {PageRef EditCom MARK}) the chain corresponding to {arg COM}, he can use {editcom NEX} to step through the sublists. {index *PRIMARY* NTH EditCom} {Def {Type EditCom} {Name NTH} {Args COM} {text Generalized {editcom NTH} command. Effectively performs {lisp (LCL . {arg COM})}, followed by {lisp (BELOW \)}, followed by {editcom UP}. If the search is unsuccessful, {editcom NTH} generates an error and the edit chain is not changed. Note that {lisp (NTH {arg NUMBER})} is just a special case of {lisp (NTH {arg COM})}, and in fact, no special check is made for {arg COM} a number; both commands are executed identically. }} In other words, {editcom NTH} locates {arg COM}, using a search restricted to the current expression, and then backs up to the current level, where the new current expression is the tail whose first element contains, however deeply, the expression that was the terminus of the location operation. For example: {lispcode *P (PROG (& &) LP (COND & &) (EDITCOM &) (SETQ UNFIND UF) (RETURN L)) *(NTH UF) *P ... (SETQ UNFIND UF) (RETURN L)) *} {Def {Type EditCom} {name ..} {PrintName {lisp {arg PATTERN} .. @} } {text E.g., {lisp (COND .. RETURN)}. Finds a {fn COND} that contains a {fn RETURN}, at any depth. Equivalent to (but more efficient than) {lisp (F {arg PATTERN} N)}, {lisp (LCL . @)} followed by {lisp (← {arg PATTERN})}. An infix command, "{lisp ..}" is not a meta-symbol, it {it is} the name of the command. {lisp @} is {fn CDDR} of the command. Note that {lisp ({arg PATTERN} .. @)} can also be used directly as an edit pattern as described on {PageRef Tag EditPattern}, e.g. {lisp F ({arg PATTERN} .. @)}. }} For example, if the current expression is {lisp (PROG NIL [COND ((NULL L) (COND (FLG (RETURN L] --)}, then {lisp (COND .. RETURN)} will make {lisp (COND (FLG (RETURN L)))} be the current expression. Note that it is the innermost {lisp COND} that is found, because this is the first {lisp COND} encountered when ascending from the {lisp RETURN}. In other words, {lisp ({arg PATTERN} .. @)} is not {it always} equivalent to {lisp (F {arg PATTERN} N)}, followed by {lisp (LCL . @)} followed by {lisp \}. Note that {lisp @} is a location specification, not just a pattern. Thus {lisp (RETURN .. COND 2 3)} can be used to find the {lisp RETURN} which contains a {lisp COND} whose first clause contains (at least) three elements. Note also that since {lisp @} permits any edit command, the user can write commands of the form {lisp (COND .. (RETURN .. COND))}, which will locate the first {lisp COND} that contains a {lisp RETURN} that contains a {lisp COND}. }{End subsec Location Specification} {index *END* edit commands that search} }{End subsec Commands That Search} {begin subsec Commands That Save and Restore the Edit Chain} {title Commands That Save and Restore the Edit Chain} {Text Several facilities are available for saving the current edit chain and later retrieving it: {editcom MARK}, which marks the current chain for future reference, {editcom ←}, which returns to the last mark without destroying it, and {editcom ←←}, which returns to the last mark and also erases it. {Def {Type EditCom} {name MARK} {Text Adds the current edit chain to the front of the list {var MARKLST}.{index *PRIMARY* MARKLST Var} }} {Def {Type EditCom} {name ←} {Text Makes the new edit chain be {lisp (CAR MARKLST)}. Generates an error if {var MARKLST} is {lisp NIL}, i.e., no {lisp MARK}s have been performed, or all have been erased. This is an atomic command; do not confuse it with the list command {lisp (← {arg PATTERN})}. }} {Def {Type EditCom} {name ←←} {Text Similar to {editcom ←} but also erases the last {lisp MARK}, i.e., performs {lisp (SETQ MARKLST (CDR MARKLST))}. }} Note that if the user has two chains marked, and wishes to return to the first chain, he must perform {editcom ←←}, which removes the second mark, and then {editcom ←}. However, the second mark is then no longer accessible. If the user wants to be able to return to either of two (or more) chains, he can use the following generalized {lisp MARK}: {index *PRIMARY* MARK EditCom} {Def {Type EditCom} {name MARK} {Args LITATOM} {Text Sets {arg LITATOM} to the current edit chain, }} {index *PRIMARY* \ EditCom} {Def {Type EditCom} {name \} {Args LITATOM} {Text Makes the current edit chain become the value of {arg LITATOM}. }} If the user did not prepare in advance for returning to a particular edit chain, he may still be able to return to that chain with a single command by using {editcom \} or {editcom \P}. {Def {Type EditCom} {name \} {Text Makes the edit chain be the value of {index *PRIMARY* UNFIND Var}{var UNFIND}. Generates an error if {var UNFIND}={lisp NIL}. }} {var UNFIND} is set to the current edit chain by each command that makes a "big jump", i.e., a command that usually performs more than a single ascent or descent, namely {editcom ↑}, {editcom ←}, {editcom ←←}, {editcom !NX}, all commands that involve a search, e.g., {editcom F}, {editcom LC}, {editcom ..}, {editcom BELOW}, et al and {editcom \} and {editcom \P} themselves. One exception is that {var UNFIND} is not reset when the current edit chain is the top level expression, since this could always be returned to via the {editcom ↑} command. 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}, etc. {index *PRIMARY* \P EditCom} {Def {Type EditCom} {name \P} {Text Restores the edit chain to its state as of the last print operation, i.e., {editcom P}, {editcom ?}, or {editcom PP}. If the edit chain has not changed since the last printing, {editcom \P} restores it to its state as of the printing before that one, i.e., two chains are always saved. }} For example, if the user types {editcom P} followed by {lisp 3 2 1 P}, {editcom \P} will return 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}, i.e., the user could use {editcom \P} to flip back and forth between the two edit chains. Note that if the user had typed {editcom P} followed by {lisp F COND}, he could use {it either} {editcom \} or {editcom \P} to return to the {editcom P}, i.e., the action of {editcom \} and {editcom \P} are independent. {Def {Type EditCom} {name S} {Args LITATOM} {PrintName {lisp S {arg LITATOM} @} } {Text Sets {arg LITATOM} (using {fn SETQ}) to the current expression after performing {lisp (LC . @)}. The edit chain is not changed. }} Thus {lisp (S FOO)} will set {lisp FOO} to the current expression, and {lisp (S FOO -1 1)} will set {lisp FOO} to the first element in the last element of the current expression. {index *END* edit chain} {index *END* current expression (in Editor)} }{end subsec Commands That Save and Restore the Edit Chain}