DIRECTORY Atom, Basics, DefDWIM, DocCache, FileDWIM, FS, HashTable, IO, JaMAndStyleAnalyses, JaMScanner, Menus, MesaAnalyses, MessageWindow, RegularExpression, Rope, RopeHash, RopeList, RopeReader, TDLexing, TEditDocument, TEditDocumentPrivate, TEditInput, TEditInputExtras, TEditOps, TEditProfile, TEditRefresh, TEditSelection, TEditSelectionOps, TextFind, TextNode, TiogaOps, TiogaRopes, TiogaStreams, TreeFind, UserProfile, ViewerClasses, ViewerForkers, VM; DefDWIMImpl: CEDAR MONITOR IMPORTS Atom, DocCache, FileDWIM, FS, HashTable, IO, JaMAndStyleAnalyses, JaMScanner, MesaAnalyses, MessageWindow, RegularExpression, Rope, RopeHash, RopeList, RopeReader, TDLexing, TEditDocumentPrivate, TEditInput, TEditInputExtras, TEditOps, TEditProfile, TEditRefresh, TEditSelection, TextFind, TextNode, TiogaOps, TiogaRopes, TiogaStreams, TreeFind, UserProfile, ViewerForkers, VM EXPORTS DefDWIM SHARES Menus --in order to change existing Def button, which doesn't decode control = BEGIN OPEN TDLexing, DefDWIM; LORA: TYPE = LIST OF REF ANY; ROPEList: TYPE = LIST OF ROPE; RefTextNode: TYPE = TextNode.RefTextNode; MesaAnalysis: TYPE = MesaAnalyses.MesaAnalysis; Viewer: TYPE = ViewerClasses.Viewer; RegistrationList: TYPE = LIST OF Registration; Registration: TYPE = RECORD [Test: LanguageTest, Find: Finder]; Failure: PUBLIC ERROR [where: Place, why: ROPE] = CODE; registry: RegistrationList _ NIL; FindDef: PUBLIC PROC [subjectRope: ROPE, from: Place, deep, verbose: BOOL, interrupt: REF BOOL _ NIL] RETURNS [fp: FoundPlace] --Finder-- = { FOR rl: RegistrationList _ registry, rl.rest WHILE rl # NIL DO IF rl.first.Test[from] THEN RETURN [rl.first.Find[subjectRope, from, deep, verbose, interrupt]]; ENDLOOP; RETURN [DefaultFinder[subjectRope, from, deep, verbose, interrupt]]; }; Register: PUBLIC PROC [languageTest: LanguageTest, finder: Finder] = { registry _ CONS[[languageTest, finder], registry]; }; DefaultFinder: PROC [subjectRope: ROPE, from: Place, deep, verbose: BOOL, interrupt: REF BOOL _ NIL] RETURNS [fp: FoundPlace] --Finder-- = { quick: TextFind.Finder _ TextFind.CreateFromRope[pattern: Rope.Cat[subjectRope, ":"], literal: TRUE, word: TRUE]; textRoot: RefTextNode = TextNode.Root[from.loc.node]; start: INT = 0; startLoc: Location = TextNode.LocRelative[[textRoot, 0], start]; before, after: INT; foundLoc, nameEnd: Location; foundNode: TextNode.Ref _ NIL; TryFwd: PROC [fromLoc: Location] RETURNS [found: BOOL] = { [found: found, where: foundNode, before: before, after: after] _ TreeFind.Try[finder: quick, first: fromLoc.node, start: fromLoc.where, interrupt: interrupt]; }; TryBkwd: PROC RETURNS [found: BOOL] = { [found: found, where: foundNode, before: before, after: after] _ TreeFind.TryBackwards[finder: quick, first: from.loc.node, len: from.loc.where, last: startLoc.node, lastStart: startLoc.where, interrupt: interrupt]; }; found: BOOL _ SELECT from.searchOrder FROM fromStart => TryFwd[[textRoot, 0]], fwdThenBkwd => TryFwd[from.loc] OR TryBkwd[], bkwdThenFwd => TryBkwd[] OR TryFwd[from.loc], ENDCASE => ERROR; IF interrupt#NIL AND interrupt^ THEN Failure[from, Rope.Concat["interrupted search for ", subjectRope]]; IF found THEN { foundLoc _ TextNode.LocRelative[[foundNode, 0], before]; nameEnd _ TextNode.LocRelative[foundLoc, after-2 - before]; after _ TextNode.LocOffset[[textRoot, 0], [foundNode, after]]; fp _ [[from.fileName, foundLoc, fwdThenBkwd], nameEnd]; } ELSE Failure[from, Rope.Cat["Couldn't find ", subjectRope]]; }; RopeSeq: TYPE = REF RopeSequence; RopeSequence: TYPE = RECORD [elts: SEQUENCE length: NAT OF ROPE]; msgRing: RopeSeq _ CreateRopeSeq[100]; msgRingInsert: NAT _ 0; SetRingSize: ENTRY PROC [size: NAT] = { ENABLE UNWIND => NULL; msgRing _ CreateRopeSeq[size]; msgRingInsert _ 0; }; Msg: ENTRY PROC [clearFirst: BOOL, r1, r2, r3, r4, r5: ROPE _ NIL] = { ENABLE UNWIND => msgRingInsert _ 0; msg: ROPE = Rope.Cat[r1, r2, r3, r4, r5]; MessageWindow.Append[msg, clearFirst]; IF clearFirst THEN { msgRingInsert _ msgRingInsert + 1; IF msgRingInsert = msgRing.length THEN msgRingInsert _ 0; msgRing[msgRingInsert] _ msg; } ELSE msgRing[msgRingInsert] _ msgRing[msgRingInsert].Concat[msg]; }; CreateRopeSeq: PROC [length: NAT] RETURNS [rs: RopeSeq] = { rs _ NEW [RopeSequence[length]]}; GetMsgs: ENTRY PROC [maxLen: NAT _ 32] RETURNS [history: ROPEList] = { ENABLE UNWIND => NULL; len: NAT = MIN[msgRing.length, maxLen]; i: NAT _ msgRingInsert; history _ NIL; THROUGH [1 .. len] DO history _ CONS[msgRing[i], history]; i _ (i+msgRing.length-1) MOD msgRing.length; ENDLOOP; history _ history; }; RecognizeJS: PROC [place: Place] RETURNS [is: BOOL] --LanguageTest-- = { is _ WhatJSType[place] # Other; }; JSType: TYPE = {JaM, Style, Other}; WhatJSType: PROC [place: Place] RETURNS [type: JSType] = { cp: FS.ComponentPositions; fullFName, ext: ROPE; [fullFName, cp, ] _ FS.ExpandName[place.fileName]; ext _ fullFName.Substr[cp.ext.start, cp.ext.length]; type _ SELECT TRUE FROM ext.Equal["JaM", FALSE] => JaM, ext.Equal["Style", FALSE] => Style, ENDCASE => Other; }; JSFinder: PROC [subjectRope: ROPE, from: Place, deep, verbose: BOOL, interrupt: REF BOOL _ NIL] RETURNS [fp: FoundPlace] --Finder-- = { type: JSType = WhatJSType[from]; IF deep THEN subjectRope _ Rope.Cat["look.", subjectRope]; fp _ FindJSDef[subjectRope, from, verbose, NIL, MakeStack[], interrupt]; }; FindJSDef: PROC [subjectRope: ROPE, from: Place, verbose: BOOL, searchedOpens: HashTable.Table--file name _ $T--, stack: HashTable.Table--Frame _ $T--, interrupt: REF BOOL _ NIL] RETURNS [fp: FoundPlace] --Finder-- = { frame: Frame = NEW [FramePrivate _ [subjectRope, from]]; IF stack.Fetch[frame].found THEN { Failure[from, Rope.Concat["Recursion detected for ", subjectRope]]; }; IF NOT stack.Insert[frame, $T] THEN ERROR; IF verbose THEN Msg[TRUE, "Looking for ", subjectRope, " in ", from.fileName]; {ENABLE UNWIND => [] _ stack.Delete[frame]; quick: RegularExpression.Finder = RegularExpression.CreateFromRope[pattern: Rope.Cat["(", subjectRope, ")"], literal: TRUE, word: TRUE]; textRoot: RefTextNode = TextNode.Root[from.loc.node]; anal: JaMAndStyleAnalyses.JSAnalysis = JaMAndStyleAnalyses.GetAnalysis[textRoot, from.fileName, verbose]; start: INT = IF anal # NIL THEN anal.bodyStart ELSE 0; startLoc: Location = TextNode.LocRelative[[textRoot, 0], start]; here: INT = TextNode.LocOffset[[textRoot, 0], from.loc]; before, after: INT; foundLoc, nameEnd: Location; TryFwd: PROC [searchFrom: INT] RETURNS [found: BOOL] = { searchRope: ROPE = MF[TiogaRopes.RopeFromTioga[textRoot]]; found _ FALSE; WHILE NOT found DO [found: found, before: before, after: after] _ RegularExpression.SearchRope[finder: quick, rope: searchRope, start: searchFrom, interrupt: interrupt]; IF NOT found THEN EXIT; IF NOT JaMOKAround[searchRope, subjectRope, before, after] THEN {searchFrom _ after; found _ FALSE}; ENDLOOP; }; TryBkwd: PROC RETURNS [found: BOOL] = { searchRope: ROPE = MF[TiogaRopes.RopeFromTioga[textRoot]]; searchLength: INT _ here - start; found _ FALSE; WHILE NOT found DO [found: found, before: before, after: after] _ RegularExpression.SearchRopeBackwards[finder: quick, rope: searchRope, start: start, len: searchLength, interrupt: interrupt]; IF NOT found THEN EXIT; IF NOT JaMOKAround[searchRope, subjectRope, before, after] THEN {searchLength _ before - start; found _ FALSE}; ENDLOOP; }; found: BOOL _ SELECT from.searchOrder FROM fromStart => TryFwd[0], fwdThenBkwd => TryFwd[here] OR TryBkwd[], bkwdThenFwd => TryBkwd[] OR TryFwd[here], ENDCASE => ERROR; IF interrupt#NIL AND interrupt^ THEN Failure[from, Rope.Concat["interrupted search for ", subjectRope]]; IF found THEN { foundLoc _ TextNode.LocRelative[[textRoot, 0], before+1]; nameEnd _ TextNode.LocRelative[foundLoc, after-2 - (before+1)]; after _ TextNode.LocOffset[[textRoot, 0], nameEnd]+1; fp _ [[from.fileName, foundLoc, fwdThenBkwd], nameEnd]; } ELSE { Try: PROC [files: ROPEList, ext: ROPE] RETURNS [found: BOOL] = { FOR files _ files, files.rest WHILE files # NIL DO fileName: ROPE = files.first.Concat[ext]; IF interrupt#NIL AND interrupt^ THEN Failure[from, Rope.Concat["interrupted search for ", subjectRope]]; IF searchedOpens = NIL THEN searchedOpens _ HashTable.Create[equal: HashTable.RopeEqual, hash: HashTable.HashRope]; found _ TRUE; IF searchedOpens.Fetch[fileName].found THEN found _ FALSE ELSE { IF NOT searchedOpens.Insert[fileName, $T] THEN ERROR; fp _ FindJSDef[subjectRope, JSModuleBody[from, fileName, verbose, stack, interrupt].where, verbose, searchedOpens, stack, interrupt !Failure => {found _ FALSE; CONTINUE}]; }; IF found THEN RETURN; ENDLOOP; found _ FALSE; }; IF anal # NIL AND (Try[anal.runs, ".JaM"] OR Try[anal.attachs, ".Style"]) THEN NULL ELSE Failure[from, Rope.Cat["Couldn't find ", subjectRope]]; }; }; IF NOT stack.Delete[frame] THEN ERROR; }; JSModuleBody: PROC [from: Place, filename: ROPE, verbose: BOOL, stack: HashTable.Table--Frame _ $T--, interrupt: REF BOOL] RETURNS [fp: FoundPlace] = { ans: FileDWIM.Answer = FileDWIM.ResolveHint[filename, from.fileName, FALSE]; IF ans.fullFileName = NIL THEN ERROR Failure[from, Rope.Cat["couldn't find file ", filename]]; {next: Place = [ans.fullFileName, [DocCache.GetDoc[ans.fullFileName], 0], fromStart]; fp _ [next, next.loc]; }}; JaMOKAround: PROC [searchRope, target: ROPE, before, after: INT] RETURNS [ok: BOOL] = { Token: TYPE = JaMScanner.Token; reader: RopeReader.Ref = RopeReader.Create[]; Match: PROC [left: {leftSquare, leftCurly}] RETURNS [ok: BOOL] = { DO token: Token = JaMScanner.GetToken[reader]; SELECT token.type FROM nil => RETURN [FALSE--because EOF--]; name => { a: ATOM = NARROW[JaMScanner.ParseToken[token, searchRope]]; SELECT a FROM leftSquare => IF NOT Match[leftSquare] THEN RETURN [FALSE]; rightSquare => RETURN [left = leftSquare]; ENDCASE => NULL; }; string, int, real => NULL; lbrace => IF NOT Match[leftCurly] THEN RETURN [FALSE]; rbrace => RETURN [left = leftCurly]; comment => NULL; ENDCASE => ERROR; ENDLOOP; }; stack: LORA _ LIST[target]; IF after >= searchRope.Length[] THEN RETURN [FALSE]; reader.SetPosition[searchRope, after]; DO token: Token = JaMScanner.GetToken[reader]; SELECT token.type FROM nil => RETURN [FALSE--because EOF--]; name => { a: ATOM = NARROW[JaMScanner.ParseToken[token, searchRope]]; SELECT a FROM dotCvx => NULL; dotDef => RETURN [stack # NIL AND stack.rest # NIL AND stack.rest.rest = NIL]; $StyleRule, $ScreenRule, $PrintRule, slashDef => RETURN [stack # NIL AND stack.rest # NIL AND stack.rest.rest # NIL AND stack.rest.rest.rest = NIL]; slashXdef => RETURN [stack # NIL AND stack.rest # NIL AND stack.rest.rest # NIL AND stack.rest.rest.rest = NIL]; leftSquare => IF Match[leftSquare] THEN stack _ CONS[$square, stack] ELSE RETURN [FALSE]; ENDCASE => RETURN [FALSE]; }; string, int, real => stack _ CONS[JaMScanner.ParseToken[token, searchRope], stack]; lbrace => IF Match[leftCurly] THEN stack _ CONS[$curly, stack] ELSE RETURN [FALSE]; rbrace => RETURN [FALSE]; comment => NULL; ENDCASE => ERROR; ENDLOOP; }; dotDef: ATOM = Atom.MakeAtom[".def"]; dotCvx: ATOM = Atom.MakeAtom[".cvx"]; slashDef: ATOM = Atom.MakeAtom["/def"]; slashXdef: ATOM = Atom.MakeAtom["/xdef"]; leftSquare: ATOM = Atom.MakeAtom["["]; rightSquare: ATOM = Atom.MakeAtom["]"]; RecognizeLisp: PROC [place: Place] RETURNS [is: BOOL] = { textRoot: RefTextNode = TextNode.Root[place.loc.node]; asRope: ROPE = MF[TiogaRopes.RopeFromTioga[node: textRoot, skipCommentNode: TRUE]]; length: INT = asRope.Length[]; in: IO.STREAM = IO.RIS[asRope]; [] _ in.SkipWhitespace[]; IF in.EndOf[] THEN RETURN [FALSE]; {firstNonwhite: CHAR = in.GetChar[]; in.Close[]; IF firstNonwhite # '( THEN RETURN [FALSE]; FOR i: INT _ length-1, i-1 WHILE i > 0 DO SELECT asRope.Fetch[i] FROM IN ['\000 .. IO.SP] => NULL; ') => RETURN [TRUE]; ENDCASE => RETURN [FALSE]; ENDLOOP; RETURN [FALSE]; }}; LispFinder: PROC [subjectRope: ROPE, from: Place, deep, verbose: BOOL, interrupt: REF BOOL _ NIL] RETURNS [fp: FoundPlace] --Finder-- = { quick: TextFind.Finder _ TextFind.CreateFromRope[pattern: Rope.Cat["(defun ", subjectRope], literal: TRUE, word: TRUE]; textRoot: RefTextNode = TextNode.Root[from.loc.node]; start: INT = 0; startLoc: Location = TextNode.LocRelative[[textRoot, 0], start]; before, after: INT; foundLoc, nameEnd: Location; foundNode: TextNode.Ref _ NIL; TryFwd: PROC [fromLoc: Location] RETURNS [found: BOOL] = { [found: found, where: foundNode, before: before, after: after] _ TreeFind.Try[finder: quick, first: from.loc.node, start: from.loc.where, interrupt: interrupt]; }; TryBkwd: PROC RETURNS [found: BOOL] = { [found: found, where: foundNode, before: before, after: after] _ TreeFind.TryBackwards[finder: quick, first: from.loc.node, len: from.loc.where, last: startLoc.node, lastStart: startLoc.where, interrupt: interrupt]; }; found: BOOL _ SELECT from.searchOrder FROM fromStart => TryFwd[startLoc], fwdThenBkwd => TryFwd[from.loc] OR TryBkwd[], bkwdThenFwd => TryBkwd[] OR TryFwd[from.loc], ENDCASE => ERROR; IF interrupt#NIL AND interrupt^ THEN Failure[from, Rope.Concat["interrupted search for ", subjectRope]]; IF found THEN { foundLoc _ TextNode.LocRelative[[foundNode, 0], before+7]; nameEnd _ TextNode.LocRelative[foundLoc, after-1 - (before+7)]; after _ TextNode.LocOffset[[textRoot, 0], [foundNode, after]]; fp _ [[from.fileName, foundLoc, fwdThenBkwd], nameEnd]; } ELSE Failure[from, Rope.Cat["Couldn't find ", subjectRope]]; }; CedarTree: TYPE = REF ANY --actually UNION [FieldSelection, ROPE]--; FieldSelection: TYPE = REF FieldSelectionPrivate; FieldSelectionPrivate: TYPE = RECORD [ record: CedarTree, field: ROPE ]; Frame: TYPE = REF FramePrivate; FramePrivate: TYPE = RECORD [tree: CedarTree, from: Place]; guessDIRECTORY: BOOL _ TRUE; RecognizeCedar: PROC [place: Place] RETURNS [isCedar: BOOL] --LanguageTest-- = { cp: FS.ComponentPositions; fullFName, ext: ROPE; [fullFName, cp, ] _ FS.ExpandName[place.fileName]; ext _ fullFName.Substr[cp.ext.start, cp.ext.length]; isCedar _ ext.Equal["mesa", FALSE] OR ext.Equal["cedar", FALSE]; }; CedarFinder: PROC [subjectRope: ROPE, from: Place, deep, verbose: BOOL, interrupt: REF BOOL _ NIL] RETURNS [fp: FoundPlace] --Finder-- = { in: IO.STREAM = IO.RIS[subjectRope]; p: Lexer = MakeLexer[in]; t1: Token = GetToken[p]; IF t1.kind # tokenID THEN Failure[noplace, "selection not of form ID(\".\"ID)*"]; {tree: CedarTree = GetTree[p, t1]; in.Close[]; IF p.tokenStack = NIL OR p.tokenStack.rest # NIL OR p.tokenStack.first.kind # tokenEOF THEN Failure[noplace, "selection not of form ID(\".\"ID)*"]; fp _ FindCedarDef[tree, from, deep, verbose, TRUE, NIL, MakeStack[], interrupt]; }}; FindCedarDef: PROC [tree: CedarTree, from: Place, deep, verbose, leftmost: BOOL, searchedOpens: HashTable.Table--module name _ $T--, stack: HashTable.Table--Frame _ $T--, interrupt: REF BOOL _ NIL] RETURNS [fp: FoundPlace] = { frame: Frame = NEW [FramePrivate _ [tree, from]]; IF stack.Fetch[frame].found THEN { Failure[from, Rope.Concat["Recursion detected for ", FmtTree[tree]]]; }; IF NOT stack.Insert[frame, $T] THEN ERROR; {ENABLE UNWIND => [] _ stack.Delete[frame]; WITH tree SELECT FROM fs: FieldSelection => { next: Place = FindCedarDef[fs.record, from, TRUE, verbose, leftmost, searchedOpens, stack, interrupt].where; fp _ FindCedarDef[fs.field, next, deep, verbose, FALSE, NIL, stack, interrupt]; }; target: ROPE => { quick: TextFind.Finder _ TextFind.CreateFromRope[pattern: Rope.Cat[target, ":"], literal: TRUE, word: TRUE]; textRoot: RefTextNode = TextNode.Root[from.loc.node]; ma: MesaAnalysis = MesaAnalyses.GetAnalysis[textRoot, from.fileName, verbose]; start: INT = IF ma # NIL THEN ma.bodyStart ELSE 0; startLoc: Location = TextNode.LocRelative[[textRoot, 0], start]; before, after: INT; found: BOOL; foundLoc, nameEnd: Location; IF verbose THEN Msg[TRUE, "Looking for ", target, " in ", from.fileName]; {foundNode: TextNode.Ref _ NIL; TryFwd: PROC [fromLoc: Location] RETURNS [found: BOOL] = { [found: found, where: foundNode, before: before, after: after] _ TreeFind.Try[finder: quick, first: from.loc.node, start: from.loc.where, interrupt: interrupt]; }; TryBkwd: PROC RETURNS [found: BOOL] = { [found: found, where: foundNode, before: before, after: after] _ TreeFind.TryBackwards[finder: quick, first: from.loc.node, len: from.loc.where, last: startLoc.node, lastStart: startLoc.where, interrupt: interrupt]; }; found _ SELECT from.searchOrder FROM fromStart => TryFwd[[textRoot, 0]], fwdThenBkwd => TryFwd[from.loc] OR TryBkwd[], bkwdThenFwd => TryBkwd[] OR TryFwd[from.loc], ENDCASE => ERROR; IF interrupt#NIL AND interrupt^ THEN Failure[from, Rope.Concat["interrupted search for ", target]]; IF verbose THEN Msg[FALSE, " ."]; IF found THEN { foundLoc _ TextNode.LocRelative[[foundNode, 0], before]; nameEnd _ TextNode.LocRelative[foundLoc, after-2 - before]; after _ TextNode.LocOffset[[textRoot, 0], [foundNode, after]]; } }; IF (NOT found) AND ma # NIL THEN { def: REF ANY = ma.globalDefs.Fetch[target].value; IF def # NIL THEN { fp _ WITH def SELECT FROM it: MesaAnalyses.InterfaceType => CedarModuleBody[from, it, verbose, stack, interrupt], ir: MesaAnalyses.InterfaceRecord => IF deep THEN CedarModuleBody[from, ir.type, verbose, stack, interrupt] ELSE [[from.fileName, TextNode.LocRelative[[textRoot, 0], ir.namePos.before], fwdThenBkwd], TextNode.LocRelative[[textRoot, 0], ir.namePos.after-1]], ie: MesaAnalyses.InterfaceElement => FindCedarDef[ie.name, CedarModuleBody[from, ie.interface.type, verbose, stack, interrupt].where, deep, verbose, FALSE, searchedOpens, stack, interrupt], ENDCASE => ERROR; GOTO Dun}; }; IF NOT found THEN { here: INT = TextNode.LocOffset[[textRoot, 0], from.loc]; targLen: INT = target.Length[]; slow: RegularExpression.Finder = RegularExpression.CreateFromRope[target.Concat["[ \n\t]**(',[ \n\t]**[a..zA..Z][a..zA..Z0..9]**[ \n\t]**)**('([~'(')]++')[ \n\t]**|)':"]]; TryFwd: PROC [searchFrom: INT] RETURNS [found: BOOL] = { searchRope: ROPE = MF[TiogaRopes.RopeFromTioga[textRoot]]; found _ FALSE; WHILE NOT found DO [found: found, before: before, after: after] _ RegularExpression.SearchRope[finder: slow, rope: searchRope, start: searchFrom, interrupt: interrupt]; IF NOT found THEN EXIT; IF NOT CedarOKBefore[searchRope, before] THEN {searchFrom _ after; found _ FALSE}; ENDLOOP; }; TryBkwd: PROC RETURNS [found: BOOL] = { searchRope: ROPE = MF[TiogaRopes.RopeFromTioga[textRoot]]; searchLength: INT _ here - start; found _ FALSE; WHILE NOT found DO [found: found, before: before, after: after] _ RegularExpression.SearchRopeBackwards[finder: slow, rope: searchRope, start: start, len: searchLength, interrupt: interrupt]; IF NOT found THEN EXIT; IF NOT CedarOKBefore[searchRope, before] THEN {searchLength _ before - start; found _ FALSE}; ENDLOOP; }; IF verbose THEN Msg[TRUE, "Looking hard for ", target, " in ", from.fileName]; found _ SELECT from.searchOrder FROM fromStart => TryFwd[0], fwdThenBkwd => TryFwd[here] OR TryBkwd[], bkwdThenFwd => TryBkwd[] OR TryFwd[here], ENDCASE => ERROR; IF interrupt#NIL AND interrupt^ THEN Failure[from, Rope.Concat["interrupted search for ", target]]; IF verbose THEN Msg[FALSE, " ."]; IF found THEN { foundLoc _ TextNode.LocRelative[[textRoot, 0], before]; nameEnd _ TextNode.LocRelative[foundLoc, targLen-1]; }; }; IF (NOT found) AND ma # NIL THEN { FOR owus: MesaAnalyses.IRList _ ma.openedWithoutUsingList, owus.rest WHILE owus # NIL DO ir: MesaAnalyses.InterfaceRecord = owus.first; IF interrupt#NIL AND interrupt^ THEN Failure[from, Rope.Concat["interrupted search for ", target]]; IF searchedOpens = NIL THEN searchedOpens _ HashTable.Create[equal: HashTable.RopeEqual, hash: HashTable.HashRope]; found _ TRUE; IF searchedOpens.Fetch[ir.type.typeClass].found THEN found _ FALSE ELSE { IF NOT searchedOpens.Insert[ir.type.typeClass, $T] THEN ERROR; fp _ FindCedarDef[target, CedarModuleBody[from, ir.type, verbose, stack, interrupt].where, deep, verbose, FALSE, searchedOpens, stack, interrupt !Failure => {found _ FALSE; CONTINUE}]; }; IF found THEN GOTO Dun; ENDLOOP; }; IF (NOT found) AND guessDIRECTORY AND leftmost THEN { IF verbose THEN Msg[TRUE, "Guessing ", target, " missing from DIRECTORY in ", from.fileName]; {ans: FileDWIM.Answer = FileDWIM.ResolveHint[target.Concat[".Mesa"], from.fileName, FALSE]; IF ans.fullFileName # NIL THEN { next: Place = [ans.fullFileName, [DocCache.GetDoc[ans.fullFileName], 0], fromStart]; found _ TRUE; fp _ FindCedarDef[target, next, deep, verbose, FALSE, searchedOpens, stack, interrupt !Failure => {found _ FALSE; CONTINUE}]; IF found THEN GOTO Dun; }; IF verbose THEN Msg[FALSE, " ."]; }}; IF NOT found THEN ERROR Failure[from, Rope.Cat["couldn't find ", target]]; fp _ [[from.fileName, foundLoc, fwdThenBkwd], nameEnd]; IF NOT deep THEN GOTO Dun; {in: IO.STREAM = TiogaStreams.CreateInput[textRoot, useDirectly]; in.SetIndex[after]; {p: Lexer = MakeLexer[in]; toke: Token; SkipMumble[p]; toke _ GetToken[p]; IF toke.kind = tokenID THEN { IF NOT ReservedWord[toke.rope] THEN { defTree: CedarTree = GetTree[p, toke]; IF (WITH defTree SELECT FROM rope: ROPE => NOT PredefinedType[rope], ENDCASE => TRUE) THEN {fp _ FindCedarDef[defTree, fp.where, TRUE, verbose, TRUE, NIL, stack, interrupt]; GOTO Dun}; } ELSE IF toke.rope.Equal["TYPE"] THEN { toke2: Token = GetToken[p]; IF toke2.kind = tokenSINGLE AND RopeList.Memb[list: LIST["~", "="], r: toke2.rope] THEN { SkipMumble[p]; {toke3: Token = GetToken[p]; IF toke3.kind = tokenID AND NOT ReservedWord[toke3.rope] THEN { defTree: CedarTree = GetTree[p, toke3]; IF (WITH defTree SELECT FROM rope: ROPE => NOT PredefinedType[rope], ENDCASE => TRUE) THEN {fp _ FindCedarDef[defTree, fp.where, deep, verbose, TRUE, NIL, stack, interrupt]; GOTO Dun}; }; }}; }; }; in.Close[]; }}}; ENDCASE => ERROR; EXITS Dun => fp _ fp; }; IF NOT stack.Delete[frame] THEN ERROR; }; CedarOKBefore: PROC [searchRope: ROPE, before: INT] RETURNS [ok: BOOL] = { first: BOOL _ TRUE; WHILE before > 0 DO char: CHAR = searchRope.Fetch[before _ before - 1]; SELECT char FROM ' , '\n, '\t => NULL; ': => RETURN [FALSE]; IN ['a .. 'z], IN ['A .. 'Z], IN ['0 .. '9] => IF first THEN RETURN [FALSE] ELSE RETURN [TRUE]; ENDCASE => RETURN [TRUE]; first _ FALSE; ENDLOOP; ok _ TRUE; }; SkipMumble: PROC [p: Lexer] = { DO SELECT TRUE FROM p.NextIs["REF"] => LOOP; p.NextIs["LONG"] => LOOP; p.NextIsSeq[LIST["POINTER", "TO"]] => LOOP; p.NextIs["PRIVATE"] => LOOP; p.NextIs["PUBLIC"] => LOOP; p.NextIs["READONLY"] => LOOP; ENDCASE => NULL; EXIT; ENDLOOP; }; GetTree: PROC [p: Lexer, start: Token] RETURNS [tree: CedarTree] = { want: {dot, name} _ dot; tree _ start.rope; DO toke: Token = GetToken[p]; SELECT want FROM dot => { IF toke.rope.Equal["^"] THEN LOOP ELSE IF toke.rope.Equal["."] THEN want _ name ELSE {ReturnToken[p, toke]; EXIT}; }; name => { IF toke.kind = tokenID AND NOT ReservedWord[toke.rope] THEN { tree _ NEW [FieldSelectionPrivate _ [tree, toke.rope]]; want _ dot; } ELSE {ReturnToken[p, toke]; EXIT}; }; ENDCASE => ERROR; ENDLOOP; }; CedarModuleBody: PROC [from: Place, it: MesaAnalyses.InterfaceType, verbose: BOOL, stack: HashTable.Table--Frame _ $T--, interrupt: REF BOOL] RETURNS [fp: FoundPlace] = { ans: FileDWIM.Answer = FileDWIM.ResolveHint[it.fileHint, from.fileName, FALSE]; IF ans.fullFileName = NIL THEN ERROR Failure[from, Rope.Cat["couldn't find file ", it.fileHint, " for module ", it.name]]; {next: Place = [ans.fullFileName, [DocCache.GetDoc[ans.fullFileName], 0], fromStart]; fp _ FindCedarDef[it.typeClass, next, FALSE, verbose, FALSE, NIL, stack, interrupt]; }}; MF: PROC [rope: ROPE] RETURNS [fr: ROPE] = INLINE { fr _ IF teledebug THEN rope.Flatten[] ELSE rope}; teledebug: BOOL _ FALSE; MakeStack: PROC RETURNS [stack: HashTable.Table--Frame _ $T--] = { stack _ HashTable.Create[hash: HashFrame, equal: CompareFrames]; }; HashFrame: PROC [key: REF ANY] RETURNS [hash: CARDINAL] --HashTable.HashProc-- = { f: Frame = NARROW[key]; hash _ HashTree[f.tree] + RopeHash.FromRope[f.from.fileName] + HashRef[f.from.loc.node] + HashInt[f.from.loc.where]; }; HashTree: PROC [t: CedarTree] RETURNS [hash: CARDINAL] = { WITH t SELECT FROM x: ROPE => hash _ RopeHash.FromRope[x]; x: FieldSelection => hash _ HashTree[x.record]*3 + RopeHash.FromRope[x.field]; ENDCASE => ERROR; }; HashRef: PROC [ref: REF ANY] RETURNS [hash: CARDINAL] = TRUSTED INLINE {hash _ HashInt[LOOPHOLE[ref]]}; HashInt: PROC [i: INT] RETURNS [hash: CARDINAL] = INLINE {ln: Basics.LongNumber = [li[i]]; hash _ ln.lo + ln.hi}; CompareFrames: PROC [key1, key2: REF ANY] RETURNS [equal: BOOL] = { f1: Frame = NARROW[key1]; f2: Frame = NARROW[key2]; equal _ CompareTrees[f1.tree, f2.tree] AND f1.from.fileName.Equal[f2.from.fileName, FALSE] AND f1.from.loc = f2.from.loc; }; CompareTrees: PROC [t1, t2: CedarTree] RETURNS [equal: BOOL] = { equal _ WITH t1 SELECT FROM x: ROPE => WITH t2 SELECT FROM y: ROPE => x.Equal[y], y: FieldSelection => FALSE, ENDCASE => ERROR, x: FieldSelection => WITH t2 SELECT FROM y: ROPE => FALSE, y: FieldSelection => CompareTrees[x.record, y.record] AND x.field.Equal[y.field], ENDCASE => ERROR, ENDCASE => ERROR; }; FmtTree: PROC [t: CedarTree] RETURNS [rope: ROPE] = { rope _ WITH t SELECT FROM x: ROPE => x, x: FieldSelection => FmtTree[x.record].Cat[".", x.field], ENDCASE => ERROR; }; cmdEnabled: BOOL _ TRUE; workVerbose: BOOL _ TRUE; DefButt: PROC [parent: REF ANY, clientData: REF ANY _ NIL, mouseButton: Menus.MouseButton _ red, shift, control: BOOL _ FALSE] --Menus.ClickProc-- = { TEditInput.InterpretAtom[NARROW[parent], defAtoms[mouseButton][control][shift]]; }; defAtoms: ARRAY Menus.MouseButton OF ARRAY --control--BOOL OF ARRAY --shift--BOOL OF ATOM = [ [[$FindNextDef, $FindNextDefCaseless], [$FindNextDefCtl, $FindNextDefCaselessCtl]], [[$FindAnyDef, $FindAnyDefCaseless], [$FindAnyDefCtl, $FindAnyDefCaselessCtl]], [[$FindPrevDef, $FindPrevDefCaseless], [$FindPrevDefCtl, $FindPrevDefCaselessCtl]]]; DefCommand: PROC [viewer: Viewer _ NIL] RETURNS [recordAtom: BOOL _ TRUE, quit: BOOL _ FALSE] --TiogaOps.CommandProc-- = {[recordAtom, quit] _ DefCommandWork[viewer, FALSE]}; DefDeepCommand: PROC [viewer: Viewer _ NIL] RETURNS [recordAtom: BOOL _ TRUE, quit: BOOL _ FALSE] --TiogaOps.CommandProc-- = {[recordAtom, quit] _ DefCommandWork[viewer, TRUE]}; DefCommandWork: PROC [viewer: Viewer, deep: BOOL] RETURNS [recordAtom: BOOL _ TRUE, quit: BOOL _ FALSE] --TiogaOps.CommandProc-- = { subjectRope: ROPE = TEditOps.GetSelContents[]; msg: ROPE _ NIL; IF NOT cmdEnabled THEN RETURN [FALSE, FALSE]; recordAtom _ FALSE; quit _ TRUE; IF subjectRope.IsEmpty[] THEN { Msg[TRUE, "Select target for definition-search."]; MessageWindow.Blink[]; RETURN; }; {ENABLE Failure => { Msg[TRUE, why]; IF where # noplace THEN Msg[FALSE, " in ", where.fileName]; MessageWindow.Blink[]; CONTINUE }; start: Place; def: FoundPlace; WithLocks: PROC [tdd: TEditDocument.TEditDocumentData, tSel: TEditDocument.Selection] = { root: TextNode.Ref = IF TextNode.Parent[tdd.text]=NIL THEN tdd.text ELSE TextNode.Root[tdd.text]; startLoc: Location = IF root = TextNode.Root[tSel.start.pos.node] THEN tSel.start.pos ELSE [root, 0]; IF tiogaInterrupt # NIL THEN tiogaInterrupt^ _ FALSE; start _ [viewer.file, startLoc, bkwdThenFwd]; def _ FindDef[subjectRope, start, deep, workVerbose, tiogaInterrupt]; IF TextNode.Root[startLoc.node] = TextNode.Root[def.where.loc.node] THEN { tSel.start.pos _ def.where.loc; tSel.end.pos _ def.nameEnd; tSel.granularity _ word; tSel.viewer _ viewer; tSel.data _ tdd; tSel.pendingDelete _ FALSE; TEditOps.RememberCurrentPosition[viewer]; TEditSelection.SetSelLooks[tSel]; TEditSelection.MakeSelection[new: tSel, selection: primary]; TEditInput.CloseEvent[]; TEditRefresh.ScrollToEndOfSel[viewer, FALSE, primary]; } ELSE { TEditInput.CloseEvent[]; ViewerForkers.ForkCall[viewer, OpenAndSelect, LIST[NEW [Place _ start], NEW [FoundPlace _ def]]]; }; }; TEditSelection.CallWithSelAndDocAndTddLocks[viewer, primary, WithLocks]; MessageWindow.Clear[]; }}; tiogaInterrupt: REF BOOL _ NIL; OpenAndSelect: PROC [data: REF ANY] --ViewerForkers.CallBack-- = { args: LORA = NARROW[data]; start: REF Place = NARROW[args.first]; end: REF FoundPlace = NARROW[args.rest.first]; first: INT = TextNode.LocNumber[end.where.loc]; last: INT = first + TextNode.LocOffset[end.where.loc, end.nameEnd]; v: Viewer = TEditDocumentPrivate.DoOpenFile[end.where.fileName]; IF v = NIL THEN { Msg[TRUE, "Couldn't open ", end.where.fileName, "."]; MessageWindow.Blink[]; RETURN; }; ShowGivenPosition[v, first, last, FALSE]; }; ShowGivenPosition: PUBLIC PROC [viewer: Viewer, first, last: INT, skipCommentNodes: BOOL _ TRUE] = { DoPosition: PROC [tdd: TEditDocument.TEditDocumentData, tSel: TEditDocument.Selection] = { tSel.start.pos _ TextNode.LocWithin[tdd.text, first, 1, skipCommentNodes]; tSel.end.pos _ TextNode.LocRelative[tSel.start.pos, last-first, 1, skipCommentNodes]; tSel.granularity _ word; tSel.viewer _ viewer; tSel.data _ tdd; tSel.pendingDelete _ FALSE; tSel.insertion _ IF TEditProfile.selectionCaret=before THEN before ELSE after; TEditOps.RememberCurrentPosition[viewer]; TEditSelection.SetSelLooks[tSel]; TEditSelection.MakeSelection[new: tSel, selection: feedback]; TEditRefresh.ScrollToEndOfSel[viewer, FALSE, feedback]; }; TEditSelection.CallWithSelAndDocAndTddLocks[viewer, feedback, DoPosition]; }; AmbushMenuEntry: PROC [first: Menus.MenuEntry, name: ROPE, proc: Menus.ClickProc] = { me: Menus.MenuEntry; FOR me _ first, me.link WHILE NOT me.name.Equal[name] DO NULL ENDLOOP; me.proc _ proc; }; NoteProfile: PROC [changeReason: UserProfile.ProfileChangeReason] --UserProfile.ProfileChangedProc-- = { cmdEnabled _ UserProfile.Boolean["DefDWIM.Enabled", TRUE]; guessDIRECTORY _ UserProfile.Boolean["DefDWIM.GuessDIRECTORY", TRUE]; workVerbose _ UserProfile.Boolean["DefDWIM.Verbose", FALSE]; }; GetTiogaInterrupt: PROC RETURNS [interrupt: REF BOOL] = {interrupt _ TEditInputExtras.interrupt}; Start: PROC = { tiogaInterrupt _ GetTiogaInterrupt[!VM.AddressFault => CONTINUE]; TiogaOps.RegisterCommand[$FindAnyDef, DefCommand]; TiogaOps.RegisterCommand[$FindAnyDefCtl, DefDeepCommand]; AmbushMenuEntry[TEditDocumentPrivate.findMenu, "Def", DefButt]; UserProfile.CallWhenProfileChanges[NoteProfile]; Register[RecognizeLisp, LispFinder]; Register[RecognizeJS, JSFinder]; Register[RecognizeCedar, CedarFinder]; }; Start[]; END. |DefDWIMImpl.mesa Mike Spreitzer October 24, 1986 8:14:31 pm PDT INVARIANT The Ring. Stuff Kernel Some particular languages If not leftmost, we never guess it might be a missing DIRECTORY entry. An OPENed module without a USING list is recorded in searchedOpens, which may be NIL. User Interface Copied from TEditSelectionOpsImpl sets bit to cause scroll after refresh Setup ส!ๆ– "cedar" style˜code™K™.—K˜Kšฯk œ;œƒœ˜ฬK˜šฯn œœ˜™ K™ —Kšœ*œห˜€Kšœ˜KšœฯcF˜SK˜—K˜Kšœœ˜Ihead™Kš œœœœœœ˜Kš œ œœœœ˜Kšœ œ˜)Kšœœ˜/Kšœœ˜$L™Kšœœœœ˜.Kšœœœ$˜?K˜Kš žœœœœœ˜7K˜Kšœœ˜!K˜šžœœœœœ œœœœŸ œ˜šœ*œœ˜>Kšœœœ>˜`Kšœ˜—Kšœ>˜DK˜K˜—šžœœœ1˜FKšœ œ#˜2K˜K˜—šž œœœœ œœœœŸ œ˜ŒKšœ_œœ˜qK˜5Kšœœ˜K˜@Kšœœ˜K˜Kšœœ˜šžœœœ œ˜:Kšœž˜žK˜—šžœœœ œ˜'Kšœื˜ืK˜—šœœœ˜*Kšœ#˜#Kšœ œ ˜-Kšœœ˜-Kšœœ˜—Kšœ œœ œD˜hšœœ˜Kšœ8˜8Kšœ;˜;Kšœ>˜>Kšœ7˜7K˜—Kšœ8˜˜>Kšœ7˜7K˜—Kšœ8˜˜>K˜—Kšœ˜š œœœœœ˜"Kšœœœ%˜1šœœœ˜šœœœ˜KšœW˜Wšœ$œ˜+Kšœ:˜>Kšœ‘˜•—Kšœ•œ#˜ฝKšœœ˜—Kšœ˜ —K˜—šœœœ˜Kšœœ/˜8Kšœ œ˜Kšœซ˜ซš žœœœœ œ˜8Kšœ œœ%˜:Kšœœ˜šœœ˜Kšœ•˜•Kšœœœœ˜Kšœœ#œœ˜RKšœ˜—K˜—šžœœœ œ˜'Kšœ œœ%˜:Kšœœ˜!Kšœœ˜šœœ˜Kšœฌ˜ฌKšœœœœ˜Kšœœ#œ)œ˜]Kšœ˜—K˜—Kšœ œœ6˜Nšœœ˜$Kšœ˜Kšœœ ˜)Kšœœ˜)Kšœœ˜—Kšœ œœ œ?˜cKšœ œœ˜!šœœ˜K˜7Kšœ4˜4K˜—K˜—š œœœœœ˜"šœBœœ˜XKšœ.˜.Kšœ œœ œ?˜cKšœœœX˜sKšœœ˜ Kšœ.œ ˜Bšœ˜Kšœœ-œœ˜>Kšœjœ7œœ˜ธKšœ˜—Kšœœœ˜Kšœ˜—K˜—š œœœœ œ˜5Kšœ œœE˜]KšœTœ˜[šœœœ˜ KšœT˜TKšœœ˜ Kšœ/œ7œœ˜}Kšœœœ˜K˜—Kšœ œœ˜!K˜—Kšœœœœ3˜JKšœ7˜7Kšœœœœ˜Kšœœœ3˜AK˜Kšœ˜Kšœ ˜ Kšœ˜Kšœ˜šœœ˜šœœœ˜%K˜&šœœ œ˜Kšœœœ˜'Kšœœ˜—Kš œ'œ œœœ˜bK˜—šœœœ˜&K˜•StartOfExpansion6[list: LIST OF ROPE, r: ROPE, case: BOOL _ TRUE]šœœœœ˜YKšœ˜K˜šœœœœ˜?K˜'šœœ œ˜Kšœœœ˜'Kšœœ˜—Kšœ6œœœ˜bK˜—K˜—K˜—K˜—Kšœ ˜ K˜—Kšœœ˜—Kšœ˜K˜Kšœœœœ˜&K˜—K˜š ž œœœ œœœ˜JKšœœœ˜šœ ˜Kšœœ)˜3šœ˜Kšœœ˜Kšœœœ˜Kšœ œ œœœœœœœœ˜_Kšœœœ˜—Kšœœ˜Kšœ˜—Kšœœ˜ K˜K˜—šž œœ˜š˜šœœ˜Kšœœ˜Kšœœ˜Kšœ œœ˜+Kšœœ˜Kšœœ˜Kšœœ˜Kšœœ˜—Kšœ˜Kšœ˜—K˜K˜—šžœœœ˜DK˜K˜š˜K˜šœ˜˜Kšœœ˜!Kšœœœ ˜-Kšœœ˜"K˜—˜ šœœœœ˜=Kšœœ-˜7K˜ K˜—Kšœœ˜"K˜—Kšœœ˜—Kšœ˜—K˜K˜—šžœœ8œŸ Ÿœ œœœ˜ชKšœHœ˜OKšœœœœV˜zKšœU˜UKšœ&œ œœ˜TK˜K˜—š œœœœœœ˜3Kšœœ œœ˜1Kšœ œœ˜K˜—š ž œœœŸ Ÿœ˜BK˜@K˜K˜—šž œœœœœœŸœ˜RKšœ œ˜Kšœt˜tK˜K˜—šžœœœœ˜:šœœ˜Kšœœ ˜'KšœN˜NKšœœ˜—K˜K˜—š žœœœœœœ˜5Kšœœœœ˜1K˜—š žœœœœœ˜/Kšœœ9˜AK˜—š ž œœœœœ œ˜CKšœ œ˜Kšœ œ˜Kšœ'œ*œœ˜yK˜K˜—šž œœœ œ˜@šœœœ˜šœœœœ˜Kšœœ˜Kšœœ˜Kšœœ˜—šœœœ˜(Kšœœœ˜Kšœ6œ˜QKšœœ˜—Kšœœ˜—K˜K˜—šžœœœœ˜5šœœœ˜Kšœœ˜ Kšœ9˜9Kšœœ˜—K˜K˜—L™Kšœ œœ˜Kšœ œœ˜K˜šžœœ œœœœœ9œœŸœ˜—Kšœœ1˜PKšœ˜šœ œœœŸ œœœŸ œœœ˜]KšœS˜SKšœO˜OKšœT˜T—K˜—Kšž œœœœœœœœŸœ0œ˜ฎK˜KšžœœœœœœœœŸœ0œ˜ฑK˜šžœœœœœœœœŸœ˜„Kšœ œ˜.Kšœœœ˜Kš œœ œœœœ˜-Kšœ œ˜Kšœœ˜ šœœ˜Kšœœ*˜2K˜Kšœ˜K˜—šœœ ˜Kšœœ˜Kšœœœ˜;K˜Kš˜K˜—Kšœ ˜ K˜šž œœJ˜YKš œœœœ œ˜aKšœœ+œœ ˜eKšœœœœ˜5K˜-KšœE˜EšœBœ˜JK˜Kšœ˜Kšœ˜Kšœ˜K˜Kšœœ˜K˜)K˜!K˜