<> DIRECTORY ConvertUnsafe USING [ToRope], ExecOps USING [Outcome], Directory USING [Lookup], Feedback USING [BeginItemProc, CreateProc, DestroyProc, FinishItemProc, NoteProgressProc, Procs], File USING [Capability], IconRegistry USING [GetIcon, RegisterIcon], Icons USING [IconFlavor], InputFocus USING [GetInputFocus], IO USING [char, Error, GetToken, IDProc, noWhereStream, Put, PutF, PutRope, ROPE, rope, ResetUserAbort, SkipOver, STREAM, string, TokenProc, UserAborted, WhiteSpace], List USING [Nconc1], Menus USING [AppendMenuEntry, MenuEntry, ReplaceMenuEntry, FindEntry, ClickProc, CreateEntry, Menu], MessageWindow USING [Append, Blink], Process USING [Detach, GetPriority, MsecToTicks, Pause, Priority, priorityBackground, SetPriority, Ticks], Rope USING [Cat, Concat, Equal, Fetch, Find, IsEmpty, Length, Letter, ROPE, Substr, ToRefText], Runtime USING [IsBound, RunConfig], TemporarySpecialExecOps USING [BindUsingFeedback, CompileUsingFeedback], TiogaMenuOps USING [Open], TiogaOps USING [CommandProc, RegisterCommand], UserExec USING [AcquireResource, CheckForFile, CommandProc, ExecHandle, GetExecHandle, GetStreams, GetTheFile, HistoryEvent, RegisterCommand, RegisterTransformation, TransformProc, ReleaseResource, RopeSubst, UserAborted], UserExecExtras USING [], UserExecPrivate USING [ForAllSplitViewers, ExecPrivateRecord, HistoryEventPrivateRecord, RenameFile, SplitViewerProc, WaitUntilSaved], UserProfile USING [Boolean, CallWhenProfileChanges, ProfileChangedProc, Token], ViewerClasses USING [Column, Viewer], ViewerEvents USING [RegisterEventProc, EventProc], ViewerOps USING [BlinkIcon, CreateViewer, DestroyViewer, FindViewer, OpenIcon, RestoreViewer, PaintViewer], ViewerTools USING [GetSelectedViewer] ; CompilerExecOpsImpl: CEDAR MONITOR IMPORTS ConvertUnsafe, Directory, IconRegistry, InputFocus, IO, List, Menus, MessageWindow, Process, Rope, Runtime, TemporarySpecialExecOps, TiogaMenuOps, TiogaOps, UserExec, UserExecPrivate, UserProfile, ViewerEvents, ViewerOps, ViewerTools EXPORTS UserExec, UserExecPrivate SHARES UserProfile = BEGIN OPEN IO; <> ExecHandle: TYPE = UserExec.ExecHandle; HistoryEvent: TYPE = UserExec.HistoryEvent; Viewer: TYPE = ViewerClasses.Viewer; ClickProc: TYPE = Menus.ClickProc; <> ExecPrivateRecord: PUBLIC TYPE = UserExecPrivate.ExecPrivateRecord; <> HistoryEventPrivateRecord: PUBLIC TYPE = UserExecPrivate.HistoryEventPrivateRecord; <> <> Bind: UserExec.CommandProc -- [exec: ExecHandle] RETURNS[ok: BOOLEAN _ TRUE] -- = { ENABLE FailedToLoad => GOTO Failed; -- so don't get log, which contains result of last binding.. outCome: ExecOps.Outcome _ RunBinder[event, exec]; [] _ ShowLog["Binder.log", outCome = ok, exec]; RETURN[outCome = ok]; EXITS Failed => RETURN[FALSE]; }; RunBinder: PROC [event: HistoryEvent, exec: ExecHandle] RETURNS[outCome: ExecOps.Outcome] = TRUSTED { -- Process.SetPriority, BindUsingFeedback, RunConfig, Lookup priority: Process.Priority = Process.GetPriority[]; out: STREAM ; Release: PROC = TRUSTED {[] _ UserExec.ReleaseResource[$Binder]; Process.SetPriority[priority]}; { ENABLE UNWIND => Release[]; someMissing: BOOLEAN _ FALSE; line: ROPE; [] _ UserExec.AcquireResource[resource: $Binder, owner: "Binder", exec: exec]; binderRec.exec _ exec; [binderRec.in, binderRec.out] _ UserExec.GetStreams[exec]; out _ binderRec.out; Process.SetPriority[Process.priorityBackground]; IF NOT Runtime.IsBound[TemporarySpecialExecOps.BindUsingFeedback] THEN { ENABLE ANY => GOTO FailedToLoad; out.PutF["*mLoading Binder.bcd...\n*s"]; Runtime.RunConfig[Directory.Lookup["Binder.bcd"L], 1, TRUE]; }; [line, , someMissing] _ ProcessLine[ext: "config", event: event, exec: exec]; binderRec.cmdText _ LOOPHOLE[Rope.ToRefText[Rope.Concat[binderSwitches, line]], REF StringBody]; binderRec.cmd _ LOOPHOLE[@(binderRec.cmdText.text)]; binderRec.abort _ FALSE; outCome _ TemporarySpecialExecOps.BindUsingFeedback[binderRec.cmd, @binderProcs]; IF someMissing THEN outCome _ errors; EXITS FailedToLoad => {UserExec.GetStreams[exec].out.PutF["*eBinder Failed to load.*s\n"]; Release[]; ERROR FailedToLoad}; }; Release[]; IF outCome = aborted THEN UserExec.UserAborted[exec]; }; BCreate: Feedback.CreateProc = {RETURN[NIL]}; BDestroy: Feedback.DestroyProc = { binderRec.out.Put[string[trailer] ! IO.Error => IF ec = StreamClosed THEN {binderRec.abort _ TRUE; CONTINUE}; IO.UserAborted, ABORTED => {binderRec.abort _ TRUE; CONTINUE}; ]; }; BBeginItem: Feedback.BeginItemProc = { binderRec.out.Put[string[item] ! IO.Error => IF ec = StreamClosed THEN {binderRec.abort _ TRUE; CONTINUE}; IO.UserAborted, ABORTED => {binderRec.abort _ TRUE; CONTINUE}; ]; }; BNoteProgress: Feedback.NoteProgressProc = { binderRec.out.PutRope[" ." ! IO.Error => IF ec = StreamClosed THEN {binderRec.abort _ TRUE; CONTINUE}; IO.UserAborted, ABORTED => {binderRec.abort _ TRUE; CONTINUE}; ]; IF binderRec.abort THEN { binderRec.in.ResetUserAbort[]; ERROR IO.UserAborted; }; }; BFinishItem: Feedback.FinishItemProc = { binderRec.out.PutF[" %g\n", string[trailer] ! IO.Error => IF ec = StreamClosed THEN {binderRec.abort _ TRUE; CONTINUE}; IO.UserAborted, ABORTED => {binderRec.abort _ TRUE; CONTINUE}; ]; }; binderProcs: Feedback.Procs _ [BCreate, BDestroy, BBeginItem, BNoteProgress, BFinishItem]; BinderRec: TYPE = RECORD[ cmdText: REF StringBody _ NIL, cmd: LONG POINTER TO PACKED ARRAY [0..0) OF CHARACTER _ NIL, exec: ExecHandle _ NIL, in, out: STREAM _ NIL, separateLogs: BOOL _ FALSE, openLogs: BOOL _ TRUE, abort: BOOL _ FALSE, numberOfFiles: INT _ 0, thisFile, warnings, errors: ROPE _ NIL ]; FailedToLoad: ERROR = CODE; binderRec: REF BinderRec _ NEW[BinderRec _ []]; <> needsToBeCompiled, errorsOrWarnings: LIST OF ROPE _ NIL; saveOldLog: BOOL _ TRUE; compilerRec: REF BinderRec _ NEW[BinderRec _ []]; compilerSwitches, binderSwitches: ROPE _ NIL; SetDefaultSwitches: UserProfile.ProfileChangedProc = { compilerSwitches _ UserProfile.Token["Compiler.Switches"]; IF UserProfile.Boolean["Compiler.SeparateLogs", FALSE] THEN compilerSwitches _ Rope.Concat["/-g", compilerSwitches]; IF NOT Rope.IsEmpty[compilerSwitches] THEN compilerSwitches _ Rope.Concat[compilerSwitches, ";"]; --for Ed's parser binderSwitches _ UserProfile.Token["Binder.Switches"]; IF NOT Rope.IsEmpty[binderSwitches] THEN binderSwitches _ Rope.Concat[binderSwitches, ";"]; --for Ed's parser destroyLogOnSuccess _ UserProfile.Boolean["Compiler.DestroyLogOnSuccess", TRUE]; }; Compile: UserExec.CommandProc = { ENABLE FailedToLoad => GOTO Failed; outCome: ExecOps.Outcome; IF saveOldLog THEN [] _ UserExecPrivate.RenameFile[oldName: "Compiler.log", newName: "Compiler.log$", out: IO.noWhereStream]; IO.SkipOver[event.commandLineStream, IO.WhiteSpace]; outCome _ RunCompiler[event, exec]; SELECT outCome FROM ok, warnings => RETURN[TRUE]; -- so user can write compile ... ; bind ... and not have it stop because of warnings. ENDCASE => RETURN[FALSE]; EXITS Failed => RETURN[FALSE]; }; CompileBadGuys: UserExec.TransformProc = { IF errorsOrWarnings = NIL THEN { UserExec.GetStreams[exec].out.PutF["*n*mEverybody's cool.*u\n"]; RETURN[NIL]; }; result _ NIL; FOR l: LIST OF ROPE _ errorsOrWarnings, l.rest UNTIL l = NIL DO result _ Rope.Cat[result, " ", l.first]; ENDLOOP; RETURN[Rope.Cat["Compile", result, "\n"]]; }; -- of CompileBadGuys CompileAll: UserExec.TransformProc = { IF needsToBeCompiled = NIL THEN { UserExec.GetStreams[exec].out.PutF["*n*mEverybody's cool.*u\n"]; RETURN[NIL]; }; result _ NIL; FOR l: LIST OF ROPE _ needsToBeCompiled, l.rest UNTIL l = NIL DO result _ Rope.Cat[result, " ", l.first]; ENDLOOP; RETURN[Rope.Cat["Compile", result, "\n"]]; }; -- of CompileBadGuys RunCompiler: PROC [event: HistoryEvent, exec: ExecHandle] RETURNS[outCome: ExecOps.Outcome] = TRUSTED { priority: Process.Priority = Process.GetPriority[]; out: STREAM; Release: PROC = TRUSTED {[] _ UserExec.ReleaseResource[$Compiler]; Process.SetPriority[priority]}; { ENABLE UNWIND => Release[]; someMissing: BOOLEAN _ FALSE; line: ROPE; files: LIST OF ROPE; i: INT; [] _ UserExec.AcquireResource[resource: $Compiler, owner: "Compiler", exec: exec]; compilerRec.exec _ exec; [compilerRec.in, compilerRec.out] _ UserExec.GetStreams[exec]; out _ compilerRec.out; Process.SetPriority[Process.priorityBackground]; IF NOT Runtime.IsBound[TemporarySpecialExecOps.CompileUsingFeedback] THEN { ENABLE ANY => GOTO FailedToLoad; out.PutF["*mLoading Compiler.bcd...\n*s"]; Runtime.RunConfig[Directory.Lookup["Compiler.bcd"L], 1, TRUE]; }; [line, files, someMissing] _ ProcessLine[ext: "mesa", event: event, exec: exec]; line _ Rope.Concat[compilerSwitches, line]; compilerRec.separateLogs _ ((i _ Rope.Find[line, "/-g"]) # -1 AND Rope.Find[s1: line, s2: "/g", pos1: i] = -1); compilerRec.openLogs _ TRUE; compilerRec.abort _ FALSE; compilerRec.cmdText _ LOOPHOLE[Rope.ToRefText[line], REF StringBody]; compilerRec.cmd _ LOOPHOLE[@(compilerRec.cmdText.text)]; compilerRec.errors _ compilerRec.warnings _ NIL; compilerRec.numberOfFiles _ 0; outCome _ TemporarySpecialExecOps.CompileUsingFeedback[compilerRec.cmd, @compilerProcs]; IF compilerRec.warnings # NIL THEN out.PutF["*n*m-- Warnings in --*s%g", rope[compilerRec.warnings]]; IF compilerRec.errors # NIL THEN out.PutF["*n*e-- Errors in --*s%g", rope[compilerRec.errors]]; IF NOT Rope.Find[s1: line, s2: "/-g", case: FALSE] # -1 THEN [] _ ShowLog["Compiler.log", outCome = ok, exec] EXITS FailedToLoad => {out.PutF["*eCompiler Failed to load.*s\n"]; Release[]; ERROR FailedToLoad}; }; Release[]; IF outCome = aborted THEN UserExec.UserAborted[exec]; }; CCreate: Feedback.CreateProc = {RETURN[NIL]}; CDestroy: Feedback.DestroyProc = { compilerRec.out.Put[string[trailer] ! IO.Error => IF ec = StreamClosed THEN {compilerRec.abort _ TRUE; CONTINUE}; IO.UserAborted, ABORTED => {compilerRec.abort _ TRUE; CONTINUE}; ]; }; CBeginItem: Feedback.BeginItemProc = { compilerRec.out.Put[string[item] ! IO.Error => IF ec = StreamClosed THEN {compilerRec.abort _ TRUE; CONTINUE}; IO.UserAborted, ABORTED => {compilerRec.abort _ TRUE; CONTINUE}; ]; TRUSTED { thisFile: ROPE _ ConvertUnsafe.ToRope[item]; i: INT; thisFile _ Rope.Substr[base: thisFile, start: 11]; -- to strip off the "Compiling: " i _ Rope.Find[thisFile, "/"]; IF i # -1 THEN thisFile _ Rope.Substr[base: thisFile, len: i]; compilerRec.thisFile _ thisFile; }; }; CNoteProgress: Feedback.NoteProgressProc = { compilerRec.out.PutRope[" ." ! IO.Error => IF ec = StreamClosed THEN {compilerRec.abort _ TRUE; CONTINUE}; IO.UserAborted, ABORTED => {compilerRec.abort _ TRUE; CONTINUE}; ]; IF compilerRec.abort THEN { compilerRec.in.ResetUserAbort[]; ERROR IO.UserAborted; }; }; CFinishItem: Feedback.FinishItemProc = { name: ROPE = Rope.Concat[compilerRec.thisFile, ".mesa"]; viewer: Viewer _ ViewerOps.FindViewer[name]; space: ROPE = " "; compilerRec.numberOfFiles _ compilerRec.numberOfFiles + 1; compilerRec.out.Put[char[' ], string[trailer], char['\n] ! IO.Error => IF ec = StreamClosed THEN {compilerRec.abort _ TRUE; CONTINUE}; IO.UserAborted, ABORTED => {compilerRec.abort _ TRUE; CONTINUE}; ]; SELECT outcome FROM warnings, errors, errorsAndWarnings => { IF outcome = warnings THEN compilerRec.warnings _ Rope.Cat[compilerRec.warnings, space, compilerRec.thisFile] ELSE compilerRec.errors _ Rope.Cat[compilerRec.errors, space, compilerRec.thisFile]; AddIt[compilerRec.thisFile, TRUE]; IF compilerRec.separateLogs THEN { <> <> <> <<>> <<>> <> <> <<-- [] _ CreateLog[name: name, iconic: log.iconic] should be able to just call TiogaMenuOps here (Reason we dont call CreateLog is because that might use a different icon).>> <> <> <<}>> <> <<};>> <<>> <> <> createIconic: BOOLEAN _ TRUE; exec: UserExec.ExecHandle = compilerRec.exec; log: Viewer; IF exec # NIL AND NOT exec.viewer.iconic AND (InputFocus.GetInputFocus[].owner = exec.viewer) AND compilerRec.openLogs THEN createIconic _ FALSE; -- this test is also in ShowLog, but that isn't being called now IF UserProfile.Boolean["Compiler.IconicLogs", FALSE] THEN createIconic _ TRUE; IF viewer = NIL THEN { IF createIconic THEN viewer _ ViewerOps.CreateViewer[flavor: $Text, info: [name: name, file: name, iconic: TRUE, icon: IconRegistry.GetIcon[iconName: "NeedsToBeCompiled", default: document]]] ELSE viewer _ TiogaMenuOps.Open[name]; -- the icon will be changed below } ELSE IF viewer.iconic AND NOT createIconic THEN [] _ ViewerOps.OpenIcon[viewer]; compilerRec.openLogs _ FALSE; -- only open at most one icon IF (log _ ViewerOps.FindViewer[Rope.Concat[compilerRec.thisFile, ".errlog"]]) # NIL THEN ViewerOps.RestoreViewer[log]; }; IF viewer # NIL THEN { -- this code is exercised even if compiler.separateLogs is FALSE. proc: UserExecPrivate.SplitViewerProc = { IF compilerRec.separateLogs THEN { entry: Menus.MenuEntry _ Menus.FindEntry[viewer.menu, "ErrorLog"]; IF entry = NIL THEN { Menus.AppendMenuEntry[menu: viewer.menu, line: 0, entry: Menus.CreateEntry[name: "ErrorLog", proc: OpenErrorLog, fork: FALSE]]; IF NOT viewer.iconic THEN ViewerOps.PaintViewer[viewer, menu]; }; }; }; [] _ ThisNeedsToBeCompiled[viewer]; -- to change the icon UserExecPrivate.ForAllSplitViewers[viewer, proc]; IF viewer.iconic AND UserProfile.Boolean["Compiler.BlinkLogs", TRUE] THEN BlinkIcon[viewer]; -- do this outside of ForAllSplitViewers so that if more than one icon, only blink one }; }; ENDCASE => { -- successful IF compilerRec.separateLogs THEN -- restore or destroy log -- [] _ ShowLog[Rope.Concat[compilerRec.thisFile, ".errlog"], TRUE, compilerRec.exec]; RemoveIt[compilerRec.thisFile]; IF viewer # NIL THEN { proc: UserExecPrivate.SplitViewerProc = { IF viewer.icon = IconRegistry.GetIcon[iconName: "NeedsToBeCompiled", default: document] THEN { viewer.icon _ document; IF viewer.iconic THEN ViewerOps.PaintViewer[viewer, all]; }; IF compilerRec.separateLogs THEN { entry: Menus.MenuEntry _ Menus.FindEntry[viewer.menu, "ErrorLog"]; IF entry # NIL THEN Menus.ReplaceMenuEntry[viewer.menu, entry]; IF NOT viewer.iconic THEN ViewerOps.PaintViewer[viewer, menu]; }; }; UserExecPrivate.ForAllSplitViewers[viewer, proc]; }; }; }; compilerProcs: Feedback.Procs _ [CCreate, CDestroy, CBeginItem, CNoteProgress, CFinishItem]; ProcessLine: PROC [ext: ROPE, event: HistoryEvent, exec: ExecHandle] RETURNS[line: ROPE, files: LIST OF ROPE, someMissing: BOOLEAN _ FALSE] = { commandLineStream: STREAM = event.commandLineStream; id, nextId, lastId: ROPE; length, pos: INT _ 0; files _ NIL; id _ IO.GetToken[commandLineStream]; WHILE id # NIL DO file: ROPE; { -- to provide an exits clause IF Rope.Equal[id, "/"] THEN {[] _ IO.GetToken[commandLineStream, IO.IDProc]; -- skip over switches GOTO GetNext; }; nextId _ IO.GetToken[commandLineStream]; IF Rope.Equal[nextId, "_"] OR Rope.Equal[nextId, ":"] THEN {id _ nextId; GOTO GetNext}; IF NOT Rope.Letter[Rope.Fetch[id, 0]] THEN NULL-- e.g. skip ,' ELSE IF (file _ UserExec.GetTheFile[file: id, defaultExt: IF Rope.Equal[lastId, ":"] THEN "bcd" ELSE ext, event: event, exec: exec]) = NIL THEN someMissing _ TRUE ELSE TRUSTED { files _ LOOPHOLE[List.Nconc1[LOOPHOLE[files], file]]; UserExecPrivate.WaitUntilSaved[file, exec]; }; lastId _ id; id _ nextId; LOOP; EXITS GetNext => {lastId _ id; id _ IO.GetToken[commandLineStream]}; }; ENDLOOP; line _ event.commandLine; length _ Rope.Length[line]; IF (pos _ Rope.Find[s1: line, s2: "\n"]) # -1 AND pos # length -1 THEN -- embedded CR's, which must be changed to spaces. line _ UserExec.RopeSubst[old: "\n", new: " ", base: line]; IF (pos _ Rope.Find[s1: line, s2: "\n"]) = -1 THEN line _ Rope.Concat[line, "\n"]; -- no trailing CR. must be added because of way binder/compiler scanner works. }; destroyLogOnSuccess: BOOLEAN; ShowLog: PROC [name: ROPE, ok: BOOLEAN, exec: ExecHandle, blinkIt: BOOL _ TRUE] RETURNS[log: Viewer] = { log _ ViewerOps.FindViewer[name]; IF NOT ok THEN { createIconic: BOOLEAN _ TRUE; IF exec # NIL AND NOT exec.viewer.iconic AND (InputFocus.GetInputFocus[].owner = exec.viewer) THEN createIconic _ FALSE; IF UserProfile.Boolean["Compiler.IconicLogs", FALSE] THEN createIconic _ TRUE; IF log # NIL THEN ViewerOps.RestoreViewer[log] ELSE IF UserExec.CheckForFile[name] THEN log _ CreateLog[name: name, iconic: createIconic]; -- log not there in case of no such source IF log # NIL AND log.iconic AND blinkIt AND UserProfile.Boolean["Compiler.BlinkLogs", TRUE] THEN BlinkIcon[log]; } ELSE IF log # NIL THEN { IF destroyLogOnSuccess THEN {ViewerOps.DestroyViewer[log]; log _ NIL} ELSE ViewerOps.RestoreViewer[log]; }; }; CreateLog: PROC [name: ROPE, iconic: BOOL _ TRUE] RETURNS[viewer: Viewer] = { IF iconic THEN viewer _ ViewerOps.CreateViewer[flavor: $Text, info: [name: name, file: name, iconic: iconic, icon: typescript]] ELSE {viewer _ TiogaMenuOps.Open[name]; viewer.icon _ typescript}; }; BlinkIcon: PUBLIC PROC [icon: Viewer, n: INT _ 10] = TRUSTED { Process.Detach[FORK Blink[icon, n]]; }; Blink: PROC [viewer: Viewer, rate: INT _ 10] = TRUSTED { halfSecond: Process.Ticks = Process.MsecToTicks[500]; DO ViewerOps.BlinkIcon[viewer]; rate _ MIN[(rate * 3)/2, 3600]; -- gradually slow down rate of blinking FOR i: INT IN [0..2*rate) DO -- blink every n seconds, but wake up every half second. Process.Pause[halfSecond]; IF viewer.destroyed OR NOT viewer.iconic OR viewer = ViewerTools.GetSelectedViewer[] THEN RETURN; ENDLOOP; ENDLOOP; }; <> ResetProc: TiogaOps.CommandProc = { icon: Icons.IconFlavor; proc: UserExecPrivate.SplitViewerProc = { viewer.icon _ icon; }; IF NOT IsAMesaFile[viewer.name] THEN RETURN; icon _ IF DoesThisNeedToBeCompiled[viewer.name] THEN IconRegistry.GetIcon["NeedsToBeCompiled", document] ELSE document; UserExecPrivate.ForAllSplitViewers[viewer, proc]; }; CloseProc: ViewerEvents.EventProc = { needsToBeCompiledIcon: Icons.IconFlavor = IconRegistry.GetIcon["NeedsToBeCompiled", unInit]; IF viewer.newVersion THEN RETURN; -- should already have the dirtyDocument icon by virtue of the edit event registration IF IsAMesaFile[viewer.name] THEN viewer.icon _ IF DoesThisNeedToBeCompiled[viewer.name] THEN IconRegistry.GetIcon["NeedsToBeCompiled", document] -- necessary to do this to handle the case where you opened a new viewer on a needstobecompiled file ELSE document ELSE IF viewer.icon = IconRegistry.GetIcon["NeedsToBeCompiled", document] THEN viewer.icon _ document; -- necessary if you reloaded a non mesa file into a viewer that previously contained a needstobecompiled file. }; DoesThisNeedToBeCompiled: ENTRY PROC [name: Rope.ROPE] RETURNS[yes: BOOL] = { ENABLE UNWIND => NULL; FOR l: LIST OF ROPE _ needsToBeCompiled, l.rest UNTIL l = NIL DO IF Rope.Equal[l.first, name, FALSE] THEN RETURN[TRUE]; ENDLOOP; RETURN[FALSE]; }; ThisNeedsToBeCompiled: TiogaOps.CommandProc = { -- called for a save, and from CFinishProc when errors or warnings name: ROPE = viewer.name; IF IsAMesaFile[name] THEN { proc: UserExecPrivate.SplitViewerProc = { icon: Icons.IconFlavor = IconRegistry.GetIcon["NeedsToBeCompiled", viewer.icon]; IF icon # viewer.icon THEN { viewer.icon _ icon; IF viewer.iconic THEN ViewerOps.PaintViewer[viewer, all]; }; }; AddIt[name]; UserExecPrivate.ForAllSplitViewers[viewer, proc]; }; }; UseDirtyDocumentIcon: ViewerEvents.EventProc = { -- called when the first edit is made IF NOT IsAMesaFile[viewer.name] THEN RETURN; IF viewer.icon = IconRegistry.GetIcon["NeedsToBeCompiled", document] THEN viewer.icon _ dirtyDocument; }; IsAMesaFile: PROC [name: ROPE] RETURNS[BOOL] = INLINE { i: INT = Rope.Find[s1: name, s2: ".mesa", case: FALSE]; RETURN[i # -1 AND i = Rope.Length[name] - 5]; }; AddIt: ENTRY PROC[name: ROPE, addBoth: BOOL _ FALSE] = { IF Rope.Find[name, "."] = -1 THEN name _ Rope.Concat[name, ".mesa"]; FOR l: LIST OF ROPE _ needsToBeCompiled, l.rest UNTIL l = NIL DO IF Rope.Equal[l.first, name, FALSE] THEN EXIT; REPEAT FINISHED => needsToBeCompiled _ CONS[name, needsToBeCompiled]; ENDLOOP; IF addBoth THEN FOR l: LIST OF ROPE _ errorsOrWarnings, l.rest UNTIL l = NIL DO IF Rope.Equal[l.first, name, FALSE] THEN EXIT; REPEAT FINISHED => errorsOrWarnings _ CONS[name, errorsOrWarnings]; ENDLOOP; }; RemoveIt: ENTRY PROC[name: ROPE] = { lag: LIST OF ROPE; IF Rope.Find[name, "."] = -1 THEN name _ Rope.Concat[name, ".mesa"]; FOR l: LIST OF ROPE _ needsToBeCompiled, l.rest UNTIL l = NIL DO IF Rope.Equal[l.first, name, FALSE] THEN { IF lag = NIL THEN needsToBeCompiled _ l.rest ELSE lag.rest _ l.rest; EXIT; }; lag _ l; ENDLOOP; FOR l: LIST OF ROPE _ errorsOrWarnings, l.rest UNTIL l = NIL DO IF Rope.Equal[l.first, name, FALSE] THEN { IF lag = NIL THEN errorsOrWarnings _ l.rest ELSE lag.rest _ l.rest; EXIT; }; lag _ l; ENDLOOP; }; OpenErrorLog: ClickProc = { log: Viewer; viewer: Viewer = NARROW[parent]; name: ROPE = Rope.Concat[Rope.Substr[base: viewer.name, len: Rope.Find[viewer.name, "."]], ".errlog"]; IF (log _ ViewerOps.FindViewer[name]) # NIL THEN { IF log.iconic THEN ViewerOps.OpenIcon[log] ELSE { MessageWindow.Append[Rope.Concat[name, " is already open."], TRUE]; MessageWindow.Blink[]; }; } ELSE IF UserExec.CheckForFile[name] THEN { log: Viewer = TiogaMenuOps.Open[name]; log.icon _ typescript; } ELSE { MessageWindow.Append["Error log not found.", TRUE]; MessageWindow.Blink[]; }; }; <> UserExec.RegisterCommand["Bind", Bind, "Bind a list of configurations."]; UserExec.RegisterCommand["Binder", Bind, "Bind a list of configurations."]; -- do it this way, rather than as an alias, so I can redefine Bind to do a rename, etc. and then call binder UserExec.RegisterCommand["Compile", Compile, "Compile a list of modules."]; UserExec.RegisterCommand["Compiler", Compile, "Compile a list of modules."]; UserExec.RegisterTransformation["CompileAll", CompileAll, "Compiles everybody that needs to be compiled, i.e. all files that have been edited or whose compilation caused errors or warnings."]; UserExec.RegisterTransformation["CompileBadGuys", CompileBadGuys, "Compiles those files whose most recent compilation caused errors or warnings."]; UserProfile.CallWhenProfileChanges[SetDefaultSwitches]; IconRegistry.RegisterIcon["NeedsToBeCompiled", "UserExec.Icons", 9]; TiogaOps.RegisterCommand[name: $RedSave, proc: ThisNeedsToBeCompiled, before: FALSE]; TiogaOps.RegisterCommand[name: $BlueSave, proc: ThisNeedsToBeCompiled, before: FALSE]; TiogaOps.RegisterCommand[name: $YellowSave, proc: ThisNeedsToBeCompiled, before: FALSE]; -- takes care of changing the icon to needs to be compiled, and adding file to needsToBeCompiled. Note that the viewer might be split and one of its splittees be iconic, so can't rely on noticing the icon change when you close the viewer. TiogaOps.RegisterCommand[name: $RedReset, proc: ResetProc]; TiogaOps.RegisterCommand[name: $YellowReset, proc: ResetProc]; TiogaOps.RegisterCommand[name: $BlueReset, proc: ResetProc]; -- takes care of changing the icon back to needs to be compiled, where necessary.. [] _ ViewerEvents.RegisterEventProc[proc: UseDirtyDocumentIcon, filter: $Text, event: edit]; -- takes care of changing a NeedsToBeCompiled icon to a dirtydocument icon. <> <<>> [] _ ViewerEvents.RegisterEventProc[proc: CloseProc, filter: $Text, event: close]; <> END. <<>> <<>>