{Begin SubSec DEdit: The Interlisp-D list structure editor}
{Title DEdit}
{Text

{comment
Revised:	 1-Jul-83 17:26:34
Revised:	 Tue, 5 Jul 83 15:26 PDT   --mjs
Revised:	 Tue, 5 Jul 83 21:26 PDT   --bas
Revised:	 Wed, 6 Jul 83 09:15 PDT   --mjs
	(DEditMode removed, EDITEMBEDTOKEN def added)
Revised:	 20-NOV-83 14:22:26   --bvm: added DT.EDITMACROS
}


{index Dedit}

{Tag Dedit}


DEdit is a structure oriented, modeless, display based editor for objects represented as list structures, such as functions, property lists, data values, etc.  DEdit is an integral part of the standard Interlisp-D environment.


{Begin SubSec General Comments}
{Title General Comments}
{Text


DEdit is designed to be the user's primary editor for programs and data.  To that end, it has incorporated the interfaces of the (older) teletype oriented Interlisp editor so the two can be used interchangeably.  In addition, the full power of the teletype editor, and indeed the full Interlisp system itself, is easily accessible from within DEdit.

DEdit is structure, rather than character, oriented to facilitate selecting and operating on pieces of structure as objects in their own right, rather than as collections of characters.  However, for the occasional situation when character oriented editing is appropriate, DEdit provides access to the Interlisp-D text editing facilities.  DEdit is modeless, in that all commands operate on previously selected arguments, rather than causing the behavior of the interface to change during argument specification.

}{End SubSec General Comments}



{Begin SubSec Operation}
{Title Operation}
{Text


DEdit is normally called through of the following functions:


{FnDef {Name DF} {Args FN}
{Type NLAMBDA NOSPREAD}
{Text
Calls DEdit on the definition of the function {arg FN}.
}}


{FnDef {Name DV} {Args VAR}
{Type NLAMBDA NOSPREAD}
{Text
Calls DEdit on the value of the variable {arg VAR}.
}}


{FnDef {Name DP} {Args NAME PROP}
{Type NLAMBDA NOSPREAD}
{Text
Calls DEdit on the property {arg PROP} of the atom {arg NAME}.  If {arg PROP} is not given, the whole property list of {arg NAME} is edited.
}}


{FnDef {Name DC} {Args FILE}
{Type NLAMBDA NOSPREAD}
{Text
Calls DEdit on the file commands for the file {arg FILE}.
}}


{index EF Fn}
{index EV Fn}
{index EP Fn}

DEdit is normally installed as the default editor for all editing operations, including those invoked by other subsystems, such as the Programmer's Assistant and Masterscope.  DEdit provides functions {fn EF}, {fn EV} and {fn EP} (analogous to the corresponding {lisp D{arg x}} functions) for conveniently accessing the teletype editor from within a DEdit context, e.g. from under a call to DEdit or if DEdit is installed as the default editor.

The default editor may be set with {fn EDITMODE}:

{FnDef {Name EDITMODE} {Args NEWMODE}
{Text
If {arg NEWMODE} is non-{lisp NIL}, sets the default editor to be DEdit (if {arg NEWMODE} is {lisp DISPLAY}), or the teletype editor (if {arg NEWMODE} is {lisp TELETYPE}).  Returns the previous setting.

{note STANDARD will be accepted for TELETYPE for a while}
}}


DEdit operates by providing an alternative, plug-compatible definition of {fn EDITL} ({fn DEDITL}).{index DEDITL Fn}  The normal user entries operate by redefining {fn EDITL} and then calling the corresponding Edit function (i.e., {fn DF} calls {fn EDITF} etc).  Thus, the normal Edit file package, spelling correction, etc. behavior is obtained.

If Edit commands are specified in a call to {fn DEDITL} (e.g., in calls to the editor from Masterscope), {fn DEDITL} will pass those commands to {fn EDITL}, after having placed a {lisp TTY:} entry on {var EDITMACROS} which will cause DEdit to be invoked if any interaction with the user is called for.  In this way, automatic edits can be made completely under program control, yet DEdit's interactive interface is available for direct user interaction.



{FnDef {Name RESETDEDIT} {Args}
{Text
Completely reinitializes DEdit.  Closes all DEdit windows, so that the user must specify the window the next time DEdit is envoked.  {fn RESETDEDIT} is also used to make DEdit recognize the new values of variables such as {var DEDITTYPEINCOMS}, when the user changes them.
}}

}{End SubSec Operation}




{Begin SubSec Interactive Operation}
{Title Interactive Operation}
{Text

When DEdit is called for the first time, it prompts for an edit window, which is preserved and reused for later DEdits, and pretty prints the expression to be edited therein.  (Note: The pretty printer ignores user {var PRETTYPRINTMACROS} because they do not provide enough structural information during printing to enable selection.)  A standard Interlisp-D scroll bar is set up on the left edge of the window and an edit command menu, which remains active throughout the edit, on the right edge.  DEdit then goes into a select, command, execute loop, during which it yields control so that background activities, such as mouse commands in other windows, continue to be performed.



{Begin SubSec Selection}
{Title Selection}
{Text

Selection in a DEdit window is as follows: the {lisp LEFT} button selects the object being directly pointed at; the {lisp MIDDLE} button selects the containing list; and the {lisp RIGHT} button extends the current selection to the lowest common ancestor of that selection and the current position.  The only things that may be pointed at are atomic objects (literal atoms, numbers, etc) and parentheses, which are considered to represent the list they delimit.  White space is not selectable or editable.

When a selection is made, it is pushed on a selection stack which will be the source of operands for DEdit commands.  As each new selection pushes down the selections made before it, this stack can grow arbitrarily deep, so only the top two selections on the stack are highlighted on the screen.   This highlighting is done by underscoring the topmost (most recent) selection with a solid black line and the second topmost selection with a dashed line.  The patterns used were chosen so that their overlappings would be both visible and distinct, since selecting a sub-part of another selection is quite common.

Because one can invoke DEdit recursively, there may be several DEdit windows active on the screen at once.  This is often useful when transferring material from one object to another (as when reallocating functionality within a set of programs).  Selections may be made in any active DEdit window, in any order.  When there is more than one DEdit window, the edit command menu (and the type-in buffer, see below) will attach itself to the most recently opened (or current) DEdit window.

}{End SubSec Selection}



{Begin SubSec Typein}
{Title Typein}
{Text

Characters may be typed at the keyboard at any time.  This will create a type-in buffer window which will position itself under the current DEdit window and do a {fn LISPXREAD} (which must be terminated by a right parenthesis or a return) from the keyboard.  During the read, any character editing subsystem (such as {lisp TTYIN}) that is loaded can be used to do character level editing on the typein.  When the read is complete, the typein will become the current selection (top of stack) and be available as an operand for the next command.  Once the read is complete, objects displayed in the type-in buffer can be selected from, scrolled, or even edited, just like those in the main window.

One can also give some editing commands directly into the typein buffer.  Typing control-Z will interpret the rest of the line as a teletype editor command which will be interpreted when the line is closed.  Likewise, "control-S {arg OLD} {arg NEW}" will substitute {arg NEW} for {arg OLD} and "control-F {arg X}" will find the next occurrence of {arg X}.  

{note Control character commands can be defined by editing the variable {var DEDITTYPEINCOMS}.  A user sensible mechanism for doing this is under consideration.}

}{End SubSec Typein}



{Begin SubSec Shift-Selection}
{Title Shift-Selection}
{Text

Often, significant pieces of what one wishes to type can be found in an active DEdit window.  To aid in transferring the keystrokes that these objects represent into the typein buffer, DEdit supports shift-selection.  Whenever a selection is made in the DEdit window with the left shift key down, the selection made is not pushed on the selection stack, but is instead {it unread} into the keyboard input (and hence shows up in the typein buffer).  A characteristically different highlighting is used to indicate when shift (as opposed to normal) selection is taking place.

Note that shift-selection remains active even when DEdit is not.  Thus one can unread particularly choice pieces of text from DEdit windows into the typescript window.

}{End SubSec Shift-Selection}


{Begin SubSec Commands}
{Title Commands}
{Text

A DEdit command is invoked by selecting an item from the edit command menu.  This can be done either directly, using the {lisp LEFT} mouse button in the usual way, or by selecting a subcommand.  Subcommands are less frequently used commands than those on the main edit command menu and are grouped together in submenus "under" the command on the main menu to which they are most closely related.  For example, the teletype editor defines six commands for adding and removing parentheses (defined in terms of transformations on the underlying list structure).  Of these six commands, only two (inserting and removing parentheses as a pair) are commonly used, so DEdit provides the other four as subcommands of the common two.  The subcommands of a command are accessed by selecting the command from the commands menu with the {lisp MIDDLE} button.  This will bring up a menu of the subcommand options from which a choice can be made.  Subcommands are flagged in the list below with the name of the top level command of which they are options.

If one has a large DEdit window, or several DEdit windows active at once, the edit command window may be far away from the area of the screen in which one is operating.  To solve this problem, the DEdit command window is a "snuggle up" menu.  Whenever the {lisp TAB} key is depressed, the command window will move over to the current cursor position and stay there as long as either the {lisp TAB} key remains down or the cursor is in the command window.  Thus, one can "pull" the command window over, slide the cursor into it and then release the {lisp TAB} key (or not) while one makes a command selection in the normal way.  This eliminates a great deal of mouse movement.

Whenever a change is made, the prettyprinter reprints until the printing stablizes.  As the standard pretty print algorithm is used and as it leaves no information behind on how it makes its choices, this is a somewhat heuristic process.  The {lisp Reprint} command can be used to tidy the result up if it is not, in fact, "pretty".

All commands take their operands from the selection stack, and may push a result back on.  In general, the rule is to select {it target} selections first and {it source} selections second.  Thus, a {lisp Replace} command is done by selecting the thing to be replaced, selecting (or typing) the new material, and then buttoning the {lisp Replace} command in the command menu.  Using {arg TOP} to denote the topmost (most recent) element of the stack and {arg NXT} the second element, the DEdit commands are:


{Def {Type (DEdit Command)}  {Name After}
{Text
Inserts a copy of {arg TOP} after {arg NXT}.
}}


{Def {Type (DEdit Command)}  {Name Before}
{Text
Inserts a copy of {arg TOP} before {arg NXT}.
}}


{Def {Type (DEdit Command)}  {Name Delete}
{Text
Deletes {arg TOP} from the structure being edited.  (A copy of) {arg TOP} remains on the stack and will appear, selected, in the edit buffer.
}}


{Def {Type (DEdit Command)}  {Name Replace}
{Text
Replaces {arg NXT} with a copy of {arg TOP} obtained by substituting a copy of {arg NXT} wherever the value of the atom {var EDITEMBEDTOKEN} (initially, the {lisp &} character) appears in {arg TOP}.  This provides an {editcom MBD} facility, see Idioms below.
}}


{Def {Type (DEdit Command)}  {Name Switch}
{Text
Exchanges {arg TOP} and {arg NXT} in the structure being edited.
}}


{Def {Type (DEdit Command)}  {Name ()}
{Text
Puts parentheses around {arg TOP} and {arg NXT} (which can, of course, be the same element).
}}


{Def {Type (DEdit Command)}  {Name ( in}
{Text
Subcommand of {lisp ()}.
Inserts {lisp (} before {arg TOP} (like the {editcom LI} Edit command)
}}


{Def {Type (DEdit Command)}  {Name ) in}
{Text
Subcommand of {lisp ()}.
Inserts {lisp )} after {arg TOP} (like the {editcom RI} Edit command)
}}


{Def {Type (DEdit Command)}  {Name () out}
{Text
Removes parentheses from {arg TOP}.
}}


{Def {Type (DEdit Command)}  {Name ( out}
{Text
Subcommand of {lisp () out}.
Removes {lisp (} from before {arg TOP} (like the {editcom LO} Edit command)
}}


{Def {Type (DEdit Command)}  {Name ) out}
{Text
Subcommand of {lisp () out}.
Removes {lisp )} from after {arg TOP} (like the {editcom RO} Edit command)
}}


{Def {Type (DEdit Command)}  {Name Undo}
{Text
Undoes last command.
}}


{Def {Type (DEdit Command)}  {Name !Undo}
{Text
Subcommand of {lisp Undo}.
Undoes all changes since the start of this call on DEdit.
}}


{Def {Type (DEdit Command)}  {Name ?Undo} }
{Def {Type (DEdit Command)}  {Name &Undo}
{Text
Subcommands of {lisp Undo}.
Allows selective undoing of other than the last command.  Both of these commands bring up a menu of all the commands issued during this call on DEdit.  When the user selects an item from this menu, the corresponding command (and if {lisp &Undo}, all commands since that point) will be undone.
}}


{Def {Type (DEdit Command)}  {Name Find}
{Text
Selects, in place of {arg TOP}, the first place after {arg TOP} which matches {arg NXT}.  Uses the Edit subsystem's search routine, so supports the full wildcarding conventions of Edit.
}}


{Def {Type (DEdit Command)}  {Name Swap}
{Text
Exchanges {arg TOP} and {arg NXT} on the stack, i.e. the stack is changed, the structure being edited isn't.
}}


The following set of commands are grouped together as subcommands of {lisp Swap} because they all affect the stack and the selections, rather than the structure being edited.


{Def {Type (DEdit Command)}  {Name Center}
{Text
Subcommand of {lisp Swap}.
Scrolls until {arg TOP} is visible in its window.
}}


{Def {Type (DEdit Command)}  {Name Clear}
{Text
Subcommand of {lisp Swap}.
Discards all selections (i.e., "clears" the stack).
}}


{Def {Type (DEdit Command)}  {Name Copy}
{Text
Subcommand of {lisp Swap}.
Puts a copy of {arg TOP} into the edit buffer and makes it the new {arg TOP}.
}}


{Def {Type (DEdit Command)}  {Name Pop}
{Text
Subcommand of {lisp Swap}.
Pops {arg TOP} off the selection stack.
}}


{Def {Type (DEdit Command)}  {Name Reprint}
{Text
Reprints {arg TOP}.
}}


{Def {Type (DEdit Command)}  {Name Edit}
{Text
Runs DEdit on the definition of the atom {arg TOP} (or {fn CAR} of list {arg TOP}).  Uses {fn TYPESOF} to determine what definitions exist for {arg TOP} and, if there is more than one, asks the user, via menu, which one to use.
(Note: DEdit caches each subordinate edit window in the window from which it was entered, for as long as the higher window is active.  Thus, multiple DEdit commands do not incur the cost of repeatedly allocating a new window.)
If {arg TOP} is defined and is a non-list, calls {fn INSPECT} on that value.
{lisp Edit} also has a variety of subcommands which allow choice of editor (DEdit, TTYEdit, etc.) and whether to invoke that editor on the definition of {arg TOP} or the form itself.
}}


{Def {Type (DEdit Command)}  {Name EditCom}
{Text
Allows one to run arbitrary Edit commands on the structure being DEdited (there are far too many of these for them all to appear on the main menu).  {arg TOP} should be an Edit command, which will be applied to {arg NXT} as the current Edit expression. On return to DEdit, the (possibly changed) current Edit expression will be selected as the new {arg TOP}.  Thus, selecting some expression, typing {lisp (R FOO BAZ)}, and buttoning {lisp EditCom} will cause {lisp FOO} to be replaced with {lisp BAZ} in the expression selected.

In addition, a variety of common Edit commands are available as subcommands of {lisp EditCom}.  Currently, these include {editcom ?=}, {editcom GETD}, {editcom CL}, {editcom DW}, {editcom REPACK}, {editcom CAP}, {editcom RAISE}, and {editcom LOWER}.
}}


{Def {Type (DEdit Command)}  {Name Break}
{Text
Does a {lisp BREAKIN AROUND} the current expression {arg TOP}.  (See {PageRef Fn BREAKIN}.)
}}


{Def {Type (DEdit Command)}  {Name Eval}
{Text
Evaluates {arg TOP}, whose value is pushed onto the stack in place of {arg TOP}, and which will therefore appear, selected, in the edit buffer.
}}


{Def {Type (DEdit Command)}  {Name Exit}
{Text
Exits from DEdit (equivalent to Edit {editcom OK}).
}}


{Def {Type (DEdit Command)} {Name OK}  }
{Def {Type (DEdit Command)} {Name Stop}
{Text
Subcommands of {lisp Exit}.
{lisp OK} exits without an error; {lisp STOP} exits with an error.
Equivalent to the Edit commands with the same names.
}}

{note Facilities also exist for adding new commands and for reorganizing the edit command window. Details forthcoming.}

}{End SubSec Commands}




{Begin SubSec Multiple Commands}
{Title Multiple Commands}
{Text

It is occasionally useful to be able to give several commands at once - either because one thinks of them as a unit or because the intervening reprettyprinting is distracting.  The stack architecture of DEdit makes such multiple commands easy to construct - one just pushes whatever arguments are required for the complete suite of commands one has in mind.  Multiple commands are specified by holding down the {lisp CONTROL} key during command selection.  As long as the {lisp CONTROL} key is down, commands selected will not be executed, but merely saved on a list.  Finally, when a command is selected without the {lisp CONTROL} key down, the command sequence is terminated with that command being the last one in the sequence.

One rarely constructs long sequences of commands in this fashion, because the feedback of being able to inspect the intermediate results is usually worthwhile.  Typically, just two or three step idioms are composed in this fashion.  Some common examples are given in the next section.

}{End SubSec Multiple Commands}




{Begin SubSec Idioms}
{Title Idioms}
{Text

As with any interactive system, there are certain common idioms on which experienced users depend heavily.  Not only is discovering the idioms of a new system tiresome, but in places the designer may have assumed familiarity with one or more of them, so not knowing them can make life quite unbearable.  In the case of DEdit, many of these idioms concern easy ways to achieve the effects of specific commands from the Edit system, with which many users are already familiar.  The DEdit idioms described below are the result of the experience of the early users of the system and are by no means exhaustive.  In addition to those that each user will develop to fit his or her own particular style, there are many more to be discovered and you are encouraged to share your discoveries.

Because of the novel argument specification technique (postfix; target first) many of the DEdit idioms are very simple, but opaque until one has absorbed the "target-source-command" way of looking at the world.  Thus, one selects where typein is to go before touching the keyboard.  After typing, the target will be selected second and the typein selected on top, so that an {lisp After}, {lisp Before} or {lisp Replace} will have the desired effect.  If the order is switched, the command will try to change the typein (which may or may not succeed), or will require tiresome {lisp Swap}ping or reselection.  Although this discipline seems strange at first, it comes easily with practice.

Segment selection and manipulation are handled in DEdit by first making them into a sublist, so they can be handled in the usual way.  Thus, if one wants to remove the three elements between {lisp A} and {lisp E} in the list {lisp (A B C D E)}, one selects {lisp B}, then {lisp D} (either order), then makes them into a sublist with the "{lisp ()}" command (pronounced "both in").  This will leave the sublist {lisp (B C D)} selected, so a subsequent {lisp Delete} will remove it.  This can be issued as a single "{lisp ()}; {lisp Delete}" command using multiple command selection, as described above, in which case the intermediate state of {lisp (A (B C D) E)} will not show on the screen.

Inserting a segment proceeds in a similar fashion.  Once the location of the insertion is selected, the segment to be inserted is typed as a list (if it is a list of atoms, they can be typed without parentheses and the {fn READ} will make them into a list, as one would expect).  Then, the command sequence "{lisp After} (or {lisp Before} or {lisp Replace}); {lisp () out}" (given either as a multiple command or as two separate commands) will insert the typein and splice it in by removing its parentheses.

Moving an expression to another place in the structure being edited is easily accomplished by a delete followed by an insert.  Select the location where the moved expression is to go to; select the expression to be moved; then give the command sequence "{lisp Delete}; {lisp After} (or {lisp Before} or {lisp Replace})".  The expression will first be deleted into the edit buffer where it will remain selected.  The subsequent insertion will insert it back into the structure at the selected location.

Embedding and extracting are done with the {lisp Replace} command.  Extraction is simply a special case of replacing something with a subpiece of itself: select the thing to be replaced; select the subpart that is to replace it; {lisp Replace}.  Embedding also uses {lisp Replace}, in conjunction with the "embed token" (the value of {var EDITEMBEDTOKEN}, initially the single character atom {lisp &}).  Thus, to embed some expression in a {fn PROG}, select the expression; type "{lisp (PROG {arg VARSLST} &)}"; {lisp Replace}. 

{lisp Switch} can also be used to generate a whole variety of complex moves and embeds.  For example, switching an expression with typein not only replaces that expression with the typein, but provides a copy of the expression in the buffer, from where it can be edited or moved to somewhere else.  

Finally, one can exploit the stack structure on selections to queue multiple arguments for a sequence of commands.  Thus, to replace several expressions by one common replacement, select each of the expressions to be replaced (any number), then the replacing expression.  Now hit the {lisp Replace} command as many times as there are replacements to be done.  Each {lisp Replace} will pop one selection off the stack, leaving the most recently replaced expression selected.  As the latter is now a copy of the original source, the next Replace will have the desired effect, and so on.

}{End SubSec Idioms}


}{End SubSec Interactive Operation}



{Begin SubSec DEdit Parameters}
{Title DEdit Parameters}
{Text


There are several global variables that can be used to affect various aspects of DEdit's operation.  Although most have been alluded to above, they are summarized here for reference.


{note DEditMode no longer defined}



{index *PRIMARY* EDITEMBEDTOKEN Var}

{VarDef {Name EDITEMBEDTOKEN}
{Text
Initially {lisp &}.  Used in both DEdit and the teletype editor to indicate the special atom used as the "embed token".
}}


{VarDef {Name DEditLinger}
{Text
Initially {lisp T}.  The default behavior of the topmost DEdit window is to remain active on the screen when exited.  This is occasionally inconvenient for programs that call DEdit directly, so it can be made to close automatically when exited by setting this variable to {lisp NIL}.
}}


{VarDef {Name DEDITTYPEINCOMS}
{Text
Defines the control characters recognized as commands during DEdit typein.  The elements of this list are of the form {lisp ({arg LETTER} {arg COMMANDNAME} {arg FN})}, where {arg LETTER} is the alphabetic corresponding to the control character desired (e.g., {lisp A} for control-A), {arg COMMANDNAME} is a litatom used both as a prompt and internal tag, and {arg FN} is a function applied to the expressions typed as arguments to the command.  See the current value of {var DEDITTYPEINCOMS} for examples.  {var DEDITTYPEINCOMS} is only accessed when DEdit is initialized, so DEdit should be reinitialized with {lisp (RESETDEDIT)} if it is changed.
}}


{VarDef {Name DT.EDITMACROS}
{Text
Defines the behavior of the {lisp Edit} command when invoked on a form that is not a list or litatom, thus telling DEdit how to edit instances of certain datatypes.  {Var DT.EDITMACROS} is an association list keyed by datatype name; entries are of the form {lisp ({arg DATATYPE} {arg MAKESOURCEFN} {arg INSTALLEDITFN})}.  When told to {lisp Edit} an object of type {arg DATATYPE}, DEdit calls {arg MAKESOURCEFN} with the object as its argument.  {arg MAKESOURCEFN} can either do the editing itself, in which case it should return {lisp NIL}, or it should "destructure" the object into an editable list and return that list.  In the latter case, DEdit is then invoked recursively on the list; when that edit is finished, DEdit calls {arg INSTALLEDITFN} with two arguments, the original object and the edited list.  If {arg INSTALLEDITFN} causes an error, the recursive Dedit is invoked again, and the process repeats until the user either exits the lower editor with {lisp STOP}, or exits with an expression that {arg INSTALLEDITFN} accepts.
}}


For example, suppose the user has a datatype declared by {lisp (DATATYPE FOO (NAME AGE SEX))}.  To make instances of {lisp FOO} editable, an entry {lisp (FOO DESTRUCTUREFOO INSTALLFOO)} is added to {var DT.EDITMACROS}, where the functions are defined by

{LispCode
(DESTRUCTUREFOO (OBJECT)
  (LIST (fetch NAME of OBJECT)
        (fetch AGE of OBJECT)
        (fetch SEX of OBJECT)))}

{LispCode
(INSTALLFOO (OBJECT CONTENTS)
  (if (EQLENGTH CONTENTS 3)
    then (replace NAME of OBJECT with (CAR CONTENTS))
         (replace AGE of OBJECT with (CADR CONTENTS))
         (replace SEX of OBJECT with (CADDR CONTENTS))
    else (ERROR "Wrong number of fields for FOO" CONTENTS)))}



}{End SubSec DEdit Parameters}


}{End SubSec DEdit: The Interlisp-D list structure editor}