<> <> <> <> <> DIRECTORY Atom USING [GetPName], Commander USING [CommandProc, Handle, Register], CommandTool USING [DoCommand, GetProp], Convert USING [RopeFromInt], EditedStream USING [GetEcho, Rubout, SetEcho, SetMode, UnAppendBufferChars], FileNames USING [GetShortName], IO USING [BreakProc, Close, EndOf, EndOfStream, Error, GetRefAny, GetToken, IDProc, PeekChar, RIS, SkipWhitespace, STREAM, TokenProc], MBQueue USING [CreateMenuEntry], Menus USING [AppendMenuEntry, ClickProc, CopyEntry, CreateMenu, 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], UserCredentials USING [Get, Login], UserProfile USING [CallWhenProfileChanges, Line, ProfileChangedProc], ViewerClasses USING [Viewer], ViewerIO USING [GetBuffer, TypeChars], ViewerOps USING [EnumerateViewers, EnumProc, FetchProp, PaintViewer], ViewerTools USING [GetSelectionContents, SetSelection]; CommandsCImpl: CEDAR PROGRAM IMPORTS Atom, Commander, CommandTool, Convert, EditedStream, FileNames, IO, MBQueue, Menus, Rope, RopeList, TiogaOps, UserCredentials, UserProfile, ViewerIO, ViewerOps, ViewerTools = BEGIN AliasCellObject: TYPE = RECORD [ args: LIST OF ROPE _ NIL, def: ROPE ]; AliasCell: TYPE = REF AliasCellObject; ROPE: TYPE = Rope.ROPE; lastUser: ROPE _ NIL; <> asterisky: BOOLEAN _ FALSE; <> Alias: Commander.CommandProc = TRUSTED { commandLineStream: IO.STREAM = IO.RIS[cmd.commandLine]; name, def: ROPE; token: REF TEXT _ NEW[TEXT[30]]; args: LIST OF 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 => 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, case: BOOL _ FALSE, allOccurrences: BOOL _ TRUE] RETURNS[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]; }; RopeMemb: PROC [rope: ROPE, lst: LIST OF ROPE] RETURNS[BOOL] = { FOR l: LIST OF 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: CHAR _ 'A] RETURNS[ROPE] = { gennum _ gennum + 1; RETURN[Rope.Concat[Rope.FromChar[c], Convert.RopeFromInt[gennum, 10, FALSE]]]; }; <<>> AliasImplProc: Commander.CommandProc = TRUSTED { aliasCell: AliasCell _ NARROW[cmd.procData.clientData]; newCommandLine: ROPE _ aliasCell.def; token: REF TEXT _ NEW[TEXT[40]]; new: ROPE; restOfStream: ROPE; commandLineStream: IO.STREAM = IO.RIS[cmd.commandLine]; synonyms: LIST OF REF SynonymRecord; SynonymRecord: TYPE = RECORD[key, val: ROPE]; FOR l: LIST OF ROPE _ aliasCell.args, l.rest UNTIL l = NIL DO token.length _ 0; token _ commandLineStream.GetToken[IO.IDProc, token ! IO.EndOfStream => CONTINUE; IO.Error => GO TO Nasty ].token; new _ Rope.FromRefText[token]; IF RopeMemb[new, l.rest] THEN { <> dummy: 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 ! IO.EndOfStream => CONTINUE; IO.Error => GO TO Nasty ].token; restOfStream _ Rope.FromRefText[token]; newCommandLine _ Rope.Cat[newCommandLine, restOfStream, "\n"]; commandLineStream.Close[]; result _ CommandTool.DoCommand[commandLine: newCommandLine, parent: cmd]; EXITS Nasty => {msg _ "IO.Error while parsing arguments"; result _ $Failed}; }; CRBreak: IO.BreakProc = { IF char = '\n THEN RETURN[break]; RETURN[other]; }; CreateButton: Commander.CommandProc = TRUSTED { <> commandLineStream: IO.STREAM = IO.RIS[cmd.commandLine]; name: ROPE; def: ROPE; token: REF TEXT _ NEW[TEXT[30]]; br: ButtonImplRef _ NEW[ButtonImplObject]; cth: Commander.Handle _ cmd; reph: ReadEvalPrint.Handle _ br.rep _ GetReadEvalPrint[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]; <> token.length _ 0; token _ commandLineStream.GetToken[CRBreak, token ! IO.EndOfStream => CONTINUE].token; EXITS Die => NULL; }; IO.Close[commandLineStream]; 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; IF reph = NIL THEN RETURN [$Failure, "Can't find $ReadEvalPrintHandle"] ELSE { <> <<(0) no old entry & no new definition => ignore>> <<(1) no old entry & a new definition => create a new entry>> <<(2) old entry & no new definition => remove the old one>> <<(3) old entry & a new definition => replace the old entry>> viewer: ViewerClasses.Viewer _ reph.viewer; IF viewer # NIL THEN { menu: Menus.Menu _ reph.viewer.menu; IF menu # NIL THEN { new: Menus.MenuEntry _ IF def = NIL THEN NIL ELSE MBQueue.CreateMenuEntry[q: reph.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]; }; }; }; }; ClearMenu: Commander.CommandProc = TRUSTED { <> reph: ReadEvalPrint.Handle _ GetReadEvalPrint[cmd]; IF reph # NIL THEN { viewer: ViewerClasses.Viewer _ reph.viewer; IF viewer # NIL THEN { oldMenu: Menus.Menu _ viewer.menu; IF oldMenu # NIL THEN { <> newMenu: Menus.Menu _ Menus.CreateMenu[1]; CopyNamedEntry["STOP!", oldMenu, newMenu]; CopyNamedEntry["Find", oldMenu, newMenu]; CopyNamedEntry["Split", oldMenu, newMenu]; reph.viewer.menu _ newMenu; ViewerOps.PaintViewer[viewer: viewer, hint: menu, clearClient: FALSE]; }; RETURN; }; }; }; CopyNamedEntry: PROC [name: ROPE, oldMenu,newMenu: Menus.Menu] = { old: Menus.MenuEntry _ Menus.FindEntry[oldMenu, name]; IF old # NIL THEN Menus.AppendMenuEntry[newMenu, Menus.CopyEntry[old], 0]; }; 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; }; StuffCommand: PROC [viewer: ViewerClasses.Viewer, commands: ROPE] = { WITH ViewerOps.FetchProp[viewer, $ReadEvalPrint] SELECT FROM reph: ReadEvalPrint.Handle => { data: ButtonImplRef _ NEW[ButtonImplObject _ [rep: reph, def: commands]]; ButtonImplButtonProc[viewer, data]; }; ENDCASE; }; ButtonImplButtonProc: Menus.ClickProc = { <<[parent: REF ANY, clientData: REF ANY _ NIL, mouseButton: Menus.MouseButton _ red, shift: BOOL _ FALSE, control: BOOL _ FALSE]>> <> <<$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">> br: ButtonImplRef _ NARROW[clientData]; def: ROPE; curSel: ROPE; viewer: ViewerClasses.Viewer _ NIL; start: TiogaOps.Location; viewerName: ROPE _ NIL; fileName: ROPE _ NIL; shortFileName: ROPE _ NIL; index: INT _ -1; pos: INT; controlRope: ROPE _ IF control THEN "control" ELSE "noControl"; shiftRope: ROPE _ IF shift THEN "shift" ELSE "noShift"; buttonRope: ROPE; SELECT mouseButton FROM red => buttonRope _ "left"; 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.file; IF viewerName = NIL THEN viewerName _ viewer.name; fileName _ viewer.file; fileName _ Rope.Substr[fileName, 0, Rope.SkipTo[fileName, 0, "!"]]; }; <> pos _ curSel.Find["\n"]; IF pos # -1 THEN curSel _ Rope.Substr[base: curSel, start: 0, len: pos]; <> fileName _ IF (Rope.SkipTo[s: curSel, pos: 0, skip: " \t"] = curSel.Length[]) AND (curSel.Length[] > 1) THEN curSel ELSE fileName; shortFileName _ FileNames.GetShortName[path: fileName, stripOffVersionNumber: TRUE]; IF Rope.SkipTo[def, 0, "$"] < Rope.Length[def] THEN { <> 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 _ ViewerIO.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 viewer = br.rep.viewer THEN ViewerTools.SetSelection[viewer: viewer, selection: NIL]; ViewerIO.TypeChars[editedViewerStream: br.rep.in, chars: def]; }; ButtonImplRef: TYPE = REF ButtonImplObject; ButtonImplObject: TYPE = RECORD [ rep: ReadEvalPrint.Handle _ NIL, def: ROPE _ NIL ]; WhenProfileChanges: UserProfile.ProfileChangedProc = { IF reason = rollBack THEN { <> user: ROPE _ UserCredentials.Get[].name; IF NOT Rope.Equal[user, lastUser, FALSE] THEN { <> firstTime: BOOL _ TRUE; eachTool: ViewerOps.EnumProc = { <<[v: ViewerClasses.Viewer] RETURNS [BOOL _ TRUE]>> IF Rope.Match["CommandTool: *", v.name] AND v.class.flavor = $Typescript AND NOT v.destroyed THEN { IF firstTime THEN { firstTime _ FALSE; StuffCommand[v, "///Commands/NoteNewUser\n"]; }; StuffCommand[v, "///Commands/NotePerLogin\n"]; }; }; ViewerOps.EnumerateViewers[eachTool]; }; }; }; NotePerLogin: Commander.CommandProc = { line: ROPE _ UserProfile.Line["CommandTool.PerLogin", NIL]; IF line # NIL THEN [] _ CommandTool.DoCommand[line, cmd]; }; NoteNewUser: Commander.CommandProc = { line: ROPE _ UserProfile.Line["CommandTool.NewUser", NIL]; lastUser _ UserCredentials.Get[].name; IF line # NIL THEN [] _ CommandTool.DoCommand[line, cmd]; }; NotePerCommandTool: Commander.CommandProc = { line: ROPE _ UserProfile.Line["CommandTool.PerCommandTool", NIL]; IF line = NIL THEN line _ UserProfile.Line["CommandTool.EachCommandToolCommands", NIL]; <> IF line # NIL THEN [] _ CommandTool.DoCommand[line, cmd]; }; NoteBootCommands: Commander.CommandProc = { line: ROPE _ UserProfile.Line["CommandTool.BootCommands", NIL]; IF line # NIL THEN [] _ CommandTool.DoCommand[line, cmd]; }; Login: Commander.CommandProc = { oldEcho: IO.STREAM; StartInteraction: PROC RETURNS [in, out: IO.STREAM] = { IF asterisky THEN EditedStream.SetMode[stream: cmd.in, echoAsterisks: TRUE] ELSE { oldEcho _ EditedStream.GetEcho[cmd.in]; EditedStream.SetEcho[cmd.in, NIL]; }; in _ cmd.in; out _ cmd.out; }; EndInteraction: PROC [in, out: IO.STREAM] = { IF asterisky THEN EditedStream.SetMode[stream: cmd.in, echoAsterisks: FALSE] ELSE EditedStream.SetEcho[cmd.in, oldEcho]; }; UserCredentials.Login[ startInteraction: StartInteraction, endInteraction: EndInteraction, options: [alwaysInteract: TRUE] ! EditedStream.Rubout => {result _ $Failed; msg _ " -- Aborted.\n"; CONTINUE} ]; }; Init: PROC = { Commander.Register[key: "///Commands/Alias", proc: Alias, doc: "Create an alias for a command", interpreted: FALSE]; Commander.Register[key: "///Commands/CreateButton", proc: CreateButton, doc: "Create a CommandTool herald button", interpreted: FALSE]; Commander.Register[key: "///Commands/ClearMenu", proc: ClearMenu, doc: "Reset the CommandTool menu"]; Commander.Register[key: "///Commands/Login", proc: Login, doc: "Login a new user"]; Commander.Register[key: "///Commands/NoteBootCommands", proc: NoteBootCommands, doc: "Perform commands for the first commander after a full boot"]; Commander.Register[key: "///Commands/NoteNewUser", proc: NoteNewUser, doc: "Perform commands for a new user"]; Commander.Register[key: "///Commands/NotePerCommandTool", proc: NotePerCommandTool, doc: "Perform commands for a login (or rollback)"]; Commander.Register[key: "///Commands/NotePerLogin", proc: NotePerLogin, doc: "Perform commands for a login (or rollback)"]; Commander.Register[key: "///Commands/RemoveButton", proc: CreateButton, doc: "Remove a CommandTool herald button"]; UserProfile.CallWhenProfileChanges[WhenProfileChanges]; }; Init[]; END.