MatchGrep.mesa
Contents: Implements the file searching routines of the MatchTool.
Copyright Ó 1987 by Xerox Corporation. All rights reserved.
Kurlander, September 6, 1987 5:18:03 pm PDT
Pier, September 25, 1989 5:21:29 pm PDT
Bier, April 8, 1991 10:34 pm PDT
Spreitze, May 7, 1990 12:01 pm PDT
DIRECTORY
Feedback, FileNames, FS, GGBasicTypes, GGControlPanelTypes, GGFileIn, GGFileOps, GGInterfaceTypes, GGModelTypes, GGScene, GGState, GGUserInput, GGWindow, GList, IO, Match, MatchGrep, MatchViewer, Rope, ViewerOps;
MatchGrepImpl:
CEDAR
PROGRAM
IMPORTS Feedback, FileNames, FS, GGFileIn, GGFileOps, GGScene, GGState, GGWindow, GList, IO, Match, MatchViewer, Rope, ViewerOps
EXPORTS GGInterfaceTypes, MatchGrep = BEGIN
ControlsObj: PUBLIC TYPE = GGControlPanelTypes.ControlsObj;
GGData: TYPE = GGInterfaceTypes.GGData;
Point: TYPE = GGBasicTypes.Point;
Scene: TYPE = GGModelTypes.Scene;
fileList: LIST OF Rope.ROPE ← NIL;
lastSpawned: GGData ← NIL;
Grep:
PUBLIC GGUserInput.UserInputProc = {
The interactive grep (compared to GrepListOnly). Invokes a gargoyle viewer on the matched scene file.
GrepIt[ggData, event, FALSE];
};
GrepListOnly:
PUBLIC GGUserInput.UserInputProc = {
Greps for graphical matches, only printing out their names in the invoker commander when there's a hit.
GrepIt[ggData, event, TRUE];
};
GrepIt:
PROC [ggData: GGData, event:
LIST
OF
REF
ANY, listOnly:
BOOL ←
FALSE] = {
FillFileList: FS.NameProc = { fileList ← CONS[fullFName, fileList]; RETURN[TRUE];};
matchData: MatchViewer.MatchData ← MatchViewer.GetMatchData[];
grepData: GGData ← MatchViewer.GetGrepData[];
patRope: Rope.ROPE ← NARROW[event.rest.first];
patStream: IO.STREAM ← IO.RIS[patRope];
IF IO.SkipWhitespace[patStream]=Rope.Length[patRope] THEN MatchViewer.ErrorFeedback["Must select a filename pattern"]
ELSE {
fileList ← NIL; -- clear out global file list
UNTIL
IO.EndOf[patStream]
DO
nextPat: Rope.ROPE ← IO.GetTokenRope[patStream, IO.IDProc].token;
thisPat: Rope.ROPE ← FileNames.ResolveRelativePath[nextPat];
FS.EnumerateForNames[thisPat, FillFileList, grepData.currentWDir !
FS.Error => {
msg: Rope.ROPE ← IO.PutFR["Invalid filename pattern: %g", IO.rope[thisPat]];
MatchViewer.ErrorFeedback[msg];
GOTO Abort;
};];
ENDLOOP;
IF fileList = NIL THEN MatchViewer.ErrorFeedback["No files match the pattern"]
ELSE {
aMatch: BOOL ← FALSE;
fileList ← NARROW[GList.DReverse[fileList]]; -- back in order of enumeration
FOR files:
LIST
OF Rope.
ROPE ← fileList, files.rest
UNTIL files =
NIL
DO
fileList ← files.rest;
IF GrepFile[files.first,
TRUE]
THEN {
aMatch ← TRUE;
IF listOnly THEN IO.PutF[matchData.commander.out, "\n%g matched.", IO.rope[files.first]]
ELSE {
SpawnViewer[grepData, files.first];
RETURN; -- Expects user to invoke NextFile for more files
};
};
ENDLOOP;
MatchViewer.ErrorFeedback[IF aMatch THEN "No more matches." ELSE "No matches."];
IF listOnly THEN IO.PutRope[matchData.commander.out, "\n"];
};
};
EXITS
Abort => Feedback.Blink[MatchViewer.GetToData[].router, $Error];
};
GrepFile:
PROC [fullFName: Rope.
ROPE, verbose:
BOOL ←
TRUE]
RETURNS [found:
BOOL ← FALSE] = {
Returns true if there is a match between the MatchTool From viewer and the gargoyle scene file described by fullFName.
grepData: GGData ← MatchViewer.GetGrepData[];
fullName: Rope.ROPE;
success, versionSpecified: BOOL ← FALSE;
[fullName, success, versionSpecified] ← GGFileOps.GetGargoyleFileName["GrepFile", fullFName, NIL, grepData.router, FALSE];
IF
NOT success
THEN {
mesg: Rope.ROPE ← IO.PutFR["Couldn't load %g.\n", IO.rope[fullName]];
IF verbose THEN MatchViewer.ErrorFeedback[mesg];
RETURN[FALSE];
}
ELSE {
matchData: MatchViewer.MatchData ← MatchViewer.GetMatchData[];
f: FS.STREAM;
IF verbose THEN Feedback.PutF[grepData.router, oneLiner, $Feedback, "Finding %g.", IO.rope[fullName]];
f ←
FS.StreamOpen[fullName, $read !
FS.Error,
IO.Error => {
msgRope: Rope.ROPE ← IO.PutFR["Could not find: %g", [rope[fullName]]];
Feedback.Append[grepData.router, oneLiner, $Error, msgRope];
GOTO Abort;
};];
grepData.scene ← GGScene.CreateScene[]; -- give GGData a new clean slate/scene
[] ← GGFileIn.FileinSceneOnly[f, grepData.scene, FALSE, grepData.camera];
f.Close[];
IF verbose THEN Feedback.PutF[grepData.router, begin, $Feedback, "Searching %g ... ", IO.rope[fullName]];
Now do search
matchData.theirData ← NIL; -- force a reset of search lists
success ← Match.Search[grepData, LIST[$MatchSearch, $SearchFromTop], FALSE];
IF verbose THEN Feedback.PutF[grepData.router, end, $Feedback, IF success THEN "It matched!!!" ELSE "No match"];
RETURN[success];
};
EXITS
Abort => {
Feedback.Blink[MatchViewer.GetToData[].router, $Error];
RETURN[FALSE];
};
};
NextFile:
PUBLIC GGUserInput.UserInputProc = {
grepData: GGData ← MatchViewer.GetGrepData[];
IF fileList = NIL THEN MatchViewer.ErrorFeedback["No more file matches."]
ELSE {
FOR files:
LIST
OF Rope.
ROPE ← fileList, files.rest
UNTIL files =
NIL
DO
fileList ← files.rest;
IF GrepFile[files.first,
TRUE]
THEN {
SpawnViewer[grepData, files.first];
RETURN;
};
ENDLOOP;
MatchViewer.ErrorFeedback["No more file matches."];
};
};
SpawnViewer:
PROC [ggData: GGData, fileName: Rope.
ROPE] = {
Creates a gargoyle viewer, stuffs the scene from ggData into that Gargoyle viewer, and loads the various scene state info (from fileName)
f:
FS.
STREAM ←
FS.StreamOpen[fileName, $read !
FS.Error,
IO.Error => {
msgRope: Rope.ROPE ← IO.PutFR["Could not find: %g", [rope[fileName]]];
Feedback.Append[ggData.router, oneLiner, $Error, msgRope];
GOTO Abort;
};];
IF lastSpawned =
NIL
OR lastSpawned.controls.topper.destroyed
OR lastSpawned.controls.topper.newVersion
THEN {
-- we create a gargoyle viewer
lastSpawned ← GGWindow.CreateWindow[ggData.scene, TRUE, FALSE, ggData.currentWDir];
[] ← GGFileIn.FileinOptionsOnly[f, lastSpawned, FALSE];
GGWindow.RestoreScreenAndInvariants[paintAction: $PaintEntireScene, ggData: lastSpawned, remake: triggerBag, edited: FALSE, okToSkipCapture: FALSE];
ViewerOps.OpenIcon[lastSpawned.controls.topper];
MatchViewer.CenterSel[NIL, lastSpawned]; -- this may not happen, since OpenIcon is async.
}
ELSE {
-- we use the old unedited viewer
lastSpawned.scene ← ggData.scene;
[] ← GGFileIn.FileinOptionsOnly[f, lastSpawned, FALSE];
MatchViewer.CenterSel[NIL, lastSpawned];
GGWindow.RestoreScreenAndInvariants[paintAction: $PaintEntireScene, ggData: lastSpawned, remake: triggerBag, edited: FALSE, okToSkipCapture: FALSE];
};
Now change the viewer file, label, and name
lastSpawned.controls.topper.file ← FileNames.StripVersionNumber[fileName];
lastSpawned.controls.topper.label ← FileNames.GetShortName[path: fileName];
lastSpawned.controls.topper.name ← fileName;
GGState.ChangeIcon[lastSpawned, $clean]; -- will also repaint the label
EXITS
Abort => ERROR; -- we've already opened fileName prior to this, so something's screwy
};
END.