-- TextNodeImpl.mesa
-- written by Bill Paxton, March 1981
-- last edit by Bill Paxton, August 11, 1982 9:51 am
Last Edited by: Plass, April 20, 1983 8:59 am
DIRECTORY
TextNode,
TextLooks,
Rope,
RopeEdit,
SafeStorage;
TextNodeImpl: CEDAR PROGRAM
IMPORTS SafeStorage, RopeEdit, TextNode
EXPORTS TextNode =
BEGIN
OPEN TextNode;
-- ***** Declarations
qZone: PUBLIC ZONE ← SafeStorage.NewZone[quantized]; -- quantized zone
pZone: PUBLIC ZONE ← SafeStorage.NewZone[prefixed]; -- prefix zone
-- ***** Operations
NewTextNode: PUBLIC PROC RETURNS [txt: RefTextNode] = {
txt ← qZone.NEW[TextBody]; txt.last ← TRUE };
NewOtherNode: PUBLIC PROC RETURNS [oth: RefOtherNode] = {
oth ← qZone.NEW[OtherBody]; oth.last ← TRUE };
Parent: PUBLIC PROC [n: Ref] RETURNS [Ref] = { RETURN [InlineParent[n]] };
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 n];
n ← p;
ENDLOOP
};
Next: PUBLIC PROC [n: Ref] RETURNS [Ref] = {
RETURN[IF n=NIL OR n.last OR n.deleted THEN NIL ELSE n.next] };
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; nx ← nx2; ENDLOOP };
Forward: PUBLIC PROC [node: Ref] RETURNS [nx: Ref, levelDelta: INTEGER] = {
[nx, levelDelta] ← InlineForward[node] };
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 ~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 [parent,Parent[parent],-1];
DOIF child.last THEN ERROR; -- incorrect value supplied for parent
IF (child2 ← child.next)=node THEN EXIT;
child ← child2;
ENDLOOP;
levelDelta ← 0;
DOIF (child2 ← LastChild[child]) = NIL THEN RETURN [child,parent,levelDelta];
levelDelta ← levelDelta+1;
parent ← child; child ← child2; ENDLOOP };
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 [child,nodeLevel+1]; -- return the child
DO -- move to next node, sibling or up* then sibling
IF ~node.last THEN RETURN [node.next,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 [parent,InlineParent[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;
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: Offset,
break: NAT ← 1, skipCommentNodes: BOOLEANFALSE] RETURNS [Location] = {
n: Ref ← location.node;
size, lastSize, where: Offset;
init: Ref ← n;
txt, 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
txt ← NarrowToTextNode[n];
IF txt # NIL AND (~skipCommentNodes OR ~txt.comment) THEN {
lastSize ← size ← RopeEdit.Size[txt.rope];
IF (count ← count-(size-where)) <= 0 THEN RETURN [[n,MAX[0,count+size]]];
lastTxt ← txt;
count ← count-break };
[n,] ← InlineForward[n];
where ← 0;
ENDLOOP;
IF lastTxt # NIL THEN RETURN [[lastTxt,lastSize]]; -- end of last text node
RETURN [[init,0]] };
BadArgs: PUBLIC ERROR = CODE;
LocOffset: PUBLIC PROC [loc1, loc2: Location,
break: NAT ← 1, skipCommentNodes: BOOLEANFALSE] RETURNS [count: Offset] = {
-- returns character offset of location2 relative to location1
node: Ref ← loc2.node;
n: Ref ← loc1.node;
txt: RefTextNode;
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;
txt ← NarrowToTextNode[n];
IF txt # NIL AND (~skipCommentNodes OR ~txt.comment) THEN
count ← count+RopeEdit.Size[txt.rope]+break;
[n,] ← InlineForward[n];
ENDLOOP };
FirstSibling: PUBLIC PROC [n: Ref] RETURNS [Ref] = {
RETURN[FirstChild[Parent[n]]]};
LastSibling: PUBLIC PROC [n: Ref] RETURNS [Ref] = {
IF n=NIL THEN RETURN [NIL];
DO IF n.last THEN RETURN [n]; n ← n.next; ENDLOOP };
FirstChild: PUBLIC PROC [n: Ref] RETURNS [Ref] = {
RETURN[IF n=NIL THEN NIL ELSE n.child]};
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 [n];
n ← nxt }
ELSE n ← n.next;
ENDLOOP };
LastLocWithin: PUBLIC PROC [n: Ref] RETURNS [Location] = {
last: Ref ← LastWithin[n];
lastText: RefTextNode ← NarrowToTextNode[last];
where: Offset ← IF lastText # NIL THEN RopeEdit.Size[lastText.rope] ELSE NodeItself;
RETURN [[last,where]] };
NthChild: PUBLIC PROC [n: Ref, location: Offset] 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;
DOIF location=0 THEN RETURN [child];
IF child.last THEN RETURN [NIL];
child ← child.next;
location ← location-1; ENDLOOP };
NthSibling: PUBLIC PROC [n: Ref, cnt: Offset] RETURNS [Ref] = {
-- note: NthSibling[n,0]==n; NthSibling[n,k+1]==Next[NthSibling[n,k]]
IF n=NIL THEN RETURN [NIL];
DOIF cnt=0 THEN RETURN [n];
IF n.last THEN RETURN [NIL];
n ← n.next;
cnt ← cnt-1; ENDLOOP };
CountChildren: PUBLIC PROC [n: Ref] RETURNS [count: Offset] = {
child: Ref;
count ← 0;
IF (child ← FirstChild[n])=NIL THEN RETURN;
DO count ← count+1;
IF child.last THEN RETURN;
child ← child.next;
ENDLOOP };
CountFollowing: PUBLIC PROC [n: Ref] RETURNS [count: Offset] = {
count ← 0;
IF n=NIL THEN RETURN;
DOIF n.last THEN RETURN;
n ← n.next;
count ← count+1;
ENDLOOP };
CountToParent: PUBLIC PROC [n: Ref] RETURNS [count: Offset, parent: Ref] = {
count ← 0;
IF n=NIL THEN RETURN;
DOIF n.last THEN EXIT;
n ← n.next;
count ← count+1;
ENDLOOP;
parent ← n.next };
CountToChild: PUBLIC PROC [parent, child: Ref] RETURNS [count: Offset] = {
-- note: CountToChild[parent,FirstChild[parent]]==0
n: Ref;
count ← 0;
IF parent=NIL OR child=NIL THEN RETURN;
n ← parent.child;
DOSELECT 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]};
NodeType: PUBLIC PROC [n: Ref] RETURNS [TypeName] = {
RETURN[IF n=NIL THEN nullTypeName ELSE n.typename]};
IsLastSibling: PUBLIC PROC [n: Ref] RETURNS [BOOLEAN] = {
RETURN[IF n=NIL THEN FALSE ELSE n.last] };
EndPos: PUBLIC PROC [n: Ref] RETURNS [Offset] = {
node: RefTextNode = NarrowToTextNode[n];
IF node=NIL THEN RETURN [0];
RETURN [MAX[RopeEdit.Size[node.rope],1]-1] };
-- ***** Initialization
Start: PUBLIC PROC = {
};
END.