edited by Teitelman May 27, 1983 12:33 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: 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 ← []];
Running the Compiler
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;
[] ← 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 ← (Rope.Find[line, "/-g"] # -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];
};
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:
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, n:
INT ← 10] =
TRUSTED {
howOften: Process.Ticks = Process.MsecToTicks[500];
DO
ViewerOps.BlinkIcon[viewer];
n ← (n * 3)/2; -- gradually slow down rate of blinking
FOR i:
NAT
IN [0..2*n)
DO
-- blink every n seconds, but wake up every half second.
Process.Pause[howOften];
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 = {
IF viewer.newVersion THEN RETURN; -- should already have the dirtyDocument icon by virtue of the edit event registration
IF NOT IsAMesaFile[viewer.name] THEN RETURN
ELSE IF DoesThisNeedToBeCompiled[viewer.name] THEN viewer.icon ← IconRegistry.GetIcon["NeedsToBeCompiled", document] -- necessary if you opened a new viewer on a needstobecompiled file
ELSE viewer.icon ← document; -- necessary e.g. if you had cleared or reloaded into a needstobecompile viewer and then closed it
};
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.