@@ -11,6 +11,7 @@ trait MemberLookup {
1111 thisFactory : ModelFactory =>
1212
1313 import global ._
14+ import rootMirror .RootPackage , rootMirror .EmptyPackage
1415
1516 def makeEntityLink (title : Inline , pos : Position , query : String , inTplOpt : Option [DocTemplateImpl ]) =
1617 new EntityLink (title) { lazy val link = memberLookup(pos, query, inTplOpt) }
@@ -21,23 +22,44 @@ trait MemberLookup {
2122 var members = breakMembers(query)
2223 // println(query + " => " + members)
2324
24- // (1) Lookup in the root package, as most of the links are qualified
25- var linkTo : List [ LinkTo ] = lookupInRootPackage(pos, members)
25+ // (1) First look in the root package, as most of the links are qualified
26+ val fromRoot = lookupInRootPackage(pos, members)
2627
27- // (2) Recursively go into each
28- if (inTplOpt.isDefined) {
29- var currentTpl = inTplOpt.get
30- while (currentTpl != null && ! currentTpl.isRootPackage && (linkTo.isEmpty)) {
31- linkTo = lookupInTemplate(pos, members, currentTpl)
32- currentTpl = currentTpl.inTemplate
33- }
28+ // (2) Or recursively go into each containing template.
29+ val fromParents = inTplOpt.fold(Stream .empty[DocTemplateImpl ]) { tpl =>
30+ Stream .iterate(tpl)(_.inTemplate)
31+ }.takeWhile (tpl => tpl != null && ! tpl.isRootPackage).map { tpl =>
32+ lookupInTemplate(pos, members, tpl.asInstanceOf [EntityImpl ].sym)
3433 }
3534
36- // (3) Look at external links
37- if (linkTo.isEmpty) {
38- // TODO: IF THIS IS THE ROOT PACKAGE, LOOK AT EXTERNAL LINKS
35+ val syms = (fromRoot +: fromParents) find (! _.isEmpty) getOrElse Nil
36+ val linkTo = createLinks(syms) match {
37+ case Nil if ! syms.isEmpty =>
38+ // (3) Look at external links
39+ syms.flatMap { case (sym, owner) =>
40+
41+ // reconstruct the original link
42+ def linkName (sym : Symbol ) = {
43+ def isRoot (s : Symbol ) = s.isRootSymbol || s.isEmptyPackage || s.isEmptyPackageClass
44+ def nameString (s : Symbol ) = s.nameString + (if ((s.isModule || s.isModuleClass) && ! s.isPackage) " $" else " " )
45+ val packageSuffix = if (sym.isPackage) " .package" else " "
46+
47+ sym.ownerChain.reverse.filterNot(isRoot(_)).map(nameString(_)).mkString(" ." ) + packageSuffix
48+ }
49+
50+ if (sym.isClass || sym.isModule || sym.isTrait || sym.isPackage)
51+ findExternalLink(linkName(sym))
52+ else if (owner.isClass || owner.isModule || owner.isTrait || owner.isPackage)
53+ findExternalLink(linkName(owner) + " @" + externalSignature(sym))
54+ else
55+ None
56+ }
57+ case links => links
3958 }
4059
60+ // println(createLinks(syms))
61+ // println(linkTo)
62+
4163 // (4) if we still haven't found anything, create a tooltip, if we found too many, report
4264 if (linkTo.isEmpty){
4365 if (! settings.docNoLinkWarnings.value)
@@ -97,9 +119,23 @@ trait MemberLookup {
97119 private object OnlyType extends SearchStrategy
98120 private object OnlyTerm extends SearchStrategy
99121
100- private def lookupInRootPackage (pos : Position , members : List [String ]) = lookupInTemplate(pos, members, makeRootPackage)
122+ private def lookupInRootPackage (pos : Position , members : List [String ]) =
123+ if (members.length == 1 )
124+ lookupInTemplate(pos, members, EmptyPackage ) ::: lookupInTemplate(pos, members, RootPackage )
125+ else
126+ lookupInTemplate(pos, members, RootPackage )
101127
102- private def lookupInTemplate (pos : Position , members : List [String ], inTpl : DocTemplateImpl ): List [LinkTo ] = {
128+ private def createLinks (syms : List [(Symbol , Symbol )]): List [LinkTo ] =
129+ syms.flatMap { case (sym, owner) =>
130+ if (sym.isClass || sym.isModule || sym.isTrait || sym.isPackage)
131+ findTemplateMaybe(sym) map (LinkToTpl (_))
132+ else
133+ findTemplateMaybe(owner) flatMap { inTpl =>
134+ inTpl.members find (_.asInstanceOf [EntityImpl ].sym == sym) map (LinkToMember (_, inTpl))
135+ }
136+ }
137+
138+ private def lookupInTemplate (pos : Position , members : List [String ], container : Symbol ): List [(Symbol , Symbol )] = {
103139 // Maintaining compatibility with previous links is a bit tricky here:
104140 // we have a preference for term names for all terms except for the last, where we prefer a class:
105141 // How to do this:
@@ -108,53 +144,56 @@ trait MemberLookup {
108144 // * we look for terms with the last member's name
109145 // * we look for types with the same name, all the way up
110146 val result = members match {
111- case Nil =>
112- Nil
147+ case Nil => Nil
113148 case mbrName:: Nil =>
114- var members = lookupInTemplate(pos, mbrName, inTpl, OnlyType )
115- if (members.isEmpty)
116- members = lookupInTemplate(pos, mbrName, inTpl, OnlyTerm )
117-
118- members.map(_ match {
119- case tpl : DocTemplateEntity => LinkToTpl (tpl)
120- case mbr => LinkToMember (mbr, inTpl)
121- })
149+ var syms = lookupInTemplate(pos, mbrName, container, OnlyType ) map ((_, container))
150+ if (syms.isEmpty)
151+ syms = lookupInTemplate(pos, mbrName, container, OnlyTerm ) map ((_, container))
152+ syms
122153
123154 case tplName:: rest =>
155+ def completeSearch (syms : List [Symbol ]) =
156+ syms filter {sym => sym.isPackage || sym.isClass || sym.isModule} flatMap (lookupInTemplate(pos, rest, _))
124157
125- def completeSearch (mbrs : List [MemberImpl ]) =
126- mbrs.collect({case d: DocTemplateImpl => d}).flatMap(tpl => lookupInTemplate(pos, rest, tpl))
127-
128- var members = completeSearch(lookupInTemplate(pos, tplName, inTpl, OnlyTerm ))
129- if (members.isEmpty)
130- members = completeSearch(lookupInTemplate(pos, tplName, inTpl, OnlyType ))
131-
132- members
158+ completeSearch(lookupInTemplate(pos, tplName, container, OnlyTerm )) match {
159+ case Nil => completeSearch(lookupInTemplate(pos, tplName, container, OnlyType ))
160+ case syms => syms
161+ }
133162 }
134- // println("lookupInTemplate(" + members + ", " + inTpl + ") => " + result)
163+ // println("lookupInTemplate(" + members + ", " + container + ") => " + result)
135164 result
136165 }
137166
138- private def lookupInTemplate (pos : Position , member : String , inTpl : DocTemplateImpl , strategy : SearchStrategy ): List [MemberImpl ] = {
167+ private def lookupInTemplate (pos : Position , member : String , container : Symbol , strategy : SearchStrategy ): List [Symbol ] = {
139168 val name = member.stripSuffix(" $" ).stripSuffix(" !" ).stripSuffix(" *" )
169+ def signatureMatch (sym : Symbol ): Boolean = externalSignature(sym).startsWith(name)
170+
171+ // We need to cleanup the bogus classes created by the .class file parser. For example, [[scala.Predef]] resolves
172+ // to (bogus) class scala.Predef loaded by the class loader -- which we need to eliminate by looking at the info
173+ // and removing NoType classes
174+ def cleanupBogusClasses (syms : List [Symbol ]) = { syms.filter(_.info != NoType ) }
175+
176+ def syms (name : Name ) = container.info.nonPrivateMember(name).alternatives
177+ def termSyms = cleanupBogusClasses(syms(newTermName(name)))
178+ def typeSyms = cleanupBogusClasses(syms(newTypeName(name)))
179+
140180 val result = if (member.endsWith(" $" ))
141- inTpl.members.filter(mbr => (mbr.name == name) && (mbr.isTerm))
181+ termSyms
142182 else if (member.endsWith(" !" ))
143- inTpl.members.filter(mbr => (mbr.name == name) && (mbr.isType))
183+ typeSyms
144184 else if (member.endsWith(" *" ))
145- inTpl.members.filter(mbr => (mbr.signature.startsWith(name)))
146- else {
185+ cleanupBogusClasses(container.info.nonPrivateDecls) filter signatureMatch
186+ else
147187 if (strategy == BothTypeAndTerm )
148- inTpl.members.filter(_.name == name)
188+ termSyms ::: typeSyms
149189 else if (strategy == OnlyType )
150- inTpl.members.filter(mbr => (mbr.name == name) && (mbr.isType))
190+ typeSyms
151191 else if (strategy == OnlyTerm )
152- inTpl.members.filter(mbr => (mbr.name == name) && (mbr.isTerm))
192+ termSyms
153193 else
154194 Nil
155- }
156195
157- // println("lookupInTemplate(" + member + ", " + inTpl + ") => " + result)
196+ // println("lookupInTemplate(" + member + ", " + container + ") => " + result)
158197 result
159198 }
160199
@@ -170,7 +209,11 @@ trait MemberLookup {
170209 if ((query.charAt(index) == '.' || query.charAt(index) == '#' ) &&
171210 ((index == 0 ) || (query.charAt(index- 1 ) != '\\ ' ))) {
172211
173- members ::= query.substring(last_index, index).replaceAll(" \\\\ ([#\\ .])" , " $1" )
212+ val member = query.substring(last_index, index).replaceAll(" \\\\ ([#\\ .])" , " $1" )
213+ // we want to allow javadoc-style links [[#member]] -- which requires us to remove empty members from the first
214+ // elemnt in the list
215+ if ((member != " " ) || (! members.isEmpty))
216+ members ::= member
174217 last_index = index + 1
175218 }
176219 index += 1
@@ -184,4 +227,4 @@ trait MemberLookup {
184227object MemberLookup {
185228 private [this ] var _showExplanation = true
186229 def showExplanation : Boolean = if (_showExplanation) { _showExplanation = false ; true } else false
187- }
230+ }
0 commit comments