Move:
PUBLIC
PROC [destRoot, sourceRoot: RefBranchNode, dest: TreeLoc, source: TreeSpan,
where: Place ← after, nesting: INTEGER ← 0, event: Event ← NIL]
RETURNS [result: TreeSpan] = {
dest cannot be within source
result is moved span
sBefore, sAfter, sTop, sBottom, dBefore, dAfter: Slice;
sNesting, dNesting: INTEGER;
sDepth: NAT;
beforeSource, afterSource: RefBranchNode; -- nodes adjacent to source after do splits
afterLoc: TreeLoc;
d: BranchLoc;
s: BranchSpan;
FreeSlices:
PROC = {
FreeSlice[sBefore]; FreeSlice[sAfter]; FreeSlice[sTop]; FreeSlice[sBottom];
FreeSlice[dBefore]; FreeSlice[dAfter];
sBefore ← sAfter ← sTop ← sBottom ← dBefore ← dAfter ← NIL };
ForcedUnNest:
PROC [after: Ref] = {
OPEN TiogaTreeOps;
afterNode: RefBranchNode = TiogaNodeOps.NarrowToBranchNode[after];
span: TreeSpan = MakeTreeSpan[afterNode, LastBranchWithin[afterNode]];
IF afterNode=NIL THEN ERROR;
FreeSlices[];
[] ← UnNest[Root[afterNode], span, event] };
ForcedNest:
PROC = {
OPEN TiogaTreeOps;
bottom: RefBranchNode = TiogaNodeOps.NarrowToBranchNode[sBottom[sDepth]];
span: TreeSpan ← MakeTreeSpan[sTop[sDepth], LastBranchWithin[bottom]];
IF bottom=NIL THEN ERROR;
IF TreeSlice.SliceLength[sTop] = sDepth+1
THEN {
-- can't do it by calling Nest
IF nesting >= 1 THEN ERROR;
nesting ← nesting+1; -- move to a deeper position
FreeSlices[];
RETURN };
FreeSlices[];
[] ← Nest[Root[span.start.node], span, event] };
IF CheckForNil[source] THEN RETURN [nullSpan];
IF TextLocationAndSpan[dest, source]
THEN
-- pure text move
RETURN [DestSpanText[destRoot, sourceRoot, dest, source, TextEdit.MoveText, event]];
IF where=child THEN { where ← after; nesting ← nesting+1 }
ELSE
IF where=sibling
THEN {
parentBr, childBr: RefBranchNode;
parentBr ← TiogaTreeOps.ContainingStatement[dest.node];
childBr ← TiogaTreeOps.LastBranchWithin[parentBr];
dest ← [childBr, NodeItself];
where ← after;
nesting ← nesting + TiogaTreeOps.Level[parentBr] - TiogaTreeOps.Level[childBr] };
split source and dest, if necessary, so can deal with entire nodes
[d, s] ← DoSplitsForMove[dest, source, where, event];
beforeSource ← TiogaTreeOps.Backward[s.start.node].back;
afterSource ← TiogaTreeOps.Forward[s.end.node].nx;
afterLoc ← [afterSource, NodeItself];
{ -- for exits
check for d already in correct position
IF d # TiogaTreeOps.nullBranchLoc
AND
((where = after
AND (d.node = beforeSource
OR d.node = s.end.node))
OR (where = before AND (d.node = afterSource OR d.node = s.start.node)))
THEN {
-- not going anywhere, but might be changing nesting
IF nesting > 0
THEN
FOR i:
INTEGER
IN [0..nesting)
DO
[] ← Nest[sourceRoot, NodeSpan[s], event];
ENDLOOP
ELSE
IF nesting < 0
THEN
FOR i:
INTEGER
IN [nesting..0)
DO
[] ← UnNest[sourceRoot, NodeSpan[s], event];
ENDLOOP }
ELSE
DO
-- repeat this loop only if have forced nest or unnest or s and d in same tree
[sBefore, sAfter, sTop, sBottom, sNesting, sDepth] ←
DescribeBand[s.start.node, s.end.node ! BadBand => { s ← nullBranchSpan; GOTO ErrorReturn } ];
IF d = TiogaTreeOps.nullBranchLoc
THEN {
-- moving to limbo
d ← CreateDest[TreeSlice.SliceLength[sTop]-sDepth];
destRoot ← TiogaTreeOps.Root[d.node];
destRoot.deleted ← TRUE; -- so will free this when it falls off the edit history list
where ← after; nesting ← 1 };
[dBefore, dAfter, dNesting] ← DestSlices[d.node, where];
IF TreeSlice.CompareSliceOrder[dBefore, sBefore]=after
AND
TreeSlice.CompareSliceOrder[dBefore, sBottom]=before THEN GOTO ErrorReturn; -- d inside s
IF ~TiogaNodeOps.IsBranch[dBefore[0]]
AND BadDest[dBefore[0], sBefore, sBottom]
THEN GOTO ErrorReturn;
Dest is inside a list or box node. Check to see if statement is inside s
IF dBefore[0] = sBefore[0]
THEN {
-- s and d in same tree; too hard, so move s out first.
span: TreeSpan = NodeSpan[s];
FreeSlices[];
[] ← MoveToLimbo[sourceRoot, span, event];
LOOP };
dNesting ← dNesting + nesting;
SELECT NeedNestingChange[dBefore, dAfter, sTop, sBottom, dNesting, sDepth]
FROM
needUnNest => { ForcedUnNest[TreeSlice.LastOfSlice[dAfter]]; LOOP };
needNest => { ForcedNest[]; LOOP };
ENDCASE;
IF TreeSlice.SliceLength[sAfter] > TreeSlice.SliceLength[sBefore]+1
THEN {
ForcedUnNest[TreeSlice.LastOfSlice[sAfter]]; LOOP }
ELSE {
-- do it
notify: REF MovingNodes Change;
notify ←
NEW[MovingNodes Change ← [MovingNodes[
destRoot, sourceRoot, d.node, s.start.node, s.end.node,
TreeSlice.LastOfSlice[sBefore], sNesting, (where # before)]]];
Notify[notify, before];
DeletePrefix[sTop, sDepth]; DeletePrefix[sBottom, sDepth];
ReplaceBand[dBefore, dAfter, sTop, sBottom, dNesting, event];
Splice[sBefore, sAfter];
FreeSlices[];
Notify[notify, after];
UndoEvent.Note[event, UndoMoveNodes, notify] };
EXIT;
ENDLOOP;
IF d.where # NodeItself
THEN {
-- undo prior splits
start, end: BOOLEAN ← FALSE;
IF s.start.where # NodeItself
THEN {
-- merge start of s with front of dest
start ← TRUE;
[s, ] ← ReMerge[s, nullBranchSpan, s.start.node, event] };
IF s.end.where # NodeItself
THEN {
-- merge end of s with tail of dest
end ← TRUE;
[s, ] ← ReMerge[s, nullBranchSpan, s.end.node, event, TRUE] };
IF start
AND end
THEN {
-- merge before s with after s
afterLoc ← JoinStatements[TiogaTreeOps.Root[afterSource], afterSource, event] }}
ELSE
IF s.start.where # NodeItself
AND s.end.where # NodeItself
THEN {
afterLoc ← JoinStatements[TiogaTreeOps.Root[afterSource], afterSource, event] };
afterMoved2 ← afterMoved1; -- save previous hint
afterMoved1 ← IF afterSource=NIL THEN [beforeSource, 0] ELSE afterLoc; -- hint for repaint
source.start.node ← s.start.node; source.end.node ← s.end.node;
source.start.where ← source.end.where ← NodeItself;
RETURN [source];
EXITS ErrorReturn => {
FreeSlices[];
UndoSplitsForMove[d, s, where, event];
ERROR CannotDoEdit }}};
UndoMoveNodes:
PROC [undoRef:
REF, currentEvent: Event] =
TRUSTED {
saved: REF Change ← NARROW[undoRef];
WITH x:saved SELECT FROM
MovingNodes => {
[] ← Move[x.sourceRoot, x.destRoot,
TiogaTreeOps.MakeTreeLoc[x.pred], TiogaTreeOps.MakeTreeSpan[x.first, x.last],
after, x.nesting, currentEvent] };
ENDCASE => ERROR };
BadDest:
PROC [top: Ref, sBefore, sBottom: Slice]
RETURNS [error:
BOOL] = {
DO
-- check for dest inside source
s: RefBranchNode ← TiogaTreeOps.ContainingStatement[top];
before, after: Slice;
[before, after] ← MakeSlices[s];
top ← before[0];
error
← TreeSlice.CompareSliceOrder[before, sBefore]=after
AND
TreeSlice.CompareSliceOrder[before, sBottom]=before;
FreeSlice[before]; FreeSlice[after];
IF error THEN RETURN;
IF TiogaNodeOps.IsBranch[top] THEN RETURN;
ENDLOOP };
Transpose:
PUBLIC
PROC [alphaRoot, betaRoot: RefBranchNode, alpha, beta: TreeSpan, event: Event ←
NIL]
RETURNS [newAlpha, newBeta: TreeSpan] = {
newAlpha is new location of alpha span; ditto for newBeta
aBefore, aAfter, aTop, aBottom, bBefore, bAfter, bTop, bBottom: Slice;
aNesting, bNesting: INTEGER;
aDepth, bDepth: NAT;
beforeAlpha, afterAlpha, beforeBeta, afterBeta: RefBranchNode; -- nodes adjacent after do splits
afterAlphaLoc, afterBetaLoc: TreeLoc;
a, b: BranchSpan;
FreeSlices:
PROC = {
FreeSlice[aBefore]; FreeSlice[aAfter]; FreeSlice[aTop]; FreeSlice[aBottom];
FreeSlice[bBefore]; FreeSlice[bAfter]; FreeSlice[bTop]; FreeSlice[bBottom];
aBefore ← aAfter ← aTop ← aBottom ← bBefore ← bAfter ← bTop ← bBottom ← NIL };
{ -- for exit
IF CheckForNil[alpha]
OR CheckForNil[beta]
THEN
RETURN [nullSpan, nullSpan];
IF TextSpans[alpha, beta]
THEN {
-- pure text transpose
alphaNode: RefTextNode ← TiogaNodeOps.NarrowToTextNode[alpha.start.node];
betaNode: RefTextNode ← TiogaNodeOps.NarrowToTextNode[beta.start.node];
alphaStart: Offset ← alpha.start.where;
alphaLen: Offset ← alpha.end.where - alphaStart + 1;
betaStart: Offset ← beta.start.where;
betaLen: Offset ← beta.end.where - betaStart + 1;
alphaResultStart, alphaResultLen, betaResultStart, betaResultLen: Offset;
[alphaResultStart, alphaResultLen, betaResultStart, betaResultLen] ← TextEdit.TransposeText[
alphaRoot, betaRoot, alphaNode, alphaStart, alphaLen, betaNode, betaStart, betaLen, event];
newAlpha ← [[betaNode, alphaResultStart], [betaNode, alphaResultStart+alphaResultLen-1]];
newBeta ← [[alphaNode, betaResultStart], [alphaNode, betaResultStart+betaResultLen-1]];
RETURN };
[a, b] ← DoSplits[alpha, beta, event]; -- so can deal with entire nodes
beforeAlpha ← TiogaTreeOps.Backward[a.start.node].back;
afterAlpha ← TiogaTreeOps.Forward[a.end.node].nx;
afterAlphaLoc ← [afterAlpha, 0];
beforeBeta ← TiogaTreeOps.Backward[b.start.node].back;
afterBeta ← TiogaTreeOps.Forward[b.end.node].nx;
afterBetaLoc ← [afterBeta, 0];
now check for a b adjacent or overlapping as special cases
IF afterAlpha = b.start.node
THEN {
-- a just before b
move b nodes to before a nodes
[] ← Move[alphaRoot, betaRoot, NodeLoc[a.start], NodeSpan[b], before, 0, event] }
ELSE
IF afterBeta = a.start.node
THEN {
-- b just before a
move a nodes to before b nodes
[] ← Move[betaRoot, alphaRoot, NodeLoc[b.start], NodeSpan[a], before, 0, event] }
ELSE {
-- get slices describing the bands of nodes to be transposed
overlap: BOOLEAN;
head, tail: BranchSpan; -- sections of a or b before and after the overlap
startOrder, endOrder: Order;
[aBefore, aAfter, aTop, aBottom, aNesting, aDepth] ←
DescribeBand[a.start.node, a.end.node ! BadBand =>
{ a ← b ← nullBranchSpan; GOTO ErrorReturn }];
[bBefore, bAfter, bTop, bBottom, bNesting, bDepth] ←
DescribeBand[b.start.node, b.end.node ! BadBand =>
{ a ← b ← nullBranchSpan; GOTO ErrorReturn }];
IF ~TiogaNodeOps.IsBranch[aBefore[0]]
AND BadDest[aBefore[0], bBefore, bBottom]
THEN GOTO ErrorReturn; -- a inside statement inside b
IF ~TiogaNodeOps.IsBranch[bBefore[0]]
AND BadDest[bBefore[0], aBefore, aBottom]
THEN GOTO ErrorReturn; -- b inside statement inside a
check for overlap
[overlap, head, tail, startOrder, endOrder] ←
SliceOrder[a, b, aBefore, aBottom, bBefore, bBottom];
IF overlap
THEN {
-- bands overlap
FreeSlices[];
IF head = nullBranchSpan AND tail = nullBranchSpan THEN NULL
ELSE
IF head = nullBranchSpan
THEN {
--move tail to before alphastart
[] ← Move[alphaRoot, betaRoot, NodeLoc[a.start], NodeSpan[tail], before, 0, event];
IF endOrder=before
THEN {
-- a end before b end
b.start ← tail.start; b.end ← a.end }
ELSE {
-- b end before a end
a.start ← tail.start; a.end ← b.end }}
ELSE
IF tail = nullBranchSpan
THEN {
--move head to after alphaend
[] ← Move[alphaRoot, betaRoot, NodeLoc[a.end], NodeSpan[head], after, 0, event];
IF startOrder=before
THEN {
-- a start before b start
a.start ← b.start; a.end ← head.end }
ELSE {
-- b start before a start
b.start ← a.start; b.end ← head.end }}
ELSE IF startOrder # endOrder THEN NULL -- one contained in the other
ELSE {
--transpose head and tail
[] ← Transpose[alphaRoot, betaRoot, NodeSpan[head], NodeSpan[tail], event];
IF startOrder=before
THEN {
-- a start before b start
a.start ← b.start; a.end ← head.end;
b.start ← tail.start; b.end ← a.end }
ELSE {
-- b start before a start
b.start ← a.start; b.end ← head.end;
a.start ← tail.start; a.end ← b.end }}}
ELSE {
-- do transpose as two moves
aSpan, bSpan: TreeSpan;
after1, after2: TreeLoc;
bLoc: TreeLoc ← TiogaTreeOps.MakeTreeLoc[TreeSlice.LastOfSlice[bBefore]];
aLoc: TreeLoc ← TiogaTreeOps.MakeTreeLoc[TreeSlice.LastOfSlice[aBefore]];
FreeSlices[];
aSpan ← TiogaTreeOps.MakeTreeSpan[a.start.node, a.end.node];
bSpan ← TiogaTreeOps.MakeTreeSpan[b.start.node, b.end.node];
[] ← MoveToLimbo[alphaRoot, aSpan, event]; after1 ← afterMoved1; -- repaint hints
[] ← MoveToLimbo[betaRoot, bSpan, event]; after2 ← afterMoved1;
[] ← Move[betaRoot, TiogaTreeOps.Root[aSpan.start.node],
bLoc, aSpan, after, bNesting, event];
[] ← Move[alphaRoot, TiogaTreeOps.Root[bSpan.start.node],
aLoc, bSpan, after, aNesting, event];
afterMoved1 ← after1; afterMoved2 ← after2 }};
IF a.start.where # NodeItself
AND b.start.where # NodeItself
THEN {
-- remerge starts
[a, b] ← ReMerge[a, b, a.start.node, event];
[a, b] ← ReMerge[a, b, b.start.node, event] };
IF a.end.where # NodeItself
AND b.end.where # NodeItself
THEN {
-- remerge ends
[a, b] ← ReMerge[a, b, a.end.node, event, TRUE];
[a, b] ← ReMerge[a, b, b.end.node, event, TRUE];
afterAlphaLoc.node ← b.end.node; afterBetaLoc.node ← a.end.node };
afterMoved1 ← IF afterAlphaLoc.node=NIL THEN [beforeAlpha, 0] ELSE afterAlphaLoc; -- hint for repaint
afterMoved2 ← IF afterBetaLoc.node=NIL THEN [beforeBeta, 0] ELSE afterBetaLoc;
alpha.start ← [a.start.node, NodeItself];
alpha.end ← [a.end.node, NodeItself];
beta.start ← [b.start.node, NodeItself];
beta.end ← [b.end.node, NodeItself];
RETURN [alpha, beta];
EXITS ErrorReturn => {
FreeSlices[]; UndoSplits[a, b, event];
ERROR CannotDoEdit }}};
MoveOnto:
PUBLIC
PROC [destRoot, sourceRoot: RefBranchNode, dest, source: TreeSpan,
saveForPaste: BOOLEAN ← TRUE, event: Event ← NIL]
RETURNS [result: TreeSpan] = {
like Replace, but moves source instead of copying it
result is moved span
overlap: BOOLEAN;
newDest: TreeSpan;
head, tail: BranchSpan; -- sections of alpha or beta before and after the overlap
startOrder, endOrder: Order;
aBefore, aAfter, aTop, aBottom, bBefore, bAfter, bTop, bBottom: Slice;
aNesting, bNesting: INTEGER;
aDepth, bDepth: NAT;
d, s: BranchSpan;
FreeSlices:
PROC = {
FreeSlice[aBefore]; FreeSlice[aAfter]; FreeSlice[aTop]; FreeSlice[aBottom];
FreeSlice[bBefore]; FreeSlice[bAfter]; FreeSlice[bTop]; FreeSlice[bBottom];
aBefore ← aAfter ← aTop ← aBottom ← bBefore ← bAfter ← bTop ← bBottom ← NIL };
{ -- for exit
IF CheckForNil[source] OR CheckForNil[dest] THEN RETURN [nullSpan];
IF TextSpans[dest, source]
THEN {
-- pure text move
RETURN [TwoSpanText[destRoot, sourceRoot, dest, source, saveForPaste, TextEdit.MoveTextOnto, event]] };
[d, s] ← DoSplits[dest, source, event];
get slices for d and s
[aBefore, aAfter, aTop, aBottom, aNesting, aDepth] ←
DescribeBand[d.start.node, d.end.node ! BadBand => { d ← s ← nullBranchSpan; GOTO ErrorReturn }];
[bBefore, bAfter, bTop, bBottom, bNesting, bDepth] ←
DescribeBand[s.start.node, s.end.node ! BadBand => { d ← s ← nullBranchSpan;
GOTO ErrorReturn }];
IF ~TiogaNodeOps.IsBranch[aBefore[0]]
AND BadDest[aBefore[0], bBefore, bBottom]
THEN GOTO ErrorReturn; -- d inside statement inside s
[overlap, head, tail, startOrder, endOrder] ← SliceOrder[d, s, aBefore, aBottom, bBefore, bBottom];
FreeSlices[];
check for overlap
IF overlap
THEN {
-- bands overlap. modify d so doesn't overlap
IF head = nullBranchSpan AND tail = nullBranchSpan THEN GOTO ErrorReturn;
IF head = nullBranchSpan
THEN {
-- s start = d start
IF endOrder=before THEN GOTO ErrorReturn -- d end before s end
ELSE d.start ← tail.start }
ELSE
IF tail = nullBranchSpan
THEN {
--s end = d end
IF startOrder=before THEN d.end ← head.end -- d start before s start
ELSE GOTO ErrorReturn } -- s start before d start
ELSE {
-- have both head and tail
IF startOrder=before
AND endOrder=after
THEN {
[] ← Delete[destRoot, NodeSpan[tail], event]; d.end ← head.end }
ELSE IF startOrder=before THEN d.end ← head.end
ELSE IF endOrder=after THEN d.start ← tail.start
ELSE GOTO ErrorReturn }};
source ← MoveToLimbo[sourceRoot, NodeSpan[s], event];
sourceRoot ← TiogaTreeOps.Root[source.start.node];
[result, newDest] ← Transpose[sourceRoot, destRoot, source, NodeSpan[d], event];
IF saveForPaste THEN SaveSpanForPaste[newDest, event];
RETURN;
EXITS ErrorReturn => { UndoSplits[d, s, event];
ERROR CannotDoEdit }}};
Replace:
PUBLIC
PROC [
destRoot, sourceRoot: RefBranchNode, dest, source: TreeSpan,
saveForPaste: BOOL ← TRUE, event: Event ← NIL]
RETURNS [result: TreeSpan] = {
replace dest span by copy of source span
result is the new copy of source
newDest: TreeSpan;
IF TextSpans[dest, source]
THEN {
-- pure text replace
RETURN [TwoSpanText[destRoot, sourceRoot, dest, source, saveForPaste, TextEdit.ReplaceText, event]] };
source ← CopySpan[source];
sourceRoot ← TiogaTreeOps.Root[source.start.node];
sourceRoot.deleted ← TRUE;
[result, newDest] ← Transpose[sourceRoot, destRoot, source, dest, event];
IF saveForPaste THEN SaveSpanForPaste[newDest, event] };
TextSpans:
PROC [dest, source: TreeSpan]
RETURNS [
BOOLEAN] = {
RETURN [dest.start.node = dest.end.node
AND
dest.start.where # NodeItself AND dest.end.where # NodeItself AND
TiogaNodeOps.IsText[dest.start.node] AND
source.start.node = source.end.node AND
source.start.where # NodeItself AND source.end.where # NodeItself AND
TiogaNodeOps.IsText[source.start.node]] };
TwoSpanText:
PROC [destRoot, sourceRoot: RefBranchNode, dest, source: TreeSpan,
saveForPaste: BOOLEAN, textProc: TextEdit.TwoSpanProc, event: Event]
RETURNS [result: TreeSpan] = {
destNode: RefTextNode ← TiogaNodeOps.NarrowToTextNode[dest.start.node];
sourceNode: RefTextNode ← TiogaNodeOps.NarrowToTextNode[source.start.node];
destStart: Offset ← dest.start.where;
destLen: Offset ← dest.end.where - destStart + 1;
sourceStart: Offset ← source.start.where;
sourceLen: Offset ← source.end.where - sourceStart + 1;
resultStart, resultLen: Offset;
IF saveForPaste THEN SaveTextForPaste[destNode, destStart, destLen, event];
[resultStart, resultLen] ← textProc[destRoot, sourceRoot,
destNode, destStart, destLen, sourceNode, sourceStart, sourceLen, event];
RETURN [[[destNode, resultStart], [destNode, resultStart+resultLen-1]]] };
SaveForPaste:
PUBLIC
PROC [span: TreeSpan, event: Event ←
NIL] = {
node: RefTextNode;
IF CheckForNil[span] THEN RETURN;
IF span.start.node = span.end.node
AND
span.start.where # NodeItself AND span.end.where # NodeItself AND
(node ← TiogaNodeOps.NarrowToTextNode[span.start.node]) #
NIL
THEN {
-- pure text
start: Offset ← span.start.where;
len: Offset ← span.end.where-span.start.where+1;
SaveTextForPaste[node, start, len, event] }
ELSE SaveSpanForPaste[CopySpan[span], event] };
Delete:
PUBLIC
PROC [
root: RefBranchNode, del: TreeSpan, event: Event ← NIL, saveForPaste: BOOL ← TRUE] = {
node: RefTextNode;
IF CheckForNil[del] THEN RETURN;
IF del.start.node = del.end.node
AND
del.start.where # NodeItself
AND del.end.where # NodeItself
AND
(node ← TiogaNodeOps.NarrowToTextNode[del.start.node]) # NIL THEN { -- pure text
start: Offset ← del.start.where;
len: Offset ← del.end.where-del.start.where+1;
IF saveForPaste THEN SaveTextForPaste[node, start, len, event];
TextEdit.DeleteText[root, node, start, len, event] }
ELSE {
d: TreeSpan ← MoveToLimbo[root, del, event];
IF saveForPaste THEN SaveSpanForPaste[d, event] }};
paste: REF Change;
--really REF ChangingTextForPaste/ChangingSpanForPaste Change
SaveTextForPaste:
PROC [node: RefTextNode, start, len: Offset, event: Event] = {
SaveOldPaste[event];
paste ←
NEW[ChangingTextForPaste Change ←
[ChangingTextForPaste[node.rope, node.runs, start, len]]] };
SaveSpanForPaste:
PROC [span: TreeSpan, event: Event] = {
SaveOldPaste[event];
paste ← NEW[ChangingSpanForPaste Change ← [ChangingSpanForPaste[span]]] };
RecordGroupForPaste:
PUBLIC PROC [start, end: Ref, event: Event] = {
span: TreeSpan;
span.start ← [start, NodeItself];
span.end ← [TiogaTreeOps.LastWithin[end], NodeItself];
SaveSpanForPaste[span, event] };
SaveOldPaste:
PROC [event: Event] = {
UndoEvent.Note[event, RestorePaste, paste]; paste ← NIL };
RestorePaste:
PROC [undoRef:
REF Change, currentEvent: UndoEvent.Ref] = {
SaveOldPaste[currentEvent]; paste ← undoRef };
SavedForPaste:
PUBLIC
PROC
RETURNS [span: TreeSpan] =
TRUSTED {
savedPaste: REF Change ← paste;
IF savedPaste=NIL THEN RETURN [nullSpan];
WITH x: savedPaste SELECT FROM
ChangingTextForPaste => {
-- convert saved text to a span
node: RefTextNode;
root: RefBranchNode;
span: TreeSpan;
IF x.len <= 0 THEN RETURN [nullSpan];
node ← TiogaNodeOps.NewTextNode[TiogaNode.defaultTextClassID];
root ← TiogaNodeOps.DocFromNode[node];
[] ← TextEdit.ReplaceByText[root: root, dest: node,
sourceRope: x.rope, sourceRuns: x.runs,
sourceStart: x.start, sourceLen: x.len];
span ← [[node, 0], [node, x.len-1]];
paste ← NEW[ChangingSpanForPaste Change ← [ChangingSpanForPaste[span]]];
RETURN [span] };
ChangingSpanForPaste => RETURN [x.span];
ENDCASE => ERROR;
};
MoveToLimbo:
PROC [root: RefBranchNode, span: TreeSpan, event: Event]
RETURNS [result: TreeSpan] =
INLINE { RETURN [Move[NIL, root, TiogaTreeOps.nullLoc, span, after, 1, event]] };
Copy:
PUBLIC
PROC [
destRoot, sourceRoot: RefBranchNode, dest: TreeLoc, source: TreeSpan,
where: Place ← after, nesting: INTEGER ← 0, event: Event ← NIL]
RETURNS [result: TreeSpan] = {
result is the new copy of source
IF CheckForNil[source] OR dest.node=NIL THEN RETURN [nullSpan];
IF TextLocationAndSpan[dest, source]
THEN
-- pure text copy
RETURN [DestSpanText[destRoot, sourceRoot, dest, source, TextEdit.CopyText, event]];
source ← CopySpan[source];
sourceRoot ← TiogaTreeOps.Root[source.start.node];
sourceRoot.deleted ← TRUE;
result ← Move[destRoot, sourceRoot, dest, source, where, nesting, event] };
TextLocationAndSpan:
PROC [dest: TreeLoc, source: TreeSpan]
RETURNS [
BOOLEAN] = {
RETURN [dest.where # NodeItself
AND
source.start.node = source.end.node
AND
source.start.where # NodeItself
AND source.end.where # NodeItself
AND TiogaNodeOps.NarrowToTextNode[source.start.node] # NIL] };
DestSpanText:
PROC [
destRoot, sourceRoot: RefBranchNode, dest: TreeLoc, source: TreeSpan,
textProc: TextEdit.DestSpanProc, event: Event]
RETURNS [result: TreeSpan] = {
destNode: RefTextNode ← TiogaNodeOps.NarrowToTextNode[dest.node];
sourceNode: RefTextNode ← TiogaNodeOps.NarrowToTextNode[source.start.node];
destLoc: Offset ← dest.where;
sourceStart: Offset ← source.start.where;
sourceLen: Offset ← source.end.where - sourceStart + 1;
resultStart, resultLen: Offset;
[resultStart, resultLen] ← textProc[destRoot, sourceRoot,
destNode, destLoc, sourceNode, sourceStart, sourceLen, event];
RETURN [[[destNode, resultStart], [destNode, resultStart+resultLen-1]]] };