DIRECTORY EditSpan, EditSpanSupport, OtherNode, TextEdit, RopeEdit, TextNode; EditSpanSupportImpl: CEDAR MONITOR IMPORTS EditSpan, EditSpanSupport, TextEdit, TextNode, OtherNode, RopeEdit EXPORTS EditSpanSupport, EditSpan = BEGIN OPEN EditSpanSupport, EditSpan; freeSlice: Slice; -- free list of slices numFreeSlices: NAT _ 0; maxFreeSlices: NAT = 15; minSliceSize: NAT = 10; GetSlice: PUBLIC ENTRY PROC [len: NAT] RETURNS [slice: Slice] = { IF freeSlice # NIL THEN { -- look on free list prev: Slice; IF freeSlice.maxLength >= len THEN { -- take the first from list slice _ freeSlice; freeSlice _ freeSlice.next; slice.next _ NIL; numFreeSlices _ numFreeSlices-1; RETURN }; prev _ freeSlice; FOR s: Slice _ freeSlice.next, s.next UNTIL s=NIL DO IF s.maxLength >= len THEN { -- take this one prev.next _ s.next; s.next _ NIL; numFreeSlices _ numFreeSlices-1; RETURN [s] }; prev _ s; ENDLOOP }; RETURN [TextNode.pZone.NEW[SliceArray[MAX[len,minSliceSize]]]] }; FreeSlice: PUBLIC ENTRY PROC [slice: Slice] = { IF slice=NIL OR numFreeSlices >= maxFreeSlices OR slice.maxLength < minSliceSize THEN RETURN; FOR i:NAT IN [0..slice.length) DO slice[i] _ NIL; ENDLOOP; slice.next _ freeSlice; freeSlice _ slice; numFreeSlices _ numFreeSlices+1 }; MakeSlices: PROC [node: Ref] RETURNS [before, after: Slice] = { last: NAT; Slicer: PROC [node: Ref, height: NAT] RETURNS [level: NAT] = { height _ height+1; IF node=NIL THEN { -- have gone beyond root before _ GetSlice[height]; before.kind _ before; after _ GetSlice[height+1]; after.kind _ after; RETURN [0] }; level _ Slicer[TextNode.Parent[node],height]; before[level] _ node; after[level] _ TextNode.Next[node]; RETURN [level+1] }; IF node=NIL THEN RETURN; last _ Slicer[node,0]; before.length _ last; IF node.child # NIL THEN { after[last] _ node.child; after.length _ last+1 } ELSE after.length _ last; FOR i: NAT _ after.length, i-1 DO -- delete trailing NIL's from after IF i=0 THEN { after.length _ 0; EXIT }; IF after[i-1] # NIL THEN { after.length _ i; EXIT }; ENDLOOP }; InsertPrefix: PROC [first, last: Slice, firstLen: NAT] RETURNS [new: Slice] = { newLen: NAT; IF first = NIL OR last = NIL OR first.kind # before OR last.kind # before OR first.length < firstLen THEN ERROR; new _ GetSlice[newLen _ firstLen + last.length]; new.kind _ before; new.length _ newLen; FOR i: NAT IN [0..firstLen) DO new[i] _ first[i]; ENDLOOP; FOR i: NAT IN [0..last.length) DO new[firstLen+i] _ last[i]; ENDLOOP }; NeedNestingChange: PUBLIC PROC [before, after, top, bottom: Slice, nesting: INTEGER, depth: NAT] RETURNS [NeededNestingChange] = { bandStart, afterOver: INTEGER; topLen, botLen: NAT; nesting _ MIN[1,nesting]; topLen _ top.length; botLen _ bottom.length; bandStart _ before.length+nesting-(topLen-depth); IF bandStart <= 0 THEN RETURN [needNest]; -- must be at least 1 afterOver _ after.length-(botLen-depth+bandStart); IF afterOver > 1 THEN RETURN [needUnNest]; RETURN [ok] }; Splice: PUBLIC PROC [before, after: Slice, beforeStart, afterStart: NAT _ 0] = { a, b: Ref; beforeLen, afterLen: NAT; beforeLen _ before.length - beforeStart; afterLen _ after.length - afterStart; IF before.kind # before OR after.kind # after THEN ERROR; IF afterLen > beforeLen+1 THEN ERROR; -- cannot have gaps in tree b _ LastOfSlice[before]; IF afterLen = beforeLen+1 THEN { -- adopt children b.child _ a _ LastOfSlice[after]; IF a # NIL AND beforeLen > 0 THEN TextNode.LastSibling[a].next _ b } ELSE b.child _ NIL; IF beforeLen=0 THEN RETURN; FOR i: NAT _ beforeLen-1, i-1 DO -- do successors b _ before[beforeStart+i]; IF i >= afterLen OR (a_after[afterStart+i])=NIL THEN { -- no successor IF i > 0 THEN { b.next _ before[beforeStart+i-1]; b.last _ TRUE }} ELSE { -- has successor IF a=b THEN RETURN; IF i > 0 THEN TextNode.LastSibling[a].next _ before[beforeStart+i-1]; b.next _ a; b.last _ FALSE }; IF i=0 THEN RETURN; ENDLOOP }; ReplaceBand: PUBLIC PROC [before, after, top, bottom: Slice, nesting: INTEGER, event: Event] = { depth: NAT; fullBottom: Slice; nesting _ MIN[1,nesting]; IF top.length >= before.length+nesting THEN ERROR; depth _ MAX[1,before.length+nesting-top.length]; fullBottom _ InsertPrefix[before,bottom,depth]; Splice[fullBottom,after]; Splice[before,top,depth]; FreeSlice[fullBottom] }; BadBand: PUBLIC ERROR = CODE; DescribeBand: PUBLIC PROC [first, last: Ref] RETURNS [before, after, top, bottom: Slice, nesting: INTEGER, depth: NAT] = { BadBandError: PROC = { ERROR BadBand [! UNWIND => { FreeSlice[before]; FreeSlice[after]; FreeSlice[top]; FreeSlice[bottom] } ] }; pred: Ref _ TextNode.StepBackward[first]; minDepth: NAT; IF pred=NIL THEN ERROR BadBand; -- first is root node IF pred=last THEN ERROR BadBand; -- this actually happened during testing! [before,top] _ MakeSlices[pred]; nesting _ top.length-before.length; [bottom,after] _ MakeSlices[last]; minDepth _ MIN[before.length,bottom.length]; FOR depth _ 0, depth+1 UNTIL depth >= minDepth DO IF before[depth] # bottom[depth] THEN { -- check for legality bot: Ref _ bottom[depth]; FOR node: Ref _ before[depth], TextNode.Next[node] DO SELECT node FROM bot => EXIT; NIL => BadBandError[]; -- last must come before first ENDCASE; ENDLOOP; EXIT }; ENDLOOP; IF depth=0 THEN BadBandError[]; -- different root nodes for first and last IF LastOfSlice[top] # first THEN ERROR; IF before[before.length+nesting-2] # TextNode.Parent[first] THEN ERROR; IF LastOfSlice[bottom] # last THEN ERROR }; DeletePrefix: PUBLIC PROC [slice: Slice, depth: NAT] = { newLen: NAT; IF slice.length < depth THEN ERROR; newLen _ slice.length-depth; FOR i:NAT IN [0..newLen) DO slice[i] _ slice[i+depth]; ENDLOOP; FOR i:NAT IN [newLen..slice.length) DO slice[i] _ NIL; ENDLOOP; slice.length _ newLen }; DestSlices: PUBLIC PROC [dest: Ref, where: Place] RETURNS [before, after: Slice, nesting: INTEGER] = { SELECT where FROM after => { [before,after] _ MakeSlices[dest]; nesting _ 0 }; child => { [before,after] _ MakeSlices[dest]; nesting _ 1 }; before => { pred: Ref _ TextNode.StepBackward[dest]; [before,after] _ MakeSlices[pred]; nesting _ after.length-before.length }; ENDCASE => ERROR }; CreateDest: PUBLIC PROC [depth: NAT] RETURNS [dest: Location] = { node: Ref; UNTIL depth = 0 DO child: Ref _ TextNode.NewTextNode[]; IF node # NIL THEN { node.child _ child; child.next _ node }; node _ child; depth _ depth-1; ENDLOOP; RETURN [[node,TextNode.NodeItself]] }; CopySpan: PUBLIC PROC [span: Span] RETURNS [result: Span] = TRUSTED { node, sourceFirst, sourceLast, child, new, prev, parent, first: Ref; firstLoc, lastLoc: Offset; sourceFirst _ span.start.node; firstLoc _ span.start.where; sourceLast _ span.end.node; lastLoc _ span.end.where; IF (node _ sourceFirst)=NIL THEN RETURN [TextNode.nullSpan]; parent _ TextNode.NewTextNode[]; -- parent for the span DO -- create new node each time through the loop WITH n:node SELECT FROM text => { txt: RefTextNode; new _ txt _ TextNode.qZone.NEW[TextNode.TextBody]; txt.rope _ n.rope; txt.runs _ n.runs }; other => { othr: RefOtherNode; new _ othr _ TextNode.qZone.NEW[TextNode.OtherBody]; othr.variety _ n.variety; othr.info _ OtherNode.CopyInfo[@n] }; ENDCASE => ERROR; IF prev#NIL THEN { prev.last _ FALSE; prev.next _ new } ELSE parent.child _ new; -- insert new new.new _ new.last _ TRUE; new.next _ parent; prev _ new; Inherit[node,new,TRUE]; -- inherit properties from node IF node=sourceFirst THEN first _ new; IF node=sourceLast THEN { RETURN [[[first,firstLoc],[new,lastLoc]]] }; IF (child _ node.child) # NIL THEN { -- descend in the tree node _ child; parent _ new; prev _ NIL } ELSE DO -- move to next node, sibling or up* then sibling IF ~node.last THEN { node _ node.next; EXIT }; prev _ parent; IF (parent _ parent.next) = NIL THEN { -- need a new parent parent _ TextNode.NewTextNode[]; parent.child _ prev; prev.next _ parent }; IF (node _ node.next)=NIL THEN RETURN [TextNode.nullSpan]; -- bad arg span ENDLOOP; ENDLOOP }; CompareSliceOrder: PUBLIC PROC [s1, s2: Slice] RETURNS [order: NodeOrder] = { s1Len, s2Len: NAT; IF s1=NIL OR s2=NIL OR (s1Len_s1.length)=0 OR (s2Len_s2.length)=0 OR s1[0] # s2[0] THEN RETURN [disjoint]; IF s1.kind # before OR s2.kind # before THEN ERROR; -- only valid for parent slices FOR i:NAT _ 1, i+1 DO -- know that s1[j]=s2[j] for j { IF i=s2Len THEN RETURN [same]; -- s1Last=s2Last RETURN [before] }; -- s1Last is a parent of s2Last s2Len => RETURN [after]; -- s2Last is a parent of s1Last ENDCASE; IF s1[i] # s2[i] THEN { -- they are siblings, so can check order by Next's s2Node: Ref _ s2[i]; FOR n:Ref _ TextNode.Next[s1[i]], TextNode.Next[n] DO -- search from s1 to s2 SELECT n FROM s2Node => RETURN [before]; NIL => RETURN [after]; ENDCASE; ENDLOOP }; ENDLOOP }; CompareNodeOrder: PUBLIC PROC [node1, node2: Ref] RETURNS [order: NodeOrder] = { s1, s2: Slice; IF node1=NIL OR node2=NIL THEN RETURN [disjoint]; IF node1=node2 THEN RETURN [same]; s1 _ MakeParentSlice[node1]; s2 _ MakeParentSlice[node2]; order _ CompareSliceOrder[s1,s2]; FreeSlice[s1]; FreeSlice[s2] }; MakeParentSlice: PROC [node: Ref] RETURNS [slice: Slice] = { Slicer: PROC [node: Ref, height: NAT] RETURNS [level: NAT] = { height _ height+1; IF node=NIL THEN { -- have gone beyond root slice _ GetSlice[height]; slice.kind _ before; RETURN [0] }; level _ Slicer[TextNode.Parent[node],height]; slice[level] _ node; RETURN [level+1] }; IF node=NIL THEN RETURN; slice.length _ Slicer[node,0] }; DoSplits: PUBLIC PROC [alpha, beta: Span, event: Event] RETURNS [Span, Span] = { MakeSplit: PROC [node: Ref, offset: Offset] = { FixLoc: PROC [old: Location] RETURNS [newLoc: Location] = { where: Offset; IF old.node # node THEN RETURN [old]; SELECT where _ old.where FROM = NodeItself => ERROR; < offset => RETURN [[node,where]]; ENDCASE => RETURN [[new,where-offset]] }; new: Ref; IF node = NIL THEN RETURN; new _ Split[TextNode.Root[node],[node,offset],event]; alpha.start _ FixLoc[alpha.start]; alpha.end _ FixLoc[alpha.end]; beta.start _ FixLoc[beta.start]; beta.end _ FixLoc[beta.end] }; IF alpha.start.where # NodeItself THEN MakeSplit[alpha.start.node,alpha.start.where]; IF beta.start.where # NodeItself THEN MakeSplit[beta.start.node,beta.start.where]; IF alpha.end.where # NodeItself THEN MakeSplit[alpha.end.node,alpha.end.where+1]; IF beta.end.where # NodeItself THEN MakeSplit[beta.end.node,beta.end.where+1]; RETURN [alpha,beta] }; DoSplits2: PUBLIC PROC [dest: Location, source: Span, where: Place, nesting: INTEGER, event: Event] RETURNS [Location, Span, Place, INTEGER] = { destLoc: Location; destSpan: Span _ [dest,TextNode.nullLocation]; [destSpan,source] _ DoSplits[destSpan,source,event]; destLoc _ destSpan.start; IF dest.where # NodeItself THEN { -- did a split destLoc _ BackLoc[destLoc]; where _ after; nesting _ 0 }; RETURN [destLoc, source, where, nesting] }; ReMerge: PUBLIC PROC [alpha, beta: Span, merge: Ref, event: Event, tail: BOOLEAN _ FALSE] RETURNS [Span, Span] = { loc: Location; FixLoc: PROC [old: Location] RETURNS [Location] = { where: Offset; IF old.node = merge THEN SELECT where _ old.where FROM = NodeItself => ERROR; ENDCASE => RETURN [[loc.node,loc.where+where]]; RETURN [old] }; IF tail THEN merge _ TextNode.StepForward[merge]; loc _ Merge[TextNode.Root[merge],merge,event]; alpha.start _ FixLoc[alpha.start]; alpha.end _ FixLoc[alpha.end]; beta.start _ FixLoc[beta.start]; beta.end _ FixLoc[beta.end]; RETURN [alpha,beta] }; UndoSplits: PUBLIC PROC [alpha, beta: Span, event: Event] RETURNS [Span, Span] = { IF alpha.start.where # NodeItself THEN [alpha,beta] _ ReMerge[alpha,beta,alpha.start.node,event]; IF beta.start.where # NodeItself THEN [alpha,beta] _ ReMerge[alpha,beta,beta.start.node,event]; IF alpha.end.where # NodeItself THEN [alpha,beta] _ ReMerge[alpha,beta,alpha.end.node,event,TRUE]; IF beta.end.where # NodeItself THEN [alpha,beta] _ ReMerge[alpha,beta,beta.end.node,event,TRUE]; RETURN [alpha,beta] }; UndoSplits2: PUBLIC PROC [dest: Location, source: Span, event: Event] RETURNS [Location, Span] = { destSpan: Span _ [dest,TextNode.nullLocation]; [destSpan,source] _ UndoSplits[destSpan,source,event]; RETURN [destSpan.end,source] }; SliceOrder: PUBLIC PROC [alpha, beta: Span, aBefore, aBottom, bBefore, bBottom: Slice] RETURNS [overlap: BOOLEAN, head, tail: Span, startOrder, endOrder: NodeOrder] = { IF CompareSliceOrder[aBottom,bBefore]#after --alphaend before betastart OR CompareSliceOrder[aBefore,bBottom]#before --alphastart after betaend THEN { overlap _ FALSE; RETURN }; startOrder _ CompareSliceOrder[aBefore,bBefore]; endOrder _ CompareSliceOrder[aBottom,bBottom]; head _ SELECT startOrder FROM before => [alpha.start,BackLoc[beta.start]], same => TextNode.nullSpan, after => [beta.start,BackLoc[alpha.start]], ENDCASE => ERROR; tail _ SELECT endOrder FROM before => [ForwardLoc[alpha.end],beta.end], same => TextNode.nullSpan, after => [ForwardLoc[beta.end],alpha.end], ENDCASE => ERROR; overlap _ TRUE }; FixWordSpans: PUBLIC PROC [alpha, beta: Span, event: Event] RETURNS [newAlpha, newBeta: Span] = { FixWords: PROC [span: Span] = { FixLoc: PROC [dest: RefTextNode, old: Location, where: Offset, delta: INTEGER] RETURNS [Location] = { IF old.node # dest THEN RETURN [old]; RETURN [SELECT old.where FROM = NodeItself => old, <= where => old, ENDCASE => [old.node,old.where+delta]] }; DeleteBlank: PROC [dest: RefTextNode, where: Offset] = { [] _ TextEdit.DeleteText[TextNode.Root[dest],dest,where,1,event]; alpha.start _ FixLoc[dest,alpha.start,where,-1]; alpha.end _ FixLoc[dest,alpha.end,where,-1]; beta.start _ FixLoc[dest,beta.start,where,-1]; beta.end _ FixLoc[dest,beta.end,where,-1] }; InsertBlank: PROC [dest: RefTextNode, where: Offset] = { [] _ TextEdit.InsertChar[TextNode.Root[dest],dest,' ,where,TRUE,noLooks,event]; alpha.start _ FixLoc[dest,alpha.start,where,1]; alpha.end _ FixLoc[dest,alpha.end,where,1]; beta.start _ FixLoc[dest,beta.start,where,1]; beta.end _ FixLoc[dest,beta.end,where,1] }; startNode, endNode: RefTextNode; startLen, endLen: Offset; wantLeading, wantTrailing, hasLeading, hasTrailing: BOOLEAN; IF span.start.where = NodeItself OR span.end.where = NodeItself THEN RETURN; IF span.start=span.end THEN RETURN; IF (startNode _ TextNode.NarrowToTextNode[span.start.node])=NIL THEN RETURN; IF (endNode _ TextNode.NarrowToTextNode[span.end.node])=NIL THEN RETURN; wantLeading _ WantLeadingSpace[startNode,span.start.where]; wantTrailing _ WantTrailingSpace[endNode,span.end.where,TextEdit.Size[endNode]]; startLen _ (IF startNode=endNode THEN span.end.where ELSE TextEdit.Size[startNode]) - span.start.where; endLen _ span.end.where-(IF startNode=endNode THEN span.start.where ELSE 0); hasLeading _ HasLeadingSpace[startNode,span.start.where,startLen]; hasTrailing _ HasTrailingSpace[endNode,span.end.where-endLen,endLen]; IF hasTrailing AND ~wantTrailing THEN DeleteBlank[endNode,span.end.where-1]; IF ~hasTrailing AND wantTrailing THEN InsertBlank[endNode,span.end.where]; IF hasLeading AND ~wantLeading THEN DeleteBlank[startNode,span.start.where]; IF ~hasLeading AND wantLeading THEN InsertBlank[startNode,span.start.where] }; AdjustWordSpan: PROC [span: Span] RETURNS [Span] = { following, preceeding, leading, trailing: BOOLEAN; startNode, endNode: RefTextNode; startLen, endLen: Offset; startWhere, endWhere: Offset; IF span.start=span.end THEN RETURN [nullSpan]; IF (startWhere _ span.start.where) = NodeItself OR (endWhere _ span.end.where) = NodeItself THEN RETURN [nullSpan]; IF (startNode _ TextNode.NarrowToTextNode[span.start.node])=NIL THEN RETURN [nullSpan]; IF (endNode _ TextNode.NarrowToTextNode[span.end.node])=NIL THEN RETURN [nullSpan]; startLen _ (IF startNode=endNode THEN endWhere ELSE TextEdit.Size[startNode]) - startWhere; endLen _ endWhere-(IF startNode=endNode THEN startWhere ELSE 0); following _ HasFollowingSpace[endNode,endWhere,TextEdit.Size[endNode]]; preceeding _ HasPreceedingSpace[startNode,startWhere]; leading _ HasLeadingSpace[startNode,startWhere,TextEdit.Size[startNode]]; trailing _ HasTrailingSpace[endNode,endWhere-endLen,endLen]; IF leading THEN IF trailing THEN startWhere _ startWhere+1 ELSE IF following THEN { startWhere _ startWhere+1; endWhere _ endWhere+1 } ELSE NULL ELSE IF ~trailing THEN IF following THEN endWhere _ endWhere+1 ELSE IF preceeding THEN { startWhere _ startWhere-1; endWhere _ endWhere+1 } ELSE NULL ELSE NULL; RETURN [[[startNode,startWhere],[endNode,endWhere]]] }; WantLeadingSpace: PROC [dest: RefTextNode, destStart: Offset] RETURNS [BOOLEAN] = { RETURN [destStart > 0 AND RopeEdit.AlphaNumericChar[TextEdit.FetchChar[dest,destStart-1]]] }; WantTrailingSpace: PROC [dest: RefTextNode, destEnd, destSize: Offset] RETURNS [BOOLEAN] = { RETURN [destEnd < destSize AND RopeEdit.AlphaNumericChar[TextEdit.FetchChar[dest,destEnd]]] }; HasLeadingSpace: PROC [source: RefTextNode, start, len: Offset] RETURNS [BOOLEAN] = { RETURN [len > 0 AND RopeEdit.BlankChar[TextEdit.FetchChar[source,start]]] }; HasTrailingSpace: PROC [source: RefTextNode, start, len: Offset] RETURNS [BOOLEAN] = { RETURN [len > 0 AND RopeEdit.BlankChar[TextEdit.FetchChar[source,start+len-1]]] }; HasFollowingSpace: PROC [source: RefTextNode, end, size: Offset] RETURNS [BOOLEAN] = { RETURN [end < size AND RopeEdit.BlankChar[TextEdit.FetchChar[source,end]]] }; HasPreceedingSpace: PROC [source: RefTextNode, start: Offset] RETURNS [BOOLEAN] = { RETURN [start > 0 AND RopeEdit.BlankChar[TextEdit.FetchChar[source,start-1]]] }; FixWords[alpha]; FixWords[beta]; alpha _ AdjustWordSpan[alpha]; beta _ AdjustWordSpan[beta]; RETURN [alpha,beta] }; Apply: PUBLIC PROC [span: Span, proc: ApplyProc] = { node, last: Ref; start, lastLen: Offset; IF (node _ span.start.node) = NIL THEN RETURN; IF (last _ span.end.node) = NIL THEN RETURN; IF (start _ span.start.where)=TextNode.NodeItself THEN start _ 0; IF span.end.where=TextNode.NodeItself THEN lastLen _ MaxLen ELSE IF span.end.where=MaxLen THEN lastLen _ MaxLen ELSE { lastLen _ span.end.where+1; IF node=last THEN lastLen _ lastLen-start }; DO TRUSTED {WITH n:node SELECT FROM text => IF proc[@n,start,IF @n=last THEN lastLen ELSE MaxLen] THEN RETURN; other => NULL; ENDCASE => ERROR}; IF node=last OR (node _ TextNode.StepForward[node])=NIL THEN RETURN; start _ 0; ENDLOOP }; BackLoc: PUBLIC PROC [loc: Location] RETURNS [new: Location] = { new.node _ TextNode.StepBackward[loc.node]; new.where _ IF loc.where=NodeItself THEN NodeItself ELSE TextEdit.Size[TextNode.NarrowToTextNode[new.node]] }; ForwardLoc: PUBLIC PROC [loc: Location] RETURNS [new: Location] = { new.node _ TextNode.StepForward[loc.node]; new.where _ IF loc.where=NodeItself THEN NodeItself ELSE 0 }; END. š-- EditSpanSupportImpl.mesa -- written by Bill Paxton, June 1981 -- last edit by Bill Paxton, November 9, 1982 10:30 am Last Edited by: Maxwell, January 5, 1983 12:47 pm -- ***** Slice support routines -- -- before[0]=root; before[i]=Parent[before[i+1]]; before[before.length-1]=node -- after[i]=Next[before[i]] -- if node.child # NIL then after[before.length]=node.child -- -- -- new[i]=first[i] for i in [0..firstLen) -- new[i+firstLen]=last[i] for i in [0..last.length) -- new.length=last.length+firstLen -- -- -- join slices -- make after[afterStart+i] be successor of before[beforeStart+i] -- if more after's than before's, adopt as children of last before -- -- -- do Splices to insert (top-bottom) between (before-after) -- nesting tells how to offset last of before vs. last of top -- before[before.length-1+nesting] will be predecessor of top[top.length-1] -- -- -- top[top.length-1] = first -- before[before.length-1+nesting] = predecessor of first -- bottom[bottom.length-1] = last -- raises BadBand error if last doesn't follow first in tree structure -- or if first or last is root node -- -- -- check assertions -- -- remove entries from start of slice -- -- -- where = after means insert starting as sibling after dest -- where = child means insert starting as child of dest -- where = before means insert starting as sibling before dest -- -- create tree of parents -- go to next node -- -- determines relative order in tree of last nodes in the slices -- returns "same" if slices are identical -- returns "before" if last node of s1 comes before last node of s2 -- returns "after" if last node of s1 comes after last node of s2 -- returns "disjoint" if slices are not from the same tree -- -- result is same as MakeSlices[node].before -- split off head or tail sections of text -- returns true if char before destStart is a letter/digit -- returns true if char after destEnd is a letter/digit -- ***** Miscellaneous ʘJšÏc™Jš$™$Jš6™6JšÏk1™1J˜Jšž ˜ J˜ J˜J˜ J˜ J˜ J˜ J˜šœž œ˜#JšžœC˜JJšžœ˜#—Jšž˜Jšžœ˜J˜Jš™J˜Jšœ˜(Jšœžœ˜Jšœžœ˜Jšœžœ˜J˜š Ïnœžœžœžœžœžœ˜Ašžœ žœžœ˜.J˜ šžœžœ˜@J˜.Jšœ žœ˜Jšœ!žœ˜*—J˜šžœ#žœžœž˜4šžœžœ˜-Jšœžœ˜!Jšœ!žœ˜.—J˜ Jšžœ˜ ——Jšžœžœ žœ˜AJ˜—šŸ œžœžœžœ˜/šžœžœžœ žœ˜PJšžœžœ˜ —Jš žœžœžœžœ žœžœ˜:J˜*J˜"J˜—šŸ œžœ žœ˜?Jš™JšN™NJš™Jš;™;Jš™Jšœžœ˜ š Ÿœžœžœžœ žœ˜>J˜šžœžœžœ˜+J˜0J˜/Jšžœ˜ —J˜-J˜J˜#Jšžœ ˜—Jšžœžœžœžœ˜J˜J˜Jšžœžœžœ4˜LJšžœ˜šžœžœžœ#˜EJšžœžœžœ˜'Jšžœžœžœžœ˜4Jšžœ˜ J˜——šŸ œžœ žœžœ˜OJš™Jš)™)Jš4™4Jš"™"Jš™Jšœžœ˜ š žœ žœžœžœžœžœž˜LJšœžœžœ˜#—J˜0J˜'Jš žœžœžœžœžœ˜:Jš žœžœžœžœžœ˜GJ˜—š Ÿœžœžœ.žœ žœ˜`Jšžœ˜!Jšœžœ˜Jšœžœ˜Jšœ žœ ˜J˜,J˜1Jšžœžœžœ ˜?J˜2Jšžœžœžœ˜*Jšžœ˜J˜—šŸœžœžœ1žœ ˜PJš™Jš™JšA™AJšB™BJš™J˜ Jšœžœ˜J˜(J˜%Jšžœžœžœžœ˜9Jšžœžœžœ˜AJ˜šžœžœ˜2J˜!Jšžœžœžœžœ#˜D—Jšžœ žœ˜Jšžœ žœžœ˜šžœžœžœ˜1J˜š žœžœžœžœ˜FJšžœžœ.žœ˜B—šžœ˜Jšžœžœžœ˜Jšžœžœ8˜EJšœžœ˜—Jšžœžœžœ˜Jšžœ˜ J˜——šŸ œžœžœ.žœ˜`Jš™Jš;™;Jš=™=JšK™KJš™Jšœžœ˜ J˜Jšœ žœ ˜Jšžœ%žœžœ˜2Jšœžœ%˜0J˜/J˜J˜J˜J˜—Jšœ žœžœžœ˜šŸ œžœžœ˜,Jšžœ.žœ žœ˜MJš™Jš™Jš:™:Jš!™!JšF™FJš#™#Jš™šŸ œžœ˜šžœ žœ˜J˜$J˜(——J˜)Jšœ žœ˜Jš žœžœžœžœ ˜5Jšžœ žœžœ )˜JJ˜ J˜#J˜"Jšœ žœ˜,šžœžœž˜1šžœžœ˜=J˜šžœ0ž˜5šžœž˜Jšœžœ˜ Jšžœ˜5Jšžœ˜—Jšžœ˜—Jšžœ˜—Jšžœ˜—Jšžœ žœ*˜JJš™Jš™Jšžœžœžœ˜'Jšžœ:žœžœ˜GJšžœžœžœ˜+J˜—šŸ œžœžœžœ˜8Jš™Jš%™%Jš™Jšœžœ˜ Jšžœžœžœ˜#J˜Jš žœžœžœ žœžœ˜?Jš žœžœžœžœ žœžœ˜?J˜J˜—šŸ œžœžœ˜1Jšžœ!žœ˜4Jš™Jš<™™>Jš™Jšžœž˜J˜J˜šžœžœžœ˜+J˜.Jšžœ˜ —J˜-J˜Jšžœ ˜—Jšžœžœžœžœ˜J˜ J˜—šŸœžœžœ#žœ˜PJš+™+J˜JšŸ œžœ ˜/˜šŸœžœžœ˜;J˜Jšžœžœžœ˜%šžœž˜Jšœžœ˜Jšœ žœ˜"Jšžœžœ˜)——J˜ Jšžœžœžœžœ˜J˜5J˜"J˜J˜ J˜J˜—Jšžœ žœ/˜UJšžœžœ-˜RJšžœžœ-˜QJšžœžœ+˜NJšžœ˜J˜—šŸ œžœžœ˜5Jšœžœ˜-Jšžœžœ˜,Jšœ˜J˜.J˜4Jšœ˜šžœžœ˜0Jšœ9˜9—Jšžœ%˜+J˜—š Ÿœžœžœ5žœžœ˜YJšžœ˜J˜šŸœžœžœ˜3J˜šžœžœžœž˜6Jšœžœ˜Jšžœžœ˜/—Jšžœ ˜—Jšžœžœ%˜1J˜.J˜"J˜J˜ J˜Jšžœ˜J˜—šŸ œžœžœ#žœ˜Ršžœ ž˜&J˜:—šžœž˜%J˜9—šžœž˜$Jšœ7žœ˜=—šžœž˜#Jšœ6žœ˜<—Jšžœ˜J˜—šŸ œžœžœ-˜EJšžœ˜J˜.J˜6Jšžœ˜J˜—šŸ œžœžœ?˜VJšžœ žœ8˜Qšžœ*˜Gšžœ+˜GJšžœ žœžœ˜!——J˜0J˜.šœžœ ž˜J˜,J˜J˜+Jšžœžœ˜—šœžœ ž˜J˜+J˜J˜*Jšžœžœ˜—Jšœ žœ˜J˜—šŸ œžœžœ"˜;Jšžœ˜%J˜JšŸœžœ˜˜šŸœžœ:žœžœ˜eJšžœžœžœ˜%šžœžœ ž˜J˜J˜Jšžœ"˜)J˜——šŸ œžœ'˜8J˜AJ˜0J˜,J˜.J˜,J˜—šŸ œžœ'˜8Jšœ;žœ˜OJ˜/J˜+J˜-J˜+J˜—J˜ J˜Jšœ4žœ˜