CommandsCImpl.mesa
Copyright © 1984 by Xerox Corporation. All rights reserved.
L. Stewart, January 17, 1984 11:08 am
Russ Atkinson, January 28, 1985 7:31:23 pm PST
DIRECTORY
Atom USING [GetPName],
Commander USING [CommandProc, Handle, Lookup, Register],
CommandExtras USING [MakeUninterpreted],
CommandTool USING [DoCommand],
Convert USING [RopeFromInt],
EditedStream USING [UnAppendBufferChars],
FileNames USING [GetShortName],
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, Menu, MenuEntry, ReplaceMenuEntry],
ReadEvalPrint USING [Handle],
Rope USING [Cat, Concat, Equal, Find, FromChar, FromRefText, Length, Match, 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, FileNames, 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[];
};
};
RopeSubst:
PROC [old, new, base: Rope.
ROPE, case:
BOOL ←
FALSE, allOccurrences:
BOOL ←
TRUE]
RETURNS[Rope.
ROPE] = {
if old is not found in base, then value = base.
if allOccurrences THEN substitute for each occurrence of old, otherwise only for first.
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];
};
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];
};
CreateButton: Commander.CommandProc =
TRUSTED {
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.
commandLineStream: IO.STREAM = IO.RIS[cmd.commandLine];
name: Rope.ROPE;
def: 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 ! IO.EndOfStream => CONTINUE].token;
def ← Rope.FromRefText[token];
SELECT
TRUE
FROM
Rope.Length[def] = 0 => def ← NIL;
Rope.Match["*\n", def] => {};
ENDCASE => def ← Rope.Concat[def, "\n"];
br.def ← def;
Search up chain of parents to find a ReadEvalPrint.Handle and the associated commander
DO
br.rep ← NARROW[List.Assoc[key: $ReadEvalPrintHandle, aList: cth.propertyList]];
IF br.rep # NIL AND br.rep.viewer # NIL THEN EXIT;
cth ← NARROW[List.Assoc[key: $ParentCommander, aList: cth.propertyList]];
IF cth = NIL THEN RETURN[$Failure, "Can't find $ReadEvalPrintHandle"];
ENDLOOP;
Various cases: (1) if there is no old entry, and there is a new definition, then create a new entry, (2) if there is an old entry and a new definition, replace the old entry, (3) if there is an old entry and no new definition, remove the old one, otherwise no action.
IF br #
NIL
AND br.rep #
NIL
THEN {
viewer: ViewerClasses.Viewer ← br.rep.viewer;
IF viewer #
NIL
THEN {
menu: Menus.Menu ← br.rep.viewer.menu;
IF menu #
NIL
THEN {
new: Menus.MenuEntry ← IF def = NIL THEN NIL ELSE MBQueue.CreateMenuEntry[q: br.rep.menuHitQueue, name: name, proc: ButtonImplButtonProc, clientData: br];
old: Menus.MenuEntry ← Menus.FindEntry[menu, name];
SELECT
TRUE
FROM
old # NIL => Menus.ReplaceMenuEntry[menu, old, new];
new # NIL => Menus.AppendMenuEntry[menu, new, 0];
ENDCASE;
ViewerOps.PaintViewer[viewer: viewer, hint: menu, clearClient: FALSE];
};
};
};
};
commandLineStream.Close[];
};
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
$ShortFileNameSelection$ => same as $FileNameSelection$ except that version number and directory are omitted
$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;
shortFileName: 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;
shortFileName ← FileNames.GetShortName[path: fileName, stripOffVersionNumber: TRUE];
def ← RopeSubst[
old: "$CurrentSelection$", new: curSel, base: def, case: TRUE];
def ← RopeSubst[
old: "$FileNameSelection$", new: fileName, base: def, case: TRUE];
def ← RopeSubst[
old: "$ShortFileNameSelection$", new: shortFileName, base: def, case: TRUE];
def ← RopeSubst[
old: "$SelectedViewerName$", new: viewerName, base: def, case: TRUE];
def ← RopeSubst[
old: "$ViewerPosition$", new: Convert.RopeFromInt[index, 10, FALSE], base: def, case: TRUE];
def ← RopeSubst[
old: "$MouseButton$", new: buttonRope, base: def, case: TRUE];
def ← RopeSubst[
old: "$ControlKey$", new: controlRope, base: def, case: TRUE];
def ← RopeSubst[
old: "$ShiftKey$", new: shiftRope, base: def, case: 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 commandtool, 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/RemoveButton", proc: CreateButton, doc: "Remove a CommandTool herald button"];
};
Init[];
END.