<> DIRECTORY AMBridge USING [TVForReferent], AMModel USING [ContextName, ParentContext], BBContext USING [GlobalFrameSearch], Buttons USING [Create], ConvertUnsafe USING [ToRope], ExecOps USING [Outcome], DeskTops USING [Create], Directory USING [Lookup, ignore, GetProps], Feedback USING [BeginItemProc, CreateProc, DestroyProc, FinishItemProc, NoteProgressProc, Procs], File USING [Capability], InputFocus USING [GetInputFocus], IO USING [char, CharsAvail, CreateOutputStreamToRope, EndOf, EndOfStream, Error, Flush, GetChar, GetOutputStreamRope, GetToken, IDProc, int, noWhereStream, Put, PutF, PutRope, PutTV, ROPE, rope, SkipOver, Signal, STREAM, string, TokenProc, TV, UserAborted, WhiteSpace], List USING [Nconc1], Menus USING [AppendMenuEntry, ClickProc, CreateEntry, Menu], MessageWindow USING [Append, Blink], Process USING [Detach, GetPriority, MsecToTicks, Pause, Priority, priorityBackground, SetPriority, SetTimeout, Ticks], Rope USING [Cat, Concat, Equal, Fetch, Find, IsEmpty, Length, Letter, Replace, ROPE, Substr, ToRefText], Runtime USING [IsBound, RunConfig], Spell USING [defaultModes], TemporarySpecialExecOps USING [BindUsingFeedback, CompileUsingFeedback], TiogaMenuOps USING [Open, DefaultMenus], TiogaOps USING [GetSelection, GetTextKey, LastLocWithin, TextKeyNotFound, ViewerDoc, SelectPoint, Location, SelectionGrain], TypeScript USING [Create], UserExec USING [AcquireResource, CheckForAbort, CheckForFile, CommandProc, Confirm, CreateUserExecutive, ExecHandle, GetExecHandle, GetStreams, GetTheFile, HistoryEvent, HistoryList, MethodProc, RegisterCommand, RegisterMethod, ReleaseResource, RopeSubst, SetUserAbort, SetUserResponse, StartListening, UserAbort, UserAborted], UserExecExtras USING [], UserExecPrivate USING [CaptionForExec, DestroyExec, execHandleList, ExecPrivateRecord, GetPrivateStuff, HistoryEventPrivateRecord, ReadQuietly, RenameFile, version, SplitViewerProc, StoreInSymTab], UserProfile USING [Boolean, CallWhenProfileChanges, ProfileChanged, ProfileChangedProc, Token, GetErrorLog], ViewerAbort USING [SetUserAbort], ViewerClasses USING [Column, Viewer], ViewerEvents USING [EventProc, RegisterEventProc], ViewerOps USING [BlinkIcon, ComputeColumn, CreateViewer, DestroyViewer, FindViewer, OpenIcon, RestoreViewer, SaveViewer], ViewerTools USING [GetSelectedViewer, GetSelectionContents, SelPos, SetSelection] ; ViewerExecOpsImpl: CEDAR MONITOR LOCKS NARROW[exec.privateStuff, REF ExecPrivateRecord] USING exec: UserExec.ExecHandle IMPORTS AMBridge, AMModel, BBContext, Buttons, ConvertUnsafe, DeskTops, Directory, InputFocus, IO, List, Menus, MessageWindow, Process, Rope, Runtime, Spell, TemporarySpecialExecOps, TiogaMenuOps, TiogaOps, TypeScript, UserExec, UserExecPrivate, UserProfile, ViewerAbort, ViewerEvents, ViewerOps, ViewerTools EXPORTS UserExec, UserExecExtras, 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 = UserExec.GetStreams[exec].out; 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.out _ 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)]; 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 = { ENABLE { IO.Error => IF ec = StreamClosed THEN {UserExec.SetUserAbort[binderRec.exec]; CONTINUE}; ABORTED => CONTINUE; -- binder will notice this next time it checks. IO.UserAborted => CONTINUE; }; binderRec.out.Put[string[trailer]]; }; BBeginItem: Feedback.BeginItemProc = { ENABLE { IO.Error => IF ec = StreamClosed THEN {UserExec.SetUserAbort[binderRec.exec]; CONTINUE}; ABORTED => CONTINUE; -- binder will notice this next time it checks. IO.UserAborted => CONTINUE; }; binderRec.out.Put[string[item]]; }; BNoteProgress: Feedback.NoteProgressProc = { ENABLE { IO.Error => IF ec = StreamClosed THEN {UserExec.SetUserAbort[binderRec.exec]; CONTINUE}; ABORTED => CONTINUE; -- binder will notice this next time it checks. IO.UserAborted => CONTINUE; }; binderRec.out.PutRope[" ."]; }; BFinishItem: Feedback.FinishItemProc = { ENABLE { IO.Error => IF ec = StreamClosed THEN {UserExec.SetUserAbort[binderRec.exec]; CONTINUE}; ABORTED => CONTINUE; -- binder will notice this next time it checks. IO.UserAborted => CONTINUE; }; binderRec.out.PutF[" %g\n", string[trailer]]; }; 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, out: STREAM _ NIL, separateLogs: BOOL _ FALSE, openLogs: BOOL _ TRUE, numberOfFiles: INT _ 0, thisFile, warnings, errors, errorsOrWarnings: ROPE _ NIL ]; FailedToLoad: ERROR = CODE; binderRec: REF BinderRec _ NEW[BinderRec _ []]; <> saveOldLog: BOOL _ TRUE; compilerRec: REF BinderRec _ NEW[BinderRec _ []]; 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]; IF IO.EndOf[event.commandLineStream] AND NOT Rope.IsEmpty[compilerRec.errorsOrWarnings] THEN { line: ROPE = Rope.Concat["compile", compilerRec.errorsOrWarnings]; UserExec.GetStreams[].out.PutRope[line]; UserExecPrivate.ReadQuietly[rope: line, exec: exec]; RETURN[TRUE]; }; 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]; }; 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]; out _ UserExec.GetStreams[exec].out; compilerRec.exec _ exec; compilerRec.out _ 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.cmdText _ LOOPHOLE[Rope.ToRefText[line], REF StringBody]; compilerRec.cmd _ LOOPHOLE[@(compilerRec.cmdText.text)]; compilerRec.errors _ compilerRec.warnings _ compilerRec.errorsOrWarnings_ 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 = { ENABLE { IO.Error => IF ec = StreamClosed THEN {UserExec.SetUserAbort[compilerRec.exec]; CONTINUE}; ABORTED => CONTINUE; -- compiler will notice this next time it checks. IO.UserAborted => CONTINUE; }; compilerRec.out.Put[string[trailer]]; }; CBeginItem: Feedback.BeginItemProc = { ENABLE { IO.Error => IF ec = StreamClosed THEN {UserExec.SetUserAbort[compilerRec.exec]; CONTINUE}; ABORTED => CONTINUE; -- compiler will notice this next time it checks. IO.UserAborted => CONTINUE; }; compilerRec.out.Put[string[item]]; 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 = { ENABLE { IO.Error => IF ec = StreamClosed THEN {UserExec.SetUserAbort[compilerRec.exec]; CONTINUE}; ABORTED => CONTINUE; -- compiler will notice this next time it checks. IO.UserAborted => CONTINUE; }; compilerRec.out.PutRope[" ."]; }; CFinishItem: Feedback.FinishItemProc = { ENABLE { IO.Error => IF ec = StreamClosed THEN {UserExec.SetUserAbort[compilerRec.exec]; CONTINUE}; ABORTED => CONTINUE; -- compiler will notice this next time it checks. IO.UserAborted => CONTINUE; }; space: ROPE = " "; compilerRec.numberOfFiles _ compilerRec.numberOfFiles + 1; compilerRec.out.Put[char[' ], string[trailer], char['\n]]; SELECT outcome FROM warnings, errors, errorsAndWarnings => { compilerRec.errorsOrWarnings _ Rope.Cat[compilerRec.errorsOrWarnings, space, compilerRec.thisFile]; -- need to get these in right order. IF outcome = warnings THEN compilerRec.warnings _ Rope.Cat[compilerRec.warnings, space, compilerRec.thisFile] ELSE compilerRec.errors _ Rope.Cat[compilerRec.errors, space, compilerRec.thisFile]; IF compilerRec.separateLogs THEN { 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. name: ROPE = Rope.Concat[compilerRec.thisFile, ".mesa"]; viewer: Viewer = ViewerOps.FindViewer[name]; 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 [] _ ViewerOps.CreateViewer[flavor: $Text, info: [name: name, file: name, iconic: TRUE]] ELSE [] _ TiogaMenuOps.Open[name]; } ELSE IF viewer.iconic AND NOT log.iconic THEN [] _ ViewerOps.OpenIcon[viewer]; }; }; }; ENDCASE => IF compilerRec.separateLogs THEN -- restore log -- [] _ ShowLog[Rope.Concat[compilerRec.thisFile, ".errlog"], TRUE, compilerRec.exec]; }; compilerProcs: Feedback.Procs _ [CCreate, CDestroy, CBeginItem, CNoteProgress, CFinishItem]; 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]; experimentalNewFeatures _ UserProfile.Boolean["ExperimentalNewFeatures", FALSE]; }; 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]]; 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. }; WaitUntilSaved: PUBLIC PROC [fileName: ROPE, exec: UserExec.ExecHandle] = { viewer: Viewer = ViewerOps.FindViewer[fileName]; IF viewer # NIL AND viewer.newVersion THEN { waitForSave: CONDITION; SomeSaveInProgress: PROC RETURNS[someSaveInProgress: BOOL _ FALSE] = CHECKED { v: Viewer _ viewer; IF viewer.saveInProgress THEN RETURN[TRUE]; WHILE (v _ v.link) # NIL AND (v # viewer) DO IF v.saveInProgress THEN RETURN[TRUE]; ENDLOOP; }; WaitForSave: ENTRY PROC [exec: ExecHandle] = CHECKED { ENABLE UNWIND => NULL; WHILE SomeSaveInProgress[] DO WAIT waitForSave; UserExec.CheckForAbort[exec]; ENDLOOP; }; out: IO.STREAM; IF NOT SomeSaveInProgress[] THEN { IF UserProfile.Boolean["Compiler.AutoSave", FALSE] THEN NULL ELSE IF NOT UserExec.Confirm[msg: Rope.Concat["Save ", viewer.name], timeout: Spell.defaultModes.timeout, defaultConfirm: Spell.defaultModes.defaultConfirm, exec: exec] THEN UserExec.UserAborted[exec]; }; out _ UserExec.GetStreams[exec].out; IF SomeSaveInProgress[] THEN -- if we get to here, either a save is in progress, or the user said it was ok to save. The reason for checking again is that if the user clicked Save and then called compile, the save might not have started at the time the user asked whether or not to save. While he was confirming, the save got started. Now only necessary to do one save. TRUSTED { out.PutF["*n*mWaiting for %g to finish being saved...", rope[viewer.name]]; Process.SetTimeout[@waitForSave, 100]; WaitForSave[exec]; } ELSE { out.PutF["*n*mSaving %g...", rope[viewer.name]]; ViewerOps.SaveViewer[viewer]; }; out.PutF["ok*s\n"]; } }; 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; }; <> CreateExecProc: UserExec.CommandProc = { private: REF ExecPrivateRecord = exec.privateStuff; CreateExec[private.evalMode]; }; <> <> <> <> <> <> <> <> <> <<[] _ UserExecPrivate.CaptionForExec[exec: exec, paint: FALSE]; >> <> <> <<};>> <<>> CreateExec: PROC [evalMode: BOOL] = { exec: UserExec.ExecHandle = UserExec.CreateUserExecutive[paint: FALSE, startListening: FALSE, iconic: TRUE]; viewer: ViewerClasses.Viewer = exec.viewer; private: REF ExecPrivateRecord = exec.privateStuff; private.evalMode _ evalMode; [] _ UserExecPrivate.CaptionForExec[exec: exec, paint: FALSE]; ViewerOps.OpenIcon[viewer]; ViewerTools.SetSelection[viewer]; UserExec.StartListening[exec]; }; DestroyExec: UserExec.CommandProc = TRUSTED { IF exec = NIL THEN RETURN[FALSE]; IF UserExecPrivate.execHandleList.rest = NIL THEN Process.Detach[FORK CreateNewWorkAreaButton[]]; UserExecPrivate.DestroyExec[exec]; }; newWorkAreaButtonUp: BOOL _ FALSE; CreateNewWorkAreaButton: PROC = { IF NOT newWorkAreaButtonUp THEN { [] _ Buttons.Create[info: [name: "Exec"], documentation: "Create New Work Area", proc: NewExec]; newWorkAreaButtonUp _ TRUE; }; }; Herald: UserExec.CommandProc = { commandLine: ROPE = event.commandLine; out: STREAM; IF exec = NIL THEN RETURN; out _ UserExec.GetStreams[exec].out; out.PutF["*n"]; out.Flush[]; [] _ exec.viewer.class.scroll[self: exec.viewer, op: up, amount: LAST[INTEGER]]; out.PutF[UserExecPrivate.version]; out.PutF[commandLine]; }; ChangeAreaMode: UserExec.CommandProc = { private: REF ExecPrivateRecord = exec.privateStuff; private.evalMode _ NOT private.evalMode; [] _ UserExecPrivate.CaptionForExec[exec]; }; <> Desktop: UserExec.CommandProc = TRUSTED { commandLineStream: STREAM = event.commandLineStream; name: ROPE; name _ IO.GetToken[commandLineStream, IO.IDProc]; IF Rope.IsEmpty[name] THEN name _ "Default"; [] _ DeskTops.Create[name: name]; RETURN[TRUE]; }; NewExec: ClickProc -- PROC [viewer: Viewer, clientData: REF ANY, redButton: BOOL] -- = { CreateExec[(mouseButton = blue)]; }; Stop: ClickProc -- [parent: REF ANY, clientData: REF ANY _ NIL, mouseButton: MouseButton _ red, shift, control: BOOL _ FALSE] -- = { viewer: Viewer = NARROW[parent]; exec: ExecHandle = FindExecFromViewer[viewer]; private: REF UserExecPrivate.ExecPrivateRecord = UserExecPrivate.GetPrivateStuff[exec]; stream: IO.STREAM; stream _ private.in; UNTIL stream = NIL DO FlushChars: PROC [stream: IO.STREAM] = { WHILE stream.CharsAvail[] DO [] _ stream.GetChar[! IO.Signal => IF ec = Rubout THEN CONTINUE; ]; ENDLOOP; }; FlushChars[stream]; stream _ stream.backingStream; -- so that if user mistakenly stuffs a large amount of text, can clear it out. ENDLOOP; ViewerAbort.SetUserAbort[viewer]; UserExec.SetUserResponse[viewer, NIL]; -- in case exec is waiting on confirmation. }; CompileButton: ClickProc -- [parent: REF ANY, clientData: REF ANY _ NIL, mouseButton: MouseButton _ red, shift, control: BOOL _ FALSE] -- = { viewer: Viewer = NARROW[parent]; exec: ExecHandle = FindExecFromViewer[viewer]; selectedViewer: Viewer _ ViewerTools.GetSelectedViewer[]; sel: Rope.ROPE _ ViewerTools.GetSelectionContents[]; r: ROPE; pointSel: BOOL _ FALSE; Failed: PROC [rope: ROPE] = TRUSTED { MessageWindow.Append[rope, TRUE]; -- don't acquire streams, because may already be acquired. MessageWindow.Blink[]; }; FindConfig: PROC [name: ROPE] RETURNS[config: ROPE] = TRUSTED { tv: TV; i: INT; IF Rope.Find[s1: name, s2: ".config", case: FALSE] # -1 THEN RETURN[Rope.Concat[name, "\n"]]; IF (i _ Rope.Find[name, "."]) # -1 THEN name _ Rope.Substr[base: name, len: i]; tv _ BBContext.GlobalFrameSearch[context: NIL, frameName: name].gf; IF tv # NIL THEN {ENABLE ANY => CONTINUE; name: ROPE = AMModel.ContextName[AMModel.ParentContext[tv]]; IF NOT Rope.IsEmpty[name] AND (i _ Rope.Find[name, ":"]) # -1 THEN config _ Rope.Substr[base: name, len: Rope.Find[name, ":"]]; }; IF Rope.IsEmpty[config] THEN {MessageWindow.Append[Rope.Concat["Couldn't find config containing ", name], TRUE]; MessageWindow.Blink[]; }; }; IF selectedViewer = NIL THEN {Failed["No selected viewer"]; RETURN}; IF Rope.Length[sel] <= 1 THEN { IF selectedViewer.class.flavor # $Text THEN {Failed["Selected viewer not a tioga document"]; RETURN}; pointSel _ TRUE; sel _ selectedViewer.name; -- point selection, compile indicated viewer }; IF ViewerTools.GetSelectedViewer[] # viewer THEN ViewerTools.SetSelection[viewer, NIL]; { i: INT _ 0; WHILE (i _ Rope.Find[s1: sel, s2: "\n", pos1: i]) # -1 DO IF i + 1 < Rope.Length[sel] THEN sel _ Rope.Replace[base: sel, start: i, len: 1, with: " "]; i _ i + 1; ENDLOOP; }; SELECT mouseButton FROM red => r _ Rope.Cat[IF Rope.Find[s1: sel, s2: ".config", case: FALSE] # -1 THEN "Bind " ELSE "Compile ", sel, "\n"]; yellow => IF NOT pointSel THEN r _ Rope.Cat["Bind ", sel, "\n"] ELSE IF NOT Rope.IsEmpty[r _ FindConfig[sel]] THEN r _ Rope.Cat["Bind ", r]; blue => {r _ FindConfig[sel]; IF Rope.IsEmpty[r] THEN r _ "??"; r _ Rope.Cat["Compile ", sel, "; Bind ", r]; }; ENDCASE => ERROR; IF NOT Rope.IsEmpty[r] THEN StuffIt[exec, Rope.Cat["\021", r]];-- control q to erase any typeahead on the line. }; EvalButton: ClickProc -- [parent: REF ANY, clientData: REF ANY _ NIL, mouseButton: MouseButton _ red, shift, control: BOOL _ FALSE] -- = { viewer: Viewer = NARROW[parent]; exec: ExecHandle = FindExecFromViewer[viewer]; selectedViewer: Viewer _ ViewerTools.GetSelectedViewer[]; sel: Rope.ROPE _ ViewerTools.GetSelectionContents[]; Failed: PROC [rope: ROPE] = TRUSTED { MessageWindow.Append[rope, TRUE]; -- don't acquire streams, because may already be acquired. MessageWindow.Blink[]; }; IF selectedViewer = NIL THEN {Failed["No selection"]; RETURN}; StuffIt[exec, IF mouseButton = blue THEN "\021CurrentSelection\n" ELSE Rope.Cat["\021", "_ ", sel, "\n"]];-- control q to erase any typeahead on the line. }; RedoButton: Menus.ClickProc -- [parent: REF ANY, clientData: REF ANY _ NIL, mouseButton: MouseButton _ red, shift, control: BOOL _ FALSE] -- = { viewer: Viewer = NARROW[parent]; exec: UserExec.ExecHandle = FindExecFromViewer[viewer]; start, end: HistoryEvent; startsPrivateStuff, endsPrivateStuff: REF HistoryEventPrivateRecord; out: STREAM = CreateOutputStreamToRope[]; [start, end] _ GetEventsFromSelection[exec]; IF start = NIL THEN RETURN; startsPrivateStuff _ start.privateStuff; endsPrivateStuff _ end.privateStuff; FOR i: NAT IN [startsPrivateStuff.eventNum..endsPrivateStuff.eventNum] DO out.Put[char[' ], int[i]]; ENDLOOP; StuffIt[exec, Rope.Cat["\021", "Redo", IO.GetOutputStreamRope[out], IF mouseButton = red THEN "\n" ELSE "\030"]]; }; StuffIt: PUBLIC PROCEDURE [exec: UserExec.ExecHandle, rope: ROPE, list: LIST OF REF ANY _ NIL] = { private: REF UserExecPrivate.ExecPrivateRecord = UserExecPrivate.GetPrivateStuff[exec]; viewer: Viewer = exec.viewer; IF TiogaOps.GetSelection[].viewer = viewer THEN TiogaOps.SelectPoint[viewer: viewer, caret: TiogaOps.LastLocWithin[TiogaOps.ViewerDoc[viewer]]]; IF NOT Rope.IsEmpty[rope] THEN viewer.class.notify[viewer, LIST[rope]]; IF list # NIL THEN viewer.class.notify[viewer, list]; }; GetEventsFromSelection: PROC [exec: UserExec.ExecHandle] RETURNS [start, end: HistoryEvent] = { viewer: ViewerClasses.Viewer; private: REF UserExecPrivate.ExecPrivateRecord = UserExecPrivate.GetPrivateStuff[exec]; loc1, loc2: TiogaOps.Location; [viewer, loc1, loc2] _ TiogaOps.GetSelection[]; IF FindExecFromViewer[viewer] # exec THEN RETURN[private.historyList.first, private.historyList.first]; -- selection is in some other viewer other than the one containing the redo button IF loc1.node # loc2.node THEN ERROR; -- should worry about being in separate nodes FOR l: UserExec.HistoryList _ private.historyList, l.rest UNTIL l = NIL DO event: HistoryEvent = l.first; loc: TiogaOps.Location = TiogaOps.GetTextKey[node: loc1.node, key: l.first ! TiogaOps.TextKeyNotFound => GOTO Cant]; IF loc.node # loc1.node THEN ERROR; IF end = NIL AND loc.where < loc2.where THEN end _ event; IF loc.where < loc1.where THEN {start _ event; RETURN}; ENDLOOP; EXITS Cant => { MessageWindow.Append[message: "Can't find corresponding event", clearFirst: TRUE]; MessageWindow.Blink[]; RETURN; }; }; <> NewProc: UserExec.CommandProc = { commandLineStream: STREAM = event.commandLineStream; name: ROPE; UNTIL Rope.IsEmpty[name _ IO.GetToken[commandLineStream, IO.WhiteSpace ! IO.EndOfStream => GOTO Out]] DO OpenViewer[name: name, event: event, exec: exec, newViewer: TRUE]; ENDLOOP; EXITS Out => RETURN; }; OpenProc: UserExec.CommandProc = { commandLineStream: STREAM = event.commandLineStream; name: ROPE; UNTIL Rope.IsEmpty[name _ IO.GetToken[commandLineStream, IO.WhiteSpace ! IO.EndOfStream => GOTO Out]] DO OpenViewer[name: name, event: event, exec: exec, newViewer: FALSE]; ENDLOOP; EXITS Out => RETURN; }; ImplicitOpen: UserExec.MethodProc = { commandLineStream: STREAM = event.commandLineStream; name: ROPE; UNTIL Rope.IsEmpty[name _ IO.GetToken[commandLineStream, IO.WhiteSpace ! IO.EndOfStream => GOTO Out]] DO IF Rope.Find[s1: name, s2: ".mesa", case: FALSE] = -1 THEN RETURN[FALSE]; OpenViewer[name: name, event: event, exec: exec, newViewer: FALSE]; ENDLOOP; EXITS Out => RETURN; }; OpenViewer: PROC [name: ROPE, event: HistoryEvent, exec: ExecHandle, newViewer: BOOLEAN _ FALSE] = { viewer: Viewer; out: IO.STREAM = UserExec.GetStreams[exec].out; IF newViewer THEN { viewer _ ViewerOps.CreateViewer[flavor: $Text, info: [name: IF Rope.IsEmpty[name] THEN "No Name" ELSE name, file: name, iconic: FALSE], paint: FALSE]; TiogaMenuOps.DefaultMenus[viewer, FALSE]; -- does not recompute column ViewerOps.ComputeColumn[viewer.column]; } ELSE viewer _ TiogaMenuOps.Open[name-- , exec.viewer --]; -- workaround IF viewer = NIL THEN out.PutF["*n*mViewer file not found: %g*s", rope[name]] ELSE TRUSTED { tv: TV = AMBridge.TVForReferent[NEW[ViewerClasses.Viewer _ viewer]]; out.PutF["*nCreated Viewer: %g\n", rope[viewer.name]]; out.PutTV[tv: tv]; UserExecPrivate.StoreInSymTab[value: tv, event: event, exec: exec]; }; }; DirectoryLookup: PROC [name: LONG STRING] = TRUSTED { f: File.Capability _ Directory.Lookup[fileName: name, permissions: Directory.ignore]; [] _ Directory.GetProps[f, name]; }; FindExecFromViewer: PUBLIC PROC [viewer: Viewer] RETURNS [ExecHandle] = { FOR lst: LIST OF ExecHandle _ UserExecPrivate.execHandleList, lst.rest UNTIL lst = NIL DO v1: Viewer _ NARROW[lst.first.viewer]; v2: Viewer; IF v1 = viewer THEN RETURN[lst.first]; IF (v2 _ v1.link) # NIL THEN UNTIL v2 = v1 DO IF v2 = viewer THEN RETURN[lst.first]; v2 _ v2.link; ENDLOOP; ENDLOOP; RETURN[NIL]; }; CurrentSelection: UserExec.CommandProc = { value: REF ANY; handle: UserExec.ExecHandle; TiogaSelection: TYPE = RECORD[ viewer: Viewer, start, end: TiogaOps.Location, level: TiogaOps.SelectionGrain, caretBefore: BOOL, pendingDelete: BOOL ]; TiogaSelection1: TYPE = RECORD[ handle: UserExec.ExecHandle, viewer: Viewer, start, end: TiogaOps.Location, level: TiogaOps.SelectionGrain, caretBefore: BOOL, pendingDelete: BOOL ]; sel: TiogaSelection; [sel.viewer, sel.start, sel.end, sel.level, sel.caretBefore, sel.pendingDelete] _ TiogaOps.GetSelection[]; IF sel.viewer = NIL THEN { viewer: Viewer _ ViewerTools.GetSelectedViewer[]; IF viewer # NIL THEN { IF (handle _ UserExec.GetExecHandle[viewer: viewer]) # NIL THEN value _ NEW[UserExec.ExecHandle _ handle] ELSE value _ NEW[ViewerClasses.Viewer _ viewer]; }; } ELSE { IF (handle _ UserExec.GetExecHandle[viewer: sel.viewer]) # NIL THEN value _ NEW[TiogaSelection1 _ [handle, sel.viewer, sel.start, sel.end, sel.level, sel.caretBefore, sel.pendingDelete]] ELSE value _ NEW[TiogaSelection _ sel]; }; IF value = NIL THEN UserExec.GetStreams[exec].out.PutF["*n*mNothing selected\n"] ELSE TRUSTED { tv: TV = AMBridge.TVForReferent[value]; UserExec.GetStreams[exec].out.PutTV[tv: tv]; UserExecPrivate.StoreInSymTab[value: tv, event: event, exec: exec]; }; }; <> ForAllSplitViewers: PUBLIC PROC [viewer: Viewer, proc: UserExecPrivate.SplitViewerProc] = { v: Viewer _ viewer; proc[viewer]; WHILE (v _ v.link) # NIL AND (v # viewer) DO proc[v]; ENDLOOP; }; WasProfileEdited: ViewerEvents.EventProc -- [viewer: ViewerClasses.Viewer, event: ViewerEvent] -- = { name: ROPE = viewer.name; pos: INT = Rope.Find[name, "."]; IF pos # -1 AND Rope.Equal["profile", Rope.Substr[base: name, start: pos + 1], FALSE] THEN TRUSTED {Process.Detach[FORK ProcessProfile[]]}; }; WasAnExecDestroyed: ViewerEvents.EventProc -- [viewer: ViewerClasses.Viewer, event: ViewerEvent] -- = { exec: ExecHandle = FindExecFromViewer[viewer]; IF exec # NIL THEN { v: Viewer _ viewer; WHILE (v _ v.link) # NIL AND (v # viewer) DO -- split IF NOT v.destroyed THEN { exec.viewer _ v; IF ViewerTools.GetSelectedViewer[] = viewer THEN ViewerTools.SetSelection[v, NIL]; RETURN; }; ENDLOOP; [] _ DestroyExec[event: NIL, exec: exec]; }; }; ProcessProfile: PUBLIC PROC = { errorLog: ROPE; MessageWindow.Append[message: "Processing User Profile for changed entries...", clearFirst: TRUE]; UserProfile.ProfileChanged[edit]; IF (errorLog _ UserProfile.GetErrorLog[]) # NIL THEN MessageWindow.Append[Rope.Cat["problems encountered, see ", errorLog, "..."]]; MessageWindow.Append["Done"]; IF errorLog # NIL THEN MessageWindow.Blink[]; }; <> experimentalNewFeatures: PUBLIC BOOLEAN; execMenu: PUBLIC Menus.Menu _ NIL; Init: PROC = { viewer: ViewerClasses.Viewer = TypeScript.Create[info: [], paint: FALSE]; execMenu _ viewer.menu; ViewerOps.DestroyViewer[viewer]; -- created solely to get hold of the menu Menus.AppendMenuEntry[menu: execMenu, entry: Menus.CreateEntry[name: "New", proc: NewExec, documentation: "Create New Work Area", fork: FALSE]]; -- not forked since is going to change the input focus Menus.AppendMenuEntry[menu: execMenu, entry: Menus.CreateEntry[name: "Stop", proc: Stop, documentation: "Aborts current operation", fork: FALSE]]; Menus.AppendMenuEntry[menu: execMenu, entry: Menus.CreateEntry [name: "Compile", proc: CompileButton, fork: TRUE, documentation: "Compiles the indicated file/viewer, first saving contents if edits have been performed."]]; Menus.AppendMenuEntry[menu: execMenu, entry: Menus.CreateEntry [name: "Eval", proc: EvalButton, fork: TRUE, documentation: "Evaluates the current selection in the corresponding work area."]]; Menus.AppendMenuEntry[menu: execMenu, entry: Menus.CreateEntry[name: "Redo", proc: RedoButton, documentation: "Redo selected event(s)"]]; }; UserExec.RegisterCommand["Bind", Bind, "Bind a list of configurations."]; UserExec.RegisterCommand["Compile", Compile, "Compile a list of modules."]; UserExec.RegisterCommand["New", NewProc, "Create new viewer(s)."]; UserExec.RegisterCommand["Open", OpenProc, "Create viewer(s) on existing file(s)."]; UserExec.RegisterMethod[proc: ImplicitOpen, name: "Implicit Open", doc: "If the first token on the input line is of the form file.mesa, same as Open file.mesa, i.e. creates a viewer on file.mesa."]; UserExec.RegisterCommand["DestroyExec", DestroyExec, "Terminates an executive."]; UserExec.RegisterCommand["CreateExec", CreateExecProc, "Creates a new executive.", "Creates a new executive."]; UserExec.RegisterCommand["Desktop", Desktop, "Creates a new desktop with the given name."]; UserExec.RegisterCommand["Herald", Herald, "Scrolls executive to top and reprints herald."]; UserExec.RegisterCommand["ChangeAreaMode", ChangeAreaMode, "Makes an Interpreter into an Executive and vice versa."]; UserExec.RegisterCommand["CurrentSelection", CurrentSelection, "Describes the selected object.", "Describes the selected object. Not terribly useful unless invoked via (right clicking) the Eval menu button."]; [] _ ViewerEvents.RegisterEventProc[proc: WasProfileEdited, event: save, filter: $Text, before: FALSE]; [] _ ViewerEvents.RegisterEventProc[proc: WasAnExecDestroyed, filter: $Typescript, event: destroy]; UserProfile.CallWhenProfileChanges[SetDefaultSwitches]; Init[]; END. <> <> <> <> < bind, blue button => compile and bind>> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <<>> <> <> <> <> <> <<>> <<>>