UserExecMethodsImpl.mesa; Edited by Teitelman on April 23, 1983 1:45 pm
DIRECTORY
AMTypes USING [Class, TypeClass, UnderType, TVType, TVToType, Range, TypeToName],
Commander USING [Lookup, Enumerate, CommandProc],
IO USING [char, ControlX, CreateViewerStreams, CurrentPosition, Flush, EndOf, EraseChar, ESC, NewLine, Put, PutChar, PutF, PutRope, PutType, ROPE, rope, RIS, STREAM, text, type, UserAbort, BreakProc, SpaceTo, GetToken, TokenProc, WhiteSpace, SkipOver, UserAborted],
List USING [Sort, CompareProc],
MessageWindow USING [Append, Blink],
Rope USING [Compare, Concat, Equal, Fetch, Find, IsEmpty, Length, Replace, Run, Substr],
Spell USING [defaultModes, ModesRecord],
UserProfile USING [ProfileChangedProc, CallWhenProfileChanges],
UserExec USING [HistoryEvent, CommandProc, ExecHandle, Expression, MethodProc, RegisterCommand, RegisterMethod, TV, Type, CheckForFile, GetMatchingList, GetMatchingFileList, GetStreams],
UserExecPrivate USING [commandGenerator, DoesUserMean, EventFailed, EvalEvent, ExecPrivateRecord, ExpandCommandLine, GetRegistrationData, PrintDeclFromSource, RunAndCall, ReadQuietly, CallRegisteredProc, CommandRecord, LookupCommand, methodList, MethodList, GetRestOfStream, HistoryEventPrivateRecord]
;
UserExecMethodsImpl:
CEDAR
PROGRAM
IMPORTS AMTypes, IO, Commander, List, MessageWindow, Rope, Spell, UserExec, UserExecPrivate, UserProfile
EXPORTS UserExec, UserExecPrivate
= BEGIN OPEN IO, UserExec;
connecting concrete and opaque types
MethodProc: TYPE = UserExec.MethodProc; -- PROC [exec: ExecHandle] RETURNS[handled: BOOLEAN ← FALSE]
ExecPrivateRecord: PUBLIC TYPE = UserExecPrivate.ExecPrivateRecord;
HistoryEventPrivateRecord: PUBLIC TYPE = UserExecPrivate.HistoryEventPrivateRecord;
constants
dontConfirmModes: REF Spell.ModesRecord ← NEW[Spell.ModesRecord ← [inform: NULL, confirm: NULL, disabled: NULL, timeout: NULL, defaultConfirm: NULL]]; -- if user profile indicates confirmation required for * expansion, this will override that for benign operations such as ?, ^X etc.
SetDontConfirmModes: UserProfile.ProfileChangedProc = {
dontConfirmModes^ ← Spell.defaultModes^;
dontConfirmModes.confirm ← MIN[allAccountedFor, dontConfirmModes.confirm];
dontConfirmModes.inform ← MIN[allAccountedFor, dontConfirmModes.inform];
};
Methods
Help: MethodProc = {
OPEN Rope;
out: STREAM;
commandLine: ROPE = event.commandLine;
inRopeStream: STREAM;
len: INT;
allButLast, firstToken: ROPE;
len ← Rope.Length[commandLine];
SELECT Rope.Fetch[commandLine, len - 1]
FROM
'? => NULL;
ENDCASE => RETURN[FALSE];
out ← UserExec.GetStreams[exec].out;
NewLine[out];
IF len = 1
THEN {
PrintAllCommands[out];
RETURN[TRUE];
};
allButLast ← Rope.Substr[commandLine, 0, len - 1];
inRopeStream ← RIS[allButLast];
firstToken ← IO.GetToken[inRopeStream, IF Rope.Fetch[allButLast, 0] = '← THEN IO.TokenProc ELSE IO.WhiteSpace]; -- added IO.WhiteSpace to handle things like foo*?
IO.SkipOver[inRopeStream, IO.WhiteSpace];
IF inRopeStream.EndOf[]
THEN {
-- command followed by ?
IF NOT PrintCommand[name: firstToken, event: event, exec: exec] THEN out.PutF["*nnot a command\n"]; RETURN[TRUE];
}
ELSE IF Rope.Equal[firstToken, "←"]
THEN
{
-- print type of object
OPEN AMTypes;
expr: Expression;
i: INT;
target: ROPE;
event.commandLine ← Rope.Concat[UserExecPrivate.GetRestOfStream[inRopeStream], "\n"];
[] ← IO.RIS[event.commandLine, event.commandLineStream];
UserExecPrivate.EvalEvent[event, exec];
expr ← event.expression;
IF expr.correctionMade THEN event.input ← Rope.Concat[event.input, "?"];
out.PutRope["is of type "]; -- start printing so user doesnt see such a long delay.
IF (i ← Rope.Find[expr.rope, "."]) # -1
-- is of form a.b, rather than a.b.c
AND Rope.Find[s1: expr.rope, s2: ".", pos1: i + 1] = -1
THEN {
fileName: ROPE = Rope.Concat[Rope.Substr[base: expr.rope, len: i], ".mesa"];
target ← Rope.Substr[base: expr.rope, start: i + 1, len: Rope.Length[expr.rope] - i - 1];
IF IO.WhiteSpace[Rope.Fetch[target, Rope.Length[target] - 1]] = sepr THEN target ← Rope.Substr[base: target, len: Rope.Length[target] - 1];
IF NOT UserExecPrivate.PrintDeclFromSource[target: target, file: fileName, exec: exec] THEN target ← NIL; -- to indicate that it didnt find it in file
};
{
typ, underType: Type;
class: AMTypes.Class;
typeName: ROPE;
typ ← TVType[expr.value];
underType ← UnderType[typ];
class ← TypeClass[underType];
IF target =
NIL
-- didn't find it in file.
OR Rope.Length[typeName ← TypeToName[typ]] # 0
-- named type. All that was printed was the name (and possibly comments), e.g. IO.CRBreak. calling PutType will print undertype.
THEN out.PutType[type: typ, verbose: TRUE]
ELSE
SELECT class
FROM
type =>
-- print the undertype. Assumption is that the top level type would be the same as what you got from the file, e.g. STREAM? the file says of TYPE = REF HandleRecord, now printing from type system gives you what HandleRecord is. However, note that for synonyms, you haven't seen type yet, e.g. if I ask for UserExec.HistoryEvent, it tells me from the file that this is History.HistoryEvent, and now I will print out what HistoryEventNode is. The right thing to do probably is to try to print the type from type system and see if what you get is the same.
{
range: Type = UnderType[AMTypes.TVToType[expr.value]];
SELECT TypeClass[range]
FROM
ref, pointer, longPointer =>
-- e.g. STREAM?
{
rangeType: Type = Range[range];
underRangeType: Type = UnderType[rangeType];
SELECT TypeClass[underRangeType]
FROM
record, structure =>
out.Put[type[rangeType], text[": TYPE = "], type[underRangeType]];
ENDCASE;
};
ENDCASE;
};
ENDCASE;
};
RETURN[TRUE];
}
-- FUTURE EXTENSIONS TO ALLOW ? AT SOME LATER POINT IN COMMAND LINE.
}; -- Help
ExpandControlX: MethodProc = {
OPEN Rope;
breakProc: IO.BreakProc = {
RETURN[IF char = ControlX THEN sepr ELSE IO.WhiteSpace[char]];
};
commandLine: ROPE ← event.commandLine;
out: STREAM;
inRopeStream: STREAM;
registration: REF UserExecPrivate.CommandRecord;
len: INT;
allButLast, firstToken: ROPE;
len ← Rope.Length[commandLine];
SELECT Rope.Fetch[commandLine, len - 1]
FROM
ControlX => NULL;
ENDCASE => RETURN[FALSE];
out ← UserExec.GetStreams[exec].out;
out.EraseChar[ControlX];
out.Flush[];
allButLast ← Rope.Substr[commandLine, 0, len - 1];
inRopeStream ← RIS[rope: allButLast];
firstToken ← IO.GetToken[inRopeStream, breakProc];
registration ← UserExecPrivate.GetRegistrationData[UserExecPrivate.LookupCommand[name: firstToken, event: event, exec: exec]];
IF registration #
NIL
AND registration.transformProc #
NIL
THEN {
event.commandLine ← Rope.Concat[UserExecPrivate.GetRestOfStream[inRopeStream], "\n"];
[] ← RIS[rope: event.commandLine, oldStream: event.commandLineStream]; -- set up to call transformProc
commandLine ← registration.transformProc[event, exec, registration.clientData];
}
ELSE {
commandLine ← UserExecPrivate.ExpandCommandLine[line: Rope.Substr[commandLine, 0, len - 1], modes: dontConfirmModes, event: event, exec: exec];
};
WHILE (len ← Rope.Length[commandLine]) # 0
AND IO.WhiteSpace[Rope.Fetch[commandLine, len -1]] = sepr
DO
commandLine ← Rope.Substr[base: commandLine, len: len - 1];
ENDLOOP;
out.Put[char['\n], rope[commandLine]];
UserExecPrivate.ReadQuietly[rope: commandLine, exec: exec];
RETURN[TRUE];
}; -- of Expand
Escape: MethodProc = {
OPEN Rope;
commandLine: ROPE = event.commandLine;
out: STREAM;
inRopeStream: STREAM;
allButLast, firstToken: ROPE;
first: BOOLEAN ← TRUE;
r: ROPE;
matches: LIST OF ROPE;
len: INT;
len ← Rope.Length[commandLine];
SELECT Rope.Fetch[commandLine, len - 1]
FROM
ESC => NULL;
ENDCASE => RETURN[FALSE];
out ← UserExec.GetStreams[exec].out;
out.EraseChar[ESC];
out.Flush[];
allButLast ← Rope.Substr[commandLine, 0, len - 1];
inRopeStream ← RIS[rope: allButLast];
DO
firstToken ← IO.GetToken[inRopeStream];
IO.SkipOver[inRopeStream, IO.WhiteSpace];
IF inRopeStream.EndOf[] THEN EXIT;
first ← FALSE;
ENDLOOP;
IF first
THEN
-- registered command
matches ← UserExec.GetMatchingList[unknown: Rope.Concat[firstToken, "*"], generator: UserExecPrivate.commandGenerator, modes: dontConfirmModes, event: event, exec: exec]
ELSE
-- assume is a file name
matches ← UserExec.GetMatchingFileList[file: Rope.Concat[firstToken, "*"], modes: dontConfirmModes, event: event, exec: exec];
r ← EscComplete[unknown: firstToken, matches: matches];
out.PutRope[r]; -- want to make it look like event just continued, so output extra characters.
IF Rope.IsEmpty[r]
THEN {
MessageWindow.Append["No match", TRUE];
MessageWindow.Blink[];
};
UserExecPrivate.ReadQuietly[rope: Rope.Replace[base: commandLine, start: len - 1, len: 1, with: r], exec: exec];
RETURN[TRUE];
}; -- of Escape
matches is a LIST of ropes whose leading characters match unknown (probably obtained via FixSpell or FileSpell). EscComplete computes the longest common substring, echoes the characters to exec.out, followed by a space if only one candidate, and returns the longest common substring.
ImplicitRunAndCall: MethodProc = {
commandLine: ROPE = event.commandLine;
commandLineStream: STREAM = event.commandLineStream;
privateEvent: REF HistoryEventPrivateRecord = event.privateStuff;
firstToken, name, error: ROPE ← NIL;
i: INT;
IF privateEvent.inCMFile THEN {[] ← UserExecPrivate.RunAndCall[event: event, exec: exec, clientData: NIL]; RETURN[TRUE]};
IF privateEvent.inCommandFile THEN RETURN[FALSE];
firstToken ← IO.GetToken[commandLineStream];
IF (i ← Rope.Find[s1: firstToken, s2: "."]) = -1 THEN firstToken ← Rope.Concat[firstToken, ".bcd"]
ELSE IF Rope.Find[s1: firstToken, s2: "bcd", pos1: i, case: FALSE] = -1 THEN RETURN[FALSE];
IF NOT UserExec.CheckForFile[firstToken] THEN RETURN[FALSE];
IO.SkipOver[commandLineStream, IO.WhiteSpace];
UserExecPrivate.DoesUserMean[Rope.Concat[
IF commandLineStream.EndOf[]
THEN "Run "
ELSE "RunAndCall ",
Rope.Substr[base: commandLine, len: Rope.Length[commandLine] - 1]], exec];
RETURN[TRUE];
};
Registered: MethodProc = {
breakProc: IO.BreakProc = {
RETURN[IF char = ControlX THEN sepr ELSE IO.TokenProc[char]];
};
commandLineStream: STREAM = event.commandLineStream;
privateEvent: REF HistoryEventPrivateRecord = event.privateStuff;
firstToken: ROPE;
command: ROPE;
i: INT;
firstToken ← IO.GetToken[commandLineStream, breakProc];
command ← UserExecPrivate.LookupCommand[name: firstToken, event: event, exec: exec]; -- does correction.
IF command =
NIL
AND privateEvent.inCMFile
AND (i ← Rope.Find[s1: firstToken, s2: ".~"]) # -1
THEN
{
token: ROPE = Rope.Substr[base: firstToken, len: i];
command ← UserExecPrivate.LookupCommand[name: token, event: NIL, exec: NIL];
IF command = NIL THEN UserExecPrivate.EventFailed[event: event, msg: Rope.Concat[token, " is not a registered command.\n"], offender: token];
};
event.commandLine ← UserExecPrivate.GetRestOfStream[commandLineStream];
[] ← IO.RIS[event.commandLine, event.commandLineStream];
RETURN[UserExecPrivate.CallRegisteredProc[command, event, exec]];
};
Miscellaneous
EscComplete:
PROC [unknown:
ROPE, matches:
LIST
OF
ROPE]
RETURNS [common:
ROPE] = {
lenX: INT = Rope.Length[unknown];
r: ROPE;
i: INT ← 0;
IF matches = NIL THEN RETURN[""];
r ← matches.first;
IF matches.rest = NIL THEN RETURN[Rope.Concat[Rope.Substr[base: r, start: lenX], " "]];
i ← Rope.Length[r];
FOR l: LIST OF ROPE ← matches.rest, l.rest UNTIL l = NIL DO
i ← MIN[Rope.Run[s1: r, s2: l.first, case: FALSE]];
ENDLOOP;
RETURN[Rope.Substr[base: r, start: lenX, len: i - lenX]];
};
ExplainExec: UserExec.CommandProc = {
out: STREAM = IO.CreateViewerStreams["UserExec.doc"].out;
out.PutRope["\nMethods are applied to the input line in the order listed below until one succeeds. If none succeed, print a message and go to next event."];
out.PutRope["\n\nMethods:\n"];
FOR lst: UserExecPrivate.MethodList ← UserExecPrivate.methodList, lst.rest
UNTIL lst =
NIL
DO
out.PutF["\n%g\n\t\t%g", rope[lst.first.name], rope[lst.first.doc]];
ENDLOOP;
out.PutRope["\n\nRegistered Commands: (*) following the explanation indicates further explanation available by typing {command}?\n"];
PrintAllCommands[out];
};
PrintCommand:
PUBLIC
PROC [name:
ROPE, event: HistoryEvent, exec: UserExec.ExecHandle]
RETURNS[
BOOLEAN] = {
command: ROPE ← UserExecPrivate.LookupCommand[name: name, event: event, exec: exec];
out: STREAM = UserExec.GetStreams[exec].out;
IF command =
NIL
THEN
-- name might be a pattern that matches more than one command
TRUSTED {
lst: LIST OF ROPE = UserExec.GetMatchingList[unknown: name, generator: UserExecPrivate.commandGenerator, modes: dontConfirmModes, event: event, exec: exec];
compare: List.CompareProc =
TRUSTED {
RETURN[Rope.Compare[NARROW[ref1], NARROW[ref2], FALSE]];
};
IF lst = NIL THEN RETURN[FALSE];
FOR commands:
LIST
OF
ROPE ←
LOOPHOLE[List.Sort[
LOOPHOLE[lst], compare]], commands.rest
UNTIL commands =
NIL
DO
[] ← PrintCommand[name: commands.first, event: event, exec: exec];
ENDLOOP;
}
ELSE {
reg: REF UserExecPrivate.CommandRecord = UserExecPrivate.GetRegistrationData[command];
doc: ROPE;
out.PutF["*n%-15g", rope[command]];
IF reg # NIL THEN doc ← reg.documentation ELSE doc ← Commander.Lookup[command].doc;
IF Rope.Length[doc] = 0 THEN out.PutRope["no documentation supplied with command"]
ELSE out.Put[rope[doc]];
IF reg # NIL AND reg.nameOfBCD # NIL THEN out.PutF["*n{not loaded yet}"];
};
RETURN[TRUE];
};
PrintAllCommands:
PROC [out:
STREAM] = {
commandList: LIST OF REF Reg;
Reg: TYPE = RECORD[name, doc: ROPE];
makeList:
PROCEDURE [name:
ROPE, proc: Commander.CommandProc, doc:
ROPE]
RETURNS [stop:
BOOL] = {
commandList ← CONS[NEW[Reg ← [name, doc]], commandList];
RETURN[FALSE];
};
compare: List.CompareProc = {
a, b: REF Reg;
a ← NARROW[ref1];
b ← NARROW[ref2];
RETURN[Rope.Compare[a.name, b.name, FALSE]];
};
[] ← Commander.Enumerate[makeList];
TRUSTED {commandList ← LOOPHOLE[List.Sort[LOOPHOLE[commandList], compare]]};
FOR l:
LIST
OF
REF Reg ← commandList, l.rest
UNTIL l =
NIL
DO
reg: REF UserExecPrivate.CommandRecord = UserExecPrivate.GetRegistrationData[l.first.name];
IF IO.UserAbort[out] THEN ERROR IO.UserAborted[out];
IF reg # NIL AND reg.fromCatalogue THEN LOOP;
out.PutF["\n%g", rope[l.first.name]];
IO.SpaceTo[out, 20];
IF reg #
NIL
THEN {
out.PutRope[reg.briefDocumentation];
IF reg # NIL AND NOT Rope.Equal[reg.documentation, reg.briefDocumentation] THEN out.PutRope[" (*)"];
}
ELSE {
doc: ROPE = l.first.doc;
i: INT = Rope.Find[doc, "\n"];
IF i # -1 THEN out.PutF["%g (**)", rope[Rope.Substr[base: doc, len: i]]] -- when registering with commander, this is the way to handle the brief versus full documentation issue
ELSE out.PutRope[doc]; -- when registering with commander, this is the way to handle the brief versus full documentation issue
};
ENDLOOP;
out.PutChar['\n];
};
Initialization
UserExec.RegisterMethod [proc: Help, name: "Help", doc: "? following a mesa expression, evaluates the expression and prints its type, e.g. {proc}? will print the arguments and return values of proc. If the mesa expression is of the form module.id, will also look in module for the declaration of id, and if found, print that declaration including its comments, instead of using the runtime type system. // In other contexts, ? presents explanation/documentation regarding the token immediately preceding the ?."];
UserExec.RegisterMethod [proc: ExpandControlX, name: "ControlX", doc: "causes the command line to be expanded and the result displayed to the user for confirmation before execution, e.g. @file => contents of file, history commands => corresponding input, * expansion performed, etc. "];
UserExec.RegisterMethod [proc: Escape, name: "ESC", doc: "completes the previous token where possible"];
UserExec.RegisterMethod [proc: Registered, name: "Registered Command", doc: "If the first token on the input line is the name of one of the registered commmands listed below (case does not matter), call the corresponding registered procedure on the remainder of the input line, e.g. Compile Foo Fie. // @{fileName} appearing in the input line is replaced by the contents of {fileNme} or {fileName}.cm, e.g. Print @myfiles."];
UserExec.RegisterMethod [proc: ImplicitRunAndCall, name: "Implicit RunAndCall", doc: "If the first token on the input line is the name of a bcd file, ask the user whether he mean to Run the bcd file, allowing him to confirm by simply typing CR."];
UserExec.RegisterCommand["Help", ExplainExec, "Provides more complete explanation of user exec in separate viewer."];
UserProfile.CallWhenProfileChanges[SetDontConfirmModes];
END. -- of UserExecMethodsImpl.mesa
Edited on December 9, 1982 12:30 am, by Teitelman
fixed bug in Escape causing BoundsFault
changes to: Escape
Edited on December 18, 1982 7:07 pm, by Teitelman
fixed PrintCommands to take a stream, rather than exec so that ExplainExec could output all material to the other viewer.
changes to: ExplainExec, PrintCommands
, DIRECTORY, Help
Edited on December 22, 1982 12:57 pm, by Teitelman
changes to: DIRECTORY, IMPORTS, Help, breakProc (local of ExpandControlX), ExpandControlX, Escape, ImplicitRunAndCall, breakProc (local of Registered), Registered
Edited on January 4, 1983 11:56 am, by Teitelman
changes to: Help, PrintCommand, ExpandControlX, Escape
Edited on January 21, 1983 4:23 pm, by Teitelman
changes to: ExpandControlX
Edited on March 1, 1983 11:41 pm, by Teitelman
changes to: PrintCommand
Edited on March 4, 1983 7:37 pm, by Teitelman
changes to: Help
Edited on March 6, 1983 2:44 pm, by Teitelman
changes to: DIRECTORY, Registered
Edited on March 10, 1983 3:06 am, by Teitelman
changes to: DIRECTORY, IMPORTS, Registered
Edited on March 28, 1983 3:51 pm, by Teitelman
changes to: DIRECTORY, IMPORTS, Escape
Edited on April 5, 1983 7:36 pm, by Teitelman
changes to: Help
Edited on April 7, 1983 2:33 pm, by Teitelman
changes to: DIRECTORY, IMPORTS, Help, Escape, Registered, PrintCommand, PrintCommands, SetDontConfirmModes, RegisterCommanderProc, CommanderProcRecord, CallCommanderProc, IsACommanderProc, EscComplete, [, UserProfile, Help, Registered, UserExec, IMPORTS, Registered, Registered
Edited on April 19, 1983 1:00 pm, by Teitelman
changes to: DIRECTORY, ExpandControlX, Registered, PrintCommand, IMPORTS, print (local of PrintCommands), PrintCommands, Help, PrintAllCommands, makeList (local of PrintAllCommands), compare (local of PrintAllCommands), IMPORTS, ExplainExec, PrintAllCommands, PrintAllCommands, PrintAllCommands
Edited on April 23, 1983 1:45 pm, by Teitelman
changes to: PrintCommand, compare (local of PrintCommand)