CommandsCImpl.mesa
L. Stewart, December 20, 1983 1:29 pm
Teitelman, earlier
DIRECTORY
Atom USING [GetPName],
Commander USING [CommandProc, Handle, Lookup, Register],
CommandExtras USING [MakeUninterpreted],
CommandTool USING [DoCommand],
Convert USING [RopeFromInt],
EditedStream USING [UnAppendBufferChars],
IO USING [BreakProc, Close, EndOf, EndOfStream, Error, GetRefAny, GetToken, IDProc, PeekChar, RIS, SkipWhitespace, STREAM, TokenProc],
List USING [Assoc],
MBQueue USING [CreateMenuEntry],
Menus USING [AppendMenuEntry, ClickProc, FindEntry, MenuEntry, ReplaceMenuEntry],
ReadEvalPrint USING [Handle],
Rope USING [Cat, Concat, Equal, Fetch, Find, FromChar, FromRefText, Length, Replace, ROPE, SkipTo, Substr],
RopeList USING [Reverse],
TiogaOps USING [GetSelection, Location, LocOffset, Ref, Root],
ViewerClasses USING [Viewer],
ViewerIOExtras USING [GetBuffer, TypeChars],
ViewerOps USING [PaintViewer],
ViewerTools USING [GetSelectionContents, SetSelection];
CommandsCImpl: CEDAR PROGRAM
IMPORTS
Atom, Commander, CommandExtras, CommandTool, Convert, EditedStream, List, MBQueue, Menus, IO, Rope, RopeList, TiogaOps, ViewerIOExtras, ViewerOps, ViewerTools =
BEGIN
AliasCellObject:
TYPE =
RECORD [
args: LIST OF Rope.ROPE ← NIL,
def: Rope.ROPE
];
AliasCell: TYPE = REF AliasCellObject;
Alias: Commander.CommandProc =
TRUSTED {
commandLineStream: IO.STREAM = IO.RIS[cmd.commandLine];
name, def: Rope.ROPE;
token: REF TEXT ← NEW[TEXT[30]];
args: LIST OF Rope.ROPE ← NIL;
aliasCell: AliasCell;
{
ENABLE {
IO.EndOfStream => GOTO Die;
IO.Error => GOTO Die;
};
token.length ← 0;
token ← commandLineStream.GetToken[IO.TokenProc, token].token;
name ← Rope.FromRefText[token];
[] ← commandLineStream.SkipWhitespace[FALSE];
IF
NOT commandLineStream.EndOf[]
AND commandLineStream.PeekChar[] = '(
THEN {
FOR l:
LIST
OF
REF
ANY ←
NARROW[commandLineStream.GetRefAny[]], l.rest
UNTIL l =
NIL
DO
WITH l.first
SELECT
FROM
r: Rope.ROPE => args ← CONS[r, args];
a: ATOM => args ← CONS[Atom.GetPName[a], args];
ENDCASE => ERROR;
ENDLOOP;
TRUSTED {args ← RopeList.Reverse[args]; };
};
token.length ← 0;
token ← commandLineStream.GetToken[CRBreak, token].token;
def ← Rope.FromRefText[token];
aliasCell ← NEW[AliasCellObject ← [args, def]];
Commander.Register[key: name, proc: AliasImplProc, doc: Rope.Concat["Alias ", cmd.commandLine], clientData: aliasCell];
commandLineStream.Close[];
EXITS
Die => NULL;
};
};
RopeSubst:
PROC [old, new, base: Rope.
ROPE, case:
BOOL ←
FALSE, allOccurrences:
BOOL ←
TRUE]
RETURNS[Rope.
ROPE] = {
lenOld: INT = old.Length[];
lenNew: INT = new.Length[];
i: INT ← 0;
WHILE (i ← Rope.Find[s1: base, s2: old, case: case, pos1: i]) # -1
DO
base ← Rope.Replace[base: base, start: i, len: lenOld, with: new];
IF ~allOccurrences THEN EXIT;
i ← i + lenNew;
ENDLOOP;
RETURN[base];
};
if old is not found in base, then value = base.
if allOccurrences THEN substitute for each occurrence of old, otherwise only for first.
RopeMemb:
PROC [rope: Rope.
ROPE, lst:
LIST
OF Rope.
ROPE]
RETURNS[
BOOL] = {
FOR l:
LIST
OF Rope.
ROPE ← lst, l.rest
UNTIL l =
NIL
DO
IF Rope.Equal[rope, l.first, FALSE] THEN RETURN[TRUE];
ENDLOOP;
RETURN[FALSE]
};
gennum: LONG CARDINAL ← 10000;
UniqueRope:
PROC [c:
CHARACTER ← 'A]
RETURNS[Rope.
ROPE] = {
gennum ← gennum + 1;
RETURN[Rope.Concat[Rope.FromChar[c], Convert.RopeFromInt[gennum, 10, FALSE]]];
}; -- of Gensym
AliasImplProc: Commander.CommandProc =
TRUSTED {
aliasCell: AliasCell ← NARROW[cmd.procData.clientData];
newCommandLine: Rope.ROPE ← aliasCell.def;
token: REF TEXT ← NEW[TEXT[30]];
new: Rope.ROPE;
restOfStream: Rope.ROPE;
commandLineStream: IO.STREAM = IO.RIS[cmd.commandLine];
synonyms: LIST OF REF SynonymRecord;
SynonymRecord: TYPE = RECORD[key, val: Rope.ROPE];
FOR l:
LIST
OF Rope.
ROPE ← aliasCell.args, l.rest
UNTIL l =
NIL
DO
token.length ← 0;
token ← commandLineStream.GetToken[IO.IDProc, token].token;
new ← Rope.FromRefText[token];
IF RopeMemb[new, l.rest]
THEN {
-- e.g. args are (x y) and substituting y gorp.
dummy: Rope.ROPE = UniqueRope[];
synonyms ← CONS[NEW[SynonymRecord ← [new, dummy]], synonyms];
new ← dummy;
};
newCommandLine ← RopeSubst[old: l.first, new: new, base: newCommandLine, case: FALSE];
ENDLOOP;
FOR l:
LIST
OF
REF SynonymRecord ← synonyms, l.rest
UNTIL l =
NIL
DO
newCommandLine ← RopeSubst[old: l.first.val, new: l.first.key, base: newCommandLine, case: FALSE];
ENDLOOP;
token.length ← 0;
token ← commandLineStream.GetToken[CRBreak, token].token;
restOfStream ← Rope.FromRefText[token];
newCommandLine ← Rope.Cat[newCommandLine, restOfStream, "\n"];
commandLineStream.Close[];
result ← CommandTool.DoCommand[commandLine: newCommandLine, parent: cmd];
};
CRBreak:
IO.BreakProc = {
IF char = '\n THEN RETURN[break];
RETURN[other];
};
The first token willl be the name of the button. The rest of the commandLine will be the thing to stuff. A special character sequence, $$, will stand for the current selection.
CreateButton: Commander.CommandProc =
TRUSTED {
commandLineStream: IO.STREAM = IO.RIS[cmd.commandLine];
name: Rope.ROPE;
token: REF TEXT ← NEW[TEXT[30]];
br: ButtonImplRef ← NEW[ButtonImplObject];
cth: Commander.Handle ← cmd;
{
ENABLE {
IO.EndOfStream => GOTO Die;
IO.Error => GOTO Die;
};
token.length ← 0;
token ← commandLineStream.GetToken[IO.TokenProc, token].token;
name ← Rope.FromRefText[token];
[] ← commandLineStream.SkipWhitespace[FALSE];
Get the rest of it!
token.length ← 0;
token ← commandLineStream.GetToken[CRBreak, token].token;
br.def ← Rope.FromRefText[token];
IF br.def.Length[] = 0 THEN RETURN[$Failure, "No definition rope"];
IF br.def.Fetch[br.def.Length[] - 1] # '\n THEN br.def ← Rope.Concat[br.def, "\n"];
Search up chain of parents to find a ReadEvalPrint.Handle and the associated commander
WHILE br.rep =
NIL
DO
br.rep ← NARROW[List.Assoc[key: $ReadEvalPrintHandle, aList: cth.propertyList]];
IF br.rep # NIL AND br.rep.viewer # NIL THEN EXIT
ELSE br.rep ← NIL;
cth ← NARROW[List.Assoc[key: $ParentCommander, aList: cth.propertyList]];
IF cth = NIL THEN RETURN[$Failure, "Can't find $ReadEvalPrintHandle"];
ENDLOOP;
Menus.AppendMenuEntry[menu: br.rep.viewer.menu, line: 0, entry: MBQueue.CreateMenuEntry[q: br.rep.menuHitQueue, name: name, proc: ButtonImplButtonProc, clientData: br]];
ViewerOps.PaintViewer[viewer: br.rep.viewer, hint: menu, clearClient: FALSE];
EXITS
Die => NULL;
};
commandLineStream.Close[];
};
The first token willl be the name of the button.
DeleteButton: Commander.CommandProc =
TRUSTED {
commandLineStream: IO.STREAM = IO.RIS[cmd.commandLine];
name: Rope.ROPE;
token: REF TEXT ← NEW[TEXT[30]];
br: ButtonImplRef ← NEW[ButtonImplObject];
cth: Commander.Handle ← cmd;
oldEntry: Menus.MenuEntry;
{
ENABLE {
IO.EndOfStream => GOTO Die;
IO.Error => GOTO Die;
};
token.length ← 0;
token ← commandLineStream.GetToken[IO.TokenProc, token].token;
name ← Rope.FromRefText[token];
commandLineStream.Close[];
Search up chain of parents to find a ReadEvalPrint.Handle and the associated commander
WHILE br.rep =
NIL
DO
br.rep ← NARROW[List.Assoc[key: $ReadEvalPrintHandle, aList: cth.propertyList]];
IF br.rep # NIL AND br.rep.viewer # NIL THEN EXIT
ELSE br.rep ← NIL;
cth ← NARROW[List.Assoc[key: $ParentCommander, aList: cth.propertyList]];
IF cth = NIL THEN RETURN[$Failure, "Can't find $ReadEvalPrintHandle"];
ENDLOOP;
oldEntry ← Menus.FindEntry[menu: br.rep.viewer.menu, entryName: name];
IF oldEntry = NIL THEN RETURN[$Failure, "Can't find that button"];
Menus.ReplaceMenuEntry[menu: br.rep.viewer.menu, oldEntry: oldEntry, newEntry: NIL];
ViewerOps.PaintViewer[viewer: br.rep.viewer, hint: menu, clearClient: FALSE];
EXITS
Die => NULL;
};
};
There are specialized tokens:
$CurrentSelection$ => replaced by the current selection up to but not including the first carriage return
$FileNameSelection$ => replaced by the current selection if it appears to be a file name, otherwise replaced by the name of the selected viewer
$SelectedViewerName$ => replaced by the name of the selected viewer
$ViewerPosition$ => replaced by the position of the current selection in a viewer
$MouseButton$ => "left", "middle", or "right"
$ShiftKey$ => "shift", "noShift"
$ControlKey$ "control", "noControl"
ButtonImplButtonProc: Menus.ClickProc = {
br: ButtonImplRef ← NARROW[clientData];
def: Rope.ROPE;
curSel: Rope.ROPE;
viewer: ViewerClasses.Viewer ← NIL;
start: TiogaOps.Location;
viewerName: Rope.ROPE ← NIL;
fileName: Rope.ROPE ← NIL;
index: INT ← -1;
pos: INT;
controlRope: Rope.ROPE ← IF control THEN "control" ELSE "noControl";
shiftRope: Rope.ROPE ← IF shift THEN "shift" ELSE "noShift";
buttonRope: Rope.ROPE;
SELECT mouseButton
FROM
red => buttonRope ← "red";
yellow => buttonRope ← "middle";
blue => buttonRope ← "right";
ENDCASE => ERROR;
IF br = NIL THEN RETURN;
def ← br.def;
[viewer: viewer, start: start] ← TiogaOps.GetSelection[primary];
curSel ← ViewerTools.GetSelectionContents[];
IF viewer #
NIL
AND
NOT viewer.destroyed
AND
NOT viewer.newFile
THEN {
root: TiogaOps.Ref ← TiogaOps.Root[start.node];
offset: INT ← TiogaOps.LocOffset[loc1: [root, 0], loc2: start, skipCommentNodes: TRUE];
index ← offset;
viewerName ← viewer.name;
};
Get prefix of current selection before the first CR
pos ← curSel.Find["\n"];
IF pos # -1 THEN curSel ← Rope.Substr[base: curSel, start: 0, len: pos];
The curSel is the fileName if curSel is longer than one character and contains no whitespace.
fileName ← IF (Rope.SkipTo[s: curSel, pos: 0, skip: " "] = curSel.Length[]) AND (curSel.Length[] > 1) THEN curSel ELSE viewerName;
def ← RopeSubst[old: "$CurrentSelection$", new: curSel, base: def, case: TRUE, allOccurrences: TRUE];
def ← RopeSubst[old: "$FileNameSelection$", new: fileName, base: def, case: TRUE, allOccurrences: TRUE];
def ← RopeSubst[old: "$SelectedViewerName$", new: viewerName, base: def, case: TRUE, allOccurrences: TRUE];
def ← RopeSubst[old: "$ViewerPosition$", new: Convert.RopeFromInt[index, 10, FALSE], base: def, case: TRUE, allOccurrences: TRUE];
def ← RopeSubst[old: "$MouseButton$", new: buttonRope, base: def, case: TRUE, allOccurrences: TRUE];
def ← RopeSubst[old: "$ControlKey$", new: controlRope, base: def, case: TRUE, allOccurrences: TRUE];
def ← RopeSubst[old: "$ShiftKey$", new: shiftRope, base: def, case: TRUE, allOccurrences: TRUE];
{
bufferContents: REF TEXT ← ViewerIOExtras.GetBuffer[br.rep.in];
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: br.rep.in, nChars: bufferContents.length - n - 1];
EXIT;
}
REPEAT
FINISHED => EditedStream.UnAppendBufferChars[stream: br.rep.in, nChars: LAST[NAT]];
ENDLOOP;
};
};
If the selected viewer is the commandtoo, then set the caret to the end, or the ViewerIOExtras.TypeChars won't work.
IF viewer = br.rep.viewer THEN ViewerTools.SetSelection[viewer: viewer, selection: NIL];
ViewerIOExtras.TypeChars[editedViewerStream: br.rep.in, chars: def];
};
ButtonImplRef: TYPE = REF ButtonImplObject;
ButtonImplObject:
TYPE =
RECORD [
rep: ReadEvalPrint.Handle ← NIL,
def: Rope.ROPE ← NIL
];
Init:
PROC = {
Commander.Register[key: "///Commands/Alias", proc: Alias, doc: "Create an alias for a command"];
Commander.Register[key: "///Commands/CreateButton", proc: CreateButton, doc: "Create a CommandTool herald button"];
CommandExtras.MakeUninterpreted[Commander.Lookup["///Commands/CreateButton"]];
Commander.Register[key: "///Commands/DeleteButton", proc: DeleteButton, doc: "Delete a CommandTool herald button"];
};
Init[];
END.