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-- = { 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 { 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] = { 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 {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. j M3TiogaImpl.mesa Copyright Σ 1991, 1992 by Xerox Corporation. All rights reserved. Spreitze, May 12, 1992 1:01 pm PDT -- scan selection looking for Mesa keywords and comments -- set the keywords look k -- set the comments look c -- set procedure names look n do the entire node a keyword is a token longer than 1 char formed of all capitals and numerics Κ–(cedarcode) style•NewlineDelimiter ™codešœ™Kšœ Οeœ7™BK™"—K˜KšΟk œœ:žœœœ œ œœœœœœ[œ[˜ΥK˜šΟn œžœž˜Kšžœœ:žœœœ œ œœœœœ œXœ%˜——K˜Kšœž˜K˜Kšžœžœžœ˜Kšœžœ˜K˜šœ žœžœ˜K˜—šŸ œžœ!žœžœžœžœžœžœΟcœ˜ŒKšœ8™8Kšœ™Kšœ™Kšœ™Kšœ3žœ˜;Kš œžœžœžœžœ˜š Ÿœžœ žœžœžœ˜4K˜$K˜$Kšžœ žœ$˜5K˜#Kšœ˜—šŸœžœ8˜CK˜5Kšœ6˜6Kšœ3˜3Kšžœ žœžœ@˜WKšžœ žœžœ=˜Sšžœžœžœžœ˜[Kšœ™K˜K˜2—K˜d˜Kšžœ žœžœ˜)Kšžœ3˜7—˜šžœ ž˜Kšžœ˜Kšžœžœ7˜?—Kšœ˜—Kšžœ žœžœ*˜?Kšžœ žœžœ'˜;Kšœ(˜(Kšœ0˜0Kšœ˜—Kšœ#˜#Kšœ#˜#Kšœ!˜!Kšœ#˜#Kšœ"˜"Kšœ#˜#Kšœ"˜"KšœG˜GKšžœžœ˜—K˜šŸœžœ%žœ5žœ˜}K˜4K˜/K˜ŠK˜K˜—K˜Kšœžœžœ5žœ˜VK˜šŸ œžœ;žœžœ5˜š Ÿ œžœ"žœžœžœžœ˜Všžœžœ žœ˜8Kšžœ "œ˜+šžœ˜K˜)KšœC˜CK˜5K˜5K˜>K˜>K˜;K˜8K˜——K˜—Kšœ'˜'K˜—K˜šŸœžœ2žœ žœžœžœžœžœ˜ŒKšœ˜Kšœ3žœ˜;Kšœžœ˜Kšœ žœ ˜Kšœžœžœ˜"šŸœžœžœžœžœžœžœžœ˜—š Ÿœžœžœžœžœžœ˜*Kšœžœ3˜<—š Ÿœžœžœžœžœžœ˜*Kšœžœ3˜<—K˜šŸœžœžœžœ˜2Kšœžœ˜'—K˜šŸœžœ žœ˜ K˜8Kšžœ˜—K˜šŸ œ$˜0Kšœ;žœ˜BKšžœ˜—K˜K˜'K˜%K˜%K˜K˜%K˜!K˜ K˜K˜K˜ K˜ K˜"K˜$K˜K˜!K˜ K˜"K˜"K˜K˜K˜K˜K˜K˜Kšœ1˜1Kšœ>žœ˜EKšœ>žœžœ˜LK˜Kšžœ˜—…—2A