TextEdit.mesa
written by Bill Paxton, February 1981
last edit by Bill Paxton, April 23, 1982 6:14 am
last edit by Russ Atkinson, July 22, 1983 9:51 am
-- This package provides editing, filing, and various other operations for text nodes
-- a text node contains a rope and runs of looks
-- looks are represented by a vector of 32 bits
-- each character in a text node has looks vector associated with it
-- run encoding is used to reduce the storage needed to represent the looks
-- see TextLooks.Mesa for more information about looks
-- see TextNode.Mesa for the details of text node structure
-- Text editing
-- operations are provided to edit the contents of text nodes
-- unlike ropes or runs of looks, text nodes are mutable
-- i.e., the edit changes the node rather than returning a new node
-- the looks related commands are
-- AddLooks, RemoveLooks, SetLooks, ClearLooks, and ChangeLooks
-- the text related commands are
-- ReplaceText, DeleteText, CopyText, MoveText, MoveTextOnto, TransposeText
-- the word commands are
-- ReplaceWords, DeleteWords, CopyWords, MoveWords, MoveWordsOnto, TransposeWords
-- in addition there are commands taking a character/string/rope as source
-- ReplaceByChar, InsertChar, AppendChar
-- ReplaceByString, InsertString, AppendString
-- ReplaceByRope, InsertRope, AppendRope
-- several commands are available for changing capitalization
-- AllCaps, AllLower, InitialCaps
-- Persistent addresses in text of a node
-- You can associate an address with a character location in a text node
-- the address persists with the same character even if the text is edited
-- the address is an arbitrary REF ANY supplied by the client program
-- see NodeAddrs.Mesa for the persistent addressing operations
-- Property lists
-- Each node includes a property list of key-value pairs
-- The keys are ATOMs and the values are arbitrary REF ANY's
-- Clients can register routines to read/write properties to files
-- and to copy property values when nodes are copied
-- see NodeProps.Mesa for the property list operations
-- 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
-- operations to read/write nodes to files are found in PutGet.Mesa
-- Edit notification procedures
-- Client's can register procedures to be called before/after edits
-- the client procedure is called with the node(s) changed by the edit
-- and a record describing the details of the edit
-- see EditNotify.Mesa for more information
-- Other kinds of nodes
-- clients can create their own varieties in addition to the basic text nodes
-- simply need to provide routines to write specs to files,
-- take specs read from file and create a corresponding node,
-- and to copy when nodes are copied
-- see OtherNode.Mesa for details
-- Tree editing
-- nodes can have children
-- there is a set of tree editing operations available
-- see EditSpan.Mesa for details
DIRECTORY
RopeEdit,
TextLooks,
TextNode,
UndoEvent;
TextEdit:
CEDAR DEFINITIONS
= BEGIN
Ref: TYPE = TextNode.Ref;
RefTextNode: TYPE = TextNode.RefTextNode;
ROPE: TYPE = RopeEdit.ROPE;
Looks: TYPE = TextLooks.Looks;
noLooks: Looks = TextLooks.noLooks;
allLooks: Looks = TextLooks.allLooks;
Offset: TYPE = TextLooks.Offset;
MaxLen, MaxOffset: Offset = LAST[Offset];
MaxNat: NAT = LAST[NAT];
Event: TYPE = UndoEvent.Ref;
-- **** General operations ****
FetchChar:
PROC [text: RefTextNode, index: Offset]
RETURNS [
CHAR];
-- fetches the indexed information
-- use rope readers if want characters from contiquous locations
FetchLooks:
PROC [text: RefTextNode, index: Offset]
RETURNS [Looks];
-- returns the looks for the character at the given location
-- use reader's if getting looks for sequence of locations
Fetch:
PROC [text: RefTextNode, index: Offset]
RETURNS [
CHAR, Looks];
-- fetches the indexed information
-- use rope & looks readers if want info from contiquous locations
GetRope: PROC [text: RefTextNode] RETURNS [ROPE];
GetRuns: PROC [text: RefTextNode] RETURNS [TextLooks.Runs];
Size: PROC [text: RefTextNode] RETURNS [Offset];
Flatten:
PROC [text: RefTextNode]
RETURNS [
BOOLEAN];
-- returns true if it decides to flatten rope & runs
-- this is done automatically after a certain number of edits to the node
AlreadySaved:
PROC [text: RefTextNode, event: Event]
RETURNS [
BOOLEAN];
-- returns TRUE if there is already a ChangingText record for given node in the event
-- **** Operations to create 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
DocFromNode:
PROC [child: Ref]
RETURNS [root: Ref];
-- returns root node which has one child
-- child typically created by FromRope or FromString
-- **** Operations to add or delete looks ****
ChangeLooks:
PROC [
root: Ref, text: RefTextNode, remove, add: Looks,
start: Offset ← 0, len: Offset ← MaxOffset, event: Event ← NIL];
-- first remove then add in the given range
AddLooks:
PROC [
root: Ref, text: RefTextNode, add: Looks,
start: Offset ← 0, len: Offset ← MaxOffset, event: Event ← NIL]
= INLINE { ChangeLooks[root, text, noLooks, add, start, len, event] };
RemoveLooks:
PROC [
root: Ref, text: RefTextNode, remove: Looks ← allLooks,
start: Offset ← 0, len: Offset ← MaxOffset, event: Event ← NIL]
= INLINE { ChangeLooks[root, text, remove, noLooks, start, len, event] };
SetLooks:
PROC [
root: Ref, text: RefTextNode, new: Looks,
start: Offset ← 0, len: Offset ← MaxOffset, event: Event ← NIL]
= INLINE { ChangeLooks[root, text, allLooks, new, start, len, event] };
ClearLooks:
PROC [
root: Ref, text: RefTextNode, start: Offset ← 0, len: Offset ← MaxOffset, event: Event ← NIL]
= INLINE { ChangeLooks[root, text, allLooks, noLooks, start, len, event] };
-- **** Changing Style / Type / Property of node ****
ChangeType: PROC [node: Ref, typeName: TextNode.TypeName, event: Event ← NIL, root: Ref ← NIL];
ChangeStyle:
PROC [
node: Ref, name: ROPE, event: Event ← NIL, root: Ref ← NIL];
-- appends <name> style to end of node prefix
PutProp:
PROC [
node: Ref, name: ROPE, value: REF, event: Event ← NIL, root: Ref ← NIL];
GetProp: PROC [node: Ref, name: ROPE] RETURNS [value: REF];
-- **** Editing Operations for text ****
-- NOTE: edit operations do not create or delete addrs,
-- but they can change their locations within the text
TwoSpanProc:
TYPE =
PROC [
destRoot, sourceRoot: Ref,
dest: RefTextNode, destStart: Offset ← 0, destLen: Offset ← MaxLen,
source: RefTextNode, sourceStart: Offset ← 0, sourceLen: Offset ← MaxLen,
event: Event ← NIL]
RETURNS [resultStart, resultLen: Offset];
DestSpanProc:
TYPE =
PROC [
destRoot, sourceRoot: Ref,
dest: RefTextNode, destLoc: Offset ← 0,
source: RefTextNode, start: Offset ← 0, len: Offset ← MaxLen, event: Event ← NIL]
RETURNS [resultStart, resultLen: Offset];
ReplaceText: TwoSpanProc;
-- 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
OneSpanProc:
TYPE =
PROC [
root: Ref,
text: RefTextNode, start: Offset ← 0, len: Offset ← MaxLen, event: Event ← NIL];
DeleteText: OneSpanProc;
-- delete the specified range of text
-- addrs that are in the deleted section move to start
CopyText: DestSpanProc;
-- copy the specified text
-- add length of inserted text to addrs that are beyond destLoc
MoveText: DestSpanProc;
-- move [start..start+len) in source to destLoc in dest
-- no-op if dest=source and destLoc IN [start..start+len]
-- 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
MoveTextOnto: TwoSpanProc;
-- move [start..start+len) onto [destStart..destStart+destLen)
-- implemented by appropriate calls on MoveText and DeleteText
TransposeProc:
TYPE =
PROC [
alphaRoot, betaRoot: Ref,
alpha: RefTextNode, alphaStart: Offset ← 0, alphaLen: Offset ← MaxLen,
beta: RefTextNode, betaStart: Offset ← 0, betaLen: Offset ← MaxLen, event: Event ← NIL]
RETURNS [alphaResultStart, alphaResultLen, betaResultStart, betaResultLen: Offset];
TransposeText: TransposeProc;
-- 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
ReplaceByText:
PROC [
root: Ref,
dest: RefTextNode, destStart: Offset ← 0, destLen: Offset ← MaxLen,
sourceRope: ROPE, sourceRuns: TextLooks.Runs,
sourceStart: Offset ← 0, sourceLen: Offset ← MaxLen, event: Event ← NIL]
RETURNS [resultStart, resultLen: Offset];
-- like ReplaceText, except give rope and runs as source instead of node
ReplaceWords: TwoSpanProc;
-- replace the dest words by a copy of the source words
-- adjusts appropriately to retain proper spacing
-- returns the start location and length of the new text
DeleteWords: OneSpanProc;
-- delete the specified words
CopyWords: DestSpanProc;
-- copy the specified words
MoveWords: DestSpanProc;
MoveWordsOnto: TwoSpanProc;
-- move the words in [start..start+len) onto those in [destStart..destStart+destLen)
-- adjusts appropriately to retain proper spacing
TransposeWords: TransposeProc;
-- transpose the alpha words and the beta words
-- adjusts appropriately to retain proper spacing
-- returns new starts and lengths
-- alphaResultStart, alphaResultLen are new start and length for alpha words
-- betaResultStart, betaResultLen are new start and length for beta words
ReplaceByChar:
PROC [
root: Ref,
dest: RefTextNode, char: CHAR,
start: Offset ← 0, len: Offset ← MaxLen,
inherit: BOOLEAN ← TRUE, looks: Looks ← noLooks, event: Event ← NIL]
RETURNS [resultStart, resultLen: Offset];
-- 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 start > 0, then looks of previous char,
-- else looks of char following replacement
InsertChar:
PROC [
root: Ref,
dest: RefTextNode, char: CHAR, destLoc: Offset ← 0,
inherit: BOOLEAN ← TRUE, looks: Looks ← noLooks, event: Event ← NIL]
RETURNS [resultStart, resultLen: Offset] = INLINE {
[resultStart,resultLen] ← ReplaceByChar[root, dest, char, destLoc, 0, inherit, looks, event] };
AppendChar:
PROC [
root: Ref,
dest: RefTextNode, char: CHAR,
inherit: BOOLEAN ← TRUE, looks: Looks ← noLooks, event: Event ← NIL]
RETURNS [resultStart, resultLen: Offset] = INLINE {
[resultStart,resultLen] ← InsertChar[root, dest, char, MaxLen, inherit, looks, event] };
ReplaceByString:
PROC [
root: Ref,
dest: RefTextNode, string: REF READONLY TEXT,
stringStart: NAT ← 0, stringNum: NAT ← MaxNat,
start: Offset ← 0, len: Offset ← MaxLen,
inherit: BOOLEAN ← TRUE, looks: Looks ← noLooks, event: Event ← NIL]
RETURNS [resultStart, resultLen: Offset];
InsertString:
PROC [
root: Ref,
dest: RefTextNode, string: REF READONLY TEXT,
stringStart: NAT ← 0, stringNum: NAT ← MaxNat,
destLoc: Offset ← 0,
inherit: BOOLEAN ← TRUE, looks: Looks ← noLooks, event: Event ← NIL]
RETURNS [resultStart, resultLen: Offset] = INLINE {
[resultStart,resultLen] ← ReplaceByString[root,dest,string,stringStart,stringNum,
destLoc,0,inherit,looks,event] };
AppendString:
PROC [
root: Ref,
dest: RefTextNode, string: REF READONLY TEXT,
stringStart: NAT ← 0, stringNum: NAT ← MaxNat,
inherit: BOOLEAN ← TRUE, looks: Looks ← noLooks, event: Event ← NIL]
RETURNS [resultStart, resultLen: Offset] = INLINE {
[resultStart,resultLen] ← InsertString[root,dest,string,stringStart,stringNum,
MaxLen,inherit,looks,event] };
ReplaceByRope:
PROC [
root: Ref,
dest: RefTextNode, rope: ROPE,
start: Offset ← 0, len: Offset ← MaxLen,
inherit: BOOLEAN ← TRUE, looks: Looks ← noLooks, event: Event ← NIL]
RETURNS [resultStart, resultLen: Offset];
InsertRope:
PROC [
root: Ref,
dest: RefTextNode, rope: ROPE, destLoc: Offset ← 0,
inherit: BOOLEAN ← TRUE, looks: Looks ← noLooks, event: Event ← NIL]
RETURNS [resultStart, resultLen: Offset] = INLINE {
[resultStart,resultLen] ← ReplaceByRope[root,dest,rope,destLoc,0,inherit,looks,event] };
AppendRope:
PROC [
root: Ref,
dest: RefTextNode, rope: ROPE,
inherit: BOOLEAN ← TRUE, looks: Looks ← noLooks, event: Event ← NIL]
RETURNS [resultStart, resultLen: Offset] = INLINE {
[resultStart,resultLen] ← InsertRope[root,dest,rope,MaxLen,inherit,looks,event] };
-- ***** Cap's and Lowercase
AllCaps:
PROC [
root: Ref,
dest: RefTextNode, start: Offset ← 0, len: Offset ← MaxLen, event: Event ← NIL] =
INLINE { ChangeCaps[root,dest,start,len,allCaps,event] };
-- force specified span to all uppercase
AllLower:
PROC [
root: Ref,
dest: RefTextNode, start: Offset ← 0, len: Offset ← MaxLen, event: Event ← NIL] =
INLINE { ChangeCaps[root,dest,start,len,allLower,event] };
-- force specified span to all lowercase
InitialCaps:
PROC [
root: Ref,
dest: RefTextNode, start: Offset ← 0, len: Offset ← MaxLen, event: Event ← NIL] =
INLINE { ChangeCaps[root,dest,start,len,initCaps,event] };
-- force first letter of words uppercase
CapChange: TYPE = {allCaps, allLower, initCaps, firstCap};
ChangeCaps:
PROC [
root: Ref,
dest: RefTextNode, start: Offset ← 0, len: Offset ← MaxLen,
how: CapChange ← allCaps, event: Event ← NIL];
-- ***** Initialization
Start: PROC; -- for initialization only
END.