-- 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.