EditorComfortsBImpl.mesa
Copyright © 1984, 1985, 1986 by Xerox Corporation. All rights reserved.
Created by Teitelman
Russ Atkinson (RRA) January 22, 1986 9:48:46 pm PST
Doug Wyatt, April 11, 1985 11:13:21 am PST
Spreitzer, February 28, 1986 3:27:14 pm PST
Eric Nickell, May 5, 1986 3:49:29 pm PDT
DIRECTORY
AMTypes USING [TV, TVToType, TVType, Type, UnderClass],
Interpreter USING [Evaluate],
IO USING [BreakProc, GetCedarTokenRope, RIS, RopeFromROS, ROS, STREAM, TokenProc],
MessageWindow USING [Append, Blink, Clear],
PrintTV USING [PrintType],
Process USING [Detach],
Rope USING [Cat, Concat, Fetch, Find, FromChar, Length, Replace, ROPE, Run, Substr],
TiogaOps USING [BackSpace, Break, CallWithLocks, ClearLooks, FirstChild, GetCaret, GetRope, GetSelection, GoToPreviousCharacter, InsertRope, IsComment, Location, Nest, Next, NextPlaceholder, PutProp, PutTextKey, Ref, RegisterAbbrevFailedProc, SaveSelA, SetComment, SetLooks, SetNotComment, SetSelection, ViewerDoc],
TiogaOpsDefs USING [Ref],
ViewerClasses USING [Viewer],
WindowManager USING [UnWaitCursor, WaitCursor];
EditorComfortsBImpl: CEDAR PROGRAM
IMPORTS AMTypes, Interpreter, IO, MessageWindow, PrintTV, Process, Rope, TiogaOps, WindowManager
= BEGIN
Types
Location: TYPE = TiogaOps.Location;
ROPE: TYPE = Rope.ROPE;
STREAM: TYPE = IO.STREAM;
TiogaRef: TYPE = TiogaOpsDefs.Ref;
TV: TYPE = AMTypes.TV;
Viewer: TYPE = ViewerClasses.Viewer;
Control-E expansion
Expand: PROC RETURNS [BOOL] = {
caret: Location ← TiogaOps.GetCaret[];
r: ROPE ← TiogaOps.GetRope[caret.node];
i: INT ← caret.where;
WHILE i >= 0 DO
IF i=0 OR (IO.TokenProc[Rope.Fetch[r, i - 1]] # other) THEN TRUSTED {
forked because evaluating the expression might require confirmation.
Process.Detach[FORK Expand0[Rope.Substr[base: r, start: i, len: caret.where - i], caret]];
RETURN [TRUE];
};
i ← i -1;
ENDLOOP;
RETURN [FALSE];
};
Expand0: PROC [name: ROPE, caret: Location] = {
Expand1: PROC [root: REF] = {
IF newName # NIL THEN {
TiogaOps.BackSpace[Rope.Length[name]];
TiogaOps.InsertRope[newName];
caret ← TiogaOps.GetCaret[];
};
TiogaOps.SaveSelA[];
TiogaOps.PutProp[n: caret.node, name: $StartOfExpansion, value: expansion];
TiogaOps.PutTextKey[node: caret.node, where: caret.where - 1, key: expansion];
associates a sticky address with the first character in the expansion. expansion is used as the key simply because it is a unique ref. could just as easily have done a gensym.
IF Rope.Run[s1: expansion, s2: " -- "] = 4
THEN {
expansion ← Rope.Substr[expansion, 4];
TiogaOps.ClearLooks[which: caret];
TiogaOps.InsertRope[" = {"];
TiogaOps.Break[];
TiogaOps.Nest[];
TiogaOps.SetComment[];
TiogaOps.SetLooks[looks: "ck", which: caret];
TiogaOps.InsertRope[expansion];
TiogaOps.ClearLooks[which: caret];
TiogaOps.Break[];
TiogaOps.SetNotComment[];
TiogaOps.InsertRope["Body"];
TiogaOps.Break[];
TiogaOps.InsertRope["};"];
[] ← TiogaOps.NextPlaceholder[dir: backwards, gotoend: FALSE];
}
ELSE {
TiogaOps.InsertRope[expansion];
TiogaOps.GoToPreviousCharacter[Rope.Length[expansion]];
[] ← TiogaOps.NextPlaceholder[gotoend: FALSE];
};
};
viewer: Viewer ← TiogaOps.GetSelection[].viewer;
expansion, newName: ROPE;
IF viewer = NIL THEN RETURN;
IF Rope.Find[name, "."] = -1 THEN {
should check and if module not loaded, then search file instead of evaluating
n: TiogaRef ← TiogaOps.FirstChild[TiogaOps.ViewerDoc[viewer]];
WHILE n # NIL DO
r: ROPE = TiogaOps.GetRope[n];
IF NOT TiogaOps.IsComment[n] AND (Rope.Find[r, "PROGRAM"] # -1 OR Rope.Find[r, "MONITOR"] # -1) THEN {
name ← Rope.Cat[IO.GetCedarTokenRope[IO.RIS[r]].token, ".", name];
EXIT
};
n ← TiogaOps.Next[n];
ENDLOOP;
};
MessageWindow.Append[message: Rope.Cat["Evaluating ", name, "..."], clearFirst: TRUE];
WindowManager.WaitCursor[];
[expansion, newName] ← ExpandRecord[name, viewer
! UNWIND => WindowManager.UnWaitCursor[]];
WindowManager.UnWaitCursor[];
IF viewer = TiogaOps.GetSelection[].viewer
THEN {
IF TiogaOps.GetCaret[] # caret THEN {
MessageWindow.Append["Caret has moved, insertion NOT performed.", TRUE];
MessageWindow.Blink[];
RETURN;
};
}
ELSE TiogaOps.SetSelection[viewer: viewer, start: caret, end: caret];
user went to some other viewer or exec while waiting.
MessageWindow.Clear[];
IF expansion # NIL THEN
IF useLocks THEN TiogaOps.CallWithLocks[proc: Expand1] ELSE Expand1[NIL];
};
ExpandRecord: PROC [name: ROPE, viewer: Viewer] RETURNS [expansion: ROPE, newName: ROPE] = {
h: STREAM = IO.ROS[];
i: INT;
tv: TV;
errorRope: ROPE;
noResult: BOOL;
type: AMTypes.Type;
[tv, errorRope, noResult] ← Interpreter.Evaluate[rope: name]; -- Abort?
IF noResult THEN {
MessageWindow.Append[errorRope, TRUE];
MessageWindow.Blink[];
GOTO Fail};
type ← AMTypes.TVType[tv];
SELECT AMTypes.UnderClass[type] FROM
type => {
PrintTV.PrintType[type: AMTypes.TVToType[tv], put: h, width: printWidth, verbose: TRUE];
if the tv describes a type, e.g. Commander.CommandProc, don't want the type of that object, which would just be TYPE, but rather the object itself, printed as a type.
expansion ← h.RopeFromROS[];
i ← Rope.Find[expansion, "["];
IF i = -1
THEN expansion ← NIL
ELSE expansion ← Rope.Concat[" -- ", Rope.Substr[base: expansion, start: i]];
RETURN;
};
procedure, signal, error, record => {
Expand1: PROC [args: ROPE] RETURNS [expansion: ROPENIL] = {
left: ROPE = Rope.FromChar['\001];
right: ROPE = Rope.FromChar['\002];
i, length: INT;
i ← 1;
expansion ← args;
length ← Rope.Length[expansion];
DO
i is the first character in this argument. find end of argument
n: INT ← -1; -- will be character position of :
j: INT ← i; -- will be position of the first character beyond this argument, i.e. the , or ]
WHILE j < length DO
SELECT Rope.Fetch[expansion, j] FROM
'', '\\ => j ← j + 2;
'], ', => EXIT;
': => n ← j;
'[ => -- default value for this argument is a record constructor. find matching right ] and replace interior by its expansion
{count: INT ← 1;
k: INT ← j + 1;
r: ROPE;
nChars: INT;
WHILE k < length DO
SELECT Rope.Fetch[expansion, k] FROM
'', '\\ => {k ← k + 2; LOOP};
'[ => count ← count + 1;
'] => IF (count ← count - 1) = 0 THEN EXIT;
ENDCASE;
k ← k + 1;
REPEAT
FINISHED => GOTO Fail; -- did not find matching ]
ENDLOOP;
nChars ← k - j + 1;
r ← Expand1[Rope.Substr[base: expansion, start: j, len: nChars]];
expansion ← Rope.Replace[base: expansion, start: j, len: nChars, with: r];
length ← Rope.Length[expansion];
j ← k + (Rope.Length[r] - nChars);
};
ENDCASE;
j ← j + 1;
ENDLOOP;
IF n = -1 OR j >= length THEN GOTO Fail;
expansion ← Rope.Replace[base: expansion, start: j, len: 0, with: right];
insert right place holder. work from right so positions accurate.
expansion ← Rope.Replace[base: expansion, start: n + 2, len: 0, with: left];
+ 2 to skip over the : and the space.
length ← length + 2;
i ← j + 3;
IF i = length THEN EXIT;
ENDLOOP;
EXITS
Fail => expansion ← args;
};
PrintTV.PrintType[type: type, put: h, width: printWidth, verbose: TRUE];
expansion ← h.RopeFromROS[];
IF (i ← Rope.Find[expansion, "RETURNS"]) # -1 THEN
expansion ← Rope.Substr[base: expansion, len: i - 1];
the minus one is to delete the space before the RETURNS. important in case of typescripts, so that ctrl-next puts you at the end of the typescript.
i ← Rope.Find[expansion, "["];
IF i = -1 THEN {expansion ← "[]"; RETURN};
expansion ← Rope.Substr[expansion, i];
strips off the PROC, ERROR, RECORD, whatever.
expansion ← Expand1[expansion];
};
ENDCASE => GO TO Fail;
EXITS Fail => expansion ← NIL;
};
printWidth: INT ← 250;
Initialization
useLocks: BOOLTRUE;
TiogaOps.RegisterAbbrevFailedProc[Expand];
END.