-- TextEdit.mesa
-- written by Bill Paxton, February 1981
-- last edit by Bill Paxton, March 19, 1981  4:55 PM

-- This package provides editing of Text nodes as used in Tioga
	-- a text node contains the following elements:
		-- a rope (supplied by TextRope.Mesa)
		-- runs of looks (supplied by TiogaLooks.Mesa)
		-- "persistent" addresses for locations within the node
		-- a property list

-- Editing
	-- Operations are available to edit the contents of text nodes
		-- unlike ropes or runs of looks, text nodes are mutable

-- Persistent addresses
	-- You can associate an address with a location in a text node
	-- the address persists at the same location even if the node is edited
	-- the address is an arbitrary REF ANY supplied by the client program 

-- Property list
	-- Each node includes a property list 
	-- The keys and the values are arbitrary REF ANY's

-- Filing
	-- There are operations to read and write files containing text nodes
	-- The characters go at the front of the file, followed by 0's
		-- then the information about looks, etc.
			-- and finally a password and a pointer to the end of the text.
	-- Thus programs that simply want to look at the characters can read up to 0's

-- Edit notification procedures
	-- Client's can register procedures to be called before/after edits
	-- The details are in EditNotify.Mesa 


DIRECTORY
--File,
TextRope,
TextLooks,
TextNode,
NodeProps,
CardAddrs,
NodeAddrs;

TextEdit: DEFINITIONS
	IMPORTS CardAddrs, NodeAddrs, NodeProps, TextLooks, TextRope =
BEGIN
OPEN
	nodeI: TextNode,
	propsI: NodeProps,
	addrsI: NodeAddrs,
	cardAddr: CardAddrs,
	ropeI: TextRope,
	looksI: TextLooks;

Ref: TYPE = nodeI.Ref;
RefTextNode: TYPE = nodeI.RefTextNode;
Rope: TYPE = ropeI.Rope;

Looks: TYPE = looksI.Looks;
noLooks: Looks = looksI.noLooks;
allLooks: Looks = looksI.allLooks;
Char: TYPE = CHARACTER;
Card: TYPE = LONG CARDINAL;
MaxLen: Card = LAST[Card];
MaxCard: Card = LAST[Card];


-- **** Operations to create/save a text node ****

FromRope: PROC [rope: Rope] RETURNS [RefTextNode];
	-- create a text node with looks from a normal rope

FromString: PROC [string: REF READONLY TEXT] RETURNS [RefTextNode];
	-- copies the contents of the string

GetRope: PROC [text: RefTextNode] RETURNS [Rope];

GetRuns: PROC [text: RefTextNode] RETURNS [looksI.Runs];

FileCapability: TYPE = REF; -- phony decl until goto Pilot

ToFile: PROC [file: FileCapability, node: Ref];
	-- write the node on the specified file

FromFile: PROC [file: FileCapability] RETURNS [Ref];
	-- create node from the contents of a file
	-- if file was not created by ToFile (i.e., password not correct)
		-- then returns FromRope[RopeFrom.File[file]]


-- **** Property List Operations ****

PutProp: PROC [node: Ref, name, val: REF] =
	-- adds name & val to property list for node
	INLINE { propsI.PutProp[node, name, val] };

RemProp: PROC [node: Ref, name: REF] RETURNS [REF] =
	-- removes name & val from property list for node
	-- returns old value, if any
	INLINE { RETURN [propsI.RemProp[node, name]] };

GetProp: PROC [node: Ref, name: REF] RETURNS [REF] =
	-- returns the value associated with the given name
		-- value is NIL if name is not on the property list
	INLINE { RETURN [propsI.GetProp[node, name]] };

MapProps: PROC [node: Ref, action: MapPropsAction] RETURNS [BOOLEAN] =
	INLINE { RETURN [propsI.MapProps[node, LOOPHOLE[action]]] };
	-- apply the action to each name & value pair for the node
	-- returns true if&when an action returns true

	MapPropsAction: TYPE = PROC [name, val: REF] RETURNS [BOOLEAN];


-- **** Persistent addressing for characters in text node ****

