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.