<> <> <> <<>> <> <<>> 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 <> 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: ROPE _ NIL] ~ { 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; }; <> directories: ROPES _ NIL; catalogs: ROPES _ NIL; refsButtonCreated: BOOL _ FALSE; rootDFs: ROPES _ NIL; 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: 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: 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]; }; <> 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: BOOL _ FALSE; 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: BOOL _ FALSE] = { 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: ROPE _ NIL; 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: BOOL _ TRUE; 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; }; <> <> <> 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; }; <> ForkInPool: PROC [pool: ProcessPool, action: CedarProcess.ForkableProc, data: REF _ NIL, 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; <> 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: BOOL _ FALSE] = { LaunderPool[pool]; FOR i: NAT IN [0 .. pool.maxProcesses) DO IF pool[i]#NIL THEN RETURN [FALSE] ENDLOOP; RETURN[TRUE]; }; <> 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] ]; }; <> UserProfile.CallWhenProfileChanges[NoteProfile]; END.