(*
======================================================================

EXPRESSION EDITOR		-- BETA TEST VERSION! NOT FOR RELEASE!
	by Cameron Smith

Copyright 1989 Wolfram Research, Inc.

Revision History:

  Created 26 May 89 by CS

  Modified 8 June 89 by CS to add "editform" and "editlen" features

  Modified 8 June 89 by CS to add "insert" and "insertitem"

  Modified 12 June 89 by CS to add "evaledit", "the", and "it", and to make a
      special case of "edit[Out[...]]"

======================================================================
*)

(*
   ========================================================================
*)
                  BeginPackage["ExprEditor`"]
(*
   ========================================================================
*)

(*
                ---------------------------------------------------
                 This Unprotect[] makes it harmless to load the
                 package a second time.  Without it we would get
                 errors from attempts to redefine these symbols,
                 which are protected at the end of the package.
                 The first time the package is loaded this serves
                 to create the symbols.
                ---------------------------------------------------
*)

Unprotect[ edit, right ]

(*
                ---------------------------------------------------
                 Now we create (and document) the symbols that we
                 want to be visible to the user.
                ---------------------------------------------------
*)

edit::usage=
"edit[ expr ] puts you in \"editing mode\" on that expression.  The expression
is never evaluated *unless* its head is Out.  edit[Out[...]] is defined as a
special case to make it easy to edit the results of previous computations."

evaledit::usage=
"evaledit[ expr ] does the same as edit[ expr ] except that it evaluates
expr before going into \"editing mode\"."

unedit::usage=
"unedit[ edit[expr] ] takes the expr out of \"editing mode\", permitting
it to be evaluated and manipulated in the ordinary way."

top::usage=
"top[ ] moves you to the top of the expression, leaving you
in \"editing mode\"."

extract::usage=
"extract[ ] makes a new editing object which consists only of the currently
visible subexpression."

the::usage=
"the[edit[expr]] extracts the currently visible subexpression and unedits it."

it::usage=
"it is a delayed evaluation that is equivalent to unedit[extract[Out[]]].
it is useful in such expressions as \"replace[%,f[it]]\"."

replace::usage=
"replace[ editexpr, replacement ] evaluates the replacement expression, then
creates a new editing expression that has the replacement in place of the
currently visible subexpression."

evalinplace::usage=
"evalinplace[ editexpr ] replaces the currently visible subexpression by
the result of evaluating it.  It does not affect any other part of the
editing expression."

up::usage=
"up[ editexpr ] moves you up one level in the editing expression.
up[ editexpr, n_Integer ] moves you up n levels."

down::usage=
"down[ editexpr ] makes the first subexpression of the currently visible
subexpression the new currently visible subexpression.
down[ editexpr, n ] moves you to the n-th subexpression; n=0 is allowed, and
moves you to the Head of the subexpression.
down[ editexpr, n1, n2, ... ] moves you down in successive stages to the
{n1,n2,...} subpart."

insert::usage=
"insert[ editexpr, n ] inserts n Nulls at the left end of the current
subexpression, if possible.  insert[ editexpr, n, k ] inserts n Nulls
after position k."

insertitem::usage=
"editform is a variable used by insert[ ] for the new expression elements
it creates. The default value is Null, but clever delayed assignments to
insertitem can be used to build complicated structures with insert."

right::usage=
"right[ editexpr ] moves to the subexpression one to the right of the current
one.  right[ editexpr, n ] moves n expressions to the right."

left::usage=
"left[ editexpr ] moves to the subexpression one to the left of the current
one.  left[ editexpr, n ] moves n expressions to the left.  If you are at
the first subexpression, left[] moves you into the head."

holdrep::usage=
"holdrep[ editexpr, replacement ] creates a new editing expression that has
the replacement, exactly as entered, without evaluation, in place of the
currently visible subexpression."

editform::usage=
"editform is a variable that specifies the print form for editing expressions.
The default value is OutputForm.  Any printing form, such as FullForm,
InputForm, MatrixForm, or TeXForm, may be assigned to editform.
Unpredictable and undesirable results may occur if editform is given a
value which does not represent a print form.  The current value of editform
is consulted each time an editing expression is printed."

editlen::usage=
"editlen is a variable that is used in Short[ ] to control the maximum length
of the printed form of an editing expression.  Its default value is 5.
The current value of editlen is consulted each time an editing expression
is printed."

(*
   ========================================================================
*)
                        Begin["`Private`"]
(*
   ========================================================================
*)

Clear[edit,unedit,top,extract,replace,evalinplace,up,myflat,downOK,down,
	right,left,holdrep,editform,editlen]

(* ========   edit   ======== *)

editform = OutputForm

editlen = 5

SetAttributes[edit, HoldFirst]

