ImmutableTioga.mesa
Copyright © 1986 by Xerox Corporation. All rights reserved.
Doug Wyatt, September 15, 1986 4:43:28 pm PDT
DIRECTORY
ImagerFont USING [BYTE, XChar],
Rope USING [ROPE],
Rosary USING [ROSARY],
TextLooks USING [Looks, allLooks, noLooks, Runs];
ImmutableTioga: CEDAR DEFINITIONS
~ BEGIN
Types
Defined elsewhere
CharSet: TYPE ~ ImagerFont.BYTE;
XChar: TYPE ~ ImagerFont.XChar;
ROPE: TYPE ~ Rope.ROPE;
ROSARY: TYPE ~ Rosary.ROSARY;
Runs: TYPE ~ TextLooks.Runs;
Looks: TYPE ~ TextLooks.Looks;
noLooks: Looks ~ TextLooks.noLooks;
allLooks: Looks ~ TextLooks.allLooks;
Node
Node: TYPE ~ REF NodeRep;
NodeRep: TYPE ~ RECORD [ -- 4 words
children: ROSARY --OF Node--,
text: Text
];
Text: TYPE ~ REF TextRep;
TextRep: TYPE ~ RECORD [ -- 10 words
rope: ROPE,
runs: Runs,
charSets: ROSARY --OF REF CharSet--,
charProps: ROSARY --OF PropList--,
props: Props
];
Props: TYPE ~ REF PropsRep;
PropsRep: TYPE ~ RECORD [ -- 5 words
propList: PropList,
formatName: ATOMNIL, -- name of format for the node
comment: BOOLFALSE, -- true if node is a comment
hasStyleDef: BOOLFALSE, -- true if node has StyleDef prop (accelerator)
hasPrefix: BOOLFALSE, -- true if node has Prefix prop (accelerator)
hasPostfix: BOOLFALSE, -- true if node has Postfix prop (accelerator)
hasArtwork: BOOLFALSE -- true if node has Artwork prop (accelerator)
];
PropList: TYPE ~ LIST OF Prop;
Prop: TYPE ~ REF PropRep;
PropRep: TYPE ~ RECORD [name: ATOM, value: REF];
NodeIndex, Location, Text, Span
NodeIndex: TYPE ~ REF NodeIndexRep;
NodeIndexRep: TYPE ~ RECORD [depth: NAT, seq: SEQUENCE size: NAT OF INT];
IndexedNode: PROC [root: Node, index: NodeIndex] RETURNS [Node] ~ {
node: Node ← root;
FOR k: NAT IN [0..index.depth) DO
node ← NthChild[node, index[k]];
ENDLOOP;
RETURN[node];
};
maxLen: INT ~ INT.LAST;
nodeItself: INT ~ -1;
Location: TYPE ~ RECORD [node: NodeIndex, where: INT ← 0];
where >= length of text means at end
where = loc in [0..length) means before that character
e.g., where = 0 means at start of text
where = nodeItself means location is the node itself rather than in its contents
nullLocation: Location ~ [node: NIL, where: nodeItself];
NodeLoc: PROC [n: Node] RETURNS [Location] ~ INLINE { RETURN[[n, nodeItself]] };
Text: TYPE ~ RECORD [content: NodeContent, start: INT ← 0, len: INT ← maxLen];
represents the characters [start..start+len) in content
end: PROC [text: Text] RETURNS [INT] ~ INLINE { RETURN[text.start+text.len] };
-- hack so t.start and t.end are uniformly usable
Span: TYPE ~ RECORD [start, end: Location ← nullLocation];
start.node can equal end.node
in which case either both start.where and end.where = nodeItself, or
neither does and start.where <= end.where
otherwise, end.node should follow start.node in the tree
nodes need not be siblings
no restrictions on start.where or end.where
nullSpan: Span ~ [nullLocation, nullLocation];
World
World: TYPE ~ REF WorldRep;
WorldRep: TYPE;
Tree operations
Parent, Sibling, Child
Parent: PROC [n: Node] RETURNS [Node];
returns parent of n
returns NIL if n is root
Root: PROC [n: Node] RETURNS [Node];
returns root of tree containing n
Next: PROC [n: Node] RETURNS [Node];
returns next sibling of n
returns NIL if n is last sibling
Previous: PROC [n: Node, parent: Node ← NIL] RETURNS [Node];
returns previous sibling of n
returns NIL if n is first sibling
runs faster if can supply parent
IsLastSibling: PROC [n: Node] RETURNS [BOOL];
IsLastSibling[n] == Next[n]=NIL.
FirstChild: PROC [n: Node] RETURNS [Node];
Parent[FirstChild[n]] = n; Previous[FirstChild[n]] = NIL.
LastChild: PROC [n: Node] RETURNS [Node];
Parent[FirstChild[n]] = n; Next[LastChild[n]] = NIL.
FirstSibling: PROC [n: Node] RETURNS [Node];
FirstSibling[n] = FirstChild[Parent[n]]
LastSibling: PROC [n: Node] RETURNS [Node];
LastSibling[n] = LastChild[Parent[n]]
LastWithin: PROC [n: Node] RETURNS [Node];
returns the last node within the branch
i.e., goes to last child of last child of last child ... until no child
Level: PROC [n: Node] RETURNS [INT];
Level[Root[x]] == 0; Level[FirstChild[n]]=Level[n]+1
Counting
NthChild: PROC [n: Node, index: INT] RETURNS [Node];
NthChild[n, 0] == FirstChild[n]; NthChild[n, k+1] == Next[NthChild[n, k]]
NthSibling: PROC [n: Node, index: INT] RETURNS [Node];
CountChildren: PROC [n: Node] RETURNS [count: INT];
CountFollowing: PROC [n: Node] RETURNS [count: INT];
CountToParent: PROC [n: Node] RETURNS [count: INT, parent: Node];
CountToChild: PROC [parent, child: Node] RETURNS [count: INT];
Forward, Backward
StepForward: PROC [node: Node] RETURNS [Node];
returns next node in standard tree walk order
StepBackward: PROC [node: Node, parent: Node ← NIL] RETURNS [Node];
returns preceding node in standard tree walk order
runs faster if can supply parent
Forward: PROC [node: Node] RETURNS [nx: Node, levelDelta: INT];
returns next node in standard tree walk order
levelDelta is Level[node]-Level[nx], where level is depth in tree
levelDelta = 0 if nx and node are siblings
levelDelta = 1 if nx is child of node
levelDelta < 0 if do Parent* and Next to reach nx from node
Backward: PROC [node: Node, parent: Node ← NIL]
RETURNS
[back, backparent: Node, levelDelta: INT];
for backing through tree
runs faster if can supply parent
levelDelta same as for Forward
ForwardClipped: PROC [node: Node, maxLevel: INT, nodeLevel: INT ← 0]
RETURNS
[nx: Node, nxLevel: INT];
like Forward, but limits how deep will go in tree
if pass nodeLevel=0, correct value will be computed
nxLevel = Level[nx] <= MAX[maxLevel,Level[node]]
BackwardClipped: PROC [node: Node, maxLevel: INT, parent: Node ← NIL, nodeLevel: INT ← 0] RETURNS [back, backparent: Node, backLevel: INT];
like Backward, but limits how deep will go in tree
backLevel = Level[back] <= MAX[maxLevel,Level[node]]
Locations
BadArgs: ERROR;
LocRelative: PROC [location: Location, count: INT, break: INT ← 1,
skipCommentNodes: BOOLFALSE] RETURNS [Location];
count is interpreted as a character offset from given location
returns <node,offset> location corresponding to count
adds in break at the end of each text subnode
if skipCommentNodes is true, then ignores them in walking the tree
LocWithin: PROC [n: Node, count: INT, break: INT ← 1,
skipCommentNodes: BOOLFALSE] RETURNS [Location];
LocRelative[[n, 0], ...]; count is offset from beginning of given node
LocOffset: PROC [loc1, loc2: Location, break: INT ← 1,
skipCommentNodes: BOOLFALSE] RETURNS [count: INT];
returns character offset of location2 relative to location1
loc1 and loc2 can be in same node
but loc2 must not be in node before loc1 or get ERROR BadArgs
if skipCommentNodes is true, then ignores them in walking the tree
LocNumber: PROC [at: Location, break: INT ← 1,
skipCommentNodes: BOOLFALSE] RETURNS [count: INT];
returns character offset of location relative to root
LastLocWithin: PROC [n: Node] RETURNS [Location];
returns the last location within the branch
Node order
Order: TYPE ~ { before, same, after, disjoint };
CompareNodeOrder: PROC [node1, node2: Node] RETURNS [Order];
determines relative order in tree of the nodes
returns "same" if node1 = node2
returns "before" if node1 comes before node2
returns "after" if node1 comes after node2
returns "disjoint" if nodes are not from the same tree
CompareLocOrder: PROC [loc1, loc2: Location] RETURNS [Order];
Node operations
Creation
NewNode: PROC RETURNS [Node];
CopyNode: PROC [node: Node] RETURNS [Node];
FromRope: PROC [rope: ROPE] RETURNS [Node];
create a text node with looks from a normal rope
FromRefText: PROC [text: REF READONLY TEXT] RETURNS [Node];
copies the contents of the text
FromString: PROC [string: String] RETURNS [Node];
copies the contents of the string
DocFromNode: PROC [child: Node] RETURNS [root: Node];
returns root node which has one child
child typically created by FromRope or FromString
Size, Chars, Looks
Size: PROC [text: Text] RETURNS [INT];
FetchChar: PROC [text: Text, index: INT] RETURNS [XChar];
fetches the indexed character
use rope readers if want characters from contiguous locations
FetchLooks: PROC [text: Text, index: INT] RETURNS [Looks];
returns the looks for the character at the given location
use readers if getting looks for sequence of locations
Fetch: PROC [text: Text, index: INT] RETURNS [char: XChar, looks: Looks];
fetches the indexed information, except for character properties
use rope & looks readers if want info from contiguous locations
Properties
GetProp: PROC [text: Text, name: ATOM] RETURNS [REF];
GetComment: PROC [text: Text] RETURNS [BOOL];
GetFormat: PROC [text: Text] RETURNS [ATOM];
GetCharProp: PROC [node: Node, index: INT, name: ATOM] RETURNS [REF];
MapPropsAction: TYPE ~ PROC [name: ATOM, value: REF] RETURNS [quit: BOOLFALSE];
MapProps: PROC [node: Node, action: MapPropsAction,
formatFlag, commentFlag: BOOLTRUE] RETURNS [quit: BOOL];
apply the action to each name & value pair for the node
returns true if&when an action returns true
if commentFlag is false, skips Comment property; ditto for formatFlag
hack to accelerate Inherit and PutFile operations
MapCharProps: PROC [node: Node, index: INT, action: MapPropsAction]
RETURNS
[quit: BOOL];
apply the action to each name & value pair for the indexed character
Character PropList
GetPropFromList: PROC [propList: PropList, key: ATOM] RETURNS [value: REF];
PutPropOnList: PROC [propList: PropList, key: ATOM, value: REF] RETURNS [PropList];
GetCharPropList: PROC [node: Node, index: INT] RETURNS [PropList];
Read, write, copy props
PropReader: TYPE ~ PROC [name: ATOM, specs: ROPE] RETURNS [value: REF];
used when reading files; convert from external specs to internal REF
PropWriter: TYPE ~ PROC [name: ATOM, value: REF] RETURNS [specs: ROPE];
used when writing files; convert from internal REF to external specs
PropCopier: TYPE ~ PROC [name: ATOM, value: REF] RETURNS [new: REF];
used when copying nodes; copy internal REF
ReadProp: PropReader;
calls the registered reader for this property name
if no reader is registered, returns specs
WriteProp: PropWriter;
calls the registered writer for this property name
if no writer is registered, returns value if it is a rope, NIL otherwise
CopyProp: PropCopier;
calls the registered copier for this property name
if no copier is registered, returns old value
NullRead: PropReader;
NullWrite: PropWriter;
NullCopy: PropCopier;
these always return NIL
RegisterProp: PROC [name: ATOM, reader: PropReader, writer: PropWriter, copier: PropCopier];
registers procs for given property name
they will be called by ReadProp, WriteProp, CopyProp
Editing
Edit Text
ReplaceText: PROC [dest: Text, destStart: INT ← 0, destLen: INT ← maxLen, source: Text ← NIL, sourceStart: INT ← 0, sourceLen: INT ← maxLen] RETURNS [Text];
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 [dest: Text, destStart: INT ← 0, destLen: INT ← maxLen] RETURNS [Text];
delete the specified range of text
addrs that are in the deleted section move to start
Replace by Char, Rope, String
ReplaceByRope: PROC [dest: Text, destStart: INT, destLen: INT, rope: ROPE, inherit: BOOL, looks: Looks, charSet: CharSet] RETURNS [Text];
Edit Spans
Place: TYPE ~ { before, after, sibling, child };
these are modifiers for the destination of a Move or Copy or Insert
only apply when destination is an entire node (i.e., dest.where = nodeItself)
place = before means insert as sibling before dest
place = after means insert as sibling after dest; inherit children of dest
place = sibling means insert as sibling after dest; don't inherit children of dest
place = child means insert as first child of dest
Replace: PROC [world: World, destRoot, sourceRoot: Node, dest, source: Span,
saveForPaste: BOOLTRUE] RETURNS [result: Span];
replace dest span by copy of source span
result is the new copy of source
Delete: PROC [world: World, root: Node, del: Span, saveForPaste: BOOLTRUE];
SaveForPaste: PROC [world: World, span: Span];
SavedForPaste: PROC [world: World] RETURNS [span: Span];
result is last thing deleted or explicitly saved for Paste
Copy: PROC [world: World, destRoot, sourceRoot: Node, dest: Location, source: Span,
where: Place ← after, nesting: INTEGER ← 0] RETURNS [result: Span];
result is the new copy of source
Move: PROC [world: World, destRoot, sourceRoot: Node, dest: Location, source: Span,
where: Place ← after, nesting: INTEGER ← 0] RETURNS [result: Span];
dest cannot be within source or get error CannotDoEdit
result is moved span
nesting is relative to dest
e.g., where=after and nesting=1 makes source be child of dest
MoveOnto: PROC [world: World, destRoot, sourceRoot: Node, dest, source: Span,
saveForPaste: BOOLTRUE] RETURNS [result: Span];
like Replace, but moves source instead of copying it
result is moved span
Transpose: PROC [world: World, alphaRoot, betaRoot: Node, alpha, beta: Span]
RETURNS
[newAlpha, newBeta: Span];
alpha and beta must not overlap or get error CannotDoEdit
newAlpha is new location of alpha span; ditto for newBeta
CannotDoEdit: ERROR;
Character properties
SetCharProp: PROC [world: World, text: Text, name: ATOM, value: REF, root: Node ← NIL];
Places the value on the character property lists of the chars in the specified range.
Use value = NIL to remove a character property.
SetCharPropList: PROC [world: World, text: Text, propList: PropList, root: Node ← NIL];
ModifyPropsAction: TYPE ~ PROC [value: REF, start: INT, len: INT]
RETURNS
[quit: BOOLFALSE, newValue: REF];
ModifyCharProps: PROC [world: World, text: Text, name: ATOM, action: ModifyPropsAction, root: Node ← NIL] RETURNS [quit: BOOL];
Used for traversing and altering the values of a character property over a range of characters; the action procedure is called for runs of properties, but adjacent runs are not necessarily merged.
Node properties
SetProp: PROC [world: World, node: Node, name: ATOM, value: REF, root: Node ← NIL];
SetComment: PROC [world: World, node: Node, comment: BOOL, root: Node ← NIL];
Sets the $Comment property of a node.
SetFormat: PROC [world: World, node: Node, formatName: ATOM, root: Node ← NIL];
Sets the $Format property of a node.
SetStyle: PROC [world: World, node: Node, styleName: ROPE, root: Node ← NIL];
appends <name> style to end of node prefix
Insert, Split, Merge, Nest
Insert: PROC [world: World, root, old: Node, where: Place ← after,
inherit: BOOLTRUE] RETURNS [new: Node];
new empty node is inserted in tree in position determined by "where"
if inherit, new node inherits properties of old
Split: PROC [world: World, root: Node, loc: Location] RETURNS [new: Node];
inserts copy of loc.node is inserted directly before loc.node (as sibling)
new adopts children of old (if any)
if loc.where # nodeItself and loc.node is a text node, then
text after loc.where moves to new node
text before loc.where stays in old node
returns the new node
Merge: PROC [world: World, root, node: Node] RETURNS [loc: Location];
copies text of node to end of previous node
then deletes node
ChangeNesting: PROC [world: World, root: Node, span: Span, change: INT] RETURNS [Span];
moves all nodes of span, even if entire nodes not selected
change>0 is deeper in the tree, change<0 is shallower
Looks, Caps
ChangeTextLooks: PROC [world: World, root: Node, text: Text, remove, add: Looks];
first remove then add in the given range
ChangeLooks: PROC [world: World, root: Node, span: Span, remove, add: Looks];
first remove then add in the given span
CapChange: TYPE ~ {allCaps, allLower, initCaps, firstCap};
ChangeTextCaps: PROC [world: World, root: Node, text: Text, how: CapChange ← allCaps];
ChangeCaps: PROC [world: World, root: Node, span: Span, how: CapChange ← allCaps];
END.