DIRECTORY Atom, CedarProcess, Commander, CommandTool, Convert, EditedStream, FileNames, FS, IconRegistry, IO, List, Menus, MessageWindow, Process, ReadEvalPrint, Rope, SymTab, SystemVersion, TextReplace, TiogaOps, UserProfile, ViewerClasses, ViewerEvents, ViewerIO, ViewerOps,ViewerTools; ExecHacks: CEDAR PROGRAM IMPORTS Atom, CedarProcess, Commander, CommandTool, Convert, EditedStream, FileNames, FS, IconRegistry, IO, List, Menus, MessageWindow, Process, Rope, SymTab, SystemVersion, TextReplace, TiogaOps, UserProfile, ViewerEvents, ViewerIO, ViewerOps, ViewerTools = BEGIN ROPE: TYPE = Rope.ROPE; RopeList: TYPE = LIST OF ROPE; DoItButtonData: TYPE ~ RECORD [cmd: Commander.Handle, name: ROPE]; doitButtonNames: LIST OF ROPE; redoButtonName: ROPE; redoEnabled: BOOL _ TRUE; saveAndDoItEnabled: BOOL _ TRUE; historyKey: ATOM = $ExecHacksCommandHistory; NoteProfile: UserProfile.ProfileChangedProc= { doitButtonNames _ UserProfile.ListOfTokens["ExecHacks.DoitButtonName", LIST["DoIt"]]; redoButtonName _ UserProfile.Token["ExecHacks.RedoButtonName", "Redo"]; saveAndDoItEnabled _ UserProfile.Boolean["ExecHacks.SaveAndDoIt", TRUE]; }; Blink: PROC = {ViewerOps.BlinkDisplay[]}; DoIt: Menus.MenuProc = { sel: ROPE _ ViewerTools.GetSelectionContents[]; doitButtonData: REF DoItButtonData ~ NARROW[clientData]; IF Rope.Size[sel] <= 1 THEN { selViewer: ViewerClasses.Viewer _ ViewerTools.GetSelectedViewer[]; IF selViewer = NIL THEN {Blink[]; RETURN}; sel _ selViewer.name; }; InternalDoIt[exec: NARROW[parent], sel: sel, cmd: doitButtonData.cmd, name: doitButtonData.name]; }; dummyRope: ROPE ~ "ESN, December 23, 1986 3:02:26 pm PST"; InternalDoIt: PROC [exec: ViewerClasses.Viewer, cmd: Commander.Handle, sel, name: ROPE] = { function: ROPE; { fullFName, ext: ROPE; cp: FS.ComponentPositions; dirOmitted: BOOLEAN; UPExecHacksLine: PROC [key: ROPE] RETURNS [ROPE] ~ { upKey: ROPE ~ IF ext.Length[] > 0 THEN IO.PutFR[format: "ExecHacks.%g.%g", v1: [rope[key]], v2: [rope[ext]]] ELSE IO.PutFR[format: "ExecHacks.%gNoExtension", v1: [rope[key]]]; RETURN [UserProfile.Line[key: upKey, default: dummyRope]]; }; FoundIt: PROC [rope: ROPE] RETURNS [BOOL] ~ { function _ rope; RETURN [function#dummyRope]; }; DefaultCommands: PROC [ext: ROPE] RETURNS [default: ROPE] ~ { default _ SELECT TRUE FROM ext.IsEmpty[] => "RCompile ", ext.Equal["mesa", FALSE], ext.Equal["cedar", FALSE] => "RCompile ", ext.Equal["config", FALSE] => "Bind ", ext.Equal["cm", FALSE], ext.Equal["load", FALSE] => "", ext.Equal["df", FALSE] => "SModel ;\nVerifyDF ", ext.Equal["bcd", FALSE] => "Run ", ext.Equal["profile", FALSE] => IO.PutFR["Copy [User]<>%g.%g> _ ///.profile", IO.card[SystemVersion.release.major], IO.card[SystemVersion.release.minor]], ENDCASE => "Blink"; }; [fullFName, cp, dirOmitted] _ FS.ExpandName[sel ! FS.Error => CONTINUE]; IF fullFName = NIL THEN {Blink[]; RETURN}; ext _ Rope.Substr[fullFName, cp.ext.start, cp.ext.length]; SELECT TRUE FROM --When FoundIt returns TRUE, function is set appropriately FoundIt[UPExecHacksLine[name]] => {}; --First check under the button name FoundIt[UPExecHacksLine["For"]] => {}; --Next check under "For" FoundIt[DefaultCommands[ext]] => {}; ENDCASE => ERROR; IF function.Equal["Blink"] THEN {Blink[]; RETURN}; function _ TextReplace.RopeMapFromPairs[LIST[ ["", fullFName.Substr[cp.server.start, cp.server.length], TRUE], ["", fullFName.Substr[cp.dir.start, cp.dir.length], TRUE], ["", fullFName.Substr[cp.subDirs.start, cp.subDirs.length], TRUE], ["", fullFName.Substr[0, cp.base.start], TRUE], ["", fullFName.Substr[cp.base.start, cp.base.length], TRUE], ["", ext, TRUE], ["", fullFName.Substr[cp.base.start, cp.ext.start + cp.ext.length - cp.base.start], TRUE], ["", fullFName.Substr[cp.ver.start, cp.ver.length], TRUE], ["", fullFName, TRUE] ]].Apply[function].Concat["\n"]; }; ViewerTools.SetSelection[exec, NIL]; BackupToPrompt[cmd]; exec.class.notify[exec, LIST[function]]; }; SaveAndDoIt: TiogaOps.CommandProc = { fileName: ROPE _ viewer.name; directory: ROPE _ FileNames.Directory[fileName]; commandToolName: ROPE _ Rope.Cat["CommandTool: WD = ", directory]; exec: ViewerClasses.Viewer ~ ViewerOps.FindViewer[commandToolName]; rep: ReadEvalPrint.Handle; cmd: Commander.Handle; IF NOT saveAndDoItEnabled THEN RETURN; IF exec=NIL THEN {MessageWindow.Append["SaveAndDoIt: Can't do it, no CommandTool for this directory", TRUE]; MessageWindow.Blink[]; RETURN}; rep _ NARROW [Atom.GetPropFromList[exec.props, $ReadEvalPrint]]; cmd _ NARROW [rep.clientData]; Process.Yield[]; [] _ CedarProcess.Fork[SaveAndDoItProcess, NEW [SaveAndDoItProcessDataRec _ [viewer: viewer, exec: exec, sel: fileName, cmd: cmd]]]; }; SaveAndDoItProcessData: TYPE = REF SaveAndDoItProcessDataRec; SaveAndDoItProcessDataRec: TYPE = RECORD [ viewer, exec: ViewerClasses.Viewer, cmd: Commander.Handle, sel: ROPE ]; SaveAndDoItProcess: CedarProcess.ForkableProc = { saveData: SaveAndDoItProcessData _ NARROW [data]; Process.Yield[]; Process.Pause[Process.MsecToTicks[200]]; -- to let the time for the forked save process set the saveInProgress flag WHILE saveData.viewer.saveInProgress DO Process.Yield[]; Process.Pause[Process.MsecToTicks[400]]; -- please save ... MessageWindow.Append["SaveAndDoIt: waiting for save to complete ... ", TRUE]; ENDLOOP; Process.Yield[]; MessageWindow.Append["SaveAndDoIt: save done; doing it !", TRUE]; Process.Yield[]; InternalDoIt[exec: saveData.exec, sel: saveData.sel, cmd: saveData.cmd, name: doitButtonNames.first]; }; Redo: Menus.MenuProc = { ScanAndStuff: PROC = { exec: ViewerClasses.Viewer ~ NARROW[parent]; cmdList, cmdListTail: LIST OF REF ANY _ NIL; SELECT mouseButton FROM red, yellow => { tsRope: ROPE; start, end: INT; startLoc, endLoc: TiogaOps.Location; selectionViewer: ViewerClasses.Viewer; [selectionViewer, startLoc, endLoc, , , ] _ TiogaOps.GetSelection[]; IF NOT selectionViewer = exec THEN { --force selection into command window ViewerTools.SetSelection[exec, NIL]; [selectionViewer, startLoc, endLoc, , , ] _ TiogaOps.GetSelection[]; }; tsRope _ TiogaOps.GetRope[startLoc.node]; start _ startLoc.where; IF Rope.Find[tsRope, "%", start] = -1 THEN { start _ start-1; WHILE Rope.Fetch[tsRope, start]#'% DO start _ start-1; ENDLOOP; start _ start-1; }; WHILE Rope.Fetch[tsRope, start]#'% DO start _ start-1; ENDLOOP; --back up to "%" preceeding command of interest WHILE start+2 < endLoc.where DO rope: ROPE; end _ start _ start+2; WHILE Rope.Fetch[tsRope, end]#15C DO end _ end+1; ENDLOOP; --forward to CR rope _ Rope.Substr[tsRope, start, end-start+1]; IF cmdListTail = NIL THEN cmdList _ cmdListTail _ LIST[rope] ELSE cmdListTail _ cmdListTail.rest _ LIST[rope]; start _ end; WHILE Rope.Fetch[tsRope, start]#'% DO start _ start+1; ENDLOOP; ENDLOOP; }; blue => { cmdList _ LIST["History -d 10\n"]; }; ENDCASE => ERROR; ViewerTools.SetSelection[exec, NIL]; BackupToPrompt[cmd]; exec.class.notify[exec, cmdList]; }; cmd: Commander.Handle ~ NARROW[clientData]; ScanAndStuff[! ANY => {Blink[]; CONTINUE}]; }; UpdateHistoryProc: Commander.CommandProc = { history: RopeList _ NARROW[List.Assoc[aList: cmd.propertyList, key: historyKey]]; IF List.Assoc[aList: cmd.propertyList, key: $ParentCommander] # NIL THEN RETURN; --this filters out command files, but not "subcommands" history _ CONS[cmd.command.Cat[cmd.commandLine], history]; cmd.propertyList _ List.PutAssoc[ aList: cmd.propertyList, key: historyKey, val: history ]; }; RedoCmd: Commander.CommandProc = { history: RopeList _ NARROW[List.Assoc[aList: cmd.propertyList, key: historyKey]]; cls: IO.STREAM = IO.RIS[cmd.commandLine]; trash: INT = cls.SkipWhitespace[]; key: ROPE = IF cls.EndOf[] THEN NIL ELSE cls.GetTokenRope[IO.IDProc].token; [] _ cls.SkipWhitespace[]; IF NOT cls.EndOf[] THEN GOTO GiveUsage; cls.Close[]; IF history # NIL THEN history _ history.rest; --strip this redo itself FOR hl: RopeList _ history, hl.rest WHILE hl # NIL DO cr: ROPE = hl.first; IF key = NIL OR IsCmdPrefix[key, cr] THEN { result _ CommandTool.DoCommand[cr, cmd]; EXIT; }; REPEAT FINISHED => RETURN [$Failure, IO.PutFR["No command in my history begins with \"%q\"", [rope[key]]]]; ENDLOOP; EXITS GiveUsage => {result _ $Failure; msg _ "Usage: Redo commandNamePrefix"} }; IsCmdPrefix: PROC [prefix, command: ROPE] RETURNS [is: BOOL] = { pLen: INT = prefix.Length[]; cLen: INT = command.Length[]; c: CHAR; first, afterLast: INT _ 0; FOR afterLast _ 0, afterLast+1 WHILE afterLast < cLen AND (c _ command.Fetch[afterLast]) # ' DO SELECT c FROM '/, '> => first _ afterLast+1; ENDCASE; ENDLOOP; is _ pLen <= afterLast-first AND prefix.Equal[command.Substr[start: first, len: pLen], FALSE]; }; HistoryCmd: Commander.CommandProc = { to: IO.STREAM = cmd.out; prompt: ROPE = NARROW[List.Assoc[key: $Prompt, aList: cmd.propertyList]]; history: RopeList _ NARROW[List.Assoc[aList: cmd.propertyList, key: historyKey]]; toGive: RopeList _ NIL; cls: IO.STREAM = IO.RIS[cmd.commandLine]; includeDuplicates: BOOL _ TRUE; includePrompt: BOOL _ TRUE; n: INT _ 10; seen: SymTab.Ref; FOR i: INT _ cls.SkipWhitespace[], cls.SkipWhitespace[] WHILE NOT cls.EndOf[] DO toke: ROPE = cls.GetTokenRope[IO.IDProc].token; SELECT TRUE FROM toke.Equal["-%"] => includePrompt _ FALSE; toke.Equal["+%"] => includePrompt _ TRUE; toke.Equal["-d"] => includeDuplicates _ FALSE; toke.Equal["+d"] => includeDuplicates _ TRUE; ENDCASE => n _ Convert.IntFromRope[toke !Convert.Error => GOTO GiveUsage]; ENDLOOP; cls.Close[]; IF NOT includeDuplicates THEN seen _ SymTab.Create[case: FALSE]; WHILE n > 0 DO IF history = NIL THEN EXIT; IF includeDuplicates OR NOT seen.Fetch[history.first].found THEN { toGive _ CONS[history.first, toGive]; IF NOT includeDuplicates THEN [] _ seen.Insert[history.first, $seen]; n _ n - 1; }; history _ history.rest; ENDLOOP; FOR toGive _ toGive, toGive.rest WHILE toGive # NIL DO IF includePrompt THEN to.PutF[prompt, [rope["b"]], [rope["B"]]]; to.PutRope[toGive.first]; ENDLOOP; result _ $OK; EXITS GiveUsage => {result _ $Failure; msg _ "Usage: History [-d] [-%] [number]"} }; Greet: PROC [v: ViewerClasses.Viewer, cmd: Commander.Handle, paint: BOOL] = { Add: PROC [me: Menus.MenuEntry, name: ROPE] = { old: Menus.MenuEntry = Menus.FindEntry[v.menu, name]; IF old = NIL THEN Menus.AppendMenuEntry[v.menu, me] ELSE Menus.ReplaceMenuEntry[v.menu, old, me]; }; IF v = NIL THEN RETURN; IF redoEnabled AND cmd # NIL THEN { cmd.propertyList _ CommandTool.RemoveProcFromList[aList: cmd.propertyList, listKey: $Before, proc: updateHistory]; cmd.propertyList _ CommandTool.AddProcToList[aList: cmd.propertyList, listKey: $Before, proc: updateHistory]; }; FOR eachButton: LIST OF ROPE _ doitButtonNames, eachButton.rest UNTIL eachButton=NIL DO name: ROPE ~ eachButton.first; Add[Menus.CreateEntry[name: name, proc: DoIt, fork: FALSE, clientData: NEW[DoItButtonData _ [cmd: cmd, name: name]]], name]; ENDLOOP; Add[Menus.CreateEntry[name: redoButtonName, proc: Redo, fork: FALSE, clientData: cmd], redoButtonName]; IF paint THEN ViewerOps.PaintViewer[v, menu]; }; updateHistory: Commander.CommandProcHandle _ NEW [Commander.CommandProcObject _ [proc: UpdateHistoryProc]]; BackupToPrompt: PROC [cmd: Commander.Handle] ={ inStream: IO.STREAM _ GetReadEvalPrint[cmd].in; bufferContents: REF TEXT _ ViewerIO.GetBuffer[inStream]; IF bufferContents # NIL AND bufferContents.length > 0 AND bufferContents[bufferContents.length - 1] # '\n THEN { FOR n: NAT DECREASING IN [0..bufferContents.length) DO IF bufferContents[n] = '\n THEN { EditedStream.UnAppendBufferChars[ stream: inStream, nChars: bufferContents.length - n - 1]; EXIT; } REPEAT FINISHED => EditedStream.UnAppendBufferChars[stream: inStream, nChars: LAST[NAT]]; ENDLOOP; }; }; GetReadEvalPrint: PROC [cmd: Commander.Handle] RETURNS [ReadEvalPrint.Handle] = { DO WITH CommandTool.GetProp[cmd, $ReadEvalPrintHandle] SELECT FROM reph: ReadEvalPrint.Handle => IF reph # NIL AND reph.viewer # NIL THEN RETURN [reph]; ENDCASE; WITH CommandTool.GetProp[cmd, $ParentCommander] SELECT FROM next: Commander.Handle => {cmd _ next; LOOP;}; ENDCASE; RETURN [NIL]; ENDLOOP; }; AttachMenuItemsToExecs: PROC = { PerViewer: ViewerOps.EnumProc = { Greet[v, NIL, TRUE]; }; ViewerOps.EnumerateViewers[PerViewer]; }; ExecHacksCmd: Commander.CommandProc ~ { handle: ReadEvalPrint.Handle _ NARROW[ Commander.GetProperty[$ReadEvalPrintHandle, cmd.propertyList]]; Greet[handle.viewer, cmd, TRUE]; }; AlterIcon: ViewerEvents.EventProc = { IF viewer.parent=NIL AND viewer.file#NIL THEN { Icon: PROC [r1, r2: ROPE _ NIL] RETURNS [found: BOOL] ~ { iconName _ UserProfile.Token[key: Rope.Cat["ExecHacks.", r1, r2]]; RETURN [iconName#NIL] }; cp: FS.ComponentPositions; fullFName, extension, iconName: ROPE; [cp: cp, fullFName: fullFName] _ FS.ExpandName[name: viewer.file]; extension _ Rope.Substr[base: fullFName, start: cp.ext.start, len: cp.ext.length]; SELECT TRUE FROM viewer.newVersion AND Icon["DirtyIcon.", extension] => {}; Icon["Icon.", extension] => {}; viewer.newVersion AND (iconName _ IconRegistry.IsRegistered[Rope.Cat["Dirty", extension]].name)#NIL => {}; (iconName _ IconRegistry.IsRegistered[extension].name)#NIL => {}; viewer.newVersion AND Icon["DefaultDirtyIcon"] => {}; Icon["DefaultIcon"] => {}; ENDCASE; IF iconName#NIL THEN viewer.icon _ IconRegistry.GetIcon[iconName: iconName, default: viewer.icon]; }; }; UserProfile.CallWhenProfileChanges[NoteProfile]; [] _ ViewerEvents.RegisterEventProc[proc: AlterIcon, event: close, before: TRUE]; [] _ ViewerEvents.RegisterEventProc[proc: AlterIcon, event: save, before: FALSE]; [] _ ViewerEvents.RegisterEventProc[proc: AlterIcon, event: edit, before: FALSE]; TiogaOps.RegisterCommand[name: $BlueSave, proc: SaveAndDoIt, before: FALSE]; Commander.Register[key: "///Commands/ExecHacks", proc: ExecHacksCmd, doc: "registers CommandTool menu buttons for compiling programs and redoing the last command."]; Commander.Register[key: "///Commands/Redo", proc: RedoCmd, doc: "Redo  redoes the last command that begins with string"]; Commander.Register[key: "///Commands/History", proc: HistoryCmd, doc: "History