<> <> <> <> <> <> <> <> <<>> <> <<>> <> <<>> <> <<>> 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]; TextNodeImpl: CEDAR PROGRAM IMPORTS Atom, NP: NodeProps, RefTab, Rope EXPORTS TextNode, TextNodeRegistry = BEGIN OPEN TextNode; ROPE: TYPE ~ Rope.ROPE; <> ProcSet: TYPE ~ TextNodeRegistry.ProcSet; registrationTable: RefTab.Ref = RefTab.Create[]; Register: PUBLIC PROC [activity: ATOM, procs: ProcSet] RETURNS [] ~ { <> [] _ RefTab.Store[registrationTable, activity, procs]; }; <<>> UnRegister: PUBLIC PROC [activity: ATOM] RETURNS [] ~ { <> [] _ RefTab.Delete[registrationTable, activity]; }; <<>> GetProcs: PUBLIC PROC [activity: ATOM] RETURNS [procs: ProcSet] ~ { <> RETURN[NARROW[RefTab.Fetch[registrationTable, activity].val]]; }; <<>> ActivityOn: PUBLIC PROC [activity: ATOM, on: BOOL _ TRUE] RETURNS [wasOn: BOOL] ~ { <> 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] ~ { <> 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] ~ { <> 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]; }; <<>> <> PTransform: PROC [node: Node, parent: Node, wantFirst: BOOL] RETURNS [Node] ~ { activity: ROPE ~ NARROW[NP.GetProp[node, activeAtom]]; RETURN [DoTransform[activity: Atom.MakeAtom[activity], node: node, parent: parent, wantFirst: wantFirst]]; }; PSize: PROC [node: Node] RETURNS [INT] ~ { activity: ROPE ~ NARROW[NP.GetProp[node, activeAtom]]; RETURN [GetSize[activity: Atom.MakeAtom[activity], node: node]]; }; activeAtom: ATOM ~ $Active; Transform: PROC [node: Node, parent: Node _ NIL, wantFirst: BOOL _ TRUE] RETURNS [Node] ~ INLINE { IF node = NIL OR node.props = NIL OR NP.GetProp[node, activeAtom] = NIL THEN RETURN [node]; RETURN [PTransform[node: node, parent: parent, wantFirst: wantFirst]]; }; Size: PROC [node: Node] RETURNS [INT] ~ INLINE { IF node.props = NIL OR NP.GetProp[node, activeAtom] = NIL THEN RETURN [Rope.InlineSize[node.rope]]; RETURN [PSize[node: node]]; }; <> <<>> 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]}; <> 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] = { <> 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 { <> 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] = { <> 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] }; <> StepBackward: PUBLIC PROC [node: Node, parent: Node _ NIL] RETURNS [Node] = { RETURN[Backward[node, parent].back] }; <> Level: PUBLIC PROC [node: Node] RETURNS [level: INTEGER] = { <> 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] = { <> <> <> 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] = { <> <> 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] = { <> 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]] }; <> 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] = { <> 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] = { <> 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] = { <> 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]; }; END.