-- WordEditImpl.mesa
-- written by Bill Paxton, March 1981
-- last edit by Bill Paxton, May 5, 1982 8:27 am
DIRECTORY
TextEdit,
RopeEdit,
TextNode;
WordEditImpl: CEDAR PROGRAM
IMPORTS TextEdit, RopeEdit, TextNode
EXPORTS TextEdit =
BEGIN
OPEN TextEdit;
-- ***** Word Editing Operations ****
ReplaceWords: PUBLIC PROC [
destRoot, sourceRoot: Ref,
dest: RefTextNode, destStart: Offset ← 0, destLen: Offset ← MaxLen,
source: RefTextNode, sourceStart: Offset ← 0, sourceLen: Offset ← MaxLen,
event: Event]
RETURNS [resultStart, resultLen: Offset] = {
-- replace the dest words by a copy of the source words
wantLeading, hasLeading, wantTrailing, hasTrailing: BOOLEAN;
destSize, sourceSize: Offset;
IF source # NIL THEN {
sourceSize ← Size[source];
sourceStart ← MIN[MAX[0,sourceStart],sourceSize];
sourceLen ← MIN[MAX[0,sourceLen],sourceSize-sourceStart] }
ELSE sourceSize ← sourceStart ← sourceLen ← 0;
destSize ← IF dest=source THEN sourceSize ELSE Size[dest];
destStart ← MIN[MAX[0,destStart],destSize];
destLen ← MIN[MAX[0,destLen],destSize-destStart];
IF destLen=0 AND sourceLen=0 THEN RETURN [destStart,0];
[wantLeading,hasLeading,wantTrailing,hasTrailing] ←
CheckSpaces[dest,destStart,destLen,destSize,source,sourceStart,sourceLen];
IF hasTrailing AND ~wantTrailing THEN { -- get rid of trailing space
sourceLen ← sourceLen-1; hasTrailing ← FALSE };
IF hasLeading AND ~wantLeading THEN { -- get rid of leading space
sourceStart ← sourceStart+1; sourceLen ← sourceLen-1; hasLeading ← FALSE };
[] ← ReplaceText[destRoot,sourceRoot,
dest,destStart,destLen,source,sourceStart,sourceLen,event];
resultStart ← destStart;
resultLen ← sourceLen +
AdjustSpaces[wantLeading,hasLeading,wantTrailing,hasTrailing,
dest,destStart,sourceLen,event];
[resultStart,resultLen] ← AdjustResults[dest,resultStart,resultLen] };
DeleteWords: PUBLIC OneSpanProc =
-- delete the specified words
{ [] ← ReplaceWords[root,NIL,text,start,len,NIL,0,MaxLen,event] };
CopyWords: PUBLIC DestSpanProc =
-- copy the specified words
{ [resultStart,resultLen] ← ReplaceWords[destRoot,sourceRoot,
dest,destLoc,0,source,start,len,event] };
MoveWordsOnto: PUBLIC PROC [
destRoot, sourceRoot: Ref,
dest: RefTextNode, destStart: Offset ← 0, destLen: Offset ← MaxLen,
source: RefTextNode, sourceStart: Offset ← 0, sourceLen: Offset ← MaxLen,
event: Event ← NIL]
RETURNS [resultStart, resultLen: Offset] = {
sourceSize, destSize, start, len, end, destEnd: Offset;
start ← sourceStart; len ← sourceLen;
sourceSize ← Size[source];
start ← MIN[MAX[0,start],sourceSize];
len ← MIN[MAX[0,len],sourceSize-start];
end ← start+len;
destSize ← IF dest=source THEN sourceSize ELSE Size[dest];
destStart ← MIN[MAX[0,destStart],destSize];
destLen ← MIN[MAX[0,destLen],destSize-destStart];
destEnd ← destStart+destLen;
resultStart ← destStart; resultLen ← len;
IF source=dest THEN { -- check for overlapping or adjacent
IF start IN [destStart..destEnd) THEN {
IF end < destEnd THEN [] ← DeleteText[sourceRoot,source,end,destEnd-end,event];
IF start > destStart THEN [] ← DeleteText[sourceRoot,source,destStart,start-destStart,event];
RETURN };
IF end IN (destStart..destEnd) THEN {
[] ← DeleteText[sourceRoot,source,end,destEnd-end,event]; RETURN [start,len] };
IF start <= destStart AND end >= destEnd THEN RETURN [start,len];
IF end=destStart THEN {
[] ← DeleteText[sourceRoot,source,destStart,destLen,event]; RETURN [start,len] };
IF start=destEnd THEN {
[] ← DeleteText[sourceRoot,source,destStart,destLen,event]; RETURN }};
[] ← DeleteText[destRoot,dest,destStart,destLen,event];
IF source=dest AND start > destStart THEN start ← start-destLen;
[resultStart,resultLen] ← MoveWords[destRoot,sourceRoot,dest,destStart,source,start,len,event] };
MoveWords: PUBLIC PROC [
destRoot, sourceRoot: Ref,
dest: RefTextNode, destLoc: Offset ← 0,
source: RefTextNode, start: Offset ← 0, len: Offset ← MaxLen, event: Event]
RETURNS [resultStart, resultLen: Offset] = {
-- move the specified words
wantLeading, hasLeading, wantTrailing, hasTrailing: BOOLEAN;
destSize, sourceSize: Offset;
sourceSize ← Size[source];
start ← MIN[MAX[0,start],sourceSize];
len ← MIN[MAX[0,len],sourceSize-start];
IF len=0 THEN RETURN [destLoc,0];
destSize ← IF source=dest THEN sourceSize ELSE Size[dest];
IF source=dest AND destLoc IN [start..start+len] THEN RETURN [start,len];
[wantLeading,hasLeading,wantTrailing,hasTrailing] ←
CheckSpaces[dest,destLoc,0,destSize,source,start,len];
IF wantTrailing AND ~hasTrailing AND hasLeading AND ~wantLeading AND
start+len < sourceSize AND Fetch[source,start]=Fetch[source,start+len]
THEN { start ← start+1; hasLeading ← FALSE; hasTrailing ← TRUE };
IF wantLeading AND ~hasLeading AND hasTrailing AND ~wantTrailing AND
start > 0 AND Fetch[source,start-1]=Fetch[source,start+len-1]
THEN { start ← start-1; hasLeading ← TRUE; hasTrailing ← FALSE };
[] ← MoveText[destRoot,sourceRoot,dest,destLoc,source,start,len,event];
IF source=dest AND destLoc > start THEN destLoc ← destLoc-len;
resultStart ← destLoc;
resultLen ← len + AdjustSpaces[wantLeading,hasLeading,wantTrailing,hasTrailing,
dest,destLoc,len,event];
[resultStart,resultLen] ← AdjustResults[dest,resultStart,resultLen] };
TransposeWords: PUBLIC PROC [
alphaRoot, betaRoot: Ref,
alpha: RefTextNode, alphaStart: Offset ← 0, alphaLen: Offset ← MaxLen,
beta: RefTextNode, betaStart: Offset ← 0, betaLen: Offset ← MaxLen, event: Event]
RETURNS [alphaResultStart, alphaResultLen, betaResultStart, betaResultLen: Offset] = {
-- transpose the alpha words and the beta words
SwitchResults: PROC = {
start, len: Offset;
start ← betaResultStart; len ← betaResultLen;
betaResultStart ← alphaResultStart; betaResultLen ← alphaResultLen;
alphaResultStart ← start; alphaResultLen ← len };
wantLeadingAlpha, hasLeadingAlpha, wantTrailingAlpha, hasTrailingAlpha: BOOLEAN;
wantLeadingBeta, hasLeadingBeta, wantTrailingBeta, hasTrailingBeta: BOOLEAN;
alphaSize, betaSize: Offset;
alphaCnt, betaCnt: INTEGER;
switched: BOOLEANFALSE;
alphaSize ← Size[alpha];
alphaStart ← MIN[MAX[0,alphaStart],alphaSize];
alphaLen ← MIN[MAX[0,alphaLen],alphaSize-alphaStart];
betaSize ← IF beta=alpha THEN alphaSize ELSE Size[beta];
betaStart ← MIN[MAX[0,betaStart],betaSize];
betaLen ← MIN[MAX[0,betaLen],betaSize-betaStart];
IF alpha=beta THEN {
alphaEnd: Offset;
IF alphaStart > betaStart THEN { -- switch them
start: Offset ← alphaStart;
len: Offset ← alphaLen;
root: Ref ← alphaRoot;
alphaStart ← betaStart; betaStart ← start;
alphaResultLen ← alphaLen ← betaLen;
betaResultLen ← betaLen ← len;
alphaRoot ← betaRoot; betaRoot ← root;
switched ← TRUE };
alphaEnd ← alphaStart+alphaLen;
IF alphaEnd = betaStart THEN { -- turn into a Move instead
[betaResultStart,betaResultLen] ←
MoveWords[alphaRoot,betaRoot,alpha,alphaStart,alpha,betaStart,betaLen,event];
alphaResultStart ← betaStart+betaResultLen-betaLen;
alphaResultLen ← alphaLen;
[alphaResultStart,alphaResultLen] ←
AdjustResults[alpha,alphaResultStart,alphaResultLen];
IF switched THEN SwitchResults;
RETURN };
IF alphaEnd > betaStart THEN { -- overlapping
overlap, alphaHeadLen, betaTailLen: Offset;
alphaHeadLen ← betaStart-alphaStart;
betaTailLen ← betaStart+betaLen-alphaEnd;
IF alphaHeadLen < 0 OR betaTailLen < 0 THEN
RETURN [alphaStart,alphaLen,betaStart,betaLen];
overlap ← alphaEnd-betaStart;
[alphaResultStart,alphaResultLen,betaResultStart,betaResultLen] ←
TransposeWords[alphaRoot,betaRoot,alpha,alphaStart,alphaHeadLen,
alpha,alphaEnd,betaTailLen,event];
betaResultLen ← betaResultLen+overlap;
alphaResultStart ← alphaResultStart-overlap;
alphaResultLen ← alphaResultLen+overlap;
IF switched THEN SwitchResults;
RETURN }};
[wantLeadingAlpha,hasLeadingAlpha,wantTrailingAlpha,hasTrailingAlpha] ←
CheckSpaces[alpha,alphaStart,alphaLen,alphaSize,beta,betaStart,betaLen];
[wantLeadingBeta,hasLeadingBeta,wantTrailingBeta,hasTrailingBeta] ←
CheckSpaces[beta, betaStart,betaLen,betaSize,alpha,alphaStart,alphaLen];
[] ← TransposeText[alphaRoot,betaRoot,alpha,alphaStart,alphaLen,beta,betaStart,betaLen,event];
betaCnt ← AdjustSpaces[
wantLeadingAlpha,hasLeadingAlpha,wantTrailingAlpha,hasTrailingAlpha,
alpha,alphaStart,betaLen,event];
IF alpha=beta THEN betaStart ← betaStart+betaLen-alphaLen+betaCnt;
alphaCnt ← AdjustSpaces[wantLeadingBeta,hasLeadingBeta,wantTrailingBeta,hasTrailingBeta,
beta,betaStart,alphaLen,event];
alphaResultStart ← betaStart;
alphaResultLen ← alphaLen+alphaCnt;
[alphaResultStart,alphaResultLen] ← AdjustResults[beta,alphaResultStart,alphaResultLen];
betaResultStart ← alphaStart;
betaResultLen ← betaLen+betaCnt;
[betaResultStart,betaResultLen] ← AdjustResults[alpha,betaResultStart,betaResultLen];
IF switched THEN SwitchResults };
CheckSpaces: PROC [dest: RefTextNode, destStart, destLen, destSize: Offset,
source: RefTextNode, sourceStart, sourceLen: Offset]
RETURNS [wantLeading, hasLeading, wantTrailing, hasTrailing: BOOLEAN] = {
wantLeading ← WantLeadingSpace[dest,destStart];
wantTrailing ← WantTrailingSpace[dest,destStart+destLen,destSize];
hasLeading ← HasLeadingSpace[source,sourceStart,sourceLen];
hasTrailing ← HasTrailingSpace[source,sourceStart,sourceLen] };
AdjustSpaces: PROC [wantLeading, hasLeading, wantTrailing, hasTrailing: BOOLEAN,
dest: RefTextNode, start, len: Offset, event: Event] RETURNS [cnt: INTEGER] = {
root: Ref = TextNode.Root[dest];
cnt ← 0;
IF hasTrailing AND ~wantTrailing THEN {
[] ← DeleteText[root,dest,start+len-1,1,event]; cnt𡤌nt-1 };
IF ~hasTrailing AND wantTrailing THEN {
[] ← InsertChar[root,dest,' ,start+len,TRUE,noLooks,event]; cnt𡤌nt+1 };
IF hasLeading AND ~wantLeading THEN {
[] ← DeleteText[root,dest,start,1,event]; cnt𡤌nt-1 };
IF ~hasLeading AND wantLeading THEN {
[] ← InsertChar[root,dest,' ,start,TRUE,noLooks,event]; cnt𡤌nt+1 }};
WantLeadingSpace: PROC [dest: RefTextNode, destStart: Offset]
RETURNS [BOOLEAN] = INLINE {
-- returns true if char before destStart is a letter/digit
RETURN [destStart > 0 AND RopeEdit.AlphaNumericChar[FetchChar[dest,destStart-1]]] };
WantTrailingSpace: PROC [dest: RefTextNode, destEnd, destSize: Offset]
RETURNS [BOOLEAN] = INLINE {
-- returns true if char after destEnd is a letter/digit
RETURN [destEnd < destSize AND RopeEdit.AlphaNumericChar[FetchChar[dest,destEnd]]] };
HasLeadingSpace: PROC [source: RefTextNode, start, len: Offset]
RETURNS [BOOLEAN] = INLINE {
RETURN [len > 0 AND RopeEdit.BlankChar[FetchChar[source,start]]] };
HasTrailingSpace: PROC [source: RefTextNode, start, len: Offset]
RETURNS [BOOLEAN] = INLINE {
RETURN [len > 0 AND RopeEdit.BlankChar[FetchChar[source,start+len-1]]] };
HasFollowingSpace: PROC [source: RefTextNode, end, size: Offset]
RETURNS [BOOLEAN] = INLINE {
RETURN [end < size AND RopeEdit.BlankChar[FetchChar[source,end]]] };
HasPreceedingSpace: PROC [source: RefTextNode, start: Offset]
RETURNS [BOOLEAN] = INLINE {
RETURN [start > 0 AND RopeEdit.BlankChar[FetchChar[source,start-1]]] };
AdjustResults: PROC [dest: RefTextNode, start, len: Offset]
RETURNS [resultStart, resultLen: Offset] = {
size: Offset ← Size[dest];
following: BOOLEAN ← HasFollowingSpace[dest,start+len,size];
preceeding: BOOLEAN ← HasPreceedingSpace[dest,start];
leading: BOOLEAN ← HasLeadingSpace[dest,start,size];
trailing: BOOLEAN ← HasTrailingSpace[dest,start,len];
resultStart ← start; resultLen ← len;
IF leading THEN
IF trailing THEN { resultLen ← resultLen-1; resultStart ← resultStart+1 }
ELSE IF following THEN resultStart ← resultStart+1
ELSE NULL
ELSE IF ~trailing THEN
IF following THEN resultLen ← resultLen+1
ELSE IF preceeding THEN { resultStart ← resultStart-1; resultLen ← resultLen+1 }
ELSE NULL
ELSE NULL };
END.