TextNodeImpl.mesa
Copyright Ó 1985, 1986, 1987, 1988 by Xerox Corporation. All rights reserved.
written by Bill Paxton, March 1981
last edit by Bill Paxton, August 11, 1982 9:51 am
Rick Beach, March 27, 1985 10:13:12 am PST
Michael Plass, July 27, 1987 9:21:44 am PDT
Doug Terry, June 25, 1987 2:18:19 pm PDT
Doug Wyatt, February 17, 1988 6:06:44 pm PST
DIRECTORY
Atom USING [MakeAtom],
NodeProps USING [GetProp],
RefTab USING [Create, Delete, EachPairAction, Fetch, Pairs, Ref, Store],
Rope USING [InlineSize, ROPE, Size],
TextNode USING [Location, MaxLen, NodeItself, Node, NodeProps, NodeRep, Span],
TextNodeRegistry USING [ProcSet];
TextNodeRegistryImpl
ProcSet: TYPE ~ TextNodeRegistry.ProcSet;
registrationTable: RefTab.Ref = RefTab.Create[];
Register:
PUBLIC
PROC [activity:
ATOM, procs: ProcSet]
RETURNS [] ~ {
The given TransformProc will hereafter be applied to all nodes having an active property with value=activity.
[] ← RefTab.Store[registrationTable, activity, procs];
};
UnRegister:
PUBLIC
PROC [activity:
ATOM]
RETURNS [] ~ {
Removes the procedures associated with the given activity.
[] ← RefTab.Delete[registrationTable, activity];
};
GetProcs:
PUBLIC
PROC [activity:
ATOM]
RETURNS [procs: ProcSet] ~ {
Return the procedures associated with the given activity.
RETURN[NARROW[RefTab.Fetch[registrationTable, activity].val]];
};
ActivityOn:
PUBLIC
PROC [activity:
ATOM, on:
BOOL ←
TRUE]
RETURNS [wasOn:
BOOL] ~ {
Determines whether or not the registered transformations should be currently applied; if activity=NIL then activity is turned on/off for all registered clients. Registered clients are informed via callbacks when this switch is toggled so they can take appropriate action.
SetActivity: RefTab.EachPairAction = {
[key: RefTab.Key, val: RefTab.Val] RETURNS [quit: BOOL ← FALSE]
procs: ProcSet = NARROW[val];
IF procs#
NIL
THEN {
wasOn ← procs.activityOn;
procs.activityOn ← on;
IF wasOn#on
AND procs.activityProc#
NIL
THEN
procs.activityProc[on, procs.activityProcData];
};
};
wasOn ← FALSE;
IF activity#
NIL
THEN [] ← SetActivity[activity, RefTab.Fetch[registrationTable, activity].val]
ELSE [] ← RefTab.Pairs[registrationTable, SetActivity];
};
DoTransform:
PUBLIC
PROC [activity:
ATOM, node: Node, parent: Node ←
NIL, wantFirst:
BOOL ←
TRUE]
RETURNS [new: Node] ~ {
Force the TransformProc associated with the given activity to be applied to the node. (This is intended primarily for use by TextNodeImpl.)
procs: ProcSet = NARROW[RefTab.Fetch[registrationTable, activity].val];
new ← IF procs=NIL OR NOT procs.activityOn OR procs.transformProc=NIL THEN node ELSE procs.transformProc[node, parent, wantFirst, procs.transformProcData];
};
GetSize:
PUBLIC
PROC [activity:
ATOM, node: Node]
RETURNS [size:
INT] ~ {
Computes or estimates the size that the given node will be AFTER it is transformed by the specified activity. (This is intended primarily for use by TextNodeImpl.)
procs: ProcSet = NARROW[RefTab.Fetch[registrationTable, activity].val];
size ← IF procs=NIL OR NOT procs.activityOn OR procs.sizeProc=NIL THEN Rope.Size[node.rope] ELSE procs.sizeProc[node, procs.sizeProcData];
};
The rest of TextNodeImpl
MakeNodeLoc:
PUBLIC
PROC [n: Node]
RETURNS [Location]
= { RETURN [[node: n, where: NodeItself]] };
MakeNodeSpan:
PUBLIC
PROC [first, last: Node]
RETURNS [Span]
= { RETURN [[MakeNodeLoc[first], MakeNodeLoc[last]]] };
NarrowToTextNode:
PUBLIC
PROC [n: Node]
RETURNS [txt: Node] ~ {
RETURN [n]};
For backwards compatability.
NewTextNode:
PUBLIC
PROC
RETURNS [txt: Node] = {
txt ← NEW[NodeRep]; txt.last ← TRUE
};
InlineParent:
PROC [n: Node]
RETURNS [Node] =
INLINE {
DO
IF n=
NIL
OR n.deleted
THEN
RETURN [
NIL];
IF n.last THEN RETURN [n.next];
n ← n.next;
ENDLOOP;
};
Parent: PUBLIC PROC [n: Node] RETURNS [Node] = { RETURN [Transform[node: InlineParent[n], wantFirst: FALSE]] };
Root:
PUBLIC
PROC [n: Node]
RETURNS [Node] = {
applies Parent repeatedly until reaches root
p: Node;
DO
IF (p ← InlineParent[n])=
NIL
THEN
RETURN [
IF n=
NIL
OR n.deleted
THEN
NIL
ELSE Transform[node: n, wantFirst:
TRUE]];
n ← p;
ENDLOOP;
};
Next:
PUBLIC
PROC [n: Node]
RETURNS [Node] = {
RETURN[IF n=NIL OR n.last OR n.deleted THEN NIL ELSE Transform[node: n.next, wantFirst: TRUE]];
};
Previous:
PUBLIC
PROC [n: Node, parent: Node ←
NIL]
RETURNS [nx: Node] = {
nx2: Node;
IF parent=NIL THEN parent ← InlineParent[n];
IF n=NIL OR parent=NIL OR (nx ← parent.child)=n THEN RETURN [NIL];
DO IF (nx2←nx.next)=n THEN RETURN [Transform[node: nx, wantFirst: FALSE]]; nx ← nx2; ENDLOOP;
};
Forward:
PUBLIC
PROC [node: Node]
RETURNS [nx: Node, levelDelta:
INTEGER] = {
[nx, levelDelta] ← InlineForward[node];
nx ← Transform[node: nx, wantFirst: TRUE];
};
InlineForward:
PROC [node: Node]
RETURNS [nx: Node, levelDelta:
INTEGER] =
INLINE {
returns next node in standard tree walk order
child: Node;
IF node=NIL THEN RETURN [NIL, 0];
IF (child ← node.child) # NIL THEN RETURN [child, 1]; -- descend in the tree
levelDelta ← 0;
DO
-- move to next node,
sibling or up* then sibling
IF NOT node.last THEN RETURN [node.next, levelDelta]; -- the sibling
IF (node ← node.next) = NIL THEN RETURN [NIL, levelDelta]; -- the parent
levelDelta ← levelDelta-1;
ENDLOOP;
};
Backward:
PUBLIC
PROC [node: Node, parent: Node ←
NIL]
RETURNS [back, backparent: Node, levelDelta: INTEGER] = {
returns preceeding node in standard tree walk order
child, child2: Node;
IF parent = NIL THEN parent ← InlineParent[node];
IF parent = NIL OR node = NIL THEN RETURN [NIL, NIL, 0];
IF (child ← parent.child) = node THEN RETURN [Transform[node: parent, wantFirst: FALSE], Parent[parent], -1];
DO
IF child.last
THEN
ERROR;
-- incorrect value supplied for parent
IF (child2 ← child.next)=node THEN EXIT;
child ← child2;
ENDLOOP;
levelDelta ← 0;
child ← Transform[node: child, parent: parent, wantFirst: FALSE];
DO
IF (child2 ← LastChild[child]) =
NIL
THEN
RETURN [child, parent, levelDelta];
levelDelta ← levelDelta+1;
parent ← child; child ← child2;
ENDLOOP;
};
StepForward:
PUBLIC
PROC [node: Node]
RETURNS [Node]
= { RETURN[Forward[node].nx] };
returns next node in standard tree walk order
StepBackward:
PUBLIC
PROC [node: Node, parent: Node ←
NIL]
RETURNS [Node]
= { RETURN[Backward[node, parent].back] };
returns preceding node in standard tree walk order
Level:
PUBLIC
PROC [node: Node]
RETURNS [level:
INTEGER] = {
Level[Root[x]] == 0; Level[FirstChild[n]]=Level[n]+1
level ← 0;
UNTIL (node ← InlineParent[node])=NIL DO level ← level+1 ENDLOOP;
};
ForwardClipped:
PUBLIC
PROC [
node: Node, maxLevel: INTEGER, nodeLevel: INTEGER ← 0]
RETURNS [nx: Node, nxLevel: INTEGER] = {
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]]
child: Node;
IF node=NIL THEN RETURN [NIL, 0];
IF nodeLevel <= 0 THEN nodeLevel ← Level[node];
IF nodeLevel < maxLevel
AND (child ← node.child) #
NIL
THEN
RETURN [Transform[node: child, parent: node, wantFirst: TRUE], nodeLevel+1]; -- return the child
DO
-- move to next node,
sibling or up* then sibling
IF NOT node.last THEN RETURN [Transform[node: node.next, wantFirst: TRUE], nodeLevel]; -- return the sibling
IF (node ← node.next) = NIL THEN RETURN [NIL, 0]; -- go to the parent
nodeLevel ← nodeLevel-1;
ENDLOOP;
};
BackwardClipped:
PUBLIC
PROC [
node: Node, maxLevel: INTEGER, parent: Node ← NIL, nodeLevel: INTEGER ← 0]
RETURNS [back, backparent: Node, backLevel: INTEGER] = {
like Backward, but limits how deep will go in tree
backLevel = Level[back] <= MAX[maxLevel, Level[node]]
child, child2: Node;
IF parent = NIL THEN parent ← InlineParent[node];
IF parent = NIL OR node = NIL THEN RETURN [NIL, NIL, 0];
IF nodeLevel <= 0 THEN nodeLevel ← Level[node];
IF (child ← parent.child) = node THEN RETURN [Transform[node: parent, wantFirst: FALSE], Parent[parent], nodeLevel-1];
DO
-- search for sibling just before node
IF child.last THEN ERROR; -- incorrect value supplied for parent
IF (child2 ← child.next)=node THEN EXIT;
child ← child2;
ENDLOOP;
child ← Transform[node: child, parent: parent, wantFirst: FALSE];
DO
-- go deeper in tree until reach maxLevel
IF nodeLevel >= maxLevel OR (child2 ← LastChild[child]) = NIL THEN
RETURN [child, parent, nodeLevel];
nodeLevel ← nodeLevel+1;
parent ← child;
child ← child2;
ENDLOOP;
};
LocRelative:
PUBLIC
PROC [location: Location, count:
INT ← 0,
break: NAT ← 1, skipCommentNodes: BOOL ← FALSE] RETURNS [Location] = {
n: Node ← location.node;
size, lastSize, where: INT ← 0;
init: Node ← n;
lastTxt: Node;
IF count=0 AND InlineParent[n]=NIL THEN
RETURN [[FirstChild[n], 0]]; -- avoid returning root node
where ← MAX[location.where, 0]; -- where we are in the current node
WHILE n #
NIL
DO
IF n #
NIL
AND (
NOT skipCommentNodes
OR
NOT n.comment)
THEN {
lastSize ← size ← Size[n];
IF (count ← count-(size-where)) <= 0 THEN RETURN [[Transform[node: n, wantFirst: TRUE], MAX[0, count+size]]];
lastTxt ← n;
count ← count-break;
};
[n, ] ← InlineForward[n];
where ← 0;
ENDLOOP;
IF lastTxt # NIL THEN RETURN [[Transform[node: lastTxt, wantFirst: FALSE], lastSize]]; -- end of last text node
RETURN [[init, 0]];
};
LocWithin:
PUBLIC
PROC [n: Node, count:
INT, break:
NAT ← 1, skipCommentNodes:
BOOL ←
FALSE]
RETURNS [Location]
= { RETURN[LocRelative[[n, 0], count, break, skipCommentNodes]] };
BadArgs: PUBLIC ERROR = CODE;
LocOffset:
PUBLIC
PROC [loc1, loc2: Location, break:
NAT ← 1, skipCommentNodes:
BOOL ←
FALSE]
RETURNS [count:
INT ← 0] = {
returns character offset of location2 relative to location1
node: Node ← loc2.node;
n: Node ← loc1.node;
count ← IF loc2.where # NodeItself THEN loc2.where ELSE 0;
count ← count - MAX[loc1.where, 0];
DO
-- add in counts for text nodes before location
SELECT n
FROM
node => RETURN;
NIL => ERROR BadArgs;
ENDCASE;
IF n #
NIL
AND (
NOT skipCommentNodes
OR
NOT n.comment)
THEN
count ← count+Size[n]+break;
[n, ] ← InlineForward[n];
ENDLOOP;
};
LocNumber:
PUBLIC
PROC [at: Location, break:
NAT ← 1, skipCommentNodes:
BOOL ←
FALSE]
RETURNS [count:
INT]
= { RETURN[LocOffset[[Root[at.node], 0], at, break, skipCommentNodes]] };
returns character offset of location relative to root
FirstSibling:
PUBLIC
PROC [n: Node]
RETURNS [Node] = {
RETURN[FirstChild[Parent[n]]];
};
LastSibling:
PUBLIC
PROC [n: Node]
RETURNS [Node] = {
IF n=NIL THEN RETURN [NIL];
UNTIL n.last DO n ← n.next ENDLOOP;
RETURN[Transform[node: n, wantFirst: FALSE]];
};
FirstChild:
PUBLIC
PROC [n: Node]
RETURNS [Node] = {
RETURN[IF n=NIL THEN NIL ELSE Transform[node: n.child, parent: n, wantFirst: TRUE]];
};
LastChild:
PUBLIC
PROC [n: Node]
RETURNS [Node] = {
RETURN[LastSibling[FirstChild[n]]];
};
LastWithin:
PUBLIC
PROC [n: Node]
RETURNS [Node] = {
nxt: Node;
IF n=NIL THEN RETURN [NIL];
IF (nxt ← n.child)=NIL THEN RETURN [n];
n ← nxt;
DO
-- keep going to child of last sibling
IF n.last
THEN {
IF (nxt ← n.child)=NIL THEN RETURN [Transform[node: n, wantFirst: FALSE]];
n ← nxt
}
ELSE n ← n.next;
ENDLOOP;
};
LastLocWithin:
PUBLIC
PROC [n: Node]
RETURNS [Location] = {
last: Node ← LastWithin[n];
where: INT ← IF last # NIL THEN Rope.Size[last.rope] ELSE NodeItself;
RETURN [[last, where]];
};
NthChild:
PUBLIC
PROC [n: Node, location:
INT ← 0]
RETURNS [child: Node] = {
note: NthChild[n, 0]==FirstChild[n]; NthChild[n, k+1]==Next[NthChild[n, k]]
IF n=NIL OR (child ← n.child)=NIL THEN RETURN;
DO
IF location=0
THEN
RETURN [Transform[node: child, wantFirst:
TRUE]];
child ← Transform[node: child, wantFirst: TRUE];
IF child.last THEN RETURN [NIL];
child ← child.next;
location ← location-1;
ENDLOOP;
};
NthSibling:
PUBLIC
PROC [n: Node, cnt:
INT ← 0]
RETURNS [Node] = {
note: NthSibling[n, 0]==n; NthSibling[n, k+1]==Next[NthSibling[n, k]]
IF n=NIL THEN RETURN [NIL];
DO
IF cnt=0
THEN
RETURN [Transform[node: n, wantFirst:
TRUE]];
n ← Transform[node: n, wantFirst: TRUE];
IF n.last THEN RETURN [NIL];
n ← n.next;
cnt ← cnt-1;
ENDLOOP;
};
CountChildren:
PUBLIC
PROC [n: Node]
RETURNS [count:
INT ← 0] = {
child: Node;
IF (child ← FirstChild[n])=NIL THEN RETURN;
DO count ← count+1;
child ← Transform[node: child, wantFirst: TRUE];
IF child.last THEN RETURN;
child ← child.next;
ENDLOOP;
};
CountFollowing:
PUBLIC
PROC [n: Node]
RETURNS [count:
INT ← 0] = {
IF n=NIL THEN RETURN;
UNTIL n.last
DO
n ← n.next;
count ← count+1;
n ← Transform[node: n, wantFirst: TRUE];
ENDLOOP;
};
CountToParent:
PUBLIC
PROC [n: Node]
RETURNS [count:
INT ← 0, parent: Node] = {
IF n=NIL THEN RETURN;
UNTIL n.last
DO
n ← n.next;
count ← count+1;
n ← Transform[node: n, wantFirst: TRUE];
ENDLOOP;
parent ← n.next;
parent ← Transform[node: parent, wantFirst: FALSE];
};
CountToChild:
PUBLIC
PROC [parent, child: Node]
RETURNS [count:
INT ← 0] = {
note: CountToChild[parent, FirstChild[parent]]==0
n: Node;
IF parent=NIL OR child=NIL THEN RETURN;
n ← Transform[node: parent.child, wantFirst: TRUE];
DO
SELECT n
FROM
child => RETURN [count];
NIL => RETURN [MaxLen];
ENDCASE;
n ← Next[n];
count ← count+1;
ENDLOOP;
};
NodeFormat:
PUBLIC
PROC [n: Node]
RETURNS [
ATOM] = {
RETURN[IF n=NIL THEN NIL ELSE n.formatName];
};
IsLastSibling:
PUBLIC
PROC [n: Node]
RETURNS [
BOOL] = {
RETURN[IF n=NIL THEN FALSE ELSE n.last];
};
EndPos:
PUBLIC
PROC [n: Node]
RETURNS [
INT] = {
IF n=NIL THEN RETURN [0];
RETURN [MAX[Rope.Size[n.rope], 1]-1];
};