TopButtonsImpl.mesa
Copyright © 1986 by Xerox Corporation. All rights reserved.
Bertrand Serlet, January 27, 1987 6:07:25 pm PST
This module turns the Open and New buttons located on top on a regular Cedar screen into something useful.
DIRECTORY
BasicTime, Buttons, CedarProcess, DFUtilities, DFUtilitiesExtras, FileNames, FS, HashTable, IO, MessageWindow, Process, PopUpSelection, RefText, RegularExpression, Rope, RopeFile, TiogaMenuOps, UserProfile, ViewerClasses, ViewerOps, ViewerTools;
TopButtonsImpl: CEDAR MONITOR
IMPORTS BasicTime, Buttons, CedarProcess, DFUtilities, DFUtilitiesExtras, FileNames, FS, HashTable, IO, MessageWindow, Process, PopUpSelection, RefText, RegularExpression, Rope, RopeFile, TiogaMenuOps, UserProfile, ViewerOps, ViewerTools ~ BEGIN
Auxiliary functions
ROPE: TYPE = Rope.ROPE;
ROPES: TYPE = LIST OF ROPE;
IsFileName: PROC [selection: ROPE] RETURNS [BOOL] = {
RETURN [Rope.Length[selection]>1 AND Rope.SkipTo[selection, 0, " \n\t\l\f\b;,""^←|=)(*&~@"]=Rope.Length[selection]];
};
RequestSelection: PROC [label: ROPE, choice: ROPES] RETURNS [chosen: ROPENIL] ~ {
index: INT ← PopUpSelection.Request[header: label, choice: choice, default: 0, timeOut: 10];
IF index=0 THEN RETURN [""];
IF index=-1 THEN RETURN;
WHILE index#1 DO choice ← choice.rest; index ← index -1 ENDLOOP;
chosen ← choice.first;
};
User Profile Management
directories: ROPESNIL;
catalogs: ROPESNIL;
refsButtonCreated: BOOLFALSE;
rootDFs: ROPESNIL;
NoteProfile: UserProfile.ProfileChangedProc ~ {
directories ← UserProfile.ListOfTokens["TopButtons.Directories", LIST [FileNames.HomeDirectory[]]];
catalogs ← UserProfile.ListOfTokens["TopButtons.Catalogs", LIST ["CedarCatalog", "CedarChestCatalog"]];
rootDFs ← UserProfile.ListOfTokens["TopButtons.RootDFs"];
IF NOT refsButtonCreated AND rootDFs#NIL THEN {[] ← Buttons.Create[info: [name: "Refs"], proc: Refs]; refsButtonCreated ← TRUE};
};
Open
Open: Buttons.ButtonProc ~ {
selection: ROPE ← ViewerTools.GetSelectionContents[];
viewer: ViewerClasses.Viewer;
IF IsFileName[selection]
THEN {
selection ← FileNames.ConvertToSlashFormat[selection];
IF Rope.Fetch[selection]#'/ THEN {
directory: ROPE ← RequestSelection[Rope.Cat["Open ", selection, " among:"], directories];
IF directory=NIL THEN RETURN; -- menu discarded
directory ← FileNames.ConvertToSlashFormat[directory];
IF NOT Rope.IsEmpty[directory] AND Rope.Fetch[directory, Rope.Length[directory]-1]='/ THEN selection ← Rope.Cat[directory, selection];
};
viewer ← TiogaMenuOps.Open[selection];
IF viewer=NIL THEN RETURN;
}
ELSE {
directory: ROPE ← RequestSelection["New in directory:", directories];
IF Rope.IsEmpty[directory] THEN {MessageWindow.Append["Directory menu for new discarded", TRUE]; RETURN};
viewer ← TiogaMenuOps.Open[directory];
IF viewer=NIL THEN RETURN;
IF Rope.IsEmpty[ViewerTools.GetContents[viewer]] THEN ViewerTools.SetSelection[viewer];
};
IF shift THEN ViewerOps.GrowViewer[viewer];
};
Doc
Doc: Buttons.ButtonProc ~ {
selection: ROPE ← ViewerTools.GetSelectionContents[];
viewer: ViewerClasses.Viewer;
IF IsFileName[selection]
THEN {
SELECT TRUE FROM
Rope.Match["*Doc.tioga", selection] => {};
Rope.Match["*Doc", selection] => {};
Rope.Match["*.*", selection] => selection ← Rope.Replace[base: selection, start: Rope.Index[selection, 0, "."], with: "Doc.tioga"];
ENDCASE   => selection ← Rope.Cat[selection, "Doc.tioga"];
viewer ← TiogaMenuOps.Open[selection];
}
ELSE {
catalog: ROPE ← RequestSelection["Catalogs:", catalogs];
IF Rope.IsEmpty[catalog] THEN {MessageWindow.Append["Catalog menu discarded", TRUE]; RETURN};
viewer ← TiogaMenuOps.Open[catalog];
};
IF viewer=NIL THEN RETURN;
IF shift THEN ViewerOps.GrowViewer[viewer];
};
References
CheckDFData: TYPE = REF CheckDFDataRec;
CheckDFDataRec: TYPE = RECORD [dfName: ROPE, state: State];
CheckFileData: TYPE = REF CheckFileDataRec;
CheckFileDataRec: TYPE = RECORD [fileName: ROPE, state: State];
State: TYPE = REF StateRec; -- modification of state must always be monitored
StateRec: TYPE = RECORD [
dfSeen: HashTable.Table, -- HashTable [DF full Name -> NIL]: list of all DF full names already seen (or being seen)
moduleName, functionName: ROPE,
output: ViewerClasses.Viewer ← NIL,
pool: ProcessPool,
matches: INT ← 0
];
streamOptions: FS.StreamOptions = [tiogaRead: FALSE, commitAndReopenTransOnFlush: TRUE, truncatePagesOnClose: TRUE, finishTransOnClose: TRUE, closeFSOpenFileOnClose: TRUE];
CheckDF: CedarProcess.ForkableProc = {
ProcessItem1: DFUtilities.ProcessItemProc = {
WITH item SELECT FROM
directory: REF DFUtilities.DirectoryItem => {};
file: REF DFUtilities.FileItem    =>
IF Rope.Match[importeePattern, file.name, FALSE] THEN defining ← TRUE;
import: REF DFUtilities.ImportsItem   => {
DFUtilities.SortUsingList[import.list, TRUE];
IF import.form=list AND DFUtilities.SearchUsingList[importeeName, import.list].found THEN importing ← TRUE;
IF import.exported THEN {
new: CheckDFData = NEW [CheckDFDataRec ← [dfName: import.path1, state: state]];
[] ← ForkInPool[state.pool, CheckDF, new];
};
};
include: REF DFUtilities.IncludeItem   => {
new: CheckDFData = NEW [CheckDFDataRec ← [dfName: include.path1, state: state]];
[] ← ForkInPool[state.pool, CheckDF, new];
};
ENDCASE          => ERROR;
};
ProcessItem2: DFUtilities.ProcessItemProc = {
WITH item SELECT FROM
directory: REF DFUtilities.DirectoryItem => currentDirectory ← directory.path1;
file: REF DFUtilities.FileItem    => IF Rope.Match["*.mesa!*", file.name, FALSE]
THEN {
new: CheckFileData = NEW [CheckFileDataRec ← [fileName: FS.ExpandName[file.name, currentDirectory].fullFName, state: state]];
[] ← ForkInPool[state.pool, CheckFile, new];
};
import: REF DFUtilities.ImportsItem  => {};
include: REF DFUtilities.IncludeItem  => {};
ENDCASE          => ERROR;
};
processes: LIST OF CedarProcess.Process ← NIL;
checkDFData: CheckDFData = NARROW [data];
dfName: ROPE = checkDFData.dfName;
state: State = checkDFData.state;
currentDirectory: ROPE;
importing, defining: BOOLFALSE;
importeeName: ROPE = Rope.Cat[state.moduleName, ".bcd"];
importeePattern: ROPE = Rope.Cat[state.moduleName, ".bcd*"];
IF HashTable.Fetch[state.dfSeen, dfName].found THEN RETURN;
[] ← HashTable.Store[state.dfSeen, dfName, NIL];
DFUtilitiesExtras.ParseFromFile[dfName, ProcessItem1 !
FS.Error => {FailedMessage[state, Rope.Cat["Cannot open: ", dfName]]; GOTO Fails};
DFUtilitiesExtras.FileSyntaxError => {FailedMessage[state, Rope.Cat["Cannot parse: ", dfName]]; GOTO Fails}];
IF importing OR defining THEN DFUtilitiesExtras.ParseFromFile[dfName, ProcessItem2];
EXITS Fails => {};
};
QuickSearch: PROC [fileName: ROPE, rope: ROPE] RETURNS [possible: BOOLFALSE] = {
stream: IO.STREAM = FS.StreamOpen[fileName: fileName, streamOptions: streamOptions];
firstChar: CHAR = Rope.Fetch[rope];
restText: REF TEXT = Rope.ToRefText[Rope.Substr[rope, 1]];
buffer: REF TEXT = RefText.ObtainScratch[RefText.line];
DO
WHILE IO.GetChar[stream ! IO.EndOfStream => EXIT]#firstChar DO ENDLOOP; -- optimization
IF RefText.Find[IO.GetLine[stream, buffer ! ANY => EXIT], restText]#-1 THEN {possible ← TRUE; EXIT};
ENDLOOP;
IO.Close[stream];
RefText.ReleaseScratch[buffer];
};
CheckFile: CedarProcess.ForkableProc = {
checkFileData: CheckFileData = NARROW [data];
fileName: ROPE = checkFileData.fileName;
state: State = checkFileData.state;
MessageWindow.Append[Rope.Cat["Searching: ", fileName], TRUE];
-- We check for functionName and moduleName to appear somewhere in that source
IF NOT QuickSearch[fileName, state.functionName] OR NOT QuickSearch[fileName, state.moduleName] THEN RETURN;
BEGIN
output: ROPENIL;
rope: ROPE ← RopeFile.Create[fileName];
finder: RegularExpression.Finder = RegularExpression.CreateFromRope[pattern: state.functionName, literal: TRUE, word: TRUE, ignoreCase: FALSE];
localMatches: INT ← 0;
start: INT ← 0;
found: BOOLTRUE;
WHILE found DO
at, atEnd: INT;
[found: found, at: at, atEnd: atEnd] ← RegularExpression.SearchRope[finder, rope, start];
IF ~found THEN LOOP;
IF localMatches=0 THEN output ← Rope.Cat[fileName, ":\t"];
output ← Rope.Cat[output, IO.PutR1[IO.int[at]], " "];
localMatches ← localMatches + 1;
start ← atEnd + 1;
ENDLOOP;
IF localMatches=0 THEN RETURN;
IncrMatches[state, output];
END;
};
rootDF: root of the search
moduleName: short name of the BCD we are looking for
functionName: name of the function we are searching for
FindReferences: PROC [rootDF, moduleName, functionName: ROPE] = {
time: BasicTime.GMT ← BasicTime.Now[];
state: State = NEW [StateRec ← [
moduleName: moduleName, functionName: functionName,
dfSeen: HashTable.Create[equal: HashTable.RopeEqualModCase, hash: HashTable.HashRopeModCase],
output: ViewerTools.MakeNewTextViewer[[
name: Rope.Cat["REFS OF: ", moduleName, ".", functionName]
]],
pool: CreateProcessPool[]
]];
ViewerTools.SetContents[state.output, Rope.Cat["References of ", moduleName, ".", functionName, Rope.Cat[" in ", rootDF, ":\n"]]];
[] ← ForkInPool[state.pool, CheckDF, NEW [CheckDFDataRec ← [dfName: rootDF, state: state]], [priority: background, usePriority: TRUE]];
JoinPool[state.pool];
MessageWindow.Append["References search done!", TRUE];
ViewerTools.SetContents[state.output, IO.PutFR["%g%g DF files considered in %g seconds;\t%g file%g matched.\n", IO.rope[ViewerTools.GetContents[state.output]], IO.int[HashTable.GetSize[state.dfSeen]], IO.int[BasicTime.Period[time, BasicTime.Now[]]], IO.int[state.matches], IO.rope[IF state.matches=1 THEN NIL ELSE "s"]]];
ViewerOps.OpenIcon[state.output];
};
ProcessPool: TYPE = REF ProcessPoolRec;
ProcessPoolRec: TYPE = RECORD [
resultAction: ResultActionProc ← NIL,
processes: SEQUENCE maxProcesses: NAT OF CedarProcess.Process
];
ResultActionProc: TYPE = PROC [REF]; -- applied to results of actions, only if those results are NON-NIL
CreateProcessPool: ENTRY PROC [maxProcesses: NAT ← 4, resultAction: ResultActionProc ← NIL] RETURNS [pool: ProcessPool] = {
pool ← NEW [ProcessPoolRec[maxProcesses]];
pool.resultAction ← resultAction;
};
resultAction is only called for processes which are done and when results are not NIL
ForkInPool: PROC [pool: ProcessPool, action: CedarProcess.ForkableProc, data: REFNIL, options: CedarProcess.ForkOptions ← CedarProcess.DefaultForkOptions] RETURNS [forked: BOOL] = {
IF TryToForkInPool[pool, action, data, options] THEN RETURN;
Process.Yield[];
IF TryToForkInPool[pool, action, data, options] THEN RETURN;
No more process possible in the pool, we do not fork!
BEGIN
results: REF = action[data];
IF results#NIL AND pool.resultAction#NIL THEN pool.resultAction[results];
RETURN [FALSE];
END;
};
TryToForkInPool: ENTRY PROC [pool: ProcessPool, action: CedarProcess.ForkableProc, data: REF, options: CedarProcess.ForkOptions] RETURNS [forked: BOOL] = {
LaunderPool[pool];
FOR i: NAT IN [0 .. pool.maxProcesses) DO
IF pool[i]=NIL THEN {pool[i] ← CedarProcess.Fork[action, data, options]; RETURN [TRUE]};
ENDLOOP;
RETURN [FALSE];
};
JoinPool: PROC [pool: ProcessPool] = {
UNTIL IsPoolDone[pool] DO Process.Pause[Process.MsecToTicks[1000]] ENDLOOP;
};
IsPoolDone: ENTRY PROC [pool: ProcessPool] RETURNS [done: BOOLFALSE] = {
LaunderPool[pool];
FOR i: NAT IN [0 .. pool.maxProcesses) DO
IF pool[i]#NIL THEN RETURN [FALSE]
ENDLOOP;
RETURN[TRUE];
};
must only be called from an ENTRY PROC
LaunderPool: PROC [pool: ProcessPool] = {
FOR i: NAT IN [0 .. pool.maxProcesses) DO
IF pool[i]=NIL THEN LOOP;
IF pool[i].status=aborted THEN {pool[i] ← NIL; LOOP};
IF pool[i].status=done THEN {
IF pool[i].results#NIL AND pool.resultAction#NIL THEN pool.resultAction[pool[i].results];
pool[i] ← NIL;
};
ENDLOOP;
};
Message: INTERNAL PROC [viewer: ViewerClasses.Viewer, rope: ROPE] = {
ViewerTools.InhibitUserEdits[viewer];
ViewerTools.SetContents[viewer, Rope.Cat[ViewerTools.GetContents[viewer], rope, "\n"]];
ViewerTools.EnableUserEdits[viewer];
};
FailedMessage: ENTRY PROC [state: State, rope: ROPE] = {
Message[state.output, rope];
};
IncrMatches: ENTRY PROC [state: State, rope: ROPE] = {
Message[state.output, rope];
state.matches ← state.matches + 1;
};
Refs: Buttons.ButtonProc ~ {
selection: ROPE ← ViewerTools.GetSelectionContents[];
dot: INT = Rope.Find[selection, "."];
rootDF: ROPE;
IF dot<=0 OR Rope.Length[selection]<=dot+1 OR Rope.Find[selection, ".", dot+1]#-1 THEN {MessageWindow.Append["Selection is not of the from *.*", TRUE]; RETURN};
rootDF ← RequestSelection[Rope.Cat["Search ", selection, " in:"], rootDFs];
IF NOT Rope.IsEmpty[rootDF] THEN FindReferences[
rootDF: rootDF,
moduleName: Rope.Substr[selection, 0, dot],
functionName: Rope.Substr[selection, dot+1]
];
};
Initialization
UserProfile.CallWhenProfileChanges[NoteProfile];
END.