<> <> DIRECTORY ActionAreasDefs USING [actionAreaIcon, RestoreSelection, inactiveActionAreaIcon], AMBridge USING [GetWorld], AMEvents USING [Debugging, Debugged], BBAction USING [Abort, Action, ActionId, TakeData], BBBreak USING [BreakEntry, ClearBreak, BreakExit, BreakId, NextBreak, NullIndex, FindBreakId, BreakIndex, ClearAllBreaks], BBContext USING [Context, ContextForLocalFrame, GetContents], BBEval USING [EvalHead], BBObjectLocation USING [LocationToSource, TVFromLocation], BBSafety USING [Mother], BBVForUserExec USING [ReportProc, Severity, ClearBreak, SourceFromTV, OpenSource], BBVExtras USING [ClearActionBreakPoint, DisplayLocalFrame, WalkContext, SetBreakFromPosition], Convert USING [ValueToRope], IO USING [CurrentPosition, SyntaxError, SkipOver, PeekChar, EndOf, EndOfStream, Flush, GetInt, PutChar, PutF, PutRope, int, rope, ROPE, STREAM, tv, TV, RIS, GetToken, IDProc, WhiteSpace], Menus USING [AppendMenuEntry, ClickProc, CreateEntry, GetLine, Menu, MenuEntry, SetLine], MessageWindow USING [Append, Blink, Clear], Process USING [Detach, Pause, MsecToTicks, Ticks], Rope USING [Equal, Fetch, Find, IsEmpty, Length, Cat, Concat, Substr], SymTab USING [Fetch], TiogaOps USING [FirstChild, GetCaret, GetRope, GetSelection, GoToPreviousCharacter, InsertChar, LastLocWithin, Location, LockSel, UnlockSel, LocOffset, LocRelative, Paste, SaveSpanForPaste, SelectionGrain, SelectPoint, AddLooks, SetSelection, ViewerDoc], UserExec USING [HistoryEvent, RegisterCommand, CommandProc, ExecHandle, Expression, EvaluationFailed, CreateExpr, EvalExpr, Viewer, GetExecHandle, GetStreams, StuffIt, DoIt, RopeSubst], UserExecPrivate USING [ActionAreaData, CaptionForExec, GetEvalHead, execMenu, ExecPrivateRecord, GoToSleep, execHandleList, GetPrivateStuff, tildas, secondMenuLine, ForAllSplitViewers, SplitViewerProc, StoreInSymTab, GetEventsFromSelection, HistoryEventPrivateRecord], UserProfile USING [Boolean], ViewerClasses USING [Viewer], ViewerOps USING [PaintViewer, SetMenu, CloseViewer, DestroyViewer, FindViewer], ViewerTools USING [GetSelectedViewer, GetSelectionContents], WorldVM USING [World, WorldName, LocalWorld] ; ActionAreasOpsImpl: CEDAR PROGRAM IMPORTS ActionAreasDefs, AMBridge, AMEvents, BBAction, BBBreak, BBContext, BBObjectLocation, BBSafety, BBVExtras, BBVForUserExec, Convert, IO, Menus, MessageWindow, Process, Rope, SymTab, TiogaOps, UserExec, UserExecPrivate, UserProfile, ViewerOps, ViewerTools, WorldVM EXPORTS UserExec, UserExecPrivate, ActionAreasDefs = BEGIN OPEN IO; tildas: PUBLIC ROPE _ "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"; <> ExecHandle: TYPE = UserExec.ExecHandle; Viewer: TYPE = UserExec.Viewer; ClickProc: TYPE = Menus.ClickProc; Action: TYPE = BBAction.Action; ExecPrivateRecord: PUBLIC TYPE = UserExecPrivate.ExecPrivateRecord; HistoryEventPrivateRecord: PUBLIC TYPE = UserExecPrivate.HistoryEventPrivateRecord; <> <> SetBreak: ClickProc = { viewer: Viewer _ NARROW[parent]; exec: ExecHandle = UserExec.GetExecHandle[viewer: viewer]; start: TiogaOps.Location; command: ROPE; private: REF ExecPrivateRecord = UserExecPrivate.GetPrivateStuff[exec]; IF private.execState = suspended THEN { MessageWindow.Append["That exec is busy. Use another one.", TRUE]; MessageWindow.Blink[]; RETURN; }; [viewer, start] _ TiogaOps.GetSelection[]; IF viewer = NIL THEN {ReportExec[exec, "Make a selection.", fatal]; RETURN}; SELECT mouseButton FROM red => command _ "SetBreak"; yellow, blue => { sel: ROPE = ViewerTools.GetSelectionContents[]; IF mouseButton = yellow THEN command _ "BreakAtEntry" ELSE command _ "BreakAtExit"; IF Rope.Length[sel] > 1 THEN { command _ Rope.Cat[command, " ", sel]; viewer _ NIL; }; }; ENDCASE => ERROR; IF viewer # NIL THEN command _ Rope.Cat[command, " ", viewer.name, " ", Convert.ValueToRope[[signed[TiogaOps.LocOffset[loc1: [TiogaOps.ViewerDoc[viewer], 0], loc2: start, skipCommentNodes: TRUE]]]]]; IF private.execState = dormant THEN -- dormant, i.e. not waiting for input. must call direct, but have to fork because the command is not supposed to be executed under notifier TRUSTED {Process.Detach[FORK UserExec.DoIt[input: Rope.Concat[command, "\n"], exec: exec]]} ELSE MakeExecCommand[NARROW[parent], command, TRUE]; <> }; ClearBreak: ClickProc = { viewer: Viewer = NARROW[parent]; exec: ExecHandle = UserExec.GetExecHandle[viewer: viewer]; private: REF ExecPrivateRecord = UserExecPrivate.GetPrivateStuff[exec]; command: ROPE; command _ SELECT mouseButton FROM red => IF ViewerTools.GetSelectedViewer[] = viewer THEN "ClearActionBreak" ELSE "ClearSelectedBreak", yellow => "ClearActionBreak", blue => "ClearAllBreaks", ENDCASE => ERROR; IF private.execState = suspended THEN { MessageWindow.Append["That exec is busy. Use another one.", TRUE]; MessageWindow.Blink[]; } ELSE IF private.execState = dormant THEN -- dormant, i.e. not waiting for input. must call direct TRUSTED {Process.Detach[FORK UserExec.DoIt[input: Rope.Concat[command, "\n"], exec: exec]]} ELSE MakeExecCommand[NARROW[parent], command, TRUE]; }; Proceed: ClickProc -- PROC [parent: REF ANY, clientData: REF ANY, redButton: BOOL] -- = { MakeExecCommand[NARROW[parent], SELECT mouseButton FROM red => "Proceed", yellow => "Proceed Destroy", blue => "Proceed Close", ENDCASE => ERROR]; }; Abort: ClickProc -- PROC [parent: REF ANY, clientData: REF ANY, redButton: BOOL] -- = { viewer: Viewer = NARROW[parent]; exec: ExecHandle = UserExec.GetExecHandle[viewer: viewer]; AbortExec: PROC [exec: ExecHandle] = { private: REF ExecPrivateRecord = UserExecPrivate.GetPrivateStuff[exec]; viewer: Viewer = exec.viewer; IF private.execState = suspended THEN { WaitTillReady: PROC [exec: ExecHandle] = { ticks: Process.Ticks = Process.MsecToTicks[100]; UNTIL private.eventState = readyForNext DO Process.Pause[ticks]; ENDLOOP; }; AbortExec[private.spawnedActionArea];-- abort the child first. WaitTillReady[exec]; -- wait until the abort gets noticed by this work area, since otherwise the "Abort" stuffed in the buffer would be cleared, and therefore never take place. }; MakeExecCommand[viewer, SELECT mouseButton FROM red => "Abort", yellow => "Abort Destroy", blue => "Abort Close", ENDCASE => ERROR]; }; IF exec = NIL THEN {ReportProc["Exec = NIL", fatal]; RETURN}; AbortExec[exec]; }; Source: ClickProc -- PROC [parent: REF ANY, clientData: REF ANY, redButton: BOOL] -- = { r: ROPE _ "Source"; IF control THEN r _ Rope.Concat[r, " global"]; MakeExecCommand[NARROW[parent], r, TRUE]; }; WalkStack: ClickProc -- PROC [parent: REF ANY, clientData: REF ANY, redButton: BOOL] -- = { viewer: Viewer = NARROW[parent]; target: ROPE; IF ViewerTools.GetSelectedViewer[] = viewer AND NOT Rope.IsEmpty[ViewerTools.GetSelectionContents[]] THEN { start, end: UserExec.HistoryEvent; eventPrivate: REF UserExecPrivate.HistoryEventPrivateRecord; [start, end] _ UserExecPrivate.GetEventsFromSelection[UserExec.GetExecHandle[viewer: viewer]]; eventPrivate _ start.privateStuff; target _ Rope.Concat[" &", Convert.ValueToRope[[signed[eventPrivate.eventNum]]]]; }; MakeExecCommand[ NARROW[parent], SELECT mouseButton FROM red => Rope.Concat["WalkStack", target], yellow => "WalkStack 0", blue => "WalkStack -1", ENDCASE => ERROR, TRUE]; }; ShowFrame: ClickProc -- [parent: REF ANY, clientData: REF ANY _ NIL, mouseButton: Menus.MouseButton _ red, shift: BOOL _ FALSE, control: BOOL _ FALSE] -- = { r: ROPE _ SELECT mouseButton FROM red => "ShowFrame", yellow => "ShowFrame args", blue => "ShowFrame args allVars", ENDCASE => ERROR; IF control THEN r _ Rope.Concat[r, " globals"]; MakeExecCommand[NARROW[parent], r, TRUE]; }; ListBreaks: ClickProc -- PROC [parent: REF ANY, clientData: REF ANY, redButton: BOOL] -- = { MakeExecCommand[NARROW[parent], "ListBreaks"]; }; MakeExecCommand: PROCEDURE [viewer: Viewer, command: ROPE, sameLine: BOOL _ FALSE] = { exec: ExecHandle = UserExec.GetExecHandle[viewer: viewer]; private: REF ExecPrivateRecord; IF exec = NIL THEN {ReportProc["Exec = NIL", fatal]; RETURN}; private _ UserExecPrivate.GetPrivateStuff[exec]; IF sameLine THEN { IF private.eventState = running THEN UserExec.StuffIt[exec, Rope.Cat["\021", command, "\t"]] -- workaround, because currently, changing a typescript look will not take effect until the typescript is waiting for a character, which means the notifier gets wedged until the event that is currently executing finishes ELSE UserExec.StuffIt[exec: exec, rope: NIL, list: LIST[NEW[INT _ ('S - 'A)], $ApplyTypeScriptLook, "\021", command, NEW[INT _ ('S - 'A)], $RemoveTypeScriptLook, "\t"]] } ELSE UserExec.StuffIt[exec, Rope.Cat["\021", command, "\n"]]; }; <> SetBreakCommand: UserExec.CommandProc -- [exec: ExecHandle] RETURNS[ok: BOOLEAN _ TRUE] -- = { action: Action = GetAction[exec]; viewerName: ROPE; viewer: ViewerClasses.Viewer; position: INT _ -1; break: BBBreak.BreakIndex; Report: BBVForUserExec.ReportProc = { ReportExec[exec, msg, severity]; }; world: WorldVM.World; IF action # NIL THEN world _ action.event.world; MessageWindow.Clear[]; viewerName _ event.commandLineStream.GetToken[IO.WhiteSpace ! IO.EndOfStream => CONTINUE]; position _ event.commandLineStream.GetInt[ ! IO.EndOfStream => CONTINUE ]; viewer _ ViewerOps.FindViewer[viewerName]; IF viewer = NIL THEN Report["No such viewer", fatal] ELSE IF position = -1 THEN Report["No such location", fatal] ELSE break _ BBVExtras.SetBreakFromPosition[report: Report, viewer: viewer, position: position, world: world]; ShowBreakPoint[exec, break]; }; BreakAtEntryCommand: UserExec.CommandProc -- [exec: ExecHandle] RETURNS[ok: BOOLEAN _ TRUE] -- = { BreakEntryOrExit[event: event, exec: exec, entry: TRUE]; }; BreakAtExitCommand: UserExec.CommandProc -- [exec: ExecHandle] RETURNS[ok: BOOLEAN _ TRUE] -- = { BreakEntryOrExit[event: event, exec: exec, exit: TRUE]; }; BreakEntryOrExit: PROC [event: UserExec.HistoryEvent, exec: ExecHandle, entry: BOOL _ FALSE, exit: BOOL _ FALSE] = { action: Action = GetAction[exec]; break: BBBreak.BreakIndex; name: ROPE = IO.GetToken[event.commandLineStream, IO.IDProc]; position: INT _ -1; Report: BBVForUserExec.ReportProc = { ReportExec[exec, msg, severity]; }; world: WorldVM.World; IF NOT entry AND NOT exit THEN RETURN; IF action # NIL THEN world _ action.event.world; position _ event.commandLineStream.GetInt[ ! IO.EndOfStream => CONTINUE; ]; IF position # -1 THEN { viewer: ViewerClasses.Viewer = ViewerOps.FindViewer[name]; IF viewer = NIL THEN Report["No such viewer", fatal] ELSE break _ BBVExtras.SetBreakFromPosition[viewer: viewer, position: position, report: Report, world: world, entry: entry, exit: exit] } ELSE { inner: PROC = { IF exit THEN break _ BBBreak.BreakExit[expr.value]; IF entry THEN break _ BBBreak.BreakEntry[expr.value]; }; error: ROPE; expr: UserExec.Expression = UserExec.CreateExpr[name]; MessageWindow.Append[Rope.Cat["Evaluating ", name, "..."], TRUE]; [] _ UserExec.EvalExpr[expr: expr, exec: exec ! UserExec.EvaluationFailed => { error _ msg; CONTINUE }]; MessageWindow.Clear[]; IF error = NIL THEN error _ BBSafety.Mother[inner]; IF error # NIL THEN Report[error, fatal]; }; ShowBreakPoint[exec, break]; }; ShowBreakPoint: PROCEDURE [exec: UserExec.ExecHandle, break: BBBreak.BreakIndex] = { Report: BBVForUserExec.ReportProc = {ReportExec[exec, msg, severity]}; breakId: BBBreak.BreakId; index: INT; name: ROPE; viewer: Viewer; breakTV, gf: TV; MessageWindow.Clear[]; IF break = BBBreak.NullIndex THEN RETURN; breakId _ BBBreak.FindBreakId[break]; breakTV _ BBObjectLocation.TVFromLocation[breakId.loc]; [gf, index] _ BBObjectLocation.LocationToSource[breakId.loc]; name _ BBVForUserExec.SourceFromTV[tv: gf, report: Report].name; viewer _ ShowSource[name: name, index: index, exec: exec, onlyIfAlreadyOpen: FALSE -- should be TRUE if this is a break at entry/exit --]; PrintBreak[exec: exec, breakId: breakId, breakTV: breakTV, viewer: viewer, index: index]; <> <> }; ClearSelectedBreakCommand: UserExec.CommandProc -- [exec: ExecHandle] RETURNS[ok: BOOLEAN _ TRUE] -- = { Report: BBVForUserExec.ReportProc = {ReportExec[exec, msg, severity]}; action: Action = GetAction[exec]; MessageWindow.Clear[]; IF ViewerTools.GetSelectedViewer[] = exec.viewer THEN -- selection is in the action area. clear the break associated with that area BBVExtras.ClearActionBreakPoint[GetAction[exec], Report] ELSE [] _ BBVForUserExec.ClearBreak[world: IF action # NIL THEN action.event.world ELSE NIL, report: Report]; }; ClearActionBreakCommand: UserExec.CommandProc = { Report: BBVForUserExec.ReportProc = {ReportExec[exec, msg, severity]}; BBVExtras.ClearActionBreakPoint[GetAction[exec], Report]; }; ClearAllBreaksCommand: UserExec.CommandProc = { Report: BBVForUserExec.ReportProc = {ReportExec[exec, msg, severity]}; Report["Clearing breaks...", comment]; BBBreak.ClearAllBreaks[]; Report["All breaks cleared", success]; }; ClearBreakCommand: UserExec.CommandProc -- [exec: ExecHandle] RETURNS[ok: BOOLEAN _ TRUE] -- = { out: IO.STREAM = UserExec.GetStreams[exec].out; UNTIL IO.EndOf[event.commandLineStream] DO ENABLE IO.EndOfStream => CONTINUE; id: BBBreak.BreakIndex; id _ IO.GetInt[event.commandLineStream]; IF BBBreak.ClearBreak[id] # NIL THEN out.PutF["*nBreak #%d cleared\n", int[id]] ELSE out.PutF["*n*eNo such break: %d*s\n", int[id]]; ENDLOOP; }; ProceedCommand: UserExec.CommandProc -- [exec: ExecHandle] RETURNS[ok: BOOLEAN _ TRUE] -- = { out: STREAM = UserExec.GetStreams[exec].out; action: Action = GetAction[exec]; r: ROPE; IF action = NIL THEN {out.PutF["*eNo action associated with this work area."]; RETURN}; -- user typed this to a work area no longer associated with an action. In future, this will simply be disabled via predicate associated with command. LeaveActionArea[exec: exec, abort: FALSE]; IF action.status = pendingOut THEN [] _ BBAction.TakeData[action] ELSE IF action.status = busy THEN NULL ELSE ERROR; r _ IO.GetToken[event.commandLineStream, IO.IDProc]; SELECT TRUE FROM Rope.Equal[r, "Destroy", FALSE] => ViewerOps.CloseViewer[exec.viewer]; Rope.Equal[r, "Close", FALSE] => ViewerOps.DestroyViewer[exec.viewer]; ENDCASE; ERROR UserExecPrivate.GoToSleep[]; }; AbortCommand: UserExec.CommandProc -- [exec: ExecHandle] RETURNS[ok: BOOLEAN _ TRUE] -- = { out: STREAM = UserExec.GetStreams[exec].out; action: Action = GetAction[exec]; r: ROPE; IF action = NIL THEN {out.PutF["*eNo action associated with this work area."]; RETURN}; LeaveActionArea[exec: exec, abort: TRUE]; IF action.status = pendingOut THEN [] _ BBAction.Abort[action] ELSE IF action.status = dead THEN NULL ELSE ERROR; r _ IO.GetToken[event.commandLineStream, IO.IDProc]; SELECT TRUE FROM Rope.Equal[r, "Destroy", FALSE] => ViewerOps.CloseViewer[exec.viewer]; Rope.Equal[r, "Close", FALSE] => ViewerOps.DestroyViewer[exec.viewer]; ENDCASE; ERROR UserExecPrivate.GoToSleep[]; }; EndSessionCommand: UserExec.CommandProc -- [exec: ExecHandle] RETURNS[ok: BOOLEAN _ TRUE] -- = { out: STREAM = UserExec.GetStreams[exec].out; action: Action = GetAction[exec]; IF action = NIL THEN {out.PutF["*eNo action associated with this work area."]; RETURN}; LeaveActionArea[exec: exec, abort: FALSE, booted: TRUE]; IF action.status = pendingOut THEN { [] _ BBAction.Abort[action]; ERROR UserExecPrivate.GoToSleep[]; } ELSE IF action.status = dead THEN NULL ELSE ERROR; }; LeaveActionArea: PUBLIC PROC [exec: ExecHandle, abort: BOOL, booted: BOOL _ FALSE] = { viewer: Viewer = exec.viewer; private: REF ExecPrivateRecord = UserExecPrivate.GetPrivateStuff[exec]; out: STREAM = IF booted THEN private.out ELSE UserExec.GetStreams[exec].out; parentExec: ExecHandle; parentsPrivate: REF ExecPrivateRecord; actionAreaData: REF UserExecPrivate.ActionAreaData = private.actionAreaData; action: Action = actionAreaData.action; msg: ROPE = IF abort THEN "aborted" ELSE "proceeded"; IF booted THEN out.PutF["*n*sEnd Of Session\n"] ELSE { out.PutF["*n*s%g Action #%d", rope[msg], int[action.id]]; IF actionAreaData.parentExec # NIL THEN { parentExec _ actionAreaData.parentExec; parentsPrivate _ UserExecPrivate.GetPrivateStuff[parentExec]; out.PutF[", returning to %g Area %g", rope[IF parentsPrivate.actionAreaData # NIL THEN "Action" ELSE "Work"], rope[parentsPrivate.id]]; }; }; out.PutF["*n%g\n", rope[UserExecPrivate.tildas]]; IO.Flush[out]; actionAreaData.action _ NIL; actionAreaData.aborted _ abort; actionAreaData.booted _ booted; private.process _ NIL; IF private.evalHead # NIL THEN { private.evalHead.context _ NIL; private.evalHead.globalContext _ NIL; }; {viewerProc: UserExecPrivate.SplitViewerProc = { Menus.SetLine[menu: viewer.menu, line: 1, entryList: NIL]; viewer.icon _ ActionAreasDefs.inactiveActionAreaIcon; ViewerOps.PaintViewer[viewer, IF viewer.iconic THEN all ELSE menu]; }; UserExecPrivate.ForAllSplitViewers[viewer, viewerProc]; }; [] _ UserExecPrivate.CaptionForExec[exec, TRUE]; IF booted THEN RETURN; IF parentExec # NIL THEN RestoreActionArea[parentExec]; ActionAreasDefs.RestoreSelection[exec]; }; RestoreActionArea: PUBLIC PROC [exec: ExecHandle] = { private: REF ExecPrivateRecord = UserExecPrivate.GetPrivateStuff[exec]; actionAreaData: REF UserExecPrivate.ActionAreaData = private.actionAreaData; viewerProc: UserExecPrivate.SplitViewerProc = TRUSTED { IF private.actionAreaData # NIL THEN { viewer.icon _ ActionAreasDefs.actionAreaIcon; Menus.SetLine[menu: viewer.menu, line: 1, entryList: UserExecPrivate.secondMenuLine]; ViewerOps.PaintViewer[viewer, IF viewer.iconic THEN all ELSE menu]; } ELSE viewer.icon _ typescript; }; private.spawnedActionArea _ NIL; UserExecPrivate.ForAllSplitViewers[exec.viewer, viewerProc]; private.execState _ listening; [] _ UserExecPrivate.CaptionForExec[exec, TRUE]; }; SourceCommand: UserExec.CommandProc -- [exec: ExecHandle] RETURNS[ok: BOOLEAN _ TRUE] -- = { private: REF ExecPrivateRecord = UserExecPrivate.GetPrivateStuff[exec]; commandLineStream: STREAM = IO.RIS[event.commandLine]; useGlobal: BOOL _ FALSE; ctx: BBContext.Context; tv, gfTV: TV; r: ROPE; name: ROPE; index: INT; viewer: Viewer; Report: BBVForUserExec.ReportProc = { IF Rope.IsEmpty[msg] THEN RETURN; <