ActiveTextNodeImpl.mesa
Copyright Ó 1985, 1986 by Xerox Corporation. All rights reserved.
written by Bill Paxton, March 1981
last edit by Bill Paxton, August 11, 1982 9:51 am
Doug Wyatt, March 3, 1985 3:01:39 pm PST
Michael Plass, March 27, 1985 3:50:32 pm PST
Rick Beach, March 27, 1985 10:13:12 am PST
Doug Terry, June 25, 1987 2:18:19 pm PDT
This is a modified version of TextNodeImpl.mesa that performs transformations on "active" nodes as they are requested. Active nodes as those with the "Active" property; the value associated with this property identifies a particular transformation. Transformations must be registered using TextNodeRegistry.
Note: All of the public procedures in this module assume that any nodes passed as inputs are regular text nodes (i.e. do not need to be transformed) and ensure that any nodes returned as results are regular text nodes. Strange behavior might result if the transformation of an active node yields another active node.
Note: TextNode.Ref should have a field, "hasactive", to accelerate the checking for the active property. This change was not made since modifying TextNode.mesa triggers lots of recompilations.
DIRECTORY
Atom USING [MakeAtom],
NodeProps USING [GetProp],
Rope USING [ROPE, Size],
TextLooks USING [Runs],
TextNode USING [Body, Location, MaxLen, NodeItself, NodeProps, Ref, RefTextNode, Span],
TextNodeRegistry USING [DoTransform, GetSize];
ActiveTextNodeImpl: CEDAR PROGRAM
IMPORTS Atom, NP: NodeProps, Rope, TextNodeRegistry
EXPORTS TextNode
= BEGIN OPEN TextNode;
ROPE: TYPE ~ Rope.ROPE;
MakeNodeLoc: PUBLIC PROC [n: Ref] RETURNS [Location]
= { RETURN [[node: n, where: NodeItself]] };
MakeNodeSpan: PUBLIC PROC [first, last: Ref] RETURNS [Span]
= { RETURN [[MakeNodeLoc[first], MakeNodeLoc[last]]] };
NarrowToTextNode: PUBLIC PROC [n: Ref] RETURNS [txt: RefTextNode] ~ {RETURN [n]};
For backwards compatability.
NewTextNode: PUBLIC PROC RETURNS [txt: RefTextNode] = {
txt ← NEW[Body]; txt.last ← TRUE
};
Parent: PUBLIC PROC [n: Ref] RETURNS [Ref] = { RETURN [Transform[node: InlineParent[n], wantFirst: FALSE]] };
InlineParent: PROC [n: Ref] RETURNS [Ref] = INLINE {
DO IF n=NIL OR n.deleted THEN RETURN [NIL];
IF n.last THEN RETURN [n.next];
n ← n.next;
ENDLOOP;
};
Root: PUBLIC PROC [n: Ref] RETURNS [Ref] = {
applies Parent repeatedly until reaches root
p: Ref;
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: Ref] RETURNS [Ref] = {
RETURN[IF n=NIL OR n.last OR n.deleted THEN NIL ELSE Transform[node: n.next, wantFirst: TRUE]];
};
Previous: PUBLIC PROC [n: Ref, parent: Ref ← NIL] RETURNS [nx: Ref] = {
nx2: Ref;
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: Ref] RETURNS [nx: Ref, levelDelta: INTEGER] = {
[nx, levelDelta] ← InlineForward[node];
nx ← Transform[node: nx, wantFirst: TRUE];
};
InlineForward: PROC [node: Ref] RETURNS [nx: Ref, levelDelta: INTEGER] = INLINE {
returns next node in standard tree walk order
child: Ref;
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: Ref, parent: Ref ← NIL]
RETURNS [back, backparent: Ref, levelDelta: INTEGER] = {
returns preceeding node in standard tree walk order
child, child2: Ref;
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: Ref] RETURNS [Ref]
= { RETURN[Forward[node].nx] };
returns next node in standard tree walk order
StepBackward: PUBLIC PROC [node: Ref, parent: Ref ← NIL] RETURNS [Ref]
= { RETURN[Backward[node, parent].back] };
returns preceding node in standard tree walk order
Level: PUBLIC PROC [node: Ref] 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: Ref, maxLevel: INTEGER, nodeLevel: INTEGER ← 0]
RETURNS [nx: Ref, 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: Ref;
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: Ref, maxLevel: INTEGER, parent: Ref ← NIL, nodeLevel: INTEGER ← 0]
RETURNS [back, backparent: Ref, backLevel: INTEGER] = {
like Backward, but limits how deep will go in tree
backLevel = Level[back] <= MAX[maxLevel, Level[node]]
child, child2: Ref;
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: BOOLFALSE] RETURNS [Location] = {
n: Ref ← location.node;
size, lastSize, where: INT ← 0;
init: Ref ← n;
lastTxt: RefTextNode;
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: Ref, count: INT, break: NAT ← 1, skipCommentNodes: BOOLFALSE] RETURNS [Location]
= { RETURN[LocRelative[[n, 0], count, break, skipCommentNodes]] };
BadArgs: PUBLIC ERROR = CODE;
LocOffset: PUBLIC PROC [loc1, loc2: Location, break: NAT ← 1, skipCommentNodes: BOOLFALSE] RETURNS [count: INT ← 0] = {
returns character offset of location2 relative to location1
node: Ref ← loc2.node;
n: Ref ← 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: BOOLFALSE] RETURNS [count: INT]
= { RETURN[LocOffset[[Root[at.node], 0], at, break, skipCommentNodes]] };
returns character offset of location relative to root
FirstSibling: PUBLIC PROC [n: Ref] RETURNS [Ref] = {
RETURN[FirstChild[Parent[n]]];
};
LastSibling: PUBLIC PROC [n: Ref] RETURNS [Ref] = {
IF n=NIL THEN RETURN [NIL];
UNTIL n.last DO n ← n.next ENDLOOP;
RETURN[Transform[node: n, wantFirst: FALSE]];
};
FirstChild: PUBLIC PROC [n: Ref] RETURNS [Ref] = {
RETURN[IF n=NIL THEN NIL ELSE Transform[node: n.child, parent: n, wantFirst: TRUE]];
};
LastChild: PUBLIC PROC [n: Ref] RETURNS [Ref] = {
RETURN[LastSibling[FirstChild[n]]];
};
LastWithin: PUBLIC PROC [n: Ref] RETURNS [Ref] = {
nxt: Ref;
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: Ref] RETURNS [Location] = {
last: Ref ← LastWithin[n];
where: INTIF last # NIL THEN Rope.Size[last.rope] ELSE NodeItself;
RETURN [[last, where]];
};
NthChild: PUBLIC PROC [n: Ref, location: INT ← 0] RETURNS [child: Ref] = {
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: Ref, cnt: INT ← 0] RETURNS [Ref] = {
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: Ref] RETURNS [count: INT ← 0] = {
child: Ref;
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: Ref] 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: Ref] RETURNS [count: INT ← 0, parent: Ref] = {
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: Ref] RETURNS [count: INT ← 0] = {
note: CountToChild[parent, FirstChild[parent]]==0
n: Ref;
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;
};
NodeRope: PUBLIC PROC [n: RefTextNode] RETURNS [ROPE] = {
RETURN[IF n=NIL THEN NIL ELSE n.rope];
};
NodeRuns: PUBLIC PROC [n: RefTextNode] RETURNS [TextLooks.Runs] = {
RETURN[IF n=NIL THEN NIL ELSE n.runs];
};
Props: PUBLIC PROC [n: Ref] RETURNS [NodeProps] = {
RETURN[IF n=NIL THEN NIL ELSE n.props];
};
NodeFormat: PUBLIC PROC [n: Ref] RETURNS [ATOM] = {
RETURN[IF n=NIL THEN NIL ELSE n.formatName];
};
IsLastSibling: PUBLIC PROC [n: Ref] RETURNS [BOOL] = {
RETURN[IF n=NIL THEN FALSE ELSE n.last];
};
EndPos: PUBLIC PROC [n: Ref] RETURNS [INT] = {
IF n=NIL THEN RETURN [0];
RETURN [MAX[Rope.Size[n.rope], 1]-1];
};
Routines to check for and generate registered activities
Transform: PROC [node: Ref, parent: Ref ← NIL, wantFirst: BOOLEANTRUE] RETURNS [new: Ref] ~ INLINE {
activity: ROPENARROW[NP.GetProp[node, $Active]];
new ← IF activity=NIL THEN node
ELSE TextNodeRegistry.DoTransform[activity: Atom.MakeAtom[activity], node: node, parent: parent, wantFirst: wantFirst];
};
Size: PROC [node: Ref] RETURNS [size: INT] ~ INLINE {
activity: ROPENARROW[NP.GetProp[node, $Active]];
size ← IF activity=NIL THEN Rope.Size[node.rope] ELSE TextNodeRegistry.GetSize[activity: Atom.MakeAtom[activity], node: node];
};
END.