PutTextAddr: PROC [node: RefTextNode, addr: REF, location: Card] = 
	-- assigns addr to location in text
	-- ok if addr was previously assigned elsewhere in the text
	-- location automatically updated when text is edited
	INLINE { addrsI.PutTextAddr[node, addr, location] };

RemTextAddr: PROC [node: RefTextNode, addr: REF] =
	-- removes the given addr
	INLINE { addrsI.RemTextAddr[node, addr] };

GetTextAddr: PROC [node: RefTextNode, addr: REF] RETURNS [location: Card] =
	-- generates ERROR TextAddrNotFound if the addr is not in the mapping
	INLINE { RETURN [
		addrsI.GetTextAddr[node, addr
			! cardAddr.AddrNotFound => ERROR TextAddrNotFound]] };

TryGetTextAddr: PROC [node: RefTextNode, addr: REF]
	RETURNS [found: BOOLEAN, location: Card] =
	INLINE { [found, location] ← addrsI.TryGetTextAddr[node, addr] };

TextAddrNotFound: ERROR;

MapTextAddrs: PROC [node: RefTextNode, action: TextAddrsAction]
	RETURNS [BOOLEAN] =
	INLINE { RETURN [addrsI.MapTextAddrs[node, action]] };
	-- apply the action to each addr&location pair for the text
	-- returns true if&when an action returns true

	TextAddrsAction: TYPE = addrsI.TextAddrsAction;
		-- = PROC [addr: REF, location: Card] RETURNS [BOOLEAN];


-- **** Persistent addressing for children of node ****

PutChildAddr: PROC [node: Ref, addr: REF, location: Card] = 
	-- assigns addr to node child number given by location
	-- ok if addr was previously assigned in the node
	-- location automatically updated when node children are edited
	INLINE { addrsI.PutChildAddr[node, addr, location] };

RemChildAddr: PROC [node: Ref, addr: REF] =
	-- removes the given addr
	INLINE { addrsI.RemChildAddr[node, addr] };

GetChildAddr: PROC [node: Ref, addr: REF] RETURNS [location: Card] =
	-- generates ERROR ChildAddrNotFound if the addr is not in the mapping
	INLINE { RETURN [
		addrsI.GetChildAddr[node, addr
			! cardAddr.AddrNotFound => ERROR ChildAddrNotFound]] };

TryGetChildAddr: PROC [node: Ref, addr: REF]
	RETURNS [found: BOOLEAN, location: Card] =
	INLINE { [found, location] ← addrsI.TryGetChildAddr[node, addr] };

ChildAddrNotFound: ERROR;

FollowChildAddrs: PROC [node: Ref, addr: REF] RETURNS [Ref] =
	-- follows addr chain down starting from node
	-- returns first node in chain for which addr not found
	INLINE { RETURN [ addrsI.FollowChildAddrs[node, addr]] };

MapChildAddrs: PROC [node: Ref, action: ChildAddrsAction]
	RETURNS [BOOLEAN] =
	INLINE { RETURN [addrsI.MapChildAddrs[node, action]] };
	-- apply the action to each addr&location pair for the node
	-- returns true if&when an action returns true

	ChildAddrsAction: TYPE = addrsI.ChildAddrsAction;
		-- = PROC [addr: REF, location: Card] RETURNS [BOOLEAN];


-- **** Operations to add or delete looks ****

	-- Looks are represented by a vector of 32 bits
	-- Each character in a text node has looks associated with it
	-- see TiogaLooks.Mesa for more operations

ChangeLooks: PROC [
	text: RefTextNode, remove, add: Looks,
	start: Card ← 0, len: Card ← MaxCard];
	-- first remove then add in the given range

AddLooks: PROC [
	text: RefTextNode, add: Looks,
	start: Card ← 0, len: Card ← MaxCard]
	= INLINE { ChangeLooks[text, noLooks, add, start, len] };

RemoveLooks: PROC [
	text: RefTextNode, remove: Looks ← allLooks,
	start: Card ← 0, len: Card ← MaxCard]
	= INLINE { ChangeLooks[text, remove, noLooks, start, len] };

