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