<<>> <> <> <> <> <> <> <> <> <> <> <<>> <> <<>> <> <<>> DIRECTORY Atom USING [MakeAtom], NodeProps USING [GetProp, nameActive], RefTab USING [Create, Delete, EachPairAction, Fetch, Pairs, Ref, Store], Rope USING [ROPE, Size], TextNode, TextNodeRegistry USING [NotifySet, NotifyProc, ProcSet], Tioga; TextNodeImpl: CEDAR PROGRAM IMPORTS Atom, NodeProps, RefTab, Rope EXPORTS TextNode, TextNodeRegistry = BEGIN Node: TYPE ~ Tioga.Node; Location: TYPE ~ Tioga.Location; ROPE: TYPE ~ Rope.ROPE; <> ProcSet: TYPE ~ TextNodeRegistry.ProcSet; NotifySet: TYPE ~ TextNodeRegistry.NotifySet; NotifyProc: TYPE ~ TextNodeRegistry.NotifyProc; registrationTable: RefTab.Ref = RefTab.Create[]; notificationTable: RefTab.Ref = RefTab.Create[]; Register: PUBLIC PROC [activity: ATOM, procs: ProcSet] RETURNS [] ~ { <> NotifyRegistration: RefTab.EachPairAction = { <<[key: RefTab.Key, val: RefTab.Val] RETURNS [quit: BOOL _ FALSE]>> IF val#NIL THEN { notifier: NotifyProc = NARROW[val, NotifySet].registrationNotify; IF notifier#NIL THEN notifier[key, activity, procs.activityOn]; }; }; [] ¬ RefTab.Store[registrationTable, activity, procs]; [] ¬ RefTab.Pairs[notificationTable, NotifyRegistration]; }; <<>> UnRegister: PUBLIC PROC [activity: ATOM] RETURNS [] ~ { <> on: BOOLEAN = IsActivityOn[activity]; NotifyUnRegistration: RefTab.EachPairAction = { <<[key: RefTab.Key, val: RefTab.Val] RETURNS [quit: BOOL _ FALSE]>> IF val#NIL THEN { notifier: NotifyProc = NARROW[val, NotifySet].unRegistrationNotify; IF notifier#NIL THEN notifier[key, activity, on]; }; }; [] ¬ RefTab.Pairs[notificationTable, NotifyUnRegistration]; [] ¬ 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]>> enumeratedActivity: ATOM = NARROW[key]; procs: ProcSet = NARROW[val]; NotifyActivityOn: RefTab.EachPairAction = { <<[key: RefTab.Key, val: RefTab.Val] RETURNS [quit: BOOL _ FALSE]>> notifier: NotifyProc = NARROW[val, NotifySet].activityOnNotify; IF notifier#NIL THEN notifier[key, enumeratedActivity, on]; }; IF procs#NIL THEN { wasOn ¬ procs.activityOn; procs.activityOn ¬ on; IF wasOn#on THEN { IF procs.activityProc#NIL THEN procs.activityProc[on, procs.activityProcData]; [] ¬ RefTab.Pairs[notificationTable, NotifyActivityOn]; }; }; }; wasOn ¬ FALSE; IF activity#NIL THEN [] ¬ SetActivity[activity, RefTab.Fetch[registrationTable, activity].val] ELSE [] ¬ RefTab.Pairs[registrationTable, SetActivity]; }; IsActivityOn: PUBLIC PROC [activity: ATOM] RETURNS [isOn: BOOLEAN] ~ { <> RETURN[NARROW[RefTab.Fetch[registrationTable, activity].val, ProcSet].activityOn]; }; NotificationRegister: PUBLIC PROC [handle: REF ANY, notifyProcs: NotifySet] RETURNS [] ~ { <> notifier: NotifyProc; NotifyAllRegister: RefTab.EachPairAction = { <<[key: RefTab.Key, val: RefTab.Val] RETURNS [quit: BOOL _ FALSE]>> IF val#NIL THEN notifier[handle, NARROW[key], NARROW[val, ProcSet].activityOn]; }; [] ¬ RefTab.Store[notificationTable, handle, notifyProcs]; IF notifyProcs#NIL THEN { notifier ¬ notifyProcs.registrationNotify; IF notifier#NIL THEN [] ¬ RefTab.Pairs[registrationTable, NotifyAllRegister]; }; }; NotificationUnRegister: PUBLIC PROC [handle: REF ANY, doNotify: BOOLEAN ¬ FALSE] RETURNS [] ~ { <> notifier: NotifyProc; NotifyAllUnRegister: RefTab.EachPairAction = { <<[key: RefTab.Key, val: RefTab.Val] RETURNS [quit: BOOL _ FALSE]>> IF val#NIL THEN notifier[handle, NARROW[key], NARROW[val, ProcSet].activityOn]; }; notifyProcs: NotifySet = NARROW[RefTab.Fetch[notificationTable, handle].val]; [] ¬ RefTab.Delete[notificationTable, handle]; IF doNotify AND notifyProcs#NIL THEN { notifier ¬ notifyProcs.unRegistrationNotify; IF notifier#NIL THEN [] ¬ RefTab.Pairs[registrationTable, NotifyAllUnRegister]; }; }; <<>> 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]; }; <<>> <> activeAtom: ATOM ~ NodeProps.nameActive; PTransform: PROC [node: Node, wantFirst: BOOL] RETURNS [Node] ~ { WITH NodeProps.GetProp[node, activeAtom] SELECT FROM activity: ROPE => RETURN[DoTransform[activity: Atom.MakeAtom[activity], node: node, parent: node.parent, wantFirst: wantFirst]]; ENDCASE => RETURN[node]; }; PSize: PROC [node: Node] RETURNS [INT] ~ { WITH NodeProps.GetProp[node, activeAtom] SELECT FROM activity: ROPE => RETURN[GetSize[activity: Atom.MakeAtom[activity], node: node]]; ENDCASE => RETURN[Rope.Size[node.rope]]; }; Transform: PROC [node: Node, wantFirst: BOOL ¬ TRUE] RETURNS [Node] ~ INLINE { RETURN[IF node#NIL AND node.hasActive THEN PTransform[node, wantFirst] ELSE node]; }; Size: PROC [node: Node] RETURNS [INT] ~ INLINE { RETURN[IF node.hasActive THEN PSize[node] ELSE Rope.Size[node.rope]]; }; <> <<>> NewTextNode: PUBLIC PROC RETURNS [Node] = { RETURN[NEW[Tioga.NodeRep ¬ []]]; }; <<>> XParent: PROC [n: Node] RETURNS [Node] ~ INLINE { RETURN[n.parent] }; XNext: PROC [n: Node] RETURNS [Node] ~ INLINE { RETURN[n.next] }; XChild: PROC [n: Node] RETURNS [Node] ~ INLINE { RETURN[n.child] }; <<>> XRoot: PROC [n: Node] RETURNS [root: Node ¬ NIL] ~ { FOR x: Node ¬ n, XParent[x] UNTIL x=NIL DO root ¬ x ENDLOOP; }; <<>> XFirstSibling: PROC [n: Node] RETURNS [Node] ~ { p: Node ~ XParent[n]; RETURN[IF p=NIL THEN n ELSE XChild[p]]; }; <<>> XPrevious: PROC [n: Node] RETURNS [prev: Node ¬ NIL] ~ { FOR x: Node ¬ XFirstSibling[n], XNext[x] UNTIL x=n DO prev ¬ x ENDLOOP; }; XForward: PROC [n: Node] RETURNS [nx: Node, deltaLevel: INTEGER] ~ { child: Node ~ XChild[n]; IF child#NIL THEN RETURN[child, 1] -- descend in the tree ELSE { -- move to sibling or up* then sibling x: Node ¬ n; FOR delta: INTEGER ¬ 0, delta-1 DO next: Node ~ XNext[x]; IF next#NIL THEN RETURN[next, delta]; -- the sibling IF (x ¬ XParent[x])=NIL THEN RETURN[NIL, delta]; -- the parent ENDLOOP; }; }; Parent: PUBLIC PROC [n: Node] RETURNS [Node] ~ { RETURN[IF n=NIL THEN NIL ELSE Transform[node: XParent[n], wantFirst: FALSE]]; }; <<>> Root: PUBLIC PROC [n: Node] RETURNS [Node] ~ { RETURN[IF n=NIL THEN NIL ELSE Transform[node: XRoot[n], wantFirst: TRUE]]; }; Level: PUBLIC PROC [n: Node] RETURNS [level: INTEGER ¬ 0] ~ { p: Node ¬ IF n=NIL THEN NIL ELSE XParent[n]; UNTIL p=NIL DO level ¬ level+1; p ¬ XParent[p] ENDLOOP; }; Next: PUBLIC PROC [n: Node] RETURNS [Node] ~ { RETURN[IF n=NIL THEN NIL ELSE Transform[node: XNext[n], wantFirst: TRUE]]; }; Previous: PUBLIC PROC [n: Node, parent: Node ¬ NIL] RETURNS [nx: Node] ~ { RETURN[IF n=NIL THEN NIL ELSE Transform[node: XPrevious[n], wantFirst: FALSE]]; }; <<>> FirstChild: PUBLIC PROC [n: Node] RETURNS [Node] ~ { RETURN[IF n=NIL THEN NIL ELSE Transform[node: XChild[n], wantFirst: TRUE]]; }; <<>> Forward: PUBLIC PROC [node: Node] RETURNS [nx: Node, levelDelta: INTEGER] ~ { IF node=NIL THEN RETURN[NIL, 0]; [nx, levelDelta] ¬ XForward[node]; nx ¬ Transform[node: nx, wantFirst: TRUE]; }; <<>> StepForward: PUBLIC PROC [node: Node] RETURNS [Node] ~ { RETURN[Forward[node].nx]; }; Backward: PUBLIC PROC [node: Node, parent: Node ¬ NIL] RETURNS [back, backparent: Node, levelDelta: INTEGER] = { <> child, child2: Node; IF node=NIL OR (parent ¬ XParent[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.next=NIL THEN ERROR; -- incorrect value supplied for parent IF (child2 ¬ child.next)=node THEN EXIT; child ¬ child2; ENDLOOP; levelDelta ¬ 0; child ¬ Transform[node: child, wantFirst: FALSE]; DO IF (child2 ¬ LastChild[child]) = NIL THEN RETURN [child, parent, levelDelta]; levelDelta ¬ levelDelta+1; parent ¬ child; child ¬ child2; ENDLOOP; }; <<>> StepBackward: PUBLIC PROC [node: Node, parent: Node ¬ NIL] RETURNS [Node] ~ { RETURN[Backward[node, parent].back]; }; 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, wantFirst: TRUE], nodeLevel+1]; -- return the child DO -- move to next node, sibling or up* then sibling IF node.next#NIL THEN RETURN [Transform[node: node.next, wantFirst: TRUE], nodeLevel]; -- return the sibling IF (node ¬ node.parent) = 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 node=NIL OR (parent ¬ XParent[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.next=NIL THEN ERROR; -- incorrect value supplied for parent IF (child2 ¬ child.next)=node THEN EXIT; child ¬ child2; ENDLOOP; child ¬ Transform[node: child, 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; }; XLocRelative: PROC [location: Location, count: INT, break: NAT ¬ 1, skipCommentNodes: BOOL ¬ FALSE] RETURNS [Location] ~ { rem: INT ¬ MAX[count, 0]; last: Location ¬ [NIL, 0]; FOR node: Node _ location.node, XForward[node].nx UNTIL node=NIL DO IF NOT (skipCommentNodes AND node.comment) THEN { size: INT ~ Size[node]; start: INT ~ IF node=location.node THEN MIN[MAX[location.where, 0], size] ELSE 0; len: INT ~ size-start; IF rem<=len THEN RETURN[[node, start+rem]]; rem ¬ rem-MIN[len+break, rem]; last ¬ [node, size]; }; ENDLOOP; RETURN[last]; }; <<>> LocRelative: PUBLIC PROC [location: Location, count: INT, break: NAT ¬ 1, skipCommentNodes: BOOL ¬ FALSE] RETURNS [Location] ~ { loc: Location ~ XLocRelative[location, count, break, skipCommentNodes]; RETURN[[Transform[node: loc.node, wantFirst: TRUE], loc.where]]; }; <<>> 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] ~ { FOR node: Node _ loc1.node, XForward[node].nx UNTIL node=NIL DO IF NOT (skipCommentNodes AND node.comment) THEN { size: INT ~ Size[node]; i0: INT ~ IF node=loc1.node THEN MIN[MAX[loc1.where, 0], size] ELSE 0; i1: INT ~ IF node=loc2.node THEN MIN[MAX[loc2.where, 0], size] ELSE size+break; count ¬ count+(i1-i0); }; IF node=loc2.node THEN RETURN; ENDLOOP; IF loc2.node#NIL THEN ERROR BadArgs; }; <<>> 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.next=NIL DO n ¬ n.next ENDLOOP; RETURN[Transform[node: n, wantFirst: FALSE]]; }; <<>> 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.next=NIL 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 Tioga.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.next=NIL 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.next=NIL 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.next=NIL THEN RETURN; child ¬ child.next; ENDLOOP; }; CountFollowing: PUBLIC PROC [n: Node] RETURNS [count: INT ¬ 0] = { IF n=NIL THEN RETURN; UNTIL n.next=NIL 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.next=NIL 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 [TextNode.MaxLen]; ENDCASE; n ¬ Next[n]; count ¬ count+1; ENDLOOP; }; <<>> EndPos: PUBLIC PROC [n: Node] RETURNS [INT] = { IF n=NIL THEN RETURN [0]; RETURN [MAX[Rope.Size[n.rope], 1]-1]; }; <<>> DestroyTree: PUBLIC PROC [root: Node] ~ { parent: Node ~ Parent[root]; next: Node ¬ root; UNTIL next=parent DO -- go through the tree zapping REF's node: Node ~ next; IF node.child#NIL THEN { next ¬ node.child; node.child ¬ NIL } ELSE { next ¬ IF node.next#NIL THEN node.next ELSE node.parent; node.deleted ¬ TRUE; node.parent ¬ NIL; node.next ¬ NIL; node.rope ¬ NIL; node.runs ¬ NIL; node.charSets ¬ NIL; node.charProps ¬ NIL; node.nodeProps ¬ NIL; }; ENDLOOP; }; <<>> END.