SetLooks: PROC [
	text: RefTextNode, new: Looks,
	start: Card ← 0, len: Card ← MaxCard]
	= INLINE { ChangeLooks[text, allLooks, new, start, len] };

ClearLooks: PROC [
	text: RefTextNode, start: Card ← 0, len: Card ← MaxCard]
	= INLINE { ChangeLooks[text, allLooks, noLooks, start, len] };

FetchLooks: PROC [text: RefTextNode, location: Card] RETURNS [Looks];
	-- returns the looks for the character at the given location
	-- use reader's if getting looks for sequence of locations


-- **** Changing Style / Type of node ****
	
ChangeType: PROC [
	node: Ref, newStyle, newType: BOOLEAN, 
	styleName: nodeI.StyleName, typeName: nodeI.TypeName];
	-- change style and/or type of node


-- **** Editing Operations for text ****

	-- NOTE: edit operations do not create or delete addrs,
		-- but they can change their locations within the text

ReplaceText: PROC [
	dest: RefTextNode, destStart: Card ← 0, destLen: Card ← MaxLen,
	source: RefTextNode, sourceStart: Card ← 0, sourceLen: Card ← MaxLen];
	-- replace the dest text by a copy of the source text
	-- addrs that are in the replaced text move to destStart
	-- addrs that are after the replaced text are adjusted

DeleteText: PROC [
	text: RefTextNode, start: Card ← 0, len: Card ← MaxLen] = INLINE {
	-- delete the specified range of text
	-- addrs that are in the deleted section move to start
	ReplaceText[text, start, len, NIL] };

CopyText: PROC [
	dest: RefTextNode, destLoc: Card,
	source: RefTextNode, start: Card ← 0, len: Card ← MaxLen] = INLINE {
	-- copy the specified text
	-- add length of inserted text to addrs that are beyond destLoc
	ReplaceText[dest, destLoc, 0, source, start, len] };

MoveText: PROC [
	dest: RefTextNode, destLoc: Card,
	source: RefTextNode, start: Card ← 0, len: Card ← MaxLen];
	-- move the specified text
	-- addrs that are in the moved text do one of the following:
		-- move with the text if dest = source,
		-- or move to start if dest # source

TransposeText: PROC [
	alpha: RefTextNode, alphaStart: Card ← 0, alphaLen: Card ← MaxLen,
	beta: RefTextNode, betaStart: Card ← 0, betaLen: Card ← MaxLen];
	-- transpose the alpha text and the beta text
	-- addrs treated same as in Move
		-- move with the text if alpha = beta,
		-- or move to respective starts if alpha # beta

ReplaceWords: PROC [
	dest: RefTextNode, destStart: Card ← 0, destLen: Card ← MaxLen,
	source: RefTextNode, sourceStart: Card ← 0, sourceLen: Card ← MaxLen];
	-- replace the dest words by a copy of the source words
	-- adjusts appropriately to retain proper spacing
	-- calls notify procs with a ReplacingText change record

CopyWords: PROC [
	dest: RefTextNode, destLoc: Card,
	source: RefTextNode, start: Card ← 0, len: Card ← MaxLen] = INLINE {
	-- copy the specified words
	-- adjusts appropriately to retain proper spacing
	ReplaceWords[dest, destLoc, 0, source, start, len] };

MoveWords: PROC [
	dest: RefTextNode, destLoc: Card,
	source: RefTextNode, start: Card ← 0, len: Card ← MaxLen];
	-- move the specified words
	-- adjusts appropriately to retain proper spacing
	-- calls notify procs with a MovingText change record

TransposeWords: PROC [
	alpha: RefTextNode, alphaStart: Card ← 0, alphaLen: Card ← MaxLen,
	beta: RefTextNode, betaStart: Card ← 0, betaLen: Card ← MaxLen];
	-- transpose the alpha words and the beta words
	-- adjusts appropriately to retain proper spacing
	-- calls notify procs with a TransposingText change record