edit[Out[x___]] := evaledit[Out[x]]

edit[edit[x___],___] := edit[x]

Format[e:edit[x_, y___Integer]] := ColumnForm[{
	SequenceForm[
			"ExprEdit: position ",
			{y},
			", ",
			If[ {y}=={}, 1,
				Length[ Apply[myflat,extract[up[e]]] ]
			  ],
			" at this level"
		],
	" ",
	Short[
		MapAt[ HoldForm,
			MapAt[ editform, Hold[x], {1, y}],
			{1, y}
		    ][[1,y]],
		editlen
	    ]
}]

evaledit[x___] := edit[x]

(* ========   unedit   ======== *)

unedit[edit[x_,y___Integer]] := x

(* ========   top   ======== *)

top[edit[x_,y___Integer]] := edit[x]

(* ========   the   ======== *)

the[x_] := unedit[extract[x]]

(* ========   it   ======== *)

it := the[Out[]]

(* ========   extract   ======== *)

extract[edit[x_,y___Integer]] := MapAt[ edit, Hold[x], {1,y} ] [[1,y]]

(* ========   replace   ======== *)

replace[edit[x_,y___Integer],z_] := Block[ {t=edit[x,y]}, t[[1,y]]=z; t ]

(* ========   evalinplace   ======== *)

evalinplace[z:edit[x_,y___Integer]] := replace[ z, unedit[extract[z]] ]

(* ========   up   ======== *)

up[ edit[x_,y__Integer], z_Integer:1 ] := 
	Take[ edit[x,y], 1+ Max[0, Min[ Length[{y}], Length[{y}]-z ] ] ]

up[edit[x_],z_Integer:1] := (
	Print["Already at top of expression!\n"];
	edit[x]
	)

(* ========   HoldMany   ======== *)

SetAttributes[HoldMany,HoldAll]

SetAttributes[myflat,HoldAll]

myflat[ _[y___] ] := HoldMany[y]

(* ========   down   ======== *)

downOK[ w:edit[x_,y___Integer], z_Integer ] := Block[ {t=extract[w]},
	If[ Depth[t] >2, 0 <= z <= Length[ Apply[myflat,t] ], False, False ]
]

down[ e:edit[x_,y___Integer], z_Integer, w__Integer ] :=
	down[edit[x,y,z],w] /; downOK[ e, z ]

down[ e:edit[x_,y___Integer], z_Integer:1 ] :=
	edit[x,y,z] /; downOK[ e, z ]

down[ e:edit[x_,y___Integer], ___ ] := (
	Print["Attempt to select nonexistent part!\n"];
	e
	)

(* ========   right   ======== *)

fixinsert[ e:edit[ x_,y___], edit[ z_[___] ] ] := up[ holdrep[down[e,0],z] ]

insertitem = Null

insert[ e:edit[x_,y___Integer], z_Integer, w_Integer:0 ] :=
Block[ {t,s},
	t = s = extract[e];
	t[[1,0]] = HoldMany;
	t = t[[1]];
	t = Join[	Take[t,w],
			Apply[HoldMany,Table[insertitem,{z}]],
			Take[t,w-Length[t]]
		];
	t = replace[ e, t];
	fixinsert[t,s] 
] /; downOK[ extract[e], w ]

insert[ e:edit[x_,y___Integer], ___ ] := (
	Print["Illegal insertion: out of range, or atomic.\n"];
	e
	)

(* ========   right   ======== *)

right[ edit[x_,y___Integer,z_Integer], w_Integer:1 ] :=
	down[ edit[x,y], z+w ] /; downOK[edit[x,y],z+w]

right[ e:edit[x_,y___Integer], ___ ] := (
	Print["Attempt to select nonexistent part!\n"];
	e
	)

(* ========   left   ======== *)

left[ edit[x_,y___Integer,z_Integer], w_Integer:1 ] :=
	down[ edit[x,y], z-w ] /; downOK[edit[x,y],z-w]

left[ e:edit[x_,y___Integer], ___ ] := (
	Print["Attempt to select nonexistent part!\n"];
	e
	)

(* ========   holdrep   ======== *)

SetAttributes[ holdrep, HoldRest ]

holdrep[ e:edit[x_,y___Integer], z_[w___] ] :=
	up[ holdrep[ down[ replace[e,HoldMany[w]] , 0], z ] ]

holdrep[ e:edit[x_,y___Integer], z_ ] :=
	Block[ {t=edit[x,y]}, t[[1,y]]:=z; t ]

(*
   ========================================================================
*)
                               End[]
(*
   ========================================================================
*)

Protect[ edit, right ]

(*
   ========================================================================
*)
                            EndPackage[]
(*
   ========================================================================
*)

Null
