M3TiogaImpl.mesa
Copyright Ó 1991, 1992 by Xerox Corporation. All rights reserved.
Spreitze, May 12, 1992 1:01 pm PDT
DIRECTORY CharOps, Convert, EditSpan, EditSpanSupport, EditToolOps, IO, M3Scan, NodeAddrs, RefText, Rope, RuntimeError, SimpleFeedback, SymTab, TEditDocument, TEditInput, TEditInputOps, TEditLocks, TEditSelection, TextEdit, TextEditBogus, TextLooks, TextNode, Tioga, TiogaOps, TiogaRopes, TiogaStreams, UndoEvent, UserProfile, ViewerClasses;
M3TiogaImpl: CEDAR PROGRAM
IMPORTS CharOps, Convert, EditSpan, EditSpanSupport, EditToolOps, IO, M3Scan, NodeAddrs, RefText, Rope, RuntimeError, SimpleFeedback, SymTab, TEditInput, TEditInputOps, TEditLocks, TEditSelection, TextEdit, TextEditBogus, TextNode, TiogaOps, TiogaRopes, TiogaStreams, UserProfile
= BEGIN
ROPE: TYPE = Rope.ROPE;
Event: TYPE ~ Tioga.Event;
namifyTypes: BOOL ¬ FALSE;
SetM3LooksOp: PROC [viewer: ViewerClasses.Viewer ¬ NIL] RETURNS [recordAtom: BOOL ¬ TRUE, quit: BOOL ¬ FALSE] --TEditInput.CommandProc-- = {
-- scan selection looking for Mesa keywords and comments
-- set the keywords look k
-- set the comments look c
-- set procedure names look n
procs, types, comments, keywords, modules, errors: INT ¬ 0;
msg: REF TEXT ¬ NEW[TEXT[40]];
Append: PROC [value: INT, what: ROPE, dlm: ROPE] ~ {
msg ¬ Convert.AppendInt[msg, value];
msg ¬ RefText.AppendRope[msg, what];
IF value # 1 THEN msg ¬ RefText.AppendRope[msg, "s"];
msg ¬ RefText.AppendRope[msg, dlm];
};
DoSet: PROC [root: TextNode.Ref, tSel: TEditDocument.Selection] = {
span: TextNode.Span ¬ [tSel.start.pos, tSel.end.pos];
firstText: TextNode.RefTextNode ~ tSel.start.pos.node;
lastText: TextNode.RefTextNode ~ tSel.end.pos.node;
IF firstText # NIL THEN NodeAddrs.PutTextAddr[firstText, $Start, tSel.start.pos.where];
IF lastText # NIL THEN NodeAddrs.PutTextAddr[lastText, $End, tSel.end.pos.where+1];
IF tSel.granularity=point OR (tSel.granularity=char AND tSel.start.pos=tSel.end.pos) THEN {
do the entire node
span.start.where ¬ 0;
span.end.where ¬ TextNode.EndPos[span.end.node] };
[procs, types, comments, keywords, modules, errors] ¬ SetSpanM3Looks[span, TEditInput.currentEvent];
tSel.start.pos ¬ [firstText,
IF firstText=NIL THEN TextNode.NodeItself
ELSE NodeAddrs.GetTextAddr[firstText,$Start].location];
tSel.end.pos ¬ [lastText,
IF lastText=NIL
THEN TextNode.NodeItself
ELSE MAX[NodeAddrs.GetTextAddr[lastText, $End].location, 1] - 1
];
IF firstText#NIL THEN NodeAddrs.RemTextAddr[firstText, $Start];
IF lastText#NIL THEN NodeAddrs.RemTextAddr[lastText, $End];
TEditSelection.MakeSelection[new: tSel];
TEditSelection.SetSelLooks[TEditSelection.pSel];
};
TEditInputOps.CallWithLocks[DoSet];
Append[keywords, " keyword", ", "];
Append[modules, " module", ", "];
Append[comments, " comment", ", "];
Append[procs, " proc name", ", "];
Append[types, " other name", ", "];
Append[errors, " lex error", "."];
SimpleFeedback.Append[$M3Tioga, oneLiner, $FYI, Rope.FromRefText[msg]];
RETURN [quit: TRUE]};
SetSpanM3Looks: PROC [span: TextNode.Span, event: Event] RETURNS [procs, types, comments, keywords, modules, errors: INT] = {
root: TextNode.Ref = TextNode.Root[span.start.node];
[] ¬ TEditLocks.Lock[root, "SetSpanMesaLooks"];
[[procs: procs, types: types, comments: comments, keywords: keywords, modules: modules, errors: errors]] ¬ SpanM3Looks[root, span, event];
TEditLocks.Unlock[root];
};
M3LooksResult: TYPE ~ RECORD [procs, types, comments, keywords, modules, errors: INT];
SpanM3Looks: PROC [root: TextNode.Ref, span: TextNode.Span,
event: Event ¬ NIL] RETURNS [spanResult: M3LooksResult ¬ [0, 0, 0, 0, 0, 0]] ~ {
DoM3Looks: PROC [node: TextNode.Ref, start, len: INT] RETURNS [stop: BOOL ¬ FALSE] ~ {
IF node.comment AND start=0 AND len>=TextEdit.Size[node]
THEN { -- skip the entire comment node -- }
ELSE {
root: TextNode.Ref ~ TextNode.Root[node];
textResult: M3LooksResult ~ M3Looks[root, node, start, len, event];
spanResult.procs ¬ spanResult.procs+textResult.procs;
spanResult.types ¬ spanResult.types+textResult.types;
spanResult.comments ¬ spanResult.comments+textResult.comments;
spanResult.keywords ¬ spanResult.keywords+textResult.keywords;
spanResult.modules ¬ spanResult.modules+textResult.modules;
spanResult.errors ¬ spanResult.errors+textResult.errors;
};
};
EditSpanSupport.Apply[span, DoM3Looks];
};
M3Looks: PROC [root: TextNode.Ref, text: TextNode.Ref,
start: INT ¬ 0, len: INT ¬ INT.LAST, event: Event ¬ NIL] RETURNS [M3LooksResult] ~ {
node: TextNode.Ref ~ text;
procs, types, comments, keywords, modules, errors: INT ¬ 0;
pStart, pLen: INT ¬ 0;
lastChar: CHAR ¬ '\000;
token: REF TEXT ¬ RefText.New[40];
Match: PROC [a: REF READONLY TEXT] RETURNS [BOOL] ~ INLINE {
RETURN [a.length=token.length AND RefText.Equal[a, token]]
};
tokenKind: M3Scan.TokenKind ¬ tokenERROR;
CheckID: PROC RETURNS [alpha, keyword: BOOL ¬ TRUE] = {
a keyword is a token longer than 1 char formed of all capitals and numerics
IF token = NIL THEN RETURN [FALSE, FALSE];
IF token.length = 1 THEN keyword ¬ FALSE;
FOR index: NAT IN [0..token.length) DO
lastChar ¬ token[index];
IF NOT lastChar IN ['A..'Z] AND NOT lastChar IN ['0..'9] THEN keyword ¬ FALSE;
IF NOT (CharOps.AlphaNumeric[lastChar] OR lastChar='←) THEN alpha ¬ FALSE;
ENDLOOP;
};
allComments: BOOL ¬ TRUE;
mesaLooks: TextLooks.Looks ¬ TextLooks.noLooks;
stream: IO.STREAM ¬ NIL;
size: INT ~ Rope.Size[node.rope];
first: INT ~ MIN[start, size];
length: INT ~ MIN[len, size-start];
end: INT ~ start+length;
state: {null, procwd, modwd, typwd} ¬ null;
stream ¬ IO.RIS[node.rope.Substr[0, end]];
IO.SetIndex[stream, first];
mesaLooks['k] ¬ mesaLooks['c] ¬ mesaLooks['n] ¬ mesaLooks['l] ¬ mesaLooks['w] ¬ TRUE;
IF end > first THEN EditSpan.ChangeLooks[root, [[node, first], [node, end-1]], mesaLooks, Tioga.noLooks, event];
UNTIL tokenKind = tokenEOF DO
add, alpha, keyword: BOOL ¬ FALSE;
addLooks, remLooks: TextLooks.Looks ¬ TextLooks.noLooks;
tokStart, tokLen: INT ¬ 0;
[tokenKind, token] ¬ M3Scan.GetM3Token[stream, token, FALSE !
RuntimeError.BoundsFault => { tokenKind ¬ tokenERROR; token.length ¬ 0; CONTINUE }
];
tokLen ¬ token.length;
tokStart ¬ IO.GetIndex[stream]-tokLen;
remLooks ¬ mesaLooks;
SELECT tokenKind FROM
tokenEOF => NULL;
tokenCOMMENT => {
add ¬ TRUE;
addLooks['c] ¬ TRUE;
remLooks['c] ¬ FALSE;
comments ¬ comments+1;
};
tokenERROR => {
add ¬ TRUE;
addLooks['w] ¬ TRUE;
remLooks['w] ¬ FALSE;
errors ¬ errors+1;
};
tokenID => {
allComments ¬ FALSE;
[alpha, keyword] ¬ CheckID[];
SELECT TRUE FROM
keyword AND state=null => {
add ¬ TRUE;
addLooks['k] ¬ TRUE;
remLooks['k] ¬ FALSE;
keywords ¬ keywords+1;
IF Match["PROCEDURE"] THEN state ¬ procwd
ELSE IF Match["MODULE"] OR Match["INTERFACE"] OR Match["SERVICE"] THEN state ¬ modwd
ELSE IF Match["CONST"] OR Match["TYPE"] OR Match["EXCEPTION"] OR Match["VAR"] OR Match["REVEAL"] THEN state ¬ typwd
ELSE state ¬ null;
};
alpha OR keyword => SELECT state FROM
procwd => {
add ¬ TRUE;
addLooks['n] ¬ TRUE;
remLooks['n] ¬ FALSE;
procs ¬ procs+1;
state ¬ null};
typwd => IF namifyTypes THEN {
add ¬ TRUE;
addLooks['n] ¬ TRUE;
remLooks['n] ¬ FALSE;
types ¬ types+1;
state ¬ null};
modwd => {
add ¬ TRUE;
addLooks['n] ¬ addLooks['l] ¬ TRUE;
remLooks['n] ¬ remLooks['l] ¬ FALSE;
modules ¬ modules+1;
state ¬ null};
ENDCASE => NULL;
ENDCASE;
};
tokenSINGLE => {
allComments ¬ FALSE;
state ¬ null;
};
ENDCASE => {allComments ¬ FALSE; state ¬ null};
IF add THEN {
EditSpan.ChangeLooks[root, [[node,tokStart], [node,tokStart+tokLen-1]], remLooks, addLooks, event];
add ¬ FALSE;
addLooks ¬ TextLooks.noLooks;
remLooks ¬ mesaLooks;
};
ENDLOOP;
RETURN[[procs: procs, types: types, comments: comments, keywords: keywords, modules: modules, errors: errors]];
};
ExpandM3Sel: PROC [viewer: ViewerClasses.Viewer ¬ NIL] RETURNS [recordAtom: BOOL ¬ TRUE, quit: BOOL ¬ FALSE] --TEditInput.CommandProc-- ~ {
opsRoot: TiogaOps.Ref;
root, firstChild: TextNode.Ref;
sv: ViewerClasses.Viewer;
opsSelStart, opsSelEnd: TiogaOps.Location;
selStart, selEnd: TextNode.Location;
selLevel: TiogaOps.SelectionGrain;
selBefore: BOOL;
urPos, bPos, fPos, skipped, depth, preDepth, startPos, endPos: INT ¬ 0;
docRope: ROPE;
fwdStream, bwdStream: IO.STREAM ¬ NIL;
buffer, toke: REF TEXT;
tokind: M3Scan.TokenKind;
tokerr: M3Scan.TokenError;
start, end: TextNode.Location;
firstComp: BOOL ¬ TRUE;
IF viewer = NIL THEN RETURN [FALSE, FALSE];
opsRoot ¬ TiogaOps.ViewerDoc[viewer];
IF opsRoot = NIL THEN RETURN [FALSE, FALSE];
root ¬ opsRoot;
firstChild ¬ TextNode.FirstChild[root];
quit ¬ TRUE;
toke ¬ buffer ¬ RefText.ObtainScratch[256];
{
[sv, opsSelStart, opsSelEnd, selLevel, selBefore,] ¬ TiogaOps.GetSelection[];
IF sv#viewer THEN {Bitch["selection moved already"]; GOTO Done};
selStart ¬ opsSelStart;
selEnd ¬ opsSelEnd;
IF selStart.where = TextNode.NodeItself THEN selStart.where ¬ 0;
IF selEnd.where = TextNode.NodeItself THEN selEnd.where ¬ NodeRope[selEnd.node].Length[];
urPos ¬ TextNode.LocOffset[[firstChild, 0], IF selBefore THEN selStart ELSE selEnd];
SELECT selLevel FROM
char, word => IF NOT selBefore THEN urPos ¬ urPos+1;
point, node, branch => NULL;
ENDCASE => ERROR;
bPos ¬ urPos;
docRope ¬ TiogaRopes.RopeFromTioga[node: firstChild];
fwdStream ¬ TiogaStreams.CreateInput[from: root, commentHandling: [TRUE[NIL, NIL]] ];
bwdStream ¬ TiogaStreams.CreateInput[from: root, commentHandling: [TRUE[NIL, NIL]] ];
IF TiogaStreams.CurInLoc[fwdStream] # [firstChild, 0]
THEN {Bitch["No workee"]; GOTO Done};
DO -- depth <= 0
sep: BOOL ¬ FALSE;
Finish: PROC ~ {
IF sep THEN fwdStream.SetIndex[endPos];
end ¬ TiogaStreams.CurInLoc[fwdStream];
end ¬ PrevLoc[end];
TiogaOps.SetSelection[
viewer: viewer,
start: OpacifyLoc[start],
end: OpacifyLoc[end],
level: word,
caretBefore: TRUE];
RETURN};
DO
bPos ¬ M3Scan.ScanBack[docRope, bPos];
bwdStream.SetIndex[bPos];
[tokind, toke, skipped, tokerr] ¬ M3Scan.GetM3Token[bwdStream, toke, TRUE];
IF bPos+skipped<urPos THEN SELECT TRUE FROM
IsSeper[toke] => {sep ¬ TRUE; IF depth=0 THEN EXIT};
tokind=tokenID AND IsStarter[toke] => {
IF (depth ¬ depth+1) >= 0 THEN EXIT};
ENDCASE => NULL;
IF bPos=0 THEN {Bitch["No enclosing compound"]; GOTO Done};
ENDLOOP;
bwdStream.SetIndex[startPos ¬ bPos+skipped];
start ¬ TiogaStreams.CurInLoc[bwdStream];
IF firstComp THEN {
fwdStream.SetIndex[fPos ¬ startPos + toke.length];
preDepth ¬ depth};
IF debug THEN SimpleFeedback.PutFL[$M3Pretty, oneLiner, $FYI, "Trying compound beginning at %g with %g.", LIST[ [integer[startPos]], [rope[Rope.FromRefText[toke]]] ]];
IF depth=0 AND (preDepth=0 OR NOT sep) AND (IF sep THEN endPos ELSE fPos) > urPos THEN {Finish[]; GOTO Done};
DO
try: BOOL ¬ FALSE;
preDepth ¬ depth;
endPos ¬ fPos;
[tokind, toke, skipped, tokerr] ¬ M3Scan.GetM3Token[fwdStream, toke, TRUE];
fPos ¬ fPos + skipped + toke.length;
IF tokind=tokenEOF THEN {Bitch["EOF too soon"]; GOTO Done}
ELSE SELECT TRUE FROM
tokind=tokenID AND IsStarter[toke] => {try ¬ TRUE; depth ¬ depth+1};
tokind=tokenID AND IsEnder[toke] => {try ¬ TRUE; depth ¬ depth-1};
IsSeper[toke] => try ¬ TRUE;
ENDCASE => NULL;
IF depth>0 OR (sep AND preDepth>0) OR NOT try THEN NULL
ELSE IF (IF sep THEN endPos ELSE fPos) > urPos
THEN {Finish[]; GOTO Done}
ELSE EXIT;
ENDLOOP;
ENDLOOP;
EXITS Done => NULL};
RefText.ReleaseScratch[buffer];
IF fwdStream#NIL THEN fwdStream.Close[];
IF bwdStream#NIL THEN bwdStream.Close[];
RETURN};
debug: BOOL ¬ FALSE;
PrevLoc: PROC [loc: TextNode.Location] RETURNS [prev: TextNode.Location] = {
IF loc.where > 0 THEN RETURN [[loc.node, loc.where-1]];
prev ¬ [TextNode.StepBackward[loc.node], 0];
prev.where ¬ NodeRope[prev.node].Length[];
RETURN};
OpacifyLoc: PROC [l: TextNode.Location] RETURNS [TiogaOps.Location]
~ {RETURN [l]};
IsStarter: PROC [t: REF TEXT] RETURNS [BOOL]
~ {RETURN [starters.Fetch[RefText.TrustTextAsRope[t]].found]};
IsSeper: PROC [t: REF TEXT] RETURNS [BOOL]
~ {RETURN [sepers.Fetch[RefText.TrustTextAsRope[t]].found]};
IsEnder: PROC [t: REF TEXT] RETURNS [BOOL]
~ {RETURN [enders.Fetch[RefText.TrustTextAsRope[t]].found]};
NodeRope: PROC [node: TextNode.Ref] RETURNS [ROPE]
~ {RETURN TextEditBogus.GetRope[node]};
Bitch: PROC [msg: Rope.ROPE] ~ {
SimpleFeedback.Append[$M3Pretty, oneLiner, $Error, msg];
RETURN};
TrackProfile: UserProfile.ProfileChangedProc ~ {
namifyTypes ¬ UserProfile.Boolean["M3Pretty.SimpleSeries", FALSE];
RETURN};
starters: SymTab.Ref ¬ SymTab.Create[];
sepers: SymTab.Ref ¬ SymTab.Create[];
enders: SymTab.Ref ¬ SymTab.Create[];
[] ¬ starters.Store["INTERFACE", $T];
[] ¬ starters.Store["BEGIN", $T];
[] ¬ starters.Store["CASE", $T];
[] ¬ starters.Store["FOR", $T];
[] ¬ starters.Store["IF", $T];
[] ¬ starters.Store["LOCK", $T];
[] ¬ starters.Store["LOOP", $T];
[] ¬ starters.Store["REPEAT", $T];
[] ¬ starters.Store["TYPECASE", $T];
[] ¬ starters.Store["TRY", $T];
[] ¬ starters.Store["WHILE", $T];
[] ¬ starters.Store["WITH", $T];
[] ¬ starters.Store["OBJECT", $T];
[] ¬ starters.Store["RECORD", $T];
[] ¬ sepers.Store["ELSIF", $T];
[] ¬ sepers.Store["ELSE", $T];
[] ¬ sepers.Store["|", $T];
[] ¬ enders.Store["END", $T];
[] ¬ enders.Store["UNTIL", $T];
UserProfile.CallWhenProfileChanges[TrackProfile];
EditToolOps.RegisterCommandButton["SetM3Looks", SetM3LooksOp, FALSE];
EditToolOps.RegisterCommandButton["ExpandM3Sel", ExpandM3Sel, FALSE, FALSE];
END.