InsertChar: PROC [
	dest: RefTextNode, char: Char, destLoc: Card ← 0,
	inherit: BOOLEAN ← TRUE, looks: Looks ← noLooks];
	-- insert character at specified location
	-- if destLoc >= size[dest], put character at end of dest
	-- if inherit is false, char gets specifed looks 	
	-- if inherit is true, char gets looks in following manner:
		-- if dest length is 0, then gets looks from argument list, else
		-- if location > 0, then looks and list of previous char,
		-- else looks and list of following char
	-- add 1 to location of names that are beyond destLoc

AppendChar: PROC [
	dest: RefTextNode, char: Char, destLoc: Card ← MaxLen-1,
	inherit: BOOLEAN ← TRUE, looks: Looks ← noLooks] = INLINE {
	InsertChar[dest, char, destLoc+1, inherit, looks] };

InsertString: PROC [
	dest: RefTextNode, string: REF READONLY TEXT, destLoc: Card ← 0,
	stringStart: NAT ← 0, stringNum: NAT ← LAST[NAT],
	inherit: BOOLEAN ← TRUE, looks: Looks ← noLooks];
	-- if destLoc >= size[dest], put string at end of dest
	-- insert stringNum chars from string starting at stringStart
	-- looks specified same as for InsertChar
	-- add length of insert to names that are beyond destLoc

AppendString: PROC [
	dest: RefTextNode, string: REF READONLY TEXT, destLoc: Card ← MaxLen-1,
	stringStart: NAT ← 0, stringNum: NAT ← LAST[NAT],
	inherit: BOOLEAN ← TRUE, looks: Looks ← noLooks] = INLINE {
	InsertString[dest, string, destLoc+1, stringStart, stringNum,
							inherit, looks] };

InsertRope: PROC [
	dest: RefTextNode, rope: Rope, destLoc: Card ← 0,
	inherit: BOOLEAN ← TRUE, looks: Looks ← noLooks];
	-- insert rope at specified location
	-- if destLoc >= size[dest], put rope at end of dest
	-- looks specified same as for InsertChar
	-- add length of insert to names that are beyond destLoc

AppendRope: PROC [
	dest: RefTextNode, rope: Rope, destLoc: Card ← MaxLen-1,
	inherit: BOOLEAN ← TRUE, looks: Looks ← noLooks] = INLINE {
	InsertRope[dest, rope, destLoc+1, inherit, looks] };

FetchChar: PROC [text: RefTextNode, index: Card] RETURNS [Char];
	-- fetches the indexed information
	-- use readers if want info from contiquous locations

Fetch: PROC [text: RefTextNode, index: Card] RETURNS [Char, Looks];
	-- fetches the indexed information
	-- use readers if want info from contiquous locations


-- **** Editing Operations related to node structure ****
	
MoveNodes: PROC [
	destParent: Ref, destLoc: Card,
	sourceParent: Ref, sourceStart, sourceLen: Card];
		-- delete sourceLen children from sourceParent starting at sourceStart
		-- insert them in destParent at destLoc
			-- e.g., destLoc=0 to move to start of dest
			-- and destLoc=NumChildren[dest] to move to end

ReplaceNodes: PROC [
	destParent: Ref, destStart, destLen: Card,
	sourceParent: Ref, sourceStart, sourceLen: Card];
	-- replace destLen children in destParent starting at destStart
	-- by sourceLen children from sourceParent starting at sourceStart
	-- this covers copying and deleting nodes also

TransposeNodes: PROC [
	alphaParent: Ref, alphaStart, alphaLen: Card,
	betaParent: Ref, betaStart, betaLen: Card];
	-- exchange alphaLen children of alphaParent starting with alphaStart
		-- with betaLen children of betaParent starting with betaStart

InsertNode: PROC [
	destParent: Ref, destLoc: Card];
	-- insert a new node as destLoc'th child of destParent

SplitNode: PROC [
	node: RefTextNode, loc: Card];
	-- split the text node at specified character position
		-- text before the split will remain in the node
	-- other text goes in new node that will be inserted as next sibling

MergeNodes: PROC [
	firstNode: RefTextNode, numberMerged: Card];
	-- merge numberMerged text nodes starting at firstNode

END.