<> 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, IDProc, 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, dummyCommanderProc, HistoryEventPrivateRecord] ; UserExecMethodsImpl: CEDAR PROGRAM IMPORTS AMTypes, IO, Commander, List, MessageWindow, Rope, Spell, UserExec, UserExecPrivate, UserProfile EXPORTS UserExec, UserExecPrivate = BEGIN OPEN IO, UserExec; <> MethodProc: TYPE = UserExec.MethodProc; -- PROC [exec: ExecHandle] RETURNS[handled: BOOLEAN _ FALSE] ExecPrivateRecord: PUBLIC TYPE = UserExecPrivate.ExecPrivateRecord; HistoryEventPrivateRecord: PUBLIC TYPE = UserExecPrivate.HistoryEventPrivateRecord; <> 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]; }; <> 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.IDProc]; 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 <> 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]]; }; <> 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: "]; 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 AND Commander.Lookup[command].proc = UserExecPrivate.dummyCommanderProc 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]]; }; out.PutRope["(*) following the explanation indicates further explanation available by typing {command}?\n"]; [] _ 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]; }; <> 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 <<>> <> <> <> <> <> <> <<, DIRECTORY, Help>> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <<>> <> <> <> <> <> <<>>