summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGrzegorz Kossakowski <grzegorz.kossakowski@gmail.com>2012-09-26 03:28:58 -0700
committerGrzegorz Kossakowski <grzegorz.kossakowski@gmail.com>2012-09-26 03:28:58 -0700
commit339dd82162526bf2448ce00aebe6c3de3307772a (patch)
tree198ce0c30eea1d34f0be6a554ef66a86def0c297
parent1682c0df76280dbb56e452b6e914db5e24c79daf (diff)
parent6e6423be2070263129376472e7b6a48b32fac2af (diff)
downloadscala-339dd82162526bf2448ce00aebe6c3de3307772a.tar.gz
scala-339dd82162526bf2448ce00aebe6c3de3307772a.tar.bz2
scala-339dd82162526bf2448ce00aebe6c3de3307772a.zip
Merge pull request #1401 from vigdorchik/191_for_210
Generate links to external projects in scaladoc.
-rw-r--r--src/compiler/scala/tools/nsc/doc/Settings.scala22
-rw-r--r--src/compiler/scala/tools/nsc/doc/html/HtmlPage.scala3
-rw-r--r--src/compiler/scala/tools/nsc/doc/html/page/Template.scala50
-rw-r--r--src/compiler/scala/tools/nsc/doc/model/Entity.scala5
-rw-r--r--src/compiler/scala/tools/nsc/doc/model/LinkTo.scala8
-rw-r--r--src/compiler/scala/tools/nsc/doc/model/MemberLookup.scala133
-rw-r--r--src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala15
-rw-r--r--src/compiler/scala/tools/nsc/doc/model/ModelFactoryTypeSupport.scala54
-rw-r--r--src/compiler/scala/tools/nsc/doc/model/comment/CommentFactory.scala2
-rw-r--r--src/compiler/scala/tools/nsc/doc/model/diagram/DiagramFactory.scala2
-rw-r--r--test/scaladoc/resources/links.scala37
-rw-r--r--test/scaladoc/run/links.scala4
12 files changed, 209 insertions, 126 deletions
diff --git a/src/compiler/scala/tools/nsc/doc/Settings.scala b/src/compiler/scala/tools/nsc/doc/Settings.scala
index dbbc573299..64a376b96e 100644
--- a/src/compiler/scala/tools/nsc/doc/Settings.scala
+++ b/src/compiler/scala/tools/nsc/doc/Settings.scala
@@ -194,6 +194,12 @@ class Settings(error: String => Unit, val printMsg: String => Unit = println(_))
"Expand all type aliases and abstract types into full template pages. (locally this can be done with the @template annotation)"
)
+ val docExternalUrls = MultiStringSetting (
+ "-external-urls",
+ "externalUrl(s)",
+ "comma-separated list of package_names=doc_URL for external dependencies, where package names are ':'-separated"
+ )
+
val docGroups = BooleanSetting (
"-groups",
"Group similar functions together (based on the @group annotation)"
@@ -238,6 +244,22 @@ class Settings(error: String => Unit, val printMsg: String => Unit = println(_))
}
}
+ // TODO: Enable scaladoc to scoop up the package list from another scaladoc site, just as javadoc does
+ // -external-urls 'http://www.scala-lang.org/archives/downloads/distrib/files/nightly/docs/library'
+ // should trigger scaldoc to fetch the package-list file. The steps necessary:
+ // 1 - list all packages generated in scaladoc in the package-list file, exactly as javadoc:
+ // see http://docs.oracle.com/javase/6/docs/api/package-list for http://docs.oracle.com/javase/6/docs/api
+ // 2 - download the file and add the packages to the list
+ lazy val extUrlMapping: Map[String, String] = (Map.empty[String, String] /: docExternalUrls.value) {
+ case (map, binding) =>
+ val idx = binding indexOf "="
+ val pkgs = binding substring (0, idx) split ":"
+ var url = binding substring (idx + 1)
+ val index = "/index.html"
+ url = if (url.endsWith(index)) url else url + index
+ map ++ (pkgs map (_ -> url))
+ }
+
/**
* This is the hardcoded area of Scaladoc. This is where "undesirable" stuff gets eliminated. I know it's not pretty,
* but ultimately scaladoc has to be useful. :)
diff --git a/src/compiler/scala/tools/nsc/doc/html/HtmlPage.scala b/src/compiler/scala/tools/nsc/doc/html/HtmlPage.scala
index f7c5611b8a..2c719e5d70 100644
--- a/src/compiler/scala/tools/nsc/doc/html/HtmlPage.scala
+++ b/src/compiler/scala/tools/nsc/doc/html/HtmlPage.scala
@@ -138,7 +138,8 @@ abstract class HtmlPage extends Page { thisPage =>
<span class="extmbr" name={ mbr.qualifiedName }>{ inlineToHtml(text) }</span>
case Tooltip(tooltip) =>
<span class="extype" name={ tooltip }>{ inlineToHtml(text) }</span>
- // TODO: add case LinkToExternal here
+ case LinkToExternal(name, url) =>
+ <a href={ url } class="extype" target="_top">{ inlineToHtml(text) }</a>
case NoLink =>
inlineToHtml(text)
}
diff --git a/src/compiler/scala/tools/nsc/doc/html/page/Template.scala b/src/compiler/scala/tools/nsc/doc/html/page/Template.scala
index d4e52ba120..919a45aefc 100644
--- a/src/compiler/scala/tools/nsc/doc/html/page/Template.scala
+++ b/src/compiler/scala/tools/nsc/doc/html/page/Template.scala
@@ -41,7 +41,7 @@ class Template(universe: doc.Universe, generator: DiagramGenerator, tpl: DocTemp
<script type="text/javascript" src={ relativeLinkTo{List("jquery-ui.js", "lib")} }></script>
<script type="text/javascript" src={ relativeLinkTo{List("template.js", "lib")} }></script>
<script type="text/javascript" src={ relativeLinkTo{List("tools.tooltip.js", "lib")} }></script>
- { if (universe.settings.docDiagrams.isSetByUser) {
+ { if (universe.settings.docDiagrams.value) {
<script type="text/javascript" src={ relativeLinkTo{List("modernizr.custom.js", "lib")} }></script>
<script type="text/javascript" src={ relativeLinkTo{List("diagrams.js", "lib")} } id="diagrams-js"></script>
} else NodeSeq.Empty }
@@ -289,6 +289,7 @@ class Template(universe: doc.Universe, generator: DiagramGenerator, tpl: DocTemp
fullComment={ if(memberComment.filter(_.label=="div").isEmpty) "no" else "yes" }
group={ mbr.group }>
<a id={ mbr.signature }/>
+ <a id={ mbr.signatureCompat }/>
{ signature(mbr, false) }
{ memberComment }
</li>
@@ -645,17 +646,28 @@ class Template(universe: doc.Universe, generator: DiagramGenerator, tpl: DocTemp
case _ => NodeSeq.Empty
}
- val typeHierarchy = if (s.docDiagrams.isSetByUser) mbr match {
- case dtpl: DocTemplateEntity if isSelf && !isReduced =>
- makeDiagramHtml(dtpl, dtpl.inheritanceDiagram, "Type Hierarchy", "inheritance-diagram")
- case _ => NodeSeq.Empty
- } else NodeSeq.Empty // diagrams not generated
+ def createDiagram(f: DocTemplateEntity => Option[Diagram], description: String, id: String): NodeSeq =
+ if (s.docDiagrams.value) mbr match {
+ case dtpl: DocTemplateEntity if isSelf && !isReduced =>
+ val diagram = f(dtpl)
+ if (diagram.isDefined) {
+ val s = universe.settings
+ val diagramSvg = generator.generate(diagram.get, tpl, this)
+ if (diagramSvg != NodeSeq.Empty) {
+ <div class="toggleContainer block diagram-container" id={ id + "-container"}>
+ <span class="toggle diagram-link">{ description }</span>
+ <a href="http://docs.scala-lang.org/overviews/scaladoc/usage.html#diagrams" target="_blank" class="diagram-help">Learn more about scaladoc diagrams</a>
+ <div class="diagram" id={ id }>{
+ diagramSvg
+ }</div>
+ </div>
+ } else NodeSeq.Empty
+ } else NodeSeq.Empty
+ case _ => NodeSeq.Empty
+ } else NodeSeq.Empty // diagrams not generated
- val contentHierarchy = if (s.docDiagrams.isSetByUser) mbr match {
- case dtpl: DocTemplateEntity if isSelf && !isReduced =>
- makeDiagramHtml(dtpl, dtpl.contentDiagram, "Content Hierarchy", "content-diagram")
- case _ => NodeSeq.Empty
- } else NodeSeq.Empty // diagrams not generated
+ val typeHierarchy = createDiagram(_.inheritanceDiagram, "Type Hierarchy", "inheritance-diagram")
+ val contentHierarchy = createDiagram(_.contentDiagram, "Content Hierarchy", "content-diagram")
memberComment ++ paramComments ++ attributesBlock ++ linearization ++ subclasses ++ typeHierarchy ++ contentHierarchy
}
@@ -946,22 +958,6 @@ class Template(universe: doc.Universe, generator: DiagramGenerator, tpl: DocTemp
scala.xml.Text(ub.typeParamName + " is a subclass of " + ub.upperBound.name + " (" + ub.typeParamName + " <: ") ++
typeToHtml(ub.upperBound, true) ++ scala.xml.Text(")")
}
-
- def makeDiagramHtml(tpl: DocTemplateEntity, diagram: Option[Diagram], description: String, id: String) = {
- if (diagram.isDefined) {
- val s = universe.settings
- val diagramSvg = generator.generate(diagram.get, tpl, this)
- if (diagramSvg != NodeSeq.Empty) {
- <div class="toggleContainer block diagram-container" id={ id + "-container"}>
- <span class="toggle diagram-link">{ description }</span>
- <a href="http://docs.scala-lang.org/overviews/scaladoc/usage.html#diagrams" target="_blank" class="diagram-help">Learn more about scaladoc diagrams</a>
- <div class="diagram" id={ id }>{
- diagramSvg
- }</div>
- </div>
- } else NodeSeq.Empty
- } else NodeSeq.Empty
- }
}
object Template {
diff --git a/src/compiler/scala/tools/nsc/doc/model/Entity.scala b/src/compiler/scala/tools/nsc/doc/model/Entity.scala
index 6d193c30f7..a63849e3f6 100644
--- a/src/compiler/scala/tools/nsc/doc/model/Entity.scala
+++ b/src/compiler/scala/tools/nsc/doc/model/Entity.scala
@@ -196,6 +196,9 @@ trait MemberEntity extends Entity {
/** The identity of this member, used for linking */
def signature: String
+ /** Compatibility signature, will be removed from future versions */
+ def signatureCompat: String
+
/** Indicates whether the member is inherited by implicit conversion */
def isImplicitlyInherited: Boolean
@@ -625,4 +628,4 @@ trait UpperBoundedTypeParamConstraint extends TypeParamConstraint {
/** toString for debugging */
override def toString = typeParamName + " is a subclass of " + upperBound.name + " (" + typeParamName + " <: " +
upperBound.name + ")"
-} \ No newline at end of file
+}
diff --git a/src/compiler/scala/tools/nsc/doc/model/LinkTo.scala b/src/compiler/scala/tools/nsc/doc/model/LinkTo.scala
index 664567872e..737c6a7577 100644
--- a/src/compiler/scala/tools/nsc/doc/model/LinkTo.scala
+++ b/src/compiler/scala/tools/nsc/doc/model/LinkTo.scala
@@ -9,10 +9,10 @@ package model
import scala.collection._
abstract sealed class LinkTo
-case class LinkToTpl(tpl: DocTemplateEntity) extends LinkTo
-case class LinkToMember(mbr: MemberEntity, inTpl: DocTemplateEntity) extends LinkTo
-case class Tooltip(name: String) extends LinkTo { def this(tpl: TemplateEntity) = this(tpl.qualifiedName) }
-// case class LinkToExternal(name: String, url: String) extends LinkTo // for SI-191, whenever Manohar will have time
+final case class LinkToTpl(tpl: DocTemplateEntity) extends LinkTo
+final case class LinkToMember(mbr: MemberEntity, inTpl: DocTemplateEntity) extends LinkTo
+final case class Tooltip(name: String) extends LinkTo { def this(tpl: TemplateEntity) = this(tpl.qualifiedName) }
+final case class LinkToExternal(name: String, url: String) extends LinkTo
case object NoLink extends LinkTo // you should use Tooltip if you have a name from the user, this is only in case all fails
object LinkToTpl {
diff --git a/src/compiler/scala/tools/nsc/doc/model/MemberLookup.scala b/src/compiler/scala/tools/nsc/doc/model/MemberLookup.scala
index ab14498a7c..7ab73cceff 100644
--- a/src/compiler/scala/tools/nsc/doc/model/MemberLookup.scala
+++ b/src/compiler/scala/tools/nsc/doc/model/MemberLookup.scala
@@ -11,6 +11,7 @@ trait MemberLookup {
thisFactory: ModelFactory =>
import global._
+ import rootMirror.RootPackage, rootMirror.EmptyPackage
def makeEntityLink(title: Inline, pos: Position, query: String, inTplOpt: Option[DocTemplateImpl]) =
new EntityLink(title) { lazy val link = memberLookup(pos, query, inTplOpt) }
@@ -21,23 +22,44 @@ trait MemberLookup {
var members = breakMembers(query)
//println(query + " => " + members)
- // (1) Lookup in the root package, as most of the links are qualified
- var linkTo: List[LinkTo] = lookupInRootPackage(pos, members)
+ // (1) First look in the root package, as most of the links are qualified
+ val fromRoot = lookupInRootPackage(pos, members)
- // (2) Recursively go into each
- if (inTplOpt.isDefined) {
- var currentTpl = inTplOpt.get
- while (currentTpl != null && !currentTpl.isRootPackage && (linkTo.isEmpty)) {
- linkTo = lookupInTemplate(pos, members, currentTpl)
- currentTpl = currentTpl.inTemplate
- }
+ // (2) Or recursively go into each containing template.
+ val fromParents = inTplOpt.fold(Stream.empty[DocTemplateImpl]) { tpl =>
+ Stream.iterate(tpl)(_.inTemplate)
+ }.takeWhile (tpl => tpl != null && !tpl.isRootPackage).map { tpl =>
+ lookupInTemplate(pos, members, tpl.asInstanceOf[EntityImpl].sym)
}
- // (3) Look at external links
- if (linkTo.isEmpty) {
- // TODO: IF THIS IS THE ROOT PACKAGE, LOOK AT EXTERNAL LINKS
+ val syms = (fromRoot +: fromParents) find (!_.isEmpty) getOrElse Nil
+ val linkTo = createLinks(syms) match {
+ case Nil if !syms.isEmpty =>
+ // (3) Look at external links
+ syms.flatMap { case (sym, owner) =>
+
+ // reconstruct the original link
+ def linkName(sym: Symbol) = {
+ def isRoot(s: Symbol) = s.isRootSymbol || s.isEmptyPackage || s.isEmptyPackageClass
+ def nameString(s: Symbol) = s.nameString + (if ((s.isModule || s.isModuleClass) && !s.isPackage) "$" else "")
+ val packageSuffix = if (sym.isPackage) ".package" else ""
+
+ sym.ownerChain.reverse.filterNot(isRoot(_)).map(nameString(_)).mkString(".") + packageSuffix
+ }
+
+ if (sym.isClass || sym.isModule || sym.isTrait || sym.isPackage)
+ findExternalLink(linkName(sym))
+ else if (owner.isClass || owner.isModule || owner.isTrait || owner.isPackage)
+ findExternalLink(linkName(owner) + "@" + externalSignature(sym))
+ else
+ None
+ }
+ case links => links
}
+ //println(createLinks(syms))
+ //println(linkTo)
+
// (4) if we still haven't found anything, create a tooltip, if we found too many, report
if (linkTo.isEmpty){
if (!settings.docNoLinkWarnings.value)
@@ -97,9 +119,23 @@ trait MemberLookup {
private object OnlyType extends SearchStrategy
private object OnlyTerm extends SearchStrategy
- private def lookupInRootPackage(pos: Position, members: List[String]) = lookupInTemplate(pos, members, makeRootPackage)
+ private def lookupInRootPackage(pos: Position, members: List[String]) =
+ if (members.length == 1)
+ lookupInTemplate(pos, members, EmptyPackage) ::: lookupInTemplate(pos, members, RootPackage)
+ else
+ lookupInTemplate(pos, members, RootPackage)
- private def lookupInTemplate(pos: Position, members: List[String], inTpl: DocTemplateImpl): List[LinkTo] = {
+ private def createLinks(syms: List[(Symbol, Symbol)]): List[LinkTo] =
+ syms.flatMap { case (sym, owner) =>
+ if (sym.isClass || sym.isModule || sym.isTrait || sym.isPackage)
+ findTemplateMaybe(sym) map (LinkToTpl(_))
+ else
+ findTemplateMaybe(owner) flatMap { inTpl =>
+ inTpl.members find (_.asInstanceOf[EntityImpl].sym == sym) map (LinkToMember(_, inTpl))
+ }
+ }
+
+ private def lookupInTemplate(pos: Position, members: List[String], container: Symbol): List[(Symbol, Symbol)] = {
// Maintaining compatibility with previous links is a bit tricky here:
// we have a preference for term names for all terms except for the last, where we prefer a class:
// How to do this:
@@ -108,53 +144,56 @@ trait MemberLookup {
// * we look for terms with the last member's name
// * we look for types with the same name, all the way up
val result = members match {
- case Nil =>
- Nil
+ case Nil => Nil
case mbrName::Nil =>
- var members = lookupInTemplate(pos, mbrName, inTpl, OnlyType)
- if (members.isEmpty)
- members = lookupInTemplate(pos, mbrName, inTpl, OnlyTerm)
-
- members.map(_ match {
- case tpl: DocTemplateEntity => LinkToTpl(tpl)
- case mbr => LinkToMember(mbr, inTpl)
- })
+ var syms = lookupInTemplate(pos, mbrName, container, OnlyType) map ((_, container))
+ if (syms.isEmpty)
+ syms = lookupInTemplate(pos, mbrName, container, OnlyTerm) map ((_, container))
+ syms
case tplName::rest =>
+ def completeSearch(syms: List[Symbol]) =
+ syms filter {sym => sym.isPackage || sym.isClass || sym.isModule} flatMap (lookupInTemplate(pos, rest, _))
- def completeSearch(mbrs: List[MemberImpl]) =
- mbrs.collect({case d:DocTemplateImpl => d}).flatMap(tpl => lookupInTemplate(pos, rest, tpl))
-
- var members = completeSearch(lookupInTemplate(pos, tplName, inTpl, OnlyTerm))
- if (members.isEmpty)
- members = completeSearch(lookupInTemplate(pos, tplName, inTpl, OnlyType))
-
- members
+ completeSearch(lookupInTemplate(pos, tplName, container, OnlyTerm)) match {
+ case Nil => completeSearch(lookupInTemplate(pos, tplName, container, OnlyType))
+ case syms => syms
+ }
}
- //println("lookupInTemplate(" + members + ", " + inTpl + ") => " + result)
+ //println("lookupInTemplate(" + members + ", " + container + ") => " + result)
result
}
- private def lookupInTemplate(pos: Position, member: String, inTpl: DocTemplateImpl, strategy: SearchStrategy): List[MemberImpl] = {
+ private def lookupInTemplate(pos: Position, member: String, container: Symbol, strategy: SearchStrategy): List[Symbol] = {
val name = member.stripSuffix("$").stripSuffix("!").stripSuffix("*")
+ def signatureMatch(sym: Symbol): Boolean = externalSignature(sym).startsWith(name)
+
+ // We need to cleanup the bogus classes created by the .class file parser. For example, [[scala.Predef]] resolves
+ // to (bogus) class scala.Predef loaded by the class loader -- which we need to eliminate by looking at the info
+ // and removing NoType classes
+ def cleanupBogusClasses(syms: List[Symbol]) = { syms.filter(_.info != NoType) }
+
+ def syms(name: Name) = container.info.nonPrivateMember(name).alternatives
+ def termSyms = cleanupBogusClasses(syms(newTermName(name)))
+ def typeSyms = cleanupBogusClasses(syms(newTypeName(name)))
+
val result = if (member.endsWith("$"))
- inTpl.members.filter(mbr => (mbr.name == name) && (mbr.isTerm))
+ termSyms
else if (member.endsWith("!"))
- inTpl.members.filter(mbr => (mbr.name == name) && (mbr.isType))
+ typeSyms
else if (member.endsWith("*"))
- inTpl.members.filter(mbr => (mbr.signature.startsWith(name)))
- else {
+ cleanupBogusClasses(container.info.nonPrivateDecls) filter signatureMatch
+ else
if (strategy == BothTypeAndTerm)
- inTpl.members.filter(_.name == name)
+ termSyms ::: typeSyms
else if (strategy == OnlyType)
- inTpl.members.filter(mbr => (mbr.name == name) && (mbr.isType))
+ typeSyms
else if (strategy == OnlyTerm)
- inTpl.members.filter(mbr => (mbr.name == name) && (mbr.isTerm))
+ termSyms
else
Nil
- }
- //println("lookupInTemplate(" + member + ", " + inTpl + ") => " + result)
+ //println("lookupInTemplate(" + member + ", " + container + ") => " + result)
result
}
@@ -170,7 +209,11 @@ trait MemberLookup {
if ((query.charAt(index) == '.' || query.charAt(index) == '#') &&
((index == 0) || (query.charAt(index-1) != '\\'))) {
- members ::= query.substring(last_index, index).replaceAll("\\\\([#\\.])", "$1")
+ val member = query.substring(last_index, index).replaceAll("\\\\([#\\.])", "$1")
+ // we want to allow javadoc-style links [[#member]] -- which requires us to remove empty members from the first
+ // elemnt in the list
+ if ((member != "") || (!members.isEmpty))
+ members ::= member
last_index = index + 1
}
index += 1
@@ -184,4 +227,4 @@ trait MemberLookup {
object MemberLookup {
private[this] var _showExplanation = true
def showExplanation: Boolean = if (_showExplanation) { _showExplanation = false; true } else false
-} \ No newline at end of file
+}
diff --git a/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala b/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala
index a987da8ba6..07526a5608 100644
--- a/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala
+++ b/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala
@@ -209,7 +209,8 @@ class ModelFactory(val global: Global, val settings: doc.Settings) {
((!sym.isTrait && ((sym hasFlag Flags.ABSTRACT) || (sym hasFlag Flags.DEFERRED)) && (!isImplicitlyInherited)) ||
sym.isAbstractClass || sym.isAbstractType) && !sym.isSynthetic
def isTemplate = false
- lazy val signature = {
+ def signature = externalSignature(sym)
+ lazy val signatureCompat = {
def defParams(mbr: Any): String = mbr match {
case d: MemberEntity with Def =>
@@ -1082,5 +1083,17 @@ class ModelFactory(val global: Global, val settings: doc.Settings) {
(settings.docExpandAllTypes.value && (bSym.sourceFile != null)) ||
{ val rawComment = global.expandedDocComment(bSym, inTpl.sym)
rawComment.contains("@template") || rawComment.contains("@documentable") }
+
+ def findExternalLink(name: String): Option[LinkTo] =
+ settings.extUrlMapping find {
+ case (pkg, _) => name startsWith pkg
+ } map {
+ case (_, url) => LinkToExternal(name, url + "#" + name)
+ }
+
+ def externalSignature(sym: Symbol) = {
+ sym.info // force it, otherwise we see lazy types
+ (sym.nameString + sym.signatureString).replaceAll("\\s", "")
+ }
}
diff --git a/src/compiler/scala/tools/nsc/doc/model/ModelFactoryTypeSupport.scala b/src/compiler/scala/tools/nsc/doc/model/ModelFactoryTypeSupport.scala
index 6c80c85efe..c67a398bb7 100644
--- a/src/compiler/scala/tools/nsc/doc/model/ModelFactoryTypeSupport.scala
+++ b/src/compiler/scala/tools/nsc/doc/model/ModelFactoryTypeSupport.scala
@@ -30,7 +30,7 @@ trait ModelFactoryTypeSupport {
import definitions.{ ObjectClass, NothingClass, AnyClass, AnyValClass, AnyRefClass }
import rootMirror.{ RootPackage, RootClass, EmptyPackage }
- protected var typeCache = new mutable.LinkedHashMap[Type, TypeEntity]
+ protected val typeCache = new mutable.LinkedHashMap[Type, TypeEntity]
/** */
def makeType(aType: Type, inTpl: TemplateImpl): TypeEntity = {
@@ -77,7 +77,8 @@ trait ModelFactoryTypeSupport {
// ===> in such cases we have two options:
// (0) if there's no inheritance taking place (Enum#Value) we can link to the template directly
// (1) if we generate the doc template for Day, we can link to the correct member
- // (2) if we don't generate the doc template, we should at least indicate the correct prefix in the tooltip
+ // (2) If the symbol comes from an external library for which we know the documentation URL, point to it.
+ // (3) if we don't generate the doc template, we should at least indicate the correct prefix in the tooltip
val bSym = normalizeTemplate(aSym)
val owner =
if ((preSym != NoSymbol) && /* it needs a prefix */
@@ -87,20 +88,27 @@ trait ModelFactoryTypeSupport {
else
bSym.owner
- val bTpl = findTemplateMaybe(bSym)
val link =
- if (owner == bSym.owner && bTpl.isDefined)
- // (0) the owner's class is linked AND has a template - lovely
- LinkToTpl(bTpl.get)
- else {
- val oTpl = findTemplateMaybe(owner)
- val bMbr = oTpl.map(findMember(bSym, _))
- if (oTpl.isDefined && bMbr.isDefined && bMbr.get.isDefined)
- // (1) the owner's class
- LinkToMember(bMbr.get.get, oTpl.get) //ugh
- else
- // (2) if we still couldn't find the owner, show a tooltip with the qualified name
- Tooltip(makeQualifiedName(bSym))
+ findTemplateMaybe(bSym) match {
+ case Some(bTpl) if owner == bSym.owner =>
+ // (0) the owner's class is linked AND has a template - lovely
+ LinkToTpl(bTpl)
+ case _ =>
+ val oTpl = findTemplateMaybe(owner)
+ (oTpl, oTpl flatMap (findMember(bSym, _))) match {
+ case (Some(oTpl), Some(bMbr)) =>
+ // (1) the owner's class
+ LinkToMember(bMbr, oTpl)
+ case _ =>
+ val name = makeQualifiedName(bSym)
+ if (!bSym.owner.isPackage)
+ Tooltip(name)
+ else
+ findExternalLink(name).getOrElse (
+ // (3) if we couldn't find neither the owner nor external URL to link to, show a tooltip with the qualified name
+ Tooltip(name)
+ )
+ }
}
// SI-4360 Showing prefixes when necessary
@@ -308,16 +316,8 @@ trait ModelFactoryTypeSupport {
// SI-4360: Entity caching depends on both the type AND the template it's in, as the prefixes might change for the
// same type based on the template the type is shown in.
- if (settings.docNoPrefixes.value) {
- val cached = typeCache.get(aType)
- cached match {
- case Some(typeEntity) =>
- typeEntity
- case None =>
- val typeEntity = createTypeEntity
- typeCache += aType -> typeEntity
- typeEntity
- }
- } else createTypeEntity
+ if (settings.docNoPrefixes.value)
+ typeCache.getOrElseUpdate(aType, createTypeEntity)
+ else createTypeEntity
}
-} \ No newline at end of file
+}
diff --git a/src/compiler/scala/tools/nsc/doc/model/comment/CommentFactory.scala b/src/compiler/scala/tools/nsc/doc/model/comment/CommentFactory.scala
index 47eea52095..1baa7f9831 100644
--- a/src/compiler/scala/tools/nsc/doc/model/comment/CommentFactory.scala
+++ b/src/compiler/scala/tools/nsc/doc/model/comment/CommentFactory.scala
@@ -760,7 +760,7 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory with Member
}
def link(): Inline = {
- val SchemeUri = """([^:]+:.*)""".r
+ val SchemeUri = """([a-z]+:.*)""".r
jump("[[")
var parens = 1
readUntil { parens += 1; !check("[") }
diff --git a/src/compiler/scala/tools/nsc/doc/model/diagram/DiagramFactory.scala b/src/compiler/scala/tools/nsc/doc/model/diagram/DiagramFactory.scala
index fa698afaa6..db2d0c0175 100644
--- a/src/compiler/scala/tools/nsc/doc/model/diagram/DiagramFactory.scala
+++ b/src/compiler/scala/tools/nsc/doc/model/diagram/DiagramFactory.scala
@@ -150,7 +150,7 @@ trait DiagramFactory extends DiagramDirectiveParser {
if (nodesShown.isEmpty)
None
else {
- val nodes = nodesAll.filter(nodesShown.contains(_)).map(mapNodes(_))
+ val nodes = nodesAll.filter(nodesShown.contains(_)).flatMap(mapNodes.get(_))
val edges = edgesAll.map(pair => (mapNodes(pair._1), pair._2.map(mapNodes(_)))).filterNot(pair => pair._2.isEmpty)
val diagram =
// TODO: Everyone should be able to use the @{inherit,content}Diagram annotation to change the diagrams.
diff --git a/test/scaladoc/resources/links.scala b/test/scaladoc/resources/links.scala
index 679d0b0dce..09a52a4334 100644
--- a/test/scaladoc/resources/links.scala
+++ b/test/scaladoc/resources/links.scala
@@ -34,24 +34,29 @@ package scala.test.scaladoc.links {
/**
* Links to the trait:
- * - [[scala.test.scaladoc.links.Target!.T trait Target -> type T]]
- * - [[test.scaladoc.links.Target!.S trait Target -> type S]]
- * - [[scaladoc.links.Target!.foo(Int)* trait Target -> def foo]]
- * - [[links.Target!.bar trait Target -> def bar]]
- * - [[[[Target!.foo[A[_[_]]]* trait Target -> def foo with 3 nested tparams]]]] (should exercise nested parens)
- * - [[Target$.T object Target -> type T]]
- * - [[Target$.S object Target -> type S]]
- * - [[Target$.foo(Str* object Target -> def foo]]
- * - [[Target$.bar object Target -> def bar]]
- * - [[[[Target$.foo[A[_[_]]]* trait Target -> def foo with 3 nested tparams]]]] (should exercise nested parens)
- * - [[Target.onlyInObject object Target -> def foo]] (should find the object)
- * - [[Target$.C object Target -> class C]] (should link directly to C, not as a member)
- * - [[Target!.C trait Target -> class C]] (should link directly to C, not as a member)
- * - [[Target$.baz(links\.C)* object Target -> def baz]] (should use dots in prefix)
- * - [[Target!.baz(links\.C)* trait Target -> def baz]] (should use dots in prefix)
- * - [[localMethod object TEST -> localMethod]] (should use the current template to resolve link instead of inTpl, that's the package)
+ * - [[scala.test.scaladoc.links.Target$ object Test]]
+ * - [[scala.test package scala.test]]
+ * - [[scala.test.scaladoc.links.Target!.T trait Target -> type T]]
+ * - [[test.scaladoc.links.Target!.S trait Target -> type S]]
+ * - [[scaladoc.links.Target!.foo(i:Int)* trait Target -> def foo]]
+ * - [[links.Target!.bar trait Target -> def bar]]
+ * - [[[[Target!.foo[A[_[_]]]* trait Target -> def foo with 3 nested tparams]]]] (should exercise nested parens)
+ * - [[Target$.T object Target -> type T]]
+ * - [[Target$.S object Target -> type S]]
+ * - [[Target$.foo(z:Str* object Target -> def foo]]
+ * - [[Target$.bar object Target -> def bar]]
+ * - [[[[Target$.foo[A[_[_]]]* trait Target -> def foo with 3 nested tparams]]]] (should exercise nested parens)
+ * - [[Target.onlyInObject object Target -> def foo]] (should find the object)
+ * - [[Target$.C object Target -> class C]] (should link directly to C, not as a member)
+ * - [[Target!.C trait Target -> class C]] (should link directly to C, not as a member)
+ * - [[Target$.baz(c:scala\.test\.scaladoc\.links\.C)* object Target -> def baz]] (should use dots in prefix)
+ * - [[Target!.baz(c:scala\.test\.scaladoc\.links\.C)* trait Target -> def baz]] (should use dots in prefix)
+ * - [[localMethod object TEST -> localMethod]] (should use the current template to resolve link instead of inTpl, that's the package)
+ * - [[#localMethod object TEST -> localMethod]] (should exercise Java-style links to empty members)
+ * - [[ImOutside class ImOutside (check correct lookup in EmptyPackage)]]
*/
object TEST {
def localMethod = 3
}
}
+class ImOutside \ No newline at end of file
diff --git a/test/scaladoc/run/links.scala b/test/scaladoc/run/links.scala
index 40ce6368ce..de359539cf 100644
--- a/test/scaladoc/run/links.scala
+++ b/test/scaladoc/run/links.scala
@@ -22,7 +22,7 @@ object Test extends ScaladocModelTest {
val memberLinks = countLinks(TEST.comment.get, _.link.isInstanceOf[LinkToMember])
val templateLinks = countLinks(TEST.comment.get, _.link.isInstanceOf[LinkToTpl])
- assert(memberLinks == 14, memberLinks + " == 14 (the member links in object TEST)")
- assert(templateLinks == 2, templateLinks + " == 2 (the template links in object TEST)")
+ assert(memberLinks == 15, memberLinks + " == 15 (the member links in object TEST)")
+ assert(templateLinks == 5, templateLinks + " == 5 (the template links in object TEST)")
}
} \ No newline at end of file