edited by Teitelman June 20, 1983 4:34 pm
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;
Type Definitions
ExecHandle: TYPE = UserExec.ExecHandle;
HistoryEvent: TYPE = UserExec.HistoryEvent;
Viewer: TYPE = ViewerClasses.Viewer;
ClickProc: TYPE = Menus.ClickProc;
connecting concrete and opaque types
ExecPrivateRecord: PUBLIC TYPE = UserExecPrivate.ExecPrivateRecord;
to access evalMode in CreateExecProc, CreateExec, and ChangeAreaMode, and historyList in GetEventsFromSelection
HistoryEventPrivateRecord: PUBLIC TYPE = UserExecPrivate.HistoryEventPrivateRecord;
to access eventNum in RedoButton
Running the Binder
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: BOOLEANFALSE;
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 CHARACTERNIL,
exec: ExecHandle ← NIL,
in, out: STREAMNIL,
separateLogs: BOOL ← FALSE,
openLogs: BOOLTRUE,
abort: BOOLFALSE,
numberOfFiles: INT ← 0,
thisFile, warnings, errors: ROPENIL
];
FailedToLoad: ERROR = CODE;
binderRec: REF BinderRec ← NEW[BinderRec ← []];
Running the Compiler
needsToBeCompiled, errorsOrWarnings: LIST OF ROPENIL;
saveOldLog: BOOLTRUE;
compilerRec: REF BinderRec ← NEW[BinderRec ← []];
compilerSwitches, binderSwitches: ROPENIL;
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: BOOLEANFALSE;
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 {
the old way of doing business was to create both the source viewer and the log for each file that had errors or warnings. The log and viewer were opened if a variety of checks were satisfied, e.g. if the input focus was in the exec, the exec was opened, the user profile did not indicate otherwise, etc., otherwise iconic. If iconic, the log would blink unless profile said otherwise.
log: Viewer ← ShowLog[name: Rope.Concat[compilerRec.thisFile, ".errlog"], ok: FALSE, exec: IF compilerRec.openLogs THEN compilerRec.exec ELSE NIL];
compilerRec.openLogs ← FALSE; -- only open at most one icon
IF log # NIL THEN { -- we opened the errlog, open the source too.
IF viewer = NIL 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).
IF log.iconic THEN viewer ← ViewerOps.CreateViewer[flavor: $Text, info: [name: name, file: name, iconic: TRUE]]
ELSE viewer ← TiogaMenuOps.Open[name];
}
ELSE IF viewer.iconic AND NOT log.iconic THEN [] ← ViewerOps.OpenIcon[viewer];
};
IF viewer.ww <= Menus.FindEntry[viewer.menu, "ErrorLog"].xpos THEN -- not visible, do it old way.
The new way of doing business is to open only the viewer and to post a menu button for opening the log. The viewer blinks if it is iconic and the profie says so.
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: BOOLEANFALSE] = {
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: BOOLTRUE] 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: BOOLTRUE] 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;
};
Changing icons for files that need to be compiled
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[];
};
};
Initialization
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.
the above comprise those operations which can be performed to a single viewer, but which may also affect the state of other iconic viewers in the case where a viewer has been split. The other things to worry about are where a viewer is cleared, or reloaded, in which case its icon needs to be rechecked, or where a new viewer is created via Open, or middle clicking get, getimpl. Rather than register separate events for Load, Open, CloseAndOpen, LoadImpl, etc. simply notice that the icon needs to be changed when the viewer is closed, via the following:
[] ← ViewerEvents.RegisterEventProc[proc: CloseProc, filter: $Text, event: close];
Note that if there were a single event which corresponded to all of the above, e.g. if create did the right thing, then we should use that.
END.