TiogaOps2Impl.mesa
Copyright © 1985, 1986 by Xerox Corporation. All rights reserved.
written by Bill Paxton. June 1982
last written by Paxton. December 30, 1982 11:13 am
Last Edited by: Maxwell, January 6, 1983 11:48 am
Russ Atkinson (RRA) January 23, 1986 0:02:15 am PST
Michael Plass, March 29, 1985 5:51:36 pm PST
Rick Beach, March 28, 1985 10:15:04 am PST
Doug Wyatt, September 2, 1986 5:42:23 pm PDT
DIRECTORY
Atom USING [GetPName, MakeAtom],
MessageWindow USING [Append],
NodeStyleOps USING [StyleNameForNode],
Rope USING [Concat, FromChar, IsEmpty, Map, ROPE],
RunReader USING [Backwards, FreeRunReader, Get, GetRunReader, Ref, SetPosition],
TEditDocument USING [Selection],
TEditDocumentPrivate USING [CloseAndOpenPreviousFile, DefaultMenus, DoCloseAndNewViewer, DoCloseAndOpenImplFile, DoLoadFile, DoLoadImplFile, DoNewViewer, DoOpenFile, DoOpenImplFile, DoStoreFile, EmptyViewer, JumpToPrevious, LoadPreviousFile, OpenPreviousFile, Reselect, Reset, Save],
TEditInput USING [AllLevels, CloseEvent, FewerLevels, FirstLevelOnly, MoreLevels, Normalize],
TEditInputOps USING [CallWithLocks, DoFindPlaceholders, DoNextViewer, DoSelectMatchingBrackets],
TEditOps USING [RememberCurrentPosition],
TEditProfile USING [selectionCaret],
TEditRefresh USING [ScrollToEndOfSel],
TEditScrolling USING [ScrollToPosition],
TEditSelection USING [FindWhere, MakeSelection, Position, pSel, SetSelLooks],
TextEdit USING [FetchLooks, FromRope, SetLooks, Size],
TextFind USING [MalformedPattern, PatternErrorCode],
TextFindPrivate USING [FinderRecord],
TextLooks USING [Look, Looks, noLooks, Runs],
TextLooksSupport USING [LooksAND],
TextNode USING [Location, Node, NodeBody, StepBackward, StepForward],
TiogaMenuOps USING [],
TiogaOps USING [CommentControl, Dir, Finder, FinderRec, Location, Pattern, PatternErrorCode, PatternRec, SearchDir, Viewer],
TreeFind USING [CommentControl, Create, CreateFromRope, Try, TryBackwards];
Search
LooksRope:
PROC [looks: TextLooks.Looks]
RETURNS [r:
ROPE] = {
FOR c:
CHAR
IN TextLooks.Look
DO
IF looks[c] THEN r ← Rope.Concat[r, Rope.FromChar[c]];
ENDLOOP
};
RopeToLooks:
PROC [r:
ROPE]
RETURNS [looks: TextLooks.Looks] = {
Set: PROC [c: CHAR] RETURNS [quit: BOOL ← FALSE] = { looks[c] ← TRUE };
[] ← Rope.Map[base: r, action: Set];
};
tiogaOpsEcFromTextFindEc:
ARRAY TextFind.PatternErrorCode
OF PatternErrorCode ← [
toobig: toobig,
endquote: endquote,
endtilda: endtilda,
boundary: boundary,
missingNameEnd: missingNameEnd,
unmatchedNameEnd: unmatchedNameEnd
];
MalformedPattern: PUBLIC ERROR [ec: PatternErrorCode] ~ CODE;
ReportMalformedPattern:
PROC [textFindEc: TextFind.PatternErrorCode] ~ {
tiogaOpsEc: PatternErrorCode ~ tiogaOpsEcFromTextFindEc[textFindEc];
ERROR MalformedPattern[tiogaOpsEc];
};
CreateGeneralPattern:
PUBLIC
PROC [
target: Node, -- node from which to get the pattern
text: BOOL ← TRUE, -- if true, match target text
looks: BOOL ← FALSE, -- if true, match target looks
format: BOOL ← FALSE, -- if true, match target format
style: BOOL ← FALSE, -- if true, match target style
comment: BOOL ← FALSE, -- if true, match target comment property
case: BOOL ← TRUE, -- if true, match case
literal: BOOL ← FALSE, -- if true, treat target literally rather than as a pattern
word: BOOL ← FALSE, -- if true, match words only
subset: BOOL ← TRUE, -- if true, use subset for looks test, else use equality
addBounds: BOOL ← FALSE] -- if true, add |'s to both ends of pattern
RETURNS [pattern: Pattern] = {
error: BOOL ← FALSE;
errorCode: TextFind.PatternErrorCode;
txt: Node = target;
patternTxt: Node ← txt;
pattern ← NEW[PatternRec];
IF looks
AND ~text
THEN {
-- make a phony search pattern and get the looks
size: INT = TextEdit.Size[txt];
lks: TextLooks.Looks = IF size=0 THEN TextLooks.noLooks ELSE TextEdit.FetchLooks[txt,0];
pattern.searchLooks ← LooksRope[lks];
FOR i:
INT
IN [1..size)
DO
IF TextEdit.FetchLooks[txt,i]#lks
THEN {
OPEN MessageWindow;
Append["Search pattern does not have uniform looks.",TRUE];
Append[" Using looks from first char."];
EXIT };
ENDLOOP;
literal ← FALSE;
patternTxt ← TextEdit.FromRope["#*"];
TextEdit.SetLooks[NIL, patternTxt, lks];
};
pattern.text ← text;
pattern.looks ← looks;
pattern.word ← word;
pattern.looksExact ← ~subset;
pattern.commentControl ←
IF ~comment
OR txt=
NIL
THEN includeComments
ELSE IF txt.comment THEN commentsOnly ELSE excludeComments;
pattern.checkFormat ← format;
pattern.format ←
IF target=
NIL
OR target.formatName=
NIL
THEN
NIL
ELSE
Atom.GetPName[target.formatName];
pattern.checkStyle ← style;
pattern.style ← IF ~style THEN NIL ELSE Atom.GetPName[NodeStyleOps.StyleNameForNode[target]];
IF text
OR looks
THEN {
-- create a description of the pattern
pattern.finder ← TreeFind.Create[patternTxt, literal, word, ~looks, ~case, addBounds !
TextFind.MalformedPattern => { errorCode ← ec; error ← TRUE; CONTINUE }];
};
IF error THEN ReportMalformedPattern[errorCode];
};
CreateSimplePattern:
PUBLIC
PROC [
target: ROPE, -- node from which to get the pattern
case: BOOL ← TRUE, -- if true, match case
literal: BOOL ← FALSE, -- if true, treat target literally rather than as a pattern
word: BOOL ← FALSE, -- if true, match words only
addBounds: BOOL ← FALSE] -- if true, add |'s to both ends of pattern
RETURNS [pattern: Pattern] = {
error: BOOL ← FALSE;
errorCode: TextFind.PatternErrorCode;
pattern ← NEW[PatternRec];
pattern.text ← TRUE;
pattern.looks ← FALSE;
pattern.word ← word;
pattern.commentControl ← includeComments;
pattern.checkFormat ← FALSE;
pattern.format ← NIL;
pattern.checkStyle ← FALSE;
pattern.style ← NIL;
pattern.finder ← TreeFind.CreateFromRope[target, literal, word, ~case, addBounds !
TextFind.MalformedPattern => { errorCode ← ec; error ← TRUE; CONTINUE }];
IF error THEN ReportMalformedPattern[errorCode];
};
FindCC:
PROC [cc: CommentControl]
RETURNS [TreeFind.CommentControl] = {
RETURN [
SELECT cc
FROM
includeComments => includeComments,
excludeComments => excludeComments,
commentsOnly => commentsOnly,
ENDCASE => ERROR] };
SelectionSearch:
PUBLIC
PROC [
pattern: Pattern, whichDir: SearchDir ← forwards, interrupt: REF BOOL ← NIL,
startBoundaryNode, endBoundaryNode: Node ← NIL,
startBoundaryOffset: INT ← 0, endBoundaryOffset: INT ← LAST[INT]]
RETURNS [found: BOOL] = {
pSel: TEditDocument.Selection = TEditSelection.pSel;
Found:
PROC [tSel: TEditDocument.Selection] = {
tSel.viewer ← pSel.viewer;
tSel.data ← pSel.data;
TEditOps.RememberCurrentPosition[pSel.viewer];
TEditSelection.SetSelLooks[tSel];
TEditSelection.MakeSelection[new: tSel];
TEditInput.CloseEvent[];
TEditRefresh.ScrollToEndOfSel[tSel.viewer, FALSE] };
Locations:
PROC
RETURNS [start, end: TextNode.Location] = {
start ← pSel.start.pos; end ← pSel.end.pos };
found ← DoSearch[pattern, whichDir, interrupt, Found, Locations,
startBoundaryNode, endBoundaryNode,
startBoundaryOffset, endBoundaryOffset] };
DocLoc:
PROC [loc: Location]
RETURNS [TextNode.Location] = {
RETURN [[loc.node, loc.where]] };
MyLoc:
PROC [loc: TextNode.Location]
RETURNS [Location] = {
RETURN [[loc.node, loc.where]] };
NodeSearch:
PUBLIC
PROC [
pattern: Pattern, whichDir: SearchDir ← forwards,
startLoc, endLoc: Location, interrupt: REF BOOL ← NIL,
startBoundaryNode, endBoundaryNode: Node ← NIL,
startBoundaryOffset: INT ← 0, endBoundaryOffset: INT ← LAST[INT]]
RETURNS [found: BOOL, start, end: Location] = {
Found:
PROC [tSel: TEditDocument.Selection] = {
start ← MyLoc[tSel.start.pos]; end ← MyLoc[tSel.end.pos] };
Locations:
PROC
RETURNS [start, end: TextNode.Location] = {
start ← DocLoc[startLoc]; end ← DocLoc[endLoc] };
found ← DoSearch[pattern, whichDir, interrupt, Found, Locations,
startBoundaryNode, endBoundaryNode,
startBoundaryOffset, endBoundaryOffset] };
DoSearch:
PROC [
pattern: Pattern, whichDir: SearchDir ← forwards, interrupt: REF BOOL ← NIL,
foundProc: PROC [tSel: TEditDocument.Selection],
locationProc: PROC RETURNS [start, end: TextNode.Location],
startBoundaryNode, endBoundaryNode: Node ← NIL,
startBoundaryOffset: INT ← 0, endBoundaryOffset: INT ← LAST[INT]]
RETURNS [found: BOOL] = {
at, atEnd, offset: INT;
first: Node;
where: Node;
startLoc, endLoc: TextNode.Location;
DoLookForPattern:
PROC [root: Node, tSel: TEditDocument.Selection] = {
Forwards:
PROC = {
IF (offset ← endLoc.where+1) >=
TextEdit.Size[first ← endLoc.node] THEN {
first ← TextNode.StepForward[first]; offset ← 0 };
[found,where,at,atEnd,,] ←
TreeFind.Try[finder: pattern.finder, first: first, start: offset,
last: endBoundaryNode, lastLen: endBoundaryOffset,
interrupt: interrupt,
looksExact: pattern.looksExact,
checkFormat: pattern.checkFormat,
format: MakeName[pattern.format],
commentControl: FindCC[pattern.commentControl],
checkStyle: pattern.checkStyle,
style: MakeName[pattern.style],
styleProc: NodeStyleOps.StyleNameForNode] };
Backwards:
PROC = {
IF (offset ← startLoc.where)=0
THEN {
first ← TextNode.StepBackward[startLoc.node];
offset ← LAST[INT] }
ELSE first ← startLoc.node;
[found,where,at,atEnd,,] ←
TreeFind.TryBackwards[finder: pattern.finder,
first: first, len: offset,
last: startBoundaryNode, lastStart: startBoundaryOffset,
interrupt: interrupt,
looksExact: pattern.looksExact,
checkFormat: pattern.checkFormat,
format: MakeName[pattern.format],
commentControl: FindCC[pattern.commentControl],
checkStyle: pattern.checkStyle,
style: MakeName[pattern.style],
styleProc: NodeStyleOps.StyleNameForNode] };
[startLoc, endLoc] ← locationProc[];
IF interrupt#NIL THEN interrupt^ ← FALSE;
SELECT whichDir
FROM
forwards => Forwards[];
backwards => Backwards[];
anywhere => {
Forwards[];
IF found
THEN whichDir ← forwards
ELSE {
whichDir ← backwards; Backwards[] }};
ENDCASE => ERROR;
IF ~found OR where=NIL THEN RETURN;
IF pattern.looks
AND ~pattern.text
AND ~pattern.word
THEN
[at,atEnd] ← Extend[whichDir=forwards,
pattern.looksExact, RopeToLooks[pattern.searchLooks], where, at, atEnd];
tSel.start.pos ← [where,at];
tSel.end.pos ← [where,MAX[0,atEnd-1]];
tSel.granularity ←
IF ~pattern.looks AND ~pattern.text THEN node
ELSE IF pattern.word THEN word ELSE char;
tSel.insertion ← IF TEditProfile.selectionCaret=before THEN before ELSE after;
foundProc[tSel];
};
TEditInputOps.CallWithLocks[DoLookForPattern, read] };
Extend:
PROC
[forward, looksExact:
BOOL, searchLooks: TextLooks.Looks,
where: Node, at, atEnd: INT,
last: Node ← NIL, lastLen: INT ← LAST[INT]]
RETURNS [newAt, newAtEnd: INT] = {
runrdr: RunReader.Ref ← RunReader.GetRunReader[];
looks: TextLooks.Looks;
runLen: INT;
runs: TextLooks.Runs ← where.runs;
IF forward
THEN {
-- extend toward end of node
size: INT ~ TextEdit.Size[where];
IF atEnd<size
THEN {
lastLen ← IF where=last THEN MIN[size,lastLen] ELSE size;
RunReader.SetPosition[runrdr,runs,atEnd];
WHILE atEnd < lastLen
DO
IF runs=NIL THEN { runLen ← size-atEnd; looks ← TextLooks.noLooks }
ELSE [runLen,looks] ← RunReader.Get[runrdr];
IF ~looksExact THEN looks ← TextLooksSupport.LooksAND[looks,searchLooks];
IF searchLooks # looks THEN EXIT;
atEnd ← atEnd+runLen;
ENDLOOP;
atEnd ← MIN[atEnd,lastLen];
};
}
ELSE
IF at>0
THEN {
-- extend backward
RunReader.SetPosition[runrdr,runs,at];
WHILE at > 0
DO
IF runs=NIL THEN { runLen ← at; looks ← TextLooks.noLooks }
ELSE [runLen,looks] ← RunReader.Backwards[runrdr];
IF ~looksExact THEN looks ← TextLooksSupport.LooksAND[looks,searchLooks];
IF searchLooks # looks THEN EXIT;
at ← at-runLen;
ENDLOOP;
};
RunReader.FreeRunReader[runrdr];
RETURN[at, atEnd];
};
SelectMatchingBrackets:
PUBLIC
PROC [before, after:
CHAR]
RETURNS [found:
BOOL] = {
found ← TEditInputOps.DoSelectMatchingBrackets[before, after] };
NextPlaceholder:
PUBLIC
PROC [dir: Dir ← forward, gotoend:
BOOL,
startBoundaryNode, endBoundaryNode: Node ← NIL,
startBoundaryOffset: INT ← 0, endBoundaryOffset: INT ← LAST[INT]]
RETURNS [found, wenttoend: BOOL] = {
[found, wenttoend] ← TEditInputOps.DoFindPlaceholders[
dir=forward, gotoend,
startBoundaryNode, endBoundaryNode,
startBoundaryOffset, endBoundaryOffset] };
NextViewer:
PUBLIC
PROC [dir: Dir ← forward]
RETURNS [found:
BOOL] = {
found ← TEditInputOps.DoNextViewer[dir=forward] };
SearchWhere:
PROC [whichDir: SearchDir]
RETURNS [TEditSelection.FindWhere] =
INLINE {
RETURN [
SELECT whichDir
FROM
forwards => forwards,
backwards => backwards,
anywhere => anywhere,
ENDCASE => ERROR] };
Files and text viewers
Save:
PUBLIC
PROC [viewer: Viewer] = {
TEditDocumentPrivate.Save[viewer];
};
Load:
PUBLIC
PROC [viewer: Viewer, fileName:
ROPE ←
NIL,
fileNameProcViewer: Viewer ← NIL] = {
[] ← TEditDocumentPrivate.DoLoadFile[viewer, fileName, FALSE, fileNameProcViewer];
};
Open:
PUBLIC
PROC [fileName:
ROPE ←
NIL, fileNameProcViewer: Viewer ←
NIL]
RETURNS [Viewer] = {
RETURN [TEditDocumentPrivate.DoOpenFile[fileName, fileNameProcViewer]];
};
CloseAndOpen:
PUBLIC
PROC [viewer: Viewer, fileName:
ROPE ←
NIL, fileNameProcViewer: Viewer ←
NIL]
RETURNS [Viewer] = {
RETURN [TEditDocumentPrivate.DoLoadFile[viewer, fileName, TRUE, fileNameProcViewer]];
};
LoadImpl:
PUBLIC
PROC [viewer: Viewer, fileName:
ROPE ←
NIL] = {
[] ← TEditDocumentPrivate.DoLoadImplFile[viewer, fileName];
};
OpenImpl:
PUBLIC
PROC [fileName:
ROPE ←
NIL]
RETURNS [Viewer] = {
RETURN [TEditDocumentPrivate.DoOpenImplFile[fileName]];
};
CloseAndOpenImpl:
PUBLIC
PROC [viewer: Viewer, fileName:
ROPE ←
NIL]
RETURNS [Viewer] = {
RETURN [TEditDocumentPrivate.DoCloseAndOpenImplFile[viewer, fileName]];
};
LoadPreviousFile:
PUBLIC
PROC [parent: Viewer] = {
TEditDocumentPrivate.LoadPreviousFile[parent];
};
OpenPreviousFile:
PUBLIC
PROC [parent: Viewer] = {
TEditDocumentPrivate.OpenPreviousFile[parent];
};
CloseAndOpenPreviousFile:
PUBLIC
PROC [parent: Viewer] = {
TEditDocumentPrivate.CloseAndOpenPreviousFile[parent];
};
DefaultMenus:
PUBLIC
PROC [viewer: Viewer, paint:
BOOL ←
FALSE] = {
TEditDocumentPrivate.DefaultMenus[viewer, paint];
};
Store:
PUBLIC
PROC [viewer: Viewer, fileName:
ROPE ←
NIL] = {
TEditDocumentPrivate.DoStoreFile[viewer, fileName];
};
New:
PUBLIC
PROC
RETURNS [Viewer] = {
RETURN [TEditDocumentPrivate.DoNewViewer[]];
};
Empty:
PUBLIC
PROC [viewer: Viewer] = {
TEditDocumentPrivate.EmptyViewer[viewer];
};
CloseAndNewViewer:
PUBLIC
PROC [viewer: Viewer]
RETURNS [Viewer] = {
RETURN [TEditDocumentPrivate.DoCloseAndNewViewer[viewer]];
};
Reset:
PUBLIC
PROC [viewer: Viewer] = {
TEditDocumentPrivate.Reset[viewer];
};
Jump:
PUBLIC
PROC [viewer: Viewer, loc: Location] = {
[] ← TEditScrolling.ScrollToPosition[viewer, [loc.node, loc.where]];
};